PrismLauncher-11.0.3/0000755000175100017510000000000015224505336013763 5ustar runnerrunnerPrismLauncher-11.0.3/.clang-format0000644000175100017510000000070115224505336016334 0ustar runnerrunner--- BasedOnStyle: Chromium IndentWidth: 4 AllowShortIfStatementsOnASingleLine: false ColumnLimit: 140 --- Language: Cpp AccessModifierOffset: -1 AlignConsecutiveMacros: None AlignConsecutiveAssignments: None BraceWrapping: AfterFunction: true SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false BreakBeforeBraces: Custom BreakConstructorInitializers: BeforeComma Cpp11BracedListStyle: false QualifierAlignment: Left PrismLauncher-11.0.3/flake.nix0000644000175100017510000001566615224505336015603 0ustar runnerrunner{ description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)"; nixConfig = { extra-substituters = [ "https://prismlauncher.cachix.org" ]; extra-trusted-public-keys = [ "prismlauncher.cachix.org-1:9/n/FGyABA2jLUVfY+DEp4hKds/rwO+SCOtbOkDzd+c=" ]; }; inputs = { nixpkgs.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"; libnbtplusplus = { url = "github:PrismLauncher/libnbtplusplus"; flake = false; }; }; outputs = { self, nixpkgs, libnbtplusplus, }: let inherit (nixpkgs) lib; # While we only officially support aarch and x86_64 on Linux and MacOS, # we expose a reasonable amount of other systems for users who want to # build for most exotic platforms systems = lib.systems.flakeExposed; forAllSystems = lib.genAttrs systems; nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system}); in { checks = forAllSystems ( system: let pkgs = nixpkgsFor.${system}; llvm = pkgs.llvmPackages_22; in { formatting = pkgs.runCommand "check-formatting" { nativeBuildInputs = with pkgs; [ deadnix llvm.clang-tools markdownlint-cli nixfmt-rfc-style statix ]; } '' cd ${self} echo "Running clang-format...." clang-format --dry-run --style='file' --Werror */**.{c,cc,cpp,h,hh,hpp} echo "Running deadnix..." deadnix --fail echo "Running markdownlint..." markdownlint --dot . echo "Running nixfmt..." find -type f -name '*.nix' -exec nixfmt --check {} + echo "Running statix" statix check . touch $out ''; } ); devShells = forAllSystems ( system: let pkgs = nixpkgsFor.${system}; llvm = pkgs.llvmPackages_22; python = pkgs.python3; mkShell = pkgs.mkShell.override { inherit (llvm) stdenv; }; packages' = self.packages.${system}; welcomeMessage = '' Welcome to the Prism Launcher repository! 🌈 We just set some things up for you. To get building, you can run: ``` $ cd "$cmakeBuildDir" $ ninjaBuildPhase $ ninjaInstallPhase ``` Feel free to ask any questions in our Discord server or Matrix space: - https://prismlauncher.org/discord - https://matrix.to/#/#prismlauncher:matrix.org And thanks for helping out :) ''; # Re-use our package wrapper to wrap our development environment qt-wrapper-env = packages'.prismlauncher.overrideAttrs (old: { name = "qt-wrapper-env"; # Required to use script-based makeWrapper below strictDeps = true; # We don't need/want the unwrapped Prism package paths = [ ]; nativeBuildInputs = old.nativeBuildInputs or [ ] ++ [ # Ensure the wrapper is script based so it can be sourced pkgs.makeWrapper ]; # Inspired by https://discourse.nixos.org/t/python-qt-woes/11808/10 buildCommand = '' makeQtWrapper ${lib.getExe pkgs.runtimeShellPackage} "$out" sed -i '/^exec/d' "$out" ''; }); in { default = mkShell { name = "prism-launcher"; inputsFrom = [ packages'.prismlauncher-unwrapped ]; packages = [ pkgs.ccache llvm.clang-tools python # NOTE(@getchoo): Required for run-clang-tidy, etc. (pkgs.stdenvNoCC.mkDerivation { pname = "clang-tidy-diff"; inherit (llvm.clang) version; nativeBuildInputs = [ pkgs.installShellFiles python.pkgs.wrapPython ]; dontUnpack = true; dontConfigure = true; dontBuild = true; postInstall = "installBin ${llvm.libclang.python}/share/clang/clang-tidy-diff.py"; postFixup = "wrapPythonPrograms"; }) ]; cmakeBuildType = "Debug"; cmakeFlags = [ "-GNinja" ] ++ packages'.prismlauncher-unwrapped.cmakeFlags; dontFixCmake = true; shellHook = '' echo "Sourcing ${qt-wrapper-env}" source ${qt-wrapper-env} git submodule update --init --force if [ ! -f compile_commands.json ]; then cmakeConfigurePhase cd .. ln -s "$cmakeBuildDir"/compile_commands.json compile_commands.json fi echo ${lib.escapeShellArg welcomeMessage} ''; }; } ); formatter = forAllSystems (system: nixpkgsFor.${system}.nixfmt-rfc-style); overlays.default = final: prev: let llvm = final.llvmPackages_22 or prev.llvmPackages_22; in { prismlauncher-unwrapped = prev.callPackage ./nix/unwrapped.nix { inherit (llvm) stdenv; inherit libnbtplusplus self ; }; prismlauncher = final.callPackage ./nix/wrapper.nix { }; }; packages = forAllSystems ( system: let pkgs = nixpkgsFor.${system}; # Build a scope from our overlay prismPackages = lib.makeScope pkgs.newScope (final: self.overlays.default final pkgs); # Grab our packages from it and set the default packages = { inherit (prismPackages) prismlauncher-unwrapped prismlauncher; default = prismPackages.prismlauncher; }; in # Only output them if they're available on the current system lib.filterAttrs (_: lib.meta.availableOn pkgs.stdenv.hostPlatform) packages ); # We put these under legacyPackages as they are meant for CI, not end user consumption legacyPackages = forAllSystems ( system: let packages' = self.packages.${system}; legacyPackages' = self.legacyPackages.${system}; in { prismlauncher-debug = packages'.prismlauncher.override { prismlauncher-unwrapped = legacyPackages'.prismlauncher-unwrapped-debug; }; prismlauncher-unwrapped-debug = packages'.prismlauncher-unwrapped.overrideAttrs { cmakeBuildType = "Debug"; dontStrip = true; }; } ); }; } PrismLauncher-11.0.3/CODE_OF_CONDUCT.md0000644000175100017510000001310415224505336016561 0ustar runnerrunner# Contributor Covenant Code of Conduct This is a modified version of the Contributor Covenant. See commit history to see our changes. ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling (antagonistic, inflammatory, insincere behaviour), insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement via email at [coc@scrumplex.net](mailto:coc@scrumplex.net) (Email address subject to change). All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations PrismLauncher-11.0.3/vcpkg-configuration.json0000644000175100017510000000071215224505336020635 0ustar runnerrunner{ "default-registry": { "kind": "git", "baseline": "2d6a6cf3ac9a7cc93942c3d289a2f9c661a6f4a7", "repository": "https://github.com/microsoft/vcpkg" }, "registries": [ { "kind": "artifact", "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", "name": "microsoft" } ], "overlay-ports": [ "./cmake/vcpkg-ports" ], "overlay-triplets": [ "./cmake/vcpkg-triplets" ] } PrismLauncher-11.0.3/tools/0000755000175100017510000000000015224505336015123 5ustar runnerrunnerPrismLauncher-11.0.3/tools/ninjatracing.py0000644000175100017510000001572315224505336020154 0ustar runnerrunner#!/usr/bin/env python3 # Copyright 2018 Nico Weber # Patched to work with v7 logs # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Converts one (or several) .ninja_log files into chrome's about:tracing format. If clang -ftime-trace .json files are found adjacent to generated files they are embedded. Usage: ninja -C $BUILDDIR ninjatracing $BUILDDIR/.ninja_log > trace.json """ import json import os import optparse import re import sys class Target: """Represents a single line read for a .ninja_log file. Start and end times are milliseconds.""" def __init__(self, start, end): self.start = int(start) self.end = int(end) self.targets = [] def read_targets(log, show_all): """Reads all targets from .ninja_log file |log_file|, sorted by start time""" header = log.readline() m = re.search(r'^# ninja log v(\d+)\n$', header) assert m, "unrecognized ninja log version %r" % header version = int(m.group(1)) assert 5 <= version <= 7, "unsupported ninja log version %d" % version if version >= 6: # Skip header line next(log) targets = {} last_end_seen = 0 for line in log: if line.startswith('#'): continue start, end, _, name, cmdhash = line.strip().split('\t') # Ignore restat. if not show_all and int(end) < last_end_seen: # An earlier time stamp means that this step is the first in a new # build, possibly an incremental build. Throw away the previous data # so that this new build will be displayed independently. targets = {} last_end_seen = int(end) targets.setdefault(cmdhash, Target(start, end)).targets.append(name) return sorted(targets.values(), key=lambda job: job.end, reverse=True) class Threads: """Tries to reconstruct the parallelism from a .ninja_log""" def __init__(self): self.workers = [] # Maps thread id to time that thread is occupied for. def alloc(self, target): """Places target in an available thread, or adds a new thread.""" for worker in range(len(self.workers)): if self.workers[worker] >= target.end: self.workers[worker] = target.start return worker self.workers.append(target.start) return len(self.workers) - 1 def read_events(trace, options): """Reads all events from time-trace json file |trace|.""" trace_data = json.load(trace) def include_event(event, options): """Only include events if they are complete events, are longer than granularity, and are not totals.""" return ((event['ph'] == 'X') and (event['dur'] >= options['granularity']) and (not event['name'].startswith('Total'))) return [x for x in trace_data['traceEvents'] if include_event(x, options)] def trace_to_dicts(target, trace, options, pid, tid): """Read a file-like object |trace| containing -ftime-trace data and yields about:tracing dict per eligible event in that log.""" ninja_time = (target.end - target.start) * 1000 for event in read_events(trace, options): # Check if any event duration is greater than the duration from ninja. if event['dur'] > ninja_time: print("Inconsistent timing found (clang time > ninja time). Please" " ensure that timings are from consistent builds.") sys.exit(1) # Set tid and pid from ninja log. event['pid'] = pid event['tid'] = tid # Offset trace time stamp by ninja start time. event['ts'] += (target.start * 1000) yield event def embed_time_trace(ninja_log_dir, target, pid, tid, options): """Produce time trace output for the specified ninja target. Expects time-trace file to be in .json file named based on .o file.""" for t in target.targets: o_path = os.path.join(ninja_log_dir, t) json_trace_path = os.path.splitext(o_path)[0] + '.json' try: with open(json_trace_path, 'r') as trace: for time_trace_event in trace_to_dicts(target, trace, options, pid, tid): yield time_trace_event except IOError: pass def log_to_dicts(log, pid, options): """Reads a file-like object |log| containing a .ninja_log, and yields one about:tracing dict per command found in the log.""" threads = Threads() for target in read_targets(log, options['showall']): tid = threads.alloc(target) yield { 'name': '%0s' % ', '.join(target.targets), 'cat': 'targets', 'ph': 'X', 'ts': (target.start * 1000), 'dur': ((target.end - target.start) * 1000), 'pid': pid, 'tid': tid, 'args': {}, } if options.get('embed_time_trace', False): # Add time-trace information into the ninja trace. try: ninja_log_dir = os.path.dirname(log.name) except AttributeError: continue for time_trace in embed_time_trace(ninja_log_dir, target, pid, tid, options): yield time_trace def main(argv): usage = __doc__ parser = optparse.OptionParser(usage) parser.add_option('-a', '--showall', action='store_true', dest='showall', default=False, help='report on last build step for all outputs. Default ' 'is to report just on the last (possibly incremental) ' 'build') parser.add_option('-g', '--granularity', type='int', default=50000, dest='granularity', help='minimum length time-trace event to embed in ' 'microseconds. Default: %default') parser.add_option('-e', '--embed-time-trace', action='store_true', default=False, dest='embed_time_trace', help='embed clang -ftime-trace json file found adjacent ' 'to a target file') (options, args) = parser.parse_args() if len(args) == 0: print('Must specify at least one .ninja_log file') parser.print_help() return 1 entries = [] for pid, log_file in enumerate(args): with open(log_file, 'r') as log: entries += list(log_to_dicts(log, pid, vars(options))) json.dump(entries, sys.stdout) if __name__ == '__main__': sys.exit(main(sys.argv[1:])) PrismLauncher-11.0.3/Containerfile0000644000175100017510000000436515224505336016500 0ustar runnerrunnerARG DEBIAN_VERSION=stable-slim FROM docker.io/library/debian:${DEBIAN_VERSION} ARG QT_ABI=gcc_64 ARG QT_ARCH= ARG QT_HOST=linux ARG QT_TARGET=desktop ARG QT_VERSION=6.10.2 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update \ && apt-get --assume-yes upgrade \ && apt-get --assume-yes autopurge # Use Adoptium for Java 17 RUN apt-get --assume-yes --no-install-recommends install \ apt-transport-https ca-certificates curl gpg RUN curl -L https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | tee /etc/apt/trusted.gpg.d/adoptium.gpg RUN echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list RUN apt-get update # Install base dependencies RUN apt-get --assume-yes --no-install-recommends install \ # Compilers clang lld llvm temurin-17-jdk \ # Build system cmake ninja-build extra-cmake-modules pkg-config \ # Dependencies cmark gamemode-dev libarchive-dev libcmark-dev libgamemode0 libgl1-mesa-dev libqrencode-dev libtomlplusplus-dev scdoc zlib1g-dev \ # Tooling clang-format clang-tidy git # Use LLD by default for faster linking ENV CMAKE_LINKER_TYPE=lld # Prepare and install Qt ## Setup UTF-8 locale (required, apparently) RUN apt-get --assume-yes --no-install-recommends install locales RUN echo "C.UTF-8 UTF-8" > /etc/locale.gen RUN locale-gen ENV LC_ALL=C.UTF-8 ## Some libraries are required for the official binaries RUN apt-get --assume-yes --no-install-recommends install \ libglib2.0-0t64 libxkbcommon0 python3-pip RUN pip3 install --break-system-packages aqtinstall RUN aqt install-qt \ ${QT_HOST} ${QT_TARGET} ${QT_VERSION} ${QT_ARCH} \ --outputdir /opt/qt \ --modules qtimageformats qtnetworkauth ENV PATH=/opt/qt/${QT_VERSION}/${QT_ABI}/bin:$PATH ENV QT_PLUGIN_PATH=/opt/qt/${QT_VERSION}/${QT_ABI}/plugins/ ## We don't use these. Nuke them RUN rm -rf \ "$QT_PLUGIN_PATH"/designer \ "$QT_PLUGIN_PATH"/help \ # "$QT_PLUGIN_PATH"/platformthemes/libqgtk3.so \ "$QT_PLUGIN_PATH"/printsupport \ "$QT_PLUGIN_PATH"/qmllint \ "$QT_PLUGIN_PATH"/qmlls \ "$QT_PLUGIN_PATH"/qmltooling \ "$QT_PLUGIN_PATH"/sqldrivers # Setup workspace RUN mkdir /work WORKDIR /work ENTRYPOINT ["bash"] CMD ["-i"] PrismLauncher-11.0.3/buildconfig/0000755000175100017510000000000015224505336016250 5ustar runnerrunnerPrismLauncher-11.0.3/buildconfig/BuildConfig.cpp.in0000644000175100017510000001315715224505336021555 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "BuildConfig.h" const Config BuildConfig; Config::Config() { // Name and copyright LAUNCHER_NAME = "@Launcher_Name@"; LAUNCHER_APP_BINARY_NAME = "@Launcher_APP_BINARY_NAME@"; LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@"; LAUNCHER_COPYRIGHT = "@Launcher_Copyright@"; LAUNCHER_DOMAIN = "@Launcher_Domain@"; LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@"; LAUNCHER_GIT = "@Launcher_Git@"; LAUNCHER_APPID = "@Launcher_AppID@"; LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@"; LAUNCHER_ENVNAME = "@Launcher_ENVName@"; USER_AGENT = "@Launcher_UserAgent@"; // Version information VERSION_MAJOR = @Launcher_VERSION_MAJOR@; VERSION_MINOR = @Launcher_VERSION_MINOR@; VERSION_PATCH = @Launcher_VERSION_PATCH@; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@"; BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@"; UPDATER_GITHUB_REPO = "@Launcher_UPDATER_GITHUB_REPO@"; COMPILER_NAME = "@Launcher_COMPILER_NAME@"; COMPILER_VERSION = "@Launcher_COMPILER_VERSION@"; COMPILER_TARGET_SYSTEM = "@Launcher_COMPILER_TARGET_SYSTEM@"; COMPILER_TARGET_SYSTEM_VERSION = "@Launcher_COMPILER_TARGET_SYSTEM_VERSION@"; COMPILER_TARGET_SYSTEM_PROCESSOR = "@Launcher_COMPILER_TARGET_PROCESSOR@"; MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@"; MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@"; if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty()) { UPDATER_ENABLED = true; } else if (!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) { UPDATER_ENABLED = true; } #cmakedefine01 Launcher_ENABLE_JAVA_DOWNLOADER JAVA_DOWNLOADER_ENABLED = Launcher_ENABLE_JAVA_DOWNLOADER; GIT_COMMIT = "@Launcher_GIT_COMMIT@"; GIT_TAG = "@Launcher_GIT_TAG@"; GIT_REFSPEC = "@Launcher_GIT_REFSPEC@"; // Assume that builds outside of Git repos are "stable" if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND") || GIT_TAG == QStringLiteral("GITDIR-NOTFOUND") || GIT_REFSPEC == QStringLiteral("") || GIT_TAG == QStringLiteral("GIT-NOTFOUND")) { GIT_REFSPEC = "refs/heads/stable"; GIT_TAG = versionString(); GIT_COMMIT = ""; } if (GIT_REFSPEC.startsWith("refs/heads/")) { VERSION_CHANNEL = GIT_REFSPEC; VERSION_CHANNEL.remove("refs/heads/"); } else if (!GIT_COMMIT.isEmpty()) { VERSION_CHANNEL = GIT_COMMIT.mid(0, 8); } else { VERSION_CHANNEL = "unknown"; } NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@"; NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@"; WIKI_URL = "@Launcher_WIKI_URL@"; HELP_URL = "@Launcher_HELP_URL@"; LOGIN_CALLBACK_URL = "@Launcher_LOGIN_CALLBACK_URL@"; IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@"; MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@"; FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@"; META_URL = "@Launcher_META_URL@"; LEGACY_FMLLIBS_BASE_URL = "@Launcher_LEGACY_FMLLIBS_BASE_URL@"; GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@"; OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@"; BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@"; TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@"; TRANSLATION_FILES_URL = "@Launcher_TRANSLATION_FILES_URL@"; MATRIX_URL = "@Launcher_MATRIX_URL@"; DISCORD_URL = "@Launcher_DISCORD_URL@"; SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@"; } QString Config::versionString() const { return QString("%1.%2.%3").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_PATCH); } QString Config::printableVersionString() const { QString vstr = versionString(); // If the build is not a main release, append the channel if (VERSION_CHANNEL != "stable" && GIT_TAG != vstr) { vstr += "-" + VERSION_CHANNEL; } return vstr; } QString Config::compilerID() const { if (COMPILER_VERSION.isEmpty()) return COMPILER_NAME; return QStringLiteral("%1 - %2").arg(COMPILER_NAME).arg(COMPILER_VERSION); } QString Config::systemID() const { return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR); } PrismLauncher-11.0.3/buildconfig/BuildConfig.h0000644000175100017510000001533415224505336020614 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include /** * \brief The Config class holds all the build-time information passed from the build system. */ class Config { public: Config(); QString LAUNCHER_NAME; QString LAUNCHER_APP_BINARY_NAME; QString LAUNCHER_DISPLAYNAME; QString LAUNCHER_COPYRIGHT; QString LAUNCHER_DOMAIN; QString LAUNCHER_CONFIGFILE; QString LAUNCHER_GIT; QString LAUNCHER_APPID; QString LAUNCHER_SVGFILENAME; QString LAUNCHER_ENVNAME; /// The major version number. int VERSION_MAJOR; /// The minor version number. int VERSION_MINOR; /// The patch version number. int VERSION_PATCH; /** * The version channel * This is used by the updater to determine what channel the current version came from. */ QString VERSION_CHANNEL; bool UPDATER_ENABLED = false; bool JAVA_DOWNLOADER_ENABLED = false; /// A short string identifying this build's platform or distribution. QString BUILD_PLATFORM; /// A short string identifying this build's valid artifacts int he updater. For example, "lin64" or "win32". QString BUILD_ARTIFACT; /// A string containing the build timestamp QString BUILD_DATE; /// A string identifying the compiler use to build QString COMPILER_NAME; /// A string identifying the compiler version used to build QString COMPILER_VERSION; /// A string identifying the compiler target system os QString COMPILER_TARGET_SYSTEM; /// A String identifying the compiler target system version QString COMPILER_TARGET_SYSTEM_VERSION; /// A String identifying the compiler target processor QString COMPILER_TARGET_SYSTEM_PROCESSOR; /// URL for the updater's channel QString UPDATER_GITHUB_REPO; /// The public key used to sign releases for the Sparkle updater appcast QString MAC_SPARKLE_PUB_KEY; /// URL for the Sparkle updater's appcast QString MAC_SPARKLE_APPCAST_URL; /// User-Agent to use. QString USER_AGENT; /// The git commit hash of this build QString GIT_COMMIT; /// The git tag of this build QString GIT_TAG; /// The git refspec of this build QString GIT_REFSPEC; /** * This is used to fetch the news RSS feed. * It defaults in CMakeLists.txt to "https://multimc.org/rss.xml" */ QString NEWS_RSS_URL; /** * URL that gets opened when the user clicks "More News" */ QString NEWS_OPEN_URL; /** * URL that gets opened when the user clicks 'Launcher Help' */ QString WIKI_URL; /** * URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help in a dialog window */ QString HELP_URL; /** * URL that gets opened when the user succesfully logins. */ QString LOGIN_CALLBACK_URL; /** * Client ID you can get from Imgur when you register an application */ QString IMGUR_CLIENT_ID; /** * Client ID you can get from Microsoft Identity Platform when you register an application */ QString MSA_CLIENT_ID; /** * Client API key for CurseForge */ QString FLAME_API_KEY; /** * Metadata repository URL prefix */ QString META_URL; QString GLFW_LIBRARY_NAME; QString OPENAL_LIBRARY_NAME; QString BUG_TRACKER_URL; QString TRANSLATIONS_URL; QString MATRIX_URL; QString DISCORD_URL; QString SUBREDDIT_URL; QString DEFAULT_RESOURCE_BASE = "https://resources.download.minecraft.net/"; QString LIBRARY_BASE = "https://libraries.minecraft.net/"; QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; QString LEGACY_FMLLIBS_BASE_URL; QString TRANSLATION_FILES_URL; QString FTB_API_BASE_URL = "https://api.feed-the-beast.com/v1/modpacks/public"; QString LEGACY_FTB_CDN_BASE_URL = "https://dist.creeper.host/FTB2/"; QString ATL_DOWNLOAD_SERVER_URL = "https://download.nodecdn.net/containers/atl/"; QString ATL_API_BASE_URL = "https://api.atlauncher.com/v1/"; QString TECHNIC_API_BASE_URL = "https://api.technicpack.net/"; /** * The build that is reported to the Technic API. */ QString TECHNIC_API_BUILD = "multimc"; QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2"; QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2"; QStringList MODRINTH_MRPACK_HOSTS{ "cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com" }; QString MODRINTH_DOWNLOAD_HOST = "cdn.modrinth.com"; QString FLAME_BASE_URL = "https://api.curseforge.com/v1"; QString FLAME_DOWNLOAD_HOST = "edge.forgecdn.net"; QString versionString() const; /** * \brief Converts the Version to a string. * \return The version number in string format (major.minor.revision.build). */ QString printableVersionString() const; /** * \brief Compiler ID String * \return a string of the form "Name - Version" of just "Name" if the version is empty */ QString compilerID() const; /** * \brief System ID String * \return a string of the form "OS Verison Processor" */ QString systemID() const; }; extern const Config BuildConfig; PrismLauncher-11.0.3/buildconfig/CMakeLists.txt0000644000175100017510000000063415224505336021013 0ustar runnerrunner######## Configure the file with build properties ######## configure_file("${CMAKE_CURRENT_SOURCE_DIR}/BuildConfig.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp") add_library(BuildConfig STATIC BuildConfig.h ${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp ) target_link_libraries(BuildConfig Qt${QT_VERSION_MAJOR}::Core) target_include_directories(BuildConfig PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") PrismLauncher-11.0.3/CMakePresets.json0000644000175100017510000001424615224505336017213 0ustar runnerrunner{ "$schema": "https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json", "version": 8, "cmakeMinimumRequired": { "major": 3, "minor": 28 }, "configurePresets": [ { "name": "base", "hidden": true, "binaryDir": "build", "installDir": "install", "generator": "Ninja Multi-Config", "cacheVariables": { "Launcher_BUILD_ARTIFACT": "$penv{ARTIFACT_NAME}", "Launcher_BUILD_PLATFORM": "$penv{BUILD_PLATFORM}", "Launcher_ENABLE_JAVA_DOWNLOADER": "ON", "ENABLE_LTO": "ON" } }, { "name": "linux", "displayName": "Linux", "inherits": [ "base" ], "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" } }, { "name": "macos", "displayName": "macOS", "inherits": [ "base" ], "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Darwin" }, "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" } }, { "name": "macos_universal", "displayName": "macOS (Universal Binary)", "inherits": [ "macos" ], "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "CMAKE_OSX_ARCHITECTURES": "x86_64;arm64", "VCPKG_TARGET_TRIPLET": "universal-osx" } }, { "name": "windows_mingw", "displayName": "Windows (MinGW)", "inherits": [ "base" ], "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" } }, { "name": "windows_msvc", "displayName": "Windows (MSVC)", "inherits": [ "base" ], "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": "$penv{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" } } ], "buildPresets": [ { "name": "linux", "displayName": "Linux", "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" }, "configurePreset": "linux" }, { "name": "macos", "displayName": "macOS", "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Darwin" }, "configurePreset": "macos" }, { "name": "macos_universal", "displayName": "macOS (Universal Binary)", "inherits": [ "macos" ], "configurePreset": "macos_universal" }, { "name": "windows_mingw", "displayName": "Windows (MinGW)", "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, "configurePreset": "windows_mingw" }, { "name": "windows_msvc", "displayName": "Windows (MSVC)", "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, "configurePreset": "windows_msvc" } ], "testPresets": [ { "name": "base", "hidden": true, "output": { "outputOnFailure": true, "verbosity": "extra" }, "execution": { "noTestsAction": "error" }, "filter": { "exclude": { "name": "^example64|example$" } } }, { "name": "linux", "displayName": "Linux", "inherits": [ "base" ], "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" }, "configurePreset": "linux" }, { "name": "macos", "displayName": "macOS", "inherits": [ "base" ], "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Darwin" }, "configurePreset": "macos" }, { "name": "macos_universal", "displayName": "macOS (Universal Binary)", "inherits": [ "base" ], "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Darwin" }, "configurePreset": "macos_universal" }, { "name": "windows_mingw", "displayName": "Windows (MinGW)", "inherits": [ "base" ], "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, "configurePreset": "windows_mingw" }, { "name": "windows_msvc", "displayName": "Windows (MSVC)", "inherits": [ "base" ], "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, "configurePreset": "windows_msvc" } ] } PrismLauncher-11.0.3/.gitignore0000644000175100017510000000122615224505336015754 0ustar runnerrunnerThumbs.db *.kdev4 .user .directory resources/CMakeFiles *~ *.swp html/ # Project Files *.pro.user CMakeLists.txt.user CMakeLists.txt.user.* CMakeSettings.json /CMakeFiles CMakeCache.txt CMakeUserPresets.json /.project /.settings /.idea /.vscode /.vs cmake-build-*/ Debug compile_commands.json # Build dirs build /build-* # Install dirs install /install-* # Ctags File tags # YouCompleteMe config stuff. .ycm_extra_conf.* #OSX Stuff .DS_Store branding/ secrets/ run/ .cache/ # Nix/NixOS .direnv/ ## Used when manually invoking stdenv phases outputs/ ## Regular artifacts result result-* repl-result-* # Flatpak .flatpak-builder flatbuild # Snap *.snap PrismLauncher-11.0.3/.clang-tidy0000644000175100017510000000271315224505336016022 0ustar runnerrunnerFormatStyle: file Checks: "bugprone-*,clang-analyzer-*,cppcoreguidelines-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*, -*-magic-numbers, -*-non-private-member-variables-in-classes, -*-special-member-functions, -bugprone-easily-swappable-parameters, -cppcoreguidelines-owning-memory, -cppcoreguidelines-pro-type-static-cast-downcast, -modernize-use-nodiscard, -modernize-use-trailing-return-type, -portability-avoid-pragma-once, -readability-avoid-unconditional-preprocessor-if, -readability-function-cognitive-complexity, -readability-identifier-length, -readability-redundant-access-specifiers" CheckOptions: misc-include-cleaner.MissingIncludes: false readability-identifier-naming.DefaultCase: "camelBack" readability-identifier-naming.NamespaceCase: "CamelCase" readability-identifier-naming.ClassCase: "CamelCase" readability-identifier-naming.ClassConstantCase: "CamelCase" readability-identifier-naming.EnumCase: "CamelCase" readability-identifier-naming.EnumConstantCase: "CamelCase" readability-identifier-naming.MacroDefinitionCase: "UPPER_CASE" readability-identifier-naming.ClassMemberPrefix: "m_" readability-identifier-naming.StaticConstantPrefix: "s_" readability-identifier-naming.StaticVariablePrefix: "s_" readability-identifier-naming.GlobalConstantPrefix: "g_" readability-implicit-bool-conversion.AllowPointerConditions: true PrismLauncher-11.0.3/tests/0000755000175100017510000000000015224505336015125 5ustar runnerrunnerPrismLauncher-11.0.3/tests/Version_test.cpp0000644000175100017510000001777215224505336020333 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include class VersionTest : public QObject { Q_OBJECT QStringList m_flex_test_names = {}; void addDataColumns() { QTest::addColumn("first"); QTest::addColumn("second"); QTest::addColumn("lessThan"); QTest::addColumn("equal"); } void setupVersions() { addDataColumns(); QTest::newRow("equal, explicit") << "1.2.0" << "1.2.0" << false << true; QTest::newRow("equal, two-digit") << "1.42" << "1.42" << false << true; QTest::newRow("lessThan, explicit 1") << "1.2.0" << "1.2.1" << true << false; QTest::newRow("lessThan, explicit 2") << "1.2.0" << "1.3.0" << true << false; QTest::newRow("lessThan, explicit 3") << "1.2.0" << "2.2.0" << true << false; QTest::newRow("lessThan, implicit 1") << "1.2" << "1.2.0" << true << false; QTest::newRow("lessThan, implicit 2") << "1.2" << "1.2.1" << true << false; QTest::newRow("lessThan, implicit 3") << "1.2" << "1.3.0" << true << false; QTest::newRow("lessThan, implicit 4") << "1.2" << "2.2.0" << true << false; QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false; QTest::newRow("lessThan, snapshot") << "1.20.0-rc2" << "1.20.1" << true << false; QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false; QTest::newRow("greaterThan, explicit 2") << "1.3.0" << "1.2.0" << false << false; QTest::newRow("greaterThan, explicit 3") << "2.2.0" << "1.2.0" << false << false; QTest::newRow("greaterThan, implicit 1") << "1.2.0" << "1.2" << false << false; QTest::newRow("greaterThan, implicit 2") << "1.2.1" << "1.2" << false << false; QTest::newRow("greaterThan, implicit 3") << "1.3.0" << "1.2" << false << false; QTest::newRow("greaterThan, implicit 4") << "2.2.0" << "1.2" << false << false; QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false; QTest::newRow("greaterThan, snapshot") << "1.20.2-rc2" << "1.20.1" << false << false; } private slots: void test_versionCompare_data() { setupVersions(); } void test_versionCompare() { QFETCH(QString, first); QFETCH(QString, second); QFETCH(bool, lessThan); QFETCH(bool, equal); const auto v1 = Version(first); const auto v2 = Version(second); qDebug() << v1 << "vs" << v2; QCOMPARE(v1 < v2, lessThan); QCOMPARE(v1 > v2, !lessThan && !equal); QCOMPARE(v1 == v2, equal); } void test_flexVerTestVector_data() { addDataColumns(); QDir test_vector_dir(QFINDTESTDATA("testdata/Version")); QFile vector_file{ test_vector_dir.absoluteFilePath("test_vectors.txt") }; if (!vector_file.open(QFile::OpenModeFlag::ReadOnly)) { qCritical() << "Failed to open file" << vector_file.fileName() << "for reading:" << vector_file.errorString(); return; } int test_number = 0; const QString test_name_template{ "FlexVer test #%1 (%2)" }; for (auto line = vector_file.readLine(); !vector_file.atEnd(); line = vector_file.readLine()) { line = line.simplified(); if (line.startsWith('#') || line.isEmpty()) continue; test_number += 1; auto split_line = line.split('<'); if (split_line.size() == 2) { QString first{ split_line.first().simplified() }; QString second{ split_line.last().simplified() }; auto new_test_name = test_name_template.arg(QString::number(test_number), "lessThan"); m_flex_test_names.append(new_test_name); QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << true << false; continue; } split_line = line.split('='); if (split_line.size() == 2) { QString first{ split_line.first().simplified() }; QString second{ split_line.last().simplified() }; auto new_test_name = test_name_template.arg(QString::number(test_number), "equals"); m_flex_test_names.append(new_test_name); QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << false << true; continue; } split_line = line.split('>'); if (split_line.size() == 2) { QString first{ split_line.first().simplified() }; QString second{ split_line.last().simplified() }; auto new_test_name = test_name_template.arg(QString::number(test_number), "greaterThan"); m_flex_test_names.append(new_test_name); QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << false << false; continue; } qCritical() << "Unexpected separator in the test vector:"; qCritical() << line; QVERIFY(0 != 0); } vector_file.close(); } void test_flexVerTestVector() { QFETCH(QString, first); QFETCH(QString, second); QFETCH(bool, lessThan); QFETCH(bool, equal); const auto v1 = Version(first); const auto v2 = Version(second); qDebug() << v1 << "vs" << v2; QCOMPARE(v1 < v2, lessThan); QCOMPARE(v1 > v2, !lessThan && !equal); QCOMPARE(v1 == v2, equal); } static void test_strict_weak_order() { // this tests the strict_weak_order // https://en.cppreference.com/w/cpp/concepts/strict_weak_order.html const Version a("1.10 Pre-Release 1"); // this is a pre-relese is before b because ' ' is lower than '-' const Version b("1.10-pre1"); // this is a pre-release is before c that is an actual release const Version c("1.10"); auto r = [](const Version& a, const Version& b) { return a < b; }; auto e = [&r](const Version& a, const Version& b) { return !r(a, b) && !r(b, a); }; qCritical() << a << b << c; // irreflexive QCOMPARE(r(a, a), false); QCOMPARE(r(b, b), false); QCOMPARE(r(c, c), false); // transitive QCOMPARE(r(a, b), true); QCOMPARE(r(b, c), true); QCOMPARE(r(a, c), true); // transitive equivalence QCOMPARE(e(a, b) && e(b, c), e(a, c)); } }; QTEST_GUILESS_MAIN(VersionTest) #include "Version_test.moc"PrismLauncher-11.0.3/tests/Index_test.cpp0000644000175100017510000000327615224505336017747 0ustar runnerrunner#include #include #include class IndexTest : public QObject { Q_OBJECT private slots: void test_hasUid_and_getList() { Meta::Index windex({ std::make_shared("list1"), std::make_shared("list2"), std::make_shared("list3") }); QVERIFY(windex.hasUid("list1")); QVERIFY(!windex.hasUid("asdf")); QVERIFY(windex.get("list2") != nullptr); QCOMPARE(windex.get("list2")->uid(), QString("list2")); QVERIFY(windex.get("adsf") != nullptr); } void test_merge() { Meta::Index windex({ std::make_shared("list1"), std::make_shared("list2"), std::make_shared("list3") }); QCOMPARE(windex.lists().size(), 3); windex.merge(std::shared_ptr( new Meta::Index({ std::make_shared("list1"), std::make_shared("list2"), std::make_shared("list3") }))); QCOMPARE(windex.lists().size(), 3); windex.merge(std::shared_ptr( new Meta::Index({ std::make_shared("list4"), std::make_shared("list2"), std::make_shared("list5") }))); QCOMPARE(windex.lists().size(), 5); windex.merge(std::shared_ptr(new Meta::Index({ std::make_shared("list6") }))); QCOMPARE(windex.lists().size(), 6); } }; QTEST_GUILESS_MAIN(IndexTest) #include "Index_test.moc" PrismLauncher-11.0.3/tests/TexturePackParse_test.cpp0000644000175100017510000000453115224505336022125 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "FileSystem.h" #include "minecraft/mod/TexturePack.h" #include "minecraft/mod/tasks/LocalTexturePackParseTask.h" class TexturePackParseTest : public QObject { Q_OBJECT private slots: void test_parseZIP() { QString source = QFINDTESTDATA("testdata/TexturePackParse"); QString zip_rp = FS::PathCombine(source, "test_texture_pack_idk.zip"); TexturePack pack{ QFileInfo(zip_rp) }; bool valid = TexturePackUtils::processZIP(pack); QVERIFY(pack.description() == "joe biden, wake up"); QVERIFY(valid == true); } void test_parseFolder() { QString source = QFINDTESTDATA("testdata/TexturePackParse"); QString folder_rp = FS::PathCombine(source, "test_texturefolder"); TexturePack pack{ QFileInfo(folder_rp) }; bool valid = TexturePackUtils::processFolder(pack, TexturePackUtils::ProcessingLevel::BasicInfoOnly); QVERIFY(pack.description() == "Some texture pack surely"); QVERIFY(valid == true); } void test_parseFolder2() { QString source = QFINDTESTDATA("testdata/TexturePackParse"); QString folder_rp = FS::PathCombine(source, "another_test_texturefolder"); TexturePack pack{ QFileInfo(folder_rp) }; bool valid = TexturePackUtils::process(pack, TexturePackUtils::ProcessingLevel::BasicInfoOnly); QVERIFY(pack.description() == "quieres\nfor real"); QVERIFY(valid == true); } }; QTEST_GUILESS_MAIN(TexturePackParseTest) #include "TexturePackParse_test.moc" PrismLauncher-11.0.3/tests/CatPack_test.cpp0000644000175100017510000000353215224505336020201 0ustar runnerrunner#include #include #include #include #include #include "FileSystem.h" #include "ui/themes/CatPack.h" class CatPackTest : public QObject { Q_OBJECT private slots: void test_catPack() { auto dataDir = QDir(QFINDTESTDATA("testdata/CatPacks")).absolutePath(); auto fileName = FS::PathCombine(dataDir, "index.json"); auto fileinfo = QFileInfo(fileName); try { auto cat = JsonCatPack(fileinfo); QCOMPARE(cat.path(QDate(2023, 4, 12)), FS::PathCombine(fileinfo.path(), "oneDay.png")); QCOMPARE(cat.path(QDate(2023, 4, 11)), FS::PathCombine(fileinfo.path(), "maxwell.png")); QCOMPARE(cat.path(QDate(2023, 4, 13)), FS::PathCombine(fileinfo.path(), "maxwell.png")); QCOMPARE(cat.path(QDate(2023, 12, 21)), FS::PathCombine(fileinfo.path(), "christmas.png")); QCOMPARE(cat.path(QDate(2023, 12, 28)), FS::PathCombine(fileinfo.path(), "christmas.png")); QCOMPARE(cat.path(QDate(2023, 12, 29)), FS::PathCombine(fileinfo.path(), "newyear.png")); QCOMPARE(cat.path(QDate(2023, 12, 30)), FS::PathCombine(fileinfo.path(), "newyear2.png")); QCOMPARE(cat.path(QDate(2023, 12, 31)), FS::PathCombine(fileinfo.path(), "newyear2.png")); QCOMPARE(cat.path(QDate(2024, 1, 1)), FS::PathCombine(fileinfo.path(), "newyear2.png")); QCOMPARE(cat.path(QDate(2024, 1, 2)), FS::PathCombine(fileinfo.path(), "newyear.png")); QCOMPARE(cat.path(QDate(2024, 1, 3)), FS::PathCombine(fileinfo.path(), "newyear.png")); QCOMPARE(cat.path(QDate(2024, 1, 4)), FS::PathCombine(fileinfo.path(), "maxwell.png")); } catch (const Exception& e) { QFAIL(e.cause().toLatin1()); } } }; QTEST_GUILESS_MAIN(CatPackTest) #include "CatPack_test.moc" PrismLauncher-11.0.3/tests/Packwiz_test.cpp0000644000175100017510000000642515224505336020307 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "modplatform/ModIndex.h" #include class PackwizTest : public QObject { Q_OBJECT private slots: // Files taken from https://github.com/packwiz/packwiz-example-pack void loadFromFile_Modrinth() { QString source = QFINDTESTDATA("testdata/Packwiz"); QDir index_dir(source); QString slug_mod("borderless-mining"); QString file_name = slug_mod + ".pw.toml"; QVERIFY(index_dir.entryList().contains(file_name)); auto metadata = Packwiz::V1::getIndexForMod(index_dir, slug_mod); QVERIFY(metadata.isValid()); QCOMPARE(metadata.name, "Borderless Mining"); QCOMPARE(metadata.filename, "borderless-mining-1.1.1+1.18.jar"); QCOMPARE(metadata.side, ModPlatform::Side::ClientSide); QCOMPARE(metadata.url, QUrl("https://cdn.modrinth.com/data/kYq5qkSL/versions/1.1.1+1.18/borderless-mining-1.1.1+1.18.jar")); QCOMPARE(metadata.hash_format, "sha512"); QCOMPARE(metadata.hash, "c8fe6e15ddea32668822dddb26e1851e5f03834be4bcb2eff9c0da7fdc086a9b6cead78e31a44d3bc66335cba11144ee0337c6d5346f1ba6362306449" "9b3188d"); QCOMPARE(metadata.provider, ModPlatform::ResourceProvider::MODRINTH); QCOMPARE(metadata.version(), "ug2qKTPR"); QCOMPARE(metadata.mod_id(), "kYq5qkSL"); } void loadFromFile_Curseforge() { QString source = QFINDTESTDATA("testdata/Packwiz"); QDir index_dir(source); QString name_mod("screenshot-to-clipboard-fabric.pw.toml"); QVERIFY(index_dir.entryList().contains(name_mod)); // Try without the .pw.toml at the end name_mod.chop(8); auto metadata = Packwiz::V1::getIndexForMod(index_dir, name_mod); QVERIFY(metadata.isValid()); QCOMPARE(metadata.name, "Screenshot to Clipboard (Fabric)"); QCOMPARE(metadata.filename, "screenshot-to-clipboard-1.0.7-fabric.jar"); QCOMPARE(metadata.side, ModPlatform::Side::UniversalSide); QCOMPARE(metadata.url, QUrl("https://edge.forgecdn.net/files/3509/43/screenshot-to-clipboard-1.0.7-fabric.jar")); QCOMPARE(metadata.hash_format, "murmur2"); QCOMPARE(metadata.hash, "1781245820"); QCOMPARE(metadata.provider, ModPlatform::ResourceProvider::FLAME); QCOMPARE(metadata.file_id, 3509043); QCOMPARE(metadata.project_id, 327154); } }; QTEST_GUILESS_MAIN(PackwizTest) #include "Packwiz_test.moc" PrismLauncher-11.0.3/tests/GradleSpecifier_test.cpp0000644000175100017510000000416615224505336021727 0ustar runnerrunner#include #include class GradleSpecifierTest : public QObject { Q_OBJECT private slots: void initTestCase() {} void cleanupTestCase() {} void test_Positive_data() { QTest::addColumn("through"); QTest::newRow("3 parter") << "org.gradle.test.classifiers:service:1.0"; QTest::newRow("classifier") << "org.gradle.test.classifiers:service:1.0:jdk15"; QTest::newRow("jarextension") << "org.gradle.test.classifiers:service:1.0@jar"; QTest::newRow("jarboth") << "org.gradle.test.classifiers:service:1.0:jdk15@jar"; QTest::newRow("packxz") << "org.gradle.test.classifiers:service:1.0:jdk15@jar.pack.xz"; } void test_Positive() { QFETCH(QString, through); QString converted = GradleSpecifier(through).serialize(); QCOMPARE(converted, through); } void test_Path_data() { QTest::addColumn("spec"); QTest::addColumn("expected"); QTest::newRow("3 parter") << "group.id:artifact:1.0" << "group/id/artifact/1.0/artifact-1.0.jar"; QTest::newRow("doom") << "id.software:doom:1.666:demons@wad" << "id/software/doom/1.666/doom-1.666-demons.wad"; } void test_Path() { QFETCH(QString, spec); QFETCH(QString, expected); QString converted = GradleSpecifier(spec).toPath(); QCOMPARE(converted, expected); } void test_Negative_data() { QTest::addColumn("input"); QTest::newRow("too many :") << "org:gradle.test:class:::ifiers:service:1.0::"; QTest::newRow("nonsense") << "I like turtles"; QTest::newRow("empty string") << ""; QTest::newRow("missing version") << "herp.derp:artifact"; } void test_Negative() { QFETCH(QString, input); GradleSpecifier spec(input); QVERIFY(!spec.valid()); QCOMPARE(spec.serialize(), input); QCOMPARE(spec.toPath(), QString()); } }; QTEST_GUILESS_MAIN(GradleSpecifierTest) #include "GradleSpecifier_test.moc" PrismLauncher-11.0.3/tests/MojangVersionFormat_test.cpp0000644000175100017510000000356415224505336022632 0ustar runnerrunner#include #include #include class MojangVersionFormatTest : public QObject { Q_OBJECT static QJsonDocument readJson(const QString path) { QFile jsonFile(path); if (!jsonFile.open(QIODevice::ReadOnly)) { qWarning() << "Failed to open file" << jsonFile.fileName() << "for reading:" << jsonFile.errorString(); return QJsonDocument(); } auto data = jsonFile.readAll(); jsonFile.close(); return QJsonDocument::fromJson(data); } static void writeJson(const char* file, QJsonDocument doc) { QFile jsonFile(file); if (!jsonFile.open(QIODevice::WriteOnly | QIODevice::Text)) { qCritical() << "Failed to open file" << jsonFile.fileName() << "for writing:" << jsonFile.errorString(); return; } auto data = doc.toJson(QJsonDocument::Indented); qDebug() << QString::fromUtf8(data); jsonFile.write(data); jsonFile.close(); } private slots: void test_Through_Simple() { QJsonDocument doc = readJson(QFINDTESTDATA("testdata/Libraries/1.9-simple.json")); auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9-simple.json"); auto doc2 = MojangVersionFormat::versionFileToJson(vfile); writeJson("1.9-simple-passthorugh.json", doc2); QCOMPARE(doc.toJson(), doc2.toJson()); } void test_Through() { QJsonDocument doc = readJson(QFINDTESTDATA("testdata/Libraries/1.9.json")); auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9.json"); auto doc2 = MojangVersionFormat::versionFileToJson(vfile); writeJson("1.9-passthorugh.json", doc2); QCOMPARE(doc.toJson(), doc2.toJson()); } }; QTEST_GUILESS_MAIN(MojangVersionFormatTest) #include "MojangVersionFormat_test.moc" PrismLauncher-11.0.3/tests/ParseUtils_test.cpp0000644000175100017510000000177715224505336020777 0ustar runnerrunner#include #include class ParseUtilsTest : public QObject { Q_OBJECT private slots: void test_Through_data() { QTest::addColumn("timestamp"); const char* timestamps[] = { "2016-02-29T13:49:54+01:00", "2016-02-26T15:21:11+00:01", "2016-02-24T15:52:36+01:13", "2016-02-18T17:41:00+00:00", "2016-02-17T15:23:19+00:00", "2016-02-16T15:22:39+09:22", "2016-02-10T15:06:41+00:00", "2016-02-04T15:28:02-05:33" }; for (unsigned i = 0; i < (sizeof(timestamps) / sizeof(const char*)); i++) { QTest::newRow(timestamps[i]) << QString(timestamps[i]); } } void test_Through() { QFETCH(QString, timestamp); auto time_parsed = timeFromS3Time(timestamp); auto time_serialized = timeToS3Time(time_parsed); QCOMPARE(time_serialized, timestamp); } }; QTEST_GUILESS_MAIN(ParseUtilsTest) #include "ParseUtils_test.moc" PrismLauncher-11.0.3/tests/DataPackParse_test.cpp0000644000175100017510000000461615224505336021342 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include class DataPackParseTest : public QObject { Q_OBJECT private slots: void test_parseZIP() { QString source = QFINDTESTDATA("testdata/DataPackParse"); QString zip_dp = FS::PathCombine(source, "test_data_pack_boogaloo.zip"); DataPack pack{ QFileInfo(zip_dp) }; bool valid = DataPackUtils::processZIP(&pack); QVERIFY(pack.packFormat() == 4); QVERIFY(pack.description() == "Some data pack 2 boobgaloo"); QVERIFY(valid == true); } void test_parseFolder() { QString source = QFINDTESTDATA("testdata/DataPackParse"); QString folder_dp = FS::PathCombine(source, "test_folder"); DataPack pack{ QFileInfo(folder_dp) }; bool valid = DataPackUtils::processFolder(&pack); QVERIFY(pack.packFormat() == 10); QVERIFY(pack.description() == "Some data pack, maybe"); QVERIFY(valid == true); } void test_parseFolder2() { QString source = QFINDTESTDATA("testdata/DataPackParse"); QString folder_dp = FS::PathCombine(source, "another_test_folder"); DataPack pack{ QFileInfo(folder_dp) }; bool valid = DataPackUtils::process(&pack); QVERIFY(pack.packFormat() == 6); QVERIFY(pack.description() == "Some data pack three, leaves on the tree"); QVERIFY(valid == true); } }; QTEST_GUILESS_MAIN(DataPackParseTest) #include "DataPackParse_test.moc" PrismLauncher-11.0.3/tests/CMakeLists.txt0000644000175100017510000000475215224505336017675 0ustar runnerrunnerproject(tests) ecm_add_test(FileSystem_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME FileSystem) ecm_add_test(GZip_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME GZip) ecm_add_test(GradleSpecifier_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME GradleSpecifier) ecm_add_test(MojangVersionFormat_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME MojangVersionFormat) ecm_add_test(Library_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME Library) ecm_add_test(ResourceFolderModel_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME ResourceFolderModel) ecm_add_test(ResourcePackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME ResourcePackParse) ecm_add_test(TexturePackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME TexturePackParse) ecm_add_test(DataPackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME DataPackParse) ecm_add_test(ShaderPackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME ShaderPackParse) ecm_add_test(WorldSaveParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME WorldSaveParse) ecm_add_test(ParseUtils_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME ParseUtils) ecm_add_test(Task_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME Task) ecm_add_test(INIFile_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME INIFile) ecm_add_test(JavaVersion_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME JavaVersion) ecm_add_test(Packwiz_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME Packwiz) ecm_add_test(Index_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME Index) ecm_add_test(Version_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME Version) ecm_add_test(MetaComponentParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME MetaComponentParse) ecm_add_test(CatPack_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME CatPack) ecm_add_test(XmlLogs_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME XmlLogs) PrismLauncher-11.0.3/tests/ResourceFolderModel_test.cpp0000644000175100017510000002137515224505336022604 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "BaseInstance.h" #include #include #include #define EXEC_UPDATE_TASK(EXEC, VERIFY) \ QEventLoop loop; \ \ connect(&model, &ResourceFolderModel::updateFinished, &loop, &QEventLoop::quit); \ \ QTimer expire_timer; \ expire_timer.callOnTimeout(&loop, &QEventLoop::quit); \ expire_timer.setSingleShot(true); \ expire_timer.start(4000); \ \ VERIFY(EXEC); \ loop.exec(); \ \ QVERIFY2(expire_timer.isActive(), "Timer has expired. The update never finished."); \ expire_timer.stop(); \ \ disconnect(&model, nullptr, &loop, nullptr); class ResourceFolderModelTest : public QObject { Q_OBJECT private slots: // test for GH-1178 - install a folder with files to a mod list void test_1178() { // source QString source = QFINDTESTDATA("testdata/Resources/test_folder"); // sanity check QVERIFY(!source.endsWith('/')); auto verify = [](QString path) { QDir target_dir(FS::PathCombine(path, "test_folder")); QVERIFY(target_dir.entryList().contains("pack.mcmeta")); QVERIFY(target_dir.entryList().contains("assets")); }; // 1. test with no trailing / { QString folder = source; QTemporaryDir tempDir; QEventLoop loop; ModFolderModel m(tempDir.path(), nullptr, true, true); connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit); QTimer expire_timer; expire_timer.callOnTimeout(&loop, &QEventLoop::quit); expire_timer.setSingleShot(true); expire_timer.start(4000); m.installResource(folder); loop.exec(); QVERIFY2(expire_timer.isActive(), "Timer has expired. The update never finished."); expire_timer.stop(); verify(tempDir.path()); } // 2. test with trailing / { QString folder = source + '/'; QTemporaryDir tempDir; QEventLoop loop; ModFolderModel m(tempDir.path(), nullptr, true, true); connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit); QTimer expire_timer; expire_timer.callOnTimeout(&loop, &QEventLoop::quit); expire_timer.setSingleShot(true); expire_timer.start(4000); m.installResource(folder); loop.exec(); QVERIFY2(expire_timer.isActive(), "Timer has expired. The update never finished."); expire_timer.stop(); verify(tempDir.path()); } } void test_addFromWatch() { QString source = QFINDTESTDATA("testdata/Resources"); ModFolderModel model(source, nullptr, false, true); QCOMPARE(model.size(), 0); EXEC_UPDATE_TASK(model.startWatching(), ) for (auto mod : model.allMods()) qDebug() << mod->name(); QCOMPARE(model.size(), 4); model.stopWatching(); } void test_removeResource() { QString folder_resource = QFINDTESTDATA("testdata/Resources/test_folder"); QString file_mod = QFINDTESTDATA("testdata/Resources/supercoolmod.jar"); QTemporaryDir tmp; ResourceFolderModel model(QDir(tmp.path()), nullptr, false, false); QCOMPARE(model.size(), 0); { EXEC_UPDATE_TASK(model.installResource(file_mod), QVERIFY) } QCOMPARE(model.size(), 1); qDebug() << "Added first mod."; { EXEC_UPDATE_TASK(model.startWatching(), ) } QCOMPARE(model.size(), 1); qDebug() << "Started watching the temp folder."; { EXEC_UPDATE_TASK(model.installResource(folder_resource), QVERIFY) } QCOMPARE(model.size(), 2); qDebug() << "Added second mod."; { EXEC_UPDATE_TASK(model.uninstallResource("supercoolmod.jar"), QVERIFY); } QCOMPARE(model.size(), 1); qDebug() << "Removed first mod."; QString mod_file_name{ model.at(0).fileinfo().fileName() }; QVERIFY(!mod_file_name.isEmpty()); { EXEC_UPDATE_TASK(model.uninstallResource(mod_file_name), QVERIFY); } QCOMPARE(model.size(), 0); qDebug() << "Removed second mod."; model.stopWatching(); } void test_enable_disable() { QString folder_resource = QFINDTESTDATA("testdata/Resources/test_folder"); QString file_mod = QFINDTESTDATA("testdata/Resources/supercoolmod.jar"); QTemporaryDir tmp; ResourceFolderModel model(tmp.path(), nullptr, false, false); QCOMPARE(model.size(), 0); { EXEC_UPDATE_TASK(model.installResource(folder_resource), QVERIFY) } { EXEC_UPDATE_TASK(model.installResource(file_mod), QVERIFY) } for (auto res : model.allResources()) qDebug() << res->name(); QCOMPARE(model.size(), 2); auto& res_1 = model.at(0).type() != ResourceType::FOLDER ? model.at(0) : model.at(1); auto& res_2 = model.at(0).type() == ResourceType::FOLDER ? model.at(0) : model.at(1); auto id_1 = res_1.internal_id(); auto id_2 = res_2.internal_id(); bool initial_enabled_res_2 = res_2.enabled(); bool initial_enabled_res_1 = res_1.enabled(); QVERIFY(res_1.type() != ResourceType::FOLDER && res_1.type() != ResourceType::UNKNOWN); qDebug() << "res_1 is of the correct type."; QVERIFY(res_1.enabled()); qDebug() << "res_1 is initially enabled."; QVERIFY(res_1.enable(EnableAction::TOGGLE)); QVERIFY(res_1.enabled() == !initial_enabled_res_1); qDebug() << "res_1 got successfully toggled."; QVERIFY(res_1.enable(EnableAction::TOGGLE)); qDebug() << "res_1 got successfully toggled again."; QVERIFY(res_1.enabled() == initial_enabled_res_1); QVERIFY(res_1.internal_id() == id_1); qDebug() << "res_1 got back to its initial state."; QVERIFY(!res_2.enable(initial_enabled_res_2 ? EnableAction::ENABLE : EnableAction::DISABLE)); QVERIFY(res_2.enabled() == initial_enabled_res_2); QVERIFY(res_2.internal_id() == id_2); } }; QTEST_GUILESS_MAIN(ResourceFolderModelTest) #include "ResourceFolderModel_test.moc" PrismLauncher-11.0.3/tests/XmlLogs_test.cpp0000644000175100017510000001344115224505336020260 0ustar runnerrunner// SPDX-FileCopyrightText: 2025 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2025 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include class XmlLogParseTest : public QObject { Q_OBJECT private slots: void parseXml_data() { QString source = QFINDTESTDATA("testdata/TestLogs"); QString shortXml = QString::fromUtf8(FS::read(FS::PathCombine(source, "vanilla-1.21.5.xml.log"))); QString shortText = QString::fromUtf8(FS::read(FS::PathCombine(source, "vanilla-1.21.5.text.log"))); QStringList shortTextLevels_s = QString::fromUtf8(FS::read(FS::PathCombine(source, "vanilla-1.21.5-levels.txt"))) .split(QRegularExpression("\n|\r\n|\r"), Qt::SkipEmptyParts); QList shortTextLevels; shortTextLevels.reserve(24); std::transform(shortTextLevels_s.cbegin(), shortTextLevels_s.cend(), std::back_inserter(shortTextLevels), [](const QString& line) { return MessageLevel::fromName(line.trimmed()); }); QString longXml = QString::fromUtf8(FS::read(FS::PathCombine(source, "TerraFirmaGreg-Modern-forge.xml.log"))); QString longText = QString::fromUtf8(FS::read(FS::PathCombine(source, "TerraFirmaGreg-Modern-forge.text.log"))); QStringList longTextLevels_s = QString::fromUtf8(FS::read(FS::PathCombine(source, "TerraFirmaGreg-Modern-levels.txt"))) .split(QRegularExpression("\n|\r\n|\r"), Qt::SkipEmptyParts); QStringList longTextLevelsXml_s = QString::fromUtf8(FS::read(FS::PathCombine(source, "TerraFirmaGreg-Modern-xml-levels.txt"))) .split(QRegularExpression("\n|\r\n|\r"), Qt::SkipEmptyParts); QList longTextLevelsPlain; longTextLevelsPlain.reserve(974); std::transform(longTextLevels_s.cbegin(), longTextLevels_s.cend(), std::back_inserter(longTextLevelsPlain), [](const QString& line) { return MessageLevel::fromName(line.trimmed()); }); QList longTextLevelsXml; longTextLevelsXml.reserve(896); std::transform(longTextLevelsXml_s.cbegin(), longTextLevelsXml_s.cend(), std::back_inserter(longTextLevelsXml), [](const QString& line) { return MessageLevel::fromName(line.trimmed()); }); QTest::addColumn("log"); QTest::addColumn("num_entries"); QTest::addColumn>("entry_levels"); QTest::newRow("short-vanilla-plain") << shortText << 25 << shortTextLevels; QTest::newRow("short-vanilla-xml") << shortXml << 25 << shortTextLevels; QTest::newRow("long-forge-plain") << longText << 945 << longTextLevelsPlain; QTest::newRow("long-forge-xml") << longXml << 869 << longTextLevelsXml; } void parseXml() { QFETCH(QString, log); QFETCH(int, num_entries); QFETCH(QList, entry_levels); QList> entries = {}; QBENCHMARK { entries = parseLines(log.split(QRegularExpression("\n|\r\n|\r"))); } QCOMPARE(entries.length(), num_entries); QList levels = {}; std::transform(entries.cbegin(), entries.cend(), std::back_inserter(levels), [](std::pair entry) { return entry.first; }); QCOMPARE(levels, entry_levels); } private: LogParser m_parser; QList> parseLines(const QStringList& lines) { QList> out; MessageLevel last = MessageLevel::Unknown; for (const auto& line : lines) { m_parser.appendLine(line); auto items = m_parser.parseAvailable(); for (const auto& item : items) { if (std::holds_alternative(item)) { auto entry = std::get(item); auto msg = QString("[%1] [%2/%3] [%4]: %5") .arg(entry.timestamp.toString("HH:mm:ss")) .arg(entry.thread) .arg(entry.levelText) .arg(entry.logger) .arg(entry.message); out.append(std::make_pair(entry.level, msg)); last = entry.level; } else if (std::holds_alternative(item)) { auto msg = std::get(item).message; auto level = LogParser::guessLevel(msg, last); out.append(std::make_pair(level, msg)); last = level; } } } return out; } }; QTEST_GUILESS_MAIN(XmlLogParseTest) #include "XmlLogs_test.moc" PrismLauncher-11.0.3/tests/testdata/0000755000175100017510000000000015224505336016736 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/Libraries/0000755000175100017510000000000015224505336020652 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/Libraries/lib-simple.json0000644000175100017510000000057615224505336023612 0ustar runnerrunner{ "downloads": { "artifact": { "path": "com/paulscode/codecwav/20101023/codecwav-20101023.jar", "sha1": "12f031cfe88fef5c1dd36c563c0a3a69bd7261da", "size": 5618, "url": "https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar" } }, "name": "com.paulscode:codecwav:20101023" } PrismLauncher-11.0.3/tests/testdata/Libraries/lib-native-arch.json0000644000175100017510000000316015224505336024512 0ustar runnerrunner{ "downloads": { "classifiers": { "natives-osx": { "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-osx.jar", "sha1": "62503ee712766cf77f97252e5902786fd834b8c5", "size": 418331, "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-osx.jar" }, "natives-windows-32": { "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar", "sha1": "7c6affe439099806a4f552da14c42f9d643d8b23", "size": 386792, "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar" }, "natives-windows-64": { "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar", "sha1": "39d0c3d363735b4785598e0e7fbf8297c706a9f9", "size": 463390, "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar" } } }, "extract": { "exclude": [ "META-INF/" ] }, "name": "tv.twitch:twitch-platform:5.16", "natives": { "linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows-${arch}" }, "rules": [ { "action": "allow" }, { "action": "disallow", "os": { "name": "linux" } } ] } PrismLauncher-11.0.3/tests/testdata/Libraries/codecwav-20101023.jar0000644000175100017510000000002115224505336024022 0ustar runnerrunnerdummy test file. PrismLauncher-11.0.3/tests/testdata/Libraries/lib-native.json0000644000175100017510000000444615224505336023607 0ustar runnerrunner{ "downloads": { "artifact": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar", "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33", "size": 22, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar" }, "classifiers": { "natives-linux": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar", "sha1": "931074f46c795d2f7b30ed6395df5715cfd7675b", "size": 578680, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar" }, "natives-osx": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar", "sha1": "bcab850f8f487c3f4c4dbabde778bb82bd1a40ed", "size": 426822, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar" }, "natives-windows": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar", "sha1": "b84d5102b9dbfabfeb5e43c7e2828d98a7fc80e0", "size": 613748, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar" } } }, "extract": { "exclude": [ "META-INF/" ] }, "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209", "natives": { "linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows" }, "rules": [ { "action": "allow" }, { "action": "disallow", "os": { "name": "osx" } } ] } PrismLauncher-11.0.3/tests/testdata/Libraries/1.9.json0000644000175100017510000005674115224505336022071 0ustar runnerrunner{ "assetIndex": { "id": "1.9", "sha1": "cde65b47a43f638653ab1da3848b53f8a7477b16", "size": 136916, "totalSize": 119917473, "url": "https://launchermeta.mojang.com/mc-staging/assets/1.9/cde65b47a43f638653ab1da3848b53f8a7477b16/1.9.json" }, "assets": "1.9", "downloads": { "client": { "sha1": "2f67dfe8953299440d1902f9124f0f2c3a2c940f", "size": 8697592, "url": "https://launcher.mojang.com/mc/game/1.9/client/2f67dfe8953299440d1902f9124f0f2c3a2c940f/client.jar" }, "server": { "sha1": "b4d449cf2918e0f3bd8aa18954b916a4d1880f0d", "size": 8848015, "url": "https://launcher.mojang.com/mc/game/1.9/server/b4d449cf2918e0f3bd8aa18954b916a4d1880f0d/server.jar" } }, "id": "1.9", "libraries": [ { "downloads": { "artifact": { "path": "oshi-project/oshi-core/1.1/oshi-core-1.1.jar", "sha1": "9ddf7b048a8d701be231c0f4f95fd986198fd2d8", "size": 30973, "url": "https://libraries.minecraft.net/oshi-project/oshi-core/1.1/oshi-core-1.1.jar" } }, "name": "oshi-project:oshi-core:1.1" }, { "downloads": { "artifact": { "path": "net/java/dev/jna/jna/3.4.0/jna-3.4.0.jar", "sha1": "803ff252fedbd395baffd43b37341dc4a150a554", "size": 1008730, "url": "https://libraries.minecraft.net/net/java/dev/jna/jna/3.4.0/jna-3.4.0.jar" } }, "name": "net.java.dev.jna:jna:3.4.0" }, { "downloads": { "artifact": { "path": "net/java/dev/jna/platform/3.4.0/platform-3.4.0.jar", "sha1": "e3f70017be8100d3d6923f50b3d2ee17714e9c13", "size": 913436, "url": "https://libraries.minecraft.net/net/java/dev/jna/platform/3.4.0/platform-3.4.0.jar" } }, "name": "net.java.dev.jna:platform:3.4.0" }, { "downloads": { "artifact": { "path": "com/ibm/icu/icu4j-core-mojang/51.2/icu4j-core-mojang-51.2.jar", "sha1": "63d216a9311cca6be337c1e458e587f99d382b84", "size": 1634692, "url": "https://libraries.minecraft.net/com/ibm/icu/icu4j-core-mojang/51.2/icu4j-core-mojang-51.2.jar" } }, "name": "com.ibm.icu:icu4j-core-mojang:51.2" }, { "downloads": { "artifact": { "path": "net/sf/jopt-simple/jopt-simple/4.6/jopt-simple-4.6.jar", "sha1": "306816fb57cf94f108a43c95731b08934dcae15c", "size": 62477, "url": "https://libraries.minecraft.net/net/sf/jopt-simple/jopt-simple/4.6/jopt-simple-4.6.jar" } }, "name": "net.sf.jopt-simple:jopt-simple:4.6" }, { "downloads": { "artifact": { "path": "com/paulscode/codecjorbis/20101023/codecjorbis-20101023.jar", "sha1": "c73b5636faf089d9f00e8732a829577de25237ee", "size": 103871, "url": "https://libraries.minecraft.net/com/paulscode/codecjorbis/20101023/codecjorbis-20101023.jar" } }, "name": "com.paulscode:codecjorbis:20101023" }, { "downloads": { "artifact": { "path": "com/paulscode/codecwav/20101023/codecwav-20101023.jar", "sha1": "12f031cfe88fef5c1dd36c563c0a3a69bd7261da", "size": 5618, "url": "https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar" } }, "name": "com.paulscode:codecwav:20101023" }, { "downloads": { "artifact": { "path": "com/paulscode/libraryjavasound/20101123/libraryjavasound-20101123.jar", "sha1": "5c5e304366f75f9eaa2e8cca546a1fb6109348b3", "size": 21679, "url": "https://libraries.minecraft.net/com/paulscode/libraryjavasound/20101123/libraryjavasound-20101123.jar" } }, "name": "com.paulscode:libraryjavasound:20101123" }, { "downloads": { "artifact": { "path": "com/paulscode/librarylwjglopenal/20100824/librarylwjglopenal-20100824.jar", "sha1": "73e80d0794c39665aec3f62eee88ca91676674ef", "size": 18981, "url": "https://libraries.minecraft.net/com/paulscode/librarylwjglopenal/20100824/librarylwjglopenal-20100824.jar" } }, "name": "com.paulscode:librarylwjglopenal:20100824" }, { "downloads": { "artifact": { "path": "com/paulscode/soundsystem/20120107/soundsystem-20120107.jar", "sha1": "419c05fe9be71f792b2d76cfc9b67f1ed0fec7f6", "size": 65020, "url": "https://libraries.minecraft.net/com/paulscode/soundsystem/20120107/soundsystem-20120107.jar" } }, "name": "com.paulscode:soundsystem:20120107" }, { "downloads": { "artifact": { "path": "io/netty/netty-all/4.0.23.Final/netty-all-4.0.23.Final.jar", "sha1": "0294104aaf1781d6a56a07d561e792c5d0c95f45", "size": 1779991, "url": "https://libraries.minecraft.net/io/netty/netty-all/4.0.23.Final/netty-all-4.0.23.Final.jar" } }, "name": "io.netty:netty-all:4.0.23.Final" }, { "downloads": { "artifact": { "path": "com/google/guava/guava/17.0/guava-17.0.jar", "sha1": "9c6ef172e8de35fd8d4d8783e4821e57cdef7445", "size": 2243036, "url": "https://libraries.minecraft.net/com/google/guava/guava/17.0/guava-17.0.jar" } }, "name": "com.google.guava:guava:17.0" }, { "downloads": { "artifact": { "path": "org/apache/commons/commons-lang3/3.3.2/commons-lang3-3.3.2.jar", "sha1": "90a3822c38ec8c996e84c16a3477ef632cbc87a3", "size": 412739, "url": "https://libraries.minecraft.net/org/apache/commons/commons-lang3/3.3.2/commons-lang3-3.3.2.jar" } }, "name": "org.apache.commons:commons-lang3:3.3.2" }, { "downloads": { "artifact": { "path": "commons-io/commons-io/2.4/commons-io-2.4.jar", "sha1": "b1b6ea3b7e4aa4f492509a4952029cd8e48019ad", "size": 185140, "url": "https://libraries.minecraft.net/commons-io/commons-io/2.4/commons-io-2.4.jar" } }, "name": "commons-io:commons-io:2.4" }, { "downloads": { "artifact": { "path": "commons-codec/commons-codec/1.9/commons-codec-1.9.jar", "sha1": "9ce04e34240f674bc72680f8b843b1457383161a", "size": 263965, "url": "https://libraries.minecraft.net/commons-codec/commons-codec/1.9/commons-codec-1.9.jar" } }, "name": "commons-codec:commons-codec:1.9" }, { "downloads": { "artifact": { "path": "net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar", "sha1": "39c7796b469a600f72380316f6b1f11db6c2c7c4", "size": 208338, "url": "https://libraries.minecraft.net/net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar" } }, "name": "net.java.jinput:jinput:2.0.5" }, { "downloads": { "artifact": { "path": "net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar", "sha1": "e12fe1fda814bd348c1579329c86943d2cd3c6a6", "size": 7508, "url": "https://libraries.minecraft.net/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar" } }, "name": "net.java.jutils:jutils:1.0.0" }, { "downloads": { "artifact": { "path": "com/google/code/gson/gson/2.2.4/gson-2.2.4.jar", "sha1": "a60a5e993c98c864010053cb901b7eab25306568", "size": 190432, "url": "https://libraries.minecraft.net/com/google/code/gson/gson/2.2.4/gson-2.2.4.jar" } }, "name": "com.google.code.gson:gson:2.2.4" }, { "downloads": { "artifact": { "path": "com/mojang/authlib/1.5.22/authlib-1.5.22.jar", "sha1": "afaa8f6df976fcb5520e76ef1d5798c9e6b5c0b2", "size": 64539, "url": "https://libraries.minecraft.net/com/mojang/authlib/1.5.22/authlib-1.5.22.jar" } }, "name": "com.mojang:authlib:1.5.22" }, { "downloads": { "artifact": { "path": "com/mojang/realms/1.8.4/realms-1.8.4.jar", "sha1": "15f8dc326c97a96dee6e65392e145ad6d1cb46cb", "size": 1131574, "url": "https://libraries.minecraft.net/com/mojang/realms/1.8.4/realms-1.8.4.jar" } }, "name": "com.mojang:realms:1.8.4" }, { "downloads": { "artifact": { "path": "org/apache/commons/commons-compress/1.8.1/commons-compress-1.8.1.jar", "sha1": "a698750c16740fd5b3871425f4cb3bbaa87f529d", "size": 365552, "url": "https://libraries.minecraft.net/org/apache/commons/commons-compress/1.8.1/commons-compress-1.8.1.jar" } }, "name": "org.apache.commons:commons-compress:1.8.1" }, { "downloads": { "artifact": { "path": "org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar", "sha1": "18f4247ff4572a074444572cee34647c43e7c9c7", "size": 589512, "url": "https://libraries.minecraft.net/org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar" } }, "name": "org.apache.httpcomponents:httpclient:4.3.3" }, { "downloads": { "artifact": { "path": "commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar", "sha1": "f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f", "size": 62050, "url": "https://libraries.minecraft.net/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar" } }, "name": "commons-logging:commons-logging:1.1.3" }, { "downloads": { "artifact": { "path": "org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar", "sha1": "31fbbff1ddbf98f3aa7377c94d33b0447c646b6e", "size": 282269, "url": "https://libraries.minecraft.net/org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar" } }, "name": "org.apache.httpcomponents:httpcore:4.3.2" }, { "downloads": { "artifact": { "path": "org/apache/logging/log4j/log4j-api/2.0-beta9/log4j-api-2.0-beta9.jar", "sha1": "1dd66e68cccd907880229f9e2de1314bd13ff785", "size": 108161, "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-api/2.0-beta9/log4j-api-2.0-beta9.jar" } }, "name": "org.apache.logging.log4j:log4j-api:2.0-beta9" }, { "downloads": { "artifact": { "path": "org/apache/logging/log4j/log4j-core/2.0-beta9/log4j-core-2.0-beta9.jar", "sha1": "678861ba1b2e1fccb594bb0ca03114bb05da9695", "size": 681134, "url": "https://libraries.minecraft.net/org/apache/logging/log4j/log4j-core/2.0-beta9/log4j-core-2.0-beta9.jar" } }, "name": "org.apache.logging.log4j:log4j-core:2.0-beta9" }, { "downloads": { "artifact": { "path": "org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar", "sha1": "697517568c68e78ae0b4544145af031c81082dfe", "size": 1047168, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar" } }, "name": "org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209", "rules": [ { "action": "allow" }, { "action": "disallow", "os": { "name": "osx" } } ] }, { "downloads": { "artifact": { "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar", "sha1": "d51a7c040a721d13efdfbd34f8b257b2df882ad0", "size": 173887, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar" } }, "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209", "rules": [ { "action": "allow" }, { "action": "disallow", "os": { "name": "osx" } } ] }, { "downloads": { "artifact": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar", "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33", "size": 22, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar" }, "classifiers": { "natives-linux": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar", "sha1": "931074f46c795d2f7b30ed6395df5715cfd7675b", "size": 578680, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar" }, "natives-osx": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar", "sha1": "bcab850f8f487c3f4c4dbabde778bb82bd1a40ed", "size": 426822, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar" }, "natives-windows": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar", "sha1": "b84d5102b9dbfabfeb5e43c7e2828d98a7fc80e0", "size": 613748, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar" } } }, "extract": { "exclude": [ "META-INF/" ] }, "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209", "natives": { "linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows" }, "rules": [ { "action": "allow" }, { "action": "disallow", "os": { "name": "osx" } } ] }, { "downloads": { "artifact": { "path": "org/lwjgl/lwjgl/lwjgl/2.9.2-nightly-20140822/lwjgl-2.9.2-nightly-20140822.jar", "sha1": "7707204c9ffa5d91662de95f0a224e2f721b22af", "size": 1045632, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.2-nightly-20140822/lwjgl-2.9.2-nightly-20140822.jar" } }, "name": "org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-20140822", "rules": [ { "action": "allow", "os": { "name": "osx" } } ] }, { "downloads": { "artifact": { "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.2-nightly-20140822/lwjgl_util-2.9.2-nightly-20140822.jar", "sha1": "f0e612c840a7639c1f77f68d72a28dae2f0c8490", "size": 173887, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.2-nightly-20140822/lwjgl_util-2.9.2-nightly-20140822.jar" } }, "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-20140822", "rules": [ { "action": "allow", "os": { "name": "osx" } } ] }, { "downloads": { "classifiers": { "natives-linux": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar", "sha1": "d898a33b5d0a6ef3fed3a4ead506566dce6720a5", "size": 578539, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-linux.jar" }, "natives-osx": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar", "sha1": "79f5ce2fea02e77fe47a3c745219167a542121d7", "size": 468116, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-osx.jar" }, "natives-windows": { "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar", "sha1": "78b2a55ce4dc29c6b3ec4df8ca165eba05f9b341", "size": 613680, "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.2-nightly-20140822/lwjgl-platform-2.9.2-nightly-20140822-natives-windows.jar" } } }, "extract": { "exclude": [ "META-INF/" ] }, "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.2-nightly-20140822", "natives": { "linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows" }, "rules": [ { "action": "allow", "os": { "name": "osx" } } ] }, { "downloads": { "classifiers": { "natives-linux": { "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar", "sha1": "7ff832a6eb9ab6a767f1ade2b548092d0fa64795", "size": 10362, "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar" }, "natives-osx": { "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar", "sha1": "53f9c919f34d2ca9de8c51fc4e1e8282029a9232", "size": 12186, "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar" }, "natives-windows": { "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar", "sha1": "385ee093e01f587f30ee1c8a2ee7d408fd732e16", "size": 155179, "url": "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-windows.jar" } } }, "extract": { "exclude": [ "META-INF/" ] }, "name": "net.java.jinput:jinput-platform:2.0.5", "natives": { "linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows" } } ], "mainClass": "net.minecraft.client.main.Main", "minecraftArguments": "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}", "minimumLauncherVersion": 18, "releaseTime": "2016-02-29T13:49:54+00:00", "time": "2016-03-01T13:14:53+00:00", "type": "release" } PrismLauncher-11.0.3/tests/testdata/Libraries/1.9-simple.json0000644000175100017510000001256615224505336023355 0ustar runnerrunner{ "assets": "1.9", "id": "1.9", "libraries": [ { "name": "oshi-project:oshi-core:1.1" }, { "name": "net.java.dev.jna:jna:3.4.0" }, { "name": "net.java.dev.jna:platform:3.4.0" }, { "name": "com.ibm.icu:icu4j-core-mojang:51.2" }, { "name": "net.sf.jopt-simple:jopt-simple:4.6" }, { "name": "com.paulscode:codecjorbis:20101023" }, { "name": "com.paulscode:codecwav:20101023" }, { "name": "com.paulscode:libraryjavasound:20101123" }, { "name": "com.paulscode:librarylwjglopenal:20100824" }, { "name": "com.paulscode:soundsystem:20120107" }, { "name": "io.netty:netty-all:4.0.23.Final" }, { "name": "com.google.guava:guava:17.0" }, { "name": "org.apache.commons:commons-lang3:3.3.2" }, { "name": "commons-io:commons-io:2.4" }, { "name": "commons-codec:commons-codec:1.9" }, { "name": "net.java.jinput:jinput:2.0.5" }, { "name": "net.java.jutils:jutils:1.0.0" }, { "name": "com.google.code.gson:gson:2.2.4" }, { "name": "com.mojang:authlib:1.5.22" }, { "name": "com.mojang:realms:1.8.4" }, { "name": "org.apache.commons:commons-compress:1.8.1" }, { "name": "org.apache.httpcomponents:httpclient:4.3.3" }, { "name": "commons-logging:commons-logging:1.1.3" }, { "name": "org.apache.httpcomponents:httpcore:4.3.2" }, { "name": "org.apache.logging.log4j:log4j-api:2.0-beta9" }, { "name": "org.apache.logging.log4j:log4j-core:2.0-beta9" }, { "name": "org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209", "rules": [ { "action": "allow" }, { "action": "disallow", "os": { "name": "osx" } } ] }, { "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209", "rules": [ { "action": "allow" }, { "action": "disallow", "os": { "name": "osx" } } ] }, { "extract": { "exclude": [ "META-INF/" ] }, "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209", "natives": { "linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows" }, "rules": [ { "action": "allow" }, { "action": "disallow", "os": { "name": "osx" } } ] }, { "name": "org.lwjgl.lwjgl:lwjgl:2.9.2-nightly-20140822", "rules": [ { "action": "allow", "os": { "name": "osx" } } ] }, { "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.2-nightly-20140822", "rules": [ { "action": "allow", "os": { "name": "osx" } } ] }, { "extract": { "exclude": [ "META-INF/" ] }, "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.2-nightly-20140822", "natives": { "linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows" }, "rules": [ { "action": "allow", "os": { "name": "osx" } } ] }, { "extract": { "exclude": [ "META-INF/" ] }, "name": "net.java.jinput:jinput-platform:2.0.5", "natives": { "linux": "natives-linux", "osx": "natives-osx", "windows": "natives-windows" } } ], "mainClass": "net.minecraft.client.main.Main", "minecraftArguments": "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userType ${user_type} --versionType ${version_type}", "minimumLauncherVersion": 18, "releaseTime": "2016-02-29T13:49:54+00:00", "time": "2016-03-01T13:14:53+00:00", "type": "release" } PrismLauncher-11.0.3/tests/testdata/Libraries/testname-testversion-linux-32.jar0000644000175100017510000000002115224505336027123 0ustar runnerrunnerdummy test file. PrismLauncher-11.0.3/tests/testdata/Packwiz/0000755000175100017510000000000015224505336020346 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/Packwiz/screenshot-to-clipboard-fabric.pw.toml0000644000175100017510000000050315224505336027644 0ustar runnerrunnername = "Screenshot to Clipboard (Fabric)" filename = "screenshot-to-clipboard-1.0.7-fabric.jar" side = "both" [download] url = "https://edge.forgecdn.net/files/3509/43/screenshot-to-clipboard-1.0.7-fabric.jar" hash-format = "murmur2" hash = "1781245820" [update] [update.curseforge] file-id = 3509043 project-id = 327154 PrismLauncher-11.0.3/tests/testdata/Packwiz/borderless-mining.pw.toml0000644000175100017510000000065715224505336025323 0ustar runnerrunnername = "Borderless Mining" filename = "borderless-mining-1.1.1+1.18.jar" side = "client" [download] url = "https://cdn.modrinth.com/data/kYq5qkSL/versions/1.1.1+1.18/borderless-mining-1.1.1+1.18.jar" hash-format = "sha512" hash = "c8fe6e15ddea32668822dddb26e1851e5f03834be4bcb2eff9c0da7fdc086a9b6cead78e31a44d3bc66335cba11144ee0337c6d5346f1ba63623064499b3188d" [update] [update.modrinth] mod-id = "kYq5qkSL" version = "ug2qKTPR" PrismLauncher-11.0.3/tests/testdata/TestLogs/0000755000175100017510000000000015224505336020502 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/TestLogs/TerraFirmaGreg-Modern-levels.txt0000644000175100017510000001144315224505336026621 0ustar runnerrunnerUNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN INFO INFO INFO INFO INFO INFO INFO WARN WARN WARN WARN WARN INFO ERROR INFO ERROR ERROR ERROR INFO INFO INFO WARN INFO INFO INFO INFO WARN INFO INFO INFO WARN WARN INFO WARN WARN WARN WARN WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN WARN INFO INFO WARN WARN WARN INFO WARN INFO INFO WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN INFO INFO WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN INFO INFO WARN INFO WARN WARN INFO INFO INFO INFO INFO WARN INFO INFO INFO WARN INFO WARN WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO UNKNOWN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO WARN INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN WARN INFO UNKNOWN UNKNOWN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN WARN INFO INFO WARN WARN WARN INFO INFO INFO INFO INFO INFO UNKNOWN INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR INFO ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO WARN ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR INFO INFO INFO WARN ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR WARN ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO ERROR INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN WARN WARN WARN WARN WARN WARN WARN UNKNOWN INFO UNKNOWN INFO ERROR INFO INFO INFO INFO UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO UNKNOWN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO WARN WARN WARN WARN WARN WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO ERROR ERROR ERROR ERROR INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN PrismLauncher-11.0.3/tests/testdata/TestLogs/TerraFirmaGreg-Modern-xml-levels.txt0000644000175100017510000001045315224505336027417 0ustar runnerrunnerUNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN UNKNOWN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN WARN WARN WARN INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO ERROR INFO ERROR ERROR ERROR INFO INFO INFO WARN INFO INFO INFO INFO WARN INFO INFO INFO WARN WARN INFO WARN WARN WARN WARN WARN INFO WARN WARN INFO INFO WARN WARN WARN INFO WARN INFO INFO WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN INFO INFO WARN WARN WARN WARN WARN WARN WARN WARN WARN WARN INFO INFO WARN INFO WARN WARN INFO INFO INFO INFO INFO WARN INFO INFO INFO WARN INFO WARN WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO WARN INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO WARN WARN INFO INFO INFO INFO UNKNOWN UNKNOWN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN WARN INFO INFO WARN WARN INFO INFO INFO INFO INFO INFO UNKNOWN INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO ERROR INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO WARN WARN INFO INFO INFO INFO ERROR INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN WARN WARN WARN UNKNOWN INFO UNKNOWN INFO ERROR INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO UNKNOWN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO WARN WARN WARN WARN WARN WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO ERROR ERROR ERROR ERROR INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO PrismLauncher-11.0.3/tests/testdata/TestLogs/vanilla-1.21.5-levels.txt0000644000175100017510000000017515224505336024706 0ustar runnerrunnerINFO INFO INFO INFO INFO INFO INFO INFO WARN INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO PrismLauncher-11.0.3/tests/testdata/TestLogs/vanilla-1.21.5.xml.log0000644000175100017510000001273415224505336024163 0ustar runnerrunner PrismLauncher-11.0.3/tests/testdata/TestLogs/TerraFirmaGreg-Modern-forge.xml.log0000644000175100017510000077213615224505336027207 0ustar runnerrunnerChecking: MC_SLIM Checking: MERGED_MAPPINGS Checking: MAPPINGS Checking: MC_EXTRA Checking: MOJMAPS Checking: PATCHED Checking: MC_SRG , --accessToken, â�„â�„â�„â�„â�„â�„â�„â�„, --userType, msa, --versionType, release, --launchTarget, forgeclient, --fml.forgeVersion, 47.2.6, --fml.mcVersion, 1.20.1, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20230612.114412, --width, 854, --height, 480]]]> , --username, Ryexandrite, --assetIndex, 5, --accessToken, â�„â�„â�„â�„â�„â�„â�„â�„, --userType, msa, --versionType, release, --width, 854, --height, 480]]]> Outdated (Target version: "v2.5.4")]]> [Mouse Tweaks] Main.initialize() [Mouse Tweaks] Initialized. Outdated (Target version: "v1.0.5")]]> [ALSOFT] (EE) Failed to set real-time priority for thread: Operation not permitted (1) (ModelBakery.java:150) at TRANSFORMER/minecraft@1.20.1/net.minecraft.client.resources.model.ModelManager.m_246505_(ModelManager.java:83) at java.base/java.util.concurrent.CompletableFuture.biApply(CompletableFuture.java:1311) at java.base/java.util.concurrent.CompletableFuture$BiApply.tryFire(CompletableFuture.java:1280) at java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:483) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ]]> (ModelBakery.java:159) at TRANSFORMER/minecraft@1.20.1/net.minecraft.client.resources.model.ModelManager.m_246505_(ModelManager.java:83) at java.base/java.util.concurrent.CompletableFuture.biApply(CompletableFuture.java:1311) at java.base/java.util.concurrent.CompletableFuture$BiApply.tryFire(CompletableFuture.java:1280) at java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:483) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ]]> Reloading Dynamic Lights Create Crafts & Additions Initialized! [ALSOFT] (EE) Failed to set real-time priority for thread: Operation not permitted (1) PrismLauncher-11.0.3/tests/testdata/TestLogs/TerraFirmaGreg-Modern-forge.text.log0000644000175100017510000031134215224505336027357 0ustar runnerrunnerChecking: MC_SLIM Checking: MERGED_MAPPINGS Checking: MAPPINGS Checking: MC_EXTRA Checking: MOJMAPS Checking: PATCHED Checking: MC_SRG 2025-04-18 12:47:23,932 main WARN Advanced terminal features are not available in this environment [12:47:24] [main/INFO] [cp.mo.mo.Launcher/MODLAUNCHER]: ModLauncher running: args [--username, Ryexandrite, --version, 1.20.1, --gameDir, /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/instances/TerraFirmaGreg-Modern/minecraft, --assetsDir, /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/assets, --assetIndex, 5, --uuid, , --accessToken, â�„â�„â�„â�„â�„â�„â�„â�„, --userType, msa, --versionType, release, --launchTarget, forgeclient, --fml.forgeVersion, 47.2.6, --fml.mcVersion, 1.20.1, --fml.forgeGroup, net.minecraftforge, --fml.mcpVersion, 20230612.114412, --width, 854, --height, 480] [12:47:24] [main/INFO] [cp.mo.mo.Launcher/MODLAUNCHER]: ModLauncher 10.0.9+10.0.9+main.dcd20f30 starting: java version 17.0.8 by Microsoft; OS Linux arch amd64 version 6.6.85 [12:47:24] [main/INFO] [ne.mi.fm.lo.ImmediateWindowHandler/]: Loading ImmediateWindowProvider fmlearlywindow [12:47:24] [main/INFO] [EARLYDISPLAY/]: Trying GL version 4.6 [12:47:24] [main/INFO] [EARLYDISPLAY/]: Requested GL version 4.6 got version 4.6 [12:47:24] [main/INFO] [mixin/]: SpongePowered MIXIN Subsystem Version=0.8.5 Source=union:/home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/libraries/org/spongepowered/mixin/0.8.5/mixin-0.8.5.jar%23140!/ Service=ModLauncher Env=CLIENT [12:47:24] [pool-2-thread-1/INFO] [EARLYDISPLAY/]: GL info: AMD Radeon RX 5700 XT (radeonsi, navi10, LLVM 19.1.7, DRM 3.54, 6.6.85) GL version 4.6 (Core Profile) Mesa 25.0.3 (git-c3afa2a74f), AMD [12:47:25] [main/WARN] [ne.mi.fm.lo.mo.ModFileParser/LOADING]: Mod file /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/libraries/net/minecraftforge/fmlcore/1.20.1-47.2.6/fmlcore-1.20.1-47.2.6.jar is missing mods.toml file [12:47:25] [main/WARN] [ne.mi.fm.lo.mo.ModFileParser/LOADING]: Mod file /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/libraries/net/minecraftforge/javafmllanguage/1.20.1-47.2.6/javafmllanguage-1.20.1-47.2.6.jar is missing mods.toml file [12:47:25] [main/WARN] [ne.mi.fm.lo.mo.ModFileParser/LOADING]: Mod file /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/libraries/net/minecraftforge/lowcodelanguage/1.20.1-47.2.6/lowcodelanguage-1.20.1-47.2.6.jar is missing mods.toml file [12:47:25] [main/WARN] [ne.mi.fm.lo.mo.ModFileParser/LOADING]: Mod file /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/libraries/net/minecraftforge/mclanguage/1.20.1-47.2.6/mclanguage-1.20.1-47.2.6.jar is missing mods.toml file [12:47:25] [main/WARN] [ne.mi.ja.se.JarSelector/]: Attempted to select two dependency jars from JarJar which have the same identification: Mod File: and Mod File: . Using Mod File: [12:47:25] [main/INFO] [ne.mi.fm.lo.mo.JarInJarDependencyLocator/]: Found 28 dependencies adding them to mods collection [12:47:28] [main/ERROR] [mixin/]: Mixin config dynamiclightsreforged.mixins.json does not specify "minVersion" property [12:47:28] [main/INFO] [mixin/]: Compatibility level set to JAVA_17 [12:47:28] [main/ERROR] [mixin/]: Mixin config mixins.satin.client.json does not specify "minVersion" property [12:47:28] [main/ERROR] [mixin/]: Mixin config firstperson.mixins.json does not specify "minVersion" property [12:47:28] [main/ERROR] [mixin/]: Mixin config yacl.mixins.json does not specify "minVersion" property [12:47:28] [main/INFO] [cp.mo.mo.LaunchServiceHandler/MODLAUNCHER]: Launching target 'forgeclient' with arguments [--version, 1.20.1, --gameDir, /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/instances/TerraFirmaGreg-Modern/minecraft, --assetsDir, /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/assets, --uuid, , --username, Ryexandrite, --assetIndex, 5, --accessToken, â�„â�„â�„â�„â�„â�„â�„â�„, --userType, msa, --versionType, release, --width, 854, --height, 480] [12:47:28] [main/INFO] [co.ab.sa.co.Saturn/]: Loaded Saturn config file with 4 configurable options [12:47:28] [main/INFO] [ModernFix/]: Loaded configuration file for ModernFix 5.18.1+mc1.20.1: 83 options available, 1 override(s) found [12:47:28] [main/WARN] [ModernFix/]: Option 'mixin.perf.thread_priorities' overriden (by mods [smoothboot]) to 'false' [12:47:28] [main/INFO] [ModernFix/]: Applying Nashorn fix [12:47:28] [main/INFO] [ModernFix/]: Applied Forge config corruption patch [12:47:28] [main/INFO] [fpsreducer/]: OptiFine was NOT detected. [12:47:28] [main/INFO] [fpsreducer/]: OptiFabric was NOT detected. [12:47:28] [main/WARN] [EmbeddiumConfig/]: Mod 'tfc' attempted to override option 'mixin.features.fast_biome_colors', which doesn't exist, ignoring [12:47:28] [main/INFO] [Embeddium/]: Loaded configuration file for Embeddium: 205 options available, 3 override(s) found [12:47:28] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Searching for graphics cards... [12:47:28] [main/INFO] [Embeddium-GraphicsAdapterProbe/]: Found graphics card: GraphicsAdapterInfo[vendor=AMD, name=Navi 10 [Radeon RX 5600 OEM/5600 XT / 5700/5700 XT], version=unknown] [12:47:28] [main/WARN] [Embeddium-Workarounds/]: Sodium has applied one or more workarounds to prevent crashes or other issues on your system: [NO_ERROR_CONTEXT_UNSUPPORTED] [12:47:28] [main/WARN] [Embeddium-Workarounds/]: This is not necessarily an issue, but it may result in certain features or optimizations being disabled. You can sometimes fix these issues by upgrading your graphics driver. [12:47:28] [main/INFO] [Radium Config/]: Loaded configuration file for Radium: 125 options available, 7 override(s) found [12:47:28] [main/WARN] [mixin/]: Reference map 'carpeted-common-refmap.json' for carpeted-common.mixins.json could not be read. If this is a development environment you can ignore this message [12:47:28] [main/WARN] [mixin/]: Reference map 'carpeted-forge-refmap.json' for carpeted.mixins.json could not be read. If this is a development environment you can ignore this message [12:47:28] [main/WARN] [mixin/]: Reference map 'emi-forge-refmap.json' for emi-forge.mixins.json could not be read. If this is a development environment you can ignore this message [12:47:28] [main/WARN] [mixin/]: Reference map 'ftb-filter-system-common-refmap.json' for ftbfiltersystem-common.mixins.json could not be read. If this is a development environment you can ignore this message [12:47:28] [main/WARN] [mixin/]: Reference map 'ftb-filter-system-forge-refmap.json' for ftbfiltersystem.mixins.json could not be read. If this is a development environment you can ignore this message [12:47:28] [main/INFO] [Puzzles Lib/]: Loading 160 mods: - additionalplacements 1.8.0 - ae2 15.2.13 - ae2insertexportcard 1.20.1-1.3.0 - ae2netanalyser 1.20-1.0.6-forge - ae2wtlib 15.2.3-forge - aiimprovements 0.5.2 - ambientsounds 6.1.1 - architectury 9.2.14 - astikorcarts 1.1.8 - attributefix 21.0.4 - balm 7.3.9 \-- kuma_api 20.1.8 - barrels_2012 2.1 - betterf3 7.0.2 - betterfoliage 5.0.2 - betterpingdisplay 1.1 - betterthirdperson 1.9.0 - blur 3.1.1 \-- satin 1.20.1+1.15.0-SNAPSHOT - carpeted 1.20-1.4 - carryon 2.1.2.7 \-- mixinextras 0.2.0-beta.6 - catalogue 1.8.0 - chat_heads 0.13.9 - cherishedworlds 6.1.7+1.20.1 - clienttweaks 11.1.0 - cloth_config 11.1.136 - clumps 12.0.0.4 - computercraft 1.113.1 - controlling 12.0.2 - coralstfc 1.0.0 - corpse 1.20.1-1.0.19 - cosmeticarmorreworked 1.20.1-v1a - craftingtweaks 18.2.5 - craftpresence 2.5.0 - create 0.5.1.f \-- flywheel 0.6.10-7 - create_connected 0.8.2-mc1.20.1 - createaddition 1.20.1-1.2.4c - creativecore 2.12.15 - cucumber 7.0.12 - cupboard 1.20.1-2.7 - curios 5.10.0+1.20.1 - defaultoptions 18.0.1 - do_a_barrel_roll 3.5.6+1.20.1 - drippyloadingscreen 3.0.1 - dynamiclightsreforged 1.20.1_v1.6.0 - embeddium 0.3.19+mc1.20.1 \-- rubidium 0.7.1 - embeddiumplus 1.2.12 - emi 1.1.7+1.20.1+forge - enhancedvisuals 1.8.1 - etched 3.0.2 - everycomp 1.20-2.7.12 - expatternprovider 1.20-1.1.14-forge - exposure 1.7.7 - fallingtrees 0.12.7 - fancymenu 3.2.3 - ferritecore 6.0.1 - firmaciv 0.2.10-alpha-1.20.1 - firmalife 2.1.15 - firstperson 2.4.5 - flickerfix 4.0.1 - forge 47.2.6 - fpsreducer 1.20-2.5 - framedblocks 9.3.1 - ftbbackups2 1.0.23 - ftbessentials 2001.2.2 - ftbfiltersystem 1.0.2 - ftblibrary 2001.2.4 - ftbquests 2001.4.8 - ftbranks 2001.1.3 - ftbteams 2001.3.0 - ftbxmodcompat 2.1.1 - gcyr 0.1.8 - getittogetherdrops 1.3 - glodium 1.20-1.5-forge - gtceu 1.2.3.a |-- configuration 2.2.0 \-- ldlib 1.0.25.j - hangglider 8.0.1 - immediatelyfast 1.2.18+1.20.4 - inventoryhud 3.4.26 - invtweaks 1.1.0 - itemphysiclite 1.6.5 - jade 11.9.4+forge - jadeaddons 5.2.2 - jei 15.3.0.8 - konkrete 1.8.0 - ksyxis 1.3.2 - kubejs 2001.6.5-build.14 - kubejs_create 2001.2.5-build.2 - kubejs_tfc 1.20.1-1.1.3 - letmedespawn 1.3.2b - lootjs 1.20.1-2.12.0 - megacells 2.4.4-1.20.1 - melody 1.0.2 - memoryleakfix 1.1.5 - merequester 1.20.1-1.1.5 - minecraft 1.20.1 - modelfix 1.15 - modernfix 5.18.1+mc1.20.1 - moonlight 1.20-2.13.51 - morered 4.0.0.4 |-- jumbofurnace 4.0.0.5 \-- useitemonblockevent 1.0.0.2 - mousetweaks 2.25.1 - myserveriscompatible 1.0 - nanhealthfixer 1.20.1-0.0.1 - nerb 0.4.1 - noisium 2.3.0+mc1.20-1.20.1 - noreportbutton 1.5.0 - notenoughanimations 1.7.6 - octolib 0.4.2 - oculus 1.7.0 - openpartiesandclaims 0.23.2 - packetfixer 1.4.2 - pandalib 0.4.2 - patchouli 1.20.1-84-FORGE - pickupnotifier 8.0.0 - placebo 8.6.2 - playerrevive 2.0.27 - polylib 2000.0.3-build.143 - puzzleslib 8.1.23 \-- puzzlesaccessapi 8.0.7 - radium 0.12.3+git.50c5c33 - railways 1.6.4+forge-mc1.20.1 - recipeessentials 1.20.1-3.6 - rhino 2001.2.2-build.18 - saturn 0.1.3 - searchables 1.0.3 - shimmer 1.20.1-0.2.4 - showcaseitem 1.20.1-1.2 - simplylight 1.20.1-1.4.6-build.50 - smoothboot 0.0.4 - sophisticatedbackpacks 3.20.5.1044 - sophisticatedcore 0.6.22.611 - supermartijn642configlib 1.1.8 - supermartijn642corelib 1.1.17 - tfc 3.2.12 - tfc_tumbleweed 1.2.2 - tfcagedalcohol 2.1 - tfcambiental 1.20.1-3.3.0 - tfcastikorcarts 1.1.8.2 - tfcchannelcasting 0.2.3-beta - tfcea 0.0.2 - tfcgroomer 1.20.1-0.1.2 - tfchotornot 1.0.4 - tfcvesseltooltip 1.1 - tfg 0.5.9 - toofast 0.4.3.5 - toolbelt 1.20.01 - treetap 1.20.1-0.4.0 - tumbleweed 0.5.5 - unilib 1.0.2 - uteamcore 5.1.4.312 - waterflasks 3.0.3 - xaerominimap 24.4.0 - xaeroworldmap 1.39.0 - yeetusexperimentus 2.3.1-build.6+mc1.20.1 - yet_another_config_lib_v3 3.5.0+1.20.1-forge [12:47:28] [main/WARN] [mixin/]: Reference map 'packetfixer-forge-forge-refmap.json' for packetfixer-forge.mixins.json could not be read. If this is a development environment you can ignore this message [12:47:28] [main/WARN] [mixin/]: Reference map 'tfchotornot.refmap.json' for tfchotornot.mixins.json could not be read. If this is a development environment you can ignore this message [12:47:29] [main/INFO] [ne.mi.co.Co.placebo/COREMODLOG]: Patching IForgeItemStack#getEnchantmentLevel [12:47:29] [main/INFO] [ne.mi.co.Co.placebo/COREMODLOG]: Patching IForgeItemStack#getEnchantmentLevel [12:47:29] [main/WARN] [mixin/]: Error loading class: mezz/modnametooltip/TooltipEventHandler (java.lang.ClassNotFoundException: mezz.modnametooltip.TooltipEventHandler) [12:47:29] [main/WARN] [mixin/]: Error loading class: me/shedaniel/rei/impl/client/ClientHelperImpl (java.lang.ClassNotFoundException: me.shedaniel.rei.impl.client.ClientHelperImpl) [12:47:29] [main/WARN] [mixin/]: Error loading class: me/shedaniel/rei/impl/client/gui/ScreenOverlayImpl (java.lang.ClassNotFoundException: me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl) [12:47:29] [main/INFO] [co.cu.Cupboard/]: Loaded config for: recipeessentials.json [12:47:30] [main/WARN] [mixin/]: Error loading class: loaderCommon/forge/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/ChunkLoader (java.lang.ClassNotFoundException: loaderCommon.forge.com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.ChunkLoader) [12:47:30] [main/INFO] [fpsreducer/]: bre2el.fpsreducer.mixin.RenderSystemMixin will be applied. [12:47:30] [main/INFO] [fpsreducer/]: bre2el.fpsreducer.mixin.WindowMixin will NOT be applied because OptiFine was NOT detected. [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.ChatComponentMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.ChatComponentMixin2 false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.ChatListenerMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.ClientPacketListenerMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.CommandSuggestionSuggestionsListMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.ConnectionMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.DownloadedPackSourceMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.FontStringRenderOutputMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.GuiMessageLineMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.GuiMessageMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.HttpTextureMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.PlayerChatMessageMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.SkinManagerMixin false false [12:47:30] [main/WARN] [debug/]: dzwdz.chat_heads.mixin.compat.EmojifulMixin true false [12:47:30] [main/WARN] [mixin/]: Error loading class: dan200/computercraft/shared/integration/jei/JEIComputerCraft (java.lang.ClassNotFoundException: dan200.computercraft.shared.integration.jei.JEIComputerCraft) [12:47:30] [main/WARN] [mixin/]: @Mixin target dan200.computercraft.shared.integration.jei.JEIComputerCraft was not found tfg.mixins.json:common.cc.JEIComputerCraftMixin [12:47:30] [main/WARN] [mixin/]: Error loading class: com/copycatsplus/copycats/content/copycat/slab/CopycatSlabBlock (java.lang.ClassNotFoundException: com.copycatsplus.copycats.content.copycat.slab.CopycatSlabBlock) [12:47:30] [main/WARN] [mixin/]: @Mixin target com.copycatsplus.copycats.content.copycat.slab.CopycatSlabBlock was not found create_connected.mixins.json:compat.CopycatBlockMixin [12:47:30] [main/WARN] [mixin/]: Error loading class: com/copycatsplus/copycats/content/copycat/board/CopycatBoardBlock (java.lang.ClassNotFoundException: com.copycatsplus.copycats.content.copycat.board.CopycatBoardBlock) [12:47:30] [main/WARN] [mixin/]: @Mixin target com.copycatsplus.copycats.content.copycat.board.CopycatBoardBlock was not found create_connected.mixins.json:compat.CopycatBlockMixin [12:47:30] [main/WARN] [mixin/]: Error loading class: me/jellysquid/mods/lithium/common/ai/pathing/PathNodeDefaults (java.lang.ClassNotFoundException: me.jellysquid.mods.lithium.common.ai.pathing.PathNodeDefaults) [12:47:30] [main/WARN] [Radium Config/]: Force-disabling mixin 'alloc.blockstate.StateMixin' as option 'mixin.alloc.blockstate' (added by mods [ferritecore]) disables it and children [12:47:30] [main/WARN] [Radium Config/]: Force-disabling mixin 'entity.collisions.fluid.EntityMixin' as option 'mixin.entity.collisions' (added by user configuration) disables it and children [12:47:30] [main/WARN] [Radium Config/]: Force-disabling mixin 'entity.collisions.intersection.WorldMixin' as option 'mixin.entity.collisions' (added by user configuration) disables it and children [12:47:30] [main/WARN] [Radium Config/]: Force-disabling mixin 'entity.collisions.movement.EntityMixin' as option 'mixin.entity.collisions' (added by user configuration) disables it and children [12:47:30] [main/WARN] [Radium Config/]: Force-disabling mixin 'entity.collisions.unpushable_cramming.AbstractMinecartEntityMixin' as option 'mixin.entity.collisions' (added by user configuration) disables it and children [12:47:30] [main/WARN] [Radium Config/]: Force-disabling mixin 'entity.collisions.unpushable_cramming.BoatEntityMixin' as option 'mixin.entity.collisions' (added by user configuration) disables it and children [12:47:30] [main/WARN] [Radium Config/]: Force-disabling mixin 'entity.collisions.unpushable_cramming.EntityMixin' as option 'mixin.entity.collisions' (added by user configuration) disables it and children [12:47:30] [main/WARN] [Radium Config/]: Force-disabling mixin 'entity.collisions.unpushable_cramming.EntityPredicatesMixin' as option 'mixin.entity.collisions' (added by user configuration) disables it and children [12:47:30] [main/WARN] [Radium Config/]: Force-disabling mixin 'entity.collisions.unpushable_cramming.EntityTrackingSectionMixin' as option 'mixin.entity.collisions' (added by user configuration) disables it and children [12:47:30] [main/WARN] [Radium Config/]: Force-disabling mixin 'entity.collisions.unpushable_cramming.LivingEntityMixin' as option 'mixin.entity.collisions' (added by user configuration) disables it and children [12:47:30] [main/WARN] [Radium Config/]: Force-disabling mixin 'world.player_chunk_tick.ThreadedAnvilChunkStorageMixin' as option 'mixin.world.player_chunk_tick' (added by user configuration) disables it and children [12:47:30] [main/WARN] [mixin/]: Error loading class: org/cyclops/integrateddynamics/block/BlockCable (java.lang.ClassNotFoundException: org.cyclops.integrateddynamics.block.BlockCable) [12:47:30] [main/WARN] [mixin/]: @Mixin target org.cyclops.integrateddynamics.block.BlockCable was not found mixins.epp.json:MixinBlockCable [12:47:30] [main/WARN] [mixin/]: Error loading class: blusunrize/immersiveengineering/api/wires/GlobalWireNetwork (java.lang.ClassNotFoundException: blusunrize.immersiveengineering.api.wires.GlobalWireNetwork) [12:47:30] [main/WARN] [mixin/]: @Mixin target blusunrize.immersiveengineering.api.wires.GlobalWireNetwork was not found mixins.epp.json:MixinGlobalWireNetwork [12:47:30] [main/WARN] [mixin/]: Error loading class: weather2/weathersystem/storm/TornadoHelper (java.lang.ClassNotFoundException: weather2.weathersystem.storm.TornadoHelper) [12:47:30] [main/WARN] [mixin/]: @Mixin target weather2.weathersystem.storm.TornadoHelper was not found tfc_tumbleweed.mixins.json:TornadoHelperMixin [12:47:30] [main/WARN] [mixin/]: Error loading class: weather2/weathersystem/storm/TornadoHelper (java.lang.ClassNotFoundException: weather2.weathersystem.storm.TornadoHelper) [12:47:30] [main/WARN] [mixin/]: @Mixin target weather2.weathersystem.storm.TornadoHelper was not found tfc_tumbleweed.mixins.json:client.TornadoHelperMixin [12:47:30] [main/INFO] [memoryleakfix/]: [MemoryLeakFix] Will be applying 3 memory leak fixes! [12:47:30] [main/INFO] [memoryleakfix/]: [MemoryLeakFix] Currently enabled memory leak fixes: [targetEntityLeak, biomeTemperatureLeak, hugeScreenshotLeak] [12:47:31] [main/WARN] [Embeddium/]: Force-disabling mixin 'features.render.world.sky.WorldRendererMixin' as rule 'mixin.features.render.world.sky' (added by mods [oculus, tfc]) disables it and children [12:47:31] [main/WARN] [Embeddium/]: Force-disabling mixin 'features.render.world.sky.ClientWorldMixin' as rule 'mixin.features.render.world.sky' (added by mods [oculus, tfc]) disables it and children [12:47:31] [main/WARN] [Embeddium/]: Force-disabling mixin 'features.render.world.sky.BackgroundRendererMixin' as rule 'mixin.features.render.world.sky' (added by mods [oculus, tfc]) disables it and children [12:47:31] [main/WARN] [Embeddium/]: Force-disabling mixin 'features.render.gui.font.GlyphRendererMixin' as rule 'mixin.features.render.gui.font' (added by mods [oculus]) disables it and children [12:47:31] [main/WARN] [Embeddium/]: Force-disabling mixin 'features.render.gui.font.FontSetMixin' as rule 'mixin.features.render.gui.font' (added by mods [oculus]) disables it and children [12:47:31] [main/WARN] [Embeddium/]: Force-disabling mixin 'features.render.entity.shadows.EntityRenderDispatcherMixin' as rule 'mixin.features.render.entity' (added by mods [oculus]) disables it and children [12:47:31] [main/WARN] [Embeddium/]: Force-disabling mixin 'features.render.entity.fast_render.ModelPartMixin' as rule 'mixin.features.render.entity' (added by mods [oculus]) disables it and children [12:47:31] [main/WARN] [Embeddium/]: Force-disabling mixin 'features.render.entity.fast_render.CuboidMixin' as rule 'mixin.features.render.entity' (added by mods [oculus]) disables it and children [12:47:31] [main/WARN] [Embeddium/]: Force-disabling mixin 'features.render.entity.cull.EntityRendererMixin' as rule 'mixin.features.render.entity' (added by mods [oculus]) disables it and children [12:47:31] [main/WARN] [mixin/]: Error loading class: org/jetbrains/annotations/ApiStatus$Internal (java.lang.ClassNotFoundException: org.jetbrains.annotations.ApiStatus$Internal) [12:47:31] [main/INFO] [MixinExtras|Service/]: Initializing MixinExtras via com.llamalad7.mixinextras.service.MixinExtrasServiceImpl(version=0.4.1). [12:47:31] [main/INFO] [Smooth Boot (Reloaded)/]: Smooth Boot (Reloaded) config initialized [12:47:31] [main/WARN] [mixin/]: Static binding violation: PRIVATE @Overwrite method m_216202_ in modernfix-forge.mixins.json:perf.tag_id_caching.TagOrElementLocationMixin cannot reduce visibiliy of PUBLIC target method, visibility will be upgraded. [12:47:32] [pool-4-thread-1/INFO] [minecraft/Bootstrap]: ModernFix reached bootstrap stage (9.773 s after launch) [12:47:32] [pool-4-thread-1/WARN] [mixin/]: @Final field delegatesByName:Ljava/util/Map; in modernfix-forge.mixins.json:perf.forge_registry_alloc.ForgeRegistryMixin should be final [12:47:32] [pool-4-thread-1/WARN] [mixin/]: @Final field delegatesByValue:Ljava/util/Map; in modernfix-forge.mixins.json:perf.forge_registry_alloc.ForgeRegistryMixin should be final [12:47:32] [pool-4-thread-1/INFO] [ne.mi.co.Co.placebo/COREMODLOG]: Patching IForgeItemStack#getEnchantmentLevel [12:47:32] [pool-4-thread-1/INFO] [ne.mi.co.Co.placebo/COREMODLOG]: Patching IForgeItemStack#getEnchantmentLevel [12:47:32] [pool-4-thread-1/INFO] [ModernFix/]: Injecting BlockStateBase cache population hook into getNeighborPathNodeType from me.jellysquid.mods.lithium.mixin.ai.pathing.AbstractBlockStateMixin [12:47:32] [pool-4-thread-1/INFO] [ModernFix/]: Injecting BlockStateBase cache population hook into getPathNodeType from me.jellysquid.mods.lithium.mixin.ai.pathing.AbstractBlockStateMixin [12:47:32] [pool-4-thread-1/INFO] [ModernFix/]: Injecting BlockStateBase cache population hook into getAllFlags from me.jellysquid.mods.lithium.mixin.util.block_tracking.AbstractBlockStateMixin [12:47:32] [pool-4-thread-1/WARN] [mixin/]: Method overwrite conflict for m_6104_ in embeddium.mixins.json:features.options.render_layers.LeavesBlockMixin, previously written by me.srrapero720.embeddiumplus.mixins.impl.leaves_culling.LeavesBlockMixin. Skipping method. [12:47:32] [pool-4-thread-1/INFO] [ne.mi.co.Co.placebo/COREMODLOG]: Patching IForgeItemStack#getEnchantmentLevel [12:47:32] [pool-4-thread-1/INFO] [ne.mi.co.Co.placebo/COREMODLOG]: Patching IForgeItemStack#getEnchantmentLevel [12:47:33] [pool-4-thread-1/INFO] [minecraft/Bootstrap]: Vanilla bootstrap took 779 milliseconds [12:47:34] [pool-4-thread-1/WARN] [mixin/]: Method overwrite conflict for m_47505_ in lithium.mixins.json:world.temperature_cache.BiomeMixin, previously written by org.embeddedt.modernfix.common.mixin.perf.remove_biome_temperature_cache.BiomeMixin. Skipping method. [12:47:34] [pool-4-thread-1/INFO] [co.al.me.MERequester/]: Registering content [12:47:35] [Render thread/WARN] [minecraft/VanillaPackResourcesBuilder]: Assets URL 'union:/home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/libraries/net/minecraft/client/1.20.1-20230612.114412/client-1.20.1-20230612.114412-srg.jar%23444!/assets/.mcassetsroot' uses unexpected schema [12:47:35] [Render thread/WARN] [minecraft/VanillaPackResourcesBuilder]: Assets URL 'union:/home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/libraries/net/minecraft/client/1.20.1-20230612.114412/client-1.20.1-20230612.114412-srg.jar%23444!/data/.mcassetsroot' uses unexpected schema [12:47:35] [Render thread/INFO] [mojang/YggdrasilAuthenticationService]: Environment: authHost='https://authserver.mojang.com', accountsHost='https://api.mojang.com', sessionHost='https://sessionserver.mojang.com', servicesHost='https://api.minecraftservices.com', name='PROD' [12:47:35] [Render thread/INFO] [minecraft/Minecraft]: Setting user: Ryexandrite [12:47:35] [Render thread/INFO] [ModernFix/]: Bypassed Mojang DFU [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant is searching for constants in method with descriptor (Lnet/minecraft/network/chat/Component;Lnet/minecraft/client/GuiMessageTag;)V [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found STRING constant: value = , stringValue = null [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found a matching constant TYPE at ordinal 0 [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found LdcInsn [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found STRING constant: value = \\r, stringValue = null [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found a matching constant TYPE at ordinal 1 [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found LdcInsn \\r [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found STRING constant: value = , stringValue = null [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found a matching constant TYPE at ordinal 2 [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found LdcInsn [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found STRING constant: value = \\n, stringValue = null [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found a matching constant TYPE at ordinal 3 [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found LdcInsn \\n [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found CLASS constant: value = Ljava/lang/String;, typeValue = null [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found STRING constant: value = [{}] [CHAT] {}, stringValue = null [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found a matching constant TYPE at ordinal 4 [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found LdcInsn [{}] [CHAT] {} [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found STRING constant: value = [CHAT] {}, stringValue = null [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found a matching constant TYPE at ordinal 5 [12:47:35] [Render thread/INFO] [mixin/]: BeforeConstant found LdcInsn [CHAT] {} [12:47:35] [Render thread/INFO] [defaultoptions/]: Loaded default options for extra-folder [12:47:35] [Render thread/INFO] [ModernFix/]: Instantiating Mojang DFU [12:47:36] [Render thread/INFO] [minecraft/Minecraft]: Backend library: LWJGL version 3.3.1 build 7 [12:47:36] [Render thread/INFO] [KubeJS/]: Loaded client.properties [12:47:36] [Render thread/INFO] [Embeddium-PostlaunchChecks/]: OpenGL Vendor: AMD [12:47:36] [Render thread/INFO] [Embeddium-PostlaunchChecks/]: OpenGL Renderer: AMD Radeon RX 5700 XT (radeonsi, navi10, LLVM 19.1.7, DRM 3.54, 6.6.85) [12:47:36] [Render thread/INFO] [Embeddium-PostlaunchChecks/]: OpenGL Version: 4.6 (Core Profile) Mesa 25.0.3 (git-c3afa2a74f) [12:47:36] [Render thread/WARN] [Embeddium++/Config]: Loading Embeddium++Config [12:47:36] [Render thread/INFO] [Embeddium++/Config]: Updating config cache [12:47:36] [Render thread/INFO] [Embeddium++/Config]: Cache updated successfully [12:47:36] [Render thread/INFO] [ImmediatelyFast/]: Initializing ImmediatelyFast 1.2.18+1.20.4 on AMD Radeon RX 5700 XT (radeonsi, navi10, LLVM 19.1.7, DRM 3.54, 6.6.85) (AMD) with OpenGL 4.6 (Core Profile) Mesa 25.0.3 (git-c3afa2a74f) [12:47:36] [Render thread/INFO] [ImmediatelyFast/]: AMD GPU detected. Enabling coherent buffer mapping [12:47:36] [Datafixer Bootstrap/INFO] [mojang/DataFixerBuilder]: 188 Datafixer optimizations took 85 milliseconds [12:47:36] [Render thread/INFO] [ImmediatelyFast/]: Found Iris/Oculus 1.7.0. Enabling compatibility. [12:47:36] [Render thread/INFO] [Oculus/]: Debug functionality is disabled. [12:47:36] [Render thread/INFO] [Oculus/]: OpenGL 4.5 detected, enabling DSA. [12:47:36] [Render thread/INFO] [Oculus/]: Shaders are disabled because no valid shaderpack is selected [12:47:36] [Render thread/INFO] [Oculus/]: Shaders are disabled [12:47:36] [modloading-worker-0/INFO] [dynamiclightsreforged/]: [LambDynLights] Initializing Dynamic Lights Reforged... [12:47:36] [modloading-worker-0/INFO] [LowDragLib/]: LowDragLib is initializing on platform: Forge [12:47:36] [modloading-worker-0/INFO] [in.u_.u_.ut.ve.JarSignVerifier/]: Mod uteamcore is signed with a valid certificate. [12:47:36] [modloading-worker-0/INFO] [de.ke.me.Melody/]: [MELODY] Loading Melody background audio library.. [12:47:36] [modloading-worker-0/INFO] [Puzzles Lib/]: Constructing common components for pickupnotifier:main [12:47:36] [modloading-worker-0/WARN] [mixin/]: Static binding violation: PRIVATE @Overwrite method m_109501_ in embeddium.mixins.json:core.render.world.WorldRendererMixin cannot reduce visibiliy of PUBLIC target method, visibility will be upgraded. [12:47:36] [modloading-worker-0/INFO] [de.ke.ko.Konkrete/]: [KONKRETE] Successfully initialized! [12:47:36] [modloading-worker-0/INFO] [de.ke.ko.Konkrete/]: [KONKRETE] Server-side libs ready to use! [12:47:36] [modloading-worker-0/INFO] [Puzzles Lib/]: Constructing client components for pickupnotifier:main [12:47:36] [modloading-worker-0/WARN] [mixin/]: Static binding violation: PRIVATE @Overwrite method m_215924_ in modernfix-forge.mixins.json:perf.tag_id_caching.TagEntryMixin cannot reduce visibiliy of PUBLIC target method, visibility will be upgraded. [12:47:36] [modloading-worker-0/INFO] [Additional Placements/]: Attempting to manually load Additional Placements config early. [12:47:36] [modloading-worker-0/INFO] [Additional Placements/]: manual config load successful. [12:47:36] [modloading-worker-0/WARN] [Additional Placements/]: During block registration you may recieve several reports of "Potentially Dangerous alternative prefix `additionalplacements`". Ignore these, they are intended. [12:47:36] [modloading-worker-0/INFO] [Puzzles Lib/]: Constructing common components for hangglider:main [12:47:36] [modloading-worker-0/INFO] [noisium/]: Loading Noisium. [12:47:36] [modloading-worker-0/INFO] [co.cu.Cupboard/]: Loaded config for: cupboard.json [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id architectury:sync_ids [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id architectury:sync_ids [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id pandalib:config_sync [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id pandalib:config_sync [12:47:36] [modloading-worker-0/INFO] [fallingtrees | Config/]: Successfully loaded config 'fallingtrees_client' [12:47:36] [modloading-worker-0/INFO] [fallingtrees | Config/]: Successfully saved config 'fallingtrees_client' [12:47:36] [modloading-worker-0/INFO] [Puzzles Lib/]: Constructing client components for hangglider:main [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id polylib:container_to_client [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id polylib:tile_to_client [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id polylib:container_packet_server [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id polylib:tile_data_server [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id polylib:tile_packet_server [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftblibrary:edit_nbt [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftblibrary:edit_nbt_response [12:47:36] [modloading-worker-0/INFO] [fallingtrees | Config/]: Successfully loaded config 'fallingtrees_common' [12:47:36] [modloading-worker-0/INFO] [fallingtrees | Config/]: Successfully saved config 'fallingtrees_common' [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftblibrary:sync_known_server_registries [12:47:36] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftblibrary:edit_config [12:47:37] [UniLib/INFO] [unilib/]: Starting version check for "craftpresence" (MC 1.20.1) at "https://raw.githubusercontent.com/CDAGaming/VersionLibrary/master/CraftPresence/update.json" [12:47:37] [UniLib/INFO] [unilib/]: Starting version check for "unilib" (MC 1.20.1) at "https://raw.githubusercontent.com/CDAGaming/VersionLibrary/master/UniLib/update.json" [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbessentials:update_tab_name [12:47:37] [modloading-worker-0/INFO] [invtweaks/]: Registered 2 network packets [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Loaded common.properties [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Loaded dev.properties [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Looking for KubeJS plugins... [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbteams:sync_teams [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbteams:sync_message_history [12:47:37] [modloading-worker-0/INFO] [GregTechCEu/]: GregTechCEu is initializing on platform: Forge [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Found plugin source kubejs [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbteams:open_gui [12:47:37] [CraftPresence/INFO] [craftpresence/]: Configuration settings have been saved and reloaded successfully! [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbteams:open_my_team_gui [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbteams:update_settings [12:47:37] [modloading-worker-0/WARN] [KubeJS/]: Plugin dev.latvian.mods.kubejs.integration.forge.gamestages.GameStagesIntegration does not have required mod gamestages loaded, skipping [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Found plugin source ldlib [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Found plugin source exposure [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Found plugin source tfg [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbteams:update_settings_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbteams:send_message [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Found plugin source ftbxmodcompat [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbteams:send_message_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbteams:update_presence [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbteams:create_party [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbteams:player_gui_operation [12:47:37] [modloading-worker-0/WARN] [KubeJS/]: Plugin dev.ftb.mods.ftbxmodcompat.ftbchunks.kubejs.FTBChunksKubeJSPlugin does not have required mod ftbchunks loaded, skipping [12:47:37] [modloading-worker-0/INFO] [de.ke.dr.DrippyLoadingScreen/]: [DRIPPY LOADING SCREEN] Loading v3.0.1 in client-side mode on FORGE! [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Found plugin source lootjs [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Found plugin source cucumber [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Found plugin source gtceu [12:47:37] [modloading-worker-0/INFO] [ne.dr.tf.TerraFirmaCraft/]: Initializing TerraFirmaCraft [12:47:37] [modloading-worker-0/INFO] [ne.dr.tf.TerraFirmaCraft/]: Options: Assertions Enabled = false, Boostrap = false, Test = false, Debug Logging = true [12:47:37] [CraftPresence/INFO] [craftpresence/]: Checking Discord for available assets with Client Id: 1182610212121743470 [12:47:37] [CraftPresence/INFO] [craftpresence/]: Originally coded by paulhobbel - https://github.com/paulhobbel [12:47:37] [modloading-worker-0/INFO] [ne.mi.co.ForgeMod/FORGEMOD]: Forge mod loading, version 47.2.6, for MC 1.20.1 with MCP 20230612.114412 [12:47:37] [modloading-worker-0/INFO] [ne.mi.co.MinecraftForge/FORGE]: MinecraftForge v47.2.6 Initialized [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Found plugin source gcyr [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Found plugin source kubejs_tfc [12:47:37] [modloading-worker-0/WARN] [KubeJS/]: Plugin com.notenoughmail.kubejs_tfc.addons.precpros.PrecProsPlugin does not have required mod precisionprospecting loaded, skipping [12:47:37] [modloading-worker-0/WARN] [KubeJS/]: Plugin com.notenoughmail.kubejs_tfc.addons.afc.AFCPlugin does not have required mod afc loaded, skipping [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Found plugin source kubejs_create [Mouse Tweaks] Main.initialize() [Mouse Tweaks] Initialized. [12:47:37] [modloading-worker-0/INFO] [Every Compat/]: Loaded EveryCompat Create Module [12:47:37] [modloading-worker-0/INFO] [Configuration/FileWatching]: Registered gtceu config for auto-sync function [12:47:37] [modloading-worker-0/INFO] [Configuration/FileWatching]: Registered gtceu config for auto-sync function [12:47:37] [modloading-worker-0/INFO] [Configuration/FileWatching]: Registered gcyr config for auto-sync function [12:47:37] [modloading-worker-0/INFO] [GregTechCEu/]: High-Tier is Disabled. [12:47:37] [modloading-worker-0/INFO] [ne.cr.ft.re.or.qu.im.StdSchedulerFactory/]: Using default implementation for ThreadExecutor [12:47:37] [modloading-worker-0/INFO] [KubeJS/]: Done in 309.0 ms [12:47:37] [CraftPresence/INFO] [craftpresence/]: 3 total assets detected! [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:sync_quests [12:47:37] [modloading-worker-0/INFO] [Ksyxis/]: Ksyxis: Booting... (platform: Forge, manual: false) [12:47:37] [modloading-worker-0/INFO] [Ksyxis/]: Ksyxis: Found Mixin library. (version: 0.8.5) [12:47:37] [modloading-worker-0/INFO] [Ksyxis/]: Ksyxis: Ready. As always, this mod will speed up your world loading and might or might not break it. [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:sync_team_data [12:47:37] [modloading-worker-0/INFO] [ne.cr.ft.re.or.qu.co.SchedulerSignalerImpl/]: Initialized Scheduler Signaller of type: class net.creeperhost.ftbbackups.repack.org.quartz.core.SchedulerSignalerImpl [12:47:37] [modloading-worker-0/INFO] [ne.cr.ft.re.or.qu.co.QuartzScheduler/]: Quartz Scheduler v.2.0.2 created. [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:update_task_progress [12:47:37] [modloading-worker-0/INFO] [ne.cr.ft.re.or.qu.si.RAMJobStore/]: RAMJobStore initialized. [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:submit_task [12:47:37] [modloading-worker-0/INFO] [ne.cr.ft.re.or.qu.co.QuartzScheduler/]: Scheduler meta-data: Quartz Scheduler (v2.0.2) 'ftbbackups2' with instanceId 'NON_CLUSTERED' Scheduler class: 'net.creeperhost.ftbbackups.repack.org.quartz.core.QuartzScheduler' - running locally. NOT STARTED. Currently in standby mode. Number of jobs executed: 0 Using thread pool 'net.creeperhost.ftbbackups.repack.org.quartz.simpl.SimpleThreadPool' - with 1 threads. Using job-store 'net.creeperhost.ftbbackups.repack.org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered. [12:47:37] [modloading-worker-0/INFO] [ne.cr.ft.re.or.qu.im.StdSchedulerFactory/]: Quartz scheduler 'ftbbackups2' initialized from an externally provided properties instance. [12:47:37] [modloading-worker-0/INFO] [ne.cr.ft.re.or.qu.im.StdSchedulerFactory/]: Quartz scheduler version: 2.0.2 [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:claim_reward [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:claim_reward_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:sync_editing_mode [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:get_emergency_items [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:create_other_team_data [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:claim_all_rewards [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:claim_choice_reward [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:display_completion_toast [12:47:37] [modloading-worker-0/INFO] [ne.cr.ft.re.or.qu.co.QuartzScheduler/]: Scheduler ftbbackups2_$_NON_CLUSTERED started. [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:display_reward_toast [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:display_item_reward_toast [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:toggle_pinned [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:toggle_pinned_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:toggle_chapter_pinned [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:toggle_chapter_pinned_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:toggle_editing_mode [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:force_save [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:update_team_data [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:set_custom_image [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:object_started [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:object_completed [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:object_started_reset [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:object_completed_reset [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:sync_lock [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:reset_reward [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:team_data_changed [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:task_screen_config_req [12:47:37] [modloading-worker-0/INFO] [co.jo.fl.ba.Backend/]: Oculus detected. [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:task_screen_config_resp [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:change_progress [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:create_object [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:create_object_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:create_task_at [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:delete_object [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:delete_object_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:edit_object [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:edit_object_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:move_chapter [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:move_chapter_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:move_quest [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:move_quest_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:change_chapter_group [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:change_chapter_group_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:move_chapter_group [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:move_chapter_group_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:sync_reward_blocking [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:copy_quest [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:copy_chapter_image [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:sync_structures_request [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:sync_structures_response [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbquests:request_team_data [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:sync_editor_permission [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:open_quest_book [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ftbquests:clear_display_cache [12:47:37] [modloading-worker-0/INFO] [me.je.li.lo.PluginCaller/]: Sending ConfigManager... [12:47:37] [modloading-worker-0/INFO] [me.je.li.lo.PluginCaller/]: Sending ConfigManager took 11.32 ms [12:47:37] [modloading-worker-0/INFO] [MEGA Cells/]: Initialised items. [12:47:37] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ftbfiltersystem:sync_filter [12:47:37] [modloading-worker-0/INFO] [MEGA Cells/]: Initialised blocks. [12:47:37] [modloading-worker-0/INFO] [MEGA Cells/]: Initialised block entities. [12:47:37] [modloading-worker-0/INFO] [de.ke.fa.FancyMenu/]: [FANCYMENU] Loading v3.2.3 in client-side mode on FORGE! [12:47:37] [modloading-worker-0/INFO] [showcaseitem/]: Loading config: /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/instances/TerraFirmaGreg-Modern/minecraft/config/showcaseitem-common.toml [12:47:37] [modloading-worker-0/INFO] [showcaseitem/]: Built config: /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/instances/TerraFirmaGreg-Modern/minecraft/config/showcaseitem-common.toml [12:47:37] [modloading-worker-0/INFO] [showcaseitem/]: Loaded config: /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/instances/TerraFirmaGreg-Modern/minecraft/config/showcaseitem-common.toml [12:47:37] [modloading-worker-0/INFO] [FTB XMod Compat/]: [FTB Quests] Enabled KubeJS integration [12:47:37] [modloading-worker-0/INFO] [me.tr.be.BetterF3Forge/]: [BetterF3] Starting... [12:47:37] [modloading-worker-0/INFO] [me.tr.be.BetterF3Forge/]: [BetterF3] Loading... [12:47:37] [modloading-worker-0/INFO] [de.to.pa.PacketFixer/]: Packet Fixer has been initialized successfully [12:47:37] [modloading-worker-0/INFO] [YetAnotherConfigLib/]: Deserializing YACLConfig from '/home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/instances/TerraFirmaGreg-Modern/minecraft/config/yacl.json5' [12:47:37] [modloading-worker-0/INFO] [Puzzles Lib/]: Constructing common components for puzzleslib:main [12:47:37] [modloading-worker-0/INFO] [me.tr.be.BetterF3Forge/]: [BetterF3] All done! [12:47:37] [modloading-worker-0/INFO] [Puzzles Lib/]: Constructing client components for puzzleslib:main [12:47:37] [modloading-worker-0/INFO] [Rhino Script Remapper/]: Loading Rhino Minecraft remapper... [12:47:37] [modloading-worker-0/INFO] [de.la.mo.rh.mo.ut.RhinoProperties/]: Rhino properties loaded. [12:47:37] [modloading-worker-0/INFO] [Rhino Script Remapper/]: Loading mappings for 1.20.1 [12:47:37] [modloading-worker-0/WARN] [mixin/]: @Inject(@At("INVOKE")) Shift.BY=2 on create_connected.mixins.json:sequencedgearshift.SequencedGearshiftScreenMixin::handler$cfa000$updateParamsOfRow exceeds the maximum allowed value: 0. Increase the value of maxShiftBy to suppress this warning. [12:47:37] [modloading-worker-0/INFO] [Rhino Script Remapper/]: Done in 0.090 s [12:47:38] [modloading-worker-0/INFO] [Railways/]: Registered bogey styles from railways [12:47:38] [modloading-worker-0/INFO] [Railways/]: Registering data fixers [12:47:38] [modloading-worker-0/WARN] [Railways/]: Skipping Datafixer Registration due to it being disabled in the config. [12:47:38] [modloading-worker-0/INFO] [Railways/]: Registering tracks for Hex Casting [12:47:38] [modloading-worker-0/INFO] [Railways/]: Registering tracks for Oh The Biomes You'll Go [12:47:38] [modloading-worker-0/INFO] [Railways/]: Registering tracks for Blue Skies [12:47:38] [modloading-worker-0/INFO] [Railways/]: Registering tracks for Twilight Forest [12:47:38] [modloading-worker-0/INFO] [Railways/]: Registering tracks for Biomes O' Plenty [12:47:38] [modloading-worker-0/INFO] [Railways/]: Registering tracks for Nature's Spirit [12:47:38] [modloading-worker-0/INFO] [Railways/]: Registering tracks for Dreams and Desires [12:47:38] [modloading-worker-0/INFO] [Railways/]: Registering tracks for Quark [12:47:38] [modloading-worker-0/INFO] [Railways/]: Registering tracks for TerraFirmaCraft [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:main_startup_script.js in 0.058 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:tfc/constants.js in 0.024 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:horornot/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:minecraft/constants.js in 0.004 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:railways/constants.js in 0.001 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:gtceu/machines.js in 0.008 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:gtceu/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:gtceu/material_info.js in 0.002 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:gtceu/recipe_types.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:gtceu/blocks.js in 0.001 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:gtceu/items.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:framedblocks/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:firmalife/constants.js in 0.001 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:sophisticated_backpacks/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:more_red/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:mega_cells/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:create/constants.js in 0.002 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:ae2/constants.js in 0.001 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:create_additions/constants.js in 0.001 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:chisel_and_bits/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:extended_ae2/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:asticor_carts/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:firmaciv/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:firmaciv/blocks.js in 0.001 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:ftb_quests/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:tfg/fluids.js in 0.001 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:tfg/materials.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:tfg/blocks.js in 0.001 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:tfg/items.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded script startup_scripts:computer_craft/constants.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: Loaded 30/30 KubeJS startup scripts in 0.721 s with 0 errors and 0 warnings [12:47:38] [modloading-worker-0/INFO] [KubeJS Client/]: example.js#3: TerraFirmaGreg the best modpack in the world :) [12:47:38] [modloading-worker-0/INFO] [KubeJS Client/]: Loaded script client_scripts:example.js in 0.0 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Client/]: Loaded script client_scripts:tooltips.js in 0.003 s [12:47:38] [modloading-worker-0/INFO] [KubeJS Client/]: Loaded 2/2 KubeJS client scripts in 0.022 s with 0 errors and 0 warnings [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: tfg/blocks.js#48: Loaded Java class 'net.minecraft.world.level.block.AmethystClusterBlock' [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: tfg/blocks.js#49: Loaded Java class 'net.minecraft.world.level.block.Blocks' [12:47:38] [modloading-worker-0/INFO] [KubeJS Startup/]: tfg/blocks.js#50: Loaded Java class 'net.minecraft.world.level.block.state.BlockBehaviour$Properties' [12:47:38] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id kubejs:send_data_from_client [12:47:38] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id kubejs:send_data_from_server [12:47:38] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id kubejs:paint [12:47:38] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id kubejs:add_stage [12:47:38] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id kubejs:remove_stage [12:47:38] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id kubejs:sync_stages [12:47:38] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id kubejs:first_click [12:47:38] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id kubejs:toast [12:47:38] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id kubejs:reload_startup_scripts [12:47:38] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id kubejs:display_server_errors [12:47:38] [modloading-worker-0/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id kubejs:display_client_errors [12:47:38] [Render thread/INFO] [GregTechCEu/]: GTCEu common proxy init! [12:47:38] [Render thread/INFO] [GregTechCEu/]: Registering material registries [12:47:38] [Render thread/INFO] [GregTechCEu/]: Registering GTCEu Materials [12:47:38] [CraftPresence/INFO] [craftpresence/]: Attempting to connect to Discord (1/10)... [12:47:39] [Render thread/INFO] [GregTechCEu/]: Registering addon Materials [12:47:39] [Render thread/WARN] [GregTechCEu/]: FluidStorageKey{gtceu:liquid} already has an associated fluid for material gtceu:water [12:47:39] [Render thread/WARN] [GregTechCEu/]: FluidStorageKey{gtceu:liquid} already has an associated fluid for material gtceu:lava [12:47:39] [CraftPresence/INFO] [craftpresence/]: Loaded display data with Client Id: 1182610212121743470 (Logged in as RyRy) [12:47:39] [Render thread/INFO] [GregTechCEu/]: Registering KeyBinds [12:47:39] [Render thread/WARN] [ne.mi.fm.DeferredWorkQueue/LOADING]: Mod 'gtceu' took 1.043 s to run a deferred task. [12:47:42] [Render thread/WARN] [ne.mi.re.ForgeRegistry/REGISTRIES]: Registry minecraft:menu: The object net.minecraft.world.inventory.MenuType@67141ef8 has been registered twice for the same name ae2:export_card. [12:47:42] [Render thread/WARN] [ne.mi.re.ForgeRegistry/REGISTRIES]: Registry minecraft:menu: The object net.minecraft.world.inventory.MenuType@f4864e9 has been registered twice for the same name ae2:insert_card. [12:47:42] [Render thread/INFO] [Moonlight/]: Initialized block sets in 21ms [12:47:42] [Render thread/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering C2S receiver with id ae2wtlib:cycle_terminal [12:47:43] [Render thread/INFO] [Every Compat/]: Registering Compat WoodType Blocks [12:47:43] [Render thread/INFO] [Every Compat/]: EveryCompat Create Module: registered 42 WoodType blocks [12:47:43] [Render thread/INFO] [tf.TFCTumbleweed/]: Injecting TFC Tumbleweed override pack [12:47:43] [Render thread/INFO] [co.ee.fi.FirmaLife/]: Injecting firmalife override pack [ALSOFT] (EE) Failed to set real-time priority for thread: Operation not permitted (1) [12:47:43] [Render thread/INFO] [Oculus/]: Hardware information: [12:47:43] [Render thread/INFO] [Oculus/]: CPU: 16x AMD Ryzen 7 3700X 8-Core Processor [12:47:43] [Render thread/INFO] [Oculus/]: GPU: AMD Radeon RX 5700 XT (radeonsi, navi10, LLVM 19.1.7, DRM 3.54, 6.6.85) (Supports OpenGL 4.6 (Core Profile) Mesa 25.0.3 (git-c3afa2a74f)) [12:47:43] [Render thread/INFO] [Oculus/]: OS: Linux (6.6.85) [12:47:44] [Render thread/WARN] [mixin/]: Method overwrite conflict for isHidden in mixins.oculus.compat.sodium.json:copyEntity.ModelPartMixin, previously written by dev.tr7zw.firstperson.mixins.ModelPartMixin. Skipping method. [12:47:44] [Render thread/INFO] [minecraft/Minecraft]: [FANCYMENU] Registering resource reload listener.. [12:47:44] [Render thread/INFO] [de.ke.fa.cu.ScreenCustomization/]: [FANCYMENU] Initializing screen customization engine! Addons should NOT REGISTER TO REGISTRIES anymore now! [12:47:44] [Render thread/INFO] [de.ke.fa.cu.la.ScreenCustomizationLayerHandler/]: [FANCYMENU] Minecraft resource reload: STARTING [12:47:44] [Render thread/INFO] [ModernFix/]: Invalidating pack caches [12:47:44] [Render thread/INFO] [minecraft/ReloadableResourceManager]: Reloading ResourceManager: Additional Placements blockstate redirection pack, vanilla, mod_resources, gtceu:dynamic_assets, Moonlight Mods Dynamic Assets, Firmalife-1.20.1-2.1.15.jar:overload, TFCTumbleweed-1.20.1-1.2.2.jar:overload, KubeJS Resource Pack [assets], ldlib [12:47:44] [Finalizer/WARN] [ModernFix/]: One or more BufferBuilders have been leaked, ModernFix will attempt to correct this. [12:47:45] [Render thread/INFO] [Every Compat/]: Generated runtime CLIENT_RESOURCES for pack Moonlight Mods Dynamic Assets (everycomp) in: 597 ms [12:47:45] [Render thread/INFO] [Moonlight/]: Generated runtime CLIENT_RESOURCES for pack Moonlight Mods Dynamic Assets (moonlight) in: 0 ms [12:47:45] [modloading-worker-0/INFO] [Puzzles Lib/]: Loading client config for pickupnotifier [12:47:45] [modloading-worker-0/INFO] [Puzzles Lib/]: Loading client config for hangglider [12:47:45] [Worker-ResourceReload-4/INFO] [minecraft/UnihexProvider]: Found unifont_all_no_pua-15.0.06.hex, loading [12:47:45] [Worker-ResourceReload-3/INFO] [xa.pa.OpenPartiesAndClaims/]: Loading Open Parties and Claims! [12:47:45] [Worker-ResourceReload-1/INFO] [co.re.RecipeEssentials/]: recipeessentials mod initialized [12:47:45] [Worker-ResourceReload-10/INFO] [ne.dr.tf.TerraFirmaCraft/]: TFC Common Setup [12:47:45] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [saturn] Starting version check at https://github.com/AbdElAziz333/Saturn/raw/mc1.20.1/dev/updates.json [12:47:45] [Worker-ResourceReload-1/INFO] [FTB Library/]: Setting game stages provider implementation to: KubeJS Stages [12:47:45] [Worker-ResourceReload-1/INFO] [FTB XMod Compat/]: Chose [KubeJS Stages] as the active game stages implementation [12:47:45] [Worker-ResourceReload-1/INFO] [FTB Library/]: Setting permissions provider implementation to: FTB Ranks [12:47:45] [Worker-ResourceReload-1/INFO] [FTB XMod Compat/]: Chose [FTB Ranks] as the active permissions implementation [12:47:45] [Worker-ResourceReload-1/INFO] [FTB XMod Compat/]: [FTB Quests] recipe helper provider is [JEI] [12:47:45] [Worker-ResourceReload-1/INFO] [FTB XMod Compat/]: [FTB Quests] Enabled FTB Filter System integration [12:47:45] [Render thread/INFO] [GregTechCEu/]: GregTech Model loading took 520ms [12:47:46] [Render thread/INFO] [minecraft/LoadingOverlay]: [DRIPPY LOADING SCREEN] Initializing fonts for text rendering.. [12:47:46] [Render thread/INFO] [minecraft/LoadingOverlay]: [DRIPPY LOADING SCREEN] Calculating animation sizes for FancyMenu.. [12:47:46] [Render thread/INFO] [de.ke.fa.cu.la.ScreenCustomizationLayerHandler/]: [FANCYMENU] ScreenCustomizationLayer registered: drippy_loading_overlay [12:47:46] [Render thread/INFO] [de.ke.fa.cu.an.AnimationHandler/]: [FANCYMENU] Preloading animations! This could cause the loading screen to freeze for a while.. [12:47:46] [Render thread/INFO] [de.ke.fa.cu.an.AnimationHandler/]: [FANCYMENU] Finished preloading animations! [12:47:46] [Render thread/INFO] [de.ke.fa.FancyMenu/]: [FANCYMENU] Starting late client initialization phase.. [12:47:46] [Forge Version Check/WARN] [ne.mi.fm.VersionChecker/]: Failed to process update information com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 9 column 1 path $ at com.google.gson.Gson.fromJson(Gson.java:1226) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.Gson.fromJson(Gson.java:1124) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.Gson.fromJson(Gson.java:1034) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.Gson.fromJson(Gson.java:969) ~[gson-2.10.jar%2388!/:?] {} at net.minecraftforge.fml.VersionChecker$1.process(VersionChecker.java:183) ~[fmlcore-1.20.1-47.2.6.jar%23445!/:?] {} at java.lang.Iterable.forEach(Iterable.java:75) ~[?:?] {re:mixin} at net.minecraftforge.fml.VersionChecker$1.run(VersionChecker.java:114) ~[fmlcore-1.20.1-47.2.6.jar%23445!/:?] {} Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 9 column 1 path $ at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:393) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:182) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:144) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.Gson.fromJson(Gson.java:1214) ~[gson-2.10.jar%2388!/:?] {} ... 6 more [12:47:46] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [controlling] Starting version check at https://updates.blamejared.com/get?n=controlling&gv=1.20.1 [12:47:46] [Worker-ResourceReload-6/ERROR] [minecraft/SimpleJsonResourceReloadListener]: Couldn't parse data file tfc:field_guide/ru_ru/entries/tfg_ores/surface_copper from tfc:patchouli_books/field_guide/ru_ru/entries/tfg_ores/surface_copper.json com.google.gson.JsonParseException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 55 column 6 path $.pages[5] at net.minecraft.util.GsonHelper.m_13780_(GsonHelper.java:526) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:classloading,re:mixin} at net.minecraft.util.GsonHelper.m_263475_(GsonHelper.java:531) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:classloading,re:mixin} at net.minecraft.util.GsonHelper.m_13776_(GsonHelper.java:581) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:classloading,re:mixin} at net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener.m_278771_(SimpleJsonResourceReloadListener.java:41) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,re:classloading} at net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener.m_5944_(SimpleJsonResourceReloadListener.java:29) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,re:classloading} at net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener.m_5944_(SimpleJsonResourceReloadListener.java:17) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,re:classloading} at net.minecraft.server.packs.resources.SimplePreparableReloadListener.m_10786_(SimplePreparableReloadListener.java:11) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:APP:moonlight.mixins.json:ConditionHackMixin,pl:mixin:A} at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768) ~[?:?] {} at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760) ~[?:?] {} at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?] {} at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[?:?] {} at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?] {re:mixin,re:computing_frames} at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?] {re:mixin,re:computing_frames} at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?] {re:mixin} Caused by: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 55 column 6 path $.pages[5] at com.google.gson.stream.JsonReader.syntaxError(JsonReader.java:1657) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.stream.JsonReader.checkLenient(JsonReader.java:1463) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:569) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.stream.JsonReader.hasNext(JsonReader.java:422) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.internal.bind.TypeAdapters$28.read(TypeAdapters.java:779) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.internal.bind.TypeAdapters$28.read(TypeAdapters.java:725) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.internal.bind.TypeAdapters$34$1.read(TypeAdapters.java:1007) ~[gson-2.10.jar%2388!/:?] {} at net.minecraft.util.GsonHelper.m_13780_(GsonHelper.java:524) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:classloading,re:mixin} ... 13 more [12:47:46] [Render thread/INFO] [de.ke.fa.ut.wi.WindowHandler/]: [FANCYMENU] Custom window icon successfully updated! [12:47:46] [Render thread/INFO] [KubeJS Client/]: Client resource reload complete! [12:47:46] [Render thread/INFO] [defaultoptions/]: Loaded default options for keymappings [12:47:46] [Render thread/INFO] [de.ke.fa.ut.wi.WindowHandler/]: [FANCYMENU] Custom window icon successfully updated! [12:47:46] [Worker-Main-6/INFO] [minecraft/UnihexProvider]: Found unifont_all_no_pua-15.0.06.hex, loading [12:47:47] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [controlling] Found status: BETA Current: 12.0.2 Target: 12.0.2 [12:47:47] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [uteamcore] Starting version check at https://api.u-team.info/update/uteamcore.json [12:47:47] [FTB Backups Config Watcher 0/INFO] [ne.cr.ft.FTBBackups/]: Config at /home/ryex/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/instances/TerraFirmaGreg-Modern/minecraft/config/ftbbackups2.json has changed, reloaded! [12:47:47] [Worker-ResourceReload-14/WARN] [minecraft/SpriteLoader]: Texture create_connected:block/fluid_container_window_debug with size 40x32 limits mip level from 4 to 3 [12:47:47] [UniLib/INFO] [unilib/]: Received update status for "unilib" -> Outdated (Target version: "v1.0.5") [12:47:47] [UniLib/INFO] [unilib/]: Received update status for "craftpresence" -> Outdated (Target version: "v2.5.4") [12:47:47] [Render thread/INFO] [Every Compat/]: Registered 42 compat blocks making up 0.31% of total blocks registered [12:47:47] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [uteamcore] Found status: OUTDATED Current: 5.1.4.312 Target: 5.1.4.346 [12:47:47] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [pickupnotifier] Starting version check at https://raw.githubusercontent.com/Fuzss/modresources/main/update/pickupnotifier.json [12:47:47] [Render thread/INFO] [Moonlight/]: Initialized color sets in 104ms [12:47:47] [Render thread/INFO] [co.no.ku.KubeJSTFC/]: KubeJS TFC configuration: [12:47:47] [Render thread/INFO] [co.no.ku.KubeJSTFC/]: Debug mode enabled: false [12:47:47] [Render thread/INFO] [MEGA Cells/]: Initialised AE2WT integration. [12:47:47] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [pickupnotifier] Found status: UP_TO_DATE Current: 8.0.0 Target: null [12:47:47] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [corpse] Starting version check at https://update.maxhenkel.de/forge/corpse [12:47:47] [Worker-ResourceReload-0/INFO] [FirstPersonModel/]: Loading FirstPerson Mod [12:47:47] [Worker-ResourceReload-4/INFO] [xa.ma.WorldMap/]: Loading Xaero's World Map - Stage 1/2 [12:47:47] [Placebo Patreon Trail Loader/INFO] [placebo/]: Loading patreon trails data... [12:47:47] [Placebo Patreon Wing Loader/INFO] [placebo/]: Loading patreon wing data... [12:47:47] [Worker-ResourceReload-13/INFO] [de.ke.ko.Konkrete/]: [KONKRETE] Client-side libs ready to use! [12:47:47] [Placebo Patreon Trail Loader/INFO] [placebo/]: Loaded 45 patreon trails. [12:47:47] [Placebo Patreon Wing Loader/INFO] [placebo/]: Loaded 21 patreon wings. [12:47:47] [Worker-ResourceReload-7/INFO] [EMI/]: [EMI] Discovered Sodium [12:47:47] [Worker-ResourceReload-14/INFO] [xa.mi.XaeroMinimap/]: Loading Xaero's Minimap - Stage 1/2 [12:47:47] [Worker-ResourceReload-13/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ae2wtlib:update_wut [12:47:47] [Worker-ResourceReload-13/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ae2wtlib:update_restock [12:47:47] [Worker-ResourceReload-13/INFO] [de.ar.ne.fo.NetworkManagerImpl/]: Registering S2C receiver with id ae2wtlib:restock_amounts [12:47:48] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [corpse] Found status: OUTDATED Current: 1.20.1-1.0.19 Target: 1.20.1-1.0.20 [12:47:48] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [create_connected] Starting version check at https://raw.githubusercontent.com/hlysine/create_connected/main/update.json [12:47:48] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [create_connected] Found status: OUTDATED Current: 0.8.2-mc1.20.1 Target: 1.0.1-mc1.20.1 [12:47:48] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [blur] Starting version check at https://api.modrinth.com/updates/rubidium-extra/forge_updates.json [12:47:48] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [blur] Found status: AHEAD Current: 3.1.1 Target: null [12:47:48] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [hangglider] Starting version check at https://raw.githubusercontent.com/Fuzss/modresources/main/update/hangglider.json [12:47:48] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [hangglider] Found status: UP_TO_DATE Current: 8.0.1 Target: null [12:47:48] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [searchables] Starting version check at https://updates.blamejared.com/get?n=searchables&gv=1.20.1 [12:47:48] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [searchables] Found status: BETA Current: 1.0.3 Target: 1.0.3 [12:47:48] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [computercraft] Starting version check at https://api.modrinth.com/updates/cc-tweaked/forge_updates.json [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [computercraft] Found status: OUTDATED Current: 1.113.1 Target: 1.115.1 [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [unilib] Starting version check at https://raw.githubusercontent.com/CDAGaming/VersionLibrary/master/UniLib/update.json [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [unilib] Found status: AHEAD Current: 1.0.2 Target: null [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [craftpresence] Starting version check at https://raw.githubusercontent.com/CDAGaming/VersionLibrary/master/CraftPresence/update.json [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [craftpresence] Found status: AHEAD Current: 2.5.0 Target: null [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [radium] Starting version check at https://api.modrinth.com/updates/radium/forge_updates.json [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [radium] Found status: OUTDATED Current: 0.12.3+git.50c5c33 Target: 0.12.4 [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [attributefix] Starting version check at https://updates.blamejared.com/get?n=attributefix&gv=1.20.1 [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [attributefix] Found status: BETA Current: 21.0.4 Target: 21.0.4 [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [clumps] Starting version check at https://updates.blamejared.com/get?n=clumps&gv=1.20.1 [12:47:49] [Worker-ResourceReload-4/WARN] [xa.hu.mi.MinimapLogs/]: io exception while checking patreon: Online mod data expired! Date: Thu Apr 17 01:03:51 MST 2025 [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [clumps] Found status: BETA Current: 12.0.0.4 Target: 12.0.0.4 [12:47:49] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [catalogue] Starting version check at https://mrcrayfish.com/modupdatejson?id=catalogue [12:47:50] [Forge Version Check/WARN] [ne.mi.fm.VersionChecker/]: Failed to process update information com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $ at com.google.gson.Gson.fromJson(Gson.java:1226) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.Gson.fromJson(Gson.java:1124) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.Gson.fromJson(Gson.java:1034) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.Gson.fromJson(Gson.java:969) ~[gson-2.10.jar%2388!/:?] {} at net.minecraftforge.fml.VersionChecker$1.process(VersionChecker.java:183) ~[fmlcore-1.20.1-47.2.6.jar%23445!/:?] {} at java.lang.Iterable.forEach(Iterable.java:75) ~[?:?] {re:mixin} at net.minecraftforge.fml.VersionChecker$1.run(VersionChecker.java:114) ~[fmlcore-1.20.1-47.2.6.jar%23445!/:?] {} Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $ at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:393) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:182) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:144) ~[gson-2.10.jar%2388!/:?] {} at com.google.gson.Gson.fromJson(Gson.java:1214) ~[gson-2.10.jar%2388!/:?] {} ... 6 more [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [puzzlesaccessapi] Starting version check at https://raw.githubusercontent.com/Fuzss/modresources/main/update/puzzlesaccessapi.json [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [puzzlesaccessapi] Found status: BETA Current: 8.0.7 Target: null [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [forge] Starting version check at https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json [12:47:50] [Worker-ResourceReload-5/WARN] [minecraft/ModelBakery]: tfcambiental:snowshoes#inventory java.io.FileNotFoundException: tfcambiental:models/item/snowshoes.json at net.minecraft.client.resources.model.ModelBakery.m_119364_(ModelBakery.java:417) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.client.resources.model.ModelBakery.m_119362_(ModelBakery.java:266) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.client.resources.model.ModelBakery.m_119341_(ModelBakery.java:243) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.client.resources.model.ModelBakery.m_119306_(ModelBakery.java:384) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.client.resources.model.ModelBakery.(ModelBakery.java:150) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.client.resources.model.ModelManager.m_246505_(ModelManager.java:83) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:A,pl:runtimedistcleaner:A} at java.util.concurrent.CompletableFuture.biApply(CompletableFuture.java:1311) ~[?:?] {re:mixin} at java.util.concurrent.CompletableFuture$BiApply.tryFire(CompletableFuture.java:1280) ~[?:?] {} at java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:483) ~[?:?] {} at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?] {} at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[?:?] {} at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?] {re:mixin,re:computing_frames} at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?] {re:mixin,re:computing_frames} at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?] {re:mixin} [12:47:50] [Worker-ResourceReload-5/WARN] [minecraft/ModelBakery]: carpeted:block/label java.io.FileNotFoundException: carpeted:models/block/label.json at net.minecraft.client.resources.model.ModelBakery.m_119364_(ModelBakery.java:417) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.client.resources.model.ModelBakery.m_119362_(ModelBakery.java:262) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.client.resources.model.ModelBakery.m_119341_(ModelBakery.java:243) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.client.resources.model.ModelBakery.(ModelBakery.java:159) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:A,pl:runtimedistcleaner:A} at net.minecraft.client.resources.model.ModelManager.m_246505_(ModelManager.java:83) ~[client-1.20.1-20230612.114412-srg.jar%23444!/:?] {re:mixin,pl:accesstransformer:B,pl:runtimedistcleaner:A,re:classloading,pl:accesstransformer:B,pl:mixin:A,pl:runtimedistcleaner:A} at java.util.concurrent.CompletableFuture.biApply(CompletableFuture.java:1311) ~[?:?] {re:mixin} at java.util.concurrent.CompletableFuture$BiApply.tryFire(CompletableFuture.java:1280) ~[?:?] {} at java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:483) ~[?:?] {} at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?] {} at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[?:?] {} at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?] {re:mixin,re:computing_frames} at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?] {re:mixin,re:computing_frames} at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?] {re:mixin} [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [forge] Found status: OUTDATED Current: 47.2.6 Target: 47.4.0 [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [moonlight] Starting version check at https://raw.githubusercontent.com/MehVahdJukaar/Moonlight/multi-loader/forge/update.json [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [moonlight] Found status: BETA Current: 1.20-2.13.51 Target: null [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [configuration] Starting version check at https://raw.githubusercontent.com/Toma1O6/UpdateSchemas/master/configuration-forge.json [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [configuration] Found status: OUTDATED Current: 2.2.0 Target: 2.2.1 [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [smoothboot] Starting version check at https://github.com/AbdElAziz333/SmoothBoot-Reloaded/raw/mc1.20.1/dev/updates.json [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [smoothboot] Found status: UP_TO_DATE Current: 0.0.4 Target: null [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [ksyxis] Starting version check at https://raw.githubusercontent.com/VidTu/Ksyxis/main/updater_ksyxis_forge.json [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [ksyxis] Found status: OUTDATED Current: 1.3.2 Target: 1.3.3 [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [flywheel] Starting version check at https://api.modrinth.com/updates/flywheel/forge_updates.json [12:47:50] [Worker-ResourceReload-4/ERROR] [xa.ma.WorldMap/]: io exception while checking versions: Online mod data expired! Date: Thu Apr 17 01:03:51 MST 2025 [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [flywheel] Found status: BETA Current: 0.6.10-7 Target: null [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [inventoryhud] Starting version check at https://raw.githubusercontent.com/DmitryLovin/pluginUpdate/master/invupdate.json [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [inventoryhud] Found status: UP_TO_DATE Current: 3.4.26 Target: null [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [puzzleslib] Starting version check at https://raw.githubusercontent.com/Fuzss/modresources/main/update/puzzleslib.json [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [puzzleslib] Found status: OUTDATED Current: 8.1.23 Target: 8.1.32 [12:47:50] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [betterf3] Starting version check at https://api.modrinth.com/updates/betterf3/forge_updates.json [12:47:50] [Render thread/INFO] [xa.mi.XaeroMinimap/]: Loading Xaero's Minimap - Stage 2/2 [12:47:51] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [betterf3] Found status: UP_TO_DATE Current: 7.0.2 Target: null [12:47:51] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [packetfixer] Starting version check at https://api.modrinth.com/updates/packet-fixer/forge_updates.json [12:47:51] [Forge Version Check/INFO] [ne.mi.fm.VersionChecker/]: [packetfixer] Found status: OUTDATED Current: 1.4.2 Target: 2.0.0 [12:47:51] [Render thread/WARN] [xa.hu.mi.MinimapLogs/]: io exception while checking versions: Online mod data expired! Date: Thu Apr 17 01:03:51 MST 2025 [12:47:51] [Render thread/INFO] [xa.hu.mi.MinimapLogs/]: Registered player tracker system: minimap_synced [12:47:51] [Render thread/INFO] [xa.hu.mi.MinimapLogs/]: Xaero's Minimap: World Map found! [12:47:51] [Render thread/INFO] [xa.hu.mi.MinimapLogs/]: Registered player tracker system: openpartiesandclaims [12:47:51] [Render thread/INFO] [xa.hu.mi.MinimapLogs/]: Xaero's Minimap: Open Parties And Claims found! [12:47:51] [Render thread/INFO] [xa.hu.mi.MinimapLogs/]: No Optifine! [12:47:51] [Render thread/INFO] [xa.hu.mi.MinimapLogs/]: Xaero's Minimap: No Vivecraft! [12:47:51] [Render thread/INFO] [xa.hu.mi.MinimapLogs/]: Xaero's Minimap: Framed Blocks found! [12:47:51] [Render thread/INFO] [xa.hu.mi.MinimapLogs/]: Xaero's Minimap: Iris found! [12:47:51] [Render thread/INFO] [xa.ma.WorldMap/]: Loading Xaero's World Map - Stage 2/2 [12:47:51] [Render thread/INFO] [xa.ma.WorldMap/]: New world map region cache hash code: -815523079 [12:47:51] [Render thread/INFO] [xa.ma.WorldMap/]: Registered player tracker system: map_synced [12:47:51] [Render thread/INFO] [xa.ma.WorldMap/]: Xaero's WorldMap Mod: Xaero's minimap found! [12:47:51] [Render thread/INFO] [xa.ma.WorldMap/]: Registered player tracker system: minimap_synced [12:47:51] [Render thread/INFO] [xa.ma.WorldMap/]: Registered player tracker system: openpartiesandclaims [12:47:51] [Render thread/INFO] [xa.ma.WorldMap/]: Xaero's WorldMap Mod: Open Parties And Claims found! [12:47:51] [Render thread/INFO] [xa.ma.WorldMap/]: No Optifine! [12:47:51] [Render thread/INFO] [xa.ma.WorldMap/]: Xaero's World Map: No Vivecraft! [12:47:51] [Render thread/INFO] [xa.ma.WorldMap/]: Xaero's World Map: Framed Blocks found! [12:47:51] [Render thread/INFO] [xa.ma.WorldMap/]: Xaero's World Map: Iris found! [12:47:56] [Worker-ResourceReload-5/WARN] [minecraft/ModelManager]: Missing textures in model firmaciv:firmaciv_compass#inventory: minecraft:textures/atlas/blocks.png:minecraft:item/compass [12:47:56] [Worker-ResourceReload-5/WARN] [minecraft/ModelManager]: Missing textures in model gtceu:tin_double_ingot#inventory: minecraft:textures/atlas/blocks.png:gtceu:item/material_sets/dull/ingot_double_overlay [12:47:56] [Worker-ResourceReload-5/WARN] [minecraft/ModelManager]: Missing textures in model createaddition:small_light_connector#facing=west,mode=push,powered=true,rotation=x_clockwise_180,variant=default: minecraft:textures/atlas/blocks.png:create:block/chute_block [12:47:56] [Worker-ResourceReload-5/WARN] [minecraft/ModelManager]: Missing textures in model gtceu:copper_double_ingot#inventory: minecraft:textures/atlas/blocks.png:gtceu:item/material_sets/dull/ingot_double_overlay Reloading Dynamic Lights [12:47:57] [Render thread/INFO] [co.jo.fl.ba.Backend/]: Loaded all shader sources. Create Crafts & Additions Initialized! [12:47:57] [Worker-ResourceReload-2/INFO] [AttributeFix/]: Loaded values for 19 compatible attributes. [12:47:57] [Worker-ResourceReload-2/ERROR] [AttributeFix/]: Attribute ID 'minecolonies:mc_mob_damage' does not belong to a known attribute. This entry will be ignored. [12:47:57] [Worker-ResourceReload-2/INFO] [AttributeFix/]: Loaded 20 values from config. [12:47:57] [Worker-ResourceReload-2/INFO] [AttributeFix/]: Saving config file. 20 entries. [12:47:57] [Worker-ResourceReload-2/INFO] [AttributeFix/]: Applying changes for 20 attributes. [12:47:57] [Worker-ResourceReload-11/INFO] [de.me.as.AstikorCarts/]: Automatic pull animal configuration: pull_animals = [ "minecraft:camel", "minecraft:donkey", "minecraft:horse", "minecraft:mule", "minecraft:skeleton_horse", "minecraft:zombie_horse", "minecraft:player", "tfc:donkey", "tfc:mule", "tfc:horse" ] [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at io.github.mortuusars.exposure.integration.jade.ExposureJadePlugin [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at de.maxhenkel.corpse.integration.waila.PluginCorpse [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at xfacthd.framedblocks.common.compat.jade.FramedJadePlugin [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at snownee.jade.addon.general.GeneralPlugin [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at snownee.jade.addon.create.CreatePlugin [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at me.pandamods.fallingtrees.compat.JadePlugin [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at com.gregtechceu.gtceu.integration.jade.GTJadePlugin [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at net.dries007.tfc.compat.jade.JadeIntegration [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at snownee.jade.addon.vanilla.VanillaPlugin [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at snownee.jade.addon.universal.UniversalPlugin [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at snownee.jade.addon.core.CorePlugin [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at appeng.integration.modules.jade.JadeModule [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at com.glodblock.github.extendedae.xmod.jade.JadePlugin [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at cy.jdkdigital.treetap.compat.jade.JadePlugin [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at com.eerussianguy.firmalife.compat.tooltip.JadeIntegration [12:47:57] [Worker-ResourceReload-9/INFO] [Jade/]: Start loading plugin at com.ljuangbminecraft.tfcchannelcasting.compat.JadeIntegration [12:47:59] [Render thread/WARN] [ne.mi.fm.DeferredWorkQueue/LOADING]: Mod 'create_connected' took 1.342 s to run a deferred task. [12:47:59] [Render thread/INFO] [defaultoptions/]: Loaded default options for keymappings [ALSOFT] (EE) Failed to set real-time priority for thread: Operation not permitted (1) [12:47:59] [Render thread/INFO] [mojang/Library]: OpenAL initialized on device Starship/Matisse HD Audio Controller Analog Stereo [12:47:59] [Render thread/INFO] [minecraft/SoundEngine]: Sound engine started [12:47:59] [Render thread/INFO] [minecraft/SoundEngine]: [FANCYMENU] Reloading AudioResourceHandler after Minecraft SoundEngine reload.. [12:47:59] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 4096x2048x4 minecraft:textures/atlas/blocks.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 1024x512x4 minecraft:textures/atlas/signs.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 512x512x4 minecraft:textures/atlas/banner_patterns.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 512x512x4 minecraft:textures/atlas/shield_patterns.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 2048x1024x4 minecraft:textures/atlas/armor_trims.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 1024x1024x4 minecraft:textures/atlas/chest.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 128x64x4 minecraft:textures/atlas/decorated_pot.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 512x256x4 minecraft:textures/atlas/shulker_boxes.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 512x256x4 minecraft:textures/atlas/beds.png-atlas [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh particle. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_solid. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader fsh rendertype_solid. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_cutout_mipped. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader fsh rendertype_cutout_mipped. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_cutout. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader fsh rendertype_cutout. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_translucent. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_armor_cutout_no_cull. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_entity_solid. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_entity_cutout. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_entity_cutout_no_cull. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_entity_cutout_no_cull_z_offset. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_entity_translucent_cull. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_entity_translucent. [12:48:00] [Render thread/WARN] [minecraft/ShaderInstance]: Shader rendertype_entity_translucent_emissive could not find sampler named Sampler2 in the specified shader program. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_entity_smooth_cutout. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_entity_decal. [12:48:00] [Render thread/INFO] [Shimmer/]: inject shader vsh rendertype_entity_no_outline. [12:48:00] [Render thread/WARN] [minecraft/ShaderInstance]: Shader moonlight:text_alpha_color could not find sampler named Sampler2 in the specified shader program. [12:48:00] [Render thread/WARN] [minecraft/ShaderInstance]: Shader moonlight:text_alpha_color could not find uniform named IViewRotMat in the specified shader program. [12:48:00] [Render thread/WARN] [minecraft/ShaderInstance]: Shader moonlight:text_alpha_color could not find uniform named FogShape in the specified shader program. [12:48:00] [Render thread/WARN] [minecraft/ShaderInstance]: Shader shimmer:rendertype_armor_cutout_no_cull could not find sampler named Sampler2 in the specified shader program. [12:48:00] [Render thread/WARN] [minecraft/ShaderInstance]: Shader shimmer:rendertype_armor_cutout_no_cull could not find uniform named Light0_Direction in the specified shader program. [12:48:00] [Render thread/WARN] [minecraft/ShaderInstance]: Shader shimmer:rendertype_armor_cutout_no_cull could not find uniform named Light1_Direction in the specified shader program. [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 2048x1024x0 minecraft:textures/atlas/particles.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 256x256x0 minecraft:textures/atlas/paintings.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 256x128x0 minecraft:textures/atlas/mob_effects.png-atlas [12:48:00] [Render thread/INFO] [xa.ma.WorldMap/]: Successfully reloaded the world map shaders! [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Loading exposure filters: [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:light_blue_pane, exposure:shaders/light_blue_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:orange_pane, exposure:shaders/orange_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:red_pane, exposure:shaders/red_filter.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:purple_pane, exposure:shaders/purple_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:blue_pane, exposure:shaders/blue_filter.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:light_gray_pane, exposure:shaders/light_gray_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:magenta_pane, exposure:shaders/magenta_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:gray_pane, exposure:shaders/gray_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:lime_pane, exposure:shaders/lime_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:green_pane, exposure:shaders/green_filter.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:pink_pane, exposure:shaders/pink_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:yellow_pane, exposure:shaders/yellow_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:white_pane, exposure:shaders/white_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:brown_pane, exposure:shaders/brown_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:glass_pane, exposure:shaders/crisp.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:interplanar_projector, exposure:shaders/invert.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:black_pane, exposure:shaders/black_tint.json] added. [12:48:00] [Render thread/INFO] [io.gi.mo.ex.Exposure/]: Filter [exposure:cyan_pane, exposure:shaders/cyan_tint.json] added. [12:48:00] [Render thread/INFO] [patchouli/]: BookContentResourceListenerLoader preloaded 1073 jsons [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 128x128x0 computercraft:textures/atlas/gui.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 512x256x0 polylib:textures/atlas/gui.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 256x128x0 jei:textures/atlas/gui.png-atlas [12:48:00] [Render thread/INFO] [minecraft/TextureAtlas]: Created: 256x256x0 moonlight:textures/atlas/map_markers.png-atlas [12:48:00] [Render thread/INFO] [xa.hu.mi.MinimapLogs/]: Successfully reloaded the minimap shaders! [12:48:00] [Render thread/INFO] [Shimmer/]: buildIn shimmer configuration is enabled, this can be disabled by config file [12:48:00] [Render thread/INFO] [Shimmer/]: mod jar and resource pack discovery: file managed my minecraft located in [sourceName:KubeJS Resource Pack [assets],location:KubeJS Resource Pack [assets]] [12:48:00] [Render thread/ERROR] [Shimmer/]: can't find block framedblocks:framed_gate_door from file managed my minecraft located in [sourceName:KubeJS Resource Pack [assets],location:KubeJS Resource Pack [assets]] [12:48:00] [Render thread/ERROR] [Shimmer/]: can't find block framedblocks:framed_iron_gate_door from file managed my minecraft located in [sourceName:KubeJS Resource Pack [assets],location:KubeJS Resource Pack [assets]] [12:48:00] [Render thread/ERROR] [Shimmer/]: can't find block framedblocks:framed_gate_door from file managed my minecraft located in [sourceName:KubeJS Resource Pack [assets],location:KubeJS Resource Pack [assets]] [12:48:00] [Render thread/ERROR] [Shimmer/]: can't find block framedblocks:framed_iron_gate_door from file managed my minecraft located in [sourceName:KubeJS Resource Pack [assets],location:KubeJS Resource Pack [assets]] [12:48:00] [Render thread/INFO] [de.ke.fa.ut.re.ResourceHandlers/]: [FANCYMENU] Reloading resources.. [12:48:00] [Render thread/INFO] [de.ke.fa.ut.re.pr.ResourcePreLoader/]: [FANCYMENU] Pre-loading resources.. [12:48:00] [Render thread/INFO] [de.ke.fa.cu.la.ScreenCustomizationLayerHandler/]: [FANCYMENU] Updating animation sizes.. [12:48:00] [Render thread/INFO] [de.ke.fa.cu.la.ScreenCustomizationLayerHandler/]: [FANCYMENU] Minecraft resource reload: FINISHED [12:48:00] [Render thread/INFO] [de.ke.fa.cu.la.ScreenCustomizationLayerHandler/]: [FANCYMENU] ScreenCustomizationLayer registered: title_screen [12:48:00] [Render thread/INFO] [Oculus/]: Creating pipeline for dimension NamespacedId{namespace='minecraft', name='overworld'} [12:48:01] [Render thread/INFO] [ambientsounds/]: Loaded AmbientEngine 'basic' v3.1.0. 11 dimension(s), 11 features, 11 blockgroups, 2 sound collections, 37 regions, 58 sounds, 11 sound categories, 5 solids and 2 biome types [12:48:01] [Render thread/INFO] [FirstPersonModel/]: PlayerAnimator not found! [12:48:01] [Render thread/INFO] [FirstPersonModel/]: Loaded Vanilla Hands items: [] [12:48:01] [Render thread/INFO] [FirstPersonModel/]: Loaded Auto Disable items: [camera] [12:48:02] [Render thread/WARN] [ModernFix/]: Game took 40.304 seconds to start PrismLauncher-11.0.3/tests/testdata/TestLogs/vanilla-1.21.5.text.log0000644000175100017510000000462515224505336024347 0ustar runnerrunner[12:50:56] [Datafixer Bootstrap/INFO]: 263 Datafixer optimizations took 908 milliseconds [12:50:58] [Render thread/INFO]: Environment: Environment[sessionHost=https://sessionserver.mojang.com, servicesHost=https://api.minecraftservices.com, name=PROD] [12:50:58] [Render thread/INFO]: Setting user: Ryexandrite [12:50:58] [Render thread/INFO]: Backend library: LWJGL version 3.3.3+5 [12:50:58] [Render thread/INFO]: Using optional rendering extensions: GL_KHR_debug, GL_ARB_vertex_attrib_binding, GL_ARB_direct_state_access [12:50:58] [Render thread/INFO]: Reloading ResourceManager: vanilla [12:50:59] [Worker-Main-6/INFO]: Found unifont_all_no_pua-16.0.01.hex, loading [12:50:59] [Worker-Main-7/INFO]: Found unifont_jp_patch-16.0.01.hex, loading [12:50:59] [Render thread/WARN]: minecraft:pipeline/entity_translucent_emissive shader program does not use sampler Sampler2 defined in the pipeline. This might be a bug. [12:50:59] [Render thread/INFO]: OpenAL initialized on device Starship/Matisse HD Audio Controller Analog Stereo [12:50:59] [Render thread/INFO]: Sound engine started [12:50:59] [Render thread/INFO]: Created: 1024x512x4 minecraft:textures/atlas/blocks.png-atlas [12:50:59] [Render thread/INFO]: Created: 256x256x4 minecraft:textures/atlas/signs.png-atlas [12:50:59] [Render thread/INFO]: Created: 512x512x4 minecraft:textures/atlas/banner_patterns.png-atlas [12:50:59] [Render thread/INFO]: Created: 512x512x4 minecraft:textures/atlas/shield_patterns.png-atlas [12:50:59] [Render thread/INFO]: Created: 2048x1024x4 minecraft:textures/atlas/armor_trims.png-atlas [12:50:59] [Render thread/INFO]: Created: 256x256x4 minecraft:textures/atlas/chest.png-atlas [12:50:59] [Render thread/INFO]: Created: 128x64x4 minecraft:textures/atlas/decorated_pot.png-atlas [12:50:59] [Render thread/INFO]: Created: 512x256x4 minecraft:textures/atlas/beds.png-atlas [12:50:59] [Render thread/INFO]: Created: 512x256x4 minecraft:textures/atlas/shulker_boxes.png-atlas [12:50:59] [Render thread/INFO]: Created: 64x64x0 minecraft:textures/atlas/map_decorations.png-atlas [12:50:59] [Render thread/INFO]: Created: 512x256x0 minecraft:textures/atlas/particles.png-atlas [12:51:00] [Render thread/INFO]: Created: 512x256x0 minecraft:textures/atlas/paintings.png-atlas [12:51:00] [Render thread/INFO]: Created: 256x128x0 minecraft:textures/atlas/mob_effects.png-atlas [12:51:00] [Render thread/INFO]: Created: 1024x512x0 minecraft:textures/atlas/gui.png-atlas PrismLauncher-11.0.3/tests/testdata/FileSystem/0000755000175100017510000000000015224505336021022 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/FileSystem/FileSystem-test_createShortcut-unix0000755000175100017510000000013615224505336030051 0ustar runnerrunner[Desktop Entry] Type=Application TryExec=asdfDest Exec=asdfDest 'arg1' 'arg2' Name=asdf Icon= PrismLauncher-11.0.3/tests/testdata/FileSystem/test_folder/0000755000175100017510000000000015224505336023334 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/FileSystem/test_folder/pack.mcmeta0000644000175100017510000000013015224505336025434 0ustar runnerrunner{ "pack": { "pack_format": 1, "description": "Some resource pack maybe" } } PrismLauncher-11.0.3/tests/testdata/FileSystem/test_folder/pack.nfo0000644000175100017510000000000215224505336024746 0ustar runnerrunner PrismLauncher-11.0.3/tests/testdata/FileSystem/test_folder/.secret_folder/0000755000175100017510000000000015224505336026232 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/FileSystem/test_folder/.secret_folder/.secret_file.txt0000644000175100017510000000003515224505336031333 0ustar runnerrunneroooooo spooky easter egg :oo PrismLauncher-11.0.3/tests/testdata/FileSystem/test_folder/assets/0000755000175100017510000000000015224505336024636 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/FileSystem/test_folder/assets/minecraft/0000755000175100017510000000000015224505336026606 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/FileSystem/test_folder/assets/minecraft/textures/0000755000175100017510000000000015224505336030471 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/FileSystem/test_folder/assets/minecraft/textures/blah.txt0000644000175100017510000000000215224505336032130 0ustar runnerrunner PrismLauncher-11.0.3/tests/testdata/Resources/0000755000175100017510000000000015224505336020710 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/Resources/test_folder/0000755000175100017510000000000015224505336023222 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/Resources/test_folder/pack.mcmeta0000644000175100017510000000013015224505336025322 0ustar runnerrunner{ "pack": { "pack_format": 1, "description": "Some resource pack maybe" } } PrismLauncher-11.0.3/tests/testdata/Resources/test_folder/pack.nfo0000644000175100017510000000000215224505336024634 0ustar runnerrunner PrismLauncher-11.0.3/tests/testdata/Resources/test_folder/assets/0000755000175100017510000000000015224505336024524 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/Resources/test_folder/assets/minecraft/0000755000175100017510000000000015224505336026474 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/Resources/test_folder/assets/minecraft/textures/0000755000175100017510000000000015224505336030357 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/Resources/test_folder/assets/minecraft/textures/blah.txt0000644000175100017510000000000215224505336032016 0ustar runnerrunner PrismLauncher-11.0.3/tests/testdata/Resources/another_test_folder/0000755000175100017510000000000015224505336024742 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/Resources/another_test_folder/pack.mcmeta0000644000175100017510000000022715224505336027051 0ustar runnerrunner{ "pack": { "pack_format": 6, "description": "o quartel pegou fogo, policia deu sinal, acode acode acode a bandeira nacional" } } PrismLauncher-11.0.3/tests/testdata/Resources/test_resource_pack_idk.zip0000644000175100017510000000144415224505336026152 0ustar runnerrunnerPK3vUß G –Ë pack.mcmetaUT ±Mc±Mcux ééEŽ1Â0 Eg*õnæll\…4E;ehÅi˜Ê5r1L‹„ô‡ÿŸÿ—<·ÍÎdç¯æ³ú-â芲½]aÄ3æ‚”š1BO(†€—ú"ðÁ1Ód¡p} ÜGW˜þ…D�Ù%“'�ð�{7Ô0�oº± KµE10œPü–=ôaú²“R”®3úß³mTPK4�˜Uassets/PK4�˜Uassets/minecraft/PK4�˜Uassets/minecraft/textures/PKÖAVUC߈4"assets/minecraft/textures/blah.txtSàPK3vUß G –Ë ¤�pack.mcmetaUT±Mcux ééPK?4�˜UíAÛassets/PK?4�˜UíAassets/minecraft/PK?4�˜UíA3assets/minecraft/textures/PK?ÖAVUC߈4"¤�massets/minecraft/textures/blah.txtPK]±PrismLauncher-11.0.3/tests/testdata/Resources/supercoolmod.jar0000644000175100017510000000001615224505336024116 0ustar runnerrunnerthe best mod. PrismLauncher-11.0.3/tests/testdata/DataPackParse/0000755000175100017510000000000015224505336021401 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/DataPackParse/test_data_pack_boogaloo.zip0000644000175100017510000000160215224505336026753 0ustar runnerrunnerPKm ‰Udata/PKm ‰U data/dummy/PKm ‰Udata/dummy/tags/PKm ‰Udata/dummy/tags/item/PKm ‰Udata/dummy/tags/item/foo_proof/PKÓ�‰U'data/dummy/tags/item/foo_proof/bar.jsonPK0Ÿ‰U˜6\PLZ pack.mcmeta«æRPP*HLÎV²R¨²¡¼ø´ü¢ÜÄ  ‰D4%µ8¹(³ $3?(ªœŸ›ª�’X’¨R®`¤�”ŸŸ”ž˜“Ÿ¯T_ËUËPK?m ‰UíAdata/PK?m ‰U íA#data/dummy/PK?m ‰UíALdata/dummy/tags/PK?m ‰UíAzdata/dummy/tags/item/PK?m ‰UíA­data/dummy/tags/item/foo_proof/PK?Ó�‰U'¤�êdata/dummy/tags/item/foo_proof/bar.jsonPK?0Ÿ‰U˜6\PLZ ¤�/pack.mcmetaPKȤPrismLauncher-11.0.3/tests/testdata/DataPackParse/test_folder/0000755000175100017510000000000015224505336023713 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/DataPackParse/test_folder/pack.mcmeta0000644000175100017510000000012615224505336026020 0ustar runnerrunner{ "pack": { "pack_format": 10, "description": "Some data pack, maybe" } } PrismLauncher-11.0.3/tests/testdata/DataPackParse/test_folder/data/0000755000175100017510000000000015224505336024624 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/DataPackParse/test_folder/data/dummy/0000755000175100017510000000000015224505336025757 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/DataPackParse/test_folder/data/dummy/tags/0000755000175100017510000000000015224505336026715 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/DataPackParse/test_folder/data/dummy/tags/item/0000755000175100017510000000000015224505336027653 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/DataPackParse/test_folder/data/dummy/tags/item/foo_proof/0000755000175100017510000000000015224505336031643 5ustar runnerrunner././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootPrismLauncher-11.0.3/tests/testdata/DataPackParse/test_folder/data/dummy/tags/item/foo_proof/bar.jsonPrismLauncher-11.0.3/tests/testdata/DataPackParse/test_folder/data/dummy/tags/item/foo_proof/bar.jso0000644000175100017510000000000215224505336033114 0ustar runnerrunner{}PrismLauncher-11.0.3/tests/testdata/DataPackParse/another_test_folder/0000755000175100017510000000000015224505336025433 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/DataPackParse/another_test_folder/pack.mcmeta0000644000175100017510000000015015224505336027535 0ustar runnerrunner{ "pack": { "pack_format": 6, "description": "Some data pack three, leaves on the tree" } } PrismLauncher-11.0.3/tests/testdata/DataPackParse/another_test_folder/data/0000755000175100017510000000000015224505336026344 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/DataPackParse/another_test_folder/data/dummy/0000755000175100017510000000000015224505336027477 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/DataPackParse/another_test_folder/data/dummy/tags/0000755000175100017510000000000015224505336030435 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/DataPackParse/another_test_folder/data/dummy/tags/item/0000755000175100017510000000000015224505336031373 5ustar runnerrunner././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootPrismLauncher-11.0.3/tests/testdata/DataPackParse/another_test_folder/data/dummy/tags/item/foo_proof/PrismLauncher-11.0.3/tests/testdata/DataPackParse/another_test_folder/data/dummy/tags/item/foo_proof0000755000175100017510000000000015224505336033304 5ustar runnerrunner././@LongLink0000644000000000000000000000015600000000000011605 Lustar rootrootPrismLauncher-11.0.3/tests/testdata/DataPackParse/another_test_folder/data/dummy/tags/item/foo_proof/bar.jsonPrismLauncher-11.0.3/tests/testdata/DataPackParse/another_test_folder/data/dummy/tags/item/foo_proof0000644000175100017510000000000215224505336033276 0ustar runnerrunner{}PrismLauncher-11.0.3/tests/testdata/MetaComponentParse/0000755000175100017510000000000015224505336022502 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/MetaComponentParse/component_with_link.json0000644000175100017510000000046515224505336027454 0ustar runnerrunner{ "description": [ { "text": "Hello, Component!", "clickEvent": { "action": "open_url", "value": "https://google.com" } } ], "expected_output": "Hello, Component!" } PrismLauncher-11.0.3/tests/testdata/MetaComponentParse/component_with_mixed.json0000644000175100017510000000315415224505336027623 0ustar runnerrunner{ "description": [ { "text": "The quick ", "color": "blue", "italic": true }, { "text": "brown fox ", "color": "#873600", "bold": true, "underlined": true, "extra": [ { "text": "jumped over ", "color": "blue", "bold": false, "underlined": false, "italic": true, "strikethrough": true } ] }, { "text": "the lazy dog's back. ", "color": "green", "bold": true, "italic": true, "underlined": true, "strikethrough": true, "extra": [ { "text": "1234567890 ", "color": "black", "strikethrough": false, "extra": [ "How vexingly quick daft zebras jump!" ] } ] } ], "expected_output": "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" } PrismLauncher-11.0.3/tests/testdata/MetaComponentParse/component_basic.json0000644000175100017510000000021715224505336026540 0ustar runnerrunner{ "description": [ { "text": "Hello, Component!" } ], "expected_output": "Hello, Component!" } PrismLauncher-11.0.3/tests/testdata/MetaComponentParse/component_with_extra.json0000644000175100017510000000114015224505336027631 0ustar runnerrunner{ "description": [ { "text": "Hello, ", "color": "red", "bold": true, "italic": true, "extra": [ { "extra": [ "Component!" ], "bold": false, "italic": false } ] } ], "expected_output": "Hello, Component!" }PrismLauncher-11.0.3/tests/testdata/MetaComponentParse/component_with_format.json0000644000175100017510000000060115224505336027777 0ustar runnerrunner{ "description": [ { "text": "Hello, Component!", "color": "blue", "bold": true, "italic": true, "underlined": true, "strikethrough": true } ], "expected_output": "Hello, Component!" }PrismLauncher-11.0.3/tests/testdata/WorldSaveParse/0000755000175100017510000000000015224505336021637 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/WorldSaveParse/minecraft_save_3/0000755000175100017510000000000015224505336025047 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/WorldSaveParse/minecraft_save_3/world_3/0000755000175100017510000000000015224505336026420 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/WorldSaveParse/minecraft_save_3/world_3/level.dat0000644000175100017510000000000015224505336030207 0ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/WorldSaveParse/minecraft_save_4/0000755000175100017510000000000015224505336025050 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/WorldSaveParse/minecraft_save_4/saves/0000755000175100017510000000000015224505336026171 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/WorldSaveParse/minecraft_save_4/saves/world_4/0000755000175100017510000000000015224505336027543 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/WorldSaveParse/minecraft_save_4/saves/world_4/level.dat0000644000175100017510000000000015224505336031332 0ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/WorldSaveParse/minecraft_save_2.zip0000644000175100017510000000054015224505336025571 0ustar runnerrunnerPK €˜Usaves/world_2/UT Ü…§c鉧cux èèPK €˜Usaves/world_2/level.datUT Ü…§cÜ…§cux èèPK €˜UíAsaves/world_2/UTÜ…§cux èèPK €˜U¤�Hsaves/world_2/level.datUTÜ…§cux èèPK±™PrismLauncher-11.0.3/tests/testdata/WorldSaveParse/minecraft_save_1.zip0000644000175100017510000000027015224505336025570 0ustar runnerrunnerPK ©€˜Uworld_1/level.datUT ®…§c®…§cux èèPK ©€˜U¤�world_1/level.datUT®…§cux èèPKWKPrismLauncher-11.0.3/tests/testdata/CatPacks/0000755000175100017510000000000015224505336020427 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/CatPacks/index.json0000644000175100017510000000205415224505336022432 0ustar runnerrunner{ "name": "My Cute Cat", "default": "maxwell.png", "variants": [ { "startTime": { "day": 12, "month": 4 }, "endTime": { "day": 12, "month": 4 }, "path": "oneDay.png" }, { "startTime": { "day": 20, "month": 12 }, "endTime": { "day": 28, "month": 12 }, "path": "christmas.png" }, { "startTime": { "day": 30, "month": 12 }, "endTime": { "day": 1, "month": 1 }, "path": "newyear2.png" }, { "startTime": { "day": 28, "month": 12 }, "endTime": { "day": 3, "month": 1 }, "path": "newyear.png" } ] } PrismLauncher-11.0.3/tests/testdata/TexturePackParse/0000755000175100017510000000000015224505336022170 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/TexturePackParse/test_texturefolder/0000755000175100017510000000000015224505336026123 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/TexturePackParse/test_texturefolder/pack.txt0000644000175100017510000000003015224505336027573 0ustar runnerrunnerSome texture pack surelyPrismLauncher-11.0.3/tests/testdata/TexturePackParse/test_texturefolder/assets/0000755000175100017510000000000015224505336027425 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/TexturePackParse/test_texturefolder/assets/minecraft/0000755000175100017510000000000015224505336031375 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/TexturePackParse/test_texturefolder/assets/minecraft/textures/0000755000175100017510000000000015224505336033260 5ustar runnerrunner././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootrootPrismLauncher-11.0.3/tests/testdata/TexturePackParse/test_texturefolder/assets/minecraft/textures/blah.txtPrismLauncher-11.0.3/tests/testdata/TexturePackParse/test_texturefolder/assets/minecraft/textures/bl0000644000175100017510000000000215224505336033570 0ustar runnerrunner PrismLauncher-11.0.3/tests/testdata/TexturePackParse/another_test_texturefolder/0000755000175100017510000000000015224505336027643 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/TexturePackParse/another_test_texturefolder/pack.txt0000644000175100017510000000002015224505336031312 0ustar runnerrunnerquieres for realPrismLauncher-11.0.3/tests/testdata/TexturePackParse/test_texture_pack_idk.zip0000644000175100017510000000027015224505336027277 0ustar runnerrunnerPK ·t$UÐ bpack.txtUT œc!œcux èèjoe biden, wake upPK ·t$UÐ b¤�pack.txtUTœcux èèPKNTPrismLauncher-11.0.3/tests/testdata/PackageManifest/0000755000175100017510000000000015224505336021760 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/PackageManifest/inspect/0000755000175100017510000000000015224505336023425 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/PackageManifest/inspect/a/0000755000175100017510000000000015224505336023645 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/PackageManifest/inspect/a/b.txt0000755000175100017510000000000015224505336024620 0ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/PackageManifest/inspect/a/b/0000755000175100017510000000000015224505336024066 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/PackageManifest/inspect/a/b/b.txt0000777000175100017510000000000015224505336026242 2../b.txtustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/PackageManifest/1.8.0_202-x64.json0000644000175100017510000060172315224505336024332 0ustar runnerrunner{ "files": { "COPYRIGHT": { "downloads": { "lzma": { "sha1": "dd860e040807f7e53ae89da5f28dd73d57ac605d", "size": 1431, "url": "https://launcher.mojang.com/v1/objects/dd860e040807f7e53ae89da5f28dd73d57ac605d/COPYRIGHT" }, "raw": { "sha1": "c725183c757011e7ba96c83c1e86ee7e8b516a2b", "size": 3244, "url": "https://launcher.mojang.com/v1/objects/c725183c757011e7ba96c83c1e86ee7e8b516a2b/COPYRIGHT" } }, "executable": false, "type": "file" }, "LICENSE": { "downloads": { "raw": { "sha1": "3e86865deec0814c958bcf7fb87f790bccc0e8bd", "size": 40, "url": "https://launcher.mojang.com/v1/objects/3e86865deec0814c958bcf7fb87f790bccc0e8bd/LICENSE" } }, "executable": false, "type": "file" }, "README": { "downloads": { "raw": { "sha1": "f90331df1e5badeadc501d8dd70714c62a920204", "size": 46, "url": "https://launcher.mojang.com/v1/objects/f90331df1e5badeadc501d8dd70714c62a920204/README" } }, "executable": false, "type": "file" }, "THIRDPARTYLICENSEREADME-JAVAFX.txt": { "downloads": { "lzma": { "sha1": "4fee85109d7ff04b982d0576dabd15397f599125", "size": 15455, "url": "https://launcher.mojang.com/v1/objects/4fee85109d7ff04b982d0576dabd15397f599125/THIRDPARTYLICENSEREADME-JAVAFX.txt" }, "raw": { "sha1": "56ff42f87607b997b52ae0ef8bf315e36932e870", "size": 112724, "url": "https://launcher.mojang.com/v1/objects/56ff42f87607b997b52ae0ef8bf315e36932e870/THIRDPARTYLICENSEREADME-JAVAFX.txt" } }, "executable": false, "type": "file" }, "THIRDPARTYLICENSEREADME.txt": { "downloads": { "lzma": { "sha1": "419c1414ba46ae9dbfd38cf4e0601fff61644429", "size": 32266, "url": "https://launcher.mojang.com/v1/objects/419c1414ba46ae9dbfd38cf4e0601fff61644429/THIRDPARTYLICENSEREADME.txt" }, "raw": { "sha1": "b83c3f32261de3e48ccd20614a11e066b1ec9027", "size": 153824, "url": "https://launcher.mojang.com/v1/objects/b83c3f32261de3e48ccd20614a11e066b1ec9027/THIRDPARTYLICENSEREADME.txt" } }, "executable": false, "type": "file" }, "Welcome.html": { "downloads": { "lzma": { "sha1": "01c21a74b4aafb7cbe0388233c43cbdf77dcaaea", "size": 528, "url": "https://launcher.mojang.com/v1/objects/01c21a74b4aafb7cbe0388233c43cbdf77dcaaea/Welcome.html" }, "raw": { "sha1": "d98ae54f03dac87419abc19b97e315830c2da55f", "size": 955, "url": "https://launcher.mojang.com/v1/objects/d98ae54f03dac87419abc19b97e315830c2da55f/Welcome.html" } }, "executable": false, "type": "file" }, "bin": { "type": "directory" }, "bin/ControlPanel": { "target": "jcontrol", "type": "link" }, "bin/java": { "downloads": { "lzma": { "sha1": "3857eea1d59e1bc545c67a753ed2768254807b8a", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/3857eea1d59e1bc545c67a753ed2768254807b8a/java" }, "raw": { "sha1": "3d20560fb5d1a49cb689c2226972e92e06d27ba6", "size": 8464, "url": "https://launcher.mojang.com/v1/objects/3d20560fb5d1a49cb689c2226972e92e06d27ba6/java" } }, "executable": true, "type": "file" }, "bin/javaws": { "downloads": { "lzma": { "sha1": "a6bec5c049e76c4488294a256a2084ea23ddb440", "size": 38173, "url": "https://launcher.mojang.com/v1/objects/a6bec5c049e76c4488294a256a2084ea23ddb440/javaws" }, "raw": { "sha1": "955c0f0066e2f893b0c2b3ccd83e223722e4ab74", "size": 140296, "url": "https://launcher.mojang.com/v1/objects/955c0f0066e2f893b0c2b3ccd83e223722e4ab74/javaws" } }, "executable": true, "type": "file" }, "bin/jcontrol": { "downloads": { "lzma": { "sha1": "40c5e33748f252e1d950b579a4185ab2c23fc908", "size": 2166, "url": "https://launcher.mojang.com/v1/objects/40c5e33748f252e1d950b579a4185ab2c23fc908/jcontrol" }, "raw": { "sha1": "ed541733c8b51e34349c1f8010b277e58ad73f1e", "size": 6264, "url": "https://launcher.mojang.com/v1/objects/ed541733c8b51e34349c1f8010b277e58ad73f1e/jcontrol" } }, "executable": true, "type": "file" }, "bin/jjs": { "downloads": { "lzma": { "sha1": "d44d1ac421979f7671921986214812095a5b0e3b", "size": 2168, "url": "https://launcher.mojang.com/v1/objects/d44d1ac421979f7671921986214812095a5b0e3b/jjs" }, "raw": { "sha1": "f00f944c3dbe556793b5dc686aaeee3e5722e99b", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/f00f944c3dbe556793b5dc686aaeee3e5722e99b/jjs" } }, "executable": true, "type": "file" }, "bin/keytool": { "downloads": { "lzma": { "sha1": "93c607dce450976667c382f609a367167bdec05c", "size": 2175, "url": "https://launcher.mojang.com/v1/objects/93c607dce450976667c382f609a367167bdec05c/keytool" }, "raw": { "sha1": "7114b561546270e441e9ed1bcc24e5188c068a42", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/7114b561546270e441e9ed1bcc24e5188c068a42/keytool" } }, "executable": true, "type": "file" }, "bin/orbd": { "downloads": { "lzma": { "sha1": "b27dfded5e2b2f6f02c555971c94e46ca14ac81b", "size": 2254, "url": "https://launcher.mojang.com/v1/objects/b27dfded5e2b2f6f02c555971c94e46ca14ac81b/orbd" }, "raw": { "sha1": "7f31217fecb3dbbd89f1dd3783fca58793a66fd2", "size": 8656, "url": "https://launcher.mojang.com/v1/objects/7f31217fecb3dbbd89f1dd3783fca58793a66fd2/orbd" } }, "executable": true, "type": "file" }, "bin/pack200": { "downloads": { "lzma": { "sha1": "b52da4497b49b1508b6225a5740857ddb8f52e97", "size": 2183, "url": "https://launcher.mojang.com/v1/objects/b52da4497b49b1508b6225a5740857ddb8f52e97/pack200" }, "raw": { "sha1": "16ef3e801efb57e50bc6477a27a9d95d02d0775b", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/16ef3e801efb57e50bc6477a27a9d95d02d0775b/pack200" } }, "executable": true, "type": "file" }, "bin/policytool": { "downloads": { "lzma": { "sha1": "87da4c07da45f3d1a1a9d732af197cd39bf69d10", "size": 2182, "url": "https://launcher.mojang.com/v1/objects/87da4c07da45f3d1a1a9d732af197cd39bf69d10/policytool" }, "raw": { "sha1": "a52a29424470cb9b8db5c2fb1751d0b697a7ec8e", "size": 8592, "url": "https://launcher.mojang.com/v1/objects/a52a29424470cb9b8db5c2fb1751d0b697a7ec8e/policytool" } }, "executable": true, "type": "file" }, "bin/rmid": { "downloads": { "lzma": { "sha1": "1494c1174fde0c0a93ea117bc7edf7eb936c0512", "size": 2172, "url": "https://launcher.mojang.com/v1/objects/1494c1174fde0c0a93ea117bc7edf7eb936c0512/rmid" }, "raw": { "sha1": "5c8710e1ab924e5b09a07bcb4c6e106293bbd1a8", "size": 8584, "url": "https://launcher.mojang.com/v1/objects/5c8710e1ab924e5b09a07bcb4c6e106293bbd1a8/rmid" } }, "executable": true, "type": "file" }, "bin/rmiregistry": { "downloads": { "lzma": { "sha1": "7070cf2ec5a5e520a880bae699431edf02083e7e", "size": 2174, "url": "https://launcher.mojang.com/v1/objects/7070cf2ec5a5e520a880bae699431edf02083e7e/rmiregistry" }, "raw": { "sha1": "5f518daa7050028d5d9d849634c73136f2b23a54", "size": 8592, "url": "https://launcher.mojang.com/v1/objects/5f518daa7050028d5d9d849634c73136f2b23a54/rmiregistry" } }, "executable": true, "type": "file" }, "bin/servertool": { "downloads": { "lzma": { "sha1": "1db683a11cc9b7313426c84412f4d95be2fa7ccd", "size": 2185, "url": "https://launcher.mojang.com/v1/objects/1db683a11cc9b7313426c84412f4d95be2fa7ccd/servertool" }, "raw": { "sha1": "49d0ebfeb265ce5a8733e1014541ea2525674a60", "size": 8592, "url": "https://launcher.mojang.com/v1/objects/49d0ebfeb265ce5a8733e1014541ea2525674a60/servertool" } }, "executable": true, "type": "file" }, "bin/tnameserv": { "downloads": { "lzma": { "sha1": "36da9c9a2c5a8b662a3f8d52ca67339bce1c2714", "size": 2291, "url": "https://launcher.mojang.com/v1/objects/36da9c9a2c5a8b662a3f8d52ca67339bce1c2714/tnameserv" }, "raw": { "sha1": "09d998f8efcb6f55d0d87f59e08f8b89662796d9", "size": 8656, "url": "https://launcher.mojang.com/v1/objects/09d998f8efcb6f55d0d87f59e08f8b89662796d9/tnameserv" } }, "executable": true, "type": "file" }, "bin/unpack200": { "downloads": { "lzma": { "sha1": "344959e32fc7ee19eebe7b3cf5ab6d1a7d6641f2", "size": 79721, "url": "https://launcher.mojang.com/v1/objects/344959e32fc7ee19eebe7b3cf5ab6d1a7d6641f2/unpack200" }, "raw": { "sha1": "5dd933132f1b202e19e0c8e093f7113711cfdfc1", "size": 182616, "url": "https://launcher.mojang.com/v1/objects/5dd933132f1b202e19e0c8e093f7113711cfdfc1/unpack200" } }, "executable": true, "type": "file" }, "lib": { "type": "directory" }, "lib/amd64": { "type": "directory" }, "lib/amd64/jli": { "type": "directory" }, "lib/amd64/jli/libjli.so": { "downloads": { "lzma": { "sha1": "372331ee8e375888f798a2e88180a94493e141b0", "size": 48327, "url": "https://launcher.mojang.com/v1/objects/372331ee8e375888f798a2e88180a94493e141b0/libjli.so" }, "raw": { "sha1": "73b0cf8b7415686bc40c561ff77ff2740ccf7a44", "size": 108616, "url": "https://launcher.mojang.com/v1/objects/73b0cf8b7415686bc40c561ff77ff2740ccf7a44/libjli.so" } }, "executable": true, "type": "file" }, "lib/amd64/jvm.cfg": { "downloads": { "lzma": { "sha1": "86bcfebec37b38415525ffd77d3eaf70d0b1b4ca", "size": 435, "url": "https://launcher.mojang.com/v1/objects/86bcfebec37b38415525ffd77d3eaf70d0b1b4ca/jvm.cfg" }, "raw": { "sha1": "84b38bdc745de446ba0ca0232ea3aaf2efd721da", "size": 627, "url": "https://launcher.mojang.com/v1/objects/84b38bdc745de446ba0ca0232ea3aaf2efd721da/jvm.cfg" } }, "executable": false, "type": "file" }, "lib/amd64/libavplugin-53.so": { "downloads": { "lzma": { "sha1": "a332366762d9efc7b845a682b7edce62db44618c", "size": 14747, "url": "https://launcher.mojang.com/v1/objects/a332366762d9efc7b845a682b7edce62db44618c/libavplugin-53.so" }, "raw": { "sha1": "9bd1473dd8a0dc7950c7af1cc69a45548df26eb5", "size": 51720, "url": "https://launcher.mojang.com/v1/objects/9bd1473dd8a0dc7950c7af1cc69a45548df26eb5/libavplugin-53.so" } }, "executable": true, "type": "file" }, "lib/amd64/libavplugin-54.so": { "downloads": { "lzma": { "sha1": "2c615852a0720a275163e00597c1f711f11341da", "size": 15153, "url": "https://launcher.mojang.com/v1/objects/2c615852a0720a275163e00597c1f711f11341da/libavplugin-54.so" }, "raw": { "sha1": "8808050c5949c4800b42d1b19b1f8b0d120bcacb", "size": 51768, "url": "https://launcher.mojang.com/v1/objects/8808050c5949c4800b42d1b19b1f8b0d120bcacb/libavplugin-54.so" } }, "executable": true, "type": "file" }, "lib/amd64/libavplugin-55.so": { "downloads": { "lzma": { "sha1": "39ee8e7fe14f0010c78973962800f539c3e4c16b", "size": 15168, "url": "https://launcher.mojang.com/v1/objects/39ee8e7fe14f0010c78973962800f539c3e4c16b/libavplugin-55.so" }, "raw": { "sha1": "f10ea4ea3489e96d8d161a96790133c417ec44e1", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/f10ea4ea3489e96d8d161a96790133c417ec44e1/libavplugin-55.so" } }, "executable": true, "type": "file" }, "lib/amd64/libavplugin-56.so": { "downloads": { "lzma": { "sha1": "abe7feced5a559f1bdc868526dc69484e0e591a0", "size": 15169, "url": "https://launcher.mojang.com/v1/objects/abe7feced5a559f1bdc868526dc69484e0e591a0/libavplugin-56.so" }, "raw": { "sha1": "e5bfcbff5a5a5a5993a3e689a05ef358c131a3ed", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/e5bfcbff5a5a5a5993a3e689a05ef358c131a3ed/libavplugin-56.so" } }, "executable": true, "type": "file" }, "lib/amd64/libavplugin-57.so": { "downloads": { "lzma": { "sha1": "4dd26b4ef2294b6929dcb2c7546b47eac5cc78a9", "size": 15174, "url": "https://launcher.mojang.com/v1/objects/4dd26b4ef2294b6929dcb2c7546b47eac5cc78a9/libavplugin-57.so" }, "raw": { "sha1": "2949e7ff9b0ac90e8943c211cff141ab12eec3f8", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/2949e7ff9b0ac90e8943c211cff141ab12eec3f8/libavplugin-57.so" } }, "executable": true, "type": "file" }, "lib/amd64/libavplugin-ffmpeg-56.so": { "downloads": { "lzma": { "sha1": "c688ba1cfa442bf18bee43b2fa870b4dc1ce3fb6", "size": 15231, "url": "https://launcher.mojang.com/v1/objects/c688ba1cfa442bf18bee43b2fa870b4dc1ce3fb6/libavplugin-ffmpeg-56.so" }, "raw": { "sha1": "0d36c971a9ad99fc2292092fdec3a4179b1021b9", "size": 51920, "url": "https://launcher.mojang.com/v1/objects/0d36c971a9ad99fc2292092fdec3a4179b1021b9/libavplugin-ffmpeg-56.so" } }, "executable": true, "type": "file" }, "lib/amd64/libavplugin-ffmpeg-57.so": { "downloads": { "lzma": { "sha1": "087426bdbffebcfa372a438e863785f4ffbe9a6b", "size": 15180, "url": "https://launcher.mojang.com/v1/objects/087426bdbffebcfa372a438e863785f4ffbe9a6b/libavplugin-ffmpeg-57.so" }, "raw": { "sha1": "5e9c4eb4b49eb8e57c01003ec73a1eb8d6d8c462", "size": 51784, "url": "https://launcher.mojang.com/v1/objects/5e9c4eb4b49eb8e57c01003ec73a1eb8d6d8c462/libavplugin-ffmpeg-57.so" } }, "executable": true, "type": "file" }, "lib/amd64/libawt.so": { "downloads": { "lzma": { "sha1": "018be58b205b73c842a55df811b70d0e8237216e", "size": 195720, "url": "https://launcher.mojang.com/v1/objects/018be58b205b73c842a55df811b70d0e8237216e/libawt.so" }, "raw": { "sha1": "02632cd326e3161c00a7e784599dd7b9ee053dce", "size": 759184, "url": "https://launcher.mojang.com/v1/objects/02632cd326e3161c00a7e784599dd7b9ee053dce/libawt.so" } }, "executable": true, "type": "file" }, "lib/amd64/libawt_headless.so": { "downloads": { "lzma": { "sha1": "7ac2517cff75d4bbb0a0412a9b5f18c74ea188fa", "size": 11211, "url": "https://launcher.mojang.com/v1/objects/7ac2517cff75d4bbb0a0412a9b5f18c74ea188fa/libawt_headless.so" }, "raw": { "sha1": "862157ec957008d0911c5daedc004b3a202623a4", "size": 39176, "url": "https://launcher.mojang.com/v1/objects/862157ec957008d0911c5daedc004b3a202623a4/libawt_headless.so" } }, "executable": true, "type": "file" }, "lib/amd64/libawt_xawt.so": { "downloads": { "lzma": { "sha1": "d536a96af27dfe35de6bb2c8759d51c488cdd8d4", "size": 149598, "url": "https://launcher.mojang.com/v1/objects/d536a96af27dfe35de6bb2c8759d51c488cdd8d4/libawt_xawt.so" }, "raw": { "sha1": "28232b3e01b6f11bfe098bfc6eafc3a513dcebf1", "size": 470232, "url": "https://launcher.mojang.com/v1/objects/28232b3e01b6f11bfe098bfc6eafc3a513dcebf1/libawt_xawt.so" } }, "executable": true, "type": "file" }, "lib/amd64/libbci.so": { "downloads": { "lzma": { "sha1": "c36fad091d11e64c815d5ca17c0ef7a55b0776b1", "size": 3509, "url": "https://launcher.mojang.com/v1/objects/c36fad091d11e64c815d5ca17c0ef7a55b0776b1/libbci.so" }, "raw": { "sha1": "33824051db1ccb6332e22c2b63231055240d0af0", "size": 12760, "url": "https://launcher.mojang.com/v1/objects/33824051db1ccb6332e22c2b63231055240d0af0/libbci.so" } }, "executable": true, "type": "file" }, "lib/amd64/libdcpr.so": { "downloads": { "lzma": { "sha1": "70c6b0933a37f2b1124d6e7c131039241fe796ee", "size": 75969, "url": "https://launcher.mojang.com/v1/objects/70c6b0933a37f2b1124d6e7c131039241fe796ee/libdcpr.so" }, "raw": { "sha1": "fa7001bc5d80579e2716590f3eee8027da0beae7", "size": 204456, "url": "https://launcher.mojang.com/v1/objects/fa7001bc5d80579e2716590f3eee8027da0beae7/libdcpr.so" } }, "executable": true, "type": "file" }, "lib/amd64/libdecora_sse.so": { "downloads": { "lzma": { "sha1": "514acc017dfb6cefaf8cc6d18006ce55781cc9bc", "size": 24397, "url": "https://launcher.mojang.com/v1/objects/514acc017dfb6cefaf8cc6d18006ce55781cc9bc/libdecora_sse.so" }, "raw": { "sha1": "d0c84233504c916e548e29f513e25f6a7479abfc", "size": 74912, "url": "https://launcher.mojang.com/v1/objects/d0c84233504c916e548e29f513e25f6a7479abfc/libdecora_sse.so" } }, "executable": true, "type": "file" }, "lib/amd64/libdeploy.so": { "downloads": { "lzma": { "sha1": "6cf31fd98301c749ac0d2c7825f6d925a4409760", "size": 168999, "url": "https://launcher.mojang.com/v1/objects/6cf31fd98301c749ac0d2c7825f6d925a4409760/libdeploy.so" }, "raw": { "sha1": "b3832e97ed8ca794884b56a591b83d02a2c0c06f", "size": 642368, "url": "https://launcher.mojang.com/v1/objects/b3832e97ed8ca794884b56a591b83d02a2c0c06f/libdeploy.so" } }, "executable": true, "type": "file" }, "lib/amd64/libdt_socket.so": { "downloads": { "lzma": { "sha1": "4cc5c880dbb6fa180436d12d60f0abec8ebb59dc", "size": 7784, "url": "https://launcher.mojang.com/v1/objects/4cc5c880dbb6fa180436d12d60f0abec8ebb59dc/libdt_socket.so" }, "raw": { "sha1": "91ce96f252b8139fc12f0f224ed5b1a041767ab7", "size": 24616, "url": "https://launcher.mojang.com/v1/objects/91ce96f252b8139fc12f0f224ed5b1a041767ab7/libdt_socket.so" } }, "executable": true, "type": "file" }, "lib/amd64/libfontmanager.so": { "downloads": { "lzma": { "sha1": "f94e5e94c71c603ff4d3cd1e7e3d9e181fcc145d", "size": 146951, "url": "https://launcher.mojang.com/v1/objects/f94e5e94c71c603ff4d3cd1e7e3d9e181fcc145d/libfontmanager.so" }, "raw": { "sha1": "2428e805f2c53d1283a033dfd11a86fbb7bd7159", "size": 490672, "url": "https://launcher.mojang.com/v1/objects/2428e805f2c53d1283a033dfd11a86fbb7bd7159/libfontmanager.so" } }, "executable": true, "type": "file" }, "lib/amd64/libfxplugins.so": { "downloads": { "lzma": { "sha1": "a640143365d382a5ad743a784bc2f3706d9d6d67", "size": 50048, "url": "https://launcher.mojang.com/v1/objects/a640143365d382a5ad743a784bc2f3706d9d6d67/libfxplugins.so" }, "raw": { "sha1": "0fd4ac04a84c131f1aaee9e6b0898ff9ea69e3ee", "size": 151448, "url": "https://launcher.mojang.com/v1/objects/0fd4ac04a84c131f1aaee9e6b0898ff9ea69e3ee/libfxplugins.so" } }, "executable": true, "type": "file" }, "lib/amd64/libglass.so": { "downloads": { "lzma": { "sha1": "f1ff517714fa5f2c861f33b32db823fe851541f1", "size": 2856, "url": "https://launcher.mojang.com/v1/objects/f1ff517714fa5f2c861f33b32db823fe851541f1/libglass.so" }, "raw": { "sha1": "e7f4fece30ac727be8148d33b8256abd3a41cef9", "size": 13072, "url": "https://launcher.mojang.com/v1/objects/e7f4fece30ac727be8148d33b8256abd3a41cef9/libglass.so" } }, "executable": true, "type": "file" }, "lib/amd64/libglassgtk2.so": { "downloads": { "lzma": { "sha1": "15b90f7a2baacd15e80aa9785d87cf1e4258376d", "size": 220476, "url": "https://launcher.mojang.com/v1/objects/15b90f7a2baacd15e80aa9785d87cf1e4258376d/libglassgtk2.so" }, "raw": { "sha1": "e30a634c2ff2143bdee512360553d6e0304f33b2", "size": 844984, "url": "https://launcher.mojang.com/v1/objects/e30a634c2ff2143bdee512360553d6e0304f33b2/libglassgtk2.so" } }, "executable": true, "type": "file" }, "lib/amd64/libglassgtk3.so": { "downloads": { "lzma": { "sha1": "868c231165f8c9043b7f0e7de208ec023f06a6e7", "size": 220560, "url": "https://launcher.mojang.com/v1/objects/868c231165f8c9043b7f0e7de208ec023f06a6e7/libglassgtk3.so" }, "raw": { "sha1": "762a11a2b376b7b5a2a7cad780715524fdd176d5", "size": 845304, "url": "https://launcher.mojang.com/v1/objects/762a11a2b376b7b5a2a7cad780715524fdd176d5/libglassgtk3.so" } }, "executable": true, "type": "file" }, "lib/amd64/libglib-lite.so": { "downloads": { "lzma": { "sha1": "61b8871242febe1be262de167dc20ae94bf964b4", "size": 457046, "url": "https://launcher.mojang.com/v1/objects/61b8871242febe1be262de167dc20ae94bf964b4/libglib-lite.so" }, "raw": { "sha1": "63afa060fc3f120af76128e51d32603fc4336fa8", "size": 1538352, "url": "https://launcher.mojang.com/v1/objects/63afa060fc3f120af76128e51d32603fc4336fa8/libglib-lite.so" } }, "executable": true, "type": "file" }, "lib/amd64/libgstreamer-lite.so": { "downloads": { "lzma": { "sha1": "2447dc368406ba1b989a29937d41924620e01988", "size": 673056, "url": "https://launcher.mojang.com/v1/objects/2447dc368406ba1b989a29937d41924620e01988/libgstreamer-lite.so" }, "raw": { "sha1": "5505e7ca592ac64371d3db8fe53bcb602e9723d3", "size": 2263872, "url": "https://launcher.mojang.com/v1/objects/5505e7ca592ac64371d3db8fe53bcb602e9723d3/libgstreamer-lite.so" } }, "executable": true, "type": "file" }, "lib/amd64/libhprof.so": { "downloads": { "lzma": { "sha1": "94a5589c818db1fb1cf1881e24e217c309fce2e4", "size": 64471, "url": "https://launcher.mojang.com/v1/objects/94a5589c818db1fb1cf1881e24e217c309fce2e4/libhprof.so" }, "raw": { "sha1": "4bb9bdeef6133b6dd558d52d691b077c03e9b0ee", "size": 175504, "url": "https://launcher.mojang.com/v1/objects/4bb9bdeef6133b6dd558d52d691b077c03e9b0ee/libhprof.so" } }, "executable": true, "type": "file" }, "lib/amd64/libinstrument.so": { "downloads": { "lzma": { "sha1": "84ffea356caf725b42c86a8ebc9587f477ddde29", "size": 18603, "url": "https://launcher.mojang.com/v1/objects/84ffea356caf725b42c86a8ebc9587f477ddde29/libinstrument.so" }, "raw": { "sha1": "cb8009769601e3fecd7ea2b36c344f737b1a9da7", "size": 51560, "url": "https://launcher.mojang.com/v1/objects/cb8009769601e3fecd7ea2b36c344f737b1a9da7/libinstrument.so" } }, "executable": true, "type": "file" }, "lib/amd64/libj2gss.so": { "downloads": { "lzma": { "sha1": "4b2aa699506b126098b585a9617ce1c05707fa29", "size": 14132, "url": "https://launcher.mojang.com/v1/objects/4b2aa699506b126098b585a9617ce1c05707fa29/libj2gss.so" }, "raw": { "sha1": "cbce4a302b255d4d1924ef7606f038af766c5e86", "size": 47688, "url": "https://launcher.mojang.com/v1/objects/cbce4a302b255d4d1924ef7606f038af766c5e86/libj2gss.so" } }, "executable": true, "type": "file" }, "lib/amd64/libj2pcsc.so": { "downloads": { "lzma": { "sha1": "2361d3b2e3da48593c391b29b0d2b5409e4c55e5", "size": 5074, "url": "https://launcher.mojang.com/v1/objects/2361d3b2e3da48593c391b29b0d2b5409e4c55e5/libj2pcsc.so" }, "raw": { "sha1": "1274178492e7a3e997e12f67794616f7c3d8d0b9", "size": 18296, "url": "https://launcher.mojang.com/v1/objects/1274178492e7a3e997e12f67794616f7c3d8d0b9/libj2pcsc.so" } }, "executable": true, "type": "file" }, "lib/amd64/libj2pkcs11.so": { "downloads": { "lzma": { "sha1": "ef927e2790ba05931d0f0bdd63da3d275a834946", "size": 21573, "url": "https://launcher.mojang.com/v1/objects/ef927e2790ba05931d0f0bdd63da3d275a834946/libj2pkcs11.so" }, "raw": { "sha1": "bd4f2af9bfdc6168633d1920c1a1415de06bb45a", "size": 79472, "url": "https://launcher.mojang.com/v1/objects/bd4f2af9bfdc6168633d1920c1a1415de06bb45a/libj2pkcs11.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjaas_unix.so": { "downloads": { "lzma": { "sha1": "7f7e843544ee1eb1454a5826bdd4218685b79430", "size": 2404, "url": "https://launcher.mojang.com/v1/objects/7f7e843544ee1eb1454a5826bdd4218685b79430/libjaas_unix.so" }, "raw": { "sha1": "4c517925c7d464a5b719898eb0bea1b04df31f1f", "size": 8192, "url": "https://launcher.mojang.com/v1/objects/4c517925c7d464a5b719898eb0bea1b04df31f1f/libjaas_unix.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjava.so": { "downloads": { "lzma": { "sha1": "5eee7a42600a44a8bb8d6d7f510fd96a29637ac0", "size": 63113, "url": "https://launcher.mojang.com/v1/objects/5eee7a42600a44a8bb8d6d7f510fd96a29637ac0/libjava.so" }, "raw": { "sha1": "e280aeddf3fc0ec664aef7efc0e0e197a54aaf02", "size": 227672, "url": "https://launcher.mojang.com/v1/objects/e280aeddf3fc0ec664aef7efc0e0e197a54aaf02/libjava.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjava_crw_demo.so": { "downloads": { "lzma": { "sha1": "b197cf23ae3556eb0b45c663f0a8cb62408b961e", "size": 10412, "url": "https://launcher.mojang.com/v1/objects/b197cf23ae3556eb0b45c663f0a8cb62408b961e/libjava_crw_demo.so" }, "raw": { "sha1": "18f20f906977c90d0090b41dbda8dd5cfead5a4c", "size": 26144, "url": "https://launcher.mojang.com/v1/objects/18f20f906977c90d0090b41dbda8dd5cfead5a4c/libjava_crw_demo.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjavafx_font.so": { "downloads": { "lzma": { "sha1": "ffbba0e5022f829412b86063d8a90f95f16709b1", "size": 5608, "url": "https://launcher.mojang.com/v1/objects/ffbba0e5022f829412b86063d8a90f95f16709b1/libjavafx_font.so" }, "raw": { "sha1": "8634a0aca612fc40420a4a7cc8af4cc46cfc6725", "size": 17104, "url": "https://launcher.mojang.com/v1/objects/8634a0aca612fc40420a4a7cc8af4cc46cfc6725/libjavafx_font.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjavafx_font_freetype.so": { "downloads": { "lzma": { "sha1": "310271eda8a2ac264ffc3640a9d847b49438d0bd", "size": 6942, "url": "https://launcher.mojang.com/v1/objects/310271eda8a2ac264ffc3640a9d847b49438d0bd/libjavafx_font_freetype.so" }, "raw": { "sha1": "3e7572d047c12ba2bc43acec7f98a67c20af8042", "size": 27616, "url": "https://launcher.mojang.com/v1/objects/3e7572d047c12ba2bc43acec7f98a67c20af8042/libjavafx_font_freetype.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjavafx_font_pango.so": { "downloads": { "lzma": { "sha1": "a7bcf0669e70b0f43099a99c81e6b6440cb40ac0", "size": 5820, "url": "https://launcher.mojang.com/v1/objects/a7bcf0669e70b0f43099a99c81e6b6440cb40ac0/libjavafx_font_pango.so" }, "raw": { "sha1": "f0b775cc9a514c7ee8b4d6fb300653ce548caf10", "size": 25560, "url": "https://launcher.mojang.com/v1/objects/f0b775cc9a514c7ee8b4d6fb300653ce548caf10/libjavafx_font_pango.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjavafx_font_t2k.so": { "downloads": { "lzma": { "sha1": "551c29dc7c7fc83223aa36a6187f7e0c5d650538", "size": 431450, "url": "https://launcher.mojang.com/v1/objects/551c29dc7c7fc83223aa36a6187f7e0c5d650538/libjavafx_font_t2k.so" }, "raw": { "sha1": "91e5813057c3b852d411540160f8ad05fb9f1ed3", "size": 1486128, "url": "https://launcher.mojang.com/v1/objects/91e5813057c3b852d411540160f8ad05fb9f1ed3/libjavafx_font_t2k.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjavafx_iio.so": { "downloads": { "lzma": { "sha1": "c832998fd5e06ed6dcd6428816194c350785420c", "size": 101479, "url": "https://launcher.mojang.com/v1/objects/c832998fd5e06ed6dcd6428816194c350785420c/libjavafx_iio.so" }, "raw": { "sha1": "dcdf68cb25677b76c1cf0bb94294e6e9880a6678", "size": 256336, "url": "https://launcher.mojang.com/v1/objects/dcdf68cb25677b76c1cf0bb94294e6e9880a6678/libjavafx_iio.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjawt.so": { "downloads": { "lzma": { "sha1": "c1ced6aad5c69ff444dc67d0fd7e333558953831", "size": 1872, "url": "https://launcher.mojang.com/v1/objects/c1ced6aad5c69ff444dc67d0fd7e333558953831/libjawt.so" }, "raw": { "sha1": "c5032f2c6fa40bea24e56605cf76b26a27e87b67", "size": 8048, "url": "https://launcher.mojang.com/v1/objects/c5032f2c6fa40bea24e56605cf76b26a27e87b67/libjawt.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjdwp.so": { "downloads": { "lzma": { "sha1": "c1aabbb3f5a624b9ad10ed871a1d83510a99b646", "size": 94884, "url": "https://launcher.mojang.com/v1/objects/c1aabbb3f5a624b9ad10ed871a1d83510a99b646/libjdwp.so" }, "raw": { "sha1": "a043e97be47937f6f552e94cf79c76c1c57f9594", "size": 272248, "url": "https://launcher.mojang.com/v1/objects/a043e97be47937f6f552e94cf79c76c1c57f9594/libjdwp.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjfr.so": { "downloads": { "lzma": { "sha1": "11b8e6bfffdccbacbf9dd29dea4b90b753f3c1b7", "size": 8780, "url": "https://launcher.mojang.com/v1/objects/11b8e6bfffdccbacbf9dd29dea4b90b753f3c1b7/libjfr.so" }, "raw": { "sha1": "312392dd186b11c418183e818f1928e8685a07e5", "size": 28384, "url": "https://launcher.mojang.com/v1/objects/312392dd186b11c418183e818f1928e8685a07e5/libjfr.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjfxmedia.so": { "downloads": { "lzma": { "sha1": "a4e7a126eb648ce6e5e6dc151831da37d8334139", "size": 391897, "url": "https://launcher.mojang.com/v1/objects/a4e7a126eb648ce6e5e6dc151831da37d8334139/libjfxmedia.so" }, "raw": { "sha1": "5fa54944327a6012c3d34cb5c1c4432762178dc8", "size": 1636376, "url": "https://launcher.mojang.com/v1/objects/5fa54944327a6012c3d34cb5c1c4432762178dc8/libjfxmedia.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjfxwebkit.so": { "downloads": { "lzma": { "sha1": "b274debd222cdcc2ee84160ebb95144b3880bc97", "size": 20492825, "url": "https://launcher.mojang.com/v1/objects/b274debd222cdcc2ee84160ebb95144b3880bc97/libjfxwebkit.so" }, "raw": { "sha1": "ecee564c3b2f645131b35bb3004abd4caeabd291", "size": 91014584, "url": "https://launcher.mojang.com/v1/objects/ecee564c3b2f645131b35bb3004abd4caeabd291/libjfxwebkit.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjpeg.so": { "downloads": { "lzma": { "sha1": "9ad55e370c5eaaa73c3158339db3c368b1aaf0cb", "size": 113072, "url": "https://launcher.mojang.com/v1/objects/9ad55e370c5eaaa73c3158339db3c368b1aaf0cb/libjpeg.so" }, "raw": { "sha1": "651e6d53ae67db1f0efbf7f104447a9b49b7e333", "size": 292520, "url": "https://launcher.mojang.com/v1/objects/651e6d53ae67db1f0efbf7f104447a9b49b7e333/libjpeg.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjsdt.so": { "downloads": { "lzma": { "sha1": "04b6d1361a34c496b5f652b2477784d69b8b6baf", "size": 3964, "url": "https://launcher.mojang.com/v1/objects/04b6d1361a34c496b5f652b2477784d69b8b6baf/libjsdt.so" }, "raw": { "sha1": "82b48a82bf6183d34cf00a0f81661b45c616f31b", "size": 12904, "url": "https://launcher.mojang.com/v1/objects/82b48a82bf6183d34cf00a0f81661b45c616f31b/libjsdt.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjsig.so": { "downloads": { "lzma": { "sha1": "37d3b89abde397216cc4ecb1339d8543d99b8428", "size": 3536, "url": "https://launcher.mojang.com/v1/objects/37d3b89abde397216cc4ecb1339d8543d99b8428/libjsig.so" }, "raw": { "sha1": "42e52ba1bcbe0362ab24bcf65c93797354db6fb9", "size": 13336, "url": "https://launcher.mojang.com/v1/objects/42e52ba1bcbe0362ab24bcf65c93797354db6fb9/libjsig.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjsound.so": { "downloads": { "lzma": { "sha1": "7e3c565d74d8ffae716f32b05544fa4a6f108adc", "size": 2002, "url": "https://launcher.mojang.com/v1/objects/7e3c565d74d8ffae716f32b05544fa4a6f108adc/libjsound.so" }, "raw": { "sha1": "0c0fc63b92d7b83c9960fa80d45c80553ea20254", "size": 8232, "url": "https://launcher.mojang.com/v1/objects/0c0fc63b92d7b83c9960fa80d45c80553ea20254/libjsound.so" } }, "executable": true, "type": "file" }, "lib/amd64/libjsoundalsa.so": { "downloads": { "lzma": { "sha1": "b06c51858a25ff776519495f1b9b3d9f604b089f", "size": 23097, "url": "https://launcher.mojang.com/v1/objects/b06c51858a25ff776519495f1b9b3d9f604b089f/libjsoundalsa.so" }, "raw": { "sha1": "281d37f0326d4a12dc7ea316ead09c198ff7bdf7", "size": 83256, "url": "https://launcher.mojang.com/v1/objects/281d37f0326d4a12dc7ea316ead09c198ff7bdf7/libjsoundalsa.so" } }, "executable": true, "type": "file" }, "lib/amd64/liblcms.so": { "downloads": { "lzma": { "sha1": "7a239baba2086cae49114b382b74b971da02f08e", "size": 176175, "url": "https://launcher.mojang.com/v1/objects/7a239baba2086cae49114b382b74b971da02f08e/liblcms.so" }, "raw": { "sha1": "c8895cc3c3d023d9e059225969ab67954772c0a1", "size": 526872, "url": "https://launcher.mojang.com/v1/objects/c8895cc3c3d023d9e059225969ab67954772c0a1/liblcms.so" } }, "executable": true, "type": "file" }, "lib/amd64/libmanagement.so": { "downloads": { "lzma": { "sha1": "aed3fdbcefd1716abfc6a306687c8b741cbb318e", "size": 12838, "url": "https://launcher.mojang.com/v1/objects/aed3fdbcefd1716abfc6a306687c8b741cbb318e/libmanagement.so" }, "raw": { "sha1": "eba35f61e0d50e30874b7c7b335edf2d52662423", "size": 51808, "url": "https://launcher.mojang.com/v1/objects/eba35f61e0d50e30874b7c7b335edf2d52662423/libmanagement.so" } }, "executable": true, "type": "file" }, "lib/amd64/libmlib_image.so": { "downloads": { "lzma": { "sha1": "1bb181f079492d55c7a458e96488cd17fe0a7b86", "size": 310272, "url": "https://launcher.mojang.com/v1/objects/1bb181f079492d55c7a458e96488cd17fe0a7b86/libmlib_image.so" }, "raw": { "sha1": "c973c450d33873675945d4694be484e3427f58f1", "size": 1048136, "url": "https://launcher.mojang.com/v1/objects/c973c450d33873675945d4694be484e3427f58f1/libmlib_image.so" } }, "executable": true, "type": "file" }, "lib/amd64/libnet.so": { "downloads": { "lzma": { "sha1": "9dd79703b6deb86e0321afe01c6ac508263c8312", "size": 38123, "url": "https://launcher.mojang.com/v1/objects/9dd79703b6deb86e0321afe01c6ac508263c8312/libnet.so" }, "raw": { "sha1": "b3a17b7d53fcdf1e689e1ec29ce851eee6022ead", "size": 116920, "url": "https://launcher.mojang.com/v1/objects/b3a17b7d53fcdf1e689e1ec29ce851eee6022ead/libnet.so" } }, "executable": true, "type": "file" }, "lib/amd64/libnio.so": { "downloads": { "lzma": { "sha1": "5697c89d5d5d9b74f2e1555fcbba79dd4049e287", "size": 24445, "url": "https://launcher.mojang.com/v1/objects/5697c89d5d5d9b74f2e1555fcbba79dd4049e287/libnio.so" }, "raw": { "sha1": "573bf8f64dbcc397f8abd3e1da28f90ab0679f5b", "size": 93872, "url": "https://launcher.mojang.com/v1/objects/573bf8f64dbcc397f8abd3e1da28f90ab0679f5b/libnio.so" } }, "executable": true, "type": "file" }, "lib/amd64/libnpjp2.so": { "downloads": { "lzma": { "sha1": "6fe53b5951ff740e7f2ef7ffe5975af26da06718", "size": 57892, "url": "https://launcher.mojang.com/v1/objects/6fe53b5951ff740e7f2ef7ffe5975af26da06718/libnpjp2.so" }, "raw": { "sha1": "2bb13c53a4280379253475e51216b97eed1d4ce3", "size": 216592, "url": "https://launcher.mojang.com/v1/objects/2bb13c53a4280379253475e51216b97eed1d4ce3/libnpjp2.so" } }, "executable": true, "type": "file" }, "lib/amd64/libnpt.so": { "downloads": { "lzma": { "sha1": "1b170b09a32b1b8b6624fa5d1f94ec60b2bf3876", "size": 5070, "url": "https://launcher.mojang.com/v1/objects/1b170b09a32b1b8b6624fa5d1f94ec60b2bf3876/libnpt.so" }, "raw": { "sha1": "6b1ff6b9b4624f3cc7801f221c82b8046fb76364", "size": 17504, "url": "https://launcher.mojang.com/v1/objects/6b1ff6b9b4624f3cc7801f221c82b8046fb76364/libnpt.so" } }, "executable": true, "type": "file" }, "lib/amd64/libprism_common.so": { "downloads": { "lzma": { "sha1": "f4aca04c90bc7505851c074a08b2c31cae1f94fa", "size": 23315, "url": "https://launcher.mojang.com/v1/objects/f4aca04c90bc7505851c074a08b2c31cae1f94fa/libprism_common.so" }, "raw": { "sha1": "b00866b6ed8646a29a334d46e297267552f27de8", "size": 59008, "url": "https://launcher.mojang.com/v1/objects/b00866b6ed8646a29a334d46e297267552f27de8/libprism_common.so" } }, "executable": true, "type": "file" }, "lib/amd64/libprism_es2.so": { "downloads": { "lzma": { "sha1": "7ff4173c338c7a6f370f231670055737e032da3e", "size": 18416, "url": "https://launcher.mojang.com/v1/objects/7ff4173c338c7a6f370f231670055737e032da3e/libprism_es2.so" }, "raw": { "sha1": "1390a1dc14345e5a948148e59195d62f3a83863f", "size": 63808, "url": "https://launcher.mojang.com/v1/objects/1390a1dc14345e5a948148e59195d62f3a83863f/libprism_es2.so" } }, "executable": true, "type": "file" }, "lib/amd64/libprism_sw.so": { "downloads": { "lzma": { "sha1": "6728e8bf7b214067d715be6fe0325910d63c2468", "size": 29457, "url": "https://launcher.mojang.com/v1/objects/6728e8bf7b214067d715be6fe0325910d63c2468/libprism_sw.so" }, "raw": { "sha1": "7a6c34cb2bbcde411778d1b3f8677c39e32c3ac4", "size": 71608, "url": "https://launcher.mojang.com/v1/objects/7a6c34cb2bbcde411778d1b3f8677c39e32c3ac4/libprism_sw.so" } }, "executable": true, "type": "file" }, "lib/amd64/libresource.so": { "downloads": { "lzma": { "sha1": "1e35e63f1e74915fba620f1febf420b919d49bc5", "size": 2633, "url": "https://launcher.mojang.com/v1/objects/1e35e63f1e74915fba620f1febf420b919d49bc5/libresource.so" }, "raw": { "sha1": "57490353ad0d83ab1930355213dea269795434fe", "size": 13456, "url": "https://launcher.mojang.com/v1/objects/57490353ad0d83ab1930355213dea269795434fe/libresource.so" } }, "executable": true, "type": "file" }, "lib/amd64/libsctp.so": { "downloads": { "lzma": { "sha1": "4340132ed250d7849a016e071be773eaedd33aa8", "size": 9332, "url": "https://launcher.mojang.com/v1/objects/4340132ed250d7849a016e071be773eaedd33aa8/libsctp.so" }, "raw": { "sha1": "4a80e743750f127c0d5a359f5cd60b97e7ee12ae", "size": 29552, "url": "https://launcher.mojang.com/v1/objects/4a80e743750f127c0d5a359f5cd60b97e7ee12ae/libsctp.so" } }, "executable": true, "type": "file" }, "lib/amd64/libsplashscreen.so": { "downloads": { "lzma": { "sha1": "b226c8dbd73a548fc2b042ee6db6cc80e727597c", "size": 190305, "url": "https://launcher.mojang.com/v1/objects/b226c8dbd73a548fc2b042ee6db6cc80e727597c/libsplashscreen.so" }, "raw": { "sha1": "87d6a491f5ba7e6c4d972264a0c9063afea567a2", "size": 441376, "url": "https://launcher.mojang.com/v1/objects/87d6a491f5ba7e6c4d972264a0c9063afea567a2/libsplashscreen.so" } }, "executable": true, "type": "file" }, "lib/amd64/libsunec.so": { "downloads": { "lzma": { "sha1": "6ebba98fab1e3d872d1363235b76497f6f9babdc", "size": 88829, "url": "https://launcher.mojang.com/v1/objects/6ebba98fab1e3d872d1363235b76497f6f9babdc/libsunec.so" }, "raw": { "sha1": "3b262a0a530f6e4e539aed2cd27b4de1d0ed8859", "size": 283368, "url": "https://launcher.mojang.com/v1/objects/3b262a0a530f6e4e539aed2cd27b4de1d0ed8859/libsunec.so" } }, "executable": true, "type": "file" }, "lib/amd64/libt2k.so": { "downloads": { "lzma": { "sha1": "602cb812ef0b350ccf56ce209a260ddbe3743d92", "size": 190720, "url": "https://launcher.mojang.com/v1/objects/602cb812ef0b350ccf56ce209a260ddbe3743d92/libt2k.so" }, "raw": { "sha1": "b072c56df997f61e15e6b5a43b8907b0d25c2043", "size": 504840, "url": "https://launcher.mojang.com/v1/objects/b072c56df997f61e15e6b5a43b8907b0d25c2043/libt2k.so" } }, "executable": true, "type": "file" }, "lib/amd64/libunpack.so": { "downloads": { "lzma": { "sha1": "7107b615e941074f0b14c31c88fb67200aacd37f", "size": 70308, "url": "https://launcher.mojang.com/v1/objects/7107b615e941074f0b14c31c88fb67200aacd37f/libunpack.so" }, "raw": { "sha1": "b05ff862ed87928ed91e80e5604673c5ea710a53", "size": 197712, "url": "https://launcher.mojang.com/v1/objects/b05ff862ed87928ed91e80e5604673c5ea710a53/libunpack.so" } }, "executable": true, "type": "file" }, "lib/amd64/libverify.so": { "downloads": { "lzma": { "sha1": "ecd98efb8c7da441a8c3580e8f5598f3cb4165b1", "size": 21885, "url": "https://launcher.mojang.com/v1/objects/ecd98efb8c7da441a8c3580e8f5598f3cb4165b1/libverify.so" }, "raw": { "sha1": "e2c8d92531c45ab9be69ffb72c87fa12e9e59827", "size": 66112, "url": "https://launcher.mojang.com/v1/objects/e2c8d92531c45ab9be69ffb72c87fa12e9e59827/libverify.so" } }, "executable": true, "type": "file" }, "lib/amd64/libzip.so": { "downloads": { "lzma": { "sha1": "7c562342e3f7b138dc978495447e3e6a96c2cf45", "size": 54876, "url": "https://launcher.mojang.com/v1/objects/7c562342e3f7b138dc978495447e3e6a96c2cf45/libzip.so" }, "raw": { "sha1": "5f4bf35a5c3e8f8c650e891d1031589b8ab6d77f", "size": 127016, "url": "https://launcher.mojang.com/v1/objects/5f4bf35a5c3e8f8c650e891d1031589b8ab6d77f/libzip.so" } }, "executable": true, "type": "file" }, "lib/amd64/server": { "type": "directory" }, "lib/amd64/server/Xusage.txt": { "downloads": { "lzma": { "sha1": "acb2da24a4c765887df83985e4c26d6be302a0a3", "size": 629, "url": "https://launcher.mojang.com/v1/objects/acb2da24a4c765887df83985e4c26d6be302a0a3/Xusage.txt" }, "raw": { "sha1": "6983727eafe140f9dd793c78aa6f3e007438243a", "size": 1423, "url": "https://launcher.mojang.com/v1/objects/6983727eafe140f9dd793c78aa6f3e007438243a/Xusage.txt" } }, "executable": false, "type": "file" }, "lib/amd64/server/libjsig.so": { "target": "../libjsig.so", "type": "link" }, "lib/amd64/server/libjvm.so": { "downloads": { "lzma": { "sha1": "d5c6f3338aaa6712f79d680ac8c3e31beebaa886", "size": 4154311, "url": "https://launcher.mojang.com/v1/objects/d5c6f3338aaa6712f79d680ac8c3e31beebaa886/libjvm.so" }, "raw": { "sha1": "23a98e1eb505cc3fb91bc0cb2adb71ab9270e9ca", "size": 17045016, "url": "https://launcher.mojang.com/v1/objects/23a98e1eb505cc3fb91bc0cb2adb71ab9270e9ca/libjvm.so" } }, "executable": true, "type": "file" }, "lib/applet": { "type": "directory" }, "lib/calendars.properties": { "downloads": { "lzma": { "sha1": "4a757c23f2942bd802a4f80235131146d9267750", "size": 558, "url": "https://launcher.mojang.com/v1/objects/4a757c23f2942bd802a4f80235131146d9267750/calendars.properties" }, "raw": { "sha1": "42ebb0988124433b8f2a6e5d9a74ed41240bcfc6", "size": 1378, "url": "https://launcher.mojang.com/v1/objects/42ebb0988124433b8f2a6e5d9a74ed41240bcfc6/calendars.properties" } }, "executable": false, "type": "file" }, "lib/charsets.jar": { "downloads": { "lzma": { "sha1": "2bf44143b2ad9d7d55045a4de4a562330c194dc0", "size": 412367, "url": "https://launcher.mojang.com/v1/objects/2bf44143b2ad9d7d55045a4de4a562330c194dc0/charsets.jar" }, "raw": { "sha1": "d73ab9f8de255a7e112ddd13622bf7f6b18c8447", "size": 3135615, "url": "https://launcher.mojang.com/v1/objects/d73ab9f8de255a7e112ddd13622bf7f6b18c8447/charsets.jar" } }, "executable": false, "type": "file" }, "lib/classlist": { "downloads": { "lzma": { "sha1": "14e7c73d21b8513b0aff8d86e5cb34c52021fbca", "size": 15024, "url": "https://launcher.mojang.com/v1/objects/14e7c73d21b8513b0aff8d86e5cb34c52021fbca/classlist" }, "raw": { "sha1": "9c0404b63c87e2fed35e3a6cd137d6cf876c42bd", "size": 84311, "url": "https://launcher.mojang.com/v1/objects/9c0404b63c87e2fed35e3a6cd137d6cf876c42bd/classlist" } }, "executable": false, "type": "file" }, "lib/cmm": { "type": "directory" }, "lib/cmm/CIEXYZ.pf": { "downloads": { "lzma": { "sha1": "fcc5ca2fd3f45cac3434b480fa3ce00007e96529", "size": 8964, "url": "https://launcher.mojang.com/v1/objects/fcc5ca2fd3f45cac3434b480fa3ce00007e96529/CIEXYZ.pf" }, "raw": { "sha1": "b7779924c70554647b87c2a86159ca7781e929f8", "size": 51236, "url": "https://launcher.mojang.com/v1/objects/b7779924c70554647b87c2a86159ca7781e929f8/CIEXYZ.pf" } }, "executable": false, "type": "file" }, "lib/cmm/GRAY.pf": { "downloads": { "lzma": { "sha1": "5388ccfe67d3131d6d02143d8e8895003ab14ff6", "size": 299, "url": "https://launcher.mojang.com/v1/objects/5388ccfe67d3131d6d02143d8e8895003ab14ff6/GRAY.pf" }, "raw": { "sha1": "27f93961d66b8230d0cdb8b166bc8b4153d5bc2d", "size": 632, "url": "https://launcher.mojang.com/v1/objects/27f93961d66b8230d0cdb8b166bc8b4153d5bc2d/GRAY.pf" } }, "executable": false, "type": "file" }, "lib/cmm/LINEAR_RGB.pf": { "downloads": { "lzma": { "sha1": "2bd90f09c8deb64b1729d6b8173c78f9e9cab27b", "size": 678, "url": "https://launcher.mojang.com/v1/objects/2bd90f09c8deb64b1729d6b8173c78f9e9cab27b/LINEAR_RGB.pf" }, "raw": { "sha1": "7913274c2f73bafcf888f09ff60990b100214ede", "size": 1044, "url": "https://launcher.mojang.com/v1/objects/7913274c2f73bafcf888f09ff60990b100214ede/LINEAR_RGB.pf" } }, "executable": false, "type": "file" }, "lib/cmm/PYCC.pf": { "downloads": { "lzma": { "sha1": "dbb2197ecff3fcdd142e9006490c8cb5c8d19af8", "size": 171521, "url": "https://launcher.mojang.com/v1/objects/dbb2197ecff3fcdd142e9006490c8cb5c8d19af8/PYCC.pf" }, "raw": { "sha1": "4f7eed05b8f0eea7bcdc8f8f7aaeb1925ce7b144", "size": 274474, "url": "https://launcher.mojang.com/v1/objects/4f7eed05b8f0eea7bcdc8f8f7aaeb1925ce7b144/PYCC.pf" } }, "executable": false, "type": "file" }, "lib/cmm/sRGB.pf": { "downloads": { "raw": { "sha1": "9eaea0911d89d63e39e95f2e2116eaec7e0bb91e", "size": 3144, "url": "https://launcher.mojang.com/v1/objects/9eaea0911d89d63e39e95f2e2116eaec7e0bb91e/sRGB.pf" } }, "executable": false, "type": "file" }, "lib/content-types.properties": { "downloads": { "lzma": { "sha1": "43a23d9a6c637c128b14cfa3feced93cbcf85b1a", "size": 1617, "url": "https://launcher.mojang.com/v1/objects/43a23d9a6c637c128b14cfa3feced93cbcf85b1a/content-types.properties" }, "raw": { "sha1": "b21698017c4a2866b5fabe59681b7592e72c83b1", "size": 5916, "url": "https://launcher.mojang.com/v1/objects/b21698017c4a2866b5fabe59681b7592e72c83b1/content-types.properties" } }, "executable": false, "type": "file" }, "lib/currency.data": { "downloads": { "lzma": { "sha1": "451b3f166ea34ef2aefbb01606ea5adcc0d65b42", "size": 1184, "url": "https://launcher.mojang.com/v1/objects/451b3f166ea34ef2aefbb01606ea5adcc0d65b42/currency.data" }, "raw": { "sha1": "bf524381a7a9b9d5bbab48069c583d2936e367a1", "size": 4134, "url": "https://launcher.mojang.com/v1/objects/bf524381a7a9b9d5bbab48069c583d2936e367a1/currency.data" } }, "executable": false, "type": "file" }, "lib/deploy": { "type": "directory" }, "lib/deploy.jar": { "downloads": { "raw": { "sha1": "fbe1de8fcd9a3d482c59414dce9311e4194766c9", "size": 2255881, "url": "https://launcher.mojang.com/v1/objects/fbe1de8fcd9a3d482c59414dce9311e4194766c9/deploy.jar" } }, "executable": false, "type": "file" }, "lib/deploy/MixedCodeMainDialog.ui": { "downloads": { "lzma": { "sha1": "7d812964343d1e978442f5c847c709784fc18fc0", "size": 683, "url": "https://launcher.mojang.com/v1/objects/7d812964343d1e978442f5c847c709784fc18fc0/MixedCodeMainDialog.ui" }, "raw": { "sha1": "c9b1af1c229e54b2d8a3d642d4f0bb31dc15be79", "size": 4507, "url": "https://launcher.mojang.com/v1/objects/c9b1af1c229e54b2d8a3d642d4f0bb31dc15be79/MixedCodeMainDialog.ui" } }, "executable": false, "type": "file" }, "lib/deploy/MixedCodeMainDialogJs.ui": { "downloads": { "lzma": { "sha1": "54fb58dbcc59e35e0ae896d0e266ae0c5bcf85c2", "size": 792, "url": "https://launcher.mojang.com/v1/objects/54fb58dbcc59e35e0ae896d0e266ae0c5bcf85c2/MixedCodeMainDialogJs.ui" }, "raw": { "sha1": "ad6337fb6d46750e14c12b439a5856f4b6864d0d", "size": 6110, "url": "https://launcher.mojang.com/v1/objects/ad6337fb6d46750e14c12b439a5856f4b6864d0d/MixedCodeMainDialogJs.ui" } }, "executable": false, "type": "file" }, "lib/deploy/cautionshield.icns": { "downloads": { "lzma": { "sha1": "7cea751dc168605054ec38ce8bfa71812be405c1", "size": 2333, "url": "https://launcher.mojang.com/v1/objects/7cea751dc168605054ec38ce8bfa71812be405c1/cautionshield.icns" }, "raw": { "sha1": "1de7ed5d5fc75aa1bcede088c655bee3bde64c38", "size": 3588, "url": "https://launcher.mojang.com/v1/objects/1de7ed5d5fc75aa1bcede088c655bee3bde64c38/cautionshield.icns" } }, "executable": false, "type": "file" }, "lib/deploy/ffjcext.zip": { "downloads": { "lzma": { "sha1": "80bcb9b3794f69d87dba93e90230f288e651e798", "size": 1809, "url": "https://launcher.mojang.com/v1/objects/80bcb9b3794f69d87dba93e90230f288e651e798/ffjcext.zip" }, "raw": { "sha1": "76d051ca7d3666ff25ea8eb9957a05574a45287f", "size": 13454, "url": "https://launcher.mojang.com/v1/objects/76d051ca7d3666ff25ea8eb9957a05574a45287f/ffjcext.zip" } }, "executable": false, "type": "file" }, "lib/deploy/java-icon.ico": { "downloads": { "lzma": { "sha1": "2a24f0207d7ab5976a8b0d92b4b381d49e895c9d", "size": 8468, "url": "https://launcher.mojang.com/v1/objects/2a24f0207d7ab5976a8b0d92b4b381d49e895c9d/java-icon.ico" }, "raw": { "sha1": "2997ceb26ff49a7d7c5e7a2405b5fb50b62c7914", "size": 29926, "url": "https://launcher.mojang.com/v1/objects/2997ceb26ff49a7d7c5e7a2405b5fb50b62c7914/java-icon.ico" } }, "executable": false, "type": "file" }, "lib/deploy/messages.properties": { "downloads": { "lzma": { "sha1": "c1e16f80dc0b1f1a591cecf3cbab4ba5e47492f4", "size": 1225, "url": "https://launcher.mojang.com/v1/objects/c1e16f80dc0b1f1a591cecf3cbab4ba5e47492f4/messages.properties" }, "raw": { "sha1": "dc52841c708e3c1eb2a044088a43396d1291bb5e", "size": 2860, "url": "https://launcher.mojang.com/v1/objects/dc52841c708e3c1eb2a044088a43396d1291bb5e/messages.properties" } }, "executable": false, "type": "file" }, "lib/deploy/messages_de.properties": { "downloads": { "lzma": { "sha1": "42b42e6e1d2cb2d781f2226bde612ce519b29bc8", "size": 1394, "url": "https://launcher.mojang.com/v1/objects/42b42e6e1d2cb2d781f2226bde612ce519b29bc8/messages_de.properties" }, "raw": { "sha1": "d989fe1b8f7904888d5102294ebefd28d932ecdb", "size": 3306, "url": "https://launcher.mojang.com/v1/objects/d989fe1b8f7904888d5102294ebefd28d932ecdb/messages_de.properties" } }, "executable": false, "type": "file" }, "lib/deploy/messages_es.properties": { "downloads": { "lzma": { "sha1": "c4a653e9802ca982e892b45d88c1e259c09e8c8e", "size": 1404, "url": "https://launcher.mojang.com/v1/objects/c4a653e9802ca982e892b45d88c1e259c09e8c8e/messages_es.properties" }, "raw": { "sha1": "1b0334b79db481c3a59be6915d5118d760c97baa", "size": 3600, "url": "https://launcher.mojang.com/v1/objects/1b0334b79db481c3a59be6915d5118d760c97baa/messages_es.properties" } }, "executable": false, "type": "file" }, "lib/deploy/messages_fr.properties": { "downloads": { "lzma": { "sha1": "2d8dee07e3f5aab7318a22e169810b216ac44f97", "size": 1401, "url": "https://launcher.mojang.com/v1/objects/2d8dee07e3f5aab7318a22e169810b216ac44f97/messages_fr.properties" }, "raw": { "sha1": "69bd2d03c2064f8679de5b4e430ea61b567c69c5", "size": 3409, "url": "https://launcher.mojang.com/v1/objects/69bd2d03c2064f8679de5b4e430ea61b567c69c5/messages_fr.properties" } }, "executable": false, "type": "file" }, "lib/deploy/messages_it.properties": { "downloads": { "lzma": { "sha1": "7c28cdd8d9e34355ba0fc03004c4f64749cae57e", "size": 1375, "url": "https://launcher.mojang.com/v1/objects/7c28cdd8d9e34355ba0fc03004c4f64749cae57e/messages_it.properties" }, "raw": { "sha1": "dbe49949308f28540a42ae6cd2ad58afbf615592", "size": 3223, "url": "https://launcher.mojang.com/v1/objects/dbe49949308f28540a42ae6cd2ad58afbf615592/messages_it.properties" } }, "executable": false, "type": "file" }, "lib/deploy/messages_ja.properties": { "downloads": { "lzma": { "sha1": "9a6a4c086e48b9e615b72b6bbebb3c724d178ff4", "size": 1680, "url": "https://launcher.mojang.com/v1/objects/9a6a4c086e48b9e615b72b6bbebb3c724d178ff4/messages_ja.properties" }, "raw": { "sha1": "751170a7cdefcb1226604ac3f8196e06a04fd7ac", "size": 6349, "url": "https://launcher.mojang.com/v1/objects/751170a7cdefcb1226604ac3f8196e06a04fd7ac/messages_ja.properties" } }, "executable": false, "type": "file" }, "lib/deploy/messages_ko.properties": { "downloads": { "lzma": { "sha1": "0c57c2ebfa0830f816657a384898487fc492efac", "size": 1645, "url": "https://launcher.mojang.com/v1/objects/0c57c2ebfa0830f816657a384898487fc492efac/messages_ko.properties" }, "raw": { "sha1": "bf9e055b5ab138ad6d49769e2b7630b7938848d6", "size": 5712, "url": "https://launcher.mojang.com/v1/objects/bf9e055b5ab138ad6d49769e2b7630b7938848d6/messages_ko.properties" } }, "executable": false, "type": "file" }, "lib/deploy/messages_pt_BR.properties": { "downloads": { "lzma": { "sha1": "f8364dba0aa0a7e445a1a8d0e7ad66b996f70063", "size": 1388, "url": "https://launcher.mojang.com/v1/objects/f8364dba0aa0a7e445a1a8d0e7ad66b996f70063/messages_pt_BR.properties" }, "raw": { "sha1": "24e4951743521ab9a11381c77bd0cdb1ed30f5b5", "size": 3285, "url": "https://launcher.mojang.com/v1/objects/24e4951743521ab9a11381c77bd0cdb1ed30f5b5/messages_pt_BR.properties" } }, "executable": false, "type": "file" }, "lib/deploy/messages_sv.properties": { "downloads": { "lzma": { "sha1": "65e5897d552258141aacf02f087c7c9c33ad0727", "size": 1355, "url": "https://launcher.mojang.com/v1/objects/65e5897d552258141aacf02f087c7c9c33ad0727/messages_sv.properties" }, "raw": { "sha1": "bb5a4aa0ba499f6b1916a83e3c7922a4583b4adb", "size": 3384, "url": "https://launcher.mojang.com/v1/objects/bb5a4aa0ba499f6b1916a83e3c7922a4583b4adb/messages_sv.properties" } }, "executable": false, "type": "file" }, "lib/deploy/messages_zh_CN.properties": { "downloads": { "lzma": { "sha1": "de7d39a6e6748e9f47e842c9da90f515921c222c", "size": 1506, "url": "https://launcher.mojang.com/v1/objects/de7d39a6e6748e9f47e842c9da90f515921c222c/messages_zh_CN.properties" }, "raw": { "sha1": "1c2b96673dddd3596890ef4fc22017d484a1f652", "size": 4072, "url": "https://launcher.mojang.com/v1/objects/1c2b96673dddd3596890ef4fc22017d484a1f652/messages_zh_CN.properties" } }, "executable": false, "type": "file" }, "lib/deploy/messages_zh_HK.properties": { "downloads": { "lzma": { "sha1": "e8d0e3a63caa2535a4f361033941f34dcc170a7e", "size": 1529, "url": "https://launcher.mojang.com/v1/objects/e8d0e3a63caa2535a4f361033941f34dcc170a7e/messages_zh_TW.properties" }, "raw": { "sha1": "37a57aad121c14c25e149206179728fa62203bf0", "size": 3752, "url": "https://launcher.mojang.com/v1/objects/37a57aad121c14c25e149206179728fa62203bf0/messages_zh_TW.properties" } }, "executable": false, "type": "file" }, "lib/deploy/messages_zh_TW.properties": { "downloads": { "lzma": { "sha1": "e8d0e3a63caa2535a4f361033941f34dcc170a7e", "size": 1529, "url": "https://launcher.mojang.com/v1/objects/e8d0e3a63caa2535a4f361033941f34dcc170a7e/messages_zh_TW.properties" }, "raw": { "sha1": "37a57aad121c14c25e149206179728fa62203bf0", "size": 3752, "url": "https://launcher.mojang.com/v1/objects/37a57aad121c14c25e149206179728fa62203bf0/messages_zh_TW.properties" } }, "executable": false, "type": "file" }, "lib/deploy/mixcode_s.png": { "downloads": { "raw": { "sha1": "4604e9f265eec97bccd0151c3a81afa9e69499e5", "size": 3115, "url": "https://launcher.mojang.com/v1/objects/4604e9f265eec97bccd0151c3a81afa9e69499e5/mixcode_s.png" } }, "executable": false, "type": "file" }, "lib/deploy/splash.gif": { "downloads": { "raw": { "sha1": "20e7aec75f6d036d504277542e507eb7dc24aae8", "size": 8590, "url": "https://launcher.mojang.com/v1/objects/20e7aec75f6d036d504277542e507eb7dc24aae8/splash.gif" } }, "executable": false, "type": "file" }, "lib/deploy/splash@2x.gif": { "downloads": { "raw": { "sha1": "0ae4a5bda2a6d628fac51462390b503c99509fdc", "size": 15276, "url": "https://launcher.mojang.com/v1/objects/0ae4a5bda2a6d628fac51462390b503c99509fdc/splash2x.gif" } }, "executable": false, "type": "file" }, "lib/deploy/splash_11-lic.gif": { "downloads": { "raw": { "sha1": "8def364e07f40142822df84b5bb4f50846cb5e4e", "size": 7805, "url": "https://launcher.mojang.com/v1/objects/8def364e07f40142822df84b5bb4f50846cb5e4e/splash_11-lic.gif" } }, "executable": false, "type": "file" }, "lib/deploy/splash_11@2x-lic.gif": { "downloads": { "raw": { "sha1": "d2bff9bbf7920ca743b81a0ee23b0719b4d057ca", "size": 12250, "url": "https://launcher.mojang.com/v1/objects/d2bff9bbf7920ca743b81a0ee23b0719b4d057ca/splash_11%402x-lic.gif" } }, "executable": false, "type": "file" }, "lib/desktop": { "type": "directory" }, "lib/desktop/applications": { "type": "directory" }, "lib/desktop/applications/sun-java.desktop": { "downloads": { "lzma": { "sha1": "109d1cdf165f38da92da70b403ca940192a7a9a8", "size": 536, "url": "https://launcher.mojang.com/v1/objects/109d1cdf165f38da92da70b403ca940192a7a9a8/sun-java.desktop" }, "raw": { "sha1": "d346dfe90505603ce5aff5a3c6c2e4a23d5bd990", "size": 777, "url": "https://launcher.mojang.com/v1/objects/d346dfe90505603ce5aff5a3c6c2e4a23d5bd990/sun-java.desktop" } }, "executable": false, "type": "file" }, "lib/desktop/applications/sun-javaws.desktop": { "downloads": { "lzma": { "sha1": "5e1815e7f83515881e6998584dc6bb02c5bef09a", "size": 451, "url": "https://launcher.mojang.com/v1/objects/5e1815e7f83515881e6998584dc6bb02c5bef09a/sun-javaws.desktop" }, "raw": { "sha1": "50ce8e519b836e0f53d58ce1a359d98b6cafdda6", "size": 619, "url": "https://launcher.mojang.com/v1/objects/50ce8e519b836e0f53d58ce1a359d98b6cafdda6/sun-javaws.desktop" } }, "executable": false, "type": "file" }, "lib/desktop/applications/sun_java.desktop": { "downloads": { "lzma": { "sha1": "49ab0ccb54c3be68281d05055bc56a88b1281d3c", "size": 447, "url": "https://launcher.mojang.com/v1/objects/49ab0ccb54c3be68281d05055bc56a88b1281d3c/sun_java.desktop" }, "raw": { "sha1": "79120ee8160ad6f3c9b90c2641fb7edf3af96b5d", "size": 624, "url": "https://launcher.mojang.com/v1/objects/79120ee8160ad6f3c9b90c2641fb7edf3af96b5d/sun_java.desktop" } }, "executable": false, "type": "file" }, "lib/desktop/icons": { "type": "directory" }, "lib/desktop/icons/HighContrast": { "type": "directory" }, "lib/desktop/icons/HighContrast/16x16": { "type": "directory" }, "lib/desktop/icons/HighContrast/16x16/apps": { "type": "directory" }, "lib/desktop/icons/HighContrast/16x16/apps/sun-java.png": { "downloads": { "raw": { "sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", "size": 417, "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrast/16x16/apps/sun-javaws.png": { "downloads": { "raw": { "sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", "size": 417, "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrast/16x16/apps/sun-jcontrol.png": { "downloads": { "raw": { "sha1": "366e7a48e9e4fb92eaeabbcaeb4626122a66cecb", "size": 417, "url": "https://launcher.mojang.com/v1/objects/366e7a48e9e4fb92eaeabbcaeb4626122a66cecb/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrast/16x16/mimetypes": { "type": "directory" }, "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-application-x-java-archive.png": { "downloads": { "raw": { "sha1": "629c48907368ecf32d2395b6459c367f79d84689", "size": 464, "url": "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { "downloads": { "raw": { "sha1": "629c48907368ecf32d2395b6459c367f79d84689", "size": 464, "url": "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrast/16x16/mimetypes/gnome-mime-text-x-java.png": { "downloads": { "raw": { "sha1": "629c48907368ecf32d2395b6459c367f79d84689", "size": 464, "url": "https://launcher.mojang.com/v1/objects/629c48907368ecf32d2395b6459c367f79d84689/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrast/48x48": { "type": "directory" }, "lib/desktop/icons/HighContrast/48x48/apps": { "type": "directory" }, "lib/desktop/icons/HighContrast/48x48/apps/sun-java.png": { "downloads": { "raw": { "sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", "size": 3451, "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrast/48x48/apps/sun-javaws.png": { "downloads": { "raw": { "sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", "size": 3451, "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrast/48x48/apps/sun-jcontrol.png": { "downloads": { "raw": { "sha1": "8373482d072684e09830dbdb97a76ea264c9f4e9", "size": 3451, "url": "https://launcher.mojang.com/v1/objects/8373482d072684e09830dbdb97a76ea264c9f4e9/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrast/48x48/mimetypes": { "type": "directory" }, "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-application-x-java-archive.png": { "downloads": { "raw": { "sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { "downloads": { "raw": { "sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrast/48x48/mimetypes/gnome-mime-text-x-java.png": { "downloads": { "raw": { "sha1": "56a4996519f8f3541eba7b7a7a69bcdcd8ed0410", "size": 2088, "url": "https://launcher.mojang.com/v1/objects/56a4996519f8f3541eba7b7a7a69bcdcd8ed0410/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse": { "type": "directory" }, "lib/desktop/icons/HighContrastInverse/16x16": { "type": "directory" }, "lib/desktop/icons/HighContrastInverse/16x16/apps": { "type": "directory" }, "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-java.png": { "downloads": { "raw": { "sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", "size": 402, "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-javaws.png": { "downloads": { "raw": { "sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", "size": 402, "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse/16x16/apps/sun-jcontrol.png": { "downloads": { "raw": { "sha1": "bf0995acb94aa794e73c5b971282ff13ffe42793", "size": 402, "url": "https://launcher.mojang.com/v1/objects/bf0995acb94aa794e73c5b971282ff13ffe42793/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes": { "type": "directory" }, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-application-x-java-archive.png": { "downloads": { "raw": { "sha1": "1477eceda25e162fcda2e69ee3906091973d8344", "size": 473, "url": "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { "downloads": { "raw": { "sha1": "1477eceda25e162fcda2e69ee3906091973d8344", "size": 473, "url": "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse/16x16/mimetypes/gnome-mime-text-x-java.png": { "downloads": { "raw": { "sha1": "1477eceda25e162fcda2e69ee3906091973d8344", "size": 473, "url": "https://launcher.mojang.com/v1/objects/1477eceda25e162fcda2e69ee3906091973d8344/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse/48x48": { "type": "directory" }, "lib/desktop/icons/HighContrastInverse/48x48/apps": { "type": "directory" }, "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-java.png": { "downloads": { "raw": { "sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", "size": 3410, "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-javaws.png": { "downloads": { "raw": { "sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", "size": 3410, "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse/48x48/apps/sun-jcontrol.png": { "downloads": { "raw": { "sha1": "413da160dd9899b95f53d4cc11f5ee0550cc6585", "size": 3410, "url": "https://launcher.mojang.com/v1/objects/413da160dd9899b95f53d4cc11f5ee0550cc6585/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes": { "type": "directory" }, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-application-x-java-archive.png": { "downloads": { "raw": { "sha1": "d66e04dfa25c196bec2e201547325b79846ab674", "size": 2085, "url": "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { "downloads": { "raw": { "sha1": "d66e04dfa25c196bec2e201547325b79846ab674", "size": 2085, "url": "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/HighContrastInverse/48x48/mimetypes/gnome-mime-text-x-java.png": { "downloads": { "raw": { "sha1": "d66e04dfa25c196bec2e201547325b79846ab674", "size": 2085, "url": "https://launcher.mojang.com/v1/objects/d66e04dfa25c196bec2e201547325b79846ab674/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast": { "type": "directory" }, "lib/desktop/icons/LowContrast/16x16": { "type": "directory" }, "lib/desktop/icons/LowContrast/16x16/apps": { "type": "directory" }, "lib/desktop/icons/LowContrast/16x16/apps/sun-java.png": { "downloads": { "raw": { "sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", "size": 519, "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast/16x16/apps/sun-javaws.png": { "downloads": { "raw": { "sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", "size": 519, "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast/16x16/apps/sun-jcontrol.png": { "downloads": { "raw": { "sha1": "f93b7cf0a6d27d664a7f09dab6933b2768536f52", "size": 519, "url": "https://launcher.mojang.com/v1/objects/f93b7cf0a6d27d664a7f09dab6933b2768536f52/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast/16x16/mimetypes": { "type": "directory" }, "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-application-x-java-archive.png": { "downloads": { "raw": { "sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", "size": 525, "url": "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { "downloads": { "raw": { "sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", "size": 525, "url": "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast/16x16/mimetypes/gnome-mime-text-x-java.png": { "downloads": { "raw": { "sha1": "0aa1605877280b88de1f1cc3e7e4bdbeed968a73", "size": 525, "url": "https://launcher.mojang.com/v1/objects/0aa1605877280b88de1f1cc3e7e4bdbeed968a73/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast/48x48": { "type": "directory" }, "lib/desktop/icons/LowContrast/48x48/apps": { "type": "directory" }, "lib/desktop/icons/LowContrast/48x48/apps/sun-java.png": { "downloads": { "raw": { "sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", "size": 1507, "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast/48x48/apps/sun-javaws.png": { "downloads": { "raw": { "sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", "size": 1507, "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast/48x48/apps/sun-jcontrol.png": { "downloads": { "raw": { "sha1": "1fcf4fd6da61873b5f21b39412da26509734b7cc", "size": 1507, "url": "https://launcher.mojang.com/v1/objects/1fcf4fd6da61873b5f21b39412da26509734b7cc/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast/48x48/mimetypes": { "type": "directory" }, "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-application-x-java-archive.png": { "downloads": { "raw": { "sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", "size": 1948, "url": "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { "downloads": { "raw": { "sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", "size": 1948, "url": "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/LowContrast/48x48/mimetypes/gnome-mime-text-x-java.png": { "downloads": { "raw": { "sha1": "e36636b1c04dc283c18adf669b892d54b15d3ee6", "size": 1948, "url": "https://launcher.mojang.com/v1/objects/e36636b1c04dc283c18adf669b892d54b15d3ee6/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor": { "type": "directory" }, "lib/desktop/icons/hicolor/16x16": { "type": "directory" }, "lib/desktop/icons/hicolor/16x16/apps": { "type": "directory" }, "lib/desktop/icons/hicolor/16x16/apps/sun-java.png": { "downloads": { "raw": { "sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", "size": 383, "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor/16x16/apps/sun-javaws.png": { "downloads": { "raw": { "sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", "size": 383, "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor/16x16/apps/sun-jcontrol.png": { "downloads": { "raw": { "sha1": "e91d05bfe9b889bf8a227908b597cab4630da8f2", "size": 383, "url": "https://launcher.mojang.com/v1/objects/e91d05bfe9b889bf8a227908b597cab4630da8f2/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor/16x16/mimetypes": { "type": "directory" }, "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-application-x-java-archive.png": { "downloads": { "raw": { "sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", "size": 783, "url": "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { "downloads": { "raw": { "sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", "size": 783, "url": "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor/16x16/mimetypes/gnome-mime-text-x-java.png": { "downloads": { "raw": { "sha1": "d2f6abe8e498aeecb334fb43f63001d34dbf6ea5", "size": 783, "url": "https://launcher.mojang.com/v1/objects/d2f6abe8e498aeecb334fb43f63001d34dbf6ea5/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor/48x48": { "type": "directory" }, "lib/desktop/icons/hicolor/48x48/apps": { "type": "directory" }, "lib/desktop/icons/hicolor/48x48/apps/sun-java.png": { "downloads": { "raw": { "sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", "size": 1439, "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor/48x48/apps/sun-javaws.png": { "downloads": { "raw": { "sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", "size": 1439, "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor/48x48/apps/sun-jcontrol.png": { "downloads": { "raw": { "sha1": "6c90a38eaada9c32a678a282be18ec5b43a84264", "size": 1439, "url": "https://launcher.mojang.com/v1/objects/6c90a38eaada9c32a678a282be18ec5b43a84264/sun-javaws.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor/48x48/mimetypes": { "type": "directory" }, "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-application-x-java-archive.png": { "downloads": { "raw": { "sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", "size": 3202, "url": "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-application-x-java-jnlp-file.png": { "downloads": { "raw": { "sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", "size": 3202, "url": "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/icons/hicolor/48x48/mimetypes/gnome-mime-text-x-java.png": { "downloads": { "raw": { "sha1": "4d5e6e0c41d1076bc86f3ab157c88a41a5716997", "size": 3202, "url": "https://launcher.mojang.com/v1/objects/4d5e6e0c41d1076bc86f3ab157c88a41a5716997/gnome-mime-application-x-java-archive.png" } }, "executable": false, "type": "file" }, "lib/desktop/mime": { "type": "directory" }, "lib/desktop/mime/packages": { "type": "directory" }, "lib/desktop/mime/packages/x-java-archive.xml": { "downloads": { "lzma": { "sha1": "841230729f0a59de2a1071d155d96358232b2ba1", "size": 591, "url": "https://launcher.mojang.com/v1/objects/841230729f0a59de2a1071d155d96358232b2ba1/x-java-archive.xml" }, "raw": { "sha1": "b6297fd36efa799312961f95ebf0c85c920d5037", "size": 1822, "url": "https://launcher.mojang.com/v1/objects/b6297fd36efa799312961f95ebf0c85c920d5037/x-java-archive.xml" } }, "executable": false, "type": "file" }, "lib/desktop/mime/packages/x-java-jnlp-file.xml": { "downloads": { "lzma": { "sha1": "abf9acbe7c18027c4f036c4e42bb2cf1115525fa", "size": 302, "url": "https://launcher.mojang.com/v1/objects/abf9acbe7c18027c4f036c4e42bb2cf1115525fa/x-java-jnlp-file.xml" }, "raw": { "sha1": "72f03da83ddb76c9105f619fcfa4dbdad70e6b30", "size": 412, "url": "https://launcher.mojang.com/v1/objects/72f03da83ddb76c9105f619fcfa4dbdad70e6b30/x-java-jnlp-file.xml" } }, "executable": false, "type": "file" }, "lib/ext": { "type": "directory" }, "lib/ext/cldrdata.jar": { "downloads": { "raw": { "sha1": "6cacc961942d3f02a88907aa8f2eaae8e20c95a0", "size": 3860502, "url": "https://launcher.mojang.com/v1/objects/6cacc961942d3f02a88907aa8f2eaae8e20c95a0/cldrdata.jar" } }, "executable": false, "type": "file" }, "lib/ext/dnsns.jar": { "downloads": { "raw": { "sha1": "93bebdd7514e53ae31d60c5daba673878c8822ec", "size": 8286, "url": "https://launcher.mojang.com/v1/objects/93bebdd7514e53ae31d60c5daba673878c8822ec/dnsns.jar" } }, "executable": false, "type": "file" }, "lib/ext/jaccess.jar": { "downloads": { "raw": { "sha1": "2f54879df7c29ec67c40d40cfc95c0d4a968bea1", "size": 44516, "url": "https://launcher.mojang.com/v1/objects/2f54879df7c29ec67c40d40cfc95c0d4a968bea1/jaccess.jar" } }, "executable": false, "type": "file" }, "lib/ext/jfxrt.jar": { "downloads": { "lzma": { "sha1": "a6c5b6a782ba360ada6651f5322dcab88c75711c", "size": 3374270, "url": "https://launcher.mojang.com/v1/objects/a6c5b6a782ba360ada6651f5322dcab88c75711c/jfxrt.jar" }, "raw": { "sha1": "1ad7a876f045399c23ee4ab7dee380a04ca2ac08", "size": 18508094, "url": "https://launcher.mojang.com/v1/objects/1ad7a876f045399c23ee4ab7dee380a04ca2ac08/jfxrt.jar" } }, "executable": false, "type": "file" }, "lib/ext/localedata.jar": { "downloads": { "raw": { "sha1": "0cc9f550d4e410b5aa29dbfd2c1b5c99391c7f70", "size": 1178926, "url": "https://launcher.mojang.com/v1/objects/0cc9f550d4e410b5aa29dbfd2c1b5c99391c7f70/localedata.jar" } }, "executable": false, "type": "file" }, "lib/ext/meta-index": { "downloads": { "lzma": { "sha1": "1359457529f42bacf495afcb68149ae036442dd9", "size": 594, "url": "https://launcher.mojang.com/v1/objects/1359457529f42bacf495afcb68149ae036442dd9/meta-index" }, "raw": { "sha1": "334649c6e2d5d7248211f30855e97cfcb4558851", "size": 1269, "url": "https://launcher.mojang.com/v1/objects/334649c6e2d5d7248211f30855e97cfcb4558851/meta-index" } }, "executable": false, "type": "file" }, "lib/ext/nashorn.jar": { "downloads": { "raw": { "sha1": "dec5dd17a0f52ae79dfbfb38840bffb8b7a679a5", "size": 2023869, "url": "https://launcher.mojang.com/v1/objects/dec5dd17a0f52ae79dfbfb38840bffb8b7a679a5/nashorn.jar" } }, "executable": false, "type": "file" }, "lib/ext/sunec.jar": { "downloads": { "raw": { "sha1": "bf1c817820341a246f7130fe046e8310b03d04f6", "size": 41672, "url": "https://launcher.mojang.com/v1/objects/bf1c817820341a246f7130fe046e8310b03d04f6/sunec.jar" } }, "executable": false, "type": "file" }, "lib/ext/sunjce_provider.jar": { "downloads": { "raw": { "sha1": "bb3494e4b5cb3c3e60da767207731f18b267cb34", "size": 279326, "url": "https://launcher.mojang.com/v1/objects/bb3494e4b5cb3c3e60da767207731f18b267cb34/sunjce_provider.jar" } }, "executable": false, "type": "file" }, "lib/ext/sunpkcs11.jar": { "downloads": { "raw": { "sha1": "5bb1dedc3344cd3bb86828d4aa8ca82f4a606ed4", "size": 250218, "url": "https://launcher.mojang.com/v1/objects/5bb1dedc3344cd3bb86828d4aa8ca82f4a606ed4/sunpkcs11.jar" } }, "executable": false, "type": "file" }, "lib/ext/zipfs.jar": { "downloads": { "raw": { "sha1": "37b338f0e8e60d6396f51275130e8110816d7b30", "size": 68964, "url": "https://launcher.mojang.com/v1/objects/37b338f0e8e60d6396f51275130e8110816d7b30/zipfs.jar" } }, "executable": false, "type": "file" }, "lib/flavormap.properties": { "downloads": { "lzma": { "sha1": "2d5ef19ee77ccfc95c9413eea155cde59a48fadd", "size": 1541, "url": "https://launcher.mojang.com/v1/objects/2d5ef19ee77ccfc95c9413eea155cde59a48fadd/flavormap.properties" }, "raw": { "sha1": "4e66e8fe12d7f8b3b0c4e1a1915f329bb1fbf6d2", "size": 3901, "url": "https://launcher.mojang.com/v1/objects/4e66e8fe12d7f8b3b0c4e1a1915f329bb1fbf6d2/flavormap.properties" } }, "executable": false, "type": "file" }, "lib/fontconfig.RedHat.5.bfc": { "downloads": { "lzma": { "sha1": "5197f6e387f16458b7408134e38b06f20f625e4c", "size": 795, "url": "https://launcher.mojang.com/v1/objects/5197f6e387f16458b7408134e38b06f20f625e4c/fontconfig.RedHat.5.bfc" }, "raw": { "sha1": "fb806ada6e68f16a9fe2b726a39d9ef5a835c0c2", "size": 4532, "url": "https://launcher.mojang.com/v1/objects/fb806ada6e68f16a9fe2b726a39d9ef5a835c0c2/fontconfig.RedHat.5.bfc" } }, "executable": false, "type": "file" }, "lib/fontconfig.RedHat.5.properties.src": { "downloads": { "lzma": { "sha1": "3897ae198e96e5a687c9c9b218ff5df60c868e0d", "size": 1089, "url": "https://launcher.mojang.com/v1/objects/3897ae198e96e5a687c9c9b218ff5df60c868e0d/fontconfig.RedHat.5.properties.src" }, "raw": { "sha1": "c67d1a06cb37b66e69560c9f5e4be7cf08af0493", "size": 8841, "url": "https://launcher.mojang.com/v1/objects/c67d1a06cb37b66e69560c9f5e4be7cf08af0493/fontconfig.RedHat.5.properties.src" } }, "executable": false, "type": "file" }, "lib/fontconfig.RedHat.6.bfc": { "downloads": { "lzma": { "sha1": "ef2f5d1f8d620be9927db45d3a28bd75777245cb", "size": 818, "url": "https://launcher.mojang.com/v1/objects/ef2f5d1f8d620be9927db45d3a28bd75777245cb/fontconfig.RedHat.6.bfc" }, "raw": { "sha1": "9ba3b3e2c621c31d0ef1b2053c80f77419a19965", "size": 4250, "url": "https://launcher.mojang.com/v1/objects/9ba3b3e2c621c31d0ef1b2053c80f77419a19965/fontconfig.RedHat.6.bfc" } }, "executable": false, "type": "file" }, "lib/fontconfig.RedHat.6.properties.src": { "downloads": { "lzma": { "sha1": "74f4148f9d7ec3d67bbd724834d478a72cfdb0db", "size": 1111, "url": "https://launcher.mojang.com/v1/objects/74f4148f9d7ec3d67bbd724834d478a72cfdb0db/fontconfig.RedHat.6.properties.src" }, "raw": { "sha1": "768e58d4d314621c38daf9fde6d67119f370acd9", "size": 8735, "url": "https://launcher.mojang.com/v1/objects/768e58d4d314621c38daf9fde6d67119f370acd9/fontconfig.RedHat.6.properties.src" } }, "executable": false, "type": "file" }, "lib/fontconfig.SuSE.10.bfc": { "downloads": { "lzma": { "sha1": "d8fe9b1d8d02368dcd452de93024c6f60670eb87", "size": 1083, "url": "https://launcher.mojang.com/v1/objects/d8fe9b1d8d02368dcd452de93024c6f60670eb87/fontconfig.SuSE.10.bfc" }, "raw": { "sha1": "ffd0dfbd1553e15b11649a73a0b3f48318138e0d", "size": 6702, "url": "https://launcher.mojang.com/v1/objects/ffd0dfbd1553e15b11649a73a0b3f48318138e0d/fontconfig.SuSE.10.bfc" } }, "executable": false, "type": "file" }, "lib/fontconfig.SuSE.10.properties.src": { "downloads": { "lzma": { "sha1": "2c382bd741a9e23114be3da82dee8290ebfca8a9", "size": 1555, "url": "https://launcher.mojang.com/v1/objects/2c382bd741a9e23114be3da82dee8290ebfca8a9/fontconfig.SuSE.10.properties.src" }, "raw": { "sha1": "a38dbdbbc514567b8281e1aea726865f37e97894", "size": 16772, "url": "https://launcher.mojang.com/v1/objects/a38dbdbbc514567b8281e1aea726865f37e97894/fontconfig.SuSE.10.properties.src" } }, "executable": false, "type": "file" }, "lib/fontconfig.SuSE.11.bfc": { "downloads": { "lzma": { "sha1": "2b78cbf11289c9858951fea7180696ba3b7176d6", "size": 1092, "url": "https://launcher.mojang.com/v1/objects/2b78cbf11289c9858951fea7180696ba3b7176d6/fontconfig.SuSE.11.bfc" }, "raw": { "sha1": "a4d8500fcb47f6327460a95851b1368660da8302", "size": 7032, "url": "https://launcher.mojang.com/v1/objects/a4d8500fcb47f6327460a95851b1368660da8302/fontconfig.SuSE.11.bfc" } }, "executable": false, "type": "file" }, "lib/fontconfig.SuSE.11.properties.src": { "downloads": { "lzma": { "sha1": "5c1635803906e2c59d36492dec724dd7ae49a5ab", "size": 1589, "url": "https://launcher.mojang.com/v1/objects/5c1635803906e2c59d36492dec724dd7ae49a5ab/fontconfig.SuSE.11.properties.src" }, "raw": { "sha1": "c4b69589e41a7279a71866a9134213be19cdf88d", "size": 16781, "url": "https://launcher.mojang.com/v1/objects/c4b69589e41a7279a71866a9134213be19cdf88d/fontconfig.SuSE.11.properties.src" } }, "executable": false, "type": "file" }, "lib/fontconfig.Turbo.bfc": { "downloads": { "lzma": { "sha1": "1c771325d9ee4af209a3db92294451d58962c7a4", "size": 822, "url": "https://launcher.mojang.com/v1/objects/1c771325d9ee4af209a3db92294451d58962c7a4/fontconfig.Turbo.bfc" }, "raw": { "sha1": "f24368deeb85cc9d0781083bc56e773518d72219", "size": 4668, "url": "https://launcher.mojang.com/v1/objects/f24368deeb85cc9d0781083bc56e773518d72219/fontconfig.Turbo.bfc" } }, "executable": false, "type": "file" }, "lib/fontconfig.Turbo.properties.src": { "downloads": { "lzma": { "sha1": "7748ffa17e2c8a34754138efa963ba39bd1cbbb3", "size": 1113, "url": "https://launcher.mojang.com/v1/objects/7748ffa17e2c8a34754138efa963ba39bd1cbbb3/fontconfig.Turbo.properties.src" }, "raw": { "sha1": "2bb7258bed7ccd4f117e4e5f892c9b13424b0c82", "size": 9192, "url": "https://launcher.mojang.com/v1/objects/2bb7258bed7ccd4f117e4e5f892c9b13424b0c82/fontconfig.Turbo.properties.src" } }, "executable": false, "type": "file" }, "lib/fontconfig.bfc": { "downloads": { "lzma": { "sha1": "be6d49ee8c64f458c4f0e64254963fec48d25150", "size": 286, "url": "https://launcher.mojang.com/v1/objects/be6d49ee8c64f458c4f0e64254963fec48d25150/fontconfig.bfc" }, "raw": { "sha1": "de39b0e19637f58d92a0188122514aa7247ebb5b", "size": 1678, "url": "https://launcher.mojang.com/v1/objects/de39b0e19637f58d92a0188122514aa7247ebb5b/fontconfig.bfc" } }, "executable": false, "type": "file" }, "lib/fontconfig.properties.src": { "downloads": { "lzma": { "sha1": "9498d5e00e5401200667687e826e28c60fa60ba4", "size": 417, "url": "https://launcher.mojang.com/v1/objects/9498d5e00e5401200667687e826e28c60fa60ba4/fontconfig.properties.src" }, "raw": { "sha1": "3617ff1424fd204415242565541facf862b16eb4", "size": 1938, "url": "https://launcher.mojang.com/v1/objects/3617ff1424fd204415242565541facf862b16eb4/fontconfig.properties.src" } }, "executable": false, "type": "file" }, "lib/fonts": { "type": "directory" }, "lib/fonts/LucidaBrightDemiBold.ttf": { "downloads": { "lzma": { "sha1": "4f748750831a7719440dff5457f4d207d0f24d21", "size": 42347, "url": "https://launcher.mojang.com/v1/objects/4f748750831a7719440dff5457f4d207d0f24d21/LucidaBrightDemiBold.ttf" }, "raw": { "sha1": "b5c97f985639e19a3b712193ee48b55dda581fd1", "size": 75144, "url": "https://launcher.mojang.com/v1/objects/b5c97f985639e19a3b712193ee48b55dda581fd1/LucidaBrightDemiBold.ttf" } }, "executable": false, "type": "file" }, "lib/fonts/LucidaBrightDemiItalic.ttf": { "downloads": { "lzma": { "sha1": "f82e9a688553c100ecb98412b985807ed56dff5d", "size": 43119, "url": "https://launcher.mojang.com/v1/objects/f82e9a688553c100ecb98412b985807ed56dff5d/LucidaBrightDemiItalic.ttf" }, "raw": { "sha1": "1fd1f757febf3e5f5fbb7fbf7a56587a40d57de7", "size": 75124, "url": "https://launcher.mojang.com/v1/objects/1fd1f757febf3e5f5fbb7fbf7a56587a40d57de7/LucidaBrightDemiItalic.ttf" } }, "executable": false, "type": "file" }, "lib/fonts/LucidaBrightItalic.ttf": { "downloads": { "lzma": { "sha1": "6d630df719271319c3d53f90a3d425118b908266", "size": 46206, "url": "https://launcher.mojang.com/v1/objects/6d630df719271319c3d53f90a3d425118b908266/LucidaBrightItalic.ttf" }, "raw": { "sha1": "aa5c037865c563726ecd63d61ca26443589be425", "size": 80856, "url": "https://launcher.mojang.com/v1/objects/aa5c037865c563726ecd63d61ca26443589be425/LucidaBrightItalic.ttf" } }, "executable": false, "type": "file" }, "lib/fonts/LucidaBrightRegular.ttf": { "downloads": { "lzma": { "sha1": "4b2e31aaec2238b6ecf9f845bad0a1c6d09fbbfe", "size": 181085, "url": "https://launcher.mojang.com/v1/objects/4b2e31aaec2238b6ecf9f845bad0a1c6d09fbbfe/LucidaBrightRegular.ttf" }, "raw": { "sha1": "5d7ed564791c900a8786936930ba99385653139c", "size": 344908, "url": "https://launcher.mojang.com/v1/objects/5d7ed564791c900a8786936930ba99385653139c/LucidaBrightRegular.ttf" } }, "executable": false, "type": "file" }, "lib/fonts/LucidaSansDemiBold.ttf": { "downloads": { "lzma": { "sha1": "079b16dc3c4918ab1f4f760b6dc5e6586c219042", "size": 173229, "url": "https://launcher.mojang.com/v1/objects/079b16dc3c4918ab1f4f760b6dc5e6586c219042/LucidaSansDemiBold.ttf" }, "raw": { "sha1": "92b79fefc35e96190250c602a8fed85276b32a95", "size": 317896, "url": "https://launcher.mojang.com/v1/objects/92b79fefc35e96190250c602a8fed85276b32a95/LucidaSansDemiBold.ttf" } }, "executable": false, "type": "file" }, "lib/fonts/LucidaSansRegular.ttf": { "downloads": { "lzma": { "sha1": "64a65d7b94d7153d20957ef6d06bebb4dd0f48e4", "size": 326062, "url": "https://launcher.mojang.com/v1/objects/64a65d7b94d7153d20957ef6d06bebb4dd0f48e4/LucidaSansRegular.ttf" }, "raw": { "sha1": "39cc8bcb8d4a71d4657fc92ef0b9f4e3e9e67add", "size": 698236, "url": "https://launcher.mojang.com/v1/objects/39cc8bcb8d4a71d4657fc92ef0b9f4e3e9e67add/LucidaSansRegular.ttf" } }, "executable": false, "type": "file" }, "lib/fonts/LucidaTypewriterBold.ttf": { "downloads": { "lzma": { "sha1": "cdb017f7c34bea0802bc5ea5583aef721ed99c49", "size": 130412, "url": "https://launcher.mojang.com/v1/objects/cdb017f7c34bea0802bc5ea5583aef721ed99c49/LucidaTypewriterBold.ttf" }, "raw": { "sha1": "a5da2eb49448f461470387c939f0e69119310e0b", "size": 234068, "url": "https://launcher.mojang.com/v1/objects/a5da2eb49448f461470387c939f0e69119310e0b/LucidaTypewriterBold.ttf" } }, "executable": false, "type": "file" }, "lib/fonts/LucidaTypewriterRegular.ttf": { "downloads": { "lzma": { "sha1": "aeda4a09a53783b0dc97de8e20071bea874cbfe5", "size": 135184, "url": "https://launcher.mojang.com/v1/objects/aeda4a09a53783b0dc97de8e20071bea874cbfe5/LucidaTypewriterRegular.ttf" }, "raw": { "sha1": "c144dcafe4faf2e79cfd74d8134a631f30234db1", "size": 242700, "url": "https://launcher.mojang.com/v1/objects/c144dcafe4faf2e79cfd74d8134a631f30234db1/LucidaTypewriterRegular.ttf" } }, "executable": false, "type": "file" }, "lib/fonts/fonts.dir": { "downloads": { "lzma": { "sha1": "68f2dd93b215ec8b8d9409d2b9c825632c6b907d", "size": 273, "url": "https://launcher.mojang.com/v1/objects/68f2dd93b215ec8b8d9409d2b9c825632c6b907d/fonts.dir" }, "raw": { "sha1": "97f40cca185c954adf5cc582345a7cb8e4c50578", "size": 4041, "url": "https://launcher.mojang.com/v1/objects/97f40cca185c954adf5cc582345a7cb8e4c50578/fonts.dir" } }, "executable": false, "type": "file" }, "lib/hijrah-config-umalqura.properties": { "downloads": { "lzma": { "sha1": "02e8d296e3b18a450f1ed1547cbf2b7275664c9a", "size": 1969, "url": "https://launcher.mojang.com/v1/objects/02e8d296e3b18a450f1ed1547cbf2b7275664c9a/hijrah-config-umalqura.properties" }, "raw": { "sha1": "84aa425100740722e91f4725caf849e7863d12ba", "size": 13962, "url": "https://launcher.mojang.com/v1/objects/84aa425100740722e91f4725caf849e7863d12ba/hijrah-config-umalqura.properties" } }, "executable": false, "type": "file" }, "lib/images": { "type": "directory" }, "lib/images/cursors": { "type": "directory" }, "lib/images/cursors/cursors.properties": { "downloads": { "lzma": { "sha1": "612bd0f610ee1023947c4a2a8d3fc7d6f97e7d8f", "size": 385, "url": "https://launcher.mojang.com/v1/objects/612bd0f610ee1023947c4a2a8d3fc7d6f97e7d8f/cursors.properties" }, "raw": { "sha1": "f2b9a22ddd0a77869497a64f28f07e89a7d41f48", "size": 1274, "url": "https://launcher.mojang.com/v1/objects/f2b9a22ddd0a77869497a64f28f07e89a7d41f48/cursors.properties" } }, "executable": false, "type": "file" }, "lib/images/cursors/invalid32x32.gif": { "downloads": { "raw": { "sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif" } }, "executable": false, "type": "file" }, "lib/images/cursors/motif_CopyDrop32x32.gif": { "downloads": { "raw": { "sha1": "eb7620fae702172aa663a19d170a0b929d3b11d1", "size": 158, "url": "https://launcher.mojang.com/v1/objects/eb7620fae702172aa663a19d170a0b929d3b11d1/motif_CopyDrop32x32.gif" } }, "executable": false, "type": "file" }, "lib/images/cursors/motif_CopyNoDrop32x32.gif": { "downloads": { "raw": { "sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif" } }, "executable": false, "type": "file" }, "lib/images/cursors/motif_LinkDrop32x32.gif": { "downloads": { "raw": { "sha1": "9699137f990c240e714481563181069c8f6c17bb", "size": 162, "url": "https://launcher.mojang.com/v1/objects/9699137f990c240e714481563181069c8f6c17bb/motif_LinkDrop32x32.gif" } }, "executable": false, "type": "file" }, "lib/images/cursors/motif_LinkNoDrop32x32.gif": { "downloads": { "raw": { "sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif" } }, "executable": false, "type": "file" }, "lib/images/cursors/motif_MoveDrop32x32.gif": { "downloads": { "raw": { "sha1": "03c1617ce3c5ab8af03e46d30a8c8f31ab57fb1b", "size": 141, "url": "https://launcher.mojang.com/v1/objects/03c1617ce3c5ab8af03e46d30a8c8f31ab57fb1b/motif_MoveDrop32x32.gif" } }, "executable": false, "type": "file" }, "lib/images/cursors/motif_MoveNoDrop32x32.gif": { "downloads": { "raw": { "sha1": "259edc45b4569427e8319895a444f4295d54348f", "size": 153, "url": "https://launcher.mojang.com/v1/objects/259edc45b4569427e8319895a444f4295d54348f/invalid32x32.gif" } }, "executable": false, "type": "file" }, "lib/images/icons": { "type": "directory" }, "lib/images/icons/sun-java.png": { "downloads": { "raw": { "sha1": "d101b693aa054f51097eebdfeed8b8a6ca7b55b8", "size": 4707, "url": "https://launcher.mojang.com/v1/objects/d101b693aa054f51097eebdfeed8b8a6ca7b55b8/sun-java.png" } }, "executable": false, "type": "file" }, "lib/images/icons/sun-java_HighContrast.png": { "downloads": { "raw": { "sha1": "a6b1e418d6b5d03719b96f61f0c5236a60970151", "size": 3729, "url": "https://launcher.mojang.com/v1/objects/a6b1e418d6b5d03719b96f61f0c5236a60970151/sun-java_HighContrast.png" } }, "executable": false, "type": "file" }, "lib/images/icons/sun-java_HighContrastInverse.png": { "downloads": { "raw": { "sha1": "2dda28b9bddc9b5b018e3e8a8b062a99d9b2f887", "size": 3777, "url": "https://launcher.mojang.com/v1/objects/2dda28b9bddc9b5b018e3e8a8b062a99d9b2f887/sun-java_HighContrastInverse.png" } }, "executable": false, "type": "file" }, "lib/images/icons/sun-java_LowContrast.png": { "downloads": { "raw": { "sha1": "7714cc4e894c3626c8da6fe742ed22b2829122d9", "size": 4012, "url": "https://launcher.mojang.com/v1/objects/7714cc4e894c3626c8da6fe742ed22b2829122d9/sun-java_LowContrast.png" } }, "executable": false, "type": "file" }, "lib/javafx.properties": { "downloads": { "raw": { "sha1": "49e6b75d109e5fd3f6cbe7cc5fa9a7980796d14d", "size": 56, "url": "https://launcher.mojang.com/v1/objects/49e6b75d109e5fd3f6cbe7cc5fa9a7980796d14d/javafx.properties" } }, "executable": false, "type": "file" }, "lib/javaws.jar": { "downloads": { "raw": { "sha1": "04fa5ae04ead65b91be5dee575497e49ffd49fe9", "size": 488118, "url": "https://launcher.mojang.com/v1/objects/04fa5ae04ead65b91be5dee575497e49ffd49fe9/javaws.jar" } }, "executable": false, "type": "file" }, "lib/jce.jar": { "downloads": { "raw": { "sha1": "5460adee09cc5fc8829c0acfc46c34670a7d70a0", "size": 115646, "url": "https://launcher.mojang.com/v1/objects/5460adee09cc5fc8829c0acfc46c34670a7d70a0/jce.jar" } }, "executable": false, "type": "file" }, "lib/jexec": { "downloads": { "lzma": { "sha1": "2d4323d4e060f8126d026ca6c03b8972aedd2fab", "size": 3311, "url": "https://launcher.mojang.com/v1/objects/2d4323d4e060f8126d026ca6c03b8972aedd2fab/jexec" }, "raw": { "sha1": "6aa01f1d8d103974164bcfaea03c04eeeefd7d41", "size": 13376, "url": "https://launcher.mojang.com/v1/objects/6aa01f1d8d103974164bcfaea03c04eeeefd7d41/jexec" } }, "executable": true, "type": "file" }, "lib/jfr": { "type": "directory" }, "lib/jfr.jar": { "downloads": { "lzma": { "sha1": "5b9d615c91c72f4fe356d9b4105946679452d1e1", "size": 137982, "url": "https://launcher.mojang.com/v1/objects/5b9d615c91c72f4fe356d9b4105946679452d1e1/jfr.jar" }, "raw": { "sha1": "0f3fd66a336703d935bdc22ad8082bc51d34e534", "size": 560713, "url": "https://launcher.mojang.com/v1/objects/0f3fd66a336703d935bdc22ad8082bc51d34e534/jfr.jar" } }, "executable": false, "type": "file" }, "lib/jfr/default.jfc": { "downloads": { "lzma": { "sha1": "373ddd878146dd8ce8991c2c5115a05a82859bdb", "size": 2207, "url": "https://launcher.mojang.com/v1/objects/373ddd878146dd8ce8991c2c5115a05a82859bdb/default.jfc" }, "raw": { "sha1": "1a64b68d0e7d43f8149faba94440be54f4f24527", "size": 20109, "url": "https://launcher.mojang.com/v1/objects/1a64b68d0e7d43f8149faba94440be54f4f24527/default.jfc" } }, "executable": false, "type": "file" }, "lib/jfr/profile.jfc": { "downloads": { "lzma": { "sha1": "3dcdc5feee3ccfb66bc8726b666944cd4bdadae3", "size": 2199, "url": "https://launcher.mojang.com/v1/objects/3dcdc5feee3ccfb66bc8726b666944cd4bdadae3/profile.jfc" }, "raw": { "sha1": "5d7d08a595f76322c51ae43ea966fbba6b69eebe", "size": 20065, "url": "https://launcher.mojang.com/v1/objects/5d7d08a595f76322c51ae43ea966fbba6b69eebe/profile.jfc" } }, "executable": false, "type": "file" }, "lib/jfxswt.jar": { "downloads": { "raw": { "sha1": "99d9a264c898d84c01e1c42565e7fe1a89dcd72d", "size": 33932, "url": "https://launcher.mojang.com/v1/objects/99d9a264c898d84c01e1c42565e7fe1a89dcd72d/jfxswt.jar" } }, "executable": false, "type": "file" }, "lib/jsse.jar": { "downloads": { "lzma": { "sha1": "94a17dfbc2e76cd12c33970a15341424f875a9ce", "size": 187549, "url": "https://launcher.mojang.com/v1/objects/94a17dfbc2e76cd12c33970a15341424f875a9ce/jsse.jar" }, "raw": { "sha1": "92c5c626e8a2d16f41272c0e404d4f992dd8310a", "size": 675599, "url": "https://launcher.mojang.com/v1/objects/92c5c626e8a2d16f41272c0e404d4f992dd8310a/jsse.jar" } }, "executable": false, "type": "file" }, "lib/jvm.hprof.txt": { "downloads": { "lzma": { "sha1": "eccdb240a815b2a83a502749339b27bb8669965b", "size": 1863, "url": "https://launcher.mojang.com/v1/objects/eccdb240a815b2a83a502749339b27bb8669965b/jvm.hprof.txt" }, "raw": { "sha1": "fbd61d52534cdd0c15df332114d469c65d001e33", "size": 4226, "url": "https://launcher.mojang.com/v1/objects/fbd61d52534cdd0c15df332114d469c65d001e33/jvm.hprof.txt" } }, "executable": false, "type": "file" }, "lib/locale": { "type": "directory" }, "lib/locale/de": { "type": "directory" }, "lib/locale/de/LC_MESSAGES": { "type": "directory" }, "lib/locale/de/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "3061d922907cc557208109088fc6ab81d577ff6f", "size": 970, "url": "https://launcher.mojang.com/v1/objects/3061d922907cc557208109088fc6ab81d577ff6f/sunw_java_plugin.mo" }, "raw": { "sha1": "5b223a3d723ac1cfce63623fb109f2868d47d1b7", "size": 2483, "url": "https://launcher.mojang.com/v1/objects/5b223a3d723ac1cfce63623fb109f2868d47d1b7/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/es": { "type": "directory" }, "lib/locale/es/LC_MESSAGES": { "type": "directory" }, "lib/locale/es/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "24338049a89b323e17182b3a3006b50565d4fa0f", "size": 979, "url": "https://launcher.mojang.com/v1/objects/24338049a89b323e17182b3a3006b50565d4fa0f/sunw_java_plugin.mo" }, "raw": { "sha1": "6cc63dc97f2fdb2ed799e48b1dc98c4f37cdecc1", "size": 2477, "url": "https://launcher.mojang.com/v1/objects/6cc63dc97f2fdb2ed799e48b1dc98c4f37cdecc1/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/fr": { "type": "directory" }, "lib/locale/fr/LC_MESSAGES": { "type": "directory" }, "lib/locale/fr/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "22796a48ef39f57d2d6fa70f41308e493d7f05c1", "size": 1033, "url": "https://launcher.mojang.com/v1/objects/22796a48ef39f57d2d6fa70f41308e493d7f05c1/sunw_java_plugin.mo" }, "raw": { "sha1": "d9d5b458db6e83fdf85c3526aeee3f57c4929840", "size": 2746, "url": "https://launcher.mojang.com/v1/objects/d9d5b458db6e83fdf85c3526aeee3f57c4929840/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/it": { "type": "directory" }, "lib/locale/it/LC_MESSAGES": { "type": "directory" }, "lib/locale/it/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "59a4cae38bfb8927745674d0efc2f284bc277987", "size": 958, "url": "https://launcher.mojang.com/v1/objects/59a4cae38bfb8927745674d0efc2f284bc277987/sunw_java_plugin.mo" }, "raw": { "sha1": "f6e72e3b2141ccc3dffab10ae14a754e494577ba", "size": 2434, "url": "https://launcher.mojang.com/v1/objects/f6e72e3b2141ccc3dffab10ae14a754e494577ba/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/ja": { "type": "directory" }, "lib/locale/ja/LC_MESSAGES": { "type": "directory" }, "lib/locale/ja/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "7d6aeed563e1cefcf0224cf522048468088884a9", "size": 1036, "url": "https://launcher.mojang.com/v1/objects/7d6aeed563e1cefcf0224cf522048468088884a9/sunw_java_plugin.mo" }, "raw": { "sha1": "378881a8cb8dd2aebb43eacd0c68519be4f258b1", "size": 2415, "url": "https://launcher.mojang.com/v1/objects/378881a8cb8dd2aebb43eacd0c68519be4f258b1/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/ko": { "type": "directory" }, "lib/locale/ko.UTF-8": { "type": "directory" }, "lib/locale/ko.UTF-8/LC_MESSAGES": { "type": "directory" }, "lib/locale/ko.UTF-8/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "12ee3b21511e8497d95ea0ba9d6fe519227d0b16", "size": 1069, "url": "https://launcher.mojang.com/v1/objects/12ee3b21511e8497d95ea0ba9d6fe519227d0b16/sunw_java_plugin.mo" }, "raw": { "sha1": "cb19df01c59662dbe2f4050b1290d374b82fe1fa", "size": 2753, "url": "https://launcher.mojang.com/v1/objects/cb19df01c59662dbe2f4050b1290d374b82fe1fa/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/ko/LC_MESSAGES": { "type": "directory" }, "lib/locale/ko/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "6e2e47c64c360517fd436bc79c823b5679a1efe6", "size": 996, "url": "https://launcher.mojang.com/v1/objects/6e2e47c64c360517fd436bc79c823b5679a1efe6/sunw_java_plugin.mo" }, "raw": { "sha1": "12c8a118d150c78f719314df6dec49a967af71e9", "size": 2399, "url": "https://launcher.mojang.com/v1/objects/12c8a118d150c78f719314df6dec49a967af71e9/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/pt_BR": { "type": "directory" }, "lib/locale/pt_BR/LC_MESSAGES": { "type": "directory" }, "lib/locale/pt_BR/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "bcaa7e7916493f071f1bf64bf58c6b038e3569c9", "size": 940, "url": "https://launcher.mojang.com/v1/objects/bcaa7e7916493f071f1bf64bf58c6b038e3569c9/sunw_java_plugin.mo" }, "raw": { "sha1": "a3bc0c43994c53c59bba94982cf95f6d36283dd0", "size": 2420, "url": "https://launcher.mojang.com/v1/objects/a3bc0c43994c53c59bba94982cf95f6d36283dd0/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/sv": { "type": "directory" }, "lib/locale/sv/LC_MESSAGES": { "type": "directory" }, "lib/locale/sv/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "76017835d6261fe2eedbcbe5eb08a7484c3080c5", "size": 946, "url": "https://launcher.mojang.com/v1/objects/76017835d6261fe2eedbcbe5eb08a7484c3080c5/sunw_java_plugin.mo" }, "raw": { "sha1": "09a47686edec4bbb34e82fbd08559f8bb6266544", "size": 2359, "url": "https://launcher.mojang.com/v1/objects/09a47686edec4bbb34e82fbd08559f8bb6266544/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/zh": { "type": "directory" }, "lib/locale/zh.GBK": { "type": "directory" }, "lib/locale/zh.GBK/LC_MESSAGES": { "type": "directory" }, "lib/locale/zh.GBK/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "75fd04045bf5890b8bb822770bfdb90a2e9ea65b", "size": 902, "url": "https://launcher.mojang.com/v1/objects/75fd04045bf5890b8bb822770bfdb90a2e9ea65b/sunw_java_plugin.mo" }, "raw": { "sha1": "7006fe7767b8807441a1f359a90509b3e507b0d1", "size": 2002, "url": "https://launcher.mojang.com/v1/objects/7006fe7767b8807441a1f359a90509b3e507b0d1/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/zh/LC_MESSAGES": { "type": "directory" }, "lib/locale/zh/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "75fd04045bf5890b8bb822770bfdb90a2e9ea65b", "size": 902, "url": "https://launcher.mojang.com/v1/objects/75fd04045bf5890b8bb822770bfdb90a2e9ea65b/sunw_java_plugin.mo" }, "raw": { "sha1": "7006fe7767b8807441a1f359a90509b3e507b0d1", "size": 2002, "url": "https://launcher.mojang.com/v1/objects/7006fe7767b8807441a1f359a90509b3e507b0d1/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/zh_HK.BIG5HK": { "type": "directory" }, "lib/locale/zh_HK.BIG5HK/LC_MESSAGES": { "type": "directory" }, "lib/locale/zh_HK.BIG5HK/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "3a1397bb1b1741697be1479232b6d9599940c851", "size": 912, "url": "https://launcher.mojang.com/v1/objects/3a1397bb1b1741697be1479232b6d9599940c851/sunw_java_plugin.mo" }, "raw": { "sha1": "c6023544067278c78599921f1032de353ff7da42", "size": 2025, "url": "https://launcher.mojang.com/v1/objects/c6023544067278c78599921f1032de353ff7da42/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/zh_TW": { "type": "directory" }, "lib/locale/zh_TW.BIG5": { "type": "directory" }, "lib/locale/zh_TW.BIG5/LC_MESSAGES": { "type": "directory" }, "lib/locale/zh_TW.BIG5/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "3a1397bb1b1741697be1479232b6d9599940c851", "size": 912, "url": "https://launcher.mojang.com/v1/objects/3a1397bb1b1741697be1479232b6d9599940c851/sunw_java_plugin.mo" }, "raw": { "sha1": "c6023544067278c78599921f1032de353ff7da42", "size": 2025, "url": "https://launcher.mojang.com/v1/objects/c6023544067278c78599921f1032de353ff7da42/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/locale/zh_TW/LC_MESSAGES": { "type": "directory" }, "lib/locale/zh_TW/LC_MESSAGES/sunw_java_plugin.mo": { "downloads": { "lzma": { "sha1": "c05e610e75182f0c4e77f3e7a4d9670ed62bf63c", "size": 897, "url": "https://launcher.mojang.com/v1/objects/c05e610e75182f0c4e77f3e7a4d9670ed62bf63c/sunw_java_plugin.mo" }, "raw": { "sha1": "f9b972dd059eae3cd337dfcef6a178e8ed8a7db6", "size": 2025, "url": "https://launcher.mojang.com/v1/objects/f9b972dd059eae3cd337dfcef6a178e8ed8a7db6/sunw_java_plugin.mo" } }, "executable": false, "type": "file" }, "lib/logging.properties": { "downloads": { "lzma": { "sha1": "642202a58e5216d086ad37c0b5a633be802edc78", "size": 896, "url": "https://launcher.mojang.com/v1/objects/642202a58e5216d086ad37c0b5a633be802edc78/logging.properties" }, "raw": { "sha1": "89da8094484891f9ec1fa40c6c8b61f94c5869d0", "size": 2455, "url": "https://launcher.mojang.com/v1/objects/89da8094484891f9ec1fa40c6c8b61f94c5869d0/logging.properties" } }, "executable": false, "type": "file" }, "lib/management": { "type": "directory" }, "lib/management-agent.jar": { "downloads": { "lzma": { "sha1": "3ea0bf17e14b3428296a0f4011bf4025fcbfa4bd", "size": 243, "url": "https://launcher.mojang.com/v1/objects/3ea0bf17e14b3428296a0f4011bf4025fcbfa4bd/management-agent.jar" }, "raw": { "sha1": "9fbed36522aa3a80bac08a328942cbc5ef39ca8e", "size": 381, "url": "https://launcher.mojang.com/v1/objects/9fbed36522aa3a80bac08a328942cbc5ef39ca8e/management-agent.jar" } }, "executable": false, "type": "file" }, "lib/management/jmxremote.access": { "downloads": { "lzma": { "sha1": "69042ff1b14165db19c9d728614639dec16d6a31", "size": 1419, "url": "https://launcher.mojang.com/v1/objects/69042ff1b14165db19c9d728614639dec16d6a31/jmxremote.access" }, "raw": { "sha1": "21200eaad898ba4a2a8834a032efb6616fabb930", "size": 3998, "url": "https://launcher.mojang.com/v1/objects/21200eaad898ba4a2a8834a032efb6616fabb930/jmxremote.access" } }, "executable": false, "type": "file" }, "lib/management/jmxremote.password.template": { "downloads": { "lzma": { "sha1": "556c64b1e920766f8867be3964de6e49f5b81a60", "size": 1129, "url": "https://launcher.mojang.com/v1/objects/556c64b1e920766f8867be3964de6e49f5b81a60/jmxremote.password.template" }, "raw": { "sha1": "c1e0f01408bf20fbbb8b4810520c725f70050db5", "size": 2856, "url": "https://launcher.mojang.com/v1/objects/c1e0f01408bf20fbbb8b4810520c725f70050db5/jmxremote.password.template" } }, "executable": false, "type": "file" }, "lib/management/management.properties": { "downloads": { "lzma": { "sha1": "3e52f9baa6394ca6956845424c607e5cde5d3c67", "size": 3176, "url": "https://launcher.mojang.com/v1/objects/3e52f9baa6394ca6956845424c607e5cde5d3c67/management.properties" }, "raw": { "sha1": "e0451d8d7d9e84d7b1c39ec7d00993307a5cbbf1", "size": 14630, "url": "https://launcher.mojang.com/v1/objects/e0451d8d7d9e84d7b1c39ec7d00993307a5cbbf1/management.properties" } }, "executable": false, "type": "file" }, "lib/management/snmp.acl.template": { "downloads": { "lzma": { "sha1": "9a4aa6396c3b488b0663bed5e5ecb762985669c9", "size": 1121, "url": "https://launcher.mojang.com/v1/objects/9a4aa6396c3b488b0663bed5e5ecb762985669c9/snmp.acl.template" }, "raw": { "sha1": "2e9f9ac287274532eb1f0d1afcefd7f3e97cc794", "size": 3376, "url": "https://launcher.mojang.com/v1/objects/2e9f9ac287274532eb1f0d1afcefd7f3e97cc794/snmp.acl.template" } }, "executable": false, "type": "file" }, "lib/meta-index": { "downloads": { "lzma": { "sha1": "1ac60b31362fda4725c665b591c5fbe384cbc8c1", "size": 788, "url": "https://launcher.mojang.com/v1/objects/1ac60b31362fda4725c665b591c5fbe384cbc8c1/meta-index" }, "raw": { "sha1": "bf204f09242203e713c31785158a0792f9edb600", "size": 2034, "url": "https://launcher.mojang.com/v1/objects/bf204f09242203e713c31785158a0792f9edb600/meta-index" } }, "executable": false, "type": "file" }, "lib/net.properties": { "downloads": { "lzma": { "sha1": "e9ec3981a0797bf55bb87b24d9eb651ce7e6916b", "size": 1830, "url": "https://launcher.mojang.com/v1/objects/e9ec3981a0797bf55bb87b24d9eb651ce7e6916b/net.properties" }, "raw": { "sha1": "fd9471742eb759f4478bb1de9a0dc0527265b6ea", "size": 5352, "url": "https://launcher.mojang.com/v1/objects/fd9471742eb759f4478bb1de9a0dc0527265b6ea/net.properties" } }, "executable": false, "type": "file" }, "lib/oblique-fonts": { "type": "directory" }, "lib/oblique-fonts/LucidaSansDemiOblique.ttf": { "downloads": { "lzma": { "sha1": "49c8980c1b89bbdbab59d0f5bd5bebf0afcb93b2", "size": 38580, "url": "https://launcher.mojang.com/v1/objects/49c8980c1b89bbdbab59d0f5bd5bebf0afcb93b2/LucidaSansDemiOblique.ttf" }, "raw": { "sha1": "53e4e12a675ac222469341c3dbc102464a1be4c7", "size": 91352, "url": "https://launcher.mojang.com/v1/objects/53e4e12a675ac222469341c3dbc102464a1be4c7/LucidaSansDemiOblique.ttf" } }, "executable": false, "type": "file" }, "lib/oblique-fonts/LucidaSansOblique.ttf": { "downloads": { "lzma": { "sha1": "553123c0edcd08035dede4ffd92b5b81c9a7538a", "size": 116575, "url": "https://launcher.mojang.com/v1/objects/553123c0edcd08035dede4ffd92b5b81c9a7538a/LucidaSansOblique.ttf" }, "raw": { "sha1": "95a195ad4fc520b3e395c85b747fc3024d118dd9", "size": 253724, "url": "https://launcher.mojang.com/v1/objects/95a195ad4fc520b3e395c85b747fc3024d118dd9/LucidaSansOblique.ttf" } }, "executable": false, "type": "file" }, "lib/oblique-fonts/LucidaTypewriterBoldOblique.ttf": { "downloads": { "lzma": { "sha1": "2475b08151556ad4d89bb1d2b6494c6bee9abd82", "size": 29954, "url": "https://launcher.mojang.com/v1/objects/2475b08151556ad4d89bb1d2b6494c6bee9abd82/LucidaTypewriterBoldOblique.ttf" }, "raw": { "sha1": "f331fc8b0cc494702bc46b690f2b8eed36469a02", "size": 63168, "url": "https://launcher.mojang.com/v1/objects/f331fc8b0cc494702bc46b690f2b8eed36469a02/LucidaTypewriterBoldOblique.ttf" } }, "executable": false, "type": "file" }, "lib/oblique-fonts/LucidaTypewriterOblique.ttf": { "downloads": { "lzma": { "sha1": "5b970bc3b7abb21dce1aa28ff7f03459d351e552", "size": 60133, "url": "https://launcher.mojang.com/v1/objects/5b970bc3b7abb21dce1aa28ff7f03459d351e552/LucidaTypewriterOblique.ttf" }, "raw": { "sha1": "f8ea00db73f8a89a27674d050edc37c2280930e1", "size": 137484, "url": "https://launcher.mojang.com/v1/objects/f8ea00db73f8a89a27674d050edc37c2280930e1/LucidaTypewriterOblique.ttf" } }, "executable": false, "type": "file" }, "lib/oblique-fonts/fonts.dir": { "downloads": { "lzma": { "sha1": "067528c789bd713c7c3f34e779aa6e2e8253dcf6", "size": 188, "url": "https://launcher.mojang.com/v1/objects/067528c789bd713c7c3f34e779aa6e2e8253dcf6/fonts.dir" }, "raw": { "sha1": "5aee54ffba9e33de56fd84ef64fa496b898585bb", "size": 2115, "url": "https://launcher.mojang.com/v1/objects/5aee54ffba9e33de56fd84ef64fa496b898585bb/fonts.dir" } }, "executable": false, "type": "file" }, "lib/plugin.jar": { "downloads": { "raw": { "sha1": "3f250842c79112bae5369e372025b166990820e8", "size": 950772, "url": "https://launcher.mojang.com/v1/objects/3f250842c79112bae5369e372025b166990820e8/plugin.jar" } }, "executable": false, "type": "file" }, "lib/psfont.properties.ja": { "downloads": { "lzma": { "sha1": "7ca1cc244ed251cd1eb2347f1eea37d7d18c8ad4", "size": 701, "url": "https://launcher.mojang.com/v1/objects/7ca1cc244ed251cd1eb2347f1eea37d7d18c8ad4/psfont.properties.ja" }, "raw": { "sha1": "56ed1c661eeede17b4fae8c9de7b5edbad387abc", "size": 2796, "url": "https://launcher.mojang.com/v1/objects/56ed1c661eeede17b4fae8c9de7b5edbad387abc/psfont.properties.ja" } }, "executable": false, "type": "file" }, "lib/psfontj2d.properties": { "downloads": { "lzma": { "sha1": "4252fa01af8739a3545e2b705e3383892e22ab40", "size": 2278, "url": "https://launcher.mojang.com/v1/objects/4252fa01af8739a3545e2b705e3383892e22ab40/psfontj2d.properties" }, "raw": { "sha1": "aa327a22a49967f4d74afeee6726f505f209692f", "size": 10393, "url": "https://launcher.mojang.com/v1/objects/aa327a22a49967f4d74afeee6726f505f209692f/psfontj2d.properties" } }, "executable": false, "type": "file" }, "lib/resources.jar": { "downloads": { "lzma": { "sha1": "1b0e08441750dc17efe4b527aa146da6cc14e8a6", "size": 579294, "url": "https://launcher.mojang.com/v1/objects/1b0e08441750dc17efe4b527aa146da6cc14e8a6/resources.jar" }, "raw": { "sha1": "daa021906e4648d4c37e798c11733dc2047f2da1", "size": 3505206, "url": "https://launcher.mojang.com/v1/objects/daa021906e4648d4c37e798c11733dc2047f2da1/resources.jar" } }, "executable": false, "type": "file" }, "lib/rt.jar": { "downloads": { "lzma": { "sha1": "fc4a8681aeda29c2a2a3fd11bad7729543283f3d", "size": 14378994, "url": "https://launcher.mojang.com/v1/objects/fc4a8681aeda29c2a2a3fd11bad7729543283f3d/rt.jar" }, "raw": { "sha1": "5396b0954a20f3210f1f4f1886ead30880d6ebfe", "size": 66334986, "url": "https://launcher.mojang.com/v1/objects/5396b0954a20f3210f1f4f1886ead30880d6ebfe/rt.jar" } }, "executable": false, "type": "file" }, "lib/security": { "type": "directory" }, "lib/security/blacklist": { "downloads": { "lzma": { "sha1": "8206fce6c1d91a39fdf78e8e79e953913994a1cd", "size": 1969, "url": "https://launcher.mojang.com/v1/objects/8206fce6c1d91a39fdf78e8e79e953913994a1cd/blacklist" }, "raw": { "sha1": "d4ffb3857eab403955ce9d156e46d056061e6a5a", "size": 4054, "url": "https://launcher.mojang.com/v1/objects/d4ffb3857eab403955ce9d156e46d056061e6a5a/blacklist" } }, "executable": false, "type": "file" }, "lib/security/blacklisted.certs": { "downloads": { "lzma": { "sha1": "8311bead054caf6cfe678d4b7998de4caaabfa53", "size": 806, "url": "https://launcher.mojang.com/v1/objects/8311bead054caf6cfe678d4b7998de4caaabfa53/blacklisted.certs" }, "raw": { "sha1": "c5c005c29a80493f5c31cd7eb629ac1b9c752404", "size": 1273, "url": "https://launcher.mojang.com/v1/objects/c5c005c29a80493f5c31cd7eb629ac1b9c752404/blacklisted.certs" } }, "executable": false, "type": "file" }, "lib/security/cacerts": { "downloads": { "lzma": { "sha1": "654dd94809655d5b28385cbb5eba8d6ad9f2c1aa", "size": 67802, "url": "https://launcher.mojang.com/v1/objects/654dd94809655d5b28385cbb5eba8d6ad9f2c1aa/cacerts" }, "raw": { "sha1": "2917859c443c68e19f93abcd1315c3c2904cbef9", "size": 104430, "url": "https://launcher.mojang.com/v1/objects/2917859c443c68e19f93abcd1315c3c2904cbef9/cacerts" } }, "executable": false, "type": "file" }, "lib/security/java.policy": { "downloads": { "lzma": { "sha1": "b601c420d02ef3dbd8595453d08fdef91134e8b5", "size": 647, "url": "https://launcher.mojang.com/v1/objects/b601c420d02ef3dbd8595453d08fdef91134e8b5/java.policy" }, "raw": { "sha1": "c0112209a567b3b523cfed7041709f9440227968", "size": 2466, "url": "https://launcher.mojang.com/v1/objects/c0112209a567b3b523cfed7041709f9440227968/java.policy" } }, "executable": false, "type": "file" }, "lib/security/java.security": { "downloads": { "lzma": { "sha1": "531620e82ca0365ce8dc97096bb0ac5a7ace5952", "size": 10959, "url": "https://launcher.mojang.com/v1/objects/531620e82ca0365ce8dc97096bb0ac5a7ace5952/java.security" }, "raw": { "sha1": "5dcc17a168c53d0b366784e520bd4d55aa61ac18", "size": 41528, "url": "https://launcher.mojang.com/v1/objects/5dcc17a168c53d0b366784e520bd4d55aa61ac18/java.security" } }, "executable": false, "type": "file" }, "lib/security/javaws.policy": { "downloads": { "raw": { "sha1": "4384ca5e4d32f7dd86d8baddd1e690730d74e694", "size": 98, "url": "https://launcher.mojang.com/v1/objects/4384ca5e4d32f7dd86d8baddd1e690730d74e694/javaws.policy" } }, "executable": false, "type": "file" }, "lib/security/policy": { "type": "directory" }, "lib/security/policy/limited": { "type": "directory" }, "lib/security/policy/limited/US_export_policy.jar": { "downloads": { "raw": { "sha1": "7d69ea3b385bc067738520f1b5c549e1084be285", "size": 3026, "url": "https://launcher.mojang.com/v1/objects/7d69ea3b385bc067738520f1b5c549e1084be285/US_export_policy.jar" } }, "executable": false, "type": "file" }, "lib/security/policy/limited/local_policy.jar": { "downloads": { "raw": { "sha1": "238b8826e110f58acb2e1959773b0a577cd4d569", "size": 3527, "url": "https://launcher.mojang.com/v1/objects/238b8826e110f58acb2e1959773b0a577cd4d569/local_policy.jar" } }, "executable": false, "type": "file" }, "lib/security/policy/unlimited": { "type": "directory" }, "lib/security/policy/unlimited/US_export_policy.jar": { "downloads": { "raw": { "sha1": "f6fb2af1e87fc622cda194a7d6b5f5f069653ff1", "size": 3023, "url": "https://launcher.mojang.com/v1/objects/f6fb2af1e87fc622cda194a7d6b5f5f069653ff1/US_export_policy.jar" } }, "executable": false, "type": "file" }, "lib/security/policy/unlimited/local_policy.jar": { "downloads": { "raw": { "sha1": "517368ab2cbaf6b42ea0b963f98eeedd996e83e3", "size": 3035, "url": "https://launcher.mojang.com/v1/objects/517368ab2cbaf6b42ea0b963f98eeedd996e83e3/local_policy.jar" } }, "executable": false, "type": "file" }, "lib/security/trusted.libraries": { "downloads": { "raw": { "sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709", "size": 0, "url": "https://launcher.mojang.com/v1/objects/da39a3ee5e6b4b0d3255bfef95601890afd80709/trusted.libraries" } }, "executable": false, "type": "file" }, "lib/sound.properties": { "downloads": { "lzma": { "sha1": "3b5f7e4ec437d79048af35094290577f483b3fe1", "size": 473, "url": "https://launcher.mojang.com/v1/objects/3b5f7e4ec437d79048af35094290577f483b3fe1/sound.properties" }, "raw": { "sha1": "9afceb218059d981d0fa9f07aad3c5097cf41b0c", "size": 1210, "url": "https://launcher.mojang.com/v1/objects/9afceb218059d981d0fa9f07aad3c5097cf41b0c/sound.properties" } }, "executable": false, "type": "file" }, "lib/tzdb.dat": { "downloads": { "lzma": { "sha1": "39c69339965484afe89c14111baeeb862fdefd97", "size": 32547, "url": "https://launcher.mojang.com/v1/objects/39c69339965484afe89c14111baeeb862fdefd97/tzdb.dat" }, "raw": { "sha1": "b59c07e3619271a3b9861e999f4b138e971baf69", "size": 105734, "url": "https://launcher.mojang.com/v1/objects/b59c07e3619271a3b9861e999f4b138e971baf69/tzdb.dat" } }, "executable": false, "type": "file" }, "man": { "type": "directory" }, "man/ja": { "target": "ja_JP.UTF-8", "type": "link" }, "man/ja_JP.UTF-8": { "type": "directory" }, "man/ja_JP.UTF-8/man1": { "type": "directory" }, "man/ja_JP.UTF-8/man1/java.1": { "downloads": { "lzma": { "sha1": "f9da09710b6c6df23c256e324a0c4df00a0d6ded", "size": 25461, "url": "https://launcher.mojang.com/v1/objects/f9da09710b6c6df23c256e324a0c4df00a0d6ded/java.1" }, "raw": { "sha1": "b0b12a0bb66e6171771ca4b1dfca32fb759bcaec", "size": 148688, "url": "https://launcher.mojang.com/v1/objects/b0b12a0bb66e6171771ca4b1dfca32fb759bcaec/java.1" } }, "executable": false, "type": "file" }, "man/ja_JP.UTF-8/man1/javaws.1": { "downloads": { "lzma": { "sha1": "6188fae453ca09ccb19be5c9f4d2059926b36267", "size": 2154, "url": "https://launcher.mojang.com/v1/objects/6188fae453ca09ccb19be5c9f4d2059926b36267/javaws.1" }, "raw": { "sha1": "8f39d928870268ace07bedfebd18db1e1d07fc37", "size": 6641, "url": "https://launcher.mojang.com/v1/objects/8f39d928870268ace07bedfebd18db1e1d07fc37/javaws.1" } }, "executable": false, "type": "file" }, "man/ja_JP.UTF-8/man1/jjs.1": { "downloads": { "lzma": { "sha1": "6e42b989d28b185dc1aab50c0389834e649a37d4", "size": 3452, "url": "https://launcher.mojang.com/v1/objects/6e42b989d28b185dc1aab50c0389834e649a37d4/jjs.1" }, "raw": { "sha1": "e023322a2013912315a2bd1034e6f829a27c76e0", "size": 11365, "url": "https://launcher.mojang.com/v1/objects/e023322a2013912315a2bd1034e6f829a27c76e0/jjs.1" } }, "executable": false, "type": "file" }, "man/ja_JP.UTF-8/man1/keytool.1": { "downloads": { "lzma": { "sha1": "a78134a4bddd53d684a70aa677e51a215db1c9cb", "size": 20698, "url": "https://launcher.mojang.com/v1/objects/a78134a4bddd53d684a70aa677e51a215db1c9cb/keytool.1" }, "raw": { "sha1": "148583c837eaaf6333ccfd8c9e8df08574e14b0c", "size": 111033, "url": "https://launcher.mojang.com/v1/objects/148583c837eaaf6333ccfd8c9e8df08574e14b0c/keytool.1" } }, "executable": false, "type": "file" }, "man/ja_JP.UTF-8/man1/orbd.1": { "downloads": { "lzma": { "sha1": "326af0dcbff173ef8aee29163dbe146d7389cc3e", "size": 4225, "url": "https://launcher.mojang.com/v1/objects/326af0dcbff173ef8aee29163dbe146d7389cc3e/orbd.1" }, "raw": { "sha1": "95651622d33c08286858ec337edd3ea72acd93dc", "size": 16092, "url": "https://launcher.mojang.com/v1/objects/95651622d33c08286858ec337edd3ea72acd93dc/orbd.1" } }, "executable": false, "type": "file" }, "man/ja_JP.UTF-8/man1/pack200.1": { "downloads": { "lzma": { "sha1": "e0eedafa748c61a44e5be4355fe9d44b05048e80", "size": 4293, "url": "https://launcher.mojang.com/v1/objects/e0eedafa748c61a44e5be4355fe9d44b05048e80/pack200.1" }, "raw": { "sha1": "aa21a0ab75707f7fc66e83c7a392e69b37ddf80e", "size": 14482, "url": "https://launcher.mojang.com/v1/objects/aa21a0ab75707f7fc66e83c7a392e69b37ddf80e/pack200.1" } }, "executable": false, "type": "file" }, "man/ja_JP.UTF-8/man1/policytool.1": { "downloads": { "lzma": { "sha1": "3c766ed12dab58166169d35680c392a6be1814a1", "size": 1380, "url": "https://launcher.mojang.com/v1/objects/3c766ed12dab58166169d35680c392a6be1814a1/policytool.1" }, "raw": { "sha1": "80879c74e072a98fad6f32b3283331aaf9bd002f", "size": 4020, "url": "https://launcher.mojang.com/v1/objects/80879c74e072a98fad6f32b3283331aaf9bd002f/policytool.1" } }, "executable": false, "type": "file" }, "man/ja_JP.UTF-8/man1/rmid.1": { "downloads": { "lzma": { "sha1": "1e20779d990beacc32a48237777d670fcc47ca14", "size": 4836, "url": "https://launcher.mojang.com/v1/objects/1e20779d990beacc32a48237777d670fcc47ca14/rmid.1" }, "raw": { "sha1": "7e40cb8003d098d6e36f45640b26f979ac94b5c5", "size": 19715, "url": "https://launcher.mojang.com/v1/objects/7e40cb8003d098d6e36f45640b26f979ac94b5c5/rmid.1" } }, "executable": false, "type": "file" }, "man/ja_JP.UTF-8/man1/rmiregistry.1": { "downloads": { "lzma": { "sha1": "aaf4ffe07e954f8696eef1ecb7a5e244628d0ad9", "size": 1627, "url": "https://launcher.mojang.com/v1/objects/aaf4ffe07e954f8696eef1ecb7a5e244628d0ad9/rmiregistry.1" }, "raw": { "sha1": "c53c52f3ae7a011c135894c9fc51b741e729c33d", "size": 4557, "url": "https://launcher.mojang.com/v1/objects/c53c52f3ae7a011c135894c9fc51b741e729c33d/rmiregistry.1" } }, "executable": false, "type": "file" }, "man/ja_JP.UTF-8/man1/servertool.1": { "downloads": { "lzma": { "sha1": "3b9e624e9d1cf2959b438a35061162e2100ddecd", "size": 2626, "url": "https://launcher.mojang.com/v1/objects/3b9e624e9d1cf2959b438a35061162e2100ddecd/servertool.1" }, "raw": { "sha1": "50ab8bcd9dd9d0b1a3d81348fbce1c8f82e7189e", "size": 9081, "url": "https://launcher.mojang.com/v1/objects/50ab8bcd9dd9d0b1a3d81348fbce1c8f82e7189e/servertool.1" } }, "executable": false, "type": "file" }, "man/ja_JP.UTF-8/man1/tnameserv.1": { "downloads": { "lzma": { "sha1": "bb3106ff74c60a76de3d20659b9c2128c70f3bf2", "size": 4478, "url": "https://launcher.mojang.com/v1/objects/bb3106ff74c60a76de3d20659b9c2128c70f3bf2/tnameserv.1" }, "raw": { "sha1": "01e714671ecd1167edcb5310b16a9c59c33c3eaa", "size": 17722, "url": "https://launcher.mojang.com/v1/objects/01e714671ecd1167edcb5310b16a9c59c33c3eaa/tnameserv.1" } }, "executable": false, "type": "file" }, "man/ja_JP.UTF-8/man1/unpack200.1": { "downloads": { "lzma": { "sha1": "c115a881cf800b08df294df55d9f250ae944e33c", "size": 1973, "url": "https://launcher.mojang.com/v1/objects/c115a881cf800b08df294df55d9f250ae944e33c/unpack200.1" }, "raw": { "sha1": "7c882bba0067367a41ad84868d18793b8a7397a3", "size": 5382, "url": "https://launcher.mojang.com/v1/objects/7c882bba0067367a41ad84868d18793b8a7397a3/unpack200.1" } }, "executable": false, "type": "file" }, "man/man1": { "type": "directory" }, "man/man1/java.1": { "downloads": { "lzma": { "sha1": "06a6b0275c202bf698d73ca71f95618d56d81c15", "size": 25796, "url": "https://launcher.mojang.com/v1/objects/06a6b0275c202bf698d73ca71f95618d56d81c15/java.1" }, "raw": { "sha1": "69fec7a341aa91f18dbdcdb95952dede7e1b689a", "size": 124796, "url": "https://launcher.mojang.com/v1/objects/69fec7a341aa91f18dbdcdb95952dede7e1b689a/java.1" } }, "executable": false, "type": "file" }, "man/man1/javaws.1": { "downloads": { "lzma": { "sha1": "4bae251c6dfb5420f56928815cf80d0b6d517a1f", "size": 1759, "url": "https://launcher.mojang.com/v1/objects/4bae251c6dfb5420f56928815cf80d0b6d517a1f/javaws.1" }, "raw": { "sha1": "e61e44e101b1bc119c2d2d4b10320f38b36a8036", "size": 4897, "url": "https://launcher.mojang.com/v1/objects/e61e44e101b1bc119c2d2d4b10320f38b36a8036/javaws.1" } }, "executable": false, "type": "file" }, "man/man1/jjs.1": { "downloads": { "lzma": { "sha1": "29683cf2bd47015c9461b688749ddffd95f6671d", "size": 1881, "url": "https://launcher.mojang.com/v1/objects/29683cf2bd47015c9461b688749ddffd95f6671d/jjs.1" }, "raw": { "sha1": "78d419bd3a7f3e0802d5220e690429194b5d1beb", "size": 4932, "url": "https://launcher.mojang.com/v1/objects/78d419bd3a7f3e0802d5220e690429194b5d1beb/jjs.1" } }, "executable": false, "type": "file" }, "man/man1/keytool.1": { "downloads": { "lzma": { "sha1": "b67e5126d43713ee3675706724b34061578b42db", "size": 19690, "url": "https://launcher.mojang.com/v1/objects/b67e5126d43713ee3675706724b34061578b42db/keytool.1" }, "raw": { "sha1": "4c976f86057ab779763fcfb98f5702ebef47f629", "size": 86925, "url": "https://launcher.mojang.com/v1/objects/4c976f86057ab779763fcfb98f5702ebef47f629/keytool.1" } }, "executable": false, "type": "file" }, "man/man1/orbd.1": { "downloads": { "lzma": { "sha1": "147064d6f7e027002e296bb246ae572d0ce0495b", "size": 3708, "url": "https://launcher.mojang.com/v1/objects/147064d6f7e027002e296bb246ae572d0ce0495b/orbd.1" }, "raw": { "sha1": "64201e1846fcf1dcc45c786ffeab89426d1c7742", "size": 12180, "url": "https://launcher.mojang.com/v1/objects/64201e1846fcf1dcc45c786ffeab89426d1c7742/orbd.1" } }, "executable": false, "type": "file" }, "man/man1/pack200.1": { "downloads": { "lzma": { "sha1": "fe17486bbe9c58cf4182fa056b9cd124e8295607", "size": 3724, "url": "https://launcher.mojang.com/v1/objects/fe17486bbe9c58cf4182fa056b9cd124e8295607/pack200.1" }, "raw": { "sha1": "26826cf52b89924f2d2a60d6cda798891875eae6", "size": 11623, "url": "https://launcher.mojang.com/v1/objects/26826cf52b89924f2d2a60d6cda798891875eae6/pack200.1" } }, "executable": false, "type": "file" }, "man/man1/policytool.1": { "downloads": { "lzma": { "sha1": "bd154e7c39aca71d15b2098c588866f8d95bc743", "size": 1122, "url": "https://launcher.mojang.com/v1/objects/bd154e7c39aca71d15b2098c588866f8d95bc743/policytool.1" }, "raw": { "sha1": "ab296625155d9a2b25ecc2b4feff2f741b3ad136", "size": 3235, "url": "https://launcher.mojang.com/v1/objects/ab296625155d9a2b25ecc2b4feff2f741b3ad136/policytool.1" } }, "executable": false, "type": "file" }, "man/man1/rmid.1": { "downloads": { "lzma": { "sha1": "6a7da234e7f43ebca5c4ba8cd862fda3be62fbaa", "size": 4255, "url": "https://launcher.mojang.com/v1/objects/6a7da234e7f43ebca5c4ba8cd862fda3be62fbaa/rmid.1" }, "raw": { "sha1": "6f10e214d7950a6a8460524e41dc700f112f89e5", "size": 15979, "url": "https://launcher.mojang.com/v1/objects/6f10e214d7950a6a8460524e41dc700f112f89e5/rmid.1" } }, "executable": false, "type": "file" }, "man/man1/rmiregistry.1": { "downloads": { "lzma": { "sha1": "f40dd17e3a734600ad1828b0c42d3a1685c4c520", "size": 1301, "url": "https://launcher.mojang.com/v1/objects/f40dd17e3a734600ad1828b0c42d3a1685c4c520/rmiregistry.1" }, "raw": { "sha1": "d9a3d23fab689df5bb9a792b88f462f939b49f70", "size": 3449, "url": "https://launcher.mojang.com/v1/objects/d9a3d23fab689df5bb9a792b88f462f939b49f70/rmiregistry.1" } }, "executable": false, "type": "file" }, "man/man1/servertool.1": { "downloads": { "lzma": { "sha1": "74f1e10712202cd3ca0ff5833de05b7ee67092e1", "size": 2307, "url": "https://launcher.mojang.com/v1/objects/74f1e10712202cd3ca0ff5833de05b7ee67092e1/servertool.1" }, "raw": { "sha1": "e6c7b510740ac8681a9bfb5f4ee1f0306125b728", "size": 7237, "url": "https://launcher.mojang.com/v1/objects/e6c7b510740ac8681a9bfb5f4ee1f0306125b728/servertool.1" } }, "executable": false, "type": "file" }, "man/man1/tnameserv.1": { "downloads": { "lzma": { "sha1": "4bec7f4e070d023f124f9352a8971d7acd249a15", "size": 3955, "url": "https://launcher.mojang.com/v1/objects/4bec7f4e070d023f124f9352a8971d7acd249a15/tnameserv.1" }, "raw": { "sha1": "a31dbbe800d49cb371fab9a4b73d22c3bf8799ad", "size": 15747, "url": "https://launcher.mojang.com/v1/objects/a31dbbe800d49cb371fab9a4b73d22c3bf8799ad/tnameserv.1" } }, "executable": false, "type": "file" }, "man/man1/unpack200.1": { "downloads": { "lzma": { "sha1": "f8e73863187929debf2ea6dadefb2995ec7917e7", "size": 1672, "url": "https://launcher.mojang.com/v1/objects/f8e73863187929debf2ea6dadefb2995ec7917e7/unpack200.1" }, "raw": { "sha1": "437f7233d738cb9b822e99003127049005663e0f", "size": 4244, "url": "https://launcher.mojang.com/v1/objects/437f7233d738cb9b822e99003127049005663e0f/unpack200.1" } }, "executable": false, "type": "file" }, "plugin": { "type": "directory" }, "plugin/desktop": { "type": "directory" }, "plugin/desktop/sun_java.desktop": { "downloads": { "lzma": { "sha1": "49ab0ccb54c3be68281d05055bc56a88b1281d3c", "size": 447, "url": "https://launcher.mojang.com/v1/objects/49ab0ccb54c3be68281d05055bc56a88b1281d3c/sun_java.desktop" }, "raw": { "sha1": "79120ee8160ad6f3c9b90c2641fb7edf3af96b5d", "size": 624, "url": "https://launcher.mojang.com/v1/objects/79120ee8160ad6f3c9b90c2641fb7edf3af96b5d/sun_java.desktop" } }, "executable": false, "type": "file" }, "plugin/desktop/sun_java.png": { "downloads": { "raw": { "sha1": "699c41e97a35414e72a80327a54d6e14e874e951", "size": 4351, "url": "https://launcher.mojang.com/v1/objects/699c41e97a35414e72a80327a54d6e14e874e951/sun_java.png" } }, "executable": false, "type": "file" }, "release": { "downloads": { "raw": { "sha1": "cb462682644c0275d94a45b759108815f3112064", "size": 424, "url": "https://launcher.mojang.com/v1/objects/cb462682644c0275d94a45b759108815f3112064/release" } }, "executable": false, "type": "file" } } }PrismLauncher-11.0.3/tests/testdata/PackageManifest/inspect_win/0000755000175100017510000000000015224505336024302 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/PackageManifest/inspect_win/a/0000755000175100017510000000000015224505336024522 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/PackageManifest/inspect_win/a/b.txt0000644000175100017510000000000015224505336025472 0ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/PackageManifest/inspect_win/a/b/0000755000175100017510000000000015224505336024743 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/PackageManifest/inspect_win/a/b/b.txt0000644000175100017510000000000015224505336025713 0ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/ShaderPackParse/0000755000175100017510000000000015224505336021736 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/ShaderPackParse/shaderpack3.zip0000644000175100017510000000020015224505336024642 0ustar runnerrunnerPKl‹˜Uno_shaders_herePK?l‹˜U¤�no_shaders_herePK=-PrismLauncher-11.0.3/tests/testdata/ShaderPackParse/shaderpack1.zip0000644000175100017510000000036215224505336024651 0ustar runnerrunnerPKu‹˜Ushaders/PKG‹˜Ushaders/shaders.propertiesPK?u‹˜UíAshaders/PK?G‹˜U¤�&shaders/shaders.propertiesPK~^PrismLauncher-11.0.3/tests/testdata/ShaderPackParse/shaderpack2/0000755000175100017510000000000015224505336024125 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/ShaderPackParse/shaderpack2/shaders/0000755000175100017510000000000015224505336025556 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/ShaderPackParse/shaderpack2/shaders/shaders.properties0000644000175100017510000000000015224505336031313 0ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/Version/0000755000175100017510000000000015224505336020363 5ustar runnerrunnerPrismLauncher-11.0.3/tests/testdata/Version/test_vectors.txt0000644000175100017510000000326715224505336023660 0ustar runnerrunner# Test vector from: # https://git.sleeping.town/exa/FlexVer/src/branch/trunk/test/test_vectors.txt # # This test file is formatted as " ", seperated by the space character # Implementations should ignore lines starting with "#" and lines that have a length of 0 # Basic numeric ordering (lexical string sort fails these) 10 > 2 100 > 10 # Trivial common numerics 1.0 < 1.1 1.0 < 1.0.1 1.1 > 1.0.1 # SemVer compatibility 1.5 > 1.5-pre1 1.5 = 1.5+foobar # SemVer incompatibility 1.5 < 1.5-2 1.5-pre10 > 1.5-pre2 # Empty strings = 1 > < 1 # Check boundary between textual and prerelease a-a < a # Check boundary between textual and appendix a+a = a # Dash is included in prerelease comparison (if stripped it will be a smaller component) # Note that a-a < a=a regardless since the prerelease splits the component creating a smaller first component; 0 is added to force splitting regardless a0-a < a0=a # Pre-releases must contain only non-digit 1.16.5-10 > 1.16.5 # Pre-releases can have multiple dashes (should not be split) # Reasoning for test data: "p-a!" > "p-a-" (correct); "p-a!" < "p-a t-" (what happens if every dash creates a new component) -a- > -a! # Misc b1.7.3 > a1.2.6 b1.2.6 > a1.7.3 a1.1.2 < a1.1.2_01 1.16.5-0.00.5 > 1.14.2-1.3.7 1.0.0 < 1.0.0_01 1.0.1 > 1.0.0_01 1.0.0_01 < 1.0.1 0.17.1-beta.1 < 0.17.1 0.17.1-beta.1 < 0.17.1-beta.2 1.4.5_01 = 1.4.5_01+fabric-1.17 1.4.5_01 = 1.4.5_01+fabric-1.17+ohgod 14w16a < 18w40b 18w40a < 18w40b 1.4.5_01+fabric-1.17 < 18w40b 13w02a < c0.3.0_01 0.6.0-1.18.x < 0.9.beta-1.18.x # removeLeadingZeroes (#17) 0000.0.0 = 0.0.0 0000.00.0 = 0.00.0 0.0.0 = 0.00.0000 # General leading zeroes 1.0.01 = 1.0.1 1.0.0001 = 1.0.01 PrismLauncher-11.0.3/tests/WorldSaveParse_test.cpp0000644000175100017510000000547715224505336021606 0ustar runnerrunner // SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include class WorldSaveParseTest : public QObject { Q_OBJECT private slots: void test_parseZIP() { QString source = QFINDTESTDATA("testdata/WorldSaveParse"); QString zip_ws = FS::PathCombine(source, "minecraft_save_1.zip"); WorldSave save{ QFileInfo(zip_ws) }; bool valid = WorldSaveUtils::processZIP(save); QVERIFY(save.saveFormat() == WorldSaveFormat::SINGLE); QVERIFY(save.saveDirName() == "world_1"); QVERIFY(valid == true); } void test_parse_ZIP2() { QString source = QFINDTESTDATA("testdata/WorldSaveParse"); QString zip_ws = FS::PathCombine(source, "minecraft_save_2.zip"); WorldSave save{ QFileInfo(zip_ws) }; bool valid = WorldSaveUtils::processZIP(save); QVERIFY(save.saveFormat() == WorldSaveFormat::MULTI); QVERIFY(save.saveDirName() == "world_2"); QVERIFY(valid == true); } void test_parseFolder() { QString source = QFINDTESTDATA("testdata/WorldSaveParse"); QString folder_ws = FS::PathCombine(source, "minecraft_save_3"); WorldSave save{ QFileInfo(folder_ws) }; bool valid = WorldSaveUtils::processFolder(save); QVERIFY(save.saveFormat() == WorldSaveFormat::SINGLE); QVERIFY(save.saveDirName() == "world_3"); QVERIFY(valid == true); } void test_parseFolder2() { QString source = QFINDTESTDATA("testdata/WorldSaveParse"); QString folder_ws = FS::PathCombine(source, "minecraft_save_4"); WorldSave save{ QFileInfo(folder_ws) }; bool valid = WorldSaveUtils::process(save); QVERIFY(save.saveFormat() == WorldSaveFormat::MULTI); QVERIFY(save.saveDirName() == "world_4"); QVERIFY(valid == true); } }; QTEST_GUILESS_MAIN(WorldSaveParseTest) #include "WorldSaveParse_test.moc" PrismLauncher-11.0.3/tests/JavaVersion_test.cpp0000644000175100017510000001300515224505336021116 0ustar runnerrunner#include #include class JavaVersionTest : public QObject { Q_OBJECT private slots: void test_Parse_data() { QTest::addColumn("string"); QTest::addColumn("major"); QTest::addColumn("minor"); QTest::addColumn("security"); QTest::addColumn("prerelease"); QTest::newRow("old format") << "1.6.0_33" << 6 << 0 << 33 << QString(); QTest::newRow("old format prerelease") << "1.9.0_1-ea" << 9 << 0 << 1 << "ea"; QTest::newRow("new format major") << "9" << 9 << 0 << 0 << QString(); QTest::newRow("new format minor") << "9.1" << 9 << 1 << 0 << QString(); QTest::newRow("new format security") << "9.0.1" << 9 << 0 << 1 << QString(); QTest::newRow("new format prerelease") << "9-ea" << 9 << 0 << 0 << "ea"; QTest::newRow("new format long prerelease") << "9.0.1-ea" << 9 << 0 << 1 << "ea"; } void test_Parse() { QFETCH(QString, string); QFETCH(int, major); QFETCH(int, minor); QFETCH(int, security); QFETCH(QString, prerelease); JavaVersion test(string); QCOMPARE(test.m_string, string); QCOMPARE(test.toString(), string); QCOMPARE(test.m_major, major); QCOMPARE(test.m_minor, minor); QCOMPARE(test.m_security, security); QCOMPARE(test.m_prerelease, prerelease); } void test_Sort_data() { QTest::addColumn("lhs"); QTest::addColumn("rhs"); QTest::addColumn("smaller"); QTest::addColumn("equal"); QTest::addColumn("bigger"); // old format and new format equivalence QTest::newRow("1.6.0_33 == 6.0.33") << "1.6.0_33" << "6.0.33" << false << true << false; // old format major version QTest::newRow("1.5.0_33 < 1.6.0_33") << "1.5.0_33" << "1.6.0_33" << true << false << false; // new format - first release vs first security patch QTest::newRow("9 < 9.0.1") << "9" << "9.0.1" << true << false << false; QTest::newRow("9.0.1 > 9") << "9.0.1" << "9" << false << false << true; // new format - first minor vs first release/security patch QTest::newRow("9.1 > 9.0.1") << "9.1" << "9.0.1" << false << false << true; QTest::newRow("9.0.1 < 9.1") << "9.0.1" << "9.1" << true << false << false; QTest::newRow("9.1 > 9") << "9.1" << "9" << false << false << true; QTest::newRow("9 > 9.1") << "9" << "9.1" << true << false << false; // new format - omitted numbers QTest::newRow("9 == 9.0") << "9" << "9.0" << false << true << false; QTest::newRow("9 == 9.0.0") << "9" << "9.0.0" << false << true << false; QTest::newRow("9.0 == 9.0.0") << "9.0" << "9.0.0" << false << true << false; // early access and prereleases compared to final release QTest::newRow("9-ea < 9") << "9-ea" << "9" << true << false << false; QTest::newRow("9 < 9.0.1-ea") << "9" << "9.0.1-ea" << true << false << false; QTest::newRow("9.0.1-ea > 9") << "9.0.1-ea" << "9" << false << false << true; // prerelease difference only testing QTest::newRow("9-1 == 9-1") << "9-1" << "9-1" << false << true << false; QTest::newRow("9-1 < 9-2") << "9-1" << "9-2" << true << false << false; QTest::newRow("9-5 < 9-20") << "9-5" << "9-20" << true << false << false; QTest::newRow("9-rc1 < 9-rc2") << "9-rc1" << "9-rc2" << true << false << false; QTest::newRow("9-rc5 < 9-rc20") << "9-rc5" << "9-rc20" << true << false << false; QTest::newRow("9-rc < 9-rc2") << "9-rc" << "9-rc2" << true << false << false; QTest::newRow("9-ea < 9-rc") << "9-ea" << "9-rc" << true << false << false; } void test_Sort() { QFETCH(QString, lhs); QFETCH(QString, rhs); QFETCH(bool, smaller); QFETCH(bool, equal); QFETCH(bool, bigger); JavaVersion lver(lhs); JavaVersion rver(rhs); QCOMPARE(lver < rver, smaller); QCOMPARE(lver == rver, equal); QCOMPARE(lver > rver, bigger); } void test_PermGen_data() { QTest::addColumn("version"); QTest::addColumn("needs_permgen"); QTest::newRow("1.6.0_33") << "1.6.0_33" << true; QTest::newRow("1.7.0_60") << "1.7.0_60" << true; QTest::newRow("1.8.0_22") << "1.8.0_22" << false; QTest::newRow("9-ea") << "9-ea" << false; QTest::newRow("9.2.4") << "9.2.4" << false; } void test_PermGen() { QFETCH(QString, version); QFETCH(bool, needs_permgen); JavaVersion v(version); QCOMPARE(needs_permgen, v.requiresPermGen()); } }; QTEST_GUILESS_MAIN(JavaVersionTest) #include "JavaVersion_test.moc" PrismLauncher-11.0.3/tests/GZip_test.cpp0000644000175100017510000000240415224505336017541 0ustar runnerrunner#include #include #include void fib(int& prev, int& cur) { auto ret = prev + cur; prev = cur; cur = ret; } class GZipTest : public QObject { Q_OBJECT private slots: void test_Through() { // test up to 10 MB static const int size = 10 * 1024 * 1024; QByteArray random; QByteArray compressed; QByteArray decompressed; std::default_random_engine eng((std::random_device())()); std::uniform_int_distribution idis(0, std::numeric_limits::max()); // initialize random buffer for (int i = 0; i < size; i++) { random.append(static_cast(idis(eng))); } // initialize fibonacci int prev = 1; int cur = 1; // test if fibonacci long random buffers pass through GZip do { QByteArray copy = random; copy.resize(cur); compressed.clear(); decompressed.clear(); QVERIFY(GZip::zip(copy, compressed)); QVERIFY(GZip::unzip(compressed, decompressed)); QCOMPARE(decompressed, copy); fib(prev, cur); } while (cur < size); } }; QTEST_GUILESS_MAIN(GZipTest) #include "GZip_test.moc" PrismLauncher-11.0.3/tests/Library_test.cpp0000644000175100017510000003651315224505336020304 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include class LibraryTest : public QObject { Q_OBJECT private: LibraryPtr readMojangJson(const QString path) { QFile jsonFile(path); if (!jsonFile.open(QIODevice::ReadOnly)) { qCritical() << "Failed to open file" << jsonFile.fileName() << "for reading:" << jsonFile.errorString(); return LibraryPtr(); } auto data = jsonFile.readAll(); jsonFile.close(); ProblemContainer problems; return MojangVersionFormat::libraryFromJson(problems, QJsonDocument::fromJson(data).object(), path); } // get absolute path to expected storage, assuming default cache prefix QStringList getStorage(QString relative) { return { FS::PathCombine(cache->getBasePath("libraries"), relative) }; } RuntimeContext dummyContext(QString system = "linux", QString arch = "64", QString realArch = "amd64") { RuntimeContext r; r.javaArchitecture = arch; r.javaRealArchitecture = realArch; r.system = system; return r; } private slots: void initTestCase() { cache.reset(new HttpMetaCache()); cache->addBase("libraries", QDir("libraries").absolutePath()); dataDir = QDir(QFINDTESTDATA("testdata/Libraries")).absolutePath(); } void test_legacy() { RuntimeContext r = dummyContext(); Library test("test.package:testname:testversion"); QCOMPARE(test.artifactPrefix(), QString("test.package:testname")); QCOMPARE(test.isNative(), false); QStringList jar, native, native32, native64; test.getApplicableFiles(r, jar, native, native32, native64, QString()); QCOMPARE(jar, getStorage("test/package/testname/testversion/testname-testversion.jar")); QCOMPARE(native, {}); QCOMPARE(native32, {}); QCOMPARE(native64, {}); } void test_legacy_url() { RuntimeContext r = dummyContext(); QStringList failedFiles; Library test("test.package:testname:testversion"); test.setRepositoryURL("file://foo/bar"); auto downloads = test.getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(downloads.size(), 1); QCOMPARE(failedFiles, {}); Net::NetRequest::Ptr dl = downloads[0]; QCOMPARE(dl->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion.jar")); } void test_legacy_url_local_broken() { RuntimeContext r = dummyContext(); Library test("test.package:testname:testversion"); QCOMPARE(test.isNative(), false); QStringList failedFiles; test.setHint("local"); auto downloads = test.getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(downloads.size(), 0); QCOMPARE(failedFiles, { "testname-testversion.jar" }); } void test_legacy_url_local_override() { RuntimeContext r = dummyContext(); Library test("com.paulscode:codecwav:20101023"); QCOMPARE(test.isNative(), false); QStringList failedFiles; test.setHint("local"); auto downloads = test.getDownloads(r, cache.get(), failedFiles, QFINDTESTDATA("testdata/Libraries")); QCOMPARE(downloads.size(), 0); qDebug() << failedFiles; QCOMPARE(failedFiles.size(), 0); QStringList jar, native, native32, native64; test.getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Libraries")); QCOMPARE(jar, { QFileInfo(QFINDTESTDATA("testdata/Libraries/codecwav-20101023.jar")).absoluteFilePath() }); QCOMPARE(native, {}); QCOMPARE(native32, {}); QCOMPARE(native64, {}); } void test_legacy_native() { RuntimeContext r = dummyContext(); Library test("test.package:testname:testversion"); test.m_nativeClassifiers["linux"] = "linux"; QCOMPARE(test.isNative(), true); test.setRepositoryURL("file://foo/bar"); { QStringList jar, native, native32, native64; test.getApplicableFiles(r, jar, native, native32, native64, QString()); QCOMPARE(jar, {}); QCOMPARE(native, getStorage("test/package/testname/testversion/testname-testversion-linux.jar")); QCOMPARE(native32, {}); QCOMPARE(native64, {}); QStringList failedFiles; auto dls = test.getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 1); QCOMPARE(failedFiles, {}); auto dl = dls[0]; QCOMPARE(dl->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux.jar")); } } void test_legacy_native_arch() { RuntimeContext r = dummyContext(); Library test("test.package:testname:testversion"); test.m_nativeClassifiers["linux"] = "linux-${arch}"; test.m_nativeClassifiers["osx"] = "osx-${arch}"; test.m_nativeClassifiers["windows"] = "windows-${arch}"; QCOMPARE(test.isNative(), true); test.setRepositoryURL("file://foo/bar"); { QStringList jar, native, native32, native64; test.getApplicableFiles(r, jar, native, native32, native64, QString()); QCOMPARE(jar, {}); QCOMPARE(native, {}); QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-linux-32.jar")); QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar")); QStringList failedFiles; auto dls = test.getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 2); QCOMPARE(failedFiles, {}); QCOMPARE(dls[0]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-32.jar")); QCOMPARE(dls[1]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-64.jar")); } r.system = "windows"; { QStringList jar, native, native32, native64; test.getApplicableFiles(r, jar, native, native32, native64, QString()); QCOMPARE(jar, {}); QCOMPARE(native, {}); QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-windows-32.jar")); QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-windows-64.jar")); QStringList failedFiles; auto dls = test.getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 2); QCOMPARE(failedFiles, {}); QCOMPARE(dls[0]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-32.jar")); QCOMPARE(dls[1]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-64.jar")); } r.system = "osx"; { QStringList jar, native, native32, native64; test.getApplicableFiles(r, jar, native, native32, native64, QString()); QCOMPARE(jar, {}); QCOMPARE(native, {}); QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-osx-32.jar")); QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-osx-64.jar")); QStringList failedFiles; auto dls = test.getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 2); QCOMPARE(failedFiles, {}); QCOMPARE(dls[0]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-32.jar")); QCOMPARE(dls[1]->url(), QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-64.jar")); } } void test_legacy_native_arch_local_override() { RuntimeContext r = dummyContext(); Library test("test.package:testname:testversion"); test.m_nativeClassifiers["linux"] = "linux-${arch}"; test.setHint("local"); QCOMPARE(test.isNative(), true); test.setRepositoryURL("file://foo/bar"); { QStringList jar, native, native32, native64; test.getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Libraries")); QCOMPARE(jar, {}); QCOMPARE(native, {}); QCOMPARE(native32, { QFileInfo(QFINDTESTDATA("testdata/Libraries/testname-testversion-linux-32.jar")).absoluteFilePath() }); QCOMPARE(native64, { QFileInfo(QFINDTESTDATA("testdata/Libraries") + "/testname-testversion-linux-64.jar").absoluteFilePath() }); QStringList failedFiles; auto dls = test.getDownloads(r, cache.get(), failedFiles, QFINDTESTDATA("testdata/Libraries")); QCOMPARE(dls.size(), 0); QCOMPARE(failedFiles, { QFileInfo(QFINDTESTDATA("testdata/Libraries") + "/testname-testversion-linux-64.jar").absoluteFilePath() }); } } void test_onenine() { RuntimeContext r = dummyContext("osx"); auto test = readMojangJson(QFINDTESTDATA("testdata/Libraries/lib-simple.json")); { QStringList jar, native, native32, native64; test->getApplicableFiles(r, jar, native, native32, native64, QString()); QCOMPARE(jar, getStorage("com/paulscode/codecwav/20101023/codecwav-20101023.jar")); QCOMPARE(native, {}); QCOMPARE(native32, {}); QCOMPARE(native64, {}); } r.system = "linux"; { QStringList failedFiles; auto dls = test->getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 1); QCOMPARE(failedFiles, {}); QCOMPARE(dls[0]->url(), QUrl("https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar")); } r.system = "osx"; test->setHint("local"); { QStringList jar, native, native32, native64; test->getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Libraries")); QCOMPARE(jar, { QFileInfo(QFINDTESTDATA("testdata/Libraries/codecwav-20101023.jar")).absoluteFilePath() }); QCOMPARE(native, {}); QCOMPARE(native32, {}); QCOMPARE(native64, {}); } r.system = "linux"; { QStringList failedFiles; auto dls = test->getDownloads(r, cache.get(), failedFiles, QFINDTESTDATA("testdata/Libraries")); QCOMPARE(dls.size(), 0); QCOMPARE(failedFiles, {}); } } void test_onenine_local_override() { RuntimeContext r = dummyContext("osx"); auto test = readMojangJson(QFINDTESTDATA("testdata/Libraries/lib-simple.json")); test->setHint("local"); { QStringList jar, native, native32, native64; test->getApplicableFiles(r, jar, native, native32, native64, QFINDTESTDATA("testdata/Libraries")); QCOMPARE(jar, { QFileInfo(QFINDTESTDATA("testdata/Libraries/codecwav-20101023.jar")).absoluteFilePath() }); QCOMPARE(native, {}); QCOMPARE(native32, {}); QCOMPARE(native64, {}); } r.system = "linux"; { QStringList failedFiles; auto dls = test->getDownloads(r, cache.get(), failedFiles, QFINDTESTDATA("testdata/Libraries")); QCOMPARE(dls.size(), 0); QCOMPARE(failedFiles, {}); } } void test_onenine_native() { RuntimeContext r = dummyContext("osx"); auto test = readMojangJson(QFINDTESTDATA("testdata/Libraries/lib-native.json")); QStringList jar, native, native32, native64; test->getApplicableFiles(r, jar, native, native32, native64, QString()); QCOMPARE(jar, QStringList()); QCOMPARE(native, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar")); QCOMPARE(native32, {}); QCOMPARE(native64, {}); QStringList failedFiles; auto dls = test->getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 1); QCOMPARE(failedFiles, {}); QCOMPARE(dls[0]->url(), QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/" "lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar")); } void test_onenine_native_arch() { RuntimeContext r = dummyContext("windows"); auto test = readMojangJson(QFINDTESTDATA("testdata/Libraries/lib-native-arch.json")); QStringList jar, native, native32, native64; test->getApplicableFiles(r, jar, native, native32, native64, QString()); QCOMPARE(jar, {}); QCOMPARE(native, {}); QCOMPARE(native32, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar")); QCOMPARE(native64, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar")); QStringList failedFiles; auto dls = test->getDownloads(r, cache.get(), failedFiles, QString()); QCOMPARE(dls.size(), 2); QCOMPARE(failedFiles, {}); QCOMPARE(dls[0]->url(), QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar")); QCOMPARE(dls[1]->url(), QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar")); } private: std::unique_ptr cache; QString dataDir; }; QTEST_GUILESS_MAIN(LibraryTest) #include "Library_test.moc" PrismLauncher-11.0.3/tests/ResourcePackParse_test.cpp0000644000175100017510000000531615224505336022256 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "minecraft/mod/tasks/LocalDataPackParseTask.h" #include #include class ResourcePackParseTest : public QObject { Q_OBJECT private slots: void test_parseZIP() { QString source = QFINDTESTDATA("testdata/Resources"); QString zip_rp = FS::PathCombine(source, "test_resource_pack_idk.zip"); ResourcePack pack{ QFileInfo(zip_rp) }; bool valid = DataPackUtils::processZIP(&pack, DataPackUtils::ProcessingLevel::BasicInfoOnly); QVERIFY(pack.packFormat() == 3); QVERIFY(pack.description() == "um dois, feijão com arroz, três quatro, feijão no prato, cinco seis, café inglês, sete oito, comer biscoito, nove dez " "comer pastéis!!"); QVERIFY(valid == true); } void test_parseFolder() { QString source = QFINDTESTDATA("testdata/Resources"); QString folder_rp = FS::PathCombine(source, "test_folder"); ResourcePack pack{ QFileInfo(folder_rp) }; bool valid = DataPackUtils::processFolder(&pack, DataPackUtils::ProcessingLevel::BasicInfoOnly); QVERIFY(pack.packFormat() == 1); QVERIFY(pack.description() == "Some resource pack maybe"); QVERIFY(valid == true); } void test_parseFolder2() { QString source = QFINDTESTDATA("testdata/Resources"); QString folder_rp = FS::PathCombine(source, "another_test_folder"); ResourcePack pack{ QFileInfo(folder_rp) }; bool valid = DataPackUtils::process(&pack, DataPackUtils::ProcessingLevel::BasicInfoOnly); QVERIFY(pack.packFormat() == 6); QVERIFY(pack.description() == "o quartel pegou fogo, policia deu sinal, acode acode acode a bandeira nacional"); QVERIFY(valid == true); // no assets dir but it is still valid based on https://minecraft.wiki/w/Resource_pack } }; QTEST_GUILESS_MAIN(ResourcePackParseTest) #include "ResourcePackParse_test.moc" PrismLauncher-11.0.3/tests/FileSystem_test.cpp0000644000175100017510000006310715224505336020763 0ustar runnerrunner#include #include #include #include #include #include #include #include #include namespace fs = std::filesystem; class LinkTask : public Task { Q_OBJECT friend class FileSystemTest; LinkTask(QString src, QString dst) { m_lnk = new FS::create_link(src, dst, this); m_lnk->debug(true); } ~LinkTask() { delete m_lnk; } void matcher(Filter filter) { m_lnk->matcher(filter); } void linkRecursively(bool recursive) { m_lnk->linkRecursively(recursive); m_linkRecursive = recursive; } void whitelist(bool b) { m_lnk->whitelist(b); } void setMaxDepth(int depth) { m_lnk->setMaxDepth(depth); } private: void executeTask() override { if (!(*m_lnk)()) { #if defined Q_OS_WIN32 if (!m_useHard) { qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks"; qDebug() << "atempting to run with privelage"; connect(m_lnk, &FS::create_link::finishedPrivileged, this, [this](bool gotResults) { if (gotResults) { emitSucceeded(); } else { qDebug() << "Privileged run exited without results!"; emitFailed(); } }); m_lnk->runPrivileged(); } else { qDebug() << "Link Failed!" << m_lnk->getOSError().value() << m_lnk->getOSError().message().c_str(); } #else qDebug() << "Link Failed!" << m_lnk->getOSError().value() << m_lnk->getOSError().message().c_str(); #endif } else { emitSucceeded(); } } FS::create_link* m_lnk; #if defined Q_OS_WIN32 bool m_useHard = false; #endif bool m_linkRecursive = true; }; class FileSystemTest : public QObject { Q_OBJECT const QString bothSlash = "/foo/"; const QString trailingSlash = "foo/"; const QString leadingSlash = "/foo"; private slots: void test_pathCombine() { QCOMPARE(QString("/foo/foo"), FS::PathCombine(bothSlash, bothSlash)); QCOMPARE(QString("foo/foo"), FS::PathCombine(trailingSlash, trailingSlash)); QCOMPARE(QString("/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash)); QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(bothSlash, bothSlash, bothSlash)); QCOMPARE(QString("foo/foo/foo"), FS::PathCombine(trailingSlash, trailingSlash, trailingSlash)); QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash, leadingSlash)); } void test_PathCombine1_data() { QTest::addColumn("result"); QTest::addColumn("path1"); QTest::addColumn("path2"); QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl"; QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl"; #if defined(Q_OS_WIN) QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc"; QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl"; QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl"; #endif } void test_PathCombine1() { QFETCH(QString, result); QFETCH(QString, path1); QFETCH(QString, path2); QCOMPARE(FS::PathCombine(path1, path2), result); } void test_PathCombine2_data() { QTest::addColumn("result"); QTest::addColumn("path1"); QTest::addColumn("path2"); QTest::addColumn("path3"); QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl"; QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl"; QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl"; QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl"; #if defined(Q_OS_WIN) QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl"; QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl"; QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl"; QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl"; #endif } void test_PathCombine2() { QFETCH(QString, result); QFETCH(QString, path1); QFETCH(QString, path2); QFETCH(QString, path3); QCOMPARE(FS::PathCombine(path1, path2, path3), result); } void test_copy() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); FS::copy c(folder, target_dir.path()); c(); for (auto entry : target_dir.entryList()) { qDebug() << entry; } QVERIFY(target_dir.entryList().contains("pack.mcmeta")); QVERIFY(target_dir.entryList().contains("assets")); }; // first try variant without trailing / QVERIFY(!folder.endsWith('/')); f(); // then variant with trailing / folder.append('/'); QVERIFY(folder.endsWith('/')); f(); } void test_copy_with_blacklist() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); FS::copy c(folder, target_dir.path()); auto re = Filters::regexp(QRegularExpression("[.]?mcmeta")); c.matcher(re); c(); for (auto entry : target_dir.entryList()) { qDebug() << entry; } QVERIFY(!target_dir.entryList().contains("pack.mcmeta")); QVERIFY(target_dir.entryList().contains("assets")); }; // first try variant without trailing / QVERIFY(!folder.endsWith('/')); f(); // then variant with trailing / folder.append('/'); QVERIFY(folder.endsWith('/')); f(); } void test_copy_with_whitelist() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); FS::copy c(folder, target_dir.path()); auto re = Filters::regexp(QRegularExpression("[.]?mcmeta")); c.matcher(re); c.whitelist(true); c(); for (auto entry : target_dir.entryList()) { qDebug() << entry; } QVERIFY(target_dir.entryList().contains("pack.mcmeta")); QVERIFY(!target_dir.entryList().contains("assets")); }; // first try variant without trailing / QVERIFY(!folder.endsWith('/')); f(); // then variant with trailing / folder.append('/'); QVERIFY(folder.endsWith('/')); f(); } void test_copy_with_dot_hidden() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); FS::copy c(folder, target_dir.path()); c(); auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden; for (auto entry : target_dir.entryList(filter)) { qDebug() << entry; } QVERIFY(target_dir.entryList(filter).contains(".secret_folder")); target_dir.cd(".secret_folder"); QVERIFY(target_dir.entryList(filter).contains(".secret_file.txt")); }; // first try variant without trailing / QVERIFY(!folder.endsWith('/')); f(); // then variant with trailing / folder.append('/'); QVERIFY(folder.endsWith('/')); f(); } void test_copy_single_file() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); { QString file = QFINDTESTDATA("testdata/FileSystem/test_folder/pack.mcmeta"); qDebug() << "From:" << file << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "pack.mcmeta")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); FS::copy c(file, target_dir.filePath("pack.mcmeta")); c(); auto filter = QDir::Filter::Files; for (auto entry : target_dir.entryList(filter)) { qDebug() << entry; } QVERIFY(target_dir.entryList(filter).contains("pack.mcmeta")); } } void test_getDesktop() { QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); } void test_link() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); LinkTask lnk_tsk(folder, target_dir.path()); lnk_tsk.linkRecursively(false); connect(&lnk_tsk, &Task::finished, [&lnk_tsk] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); QVERIFY2(QTest::qWaitFor([&lnk_tsk]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); for (auto entry : target_dir.entryList()) { qDebug() << entry; QFileInfo entry_lnk_info(target_dir.filePath(entry)); if (!entry_lnk_info.isDir()) QVERIFY(!entry_lnk_info.isSymLink()); } QFileInfo lnk_info(target_dir.path()); QVERIFY(lnk_info.exists()); QVERIFY(lnk_info.isSymLink()); QVERIFY(target_dir.entryList().contains("pack.mcmeta")); QVERIFY(target_dir.entryList().contains("assets")); }; // first try variant without trailing / QVERIFY(!folder.endsWith('/')); f(); // then variant with trailing / folder.append('/'); QVERIFY(folder.endsWith('/')); f(); } void test_hard_link() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); auto f = [&folder]() { // use working dir to prevent makeing a hard link to a tmpfs or across devices QTemporaryDir tempDir("./tmp"); tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); FS::create_link lnk(folder, target_dir.path()); lnk.useHardLinks(true); lnk.debug(true); if (!lnk()) { qDebug() << "Link Failed!" << lnk.getOSError().value() << lnk.getOSError().message().c_str(); } for (auto entry : target_dir.entryList()) { qDebug() << entry; QFileInfo entry_lnk_info(target_dir.filePath(entry)); QVERIFY(!entry_lnk_info.isSymLink()); QFileInfo entry_orig_info(QDir(folder).filePath(entry)); if (!entry_lnk_info.isDir()) { qDebug() << "hard link equivalency?" << entry_lnk_info.absoluteFilePath() << "vs" << entry_orig_info.absoluteFilePath(); QVERIFY(fs::equivalent(fs::path(StringUtils::toStdString(entry_lnk_info.absoluteFilePath())), fs::path(StringUtils::toStdString(entry_orig_info.absoluteFilePath())))); } } QFileInfo lnk_info(target_dir.path()); QVERIFY(lnk_info.exists()); QVERIFY(!lnk_info.isSymLink()); QVERIFY(target_dir.entryList().contains("pack.mcmeta")); QVERIFY(target_dir.entryList().contains("assets")); }; // first try variant without trailing / QVERIFY(!folder.endsWith('/')); f(); // then variant with trailing / folder.append('/'); QVERIFY(folder.endsWith('/')); f(); } void test_link_with_blacklist() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); LinkTask lnk_tsk(folder, target_dir.path()); auto re = Filters::regexp(QRegularExpression("[.]?mcmeta")); lnk_tsk.matcher(re); lnk_tsk.linkRecursively(true); connect(&lnk_tsk, &Task::finished, [&lnk_tsk] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); QVERIFY2(QTest::qWaitFor([&lnk_tsk]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); for (auto entry : target_dir.entryList()) { qDebug() << entry; QFileInfo entry_lnk_info(target_dir.filePath(entry)); if (!entry_lnk_info.isDir()) QVERIFY(entry_lnk_info.isSymLink()); } QFileInfo lnk_info(target_dir.path()); QVERIFY(lnk_info.exists()); QVERIFY(!target_dir.entryList().contains("pack.mcmeta")); QVERIFY(target_dir.entryList().contains("assets")); }; // first try variant without trailing / QVERIFY(!folder.endsWith('/')); f(); // then variant with trailing / folder.append('/'); QVERIFY(folder.endsWith('/')); f(); } void test_link_with_whitelist() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); LinkTask lnk_tsk(folder, target_dir.path()); auto re = Filters::regexp(QRegularExpression("[.]?mcmeta")); lnk_tsk.matcher(re); lnk_tsk.linkRecursively(true); lnk_tsk.whitelist(true); connect(&lnk_tsk, &Task::finished, [&lnk_tsk] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); QVERIFY2(QTest::qWaitFor([&lnk_tsk]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); for (auto entry : target_dir.entryList()) { qDebug() << entry; QFileInfo entry_lnk_info(target_dir.filePath(entry)); if (!entry_lnk_info.isDir()) QVERIFY(entry_lnk_info.isSymLink()); } QFileInfo lnk_info(target_dir.path()); QVERIFY(lnk_info.exists()); QVERIFY(target_dir.entryList().contains("pack.mcmeta")); QVERIFY(!target_dir.entryList().contains("assets")); }; // first try variant without trailing / QVERIFY(!folder.endsWith('/')); f(); // then variant with trailing / folder.append('/'); QVERIFY(folder.endsWith('/')); f(); } void test_link_with_dot_hidden() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); LinkTask lnk_tsk(folder, target_dir.path()); lnk_tsk.linkRecursively(true); connect(&lnk_tsk, &Task::finished, [&lnk_tsk] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); QVERIFY2(QTest::qWaitFor([&lnk_tsk]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden; for (auto entry : target_dir.entryList(filter)) { qDebug() << entry; QFileInfo entry_lnk_info(target_dir.filePath(entry)); if (!entry_lnk_info.isDir()) QVERIFY(entry_lnk_info.isSymLink()); } QFileInfo lnk_info(target_dir.path()); QVERIFY(lnk_info.exists()); QVERIFY(target_dir.entryList(filter).contains(".secret_folder")); target_dir.cd(".secret_folder"); QVERIFY(target_dir.entryList(filter).contains(".secret_file.txt")); }; // first try variant without trailing / QVERIFY(!folder.endsWith('/')); f(); // then variant with trailing / folder.append('/'); QVERIFY(folder.endsWith('/')); f(); } void test_link_single_file() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); { QString file = QFINDTESTDATA("testdata/FileSystem/test_folder/pack.mcmeta"); qDebug() << "From:" << file << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "pack.mcmeta")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); LinkTask lnk_tsk(file, target_dir.filePath("pack.mcmeta")); connect(&lnk_tsk, &Task::finished, [&lnk_tsk] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); QVERIFY2(QTest::qWaitFor([&lnk_tsk]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); auto filter = QDir::Filter::Files; for (auto entry : target_dir.entryList(filter)) { qDebug() << entry; } QFileInfo lnk_info(target_dir.filePath("pack.mcmeta")); QVERIFY(lnk_info.exists()); QVERIFY(lnk_info.isSymLink()); QVERIFY(target_dir.entryList(filter).contains("pack.mcmeta")); } } void test_link_with_max_depth() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); LinkTask lnk_tsk(folder, target_dir.path()); lnk_tsk.linkRecursively(true); lnk_tsk.setMaxDepth(0); connect(&lnk_tsk, &Task::finished, [&lnk_tsk] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); QVERIFY2(QTest::qWaitFor([&lnk_tsk]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); QVERIFY(!QFileInfo(target_dir.path()).isSymLink()); auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden; for (auto entry : target_dir.entryList(filter)) { qDebug() << entry; if (entry == "." || entry == "..") continue; QFileInfo entry_lnk_info(target_dir.filePath(entry)); QVERIFY(entry_lnk_info.isSymLink()); } QFileInfo lnk_info(target_dir.path()); QVERIFY(lnk_info.exists()); QVERIFY(!lnk_info.isSymLink()); QVERIFY(target_dir.entryList().contains("pack.mcmeta")); QVERIFY(target_dir.entryList().contains("assets")); }; // first try variant without trailing / QVERIFY(!folder.endsWith('/')); f(); // then variant with trailing / folder.append('/'); QVERIFY(folder.endsWith('/')); f(); } void test_link_with_no_max_depth() { QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder"); auto f = [&folder]() { QTemporaryDir tempDir; tempDir.setAutoRemove(true); qDebug() << "From:" << folder << "To:" << tempDir.path(); QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder")); qDebug() << tempDir.path(); qDebug() << target_dir.path(); LinkTask lnk_tsk(folder, target_dir.path()); lnk_tsk.linkRecursively(true); lnk_tsk.setMaxDepth(-1); connect(&lnk_tsk, &Task::finished, [&lnk_tsk] { QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been."); }); lnk_tsk.start(); QVERIFY2(QTest::qWaitFor([&lnk_tsk]() { return lnk_tsk.isFinished(); }, 100000), "Task didn't finish as it should."); std::function verify_check = [&verify_check](QString check_path) { QDir check_dir(check_path); auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden; for (auto entry : check_dir.entryList(filter)) { QFileInfo entry_lnk_info(check_dir.filePath(entry)); qDebug() << entry << check_dir.filePath(entry); if (!entry_lnk_info.isDir()) { QVERIFY(entry_lnk_info.isSymLink()); } else if (entry != "." && entry != "..") { qDebug() << "Decending tree to verify symlinks:" << check_dir.filePath(entry); verify_check(entry_lnk_info.filePath()); } } }; verify_check(target_dir.path()); QFileInfo lnk_info(target_dir.path()); QVERIFY(lnk_info.exists()); QVERIFY(target_dir.entryList().contains("pack.mcmeta")); QVERIFY(target_dir.entryList().contains("assets")); }; // first try variant without trailing / QVERIFY(!folder.endsWith('/')); f(); // then variant with trailing / folder.append('/'); QVERIFY(folder.endsWith('/')); f(); } void test_path_depth() { QCOMPARE(FS::pathDepth(""), 0); QCOMPARE(FS::pathDepth("."), 0); QCOMPARE(FS::pathDepth("foo.txt"), 0); QCOMPARE(FS::pathDepth("./foo.txt"), 0); QCOMPARE(FS::pathDepth("./bar/foo.txt"), 1); QCOMPARE(FS::pathDepth("../bar/foo.txt"), 0); QCOMPARE(FS::pathDepth("/bar/foo.txt"), 1); QCOMPARE(FS::pathDepth("baz/bar/foo.txt"), 2); QCOMPARE(FS::pathDepth("/baz/bar/foo.txt"), 2); QCOMPARE(FS::pathDepth("./baz/bar/foo.txt"), 2); QCOMPARE(FS::pathDepth("/baz/../bar/foo.txt"), 1); } void test_path_trunc() { QCOMPARE(FS::pathTruncate("", 0), QDir::toNativeSeparators("")); QCOMPARE(FS::pathTruncate("foo.txt", 0), QDir::toNativeSeparators("")); QCOMPARE(FS::pathTruncate("foo.txt", 1), QDir::toNativeSeparators("")); QCOMPARE(FS::pathTruncate("./bar/foo.txt", 0), QDir::toNativeSeparators("./bar")); QCOMPARE(FS::pathTruncate("./bar/foo.txt", 1), QDir::toNativeSeparators("./bar")); QCOMPARE(FS::pathTruncate("/bar/foo.txt", 1), QDir::toNativeSeparators("/bar")); QCOMPARE(FS::pathTruncate("bar/foo.txt", 1), QDir::toNativeSeparators("bar")); QCOMPARE(FS::pathTruncate("baz/bar/foo.txt", 2), QDir::toNativeSeparators("baz/bar")); #if defined(Q_OS_WIN) QCOMPARE(FS::pathTruncate("C:\\bar\\foo.txt", 1), QDir::toNativeSeparators("C:\\bar")); #endif } }; QTEST_GUILESS_MAIN(FileSystemTest) #include "FileSystem_test.moc" PrismLauncher-11.0.3/tests/Task_test.cpp0000644000175100017510000001677015224505336017605 0ustar runnerrunner#include #include #include #include #include #include #include #include /* Does nothing. Only used for testing. */ class BasicTask : public Task { Q_OBJECT friend class TaskTest; public: BasicTask(bool show_debug_log = true) : Task(show_debug_log) {} private: void executeTask() override { emitSucceeded(); } }; /* Does nothing. Only used for testing. */ class BasicTask_MultiStep : public Task { Q_OBJECT friend class TaskTest; private: auto isMultiStep() const -> bool override { return true; } void executeTask() override {} }; class BigConcurrentTask : public ConcurrentTask { Q_OBJECT void executeNextSubTask() override { // This is here only to help fill the stack a bit more quickly (if there's an issue, of course :^)) // Each tasks thus adds 1024 * 4 bytes to the stack, at the very least. [[maybe_unused]] volatile std::array some_data_on_the_stack{}; ConcurrentTask::executeNextSubTask(); } }; class BigConcurrentTaskThread : public QThread { Q_OBJECT QTimer m_deadline; void run() override { BigConcurrentTask big_task; m_deadline.setInterval(10000); // NOTE: Arbitrary value that manages to trigger a problem when there is one. // Considering each tasks, in a problematic state, adds 1024 * 4 bytes to the stack, // this number is enough to fill up 16 MiB of stack, more than enough to cause a problem. static const unsigned s_num_tasks = 1 << 12; for (unsigned i = 0; i < s_num_tasks; i++) { auto sub_task = makeShared(false); big_task.addTask(sub_task); } connect(&big_task, &Task::finished, this, &QThread::quit); connect(&m_deadline, &QTimer::timeout, this, [this] { passed_the_deadline = true; quit(); }); if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod(this, &BigConcurrentTaskThread::start_timer, Qt::QueuedConnection); } big_task.run(); exec(); } void start_timer() { m_deadline.start(); } public: bool passed_the_deadline = false; }; class TaskTest : public QObject { Q_OBJECT private slots: void test_SetStatus_NoMultiStep() { BasicTask t; QString status{ "test status" }; t.setStatus(status); QCOMPARE(t.getStatus(), status); QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty()); } void test_SetStatus_MultiStep() { BasicTask_MultiStep t; QString status{ "test status" }; t.setStatus(status); QCOMPARE(t.getStatus(), status); // Even though it is multi step, it does not override the getStepStatus method, // so it should remain the same. QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty()); } void test_SetProgress() { BasicTask t; int current = 42; int total = 207; t.setProgress(current, total); QCOMPARE(t.getProgress(), current); QCOMPARE(t.getTotalProgress(), total); } void test_basicRun() { BasicTask t; connect(&t, &Task::finished, [&t] { QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); }); t.start(); QVERIFY2(QTest::qWaitFor([&t]() { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } void test_basicConcurrentRun() { auto t1 = makeShared(); auto t2 = makeShared(); auto t3 = makeShared(); ConcurrentTask t; t.addTask(t1); t.addTask(t2); t.addTask(t3); connect(&t, &Task::finished, [&t, &t1, &t2, &t3] { QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY(t1->wasSuccessful()); QVERIFY(t2->wasSuccessful()); QVERIFY(t3->wasSuccessful()); }); t.start(); QVERIFY2(QTest::qWaitFor([&t]() { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } // Tests if starting new tasks after the 6 initial ones is working void test_moreConcurrentRun() { auto t1 = makeShared(); auto t2 = makeShared(); auto t3 = makeShared(); auto t4 = makeShared(); auto t5 = makeShared(); auto t6 = makeShared(); auto t7 = makeShared(); auto t8 = makeShared(); auto t9 = makeShared(); ConcurrentTask t; t.addTask(t1); t.addTask(t2); t.addTask(t3); t.addTask(t4); t.addTask(t5); t.addTask(t6); t.addTask(t7); t.addTask(t8); t.addTask(t9); connect(&t, &Task::finished, [&t, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9] { QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY(t1->wasSuccessful()); QVERIFY(t2->wasSuccessful()); QVERIFY(t3->wasSuccessful()); QVERIFY(t4->wasSuccessful()); QVERIFY(t5->wasSuccessful()); QVERIFY(t6->wasSuccessful()); QVERIFY(t7->wasSuccessful()); QVERIFY(t8->wasSuccessful()); QVERIFY(t9->wasSuccessful()); }); t.start(); QVERIFY2(QTest::qWaitFor([&t]() { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } void test_basicSequentialRun() { auto t1 = makeShared(); auto t2 = makeShared(); auto t3 = makeShared(); SequentialTask t; t.addTask(t1); t.addTask(t2); t.addTask(t3); connect(&t, &Task::finished, [&t, &t1, &t2, &t3] { QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY(t1->wasSuccessful()); QVERIFY(t2->wasSuccessful()); QVERIFY(t3->wasSuccessful()); }); t.start(); QVERIFY2(QTest::qWaitFor([&t]() { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } void test_basicMultipleOptionsRun() { auto t1 = makeShared(); auto t2 = makeShared(); auto t3 = makeShared(); MultipleOptionsTask t; t.addTask(t1); t.addTask(t2); t.addTask(t3); connect(&t, &Task::finished, [&t, &t1, &t2, &t3] { QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY(t1->wasSuccessful()); QVERIFY(!t2->wasSuccessful()); QVERIFY(!t3->wasSuccessful()); }); t.start(); QVERIFY2(QTest::qWaitFor([&t]() { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } void test_stackOverflowInConcurrentTask() { QEventLoop loop; BigConcurrentTaskThread thread; connect(&thread, &BigConcurrentTaskThread::finished, &loop, &QEventLoop::quit); thread.start(); loop.exec(); QVERIFY(!thread.passed_the_deadline); } }; QTEST_GUILESS_MAIN(TaskTest) #include "Task_test.moc" PrismLauncher-11.0.3/tests/INIFile_test.cpp0000644000175100017510000001466615224505336020124 0ustar runnerrunner#include #include #include #include #include #include #include "FileSystem.h" #include class IniFileTest : public QObject { Q_OBJECT private slots: void initTestCase() {} void cleanupTestCase() {} void test_Escape_data() { QTest::addColumn("through"); QTest::newRow("unix path") << "/abc/def/ghi/jkl"; QTest::newRow("windows path") << "C:\\Program files\\terrible\\name\\of something\\"; QTest::newRow("Plain text") << "Lorem ipsum dolor sit amet."; QTest::newRow("Escape sequences") << "Lorem\n\t\n\\n\\tAAZ\nipsum dolor\n\nsit amet."; QTest::newRow("Escape sequences 2") << "\"\n\n\""; QTest::newRow("Hashtags") << "some data#something"; } void test_SaveLoad() { QString a = "a"; QString b = "a\nb\t\n\\\\\\C:\\Program files\\terrible\\name\\of something\\#thisIsNotAComment"; QString filename = "test_SaveLoad.ini"; // save INIFile f; f.set("a", a); f.set("b", b); f.saveFile(filename); // load INIFile f2; f2.loadFile(filename); QCOMPARE(f2.get("a", "NOT SET").toString(), a); QCOMPARE(f2.get("b", "NOT SET").toString(), b); } void test_SaveLoadLists() { QString slist_strings = "(\"a\",\"b\",\"c\")"; QStringList list_strings = { "a", "b", "c" }; QString slist_numbers = "(1,2,3,10)"; QList list_numbers = { 1, 2, 3, 10 }; QString filename = "test_SaveLoadLists.ini"; INIFile f; f.set("list_strings", list_strings); f.set("list_numbers", QVariantUtils::fromList(list_numbers)); f.saveFile(filename); // load INIFile f2; f2.loadFile(filename); QStringList out_list_strings = f2.get("list_strings", QStringList()).toStringList(); qDebug() << "OutStringList" << out_list_strings; QList out_list_numbers = QVariantUtils::toList(f2.get("list_numbers", QVariantUtils::fromList(QList()))); qDebug() << "OutNumbersList" << out_list_numbers; QCOMPARE(out_list_strings, list_strings); QCOMPARE(out_list_numbers, list_numbers); } void test_SaveAlreadyExistingFile() { QString fileContent = R"(InstanceType=OneSix iconKey=vanillia_icon name=Minecraft Vanillia OverrideCommands=true PreLaunchCommand="$INST_JAVA" -jar packwiz-installer-bootstrap.jar link Wrapperommand=)"; fileContent += "\""; fileContent += +R"(\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link =)"; fileContent += "\"\n"; #if defined(Q_OS_WIN) QString fileName = "test_SaveAlreadyExistingFile.ini"; QFile file(fileName); QCOMPARE(file.open(QFile::WriteOnly | QFile::Text), true); #else QTemporaryFile file; QCOMPARE(file.open(), true); QCOMPARE(file.fileName().isEmpty(), false); QString fileName = file.fileName(); #endif QTextStream stream(&file); stream << fileContent; file.close(); // load INIFile f1; f1.loadFile(fileName); QCOMPARE(f1.get("PreLaunchCommand", "NOT SET").toString(), "\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link"); QCOMPARE(f1.get("Wrapperommand", "NOT SET").toString(), "\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link ="); f1.saveFile(fileName); INIFile f2; f2.loadFile(fileName); QCOMPARE(f2.get("PreLaunchCommand", "NOT SET").toString(), "\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link"); QCOMPARE(f2.get("Wrapperommand", "NOT SET").toString(), "\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link ="); QCOMPARE(f2.get("ConfigVersion", "NOT SET").toString(), "1.3"); #if defined(Q_OS_WIN) FS::deletePath(fileName); #endif } void test_SaveAlreadyExistingFileWithSpecialChars() { #if defined(Q_OS_WIN) QString fileName = "test_SaveAlreadyExistingFileWithSpecialChars.ini"; #else QTemporaryFile file; QCOMPARE(file.open(), true); QCOMPARE(file.fileName().isEmpty(), false); QString fileName = file.fileName(); file.close(); #endif QSettings settings{ fileName, QSettings::Format::IniFormat }; settings.setFallbacksEnabled(false); settings.setValue("simple", "value1"); settings.setValue("withQuotes", R"("value2" with quotes)"); settings.setValue("withSpecialCharacters", "env mesa=true"); settings.setValue("withSpecialCharacters2", "1,2,3,4"); settings.setValue("withSpecialCharacters2", "1;2;3;4"); settings.setValue("withAll", "val=\"$INST_JAVA\" -jar; ls "); settings.sync(); QCOMPARE(settings.status(), QSettings::Status::NoError); // load INIFile f1; f1.loadFile(fileName); for (auto key : settings.allKeys()) QCOMPARE(f1.get(key, "NOT SET").toString(), settings.value(key).toString()); f1.saveFile(fileName); INIFile f2; f2.loadFile(fileName); for (auto key : settings.allKeys()) QCOMPARE(f2.get(key, "NOT SET").toString(), settings.value(key).toString()); QCOMPARE(f2.get("ConfigVersion", "NOT SET").toString(), "1.3"); #if defined(Q_OS_WIN) FS::deletePath(fileName); #endif } void test_SaveAlreadyExistingFileWithSpecialCharsV1() { QString fileContent = R"(InstanceType=OneSix ConfigVersion=1.1 iconKey=vanillia_icon name=Minecraft Vanillia OverrideCommands=true PreLaunchCommand=)"; fileContent += "\"\\\"env mesa=true\\\"\"\n"; #if defined(Q_OS_WIN) QString fileName = "test_SaveAlreadyExistingFileWithSpecialCharsV1.ini"; QFile file(fileName); QCOMPARE(file.open(QFile::WriteOnly | QFile::Text), true); #else QTemporaryFile file; QCOMPARE(file.open(), true); QCOMPARE(file.fileName().isEmpty(), false); QString fileName = file.fileName(); #endif QTextStream stream(&file); stream << fileContent; file.close(); // load INIFile f1; f1.loadFile(fileName); QCOMPARE(f1.get("PreLaunchCommand", "NOT SET").toString(), "env mesa=true"); QCOMPARE(f1.get("ConfigVersion", "NOT SET").toString(), "1.3"); #if defined(Q_OS_WIN) FS::deletePath(fileName); #endif } }; QTEST_GUILESS_MAIN(IniFileTest) #include "INIFile_test.moc" PrismLauncher-11.0.3/tests/MetaComponentParse_test.cpp0000644000175100017510000000604415224505336022440 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include class MetaComponentParseTest : public QObject { Q_OBJECT void doTest(QString name) { QString source = QFINDTESTDATA("testdata/MetaComponentParse"); QString comp_rp = FS::PathCombine(source, name); QFile file; file.setFileName(comp_rp); QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); QString data = file.readAll(); file.close(); QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8()); QJsonObject obj = doc.object(); QJsonValue description_json = obj.value("description"); QJsonValue expected_json = obj.value("expected_output"); QVERIFY(!description_json.isUndefined()); QVERIFY(expected_json.isString()); QString expected = expected_json.toString(); QString processed = DataPackUtils::processComponent(description_json); QCOMPARE(processed, expected); } private slots: void test_parseComponentBasic() { doTest("component_basic.json"); } void test_parseComponentWithFormat() { doTest("component_with_format.json"); } void test_parseComponentWithExtra() { doTest("component_with_extra.json"); } void test_parseComponentWithLink() { doTest("component_with_link.json"); } void test_parseComponentWithMixed() { doTest("component_with_mixed.json"); } }; QTEST_GUILESS_MAIN(MetaComponentParseTest) #include "MetaComponentParse_test.moc" PrismLauncher-11.0.3/tests/ShaderPackParse_test.cpp0000644000175100017510000000440115224505336021667 0ustar runnerrunner // SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include class ShaderPackParseTest : public QObject { Q_OBJECT private slots: void test_parseZIP() { QString source = QFINDTESTDATA("testdata/ShaderPackParse"); QString zip_sp = FS::PathCombine(source, "shaderpack1.zip"); ShaderPack pack{ QFileInfo(zip_sp) }; bool valid = ShaderPackUtils::processZIP(pack); QVERIFY(pack.packFormat() == ShaderPackFormat::VALID); QVERIFY(valid == true); } void test_parseFolder() { QString source = QFINDTESTDATA("testdata/ShaderPackParse"); QString folder_sp = FS::PathCombine(source, "shaderpack2"); ShaderPack pack{ QFileInfo(folder_sp) }; bool valid = ShaderPackUtils::processFolder(pack); QVERIFY(pack.packFormat() == ShaderPackFormat::VALID); QVERIFY(valid == true); } void test_parseZIP2() { QString source = QFINDTESTDATA("testdata/ShaderPackParse"); QString folder_sp = FS::PathCombine(source, "shaderpack3.zip"); ShaderPack pack{ QFileInfo(folder_sp) }; bool valid = ShaderPackUtils::process(pack); QVERIFY(pack.packFormat() == ShaderPackFormat::INVALID); QVERIFY(valid == false); } }; QTEST_GUILESS_MAIN(ShaderPackParseTest) #include "ShaderPackParse_test.moc" PrismLauncher-11.0.3/CMakeLists.txt0000644000175100017510000005556115224505336016537 0ustar runnerrunnercmake_minimum_required(VERSION 3.25) # Required for features like `CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` project(Launcher LANGUAGES C CXX) if(APPLE) enable_language(OBJC OBJCXX) endif() string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD) if(IS_IN_SOURCE_BUILD) message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.") endif() ##################################### Set CMake options ##################################### set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/") # Output all executables and shared libs in the main build folder, not in subfolders. set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) if(UNIX) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) endif() set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars) ######## Set compiler flags ######## set(CMAKE_CXX_STANDARD_REQUIRED true) set(CMAKE_C_STANDARD_REQUIRED true) set(CMAKE_CXX_STANDARD 20) set(CMAKE_C_STANDARD 11) include(GenerateExportHeader) add_compile_definitions($<$>:QT_NO_DEBUG>) add_compile_definitions(QT_WARN_DEPRECATED_UP_TO=0x060400) add_compile_definitions(QT_DISABLE_DEPRECATED_UP_TO=0x060400) if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") add_compile_options( # /GS Adds buffer security checks, default on but included anyway to mirror gcc's fstack-protector flag "$<$:/GS>" # /Gw helps reduce binary size # /Gy allows the compiler to package individual functions # /guard:cf enables control flow guard "$<$,$>:/Gw;/Gy;/guard:cf>" ) add_link_options( # /LTCG allows for linking wholy optimizated programs # /MANIFEST:NO disables generating a manifest file, we instead provide our own # /STACK sets the stack reserve size, ATL's pack list needs 3-4 MiB as of November 2022, provide 8 MiB "$<$:/LTCG;/MANIFEST:NO;/STACK:8388608>" ) # /GL enables whole program optimizations # NOTE: With Clang, this is implemented as regular LTO and only used during linking if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") add_compile_options("$<$,$>:/GL>") endif() # See https://github.com/ccache/ccache/issues/1040 # TODO(@getchoo): Is sccache affected by this? Would be nice to use `ProgramDatabase`.... set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$:Embedded>") if(CMAKE_MSVC_RUNTIME_LIBRARY STREQUAL "MultiThreadedDLL") set(CMAKE_MAP_IMPORTED_CONFIG_DEBUG Release "") set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release "") endif() else() add_compile_options("$<$:-fstack-protector-strong;--param=ssp-buffer-size=4>") # Avoid re-defining _FORTIFY_SOURCE, as it can cause redefinition errors in setups that use it by default (i.e., package builds) if(NOT (CMAKE_C_FLAGS MATCHES "-D_FORTIFY_SOURCE" OR CMAKE_CXX_FLAGS MATCHES "-D_FORTIFY_SOURCE")) # NOTE: _FORTIFY_SOURCE requires optimizations in most newer versions of glibc add_compile_options("$<$,$>:-D_FORTIFY_SOURCE=2>") endif() # ATL's pack list needs more than the default 1 Mib stack on windows if(CMAKE_SYSTEM_NAME STREQUAL "Windows") add_link_options("$<$:-Wl,--stack,8388608>") # -ffunction-sections and -fdata-sections help reduce binary size # -mguard=cf enables Control Flow Guard # TODO: Look into -gc-sections to further reduce binary size add_compile_options("$<$,$>:-ffunction-sections;-fdata-sections;-mguard=cf>") endif() endif() # Export compile commands for debug builds if we can (useful in LSPs like clangd) # https://cmake.org/cmake/help/v3.31/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html if(CMAKE_GENERATOR STREQUAL "Unix Makefiles" OR CMAKE_GENERATOR MATCHES "^Ninja") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) endif() option(USE_CLANG_TIDY "Enable the use of clang-tidy during compilation" OFF) if(USE_CLANG_TIDY) find_program(CLANG_TIDY clang-tidy OPTIONAL) if(CLANG_TIDY) message(STATUS "Using clang-tidy during compilation") set(CLANG_TIDY_COMMAND "${CLANG_TIDY}" "--config-file=${CMAKE_SOURCE_DIR}/.clang-tidy") set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_COMMAND}) else() message(WARNING "Unable to find `clang-tidy`. Not using during compilation") endif() endif() option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF) # If this is a Debug build turn on address sanitiser if (DEBUG_ADDRESS_SANITIZER) message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off") set(USE_ASAN_COMPILE_OPTIONS $,$>) if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") message(STATUS "Using Address Sanitizer compile options for MSVC frontend") add_compile_options( $<${USE_ASAN_COMPILE_OPTIONS}:/fsanitize=address> $<${USE_ASAN_COMPILE_OPTIONS}:/Oy-> ) elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") message(STATUS "Using Address Sanitizer compile options for GCC/Clang") add_compile_options( $<${USE_ASAN_COMPILE_OPTIONS}:-fsanitize=address,undefined> $<${USE_ASAN_COMPILE_OPTIONS}:-fno-omit-frame-pointer> $<${USE_ASAN_COMPILE_OPTIONS}:-fno-sanitize-recover=null> ) link_libraries("asan" "ubsan") else() message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}") endif() endif() option(ENABLE_LTO "Enable Link Time Optimization" off) if(ENABLE_LTO) include(CheckIPOSupported) check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error) if(ipo_supported) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL TRUE) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE) if(CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") message(STATUS "IPO / LTO enabled") else() message(STATUS "Not enabling IPO / LTO on debug builds") endif() else() message(STATUS "IPO / LTO will only be enabled for release builds") endif() else() message(STATUS "IPO / LTO not supported: <${ipo_error}>") endif() endif() option(BUILD_TESTING "Build the testing tree." ON) find_package(ECM NO_MODULE REQUIRED) set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}") include(CTest) include(ECMAddTests) if(BUILD_TESTING) enable_testing() endif() ##################################### Set Application options ##################################### ######## Set URLs ######## set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.") set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'") set(Launcher_WIKI_URL "https://prismlauncher.org/wiki/" CACHE STRING "URL that gets opened when the user clicks 'Launcher Help'") set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help in a dialog window") set(Launcher_LOGIN_CALLBACK_URL "https://prismlauncher.org/successful-login" CACHE STRING "URL that gets opened when the user successfully logins.") set(Launcher_LEGACY_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" CACHE STRING "URL for legacy (<=1.5.2) FML Libraries.") ######## Set version numbers ######## set(Launcher_VERSION_MAJOR 11) set(Launcher_VERSION_MINOR 0) set(Launcher_VERSION_PATCH 3) set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}.0") set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},${Launcher_VERSION_PATCH},0") # Build platform. set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.") # Github repo URL with releases for updater set(Launcher_UPDATER_GITHUB_REPO "https://github.com/PrismLauncher/PrismLauncher" CACHE STRING "Base github URL for the updater.") # Name to help updater identify valid artifacts set(Launcher_BUILD_ARTIFACT "" CACHE STRING "Artifact name to help the updater identify valid artifacts.") # The metadata server set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.") # Imgur API Client ID set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application") # Bug tracker URL set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/issues" CACHE STRING "URL for the bug tracker.") # Translations Platform URL set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.") set(Launcher_TRANSLATION_FILES_URL "https://i18n.prismlauncher.org/" CACHE STRING "URL for the translations files.") # Matrix Space set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space") # Discord URL set(Launcher_DISCORD_URL "https://prismlauncher.org/discord" CACHE STRING "URL for the Discord guild.") # Subreddit URL set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL for the subreddit.") # Builds set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against") option(Launcher_USE_PCH "Use precompiled headers where possible" ON) # Java downloader set(Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT ON) # Although we recommend enabling this, we cannot guarantee binary compatibility on # differing Linux/BSD/etc distributions. Downstream packagers should be explicitly opt-ing into this # feature if they know it will work with their distribution. if(UNIX AND NOT APPLE) set(Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT OFF) endif() # Java downloader option(Launcher_ENABLE_JAVA_DOWNLOADER "Build the java downloader feature" ${Launcher_ENABLE_JAVA_DOWNLOADER_DEFAULT}) # Native libraries if(UNIX AND APPLE) set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library") set(Launcher_OPENAL_LIBRARY_NAME "libopenal.dylib" CACHE STRING "Name of native openal library") elseif(UNIX) set(Launcher_GLFW_LIBRARY_NAME "libglfw.so" CACHE STRING "Name of native glfw library") set(Launcher_OPENAL_LIBRARY_NAME "libopenal.so" CACHE STRING "Name of native openal library") elseif(WIN32) set(Launcher_GLFW_LIBRARY_NAME "glfw.dll" CACHE STRING "Name of native glfw library") set(Launcher_OPENAL_LIBRARY_NAME "OpenAL.dll" CACHE STRING "Name of native openal library") endif() # API Keys # NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service # of these platforms, please change these API keys beforehand. # Be aware that if you were to use these API keys for malicious purposes they might get revoked, which might cause # breakage to thousands of users. # If you don't plan to use these features of this software, you can just remove these values. # By using this key in your builds you accept the terms of use laid down in # https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application") # By using this key in your builds you accept the terms and conditions laid down in # https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions # NOTE: CurseForge requires you to change this if you make any kind of derivative work. # This key was issued specifically for Prism Launcher set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform") set(Launcher_COMPILER_NAME ${CMAKE_CXX_COMPILER_ID}) set(Launcher_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}) set(Launcher_COMPILER_TARGET_SYSTEM ${CMAKE_SYSTEM_NAME}) set(Launcher_COMPILER_TARGET_SYSTEM_VERSION ${CMAKE_SYSTEM_VERSION}) set(Launcher_COMPILER_TARGET_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}) #### Check the current Git commit and branch include(GetGitRevisionDescription) git_get_exact_tag(Launcher_GIT_TAG) get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT) message(STATUS "Git commit: ${Launcher_GIT_COMMIT}") message(STATUS "Git tag: ${Launcher_GIT_TAG}") message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}") string(TIMESTAMP TODAY "%Y-%m-%d") set(Launcher_BUILD_TIMESTAMP "${TODAY}") ################################ 3rd Party Libs ################################ # Find the required Qt parts if(Launcher_QT_VERSION_MAJOR EQUAL 6) set(QT_VERSION_MAJOR 6) find_package(Qt6 6.4 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml NetworkAuth OpenGL) find_package(Qt6 COMPONENTS DBus) list(APPEND Launcher_QT_DBUS Qt6::DBus) else() message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported") endif() if(Launcher_QT_VERSION_MAJOR EQUAL 6) set(QT_PLUGINS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_PLUGINS}) set(QT_LIBS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBS}) set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS}) endif() find_package(cmark REQUIRED) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") find_package(PkgConfig REQUIRED) pkg_check_modules(gamemode REQUIRED IMPORTED_TARGET gamemode) endif() # Find libqrencode ## NOTE(@getchoo): Never use pkg-config with MSVC since the vcpkg port makes our install bundle fail to find the dll if(MSVC) find_path(LIBQRENCODE_INCLUDE_DIR qrencode.h REQUIRED) find_library(LIBQRENCODE_LIBRARY_RELEASE qrencode REQUIRED) find_library(LIBQRENCODE_LIBRARY_DEBUG qrencoded) set(LIBQRENCODE_LIBRARIES optimized ${LIBQRENCODE_LIBRARY_RELEASE} debug ${LIBQRENCODE_LIBRARY_DEBUG}) else() find_package(PkgConfig REQUIRED) pkg_check_modules(libqrencode REQUIRED IMPORTED_TARGET libqrencode) endif() # Find libarchive through CMake packages, mainly for vcpkg find_package(LibArchive) # CMake packages aren't available in most distributions of libarchive, so fallback to pkg-config if(NOT LibArchive_FOUND) find_package(PkgConfig REQUIRED) pkg_check_modules(libarchive REQUIRED IMPORTED_TARGET libarchive) endif() find_package(tomlplusplus 3.2.0) # fallback to pkgconfig, important especially as many distros package toml++ built with meson if(NOT tomlplusplus_FOUND) find_package(PkgConfig REQUIRED) pkg_check_modules(tomlplusplus REQUIRED IMPORTED_TARGET tomlplusplus>=3.2.0) endif() find_package(ZLIB REQUIRED) include(ECMQtDeclareLoggingCategory) ####################################### Program Info ####################################### set(Launcher_APP_BINARY_NAME "prismlauncher" CACHE STRING "Name of the Launcher binary") add_subdirectory(program_info) ####################################### Install layout ####################################### set(Launcher_ENABLE_UPDATER NO) set(Launcher_BUILD_UPDATER NO) if (NOT APPLE AND (NOT Launcher_UPDATER_GITHUB_REPO STREQUAL "" AND NOT Launcher_BUILD_ARTIFACT STREQUAL "")) set(Launcher_BUILD_UPDATER YES) endif() if(NOT (UNIX AND APPLE)) # Install "portable.txt" if selected component is "portable" install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL) endif() if(UNIX AND APPLE) set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS") set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS") set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS") set(FRAMEWORK_DEST_DIR "${Launcher_Name}.app/Contents/Frameworks") set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources") set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars") # Mac bundle settings set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}") set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.") set(MACOSX_BUNDLE_GUI_IDENTIFIER "${Launcher_AppID}") set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}") set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns) set(MACOSX_BUNDLE_COPYRIGHT "${Launcher_Copyright_Mac}") set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed") set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed") set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.8.0/Sparkle-2.8.0.tar.xz" CACHE STRING "URL to Sparkle release archive") set(MACOSX_SPARKLE_SHA256 "fd5681ee92bf238aaac2d08214ceaf0cc8976e452d7f882d80bac1e61581f3b1" CACHE STRING "SHA256 checksum for Sparkle release archive") set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle") if(NOT MACOSX_SPARKLE_UPDATE_PUBLIC_KEY STREQUAL "" AND NOT MACOSX_SPARKLE_UPDATE_FEED_URL STREQUAL "") set(Launcher_ENABLE_UPDATER YES) endif() # Add the icon install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns) find_program(ACTOOL_EXE actool DOC "Path to the apple asset catalog compiler") if(ACTOOL_EXE) execute_process( COMMAND xcodebuild -version OUTPUT_VARIABLE XCODE_VERSION_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE ) string(REGEX MATCH "Xcode ([0-9]+\.[0-9]+)" XCODE_VERSION_MATCH "${XCODE_VERSION_OUTPUT}") if(XCODE_VERSION_MATCH) set(XCODE_VERSION ${CMAKE_MATCH_1}) else() set(XCODE_VERSION 0.0) endif() if(XCODE_VERSION VERSION_GREATER_EQUAL 26.0) set(ASSETS_OUT_DIR "${CMAKE_BINARY_DIR}/program_info") set(GENERATED_ASSETS_CAR "${ASSETS_OUT_DIR}/Assets.car") set(ICON_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Branding_MAC_ICON}") add_custom_command( OUTPUT "${GENERATED_ASSETS_CAR}" COMMAND ${ACTOOL_EXE} "${ICON_SOURCE}" --compile "${ASSETS_OUT_DIR}" --output-partial-info-plist /dev/null --app-icon ${Launcher_Name} --enable-on-demand-resources NO --target-device mac --minimum-deployment-target ${CMAKE_OSX_DEPLOYMENT_TARGET} --platform macosx DEPENDS "${ICON_SOURCE}" COMMENT "Compiling asset catalog (${ICON_SOURCE})" VERBATIM ) add_custom_target(compile_assets ALL DEPENDS "${GENERATED_ASSETS_CAR}") install(FILES "${GENERATED_ASSETS_CAR}" DESTINATION "${RESOURCES_DEST_DIR}") else() message(WARNING "Xcode ${XCODE_VERSION} is too old. Minimum required version is 26.0. Not compiling liquid glass icons.") endif() else() message(WARNING "actool not found. Cannot compile macOS app icons.\n" "Install Xcode command line tools: 'xcode-select --install'") endif() elseif(UNIX) include(KDEInstallDirs) set(BINARY_DEST_DIR "bin") set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}") set(JARS_DEST_DIR "share/${Launcher_Name}") # Set RPATH SET(Launcher_BINARY_RPATH "$ORIGIN/") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${KDE_INSTALL_METAINFODIR}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps") install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_PNG_256} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/256x256/apps" RENAME "${Launcher_AppID}.png") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR}) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}") set(PLUGIN_DEST_DIR "plugins") set(BUNDLE_DEST_DIR ".") set(RESOURCES_DEST_DIR ".") if(Launcher_ManPage) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6") endif() # Install basic runner script if component "portable" is selected configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY) install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" DESTINATION "." RENAME ${Launcher_Name} COMPONENT portable EXCLUDE_FROM_ALL) elseif(WIN32) set(BINARY_DEST_DIR ".") set(LIBRARY_DEST_DIR ".") set(PLUGIN_DEST_DIR ".") set(RESOURCES_DEST_DIR ".") set(JARS_DEST_DIR "jars") else() message(FATAL_ERROR "Platform not supported") endif() ################################ Included Libs ################################ include(ExternalProject) set_directory_properties(PROPERTIES EP_BASE External) option(NBT_BUILD_SHARED "Build NBT shared library" OFF) option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF) option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests. add_subdirectory(libraries/libnbtplusplus) add_subdirectory(libraries/launcher) # java based launcher part for Minecraft add_subdirectory(libraries/javacheck) # java compatibility checker add_subdirectory(libraries/rainbow) # Qt extension for colors add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API add_subdirectory(libraries/qdcss) # css parser ############################### Built Artifacts ############################### add_subdirectory(buildconfig) if(BUILD_TESTING) add_subdirectory(tests) endif() # NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order. add_subdirectory(launcher) PrismLauncher-11.0.3/COPYING.md0000644000175100017510000004754615224505336015435 0ustar runnerrunner## Prism Launcher Prism Launcher - Minecraft Launcher Copyright (C) 2022-2026 Prism Launcher Contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . This file incorporates work covered by the following copyright and permission notice: Copyright 2013-2021 MultiMC Contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ## PolyMC PolyMC - Minecraft Launcher Copyright (C) 2021-2022 PolyMC Contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . This file incorporates work covered by the following copyright and permission notice: Copyright 2013-2021 MultiMC Contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ## MinGW-w64 runtime (Windows) Copyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Information on third party licenses used in MinGW-w64 can be found in its COPYING.MinGW-w64-runtime.txt. ## Qt 6 Copyright (C) 2022 The Qt Company Ltd and other contributors. Contact: https://www.qt.io/licensing Licensed under LGPL v3 ## libnbt++ libnbt++ - A library for the Minecraft Named Binary Tag format. Copyright (C) 2013, 2015 ljfa-ag libnbt++ is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. libnbt++ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with libnbt++. If not, see . ## rainbow (KGuiAddons) Copyright (C) 2007 Matthew Woehlke Copyright (C) 2007 Olaf Schmidt Copyright (C) 2007 Thomas Zander Copyright (C) 2007 Zack Rusin Copyright (C) 2015 Petr Mrazek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ## cmark Copyright (c) 2014, John MacFarlane All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## Batch icon set You are free to use Batch (the "icon set") or any part thereof (the "icons") in any personal, open-source or commercial work without obligation of payment (monetary or otherwise) or attribution. Do not sell the icon set, host the icon set or rent the icon set (either in existing or modified form). While attribution is optional, it is always appreciated. Intellectual property rights are not transferred with the download of the icons. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL ADAM WHITCROFT BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ## Material Design Icons Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/), with Reserved Font Name Material Design Icons. Copyright (c) 2014, Google (http://www.google.com/design/) uses the license at https://github.com/google/material-design-icons/blob/master/LICENSE This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ## launcher (`libraries/launcher`) PolyMC - Minecraft Launcher Copyright (C) 2021-2022 PolyMC Contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obliged to do so. If you do not wish to do so, delete this exception statement from your version. You should have received a copy of the GNU General Public License along with this program. If not, see . This file incorporates work covered by the following copyright and permission notice: Copyright 2013-2021 MultiMC Contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ## lionshead Code has been taken from https://github.com/natefoo/lionshead and loosely translated to C++ laced with Qt. MIT License Copyright (c) 2017 Nate Coraor 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. ## tomlplusplus MIT License Copyright (c) Mark Gillard 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. ## Gamemode Copyright (c) 2017-2022, Feral Interactive All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Feral Interactive nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## Breeze icons Copyright (C) 2014 Uri Herrera and others This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . ## Oxygen Icons The Oxygen Icon Theme Copyright (C) 2007 Nuno Pinheiro Copyright (C) 2007 David Vignoni Copyright (C) 2007 David Miller Copyright (C) 2007 Johann Ollivier Lapeyre Copyright (C) 2007 Kenneth Wimer Copyright (C) 2007 Riccardo Iaconelli and others This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . ## libqrencode (`fukuchi/libqrencode`) Copyright (C) 2020 libqrencode Authors This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ## vcpkg (`cmake/vcpkg-ports`) MIT License Copyright (c) Microsoft Corporation 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. PrismLauncher-11.0.3/cmake/0000755000175100017510000000000015224505336015043 5ustar runnerrunnerPrismLauncher-11.0.3/cmake/GetGitRevisionDescription.cmake0000644000175100017510000002245715224505336023165 0ustar runnerrunner# - Returns a version string from Git # # These functions force a re-configure on each git commit so that you can # trust the values of the variables in your build system. # # get_git_head_revision( [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR]) # # Returns the refspec and sha hash of the current head revision # # git_describe( [ ...]) # # Returns the results of git describe on the source tree, and adjusting # the output so that it tests false if an error occurs. # # git_describe_working_tree( [ ...]) # # Returns the results of git describe on the working tree (--dirty option), # and adjusting the output so that it tests false if an error occurs. # # git_get_exact_tag( [ ...]) # # Returns the results of git describe --exact-match on the source tree, # and adjusting the output so that it tests false if there was no exact # matching tag. # # git_local_changes() # # Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. # Uses the return code of "git diff-index --quiet HEAD --". # Does not regard untracked files. # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2020 Ryan Pavlik # http://academic.cleardefinition.com # # Copyright 2009-2013, Iowa State University. # Copyright 2013-2020, Ryan Pavlik # Copyright 2013-2020, Contributors # SPDX-License-Identifier: BSL-1.0 # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) return() endif() set(__get_git_revision_description YES) # We must run the following at "include" time, not at function call time, # to find the path to this module rather than the path to a calling list file get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) # Function _git_find_closest_git_dir finds the next closest .git directory # that is part of any directory in the path defined by _start_dir. # The result is returned in the parent scope variable whose name is passed # as variable _git_dir_var. If no .git directory can be found, the # function returns an empty string via _git_dir_var. # # Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and # neither foo nor bar contain a file/directory .git. This wil return # C:/bla/.git # function(_git_find_closest_git_dir _start_dir _git_dir_var) set(cur_dir "${_start_dir}") set(git_dir "${_start_dir}/.git") while(NOT EXISTS "${git_dir}") # .git dir not found, search parent directories set(git_previous_parent "${cur_dir}") get_filename_component(cur_dir "${cur_dir}" DIRECTORY) if(cur_dir STREQUAL git_previous_parent) # We have reached the root directory, we are not in git set(${_git_dir_var} "" PARENT_SCOPE) return() endif() set(git_dir "${cur_dir}/.git") endwhile() set(${_git_dir_var} "${git_dir}" PARENT_SCOPE) endfunction() function(get_git_head_revision _refspecvar _hashvar) _git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR) if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR") set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE) else() set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE) endif() if(NOT "${GIT_DIR}" STREQUAL "") file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}" "${GIT_DIR}") if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR) # We've gone above the CMake root dir. set(GIT_DIR "") endif() endif() if("${GIT_DIR}" STREQUAL "") set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) return() endif() # Check if the current source dir is a git submodule or a worktree. # In both cases .git is a file instead of a directory. # if(NOT IS_DIRECTORY ${GIT_DIR}) # The following git command will return a non empty string that # points to the super project working tree if the current # source dir is inside a git submodule. # Otherwise the command will return an empty string. # execute_process( COMMAND "${GIT_EXECUTABLE}" rev-parse --show-superproject-working-tree WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT "${out}" STREQUAL "") # If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule file(READ ${GIT_DIR} submodule) string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE ${submodule}) string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") else() # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree file(READ ${GIT_DIR} worktree_ref) # The .git directory contains a path to the worktree information directory # inside the parent git repo of the worktree. # string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir ${worktree_ref}) string(STRIP ${git_worktree_dir} git_worktree_dir) _git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR) set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") endif() else() set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") endif() set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") if(NOT EXISTS "${GIT_DATA}") file(MAKE_DIRECTORY "${GIT_DATA}") endif() if(NOT EXISTS "${HEAD_SOURCE_FILE}") return() endif() set(HEAD_FILE "${GIT_DATA}/HEAD") configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY) include("${GIT_DATA}/grabRef.cmake") set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) endfunction() function(git_describe _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() # TODO sanitize #if((${ARGN}" MATCHES "&&") OR # (ARGN MATCHES "||") OR # (ARGN MATCHES "\\;")) # message("Please report the following error to the project!") # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") #endif() #message(STATUS "Arguments to execute_process: ${ARGN}") execute_process( COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_describe_working_tree _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() execute_process( COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_get_exact_tag _var) git_describe(out --exact-match ${ARGN}) set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_local_changes _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() execute_process( COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(res EQUAL 0) set(${_var} "CLEAN" PARENT_SCOPE) else() set(${_var} "DIRTY" PARENT_SCOPE) endif() endfunction() PrismLauncher-11.0.3/cmake/GitFunctions.cmake0000644000175100017510000000211415224505336020457 0ustar runnerrunnerif(__GITFUNCTIONS_CMAKE__) return() endif() set(__GITFUNCTIONS_CMAKE__ TRUE) find_package(Git QUIET) include(CMakeParseArguments) if(GIT_FOUND) function(git_run) set(oneValueArgs OUTPUT_VAR DEFAULT) set(multiValueArgs COMMAND) cmake_parse_arguments(GIT_RUN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) execute_process(COMMAND ${GIT_EXECUTABLE} ${GIT_RUN_COMMAND} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} RESULT_VARIABLE GIT_RESULTVAR OUTPUT_VARIABLE GIT_OUTVAR OUTPUT_STRIP_TRAILING_WHITESPACE ) if(GIT_RESULTVAR EQUAL 0) set(${GIT_RUN_OUTPUT_VAR} "${GIT_OUTVAR}" PARENT_SCOPE) else() set(${GIT_RUN_OUTPUT_VAR} ${GIT_RUN_DEFAULT}) message(STATUS "Failed to run Git: ${GIT_OUTVAR}") endif() endfunction() else() function(git_run) set(oneValueArgs OUTPUT_VAR DEFAULT) set(multiValueArgs COMMAND) cmake_parse_arguments(GIT_RUN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(${GIT_RUN_OUTPUT_VAR} ${GIT_RUN_DEFAULT}) endfunction(git_run) endif() PrismLauncher-11.0.3/cmake/vcpkg-ports/0000755000175100017510000000000015224505336017322 5ustar runnerrunnerPrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/0000755000175100017510000000000015224505336022526 5ustar runnerrunnerPrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg_install_meson.cmake0000644000175100017510000000616715224505336027603 0ustar runnerrunnerfunction(vcpkg_install_meson) cmake_parse_arguments(PARSE_ARGV 0 arg "ADD_BIN_TO_PATH" "" "") vcpkg_find_acquire_program(NINJA) unset(ENV{DESTDIR}) # installation directory was already specified with '--prefix' option if(VCPKG_TARGET_IS_OSX) vcpkg_backup_env_variables(VARS SDKROOT MACOSX_DEPLOYMENT_TARGET) set(ENV{SDKROOT} "${VCPKG_DETECTED_CMAKE_OSX_SYSROOT}") set(ENV{MACOSX_DEPLOYMENT_TARGET} "${VCPKG_DETECTED_CMAKE_OSX_DEPLOYMENT_TARGET}") endif() foreach(buildtype IN ITEMS "debug" "release") if(DEFINED VCPKG_BUILD_TYPE AND NOT VCPKG_BUILD_TYPE STREQUAL buildtype) continue() endif() if(buildtype STREQUAL "debug") set(short_buildtype "dbg") else() set(short_buildtype "rel") endif() message(STATUS "Package ${TARGET_TRIPLET}-${short_buildtype}") if(arg_ADD_BIN_TO_PATH) vcpkg_backup_env_variables(VARS PATH) if(buildtype STREQUAL "debug") vcpkg_add_to_path(PREPEND "${CURRENT_INSTALLED_DIR}/debug/bin") else() vcpkg_add_to_path(PREPEND "${CURRENT_INSTALLED_DIR}/bin") endif() endif() vcpkg_execute_required_process( COMMAND "${NINJA}" install -v WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-${short_buildtype}" LOGNAME package-${TARGET_TRIPLET}-${short_buildtype} ) if(arg_ADD_BIN_TO_PATH) vcpkg_restore_env_variables(VARS PATH) endif() endforeach() vcpkg_list(SET renamed_libs) if(VCPKG_TARGET_IS_WINDOWS AND VCPKG_LIBRARY_LINKAGE STREQUAL static AND NOT VCPKG_TARGET_IS_MINGW) # Meson names all static libraries lib.a which basically breaks the world file(GLOB_RECURSE gen_libraries "${CURRENT_PACKAGES_DIR}*/**/lib*.a") foreach(gen_library IN LISTS gen_libraries) get_filename_component(libdir "${gen_library}" DIRECTORY) get_filename_component(libname "${gen_library}" NAME) string(REGEX REPLACE ".a$" ".lib" fixed_librawname "${libname}") string(REGEX REPLACE "^lib" "" fixed_librawname "${fixed_librawname}") file(RENAME "${gen_library}" "${libdir}/${fixed_librawname}") # For cmake fixes. string(REGEX REPLACE ".a$" "" origin_librawname "${libname}") string(REGEX REPLACE ".lib$" "" fixed_librawname "${fixed_librawname}") vcpkg_list(APPEND renamed_libs ${fixed_librawname}) set(${librawname}_old ${origin_librawname}) set(${librawname}_new ${fixed_librawname}) endforeach() file(GLOB_RECURSE cmake_files "${CURRENT_PACKAGES_DIR}*/*.cmake") foreach(cmake_file IN LISTS cmake_files) foreach(current_lib IN LISTS renamed_libs) vcpkg_replace_string("${cmake_file}" "${${current_lib}_old}" "${${current_lib}_new}" IGNORE_UNCHANGED) endforeach() endforeach() endif() if(VCPKG_TARGET_IS_OSX) vcpkg_restore_env_variables(VARS SDKROOT MACOSX_DEPLOYMENT_TARGET) endif() endfunction() PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg-port-config.cmake0000644000175100017510000000474315224505336027077 0ustar runnerrunnerinclude("${CURRENT_HOST_INSTALLED_DIR}/share/vcpkg-cmake-get-vars/vcpkg-port-config.cmake") # Overwrite builtin scripts include("${CMAKE_CURRENT_LIST_DIR}/vcpkg_configure_meson.cmake") include("${CMAKE_CURRENT_LIST_DIR}/vcpkg_install_meson.cmake") set(meson_short_hash @MESON_SHORT_HASH@) # Setup meson: set(program MESON) set(program_version @VERSION@) set(program_name meson) set(search_names meson meson.py) set(ref "${program_version}") set(path_to_search "${DOWNLOADS}/tools/meson-${program_version}-${meson_short_hash}") set(download_urls "https://github.com/mesonbuild/meson/archive/${ref}.tar.gz") set(download_filename "meson-${ref}.tar.gz") set(download_sha512 bd2e65f0863d9cb974e659ff502d773e937b8a60aaddfd7d81e34cd2c296c8e82bf214d790ac089ba441543059dfc2677ba95ed51f676df9da420859f404a907) find_program(SCRIPT_MESON NAMES ${search_names} PATHS "${path_to_search}" NO_DEFAULT_PATH) # NO_DEFAULT_PATH due top patching if(NOT SCRIPT_MESON) vcpkg_download_distfile(archive_path URLS ${download_urls} SHA512 "${download_sha512}" FILENAME "${download_filename}" ) file(REMOVE_RECURSE "${path_to_search}") file(REMOVE_RECURSE "${path_to_search}-tmp") file(MAKE_DIRECTORY "${path_to_search}-tmp") file(ARCHIVE_EXTRACT INPUT "${archive_path}" DESTINATION "${path_to_search}-tmp" #PATTERNS "**/mesonbuild/*" "**/*.py" ) z_vcpkg_apply_patches( SOURCE_PATH "${path_to_search}-tmp/meson-${ref}" PATCHES @PATCHES@ ) file(MAKE_DIRECTORY "${path_to_search}") file(RENAME "${path_to_search}-tmp/meson-${ref}/meson.py" "${path_to_search}/meson.py") file(RENAME "${path_to_search}-tmp/meson-${ref}/mesonbuild" "${path_to_search}/mesonbuild") file(REMOVE_RECURSE "${path_to_search}-tmp") set(SCRIPT_MESON "${path_to_search}/meson.py") endif() # Check required python version vcpkg_find_acquire_program(PYTHON3) vcpkg_execute_in_download_mode( COMMAND "${PYTHON3}" --version OUTPUT_VARIABLE version_contents WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}" ) string(REGEX MATCH [[[0-9]+\.[0-9]+\.[0-9]+]] python_ver "${version_contents}") set(min_required 3.7) if(python_ver VERSION_LESS "${min_required}") message(FATAL_ERROR "Found Python version '${python_ver} at ${PYTHON3}' is insufficient for meson. meson requires at least version '${min_required}'") else() message(STATUS "Found Python version '${python_ver} at ${PYTHON3}'") endif() message(STATUS "Using meson: ${SCRIPT_MESON}") PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg_configure_meson.cmake0000644000175100017510000005202415224505336030107 0ustar runnerrunnerfunction(z_vcpkg_meson_set_proglist_variables config_type) if(VCPKG_TARGET_IS_WINDOWS) set(proglist MT AR) else() set(proglist AR RANLIB STRIP NM OBJDUMP DLLTOOL MT) endif() foreach(prog IN LISTS proglist) if(VCPKG_DETECTED_CMAKE_${prog}) if(meson_${prog}) string(TOUPPER "MESON_${meson_${prog}}" var_to_set) set("${var_to_set}" "${meson_${prog}} = ['${VCPKG_DETECTED_CMAKE_${prog}}']" PARENT_SCOPE) elseif(${prog} STREQUAL AR AND VCPKG_COMBINED_STATIC_LINKER_FLAGS_${config_type}) # Probably need to move AR somewhere else string(TOLOWER "${prog}" proglower) z_vcpkg_meson_convert_compiler_flags_to_list(ar_flags "${VCPKG_COMBINED_STATIC_LINKER_FLAGS_${config_type}}") list(PREPEND ar_flags "${VCPKG_DETECTED_CMAKE_${prog}}") z_vcpkg_meson_convert_list_to_python_array(ar_flags ${ar_flags}) set("MESON_AR" "${proglower} = ${ar_flags}" PARENT_SCOPE) else() string(TOUPPER "MESON_${prog}" var_to_set) string(TOLOWER "${prog}" proglower) set("${var_to_set}" "${proglower} = ['${VCPKG_DETECTED_CMAKE_${prog}}']" PARENT_SCOPE) endif() endif() endforeach() set(compilers "${arg_LANGUAGES}") if(VCPKG_TARGET_IS_WINDOWS) list(APPEND compilers RC) endif() set(meson_RC windres) set(meson_Fortran fortran) set(meson_CXX cpp) foreach(prog IN LISTS compilers) if(VCPKG_DETECTED_CMAKE_${prog}_COMPILER) string(TOUPPER "MESON_${prog}" var_to_set) if(meson_${prog}) if(VCPKG_COMBINED_${prog}_FLAGS_${config_type}) # Need compiler flags in prog vars for sanity check. z_vcpkg_meson_convert_compiler_flags_to_list(${prog}flags "${VCPKG_COMBINED_${prog}_FLAGS_${config_type}}") endif() list(PREPEND ${prog}flags "${VCPKG_DETECTED_CMAKE_${prog}_COMPILER}") list(FILTER ${prog}flags EXCLUDE REGEX "(-|/)nologo") # Breaks compiler detection otherwise z_vcpkg_meson_convert_list_to_python_array(${prog}flags ${${prog}flags}) set("${var_to_set}" "${meson_${prog}} = ${${prog}flags}" PARENT_SCOPE) if (DEFINED VCPKG_DETECTED_CMAKE_${prog}_COMPILER_ID AND NOT VCPKG_DETECTED_CMAKE_${prog}_COMPILER_ID MATCHES "^(GNU|Intel)$" AND VCPKG_DETECTED_CMAKE_LINKER) string(TOUPPER "MESON_${prog}_LD" var_to_set) set(${var_to_set} "${meson_${prog}}_ld = ['${VCPKG_DETECTED_CMAKE_LINKER}']" PARENT_SCOPE) endif() else() if(VCPKG_COMBINED_${prog}_FLAGS_${config_type}) # Need compiler flags in prog vars for sanity check. z_vcpkg_meson_convert_compiler_flags_to_list(${prog}flags "${VCPKG_COMBINED_${prog}_FLAGS_${config_type}}") endif() list(PREPEND ${prog}flags "${VCPKG_DETECTED_CMAKE_${prog}_COMPILER}") list(FILTER ${prog}flags EXCLUDE REGEX "(-|/)nologo") # Breaks compiler detection otherwise z_vcpkg_meson_convert_list_to_python_array(${prog}flags ${${prog}flags}) string(TOLOWER "${prog}" proglower) set("${var_to_set}" "${proglower} = ${${prog}flags}" PARENT_SCOPE) if (DEFINED VCPKG_DETECTED_CMAKE_${prog}_COMPILER_ID AND NOT VCPKG_DETECTED_CMAKE_${prog}_COMPILER_ID MATCHES "^(GNU|Intel)$" AND VCPKG_DETECTED_CMAKE_LINKER) string(TOUPPER "MESON_${prog}_LD" var_to_set) set(${var_to_set} "${proglower}_ld = ['${VCPKG_DETECTED_CMAKE_LINKER}']" PARENT_SCOPE) endif() endif() endif() endforeach() endfunction() function(z_vcpkg_meson_convert_compiler_flags_to_list out_var compiler_flags) separate_arguments(cmake_list NATIVE_COMMAND "${compiler_flags}") list(TRANSFORM cmake_list REPLACE ";" [[\\;]]) set("${out_var}" "${cmake_list}" PARENT_SCOPE) endfunction() function(z_vcpkg_meson_convert_list_to_python_array out_var) z_vcpkg_function_arguments(flag_list 1) vcpkg_list(REMOVE_ITEM flag_list "") # remove empty elements if any vcpkg_list(JOIN flag_list "', '" flag_list) set("${out_var}" "['${flag_list}']" PARENT_SCOPE) endfunction() # Generates the required compiler properties for meson function(z_vcpkg_meson_set_flags_variables config_type) if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW) set(libpath_flag /LIBPATH:) else() set(libpath_flag -L) endif() if(config_type STREQUAL "DEBUG") set(path_suffix "/debug") else() set(path_suffix "") endif() set(includepath "-I${CURRENT_INSTALLED_DIR}/include") set(libpath "${libpath_flag}${CURRENT_INSTALLED_DIR}${path_suffix}/lib") foreach(lang IN LISTS arg_LANGUAGES) z_vcpkg_meson_convert_compiler_flags_to_list(${lang}flags "${VCPKG_COMBINED_${lang}_FLAGS_${config_type}}") if(lang MATCHES "^(C|CXX)$") vcpkg_list(APPEND ${lang}flags "${includepath}") endif() z_vcpkg_meson_convert_list_to_python_array(${lang}flags ${${lang}flags}) set(lang_mapping "${lang}") if(lang STREQUAL "Fortran") set(lang_mapping "FC") endif() string(TOLOWER "${lang_mapping}" langlower) if(lang STREQUAL "CXX") set(langlower cpp) endif() set(MESON_${lang_mapping}FLAGS "${langlower}_args = ${${lang}flags}\n") set(linker_flags "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_${config_type}}") z_vcpkg_meson_convert_compiler_flags_to_list(linker_flags "${linker_flags}") vcpkg_list(APPEND linker_flags "${libpath}") z_vcpkg_meson_convert_list_to_python_array(linker_flags ${linker_flags}) string(APPEND MESON_${lang_mapping}FLAGS "${langlower}_link_args = ${linker_flags}\n") set(MESON_${lang_mapping}FLAGS "${MESON_${lang_mapping}FLAGS}" PARENT_SCOPE) endforeach() endfunction() function(z_vcpkg_get_build_and_host_system build_system host_system is_cross) #https://mesonbuild.com/Cross-compilation.html set(build_unknown FALSE) if(CMAKE_HOST_WIN32) if(DEFINED ENV{PROCESSOR_ARCHITEW6432}) set(build_arch $ENV{PROCESSOR_ARCHITEW6432}) else() set(build_arch $ENV{PROCESSOR_ARCHITECTURE}) endif() if(build_arch MATCHES "(amd|AMD)64") set(build_cpu_fam x86_64) set(build_cpu x86_64) elseif(build_arch MATCHES "(x|X)86") set(build_cpu_fam x86) set(build_cpu i686) elseif(build_arch MATCHES "^(ARM|arm)64$") set(build_cpu_fam aarch64) set(build_cpu armv8) elseif(build_arch MATCHES "^(ARM|arm)$") set(build_cpu_fam arm) set(build_cpu armv7hl) else() if(NOT DEFINED VCPKG_MESON_CROSS_FILE OR NOT DEFINED VCPKG_MESON_NATIVE_FILE) message(WARNING "Unsupported build architecture ${build_arch}! Please set VCPKG_MESON_(CROSS|NATIVE)_FILE to a meson file containing the build_machine entry!") endif() set(build_unknown TRUE) endif() elseif(CMAKE_HOST_UNIX) # at this stage, CMAKE_HOST_SYSTEM_PROCESSOR is not defined execute_process( COMMAND uname -m OUTPUT_VARIABLE MACHINE OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY) # Show real machine architecture to visually understand whether we are in a native Apple Silicon terminal or running under Rosetta emulation debug_message("Machine: ${MACHINE}") if(MACHINE MATCHES "arm64|aarch64") set(build_cpu_fam aarch64) set(build_cpu armv8) elseif(MACHINE MATCHES "armv7h?l") set(build_cpu_fam arm) set(build_cpu ${MACHINE}) elseif(MACHINE MATCHES "x86_64|amd64") set(build_cpu_fam x86_64) set(build_cpu x86_64) elseif(MACHINE MATCHES "x86|i686") set(build_cpu_fam x86) set(build_cpu i686) elseif(MACHINE MATCHES "i386") set(build_cpu_fam x86) set(build_cpu i386) elseif(MACHINE MATCHES "loongarch64") set(build_cpu_fam loongarch64) set(build_cpu loongarch64) else() # https://github.com/mesonbuild/meson/blob/master/docs/markdown/Reference-tables.md#cpu-families if(NOT DEFINED VCPKG_MESON_CROSS_FILE OR NOT DEFINED VCPKG_MESON_NATIVE_FILE) message(WARNING "Unhandled machine: ${MACHINE}! Please set VCPKG_MESON_(CROSS|NATIVE)_FILE to a meson file containing the build_machine entry!") endif() set(build_unknown TRUE) endif() else() if(NOT DEFINED VCPKG_MESON_CROSS_FILE OR NOT DEFINED VCPKG_MESON_NATIVE_FILE) message(WARNING "Failed to detect the build architecture! Please set VCPKG_MESON_(CROSS|NATIVE)_FILE to a meson file containing the build_machine entry!") endif() set(build_unknown TRUE) endif() set(build "[build_machine]\n") # Machine the build is performed on string(APPEND build "endian = 'little'\n") if(CMAKE_HOST_WIN32) string(APPEND build "system = 'windows'\n") elseif(CMAKE_HOST_APPLE) string(APPEND build "system = 'darwin'\n") elseif(CYGWIN) string(APPEND build "system = 'cygwin'\n") elseif(CMAKE_HOST_UNIX) string(APPEND build "system = 'linux'\n") else() set(build_unknown TRUE) endif() if(DEFINED build_cpu_fam) string(APPEND build "cpu_family = '${build_cpu_fam}'\n") endif() if(DEFINED build_cpu) string(APPEND build "cpu = '${build_cpu}'") endif() if(NOT build_unknown) set(${build_system} "${build}" PARENT_SCOPE) endif() set(host_unkown FALSE) if(VCPKG_TARGET_ARCHITECTURE MATCHES "(amd|AMD|x|X)64") set(host_cpu_fam x86_64) set(host_cpu x86_64) elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "(x|X)86") set(host_cpu_fam x86) set(host_cpu i686) elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "^(ARM|arm)64$") set(host_cpu_fam aarch64) set(host_cpu armv8) elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "^(ARM|arm)$") set(host_cpu_fam arm) set(host_cpu armv7hl) elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "loongarch64") set(host_cpu_fam loongarch64) set(host_cpu loongarch64) elseif(VCPKG_TARGET_ARCHITECTURE MATCHES "wasm32") set(host_cpu_fam wasm32) set(host_cpu wasm32) else() if(NOT DEFINED VCPKG_MESON_CROSS_FILE OR NOT DEFINED VCPKG_MESON_NATIVE_FILE) message(WARNING "Unsupported target architecture ${VCPKG_TARGET_ARCHITECTURE}! Please set VCPKG_MESON_(CROSS|NATIVE)_FILE to a meson file containing the host_machine entry!" ) endif() set(host_unkown TRUE) endif() set(host "[host_machine]\n") # host=target in vcpkg. string(APPEND host "endian = 'little'\n") if(NOT VCPKG_CMAKE_SYSTEM_NAME OR VCPKG_TARGET_IS_MINGW OR VCPKG_TARGET_IS_UWP) set(meson_system_name "windows") else() string(TOLOWER "${VCPKG_CMAKE_SYSTEM_NAME}" meson_system_name) endif() string(APPEND host "system = '${meson_system_name}'\n") string(APPEND host "cpu_family = '${host_cpu_fam}'\n") string(APPEND host "cpu = '${host_cpu}'") if(NOT host_unkown) set(${host_system} "${host}" PARENT_SCOPE) endif() if(NOT build_cpu_fam MATCHES "${host_cpu_fam}" OR VCPKG_TARGET_IS_ANDROID OR VCPKG_TARGET_IS_IOS OR VCPKG_TARGET_IS_UWP OR (VCPKG_TARGET_IS_MINGW AND NOT CMAKE_HOST_WIN32)) set(${is_cross} TRUE PARENT_SCOPE) endif() endfunction() function(z_vcpkg_meson_setup_extra_windows_variables config_type) ## b_vscrt if(VCPKG_CRT_LINKAGE STREQUAL "static") set(crt_type "mt") else() set(crt_type "md") endif() if(config_type STREQUAL "DEBUG") set(crt_type "${crt_type}d") endif() set(MESON_VSCRT_LINKAGE "b_vscrt = '${crt_type}'" PARENT_SCOPE) ## winlibs separate_arguments(c_winlibs NATIVE_COMMAND "${VCPKG_DETECTED_CMAKE_C_STANDARD_LIBRARIES}") separate_arguments(cpp_winlibs NATIVE_COMMAND "${VCPKG_DETECTED_CMAKE_CXX_STANDARD_LIBRARIES}") z_vcpkg_meson_convert_list_to_python_array(c_winlibs ${c_winlibs}) z_vcpkg_meson_convert_list_to_python_array(cpp_winlibs ${cpp_winlibs}) set(MESON_WINLIBS "c_winlibs = ${c_winlibs}\n") string(APPEND MESON_WINLIBS "cpp_winlibs = ${cpp_winlibs}") set(MESON_WINLIBS "${MESON_WINLIBS}" PARENT_SCOPE) endfunction() function(z_vcpkg_meson_setup_variables config_type) set(meson_var_list VSCRT_LINKAGE WINLIBS MT AR RC C C_LD CXX CXX_LD OBJC OBJC_LD OBJCXX OBJCXX_LD FC FC_LD WINDRES CFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS FCFLAGS SHARED_LINKER_FLAGS) foreach(var IN LISTS meson_var_list) set(MESON_${var} "") endforeach() if(VCPKG_TARGET_IS_WINDOWS) z_vcpkg_meson_setup_extra_windows_variables("${config_type}") endif() z_vcpkg_meson_set_proglist_variables("${config_type}") z_vcpkg_meson_set_flags_variables("${config_type}") foreach(var IN LISTS meson_var_list) set(MESON_${var} "${MESON_${var}}" PARENT_SCOPE) endforeach() endfunction() function(vcpkg_generate_meson_cmd_args) cmake_parse_arguments(PARSE_ARGV 0 arg "" "OUTPUT;CONFIG" "OPTIONS;LANGUAGES;ADDITIONAL_BINARIES;ADDITIONAL_PROPERTIES" ) if(NOT arg_LANGUAGES) set(arg_LANGUAGES C CXX) endif() vcpkg_list(JOIN arg_ADDITIONAL_BINARIES "\n" MESON_ADDITIONAL_BINARIES) vcpkg_list(JOIN arg_ADDITIONAL_PROPERTIES "\n" MESON_ADDITIONAL_PROPERTIES) set(buildtype "${arg_CONFIG}") if(NOT VCPKG_CHAINLOAD_TOOLCHAIN_FILE) z_vcpkg_select_default_vcpkg_chainload_toolchain() endif() vcpkg_list(APPEND VCPKG_CMAKE_CONFIGURE_OPTIONS "-DVCPKG_LANGUAGES=${arg_LANGUAGES}") vcpkg_cmake_get_vars(cmake_vars_file) debug_message("Including cmake vars from: ${cmake_vars_file}") include("${cmake_vars_file}") vcpkg_list(APPEND arg_OPTIONS --backend ninja --wrap-mode nodownload -Doptimization=plain) z_vcpkg_get_build_and_host_system(MESON_HOST_MACHINE MESON_BUILD_MACHINE IS_CROSS) if(arg_CONFIG STREQUAL "DEBUG") set(suffix "dbg") else() string(SUBSTRING "${arg_CONFIG}" 0 3 suffix) string(TOLOWER "${suffix}" suffix) endif() set(meson_input_file_${buildtype} "${CURRENT_BUILDTREES_DIR}/meson-${TARGET_TRIPLET}-${suffix}.log") if(IS_CROSS) # VCPKG_CROSSCOMPILING is not used since it regresses a lot of ports in x64-windows-x triplets # For consistency this should proably be changed in the future? vcpkg_list(APPEND arg_OPTIONS --native "${SCRIPTS}/buildsystems/meson/none.txt") vcpkg_list(APPEND arg_OPTIONS --cross "${meson_input_file_${buildtype}}") else() vcpkg_list(APPEND arg_OPTIONS --native "${meson_input_file_${buildtype}}") endif() # User provided cross/native files if(VCPKG_MESON_NATIVE_FILE) vcpkg_list(APPEND arg_OPTIONS --native "${VCPKG_MESON_NATIVE_FILE}") endif() if(VCPKG_MESON_NATIVE_FILE_${buildtype}) vcpkg_list(APPEND arg_OPTIONS --native "${VCPKG_MESON_NATIVE_FILE_${buildtype}}") endif() if(VCPKG_MESON_CROSS_FILE) vcpkg_list(APPEND arg_OPTIONS --cross "${VCPKG_MESON_CROSS_FILE}") endif() if(VCPKG_MESON_CROSS_FILE_${buildtype}) vcpkg_list(APPEND arg_OPTIONS --cross "${VCPKG_MESON_CROSS_FILE_${buildtype}}") endif() vcpkg_list(APPEND arg_OPTIONS --libdir lib) # else meson install into an architecture describing folder vcpkg_list(APPEND arg_OPTIONS --pkgconfig.relocatable) if(arg_CONFIG STREQUAL "RELEASE") vcpkg_list(APPEND arg_OPTIONS -Ddebug=false --prefix "${CURRENT_PACKAGES_DIR}") vcpkg_list(APPEND arg_OPTIONS "--pkg-config-path;['${CURRENT_INSTALLED_DIR}/lib/pkgconfig','${CURRENT_INSTALLED_DIR}/share/pkgconfig']") if(VCPKG_TARGET_IS_WINDOWS) vcpkg_list(APPEND arg_OPTIONS "-Dcmake_prefix_path=['${CURRENT_INSTALLED_DIR}','${CURRENT_INSTALLED_DIR}/debug','${CURRENT_INSTALLED_DIR}/share']") else() vcpkg_list(APPEND arg_OPTIONS "-Dcmake_prefix_path=['${CURRENT_INSTALLED_DIR}','${CURRENT_INSTALLED_DIR}/debug']") endif() elseif(arg_CONFIG STREQUAL "DEBUG") vcpkg_list(APPEND arg_OPTIONS -Ddebug=true --prefix "${CURRENT_PACKAGES_DIR}/debug" --includedir ../include) vcpkg_list(APPEND arg_OPTIONS "--pkg-config-path;['${CURRENT_INSTALLED_DIR}/debug/lib/pkgconfig','${CURRENT_INSTALLED_DIR}/share/pkgconfig']") if(VCPKG_TARGET_IS_WINDOWS) vcpkg_list(APPEND arg_OPTIONS "-Dcmake_prefix_path=['${CURRENT_INSTALLED_DIR}/debug','${CURRENT_INSTALLED_DIR}','${CURRENT_INSTALLED_DIR}/share']") else() vcpkg_list(APPEND arg_OPTIONS "-Dcmake_prefix_path=['${CURRENT_INSTALLED_DIR}/debug','${CURRENT_INSTALLED_DIR}']") endif() else() message(FATAL_ERROR "Unknown configuration. Only DEBUG and RELEASE are valid values.") endif() # Allow overrides / additional configuration variables from triplets if(DEFINED VCPKG_MESON_CONFIGURE_OPTIONS) vcpkg_list(APPEND arg_OPTIONS ${VCPKG_MESON_CONFIGURE_OPTIONS}) endif() if(DEFINED VCPKG_MESON_CONFIGURE_OPTIONS_${buildtype}) vcpkg_list(APPEND arg_OPTIONS ${VCPKG_MESON_CONFIGURE_OPTIONS_${buildtype}}) endif() if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic") set(MESON_DEFAULT_LIBRARY shared) else() set(MESON_DEFAULT_LIBRARY static) endif() set(MESON_CMAKE_BUILD_TYPE "${cmake_build_type_${buildtype}}") z_vcpkg_meson_setup_variables(${buildtype}) configure_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/meson.template.in" "${meson_input_file_${buildtype}}" @ONLY) set("${arg_OUTPUT}" ${arg_OPTIONS} PARENT_SCOPE) endfunction() function(vcpkg_configure_meson) # parse parameters such that semicolons in options arguments to COMMAND don't get erased cmake_parse_arguments(PARSE_ARGV 0 arg "NO_PKG_CONFIG" "SOURCE_PATH" "OPTIONS;OPTIONS_DEBUG;OPTIONS_RELEASE;LANGUAGES;ADDITIONAL_BINARIES;ADDITIONAL_NATIVE_BINARIES;ADDITIONAL_CROSS_BINARIES;ADDITIONAL_PROPERTIES" ) if(DEFINED arg_ADDITIONAL_NATIVE_BINARIES OR DEFINED arg_ADDITIONAL_CROSS_BINARIES) message(WARNING "Options ADDITIONAL_(NATIVE|CROSS)_BINARIES have been deprecated. Only use ADDITIONAL_BINARIES!") endif() vcpkg_list(APPEND arg_ADDITIONAL_BINARIES ${arg_ADDITIONAL_NATIVE_BINARIES} ${arg_ADDITIONAL_CROSS_BINARIES}) vcpkg_list(REMOVE_DUPLICATES arg_ADDITIONAL_BINARIES) file(REMOVE_RECURSE "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel") file(REMOVE_RECURSE "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg") vcpkg_find_acquire_program(MESON) get_filename_component(CMAKE_PATH "${CMAKE_COMMAND}" DIRECTORY) vcpkg_add_to_path("${CMAKE_PATH}") # Make CMake invokeable for Meson vcpkg_find_acquire_program(NINJA) if(NOT arg_NO_PKG_CONFIG) vcpkg_find_acquire_program(PKGCONFIG) set(ENV{PKG_CONFIG} "${PKGCONFIG}") endif() vcpkg_find_acquire_program(PYTHON3) get_filename_component(PYTHON3_DIR "${PYTHON3}" DIRECTORY) vcpkg_add_to_path(PREPEND "${PYTHON3_DIR}") set(buildtypes "") if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") set(buildname "DEBUG") set(cmake_build_type_${buildname} "Debug") vcpkg_list(APPEND buildtypes "${buildname}") set(path_suffix_${buildname} "debug/") set(suffix_${buildname} "dbg") endif() if(NOT DEFINED VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") set(buildname "RELEASE") set(cmake_build_type_${buildname} "Release") vcpkg_list(APPEND buildtypes "${buildname}") set(path_suffix_${buildname} "") set(suffix_${buildname} "rel") endif() # configure build foreach(buildtype IN LISTS buildtypes) message(STATUS "Configuring ${TARGET_TRIPLET}-${suffix_${buildtype}}") file(MAKE_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-${suffix_${buildtype}}") vcpkg_generate_meson_cmd_args( OUTPUT cmd_args CONFIG ${buildtype} LANGUAGES ${arg_LANGUAGES} OPTIONS ${arg_OPTIONS} ${arg_OPTIONS_${buildtype}} ADDITIONAL_BINARIES ${arg_ADDITIONAL_BINARIES} ADDITIONAL_PROPERTIES ${arg_ADDITIONAL_PROPERTIES} ) vcpkg_execute_required_process( COMMAND ${MESON} setup ${cmd_args} ${arg_SOURCE_PATH} WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-${suffix_${buildtype}}" LOGNAME config-${TARGET_TRIPLET}-${suffix_${buildtype}} SAVE_LOG_FILES meson-logs/meson-log.txt meson-info/intro-dependencies.json meson-logs/install-log.txt ) message(STATUS "Configuring ${TARGET_TRIPLET}-${suffix_${buildtype}} done") endforeach() endfunction() PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/universal-osx.patch0000644000175100017510000000155215224505336026371 0ustar runnerrunnerdiff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index f57957f0b..a72e72a0b 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -1472,6 +1472,11 @@ def _get_clang_compiler_defines(compiler: T.List[str], lang: str) -> T.Dict[str, """ from .mixins.clang import clang_lang_map + # Filter out `-arch` flags passed to the compiler for Universal Binaries + # https://github.com/mesonbuild/meson/issues/5290 + # https://github.com/mesonbuild/meson/issues/8206 + compiler = [arg for i, arg in enumerate(compiler) if not (i > 0 and compiler[i - 1] == "-arch") and not arg == "-arch"] + def _try_obtain_compiler_defines(args: T.List[str]) -> str: mlog.debug(f'Running command: {join_args(args)}') p, output, error = Popen_safe(compiler + args, write='', stdin=subprocess.PIPE) PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/remove-freebsd-pcfile-specialization.patch0000644000175100017510000000312615224505336032732 0ustar runnerrunnerdiff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index cc0450a52..13501466d 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -701,16 +701,8 @@ class PkgConfigModule(NewExtensionModule): pcfile = filebase + '.pc' pkgroot = pkgroot_name = kwargs['install_dir'] or default_install_dir if pkgroot is None: - m = state.environment.machines.host - if m.is_freebsd(): - pkgroot = os.path.join(_as_str(state.environment.coredata.optstore.get_value_for(OptionKey('prefix'))), 'libdata', 'pkgconfig') - pkgroot_name = os.path.join('{prefix}', 'libdata', 'pkgconfig') - elif m.is_haiku(): - pkgroot = os.path.join(_as_str(state.environment.coredata.optstore.get_value_for(OptionKey('prefix'))), 'develop', 'lib', 'pkgconfig') - pkgroot_name = os.path.join('{prefix}', 'develop', 'lib', 'pkgconfig') - else: - pkgroot = os.path.join(_as_str(state.environment.coredata.optstore.get_value_for(OptionKey('libdir'))), 'pkgconfig') - pkgroot_name = os.path.join('{libdir}', 'pkgconfig') + pkgroot = os.path.join(_as_str(state.environment.coredata.optstore.get_value_for(OptionKey('libdir'))), 'pkgconfig') + pkgroot_name = os.path.join('{libdir}', 'pkgconfig') relocatable = state.get_option('pkgconfig.relocatable') self._generate_pkgconfig_file(state, deps, subdirs, name, description, url, version, pcfile, conflicts, variables, PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/portfile.cmake0000644000175100017510000000325415224505336025360 0ustar runnerrunner# This port represents a dependency on the Meson build system. # In the future, it is expected that this port acquires and installs Meson. # Currently is used in ports that call vcpkg_find_acquire_program(MESON) in order to force rebuilds. set(VCPKG_POLICY_CMAKE_HELPER_PORT enabled) set(patches meson-intl.patch adjust-python-dep.patch adjust-args.patch remove-freebsd-pcfile-specialization.patch fix-libcpp-enable-assertions.patch # https://github.com/mesonbuild/meson/pull/14548, Remove in 1.8.3 universal-osx.patch # NOTE(@getchoo): THIS IS THE ONLY CHANGE NEEDED FOR PRISM ) set(scripts vcpkg-port-config.cmake vcpkg_configure_meson.cmake vcpkg_install_meson.cmake meson.template.in ) set(to_hash "${CMAKE_CURRENT_LIST_DIR}/vcpkg.json" "${CMAKE_CURRENT_LIST_DIR}/portfile.cmake" ) foreach(file IN LISTS patches scripts) set(filepath "${CMAKE_CURRENT_LIST_DIR}/${file}") list(APPEND to_hash "${filepath}") file(COPY "${filepath}" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") endforeach() set(meson_path_hash "") foreach(filepath IN LISTS to_hash) file(SHA1 "${filepath}" to_append) string(APPEND meson_path_hash "${to_append}") endforeach() string(SHA512 meson_path_hash "${meson_path_hash}") string(SUBSTRING "${meson_path_hash}" 0 6 MESON_SHORT_HASH) list(TRANSFORM patches REPLACE [[^(..*)$]] [["${CMAKE_CURRENT_LIST_DIR}/\0"]]) list(JOIN patches "\n " PATCHES) configure_file("${CMAKE_CURRENT_LIST_DIR}/vcpkg-port-config.cmake" "${CURRENT_PACKAGES_DIR}/share/${PORT}/vcpkg-port-config.cmake" @ONLY) vcpkg_install_copyright(FILE_LIST "${VCPKG_ROOT_DIR}/LICENSE.txt") include("${CURRENT_PACKAGES_DIR}/share/${PORT}/vcpkg-port-config.cmake") PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/adjust-python-dep.patch0000644000175100017510000000553615224505336027137 0ustar runnerrunnerdiff --git a/mesonbuild/dependencies/python.py b/mesonbuild/dependencies/python.py index 883a29a..d9a82af 100644 --- a/mesonbuild/dependencies/python.py +++ b/mesonbuild/dependencies/python.py @@ -232,8 +232,10 @@ class _PythonDependencyBase(_Base): else: if self.is_freethreaded: libpath = Path('libs') / f'python{vernum}t.lib' + libpath = Path('libs') / f'..' / f'..' / f'..' / f'lib' / f'python{vernum}t.lib' else: libpath = Path('libs') / f'python{vernum}.lib' + libpath = Path('libs') / f'..' / f'..' / f'..' / f'lib' / f'python{vernum}.lib' # For a debug build, pyconfig.h may force linking with # pythonX_d.lib (see meson#10776). This cannot be avoided # and won't work unless we also have a debug build of @@ -250,6 +252,8 @@ class _PythonDependencyBase(_Base): vscrt = self.env.coredata.optstore.get_value('b_vscrt') if vscrt in {'mdd', 'mtd', 'from_buildtype', 'static_from_buildtype'}: vscrt_debug = True + if is_debug_build: + libpath = Path('libs') / f'..' / f'..' / f'..' / f'debug/lib' / f'python{vernum}_d.lib' if is_debug_build and vscrt_debug and not self.variables.get('Py_DEBUG'): mlog.warning(textwrap.dedent('''\ Using a debug build type with MSVC or an MSVC-compatible compiler @@ -350,9 +354,10 @@ class PythonSystemDependency(SystemDependency, _PythonDependencyBase): self.is_found = True # compile args + verdot = self.variables.get('py_version_short') inc_paths = mesonlib.OrderedSet([ self.variables.get('INCLUDEPY'), - self.paths.get('include'), + self.paths.get('include') + f'/../../../include/python${verdot}', self.paths.get('platinclude')]) self.compile_args += ['-I' + path for path in inc_paths if path] @@ -416,7 +421,7 @@ def python_factory(env: 'Environment', for_machine: 'MachineChoice', candidates.append(functools.partial(wrap_in_pythons_pc_dir, pkg_name, env, kwargs, installation)) # We only need to check both, if a python install has a LIBPC. It might point to the wrong location, # e.g. relocated / cross compilation, but the presence of LIBPC indicates we should definitely look for something. - if pkg_libdir is not None: + if True or pkg_libdir is not None: candidates.append(functools.partial(PythonPkgConfigDependency, pkg_name, env, kwargs, installation)) else: candidates.append(functools.partial(PkgConfigDependency, 'python3', env, kwargs)) PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/vcpkg.json0000644000175100017510000000037215224505336024535 0ustar runnerrunner{ "name": "vcpkg-tool-meson", "version": "1.8.2", "description": "Meson build system", "homepage": "https://github.com/mesonbuild/meson", "license": "Apache-2.0", "supports": "native", "dependencies": [ "vcpkg-cmake-get-vars" ] } PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/meson.template.in0000644000175100017510000000172115224505336026012 0ustar runnerrunner[binaries] cmake = ['@CMAKE_COMMAND@'] ninja = ['@NINJA@'] pkg-config = ['@PKGCONFIG@'] @MESON_MT@ @MESON_AR@ @MESON_RC@ @MESON_C@ @MESON_C_LD@ @MESON_CXX@ @MESON_CXX_LD@ @MESON_OBJC@ @MESON_OBJC_LD@ @MESON_OBJCPP@ @MESON_OBJCPP_LD@ @MESON_FC@ @MESON_FC_LD@ @MESON_WINDRES@ @MESON_ADDITIONAL_BINARIES@ [properties] cmake_toolchain_file = '@SCRIPTS@/buildsystems/vcpkg.cmake' @MESON_ADDITIONAL_PROPERTIES@ [cmake] CMAKE_BUILD_TYPE = '@MESON_CMAKE_BUILD_TYPE@' VCPKG_TARGET_TRIPLET = '@TARGET_TRIPLET@' VCPKG_HOST_TRIPLET = '@_HOST_TRIPLET@' VCPKG_CHAINLOAD_TOOLCHAIN_FILE = '@VCPKG_CHAINLOAD_TOOLCHAIN_FILE@' VCPKG_CRT_LINKAGE = '@VCPKG_CRT_LINKAGE@' _VCPKG_INSTALLED_DIR = '@_VCPKG_INSTALLED_DIR@' @MESON_HOST_MACHINE@ @MESON_BUILD_MACHINE@ [built-in options] default_library = '@MESON_DEFAULT_LIBRARY@' werror = false @MESON_CFLAGS@ @MESON_CXXFLAGS@ @MESON_FCFLAGS@ @MESON_OBJCFLAGS@ @MESON_OBJCPPFLAGS@ # b_vscrt @MESON_VSCRT_LINKAGE@ # c_winlibs/cpp_winlibs @MESON_WINLIBS@PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/install.cmake0000644000175100017510000000030315224505336025172 0ustar runnerrunnerfile(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/tools/meson") file(INSTALL "${SOURCE_PATH}/meson.py" "${SOURCE_PATH}/mesonbuild" DESTINATION "${CURRENT_PACKAGES_DIR}/tools/meson" ) PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/fix-libcpp-enable-assertions.patch0000644000175100017510000000452615224505336031227 0ustar runnerrunnerFrom a16ec8b0fb6d7035b669a13edd4d97ff0c307a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20D=C3=B8rum?= Date: Fri, 2 May 2025 10:56:28 +0200 Subject: [PATCH] cpp: fix _LIBCPP_ENABLE_ASSERTIONS warning libc++ deprecated _LIBCPP_ENABLE_ASSERTIONS from version 18. However, the libc++ shipped with Apple Clang backported that deprecation in version 17 already, which is the version which Apple currently ships for macOS. This PR changes the _LIBCPP_ENABLE_ASSERTIONS deprecation check to use version ">=17" on Apple Clang. --- mesonbuild/compilers/cpp.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 01b9bb9fa34f..f7dc150e8608 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -311,6 +311,9 @@ def get_option_link_args(self, target: 'BuildTarget', env: 'Environment', subpro return libs return [] + def is_libcpp_enable_assertions_deprecated(self) -> bool: + return version_compare(self.version, ">=18") + def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]: if disable: return ['-DNDEBUG'] @@ -323,7 +326,7 @@ def get_assert_args(self, disable: bool, env: 'Environment') -> T.List[str]: if self.language_stdlib_provider(env) == 'stdc++': return ['-D_GLIBCXX_ASSERTIONS=1'] else: - if version_compare(self.version, '>=18'): + if self.is_libcpp_enable_assertions_deprecated(): return ['-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST'] elif version_compare(self.version, '>=15'): return ['-D_LIBCPP_ENABLE_ASSERTIONS=1'] @@ -343,7 +346,12 @@ class ArmLtdClangCPPCompiler(ClangCPPCompiler): class AppleClangCPPCompiler(AppleCompilerMixin, AppleCPPStdsMixin, ClangCPPCompiler): - pass + def is_libcpp_enable_assertions_deprecated(self) -> bool: + # Upstream libc++ deprecated _LIBCPP_ENABLE_ASSERTIONS + # in favor of _LIBCPP_HARDENING_MODE from version 18 onwards, + # but Apple Clang 17's libc++ has back-ported that change. + # See: https://github.com/mesonbuild/meson/issues/14440 + return version_compare(self.version, ">=17") class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler): PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/README.md0000644000175100017510000000026415224505336024007 0ustar runnerrunnerThe only difference between this and the upstream vcpkg port is the addition of `universal-osx.patch`. It's very annoying we need to bundle this entire tree to do that. -@getchoo PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/meson-intl.patch0000644000175100017510000000102515224505336025632 0ustar runnerrunnerdiff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -593,7 +593,8 @@ iconv_factory = DependencyFactory( packages['intl'] = intl_factory = DependencyFactory( 'intl', + [DependencyMethods.BUILTIN, DependencyMethods.SYSTEM, DependencyMethods.CMAKE], + cmake_name='Intl', - [DependencyMethods.BUILTIN, DependencyMethods.SYSTEM], builtin_class=IntlBuiltinDependency, system_class=IntlSystemDependency, ) PrismLauncher-11.0.3/cmake/vcpkg-ports/vcpkg-tool-meson/adjust-args.patch0000644000175100017510000000117115224505336025773 0ustar runnerrunnerdiff --git a/mesonbuild/cmake/toolchain.py b/mesonbuild/cmake/toolchain.py index 11a00be5d..89ae490ff 100644 --- a/mesonbuild/cmake/toolchain.py +++ b/mesonbuild/cmake/toolchain.py @@ -202,7 +202,7 @@ class CMakeToolchain: @staticmethod def is_cmdline_option(compiler: 'Compiler', arg: str) -> bool: if compiler.get_argument_syntax() == 'msvc': - return arg.startswith('/') + return arg.startswith(('/','-')) else: if os.path.basename(compiler.get_exe()) == 'zig' and arg in {'ar', 'cc', 'c++', 'dlltool', 'lib', 'ranlib', 'objcopy', 'rc'}: return True PrismLauncher-11.0.3/cmake/GetGitRevisionDescription.cmake.in0000644000175100017510000000251215224505336023560 0ustar runnerrunner# # Internal file for GetGitRevisionDescription.cmake # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright 2009-2012, Iowa State University # Copyright 2011-2015, Contributors # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) # SPDX-License-Identifier: BSL-1.0 set(HEAD_HASH) file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) if(HEAD_CONTENTS MATCHES "ref") # named branch string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) else() configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") set(HEAD_HASH "${CMAKE_MATCH_1}") endif() endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) endif() if(NOT HEAD_HASH) file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() PrismLauncher-11.0.3/cmake/vcpkg-triplets/0000755000175100017510000000000015224505336020021 5ustar runnerrunnerPrismLauncher-11.0.3/cmake/vcpkg-triplets/universal-osx.cmake0000644000175100017510000000045615224505336023647 0ustar runnerrunner# See https://github.com/microsoft/vcpkg/discussions/19454 # NOTE: Try to keep in sync with default arm64-osx definition set(VCPKG_TARGET_ARCHITECTURE x64) set(VCPKG_CRT_LINKAGE dynamic) set(VCPKG_LIBRARY_LINKAGE static) set(VCPKG_CMAKE_SYSTEM_NAME Darwin) set(VCPKG_OSX_ARCHITECTURES "arm64;x86_64") PrismLauncher-11.0.3/cmake/MacOSXBundleInfo.plist.in0000644000175100017510000000735215224505336021574 0ustar runnerrunner NSCameraUsageDescription A Minecraft mod wants to access your camera. NSMicrophoneUsageDescription A Minecraft mod wants to access your microphone. NSDownloadsFolderUsageDescription ${Launcher_DisplayName} uses access to your Downloads folder to help you more quickly add mods that can't be automatically downloaded to your instance. You can change where ${Launcher_DisplayName} scans for downloaded mods in Settings or the prompt that appears. NSLocalNetworkUsageDescription Minecraft uses the local network to find and connect to LAN servers. NSPrincipalClass NSApplication NSHighResolutionCapable True CFBundleDevelopmentRegion English CFBundleExecutable ${MACOSX_BUNDLE_EXECUTABLE_NAME} CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile ${Launcher_Name} CFBundleIconName ${Launcher_Name} CFBundleIdentifier ${MACOSX_BUNDLE_GUI_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString ${MACOSX_BUNDLE_LONG_VERSION_STRING} CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundlePackageType APPL CFBundleShortVersionString ${MACOSX_BUNDLE_SHORT_VERSION_STRING} CFBundleSignature ???? CFBundleVersion ${MACOSX_BUNDLE_BUNDLE_VERSION} CSResourcesFileMapped LSRequiresCarbon LSApplicationCategoryType public.app-category.games NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} SUPublicEDKey ${MACOSX_SPARKLE_UPDATE_PUBLIC_KEY} SUFeedURL ${MACOSX_SPARKLE_UPDATE_FEED_URL} CFBundleDocumentTypes CFBundleTypeExtensions zip mrpack CFBundleTypeName ${Launcher_DisplayName} instance CFBundleTypeOSTypes TEXT utxt TUTX **** CFBundleTypeRole Viewer LSHandlerRank Alternate CFBundleURLTypes CFBundleURLName Curseforge CFBundleURLSchemes curseforge CFBundleURLName ${Launcher_Name} CFBundleURLSchemes prismlauncher ${MACOSX_BUNDLE_EXECUTABLE_NAME} PrismLauncher-11.0.3/.gitmodules0000644000175100017510000000020315224505336016133 0ustar runnerrunner[submodule "libraries/libnbtplusplus"] path = libraries/libnbtplusplus url = https://github.com/PrismLauncher/libnbtplusplus.git PrismLauncher-11.0.3/vcpkg.json0000644000175100017510000000066515224505336015777 0ustar runnerrunner{ "dependencies": [ { "name": "ecm", "host": true }, { "name": "libqrencode", "default-features": false }, { "name": "pkgconf", "host": true }, "cmark", { "name": "libarchive", "default-features": false, "features": [ "bzip2", "lz4", "lzma", "lzo", "zstd" ] }, "tomlplusplus", "zlib" ] } PrismLauncher-11.0.3/flake.lock0000644000175100017510000000214315224505336015717 0ustar runnerrunner{ "nodes": { "libnbtplusplus": { "flake": false, "locked": { "lastModified": 1772016279, "narHash": "sha256-7itkptyjoRcXfGLwg1/jxajetZ3a4mDc66+w4X6yW8s=", "owner": "PrismLauncher", "repo": "libnbtplusplus", "rev": "687e43031df0dc641984b4256bcca50d5b3f7de3", "type": "github" }, "original": { "owner": "PrismLauncher", "repo": "libnbtplusplus", "type": "github" } }, "nixpkgs": { "locked": { "lastModified": 1774709303, "narHash": "sha256-D4ely1FsBcvtj/qSrNhSWpq+CUZKNiKwJIxpxnfy9o4=", "rev": "8110df5ad7abf5d4c0f6fb0f8f978390e77f9685", "type": "tarball", "url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre971119.8110df5ad7ab/nixexprs.tar.xz" }, "original": { "type": "tarball", "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" } }, "root": { "inputs": { "libnbtplusplus": "libnbtplusplus", "nixpkgs": "nixpkgs" } } }, "root": "root", "version": 7 } PrismLauncher-11.0.3/shell.nix0000644000175100017510000000034215224505336015611 0ustar runnerrunner(import (fetchTarball { url = "https://github.com/edolstra/flake-compat/archive/ff81ac966bb2cae68946d5ed5fc4994f96d0ffec.tar.gz"; sha256 = "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU="; }) { src = ./.; }).shellNix PrismLauncher-11.0.3/.editorconfig0000644000175100017510000000072615224505336016445 0ustar runnerrunner# EditorConfig specs and documentation: https://EditorConfig.org # top-most EditorConfig file root = true [*] indent_style = space indent_size = 4 end_of_line = lf charset = utf-8 insert_final_newline = true trim_trailing_whitespace = true [*.{yml,nix}] indent_size = 2 # C++ Code Style settings [*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}] cpp_generate_documentation_comments = doxygen_slash_star [CMakeLists.txt] ij_continuation_indent_size = 4 PrismLauncher-11.0.3/LICENSE0000644000175100017510000010451515224505336014776 0ustar runnerrunner GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . PrismLauncher-11.0.3/CONTRIBUTING.md0000644000175100017510000001665415224505336016230 0ustar runnerrunner# Contributions Guidelines ## Restrictions on Generative AI Usage (AI Policy) > [!NOTE] > The following is adapted from [matplotlib's contributing guide](https://matplotlib.org/devdocs/devel/contribute.html#generative-ai) and the [Linux Kernel policy guide](https://www.kernel.org/doc./html/next/process/coding-assistants.html) We expect authentic engagement in our community. - Do not post output from Large Language Models or similar generative AI as comments on GitHub or our discord server, as such comments tend to be formulaic and low-quality content. - If you use generative AI tools as an aid in developing code or documentation changes, ensure that you fully understand the proposed changes and can explain why they are the correct approach. Make sure you have added value based on your personal competency to your contributions. Just taking some input, feeding it to an AI and posting the result is not of value to the project. To preserve precious core developer capacity, we reserve the right to rigorously reject seemingly AI generated low-value contributions. ### Signed-off-by and Developer Certificate of Origin AI agents MUST NOT add Signed-off-by tags. Only humans can legally certify the Developer Certificate of Origin (DCO). The human submitter is responsible for: - Reviewing all AI-generated code - Ensuring compliance with licensing requirements - Adding their own Signed-off-by tag to certify the DCO - Taking full responsibility for the contribution See [Signing your work](#signing-your-work) for more information. ### Attribution When AI tools contribute to development, proper attribution helps track the evolving role of AI in the development process. Contributions should include an Assisted-by tag in the commit message with the following format: ```text Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2] ``` Where: - `AGENT_NAME` is the name of the AI tool or framework - `MODEL_VERSION` is the specific model version used - `[TOOL1] [TOOL2]` are optional specialized analysis tools used (e.g., coccinelle, sparse, smatch, clang-tidy) Basic development tools (git, gcc, make, editors) should not be listed. Example: ```text Assisted-by: Claude:claude-3-opus coccinelle sparse ``` ## Code style All files are formatted with `clang-format` using the configuration in `.clang-format`. Ensure it is run on changed files before committing! Please also follow the project's conventions for C++: - Class and type names should be formatted as `PascalCase`: `MyClass`. - Private or protected class data members should be formatted as `camelCase` prefixed with `m_`: `m_myCounter`. - Private or protected `static` class data members should be formatted as `camelCase` prefixed with `s_`: `s_instance`. - Public class data members should be formatted as `camelCase` without the prefix: `dateOfBirth`. - Public, private or protected `static const` class data members should be formatted as `SCREAMING_SNAKE_CASE`: `MAX_VALUE`. - Class function members should be formatted as `camelCase` without a prefix: `incrementCounter`. - Global functions and non-`const` global variables should be formatted as `camelCase` without a prefix: `globalData`. - `const` global variables and macros should be formatted as `SCREAMING_SNAKE_CASE`: `LIGHT_GRAY`. - enum constants should be formatted as `PascalCase`: `CamelusBactrianus` - Avoid inventing acronyms or abbreviations especially for a name of multiple words - like `tp` for `texturePack`. - Avoid using `[[nodiscard]]` unless ignoring the return value is likely to cause a bug in cases such as: - A function allocates memory or another resource and the caller needs to clean it up. - A function has side effects and an error status is returned. - A function is likely be mistaken for having side effects. - A plain getter is unlikely to cause confusion and adding `[[nodiscard]]` can create clutter and inconsistency. Most of these rules are included in the `.clang-tidy` file, so you can run `clang-tidy` to check for any violations. Here is what these conventions with the formatting configuration look like: ```c++ #define AWESOMENESS 10 constexpr double PI = 3.14159; enum class PizzaToppings { HamAndPineapple, OreoAndKetchup }; struct Person { QString name; QDateTime dateOfBirth; long daysOld() const { return dateOfBirth.daysTo(QDateTime::currentDateTime()); } }; class ImportantClass { public: void incrementCounter() { if (m_counter + 1 > MAX_COUNTER_VALUE) throw std::runtime_error("Counter has reached limit!"); ++m_counter; } int counter() const { return m_counter; } private: static constexpr int MAX_COUNTER_VALUE = 100; int m_counter; }; ImportantClass importantClassInstance; ``` If you see any names which do not follow these conventions, it is preferred that you leave them be - renames increase the number of changes therefore make reviewing harder and make your PR more prone to conflicts. However, if you're refactoring a whole class anyway, it's fine. ## Signing your work In an effort to ensure that the code you contribute is actually compatible with the licenses in this codebase, we require you to sign-off all your contributions. This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message: ```text Signed-off-by: Author name ``` By signing off your work, you agree to the terms below: ```text Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` These terms will be enforced once you create a pull request, and you will be informed automatically if any of your commits aren't signed-off by you. As a bonus, you can also [cryptographically sign your commits][gh-signing-commits] and enable [vigilant mode][gh-vigilant-mode] on GitHub. [gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits [gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits ## Backporting to Release Branches We use [automated backports](https://github.com/PrismLauncher/PrismLauncher/blob/develop/.github/workflows/backport.yml) to merge specific contributions from develop into `release` branches. This is done when pull requests are merged and have labels such as `backport release-7.x` - which should be added along with the milestone for the release. PrismLauncher-11.0.3/.git-blame-ignore-revs0000644000175100017510000000043615224505336020066 0ustar runnerrunner# .git-blame-ignore-revs # tabs -> spaces bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9 # (nix) alejandra -> nixfmt 4c81d8c53d09196426568c4a31a4e752ed05397a # reformat codebase 1d468ac35ad88d8c77cc83f25e3704d9bd7df01b # format a part of codebase 5c8481a118c8fefbfe901001d7828eaf6866eac4 PrismLauncher-11.0.3/launcher/0000755000175100017510000000000015224505336015564 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/InstanceList.cpp0000644000175100017510000010255215224505336020675 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "InstanceList.h" #include #include #include #include #include #include #include #include #include #include #include #include "BaseInstance.h" #include "ExponentialSeries.h" #include "FileSystem.h" #include "InstanceTask.h" #include "NullInstance.h" #include "WatchLock.h" #include "minecraft/MinecraftInstance.h" #include "settings/INISettingsObject.h" #ifdef Q_OS_WIN32 #include #endif const static int GROUP_FILE_FORMAT_VERSION = 1; InstanceList::InstanceList(SettingsObject* settings, const QString& instDir, QObject* parent) : QAbstractListModel(parent), m_globalSettings(settings) { resumeWatch(); // Create aand normalize path if (!QDir::current().exists(instDir)) { QDir::current().mkpath(instDir); } connect(this, &InstanceList::instancesChanged, this, &InstanceList::providerUpdated); // NOTE: canonicalPath requires the path to exist. Do not move this above the creation block! m_instDir = QDir(instDir).canonicalPath(); m_watcher = new QFileSystemWatcher(this); connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &InstanceList::instanceDirContentsChanged); m_watcher->addPath(m_instDir); } InstanceList::~InstanceList() {} Qt::DropActions InstanceList::supportedDragActions() const { return Qt::MoveAction; } Qt::DropActions InstanceList::supportedDropActions() const { return Qt::MoveAction; } bool InstanceList::canDropMimeData(const QMimeData* data, [[maybe_unused]] Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex& parent) const { if (data && data->hasFormat("application/x-instanceid")) { return true; } return false; } bool InstanceList::dropMimeData(const QMimeData* data, [[maybe_unused]] Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex& parent) { if (data && data->hasFormat("application/x-instanceid")) { return true; } return false; } QStringList InstanceList::mimeTypes() const { auto types = QAbstractListModel::mimeTypes(); types.push_back("application/x-instanceid"); return types; } QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const { auto mimeData = QAbstractListModel::mimeData(indexes); if (indexes.size() == 1) { auto instanceId = data(indexes[0], InstanceIDRole).toString(); mimeData->setData("application/x-instanceid", instanceId.toUtf8()); } return mimeData; } QStringList InstanceList::getLinkedInstancesById(const QString& id) const { QStringList linkedInstances; for (auto& inst : m_instances) { if (inst->isLinkedToInstanceId(id)) linkedInstances.append(inst->id()); } return linkedInstances; } int InstanceList::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent); return count(); } QModelIndex InstanceList::index(int row, int column, const QModelIndex& parent) const { Q_UNUSED(parent); if (row < 0 || row >= count()) return QModelIndex(); return createIndex(row, column, m_instances.at(row).get()); } QVariant InstanceList::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } BaseInstance* pdata = static_cast(index.internalPointer()); switch (role) { case InstancePointerRole: { QVariant v = QVariant::fromValue((void*)pdata); return v; } case InstanceIDRole: { return pdata->id(); } case Qt::EditRole: case Qt::DisplayRole: { return pdata->name(); } case Qt::AccessibleTextRole: { return tr("%1 Instance").arg(pdata->name()); } case Qt::ToolTipRole: { return pdata->instanceRoot(); } case Qt::DecorationRole: { return pdata->iconKey(); } // HACK: see InstanceView.h in gui! case GroupRole: { return getInstanceGroup(pdata->id()); } default: break; } return QVariant(); } bool InstanceList::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid()) { return false; } if (role != Qt::EditRole) { return false; } BaseInstance* pdata = static_cast(index.internalPointer()); auto newName = value.toString(); if (pdata->name() == newName) { return true; } pdata->setName(newName); return true; } Qt::ItemFlags InstanceList::flags(const QModelIndex& index) const { Qt::ItemFlags f; if (index.isValid()) { f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); } return f; } GroupId InstanceList::getInstanceGroup(const InstanceId& id) const { auto inst = getInstanceById(id); if (!inst) { return GroupId(); } auto iter = m_instanceGroupIndex.find(inst->id()); if (iter != m_instanceGroupIndex.end()) { return *iter; } return GroupId(); } void InstanceList::setInstanceGroup(const InstanceId& id, GroupId name) { if (name.isEmpty() && !name.isNull()) name = QString(); auto inst = getInstanceById(id); if (!inst) { qDebug() << "Attempt to set a null instance's group"; return; } bool changed = false; auto iter = m_instanceGroupIndex.find(inst->id()); if (iter != m_instanceGroupIndex.end()) { if (*iter != name) { decreaseGroupCount(*iter); *iter = name; changed = true; } } else { changed = true; m_instanceGroupIndex[id] = name; } if (changed) { increaseGroupCount(name); auto idx = getInstIndex(inst); emit dataChanged(index(idx), index(idx), { GroupRole }); saveGroupList(); } } QStringList InstanceList::getGroups() { return m_groupNameCache.keys(); } void InstanceList::deleteGroup(const GroupId& name) { m_groupNameCache.remove(name); m_collapsedGroups.remove(name); bool removed = false; qDebug() << "Delete group" << name; for (auto& instance : m_instances) { const QString& instID = instance->id(); const QString instGroupName = getInstanceGroup(instID); if (instGroupName == name) { m_instanceGroupIndex.remove(instID); qDebug() << "Remove" << instID << "from group" << name; removed = true; auto idx = getInstIndex(instance.get()); if (idx >= 0) emit dataChanged(index(idx), index(idx), { GroupRole }); } } if (removed) saveGroupList(); } void InstanceList::renameGroup(const QString& src, const QString& dst) { m_groupNameCache.remove(src); if (m_collapsedGroups.remove(src)) m_collapsedGroups.insert(dst); bool modified = false; qDebug() << "Rename group" << src << "to" << dst; for (auto& instance : m_instances) { const QString& instID = instance->id(); const QString instGroupName = getInstanceGroup(instID); if (instGroupName == src) { m_instanceGroupIndex[instID] = dst; increaseGroupCount(dst); qDebug() << "Set" << instID << "group to" << dst; modified = true; auto idx = getInstIndex(instance.get()); if (idx >= 0) emit dataChanged(index(idx), index(idx), { GroupRole }); } } if (modified) saveGroupList(); } bool InstanceList::isGroupCollapsed(const QString& group) { return m_collapsedGroups.contains(group); } bool InstanceList::trashInstance(const InstanceId& id) { auto inst = getInstanceById(id); if (!inst) { qWarning() << "Cannot trash instance" << id << ". No such instance is present (deleted externally?)."; return false; } QString cachedGroupId = m_instanceGroupIndex[id]; qDebug() << "Will trash instance" << id; QString trashedLoc; if (m_instanceGroupIndex.remove(id)) { decreaseGroupCount(cachedGroupId); saveGroupList(); } if (!FS::trash(inst->instanceRoot(), &trashedLoc)) { qWarning() << "Trash of instance" << id << "has not been completely successful..."; return false; } qDebug() << "Instance" << id << "has been trashed by the launcher."; m_trashHistory.push({ id, inst->instanceRoot(), trashedLoc, cachedGroupId }); // Also trash all of its shortcuts; we remove the shortcuts if trash fails since it is invalid anyway for (const auto& [name, filePath, target] : inst->shortcuts()) { if (!FS::trash(filePath, &trashedLoc)) { qWarning() << "Trash of shortcut" << name << "at path" << filePath << "for instance" << id << "has not been successful, trying to delete it instead..."; if (!FS::deletePath(filePath)) { qWarning() << "Deletion of shortcut" << name << "at path" << filePath << "for instance" << id << "has not been successful, given up..."; } else { qDebug() << "Shortcut" << name << "at path" << filePath << "for instance" << id << "has been deleted by the launcher."; } continue; } qDebug() << "Shortcut" << name << "at path" << filePath << "for instance" << id << "has been trashed by the launcher."; m_trashHistory.top().shortcuts.append({ { name, filePath, target }, trashedLoc }); } return true; } bool InstanceList::trashedSomething() const { return !m_trashHistory.empty(); } bool InstanceList::undoTrashInstance() { if (m_trashHistory.empty()) { qWarning() << "Nothing to recover from trash."; return true; } auto top = m_trashHistory.pop(); while (QDir(top.path).exists()) { top.id += "1"; top.path += "1"; } if (!QFile(top.trashPath).rename(top.path)) { qWarning() << "Moving" << top.trashPath << "back to" << top.path << "failed!"; return false; } qDebug() << "Moving" << top.trashPath << "back to" << top.path; bool ok = true; for (const auto& [data, trashPath] : top.shortcuts) { if (QDir(data.filePath).exists()) { // Don't try to append 1 here as the shortcut may have suffixes like .app, just warn and skip it qWarning() << "Shortcut" << trashPath << "original directory" << data.filePath << "already exists!"; ok = false; continue; } if (!QFile(trashPath).rename(data.filePath)) { qWarning() << "Moving shortcut from" << trashPath << "back to" << data.filePath << "failed!"; ok = false; continue; } qDebug() << "Moving shortcut from" << trashPath << "back to" << data.filePath; } m_instanceGroupIndex[top.id] = top.groupName; increaseGroupCount(top.groupName); saveGroupList(); emit instancesChanged(); return ok; } void InstanceList::deleteInstance(const InstanceId& id) { auto inst = getInstanceById(id); if (!inst) { qWarning() << "Cannot delete instance" << id << ". No such instance is present (deleted externally?)."; return; } QString cachedGroupId = m_instanceGroupIndex[id]; if (m_instanceGroupIndex.remove(id)) { decreaseGroupCount(cachedGroupId); saveGroupList(); } qDebug() << "Will delete instance" << id; if (!FS::deletePath(inst->instanceRoot())) { qWarning() << "Deletion of instance" << id << "has not been completely successful..."; return; } qDebug() << "Instance" << id << "has been deleted by the launcher."; for (const auto& [name, filePath, target] : inst->shortcuts()) { if (!FS::deletePath(filePath)) { qWarning() << "Deletion of shortcut" << name << "at path" << filePath << "for instance" << id << "has not been successful..."; continue; } qDebug() << "Shortcut" << name << "at path" << filePath << "for instance" << id << "has been deleted by the launcher."; } } static QMap getIdMapping(const std::vector>& list) { QMap out; int i = 0; for (auto& item : list) { auto id = item->id(); if (out.contains(id)) { qWarning() << "Duplicate ID" << id << "in instance list"; } out[id] = std::make_pair(item.get(), i); i++; } return out; } QList InstanceList::discoverInstances() { qInfo() << "Discovering instances in" << m_instDir; QList out; QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks); while (iter.hasNext()) { QString subDir = iter.next(); QFileInfo dirInfo(subDir); if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists()) continue; // if it is a symlink, ignore it if it goes to the instance folder if (dirInfo.isSymLink()) { QFileInfo targetInfo(dirInfo.symLinkTarget()); QFileInfo instDirInfo(m_instDir); if (targetInfo.canonicalPath() == instDirInfo.canonicalFilePath()) { qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder"; continue; } } auto id = dirInfo.fileName(); out.append(id); qInfo() << "Found instance ID" << id; } instanceSet = QSet(out.begin(), out.end()); m_instancesProbed = true; return out; } InstanceList::InstListError InstanceList::loadList() { auto existingIds = getIdMapping(m_instances); std::vector> newList; for (auto& id : discoverInstances()) { if (existingIds.contains(id)) { existingIds.remove(id); qInfo() << "Should keep and soft-reload" << id; } else { std::unique_ptr instPtr = loadInstance(id); if (instPtr) { newList.push_back(std::move(instPtr)); } } } // TODO: looks like a general algorithm with a few specifics inserted. Do something about it. if (!existingIds.isEmpty()) { // get the list of removed instances and sort it by their original index, from last to first auto deadList = existingIds.values(); auto orderSortPredicate = [](const InstanceLocator& a, const InstanceLocator& b) -> bool { return a.second > b.second; }; std::sort(deadList.begin(), deadList.end(), orderSortPredicate); // remove the contiguous ranges of rows int front_bookmark = -1; int back_bookmark = -1; int currentItem = -1; auto removeNow = [this, &front_bookmark, &back_bookmark, ¤tItem]() { beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark); m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1); endRemoveRows(); front_bookmark = -1; back_bookmark = currentItem; }; for (auto& removedItem : deadList) { auto instPtr = removedItem.first; instPtr->invalidate(); currentItem = removedItem.second; if (back_bookmark == -1) { // no bookmark yet back_bookmark = currentItem; } else if (currentItem == front_bookmark - 1) { // part of contiguous sequence, continue } else { // seam between previous and current item removeNow(); } front_bookmark = currentItem; } if (back_bookmark != -1) { removeNow(); } } if (newList.size()) { add(newList); } m_dirty = false; updateTotalPlayTime(); return NoError; } void InstanceList::updateTotalPlayTime() { totalPlayTime = 0; for (const auto& itr : m_instances) { totalPlayTime += itr->totalTimePlayed(); } } void InstanceList::saveNow() { for (auto& item : m_instances) { item->saveNow(); } } void InstanceList::add(std::vector>& t) { beginInsertRows(QModelIndex(), count(), static_cast(count() + t.size() - 1)); for (auto& ptr : t) { m_instances.push_back(std::move(ptr)); connect(m_instances.back().get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged); } endInsertRows(); } void InstanceList::resumeWatch() { if (m_watchLevel > 0) { qWarning() << "Bad suspend level resume in instance list"; return; } m_watchLevel++; if (m_watchLevel > 0 && m_dirty) { loadList(); } } void InstanceList::suspendWatch() { m_watchLevel--; } void InstanceList::providerUpdated() { m_dirty = true; if (m_watchLevel == 1) { loadList(); } } BaseInstance* InstanceList::getInstanceById(QString instId) const { if (instId.isEmpty()) return nullptr; for (auto& inst : m_instances) { if (inst->id() == instId) { return inst.get(); } } return nullptr; } BaseInstance* InstanceList::getInstanceByManagedName(const QString& managed_name) const { if (managed_name.isEmpty()) return {}; for (auto& instance : m_instances) { if (instance->getManagedPackName() == managed_name) return instance.get(); } return {}; } QModelIndex InstanceList::getInstanceIndexById(const QString& id) const { return index(getInstIndex(getInstanceById(id))); } int InstanceList::getInstIndex(BaseInstance* inst) const { int count = this->count(); for (int i = 0; i < count; i++) { if (inst == m_instances.at(i).get()) { return i; } } return -1; } void InstanceList::propertiesChanged(BaseInstance* inst) { int i = getInstIndex(inst); if (i != -1) { emit dataChanged(index(i), index(i)); updateTotalPlayTime(); } } std::unique_ptr InstanceList::loadInstance(const InstanceId& id) { if (!m_groupsLoaded) { loadGroupList(); } auto instanceRoot = FS::PathCombine(m_instDir, id); auto instanceSettings = std::make_unique(FS::PathCombine(instanceRoot, "instance.cfg")); std::unique_ptr inst; instanceSettings->registerSetting("InstanceType", ""); QString inst_type = instanceSettings->get("InstanceType").toString(); // NOTE: Some launcher versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a // OneSix instance if (inst_type == "OneSix" || inst_type.isEmpty()) { inst.reset(new MinecraftInstance(m_globalSettings, std::move(instanceSettings), instanceRoot)); } else { inst.reset(new NullInstance(m_globalSettings, std::move(instanceSettings), instanceRoot)); } qDebug() << "Loaded instance" << inst->name() << "from" << inst->instanceRoot(); auto shortcut = inst->shortcuts(); if (!shortcut.isEmpty()) qDebug() << "Loaded" << shortcut.size() << "shortcut(s) for instance" << inst->name(); return inst; } void InstanceList::increaseGroupCount(const QString& group) { if (group.isEmpty()) return; ++m_groupNameCache[group]; } void InstanceList::decreaseGroupCount(const QString& group) { if (group.isEmpty()) return; if (--m_groupNameCache[group] < 1) { m_groupNameCache.remove(group); m_collapsedGroups.remove(group); } } void InstanceList::saveGroupList() { qDebug() << "Will save group list now."; if (!m_instancesProbed) { qDebug() << "Group saving prevented because we don't know the full list of instances yet."; return; } WatchLock foo(m_watcher, m_instDir); QString groupFileName = m_instDir + "/instgroups.json"; QMap> reverseGroupMap; for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) { const QString& id = iter.key(); QString group = iter.value(); if (group.isEmpty()) continue; if (!instanceSet.contains(id)) { qDebug() << "Skipping saving missing instance" << id << "to groups list."; continue; } if (!reverseGroupMap.count(group)) { QSet set; set.insert(id); reverseGroupMap[group] = set; } else { QSet& set = reverseGroupMap[group]; set.insert(id); } } QJsonObject toplevel; toplevel.insert("formatVersion", QJsonValue(QString("1"))); QJsonObject groupsArr; for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++) { auto list = iter.value(); auto name = iter.key(); QJsonObject groupObj; QJsonArray instanceArr; groupObj.insert("hidden", QJsonValue(m_collapsedGroups.contains(name))); for (auto item : list) { instanceArr.append(QJsonValue(item)); } groupObj.insert("instances", instanceArr); groupsArr.insert(name, groupObj); } toplevel.insert("groups", groupsArr); // empty string represents ungrouped "group" if (m_collapsedGroups.contains("")) { QJsonObject ungrouped; ungrouped.insert("hidden", QJsonValue(true)); toplevel.insert("ungrouped", ungrouped); } QJsonDocument doc(toplevel); try { FS::write(groupFileName, doc.toJson()); qDebug() << "Group list saved."; } catch (const FS::FileSystemException& e) { qCritical() << "Failed to write instance group file :" << e.cause(); } } void InstanceList::loadGroupList() { qDebug() << "Will load group list now."; QString groupFileName = m_instDir + "/instgroups.json"; // if there's no group file, fail if (!QFileInfo(groupFileName).exists()) return; QByteArray jsonData; try { jsonData = FS::read(groupFileName); } catch (const FS::FileSystemException& e) { qCritical() << "Failed to read instance group file :" << e.cause(); return; } QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error); // if the json was bad, fail if (error.error != QJsonParseError::NoError) { qCritical() << QString("Failed to parse instance group file: %1 at offset %2") .arg(error.errorString(), QString::number(error.offset)) .toUtf8(); return; } // if the root of the json wasn't an object, fail if (!jsonDoc.isObject()) { qWarning() << "Invalid group file. Root entry should be an object."; return; } QJsonObject rootObj = jsonDoc.object(); // Make sure the format version matches, otherwise fail. if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION) return; // Get the groups. if it's not an object, fail if (!rootObj.value("groups").isObject()) { qWarning() << "Invalid group list JSON: 'groups' should be an object."; return; } m_instanceGroupIndex.clear(); m_groupNameCache.clear(); // Iterate through all the groups. QJsonObject groupMapping = rootObj.value("groups").toObject(); for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) { QString groupName = iter.key(); if (iter.key().isEmpty()) { qWarning() << "Redundant empty group found"; continue; } // If not an object, complain and skip to the next one. if (!iter.value().isObject()) { qWarning() << QString("Group '%1' in the group list should be an object").arg(groupName).toUtf8(); continue; } QJsonObject groupObj = iter.value().toObject(); if (!groupObj.value("instances").isArray()) { qWarning() << QString("Group '%1' in the group list is invalid. It should contain an array called 'instances'.") .arg(groupName) .toUtf8(); continue; } auto hidden = groupObj.value("hidden").toBool(false); if (hidden) m_collapsedGroups.insert(groupName); // Iterate through the list of instances in the group. QJsonArray instancesArray = groupObj.value("instances").toArray(); for (auto value : instancesArray) { m_instanceGroupIndex[value.toString()] = groupName; increaseGroupCount(groupName); } } bool ungroupedHidden = false; if (rootObj.value("ungrouped").isObject()) { QJsonObject ungrouped = rootObj.value("ungrouped").toObject(); ungroupedHidden = ungrouped.value("hidden").toBool(false); } if (ungroupedHidden) { // empty string represents ungrouped "group" m_collapsedGroups.insert(""); } m_groupsLoaded = true; qDebug() << "Group list loaded."; } void InstanceList::instanceDirContentsChanged(const QString& path) { Q_UNUSED(path); emit instancesChanged(); } void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting, QVariant value) { QString newInstDir = QDir(value.toString()).canonicalPath(); if (newInstDir != m_instDir) { if (m_groupsLoaded) { saveGroupList(); } m_instDir = newInstDir; m_groupsLoaded = false; beginRemoveRows(QModelIndex(), 0, count()); m_instances.erase(m_instances.begin(), m_instances.end()); endRemoveRows(); emit instancesChanged(); } } void InstanceList::on_GroupStateChanged(const QString& group, bool collapsed) { qDebug() << "Group" << group << (collapsed ? "collapsed" : "expanded"); if (collapsed) { m_collapsedGroups.insert(group); } else { m_collapsedGroups.remove(group); } saveGroupList(); } class InstanceStaging : public Task { Q_OBJECT const unsigned minBackoff = 1; const unsigned maxBackoff = 16; public: InstanceStaging(InstanceList* parent, InstanceTask* child, SettingsObject* settings) : m_parent(parent), backoff(minBackoff, maxBackoff) { m_stagingPath = parent->getStagedInstancePath(); m_child.reset(child); m_child->setStagingPath(m_stagingPath); m_child->setParentSettings(settings); connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded); connect(child, &Task::failed, this, &InstanceStaging::childFailed); connect(child, &Task::aborted, this, &InstanceStaging::childAborted); connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable); connect(child, &Task::abortButtonTextChanged, this, &InstanceStaging::setAbortButtonText); connect(child, &Task::status, this, &InstanceStaging::setStatus); connect(child, &Task::details, this, &InstanceStaging::setDetails); connect(child, &Task::progress, this, &InstanceStaging::setProgress); connect(child, &Task::stepProgress, this, &InstanceStaging::propagateStepProgress); connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded); } ~InstanceStaging() override = default; // FIXME/TODO: add ability to abort during instance commit retries bool abort() override { if (!canAbort()) { return false; } return m_child->abort(); } bool canAbort() const override { return (m_child && m_child->canAbort()); } protected: void executeTask() override { if (m_stagingPath.isNull()) { emitFailed(tr("Could not create staging folder")); return; } m_child->start(); } QStringList warnings() const override { return m_child->warnings(); } private slots: void childSucceeded() { unsigned sleepTime = backoff(); if (m_parent->commitStagedInstance(m_stagingPath, *m_child, m_child->group(), *m_child)) { m_backoffTimer.stop(); emitSucceeded(); return; } // we actually failed, retry? if (sleepTime == maxBackoff) { m_backoffTimer.stop(); emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something.")); return; } qDebug() << "Failed to commit instance" << m_child->name() << "Initiating backoff:" << sleepTime; m_backoffTimer.start(sleepTime * 500); } void childFailed(const QString& reason) { m_backoffTimer.stop(); m_parent->destroyStagingPath(m_stagingPath); emitFailed(reason); } void childAborted() { m_backoffTimer.stop(); m_parent->destroyStagingPath(m_stagingPath); emitAborted(); } private: InstanceList* m_parent; /* * WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows. * Basically, it starts messing things up while the launcher is extracting/creating instances * and causes that horrible failure that is NTFS to lock files in place because they are open. */ ExponentialSeries backoff; QString m_stagingPath; std::unique_ptr m_child; QTimer m_backoffTimer; }; Task* InstanceList::wrapInstanceTask(InstanceTask* task) { return new InstanceStaging(this, task, m_globalSettings); } QString InstanceList::getStagedInstancePath() { const QString tempRoot = FS::PathCombine(m_instDir, ".tmp"); QString result; int tries = 0; do { if (++tries > 256) return {}; const QString key = QUuid::createUuid().toString(QUuid::Id128).left(6); result = FS::PathCombine(tempRoot, key); } while (QFileInfo::exists(result)); if (!QDir::current().mkpath(result)) return {}; #ifdef Q_OS_WIN32 SetFileAttributesA(tempRoot.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); #endif return result; } bool InstanceList::commitStagedInstance(const QString& path, const InstanceName& instanceName, QString groupName, const InstanceTask& commiting) { if (groupName.isEmpty() && !groupName.isNull()) groupName = QString(); QString instID; auto should_override = commiting.shouldOverride(); if (should_override) { instID = commiting.originalInstanceID(); } else { instID = FS::DirNameFromString(instanceName.modifiedName(), m_instDir); } Q_ASSERT(!instID.isEmpty()); { WatchLock lock(m_watcher, m_instDir); QString destination = FS::PathCombine(m_instDir, instID); if (should_override) { if (!FS::overrideFolder(destination, path)) { qWarning() << "Failed to override" << path << "to" << destination; return false; } } else { if (!FS::move(path, destination)) { qWarning() << "Failed to move" << path << "to" << destination; return false; } m_instanceGroupIndex[instID] = groupName; increaseGroupCount(groupName); } instanceSet.insert(instID); emit instancesChanged(); emit instanceSelectRequest(instID); } saveGroupList(); return true; } bool InstanceList::destroyStagingPath(const QString& keyPath) { return FS::deletePath(keyPath); } int InstanceList::getTotalPlayTime() { updateTotalPlayTime(); return totalPlayTime; } #include "InstanceList.moc" PrismLauncher-11.0.3/launcher/icons/0000755000175100017510000000000015224505336016677 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/icons/MMCIcon.cpp0000644000175100017510000000730115224505336020631 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MMCIcon.h" #include #include IconType operator--(IconType& t, int) { IconType temp = t; switch (t) { case IconType::Builtin: t = IconType::ToBeDeleted; break; case IconType::Transient: t = IconType::Builtin; break; case IconType::FileBased: t = IconType::Transient; break; default: break; } return temp; } IconType MMCIcon::type() const { return m_current_type; } QString MMCIcon::name() const { if (m_name.size()) return m_name; return m_key; } bool MMCIcon::has(IconType _type) const { return m_images[_type].present(); } QIcon MMCIcon::icon() const { if (m_current_type == IconType::ToBeDeleted) return QIcon(); auto& icon = m_images[m_current_type].icon; if (!icon.isNull()) return icon; // FIXME: inject this. return QIcon::fromTheme(m_images[m_current_type].key); } void MMCIcon::remove(IconType rm_type) { m_images[rm_type].filename = QString(); m_images[rm_type].icon = QIcon(); for (auto iter = rm_type; iter != IconType::ToBeDeleted; iter--) { if (m_images[iter].present()) { m_current_type = iter; return; } } m_current_type = IconType::ToBeDeleted; } void MMCIcon::replace(IconType new_type, QIcon icon, QString path) { if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) { m_current_type = new_type; } m_images[new_type].icon = icon; m_images[new_type].filename = path; m_images[new_type].key = QString(); } void MMCIcon::replace(IconType new_type, const QString& key) { if (new_type > m_current_type || m_current_type == IconType::ToBeDeleted) { m_current_type = new_type; } m_images[new_type].icon = QIcon(); m_images[new_type].filename = QString(); m_images[new_type].key = key; } QString MMCIcon::getFilePath() const { if (m_current_type == IconType::ToBeDeleted) { return QString(); } return m_images[m_current_type].filename; } bool MMCIcon::isBuiltIn() const { return m_current_type == IconType::Builtin; } PrismLauncher-11.0.3/launcher/icons/IconList.h0000644000175100017510000000751315224505336020602 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include "MMCIcon.h" #include "settings/Setting.h" #include "QObjectPtr.h" class QFileSystemWatcher; class IconList : public QAbstractListModel { Q_OBJECT public: explicit IconList(const QStringList& builtinPaths, const QString& path, QObject* parent = 0); virtual ~IconList() {}; QIcon getIcon(const QString& key) const; int getIconIndex(const QString& key) const; QString getDirectory() const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual QStringList mimeTypes() const override; virtual Qt::DropActions supportedDropActions() const override; virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override; bool addThemeIcon(const QString& key); bool addIcon(const QString& key, const QString& name, const QString& path, IconType type); void saveIcon(const QString& key, const QString& path, const char* format) const; bool deleteIcon(const QString& key); bool trashIcon(const QString& key); bool iconFileExists(const QString& key) const; QString iconDirectory(const QString& key) const; void installIcons(const QStringList& iconFiles); void installIcon(const QString& file, const QString& name); const MMCIcon* icon(const QString& key) const; void startWatching(); void stopWatching(); signals: void iconUpdated(QString key); private: // hide copy constructor IconList(const IconList&) = delete; // hide assign op IconList& operator=(const IconList&) = delete; void reindex(); void sortIconList(); bool addPathRecursively(const QString& path); QStringList getIconFilePaths() const; public slots: void directoryChanged(const QString& path); protected slots: void fileChanged(const QString& path); void SettingChanged(const Setting& setting, const QVariant& value); private: shared_qobject_ptr m_watcher; bool m_isWatching; QMap m_nameIndex; QList m_icons; QDir m_dir; }; PrismLauncher-11.0.3/launcher/icons/IconList.cpp0000644000175100017510000003461515224505336021140 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "IconList.h" #include #include #include #include #include #include #include #include #include "icons/IconUtils.h" #define MAX_SIZE 1024 IconList::IconList(const QStringList& builtinPaths, const QString& path, QObject* parent) : QAbstractListModel(parent) { QSet builtinNames; // add builtin icons for (const auto& builtinPath : builtinPaths) { QDir instanceIcons(builtinPath); auto fileInfoList = instanceIcons.entryInfoList(QDir::Files, QDir::Name); for (const auto& fileInfo : fileInfoList) { builtinNames.insert(fileInfo.completeBaseName()); } } for (const auto& builtinName : builtinNames) { addThemeIcon(builtinName); } m_watcher.reset(new QFileSystemWatcher()); m_isWatching = false; connect(m_watcher.get(), &QFileSystemWatcher::directoryChanged, this, &IconList::directoryChanged); connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &IconList::fileChanged); directoryChanged(path); // Forces the UI to update, so that lengthy icon names are shown properly from the start emit iconUpdated({}); } void IconList::sortIconList() { qDebug() << "Sorting icon list..."; std::sort(m_icons.begin(), m_icons.end(), [](const MMCIcon& a, const MMCIcon& b) { bool aIsSubdir = a.m_key.contains(QDir::separator()); bool bIsSubdir = b.m_key.contains(QDir::separator()); if (aIsSubdir != bIsSubdir) { return !aIsSubdir; // root-level icons come first } return a.m_key.localeAwareCompare(b.m_key) < 0; }); reindex(); } // Helper function to add directories recursively bool IconList::addPathRecursively(const QString& path) { QDir dir(path); if (!dir.exists()) return false; // Add the directory itself bool watching = m_watcher->addPath(path); // Add all subdirectories QFileInfoList entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QFileInfo& entry : entries) { if (addPathRecursively(entry.absoluteFilePath())) { watching = true; } } return watching; } QStringList IconList::getIconFilePaths() const { QStringList iconFiles{}; QStringList directories{ m_dir.absolutePath() }; while (!directories.isEmpty()) { QString first = directories.takeFirst(); QDir dir(first); for (QFileInfo& fileInfo : dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name)) { if (fileInfo.isDir()) directories.push_back(fileInfo.absoluteFilePath()); else iconFiles.push_back(fileInfo.absoluteFilePath()); } } return iconFiles; } QString formatName(const QDir& iconsDir, const QFileInfo& iconFile) { if (iconFile.dir() == iconsDir) return iconFile.completeBaseName(); constexpr auto delimiter = " » "; QString relativePathWithoutExtension = iconsDir.relativeFilePath(iconFile.dir().path()) + QDir::separator() + iconFile.completeBaseName(); return relativePathWithoutExtension.replace(QDir::separator(), delimiter); } /// Split into a separate function because the preprocessing impedes readability QSet toStringSet(const QList& list) { QSet set(list.begin(), list.end()); return set; } void IconList::directoryChanged(const QString& path) { QDir newDir(path); if (m_dir.absolutePath() != newDir.absolutePath()) { m_dir.setPath(path); m_dir.refresh(); if (m_isWatching) stopWatching(); startWatching(); } if (!m_dir.exists() && !FS::ensureFolderPathExists(m_dir.absolutePath())) return; m_dir.refresh(); const QStringList newFileNamesList = getIconFilePaths(); const QSet newSet = toStringSet(newFileNamesList); QSet currentSet; for (const MMCIcon& it : m_icons) { if (!it.has(IconType::FileBased)) continue; QFileInfo icon(it.getFilePath()); currentSet.insert(icon.absoluteFilePath()); } QSet toRemove = currentSet - newSet; QSet toAdd = newSet - currentSet; for (const QString& removedPath : toRemove) { qDebug() << "Removing icon" << removedPath; QFileInfo removedFile(removedPath); QString relativePath = m_dir.relativeFilePath(removedFile.absoluteFilePath()); QString key = QFileInfo(relativePath).completeBaseName(); int idx = getIconIndex(key); if (idx == -1) continue; m_icons[idx].remove(FileBased); if (m_icons[idx].type() == ToBeDeleted) { beginRemoveRows(QModelIndex(), idx, idx); m_icons.remove(idx); reindex(); endRemoveRows(); } else { dataChanged(index(idx), index(idx)); } m_watcher->removePath(removedPath); emit iconUpdated(key); } for (const QString& addedPath : toAdd) { qDebug() << "Adding icon" << addedPath; QFileInfo addfile(addedPath); QString relativePath = m_dir.relativeFilePath(addfile.absoluteFilePath()); QString key = QFileInfo(relativePath).completeBaseName(); QString name = formatName(m_dir, addfile); if (addIcon(key, name, addfile.filePath(), IconType::FileBased)) { m_watcher->addPath(addedPath); emit iconUpdated(key); } } sortIconList(); } void IconList::fileChanged(const QString& path) { qDebug() << "Checking icon" << path; QFileInfo checkfile(path); if (!checkfile.exists()) return; QString key = m_dir.relativeFilePath(checkfile.absoluteFilePath()); int idx = getIconIndex(key); if (idx == -1) return; QIcon icon; // special handling for jpg and jpeg to go through pixmap to keep the size constant if (path.endsWith(".jpg") || path.endsWith(".jpeg")) { icon.addPixmap(QPixmap(path)); } else { icon.addFile(path); } if (icon.availableSizes().empty()) return; m_icons[idx].m_images[IconType::FileBased].icon = icon; dataChanged(index(idx), index(idx)); emit iconUpdated(key); } void IconList::SettingChanged(const Setting& setting, const QVariant& value) { if (setting.id() != "IconsDir") return; directoryChanged(value.toString()); } void IconList::startWatching() { auto abs_path = m_dir.absolutePath(); FS::ensureFolderPathExists(abs_path); m_isWatching = addPathRecursively(abs_path); if (m_isWatching) { qDebug() << "Started watching" << abs_path; } else { qDebug() << "Failed to start watching" << abs_path; } } void IconList::stopWatching() { m_watcher->removePaths(m_watcher->files()); m_watcher->removePaths(m_watcher->directories()); m_isWatching = false; } QStringList IconList::mimeTypes() const { QStringList types; types << "text/uri-list"; return types; } Qt::DropActions IconList::supportedDropActions() const { return Qt::CopyAction; } bool IconList::dropMimeData(const QMimeData* data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex& parent) { if (action == Qt::IgnoreAction) return true; // check if the action is supported if (!data || !(action & supportedDropActions())) return false; // files dropped from outside? if (data->hasUrls()) { auto urls = data->urls(); QStringList iconFiles; for (const auto& url : urls) { // only local files may be dropped... if (!url.isLocalFile()) continue; iconFiles += url.toLocalFile(); } installIcons(iconFiles); return true; } return false; } Qt::ItemFlags IconList::flags(const QModelIndex& index) const { Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); return Qt::ItemIsDropEnabled | defaultFlags; } QVariant IconList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return {}; int row = index.row(); if (row < 0 || row >= m_icons.size()) return {}; switch (role) { case Qt::DecorationRole: return m_icons[row].icon(); case Qt::DisplayRole: return m_icons[row].name(); case Qt::UserRole: return m_icons[row].m_key; default: return {}; } } int IconList::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_icons.size(); } void IconList::installIcons(const QStringList& iconFiles) { for (const QString& file : iconFiles) installIcon(file, {}); } void IconList::installIcon(const QString& file, const QString& name) { QFileInfo fileinfo(file); if (!fileinfo.isReadable() || !fileinfo.isFile()) return; if (!IconUtils::isIconSuffix(fileinfo.suffix())) return; QString target = FS::PathCombine(getDirectory(), name.isEmpty() ? fileinfo.fileName() : name); QFile::copy(file, target); } bool IconList::iconFileExists(const QString& key) const { auto iconEntry = icon(key); return iconEntry && iconEntry->has(IconType::FileBased); } /// Returns the icon with the given key or nullptr if it doesn't exist. const MMCIcon* IconList::icon(const QString& key) const { int iconIdx = getIconIndex(key); if (iconIdx == -1) return nullptr; return &m_icons[iconIdx]; } bool IconList::deleteIcon(const QString& key) { return iconFileExists(key) && FS::deletePath(icon(key)->getFilePath()); } bool IconList::trashIcon(const QString& key) { return iconFileExists(key) && FS::trash(icon(key)->getFilePath(), nullptr); } bool IconList::addThemeIcon(const QString& key) { auto iter = m_nameIndex.find(key); if (iter != m_nameIndex.end()) { auto& oldOne = m_icons[*iter]; oldOne.replace(Builtin, key); dataChanged(index(*iter), index(*iter)); return true; } // add a new icon beginInsertRows(QModelIndex(), m_icons.size(), m_icons.size()); { MMCIcon mmc_icon; mmc_icon.m_name = key; mmc_icon.m_key = key; mmc_icon.replace(Builtin, key); m_icons.push_back(mmc_icon); m_nameIndex[key] = m_icons.size() - 1; } endInsertRows(); return true; } bool IconList::addIcon(const QString& key, const QString& name, const QString& path, const IconType type) { // replace the icon even? is the input valid? QIcon icon; // special handling for jpg and jpeg to go through pixmap to keep the size constant if (path.endsWith(".jpg") || path.endsWith(".jpeg")) { icon.addPixmap(QPixmap(path)); } else { icon.addFile(path); } if (icon.isNull()) return false; auto iter = m_nameIndex.find(key); if (iter != m_nameIndex.end()) { auto& oldOne = m_icons[*iter]; oldOne.replace(type, icon, path); dataChanged(index(*iter), index(*iter)); return true; } // add a new icon beginInsertRows(QModelIndex(), m_icons.size(), m_icons.size()); { MMCIcon mmc_icon; mmc_icon.m_name = name; mmc_icon.m_key = key; mmc_icon.replace(type, icon, path); m_icons.push_back(mmc_icon); m_nameIndex[key] = m_icons.size() - 1; } endInsertRows(); return true; } void IconList::saveIcon(const QString& key, const QString& path, const char* format) const { auto icon = getIcon(key); auto pixmap = icon.pixmap(128, 128); pixmap.save(path, format); } void IconList::reindex() { m_nameIndex.clear(); for (int i = 0; i < m_icons.size(); i++) { m_nameIndex[m_icons[i].m_key] = i; emit iconUpdated(m_icons[i].m_key); // prevents incorrect indices with proxy model } } QIcon IconList::getIcon(const QString& key) const { int iconIndex = getIconIndex(key); if (iconIndex != -1) return m_icons[iconIndex].icon(); // Fallback for icons that don't exist.b iconIndex = getIconIndex("grass"); if (iconIndex != -1) return m_icons[iconIndex].icon(); return {}; } int IconList::getIconIndex(const QString& key) const { auto iter = m_nameIndex.find(key == "default" ? "grass" : key); if (iter != m_nameIndex.end()) return *iter; return -1; } QString IconList::getDirectory() const { return m_dir.absolutePath(); } /// Returns the directory of the icon with the given key or the default directory if it's a builtin icon. QString IconList::iconDirectory(const QString& key) const { for (const auto& mmcIcon : m_icons) { if (mmcIcon.m_key == key && mmcIcon.has(IconType::FileBased)) { QFileInfo iconFile(mmcIcon.getFilePath()); return iconFile.dir().path(); } } return getDirectory(); } PrismLauncher-11.0.3/launcher/icons/IconUtils.cpp0000644000175100017510000000452615224505336021323 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "IconUtils.h" #include #include "FileSystem.h" namespace { static const QStringList validIconExtensions = { { "svg", "png", "ico", "gif", "jpg", "jpeg", "webp" } }; } namespace IconUtils { QString findBestIconIn(const QString& folder, const QString& iconKey) { QString best_filename; QDirIterator it(folder, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::NoIteratorFlags); while (it.hasNext()) { it.next(); auto fileInfo = it.fileInfo(); if ((fileInfo.completeBaseName() == iconKey || fileInfo.fileName() == iconKey) && isIconSuffix(fileInfo.suffix())) return fileInfo.absoluteFilePath(); } return {}; } QString getIconFilter() { return "(*." + validIconExtensions.join(" *.") + ")"; } bool isIconSuffix(QString suffix) { return validIconExtensions.contains(suffix); } } // namespace IconUtils PrismLauncher-11.0.3/launcher/icons/IconUtils.h0000644000175100017510000000352215224505336020763 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include namespace IconUtils { // Given a folder and an icon key, find 'best' of the icons with the given key in there and return its path QString findBestIconIn(const QString& folder, const QString& iconKey); // Get icon file type filter for file browser dialogs QString getIconFilter(); bool isIconSuffix(QString suffix); } // namespace IconUtils PrismLauncher-11.0.3/launcher/icons/MMCIcon.h0000644000175100017510000000435015224505336020277 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include enum IconType : unsigned { Builtin, Transient, FileBased, ICONS_TOTAL, ToBeDeleted }; struct MMCImage { QIcon icon; QString key; QString filename; bool present() const { return !icon.isNull() || !key.isEmpty(); } }; struct MMCIcon { QString m_key; QString m_name; MMCImage m_images[ICONS_TOTAL]; IconType m_current_type = ToBeDeleted; IconType type() const; QString name() const; bool has(IconType _type) const; QIcon icon() const; void remove(IconType rm_type); void replace(IconType new_type, QIcon icon, QString path = QString()); void replace(IconType new_type, const QString& key); bool isBuiltIn() const; QString getFilePath() const; }; PrismLauncher-11.0.3/launcher/news/0000755000175100017510000000000015224505336016540 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/news/NewsEntry.cpp0000644000175100017510000000345715224505336021213 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "NewsEntry.h" #include #include NewsEntry::NewsEntry(QObject* parent) : QObject(parent) { this->title = tr("Untitled"); this->content = tr("No content."); this->link = ""; } NewsEntry::NewsEntry(const QString& title, const QString& content, const QString& link, QObject* parent) : QObject(parent) { this->title = title; this->content = content; this->link = link; } /*! * Gets the text content of the given child element as a QVariant. */ inline QString childValue(const QDomElement& element, const QString& childName, QString defaultVal = "") { QDomNodeList nodes = element.elementsByTagName(childName); if (nodes.count() > 0) { QDomElement elem = nodes.at(0).toElement(); return elem.text(); } else { return defaultVal; } } bool NewsEntry::fromXmlElement(const QDomElement& element, NewsEntry* entry, [[maybe_unused]] QString* errorMsg) { QString title = childValue(element, "title", tr("Untitled")); QString content = childValue(element, "content", tr("No content.")); QString link = childValue(element, "id"); entry->title = title; entry->content = content; entry->link = link; return true; } PrismLauncher-11.0.3/launcher/news/NewsChecker.h0000644000175100017510000000532515224505336021117 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include "NewsEntry.h" class NewsChecker : public QObject { Q_OBJECT public: /*! * Constructs a news reader to read from the given RSS feed URL. */ NewsChecker(QNetworkAccessManager* network, const QString& feedUrl); /*! * Returns the error message for the last time the news was loaded. * Empty string if the last load was successful. */ QString getLastLoadErrorMsg() const; /*! * Returns true if the news has been loaded successfully. */ bool isNewsLoaded() const; //! True if the news is currently loading. If true, reloadNews() will do nothing. bool isLoadingNews() const; /*! * Returns a list of news entries. */ QList getNewsEntries() const; /*! * Reloads the news from the website's RSS feed. * If the news is already loading, this does nothing. */ void Q_SLOT reloadNews(); signals: /*! * Signal fired after the news has finished loading. */ void newsLoaded(); /*! * Signal fired after the news fails to load. */ void newsLoadingFailed(QString errorMsg); protected slots: void rssDownloadFinished(); void rssDownloadFailed(QString reason); protected: /* data */ //! The URL for the RSS feed to fetch. QString m_feedUrl; //! List of news entries. QList m_newsEntries; //! The network job to use to load the news. NetJob::Ptr m_newsNetJob; //! True if news has been loaded. bool m_loadedNews; //! The cache entry for the feed. MetaEntryPtr m_entry; /*! * Gets the error message that was given last time the news was loaded. * If the last news load succeeded, this will be an empty string. */ QString m_lastLoadError; QNetworkAccessManager* m_network; protected slots: /// Emits newsLoaded() and sets m_lastLoadError to empty string. void succeed(); /// Emits newsLoadingFailed() and sets m_lastLoadError to the given message. void fail(const QString& errorMsg); }; PrismLauncher-11.0.3/launcher/news/NewsChecker.cpp0000644000175100017510000001113615224505336021447 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "NewsChecker.h" #include #include #include #include "Application.h" NewsChecker::NewsChecker(QNetworkAccessManager* network, const QString& feedUrl) { m_network = network; m_feedUrl = feedUrl; } void NewsChecker::reloadNews() { // Start a netjob to download the RSS feed and call rssDownloadFinished() when it's done. if (isLoadingNews()) { qDebug() << "Ignored request to reload news. Currently reloading already."; return; } m_entry = APPLICATION->metacache()->resolveEntry("feed", "feed.xml"); qDebug() << "Reloading news."; NetJob::Ptr job{ new NetJob("News RSS Feed", m_network) }; job->addNetAction(Net::Download::makeCached(m_feedUrl, m_entry)); job->setAskRetry(false); connect(job.get(), &NetJob::succeeded, this, &NewsChecker::rssDownloadFinished); connect(job.get(), &NetJob::failed, this, &NewsChecker::rssDownloadFailed); m_newsNetJob.reset(job); job->start(); } void NewsChecker::rssDownloadFinished() { // Parse the XML file and process the RSS feed entries. qDebug() << "Finished loading RSS feed."; m_newsNetJob.reset(); QDomDocument doc; { // Stuff to store error info in. QString errorMsg = "Unknown error."; int errorLine = -1; int errorCol = -1; QFile feed(m_entry->getFullPath()); if (feed.open(QFile::ReadOnly | QFile::Text)) { QTextStream in(&feed); // Parse the XML. if (!doc.setContent(in.readAll(), false, &errorMsg, &errorLine, &errorCol)) { fail(QString("Error parsing RSS feed XML. %1 at %2:%3.").arg(errorMsg).arg(errorLine).arg(errorCol)); return; } } } // If the parsing succeeded, read it. QDomNodeList items = doc.elementsByTagName("entry"); m_newsEntries.clear(); for (int i = 0; i < items.length(); i++) { QDomElement element = items.at(i).toElement(); NewsEntryPtr entry; entry.reset(new NewsEntry()); QString errorMsg = "An unknown error occurred."; if (NewsEntry::fromXmlElement(element, entry.get(), &errorMsg)) { qDebug() << "Loaded news entry" << entry->title; m_newsEntries.append(entry); } else { qWarning() << "Failed to load news entry at index" << i << ":" << errorMsg; } } succeed(); } void NewsChecker::rssDownloadFailed(QString reason) { // Set an error message and fail. fail(tr("Failed to load news RSS feed:\n%1").arg(reason)); } QList NewsChecker::getNewsEntries() const { return m_newsEntries; } bool NewsChecker::isLoadingNews() const { return m_newsNetJob.get() != nullptr; } QString NewsChecker::getLastLoadErrorMsg() const { return m_lastLoadError; } void NewsChecker::succeed() { m_lastLoadError = ""; qDebug() << "News loading succeeded."; m_newsNetJob.reset(); emit newsLoaded(); } void NewsChecker::fail(const QString& errorMsg) { m_lastLoadError = errorMsg; qDebug() << "Failed to load news:" << errorMsg; m_newsNetJob.reset(); emit newsLoadingFailed(errorMsg); } PrismLauncher-11.0.3/launcher/news/NewsEntry.h0000644000175100017510000000306615224505336020654 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include class NewsEntry : public QObject { Q_OBJECT public: /*! * Constructs an empty news entry. */ explicit NewsEntry(QObject* parent = 0); /*! * Constructs a new news entry. * Note that content may contain HTML. */ NewsEntry(const QString& title, const QString& content, const QString& link, QObject* parent = 0); /*! * Attempts to load information from the given XML element into the given news entry pointer. * If this fails, the function will return false and store an error message in the errorMsg pointer. */ static bool fromXmlElement(const QDomElement& element, NewsEntry* entry, QString* errorMsg = 0); //! The post title. QString title; //! The post's content. May contain HTML. QString content; //! URL to the post. QString link; }; using NewsEntryPtr = std::shared_ptr; PrismLauncher-11.0.3/launcher/meta/0000755000175100017510000000000015224505336016512 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/meta/VersionList.h0000644000175100017510000000647115224505336021154 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "BaseEntity.h" #include "BaseVersionList.h" #include "meta/Version.h" namespace Meta { class VersionList : public BaseVersionList, public BaseEntity { Q_OBJECT Q_PROPERTY(QString uid READ uid CONSTANT) Q_PROPERTY(QString name READ name NOTIFY nameChanged) public: explicit VersionList(const QString& uid, QObject* parent = nullptr); virtual ~VersionList() = default; using Ptr = std::shared_ptr; enum Roles { UidRole = Qt::UserRole + 100, TimeRole, RequiresRole, VersionPtrRole }; bool isLoaded() override; Task::Ptr getLoadTask(bool forceReload = false) override; const BaseVersion::Ptr at(int i) const override; int count() const override; void sortVersions() override; BaseVersion::Ptr getRecommended() const override; Version::Ptr getRecommendedForParent(const QString& uid, const QString& version); Version::Ptr getLatestForParent(const QString& uid, const QString& version); QVariant data(const QModelIndex& index, int role) const override; RoleList providesRoles() const override; QHash roleNames() const override; void setProvidedRoles(RoleList roles); QString localFilename() const override; QString uid() const { return m_uid; } QString name() const { return m_name; } QString humanReadable() const; Version::Ptr getVersion(const QString& version); bool hasVersion(QString version) const; QList versions() const { return m_versions; } // this blocks until the version list is loaded void waitToLoad(); public: // for usage only by parsers void setName(const QString& name); void setVersions(const QList& versions); void merge(const VersionList::Ptr& other); void mergeFromIndex(const VersionList::Ptr& other); void parse(const QJsonObject& obj) override; void addExternalRecommends(const QStringList& recommends); void clearExternalRecommends(); signals: void nameChanged(const QString& name); protected slots: void updateListData(QList) override {} private: QList m_versions; QStringList m_externalRecommendsVersions; QHash m_lookup; QString m_uid; QString m_name; Version::Ptr m_recommended; RoleList m_provided_roles = { VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole, TypeRole, UidRole, TimeRole, RequiresRole, SortRole, RecommendedRole, LatestRole, VersionPtrRole }; void setupAddedVersion(int row, const Version::Ptr& version); }; } // namespace Meta Q_DECLARE_METATYPE(Meta::VersionList::Ptr) PrismLauncher-11.0.3/launcher/meta/Index.h0000644000175100017510000000433215224505336017734 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include "BaseEntity.h" #include "meta/VersionList.h" #include "net/Mode.h" class Task; namespace Meta { class Index : public QAbstractListModel, public BaseEntity { Q_OBJECT public: explicit Index(QObject* parent = nullptr); explicit Index(const QList& lists, QObject* parent = nullptr); virtual ~Index() = default; enum { UidRole = Qt::UserRole, NameRole, ListPtrRole }; QVariant data(const QModelIndex& index, int role) const override; int rowCount(const QModelIndex& parent) const override; int columnCount(const QModelIndex& parent) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QString localFilename() const override { return "index.json"; } // queries VersionList::Ptr get(const QString& uid); Version::Ptr get(const QString& uid, const QString& version); bool hasUid(const QString& uid) const; QList lists() const { return m_lists; } Task::Ptr loadVersion(const QString& uid, const QString& version = {}, Net::Mode mode = Net::Mode::Online, bool force = false); // this blocks until the version is loaded Version::Ptr getLoadedVersion(const QString& uid, const QString& version); public: // for usage by parsers only void merge(const std::shared_ptr& other); protected: void parse(const QJsonObject& obj) override; private: QList m_lists; QHash m_uids; void connectVersionList(int row, const VersionList::Ptr& list); }; } // namespace Meta PrismLauncher-11.0.3/launcher/meta/JsonFormat.cpp0000644000175100017510000001447615224505336021314 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "JsonFormat.h" // FIXME: remove this from here... somehow #include "Json.h" #include "minecraft/OneSixVersionFormat.h" #include "Index.h" #include "Version.h" #include "VersionList.h" using namespace Json; namespace Meta { MetadataVersion currentFormatVersion() { return MetadataVersion::InitialRelease; } // Index static std::shared_ptr parseIndexInternal(const QJsonObject& obj) { const QList objects = requireIsArrayOf(obj, "packages"); QList lists; lists.reserve(objects.size()); std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject& obj) { VersionList::Ptr list = std::make_shared(requireString(obj, "uid")); list->setName(obj["name"].toString()); list->setSha256(obj["sha256"].toString()); return list; }); return std::make_shared(lists); } // Version static Version::Ptr parseCommonVersion(const QString& uid, const QJsonObject& obj) { Version::Ptr version = std::make_shared(uid, requireString(obj, "version")); version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000); version->setType(obj["type"].toString()); version->setRecommended(obj["recommended"].toBool()); version->setVolatile(obj["volatile"].toBool()); RequireSet reqs, conflicts; parseRequires(obj, &reqs, "requires"); parseRequires(obj, &conflicts, "conflicts"); version->setRequires(reqs, conflicts); if (auto sha256 = obj["sha256"].toString(); !sha256.isEmpty()) { version->setSha256(sha256); } return version; } static Version::Ptr parseVersionInternal(const QJsonObject& obj) { Version::Ptr version = parseCommonVersion(requireString(obj, "uid"), obj); version->setData(OneSixVersionFormat::versionFileFromJson( QJsonDocument(obj), QString("%1/%2.json").arg(version->uid(), version->version()), obj.contains("order"))); return version; } // Version list / package static VersionList::Ptr parseVersionListInternal(const QJsonObject& obj) { const QString uid = requireString(obj, "uid"); const QList versionsRaw = requireIsArrayOf(obj, "versions"); QList versions; versions.reserve(versionsRaw.size()); std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject& vObj) { auto version = parseCommonVersion(uid, vObj); version->setProvidesRecommendations(); return version; }); VersionList::Ptr list = std::make_shared(uid); list->setName(obj["name"].toString()); list->setVersions(versions); return list; } MetadataVersion parseFormatVersion(const QJsonObject& obj, bool required) { if (!obj.contains("formatVersion")) { if (required) { return MetadataVersion::Invalid; } return MetadataVersion::InitialRelease; } if (!obj.value("formatVersion").isDouble()) { return MetadataVersion::Invalid; } switch (obj.value("formatVersion").toInt()) { case 0: case 1: return MetadataVersion::InitialRelease; default: return MetadataVersion::Invalid; } } void serializeFormatVersion(QJsonObject& obj, Meta::MetadataVersion version) { if (version == MetadataVersion::Invalid) { return; } obj.insert("formatVersion", int(version)); } void parseIndex(const QJsonObject& obj, Index* ptr) { const MetadataVersion version = parseFormatVersion(obj); switch (version) { case MetadataVersion::InitialRelease: ptr->merge(parseIndexInternal(obj)); break; case MetadataVersion::Invalid: throw ParseException(QObject::tr("Unknown format version!")); } } void parseVersionList(const QJsonObject& obj, VersionList* ptr) { const MetadataVersion version = parseFormatVersion(obj); switch (version) { case MetadataVersion::InitialRelease: ptr->merge(parseVersionListInternal(obj)); break; case MetadataVersion::Invalid: throw ParseException(QObject::tr("Unknown format version!")); } } void parseVersion(const QJsonObject& obj, Version* ptr) { const MetadataVersion version = parseFormatVersion(obj); switch (version) { case MetadataVersion::InitialRelease: ptr->merge(parseVersionInternal(obj)); break; case MetadataVersion::Invalid: throw ParseException(QObject::tr("Unknown format version!")); } } /* [ {"uid":"foo", "equals":"version"} ] */ void parseRequires(const QJsonObject& obj, RequireSet* ptr, const char* keyName) { if (obj.contains(keyName)) { auto reqArray = requireArray(obj, keyName); auto iter = reqArray.begin(); while (iter != reqArray.end()) { auto reqObject = requireObject(*iter); auto uid = requireString(reqObject, "uid"); auto equals = reqObject["equals"].toString(); auto suggests = reqObject["suggests"].toString(); ptr->insert({ uid, equals, suggests }); iter++; } } } void serializeRequires(QJsonObject& obj, RequireSet* ptr, const char* keyName) { if (!ptr || ptr->empty()) { return; } QJsonArray arrOut; for (auto& iter : *ptr) { QJsonObject reqOut; reqOut.insert("uid", iter.uid); if (!iter.equalsVersion.isEmpty()) { reqOut.insert("equals", iter.equalsVersion); } if (!iter.suggests.isEmpty()) { reqOut.insert("suggests", iter.suggests); } arrOut.append(reqOut); } obj.insert(keyName, arrOut); } } // namespace Meta PrismLauncher-11.0.3/launcher/meta/BaseEntity.cpp0000644000175100017510000001626115224505336021273 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "BaseEntity.h" #include "Exception.h" #include "FileSystem.h" #include "Json.h" #include "modplatform/helpers/HashUtils.h" #include "net/ApiDownload.h" #include "net/ChecksumValidator.h" #include "net/HttpMetaCache.h" #include "net/Mode.h" #include "net/NetJob.h" #include "Application.h" #include "settings/SettingsObject.h" #include "BuildConfig.h" #include "tasks/Task.h" namespace Meta { class ParsingValidator : public Net::Validator { public: /* con/des */ ParsingValidator(BaseEntity* entity) : m_entity(entity) {}; virtual ~ParsingValidator() = default; public: /* methods */ bool init(QNetworkRequest&) override { m_data.clear(); return true; } bool write(QByteArray& data) override { this->m_data.append(data); return true; } bool abort() override { m_data.clear(); return true; } bool validate(QNetworkReply&) override { auto fname = m_entity->localFilename(); try { auto doc = Json::requireDocument(m_data, fname); auto obj = Json::requireObject(doc, fname); m_entity->parse(obj); return true; } catch (const Exception& e) { qWarning() << "Unable to parse response:" << e.cause(); return false; } } private: /* data */ QByteArray m_data; BaseEntity* m_entity; }; QUrl BaseEntity::url() const { auto s = APPLICATION->settings(); QString metaOverride = s->get("MetaURLOverride").toString(); if (metaOverride.isEmpty()) { return QUrl(BuildConfig.META_URL).resolved(localFilename()); } return QUrl(metaOverride).resolved(localFilename()); } Task::Ptr BaseEntity::loadTask(Net::Mode mode, bool forceReload) { if (m_task && m_task->isRunning()) { return m_task; } m_task.reset(new BaseEntityLoadTask(this, mode, forceReload)); return m_task; } bool BaseEntity::isLoaded() const { // consider it loaded only if the main hash is either empty and was remote loadded or the hashes match and was loaded return m_sha256.isEmpty() ? m_load_status == LoadStatus::Remote : m_load_status != LoadStatus::NotLoaded && m_sha256 == m_file_sha256; } void BaseEntity::setSha256(QString sha256) { m_sha256 = sha256; } BaseEntity::LoadStatus BaseEntity::status() const { return m_load_status; } BaseEntityLoadTask::BaseEntityLoadTask(BaseEntity* parent, Net::Mode mode, bool forceReload) : m_entity(parent), m_mode(mode), m_force_reload(forceReload) {} void BaseEntityLoadTask::executeTask() { const QString fname = QDir("meta").absoluteFilePath(m_entity->localFilename()); auto hashMatches = false; // the file exists on disk try to load it if (QFile::exists(fname)) { try { QByteArray fileData; // read local file if nothing is loaded yet if (m_entity->m_load_status == BaseEntity::LoadStatus::NotLoaded || m_entity->m_file_sha256.isEmpty()) { setStatus(tr("Loading local file")); fileData = FS::read(fname); m_entity->m_file_sha256 = Hashing::hash(fileData, Hashing::Algorithm::Sha256); } // on online the hash needs to match const auto& expected = m_entity->m_sha256; const auto& actual = m_entity->m_file_sha256; hashMatches = expected == actual; if (m_mode == Net::Mode::Online && !m_entity->m_sha256.isEmpty() && !hashMatches) { throw Exception(QString("Checksum mismatch, expected sha256: %1, got: %2").arg(expected, actual)); } // load local file if (m_entity->m_load_status == BaseEntity::LoadStatus::NotLoaded) { auto doc = Json::requireDocument(fileData, fname); auto obj = Json::requireObject(doc, fname); m_entity->parse(obj); m_entity->m_load_status = BaseEntity::LoadStatus::Local; } } catch (const Exception& e) { qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause()); // just make sure it's gone and we never consider it again. FS::deletePath(fname); m_entity->m_load_status = BaseEntity::LoadStatus::NotLoaded; } } // if we need remote update, run the update task auto wasLoadedOffline = m_entity->m_load_status != BaseEntity::LoadStatus::NotLoaded && m_mode == Net::Mode::Offline; // if has is not present allways fetch from remote(e.g. the main index file), else only fetch if hash doesn't match auto wasLoadedRemote = m_entity->m_sha256.isEmpty() ? m_entity->m_load_status == BaseEntity::LoadStatus::Remote : hashMatches; if (wasLoadedOffline || (wasLoadedRemote && !m_force_reload)) { emitSucceeded(); return; } m_task.reset(new NetJob(QObject::tr("Download of meta file %1").arg(m_entity->localFilename()), APPLICATION->network())); auto url = m_entity->url(); auto entry = APPLICATION->metacache()->resolveEntry("meta", m_entity->localFilename()); if (m_force_reload) { // clear validators so manual refreshes fetch a fresh body entry->setETag({}); entry->setRemoteChangedTimestamp({}); } entry->setStale(true); auto dl = Net::ApiDownload::makeCached(url, entry); /* * The validator parses the file and loads it into the object. * If that fails, the file is not written to storage. */ if (!m_entity->m_sha256.isEmpty()) dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha256, m_entity->m_sha256)); dl->addValidator(new ParsingValidator(m_entity)); m_task->addNetAction(dl); m_task->setAskRetry(false); connect(m_task.get(), &Task::failed, this, &BaseEntityLoadTask::emitFailed); connect(m_task.get(), &Task::succeeded, this, &BaseEntityLoadTask::emitSucceeded); connect(m_task.get(), &Task::succeeded, this, [this]() { m_entity->m_load_status = BaseEntity::LoadStatus::Remote; m_entity->m_file_sha256 = m_entity->m_sha256; }); connect(m_task.get(), &Task::progress, this, &Task::setProgress); connect(m_task.get(), &Task::stepProgress, this, &BaseEntityLoadTask::propagateStepProgress); connect(m_task.get(), &Task::status, this, &Task::setStatus); connect(m_task.get(), &Task::details, this, &Task::setDetails); m_task->start(); } bool BaseEntityLoadTask::canAbort() const { return m_task ? m_task->canAbort() : false; } bool BaseEntityLoadTask::abort() { if (m_task) { Task::abort(); return m_task->abort(); } return Task::abort(); } } // namespace Meta PrismLauncher-11.0.3/launcher/meta/JsonFormat.h0000644000175100017510000000372415224505336020753 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "Exception.h" namespace Meta { class Index; class Version; class VersionList; enum class MetadataVersion { Invalid = -1, InitialRelease = 1 }; class ParseException : public Exception { public: using Exception::Exception; }; struct Require { bool operator==(const Require& rhs) const { return uid == rhs.uid; } bool operator<(const Require& rhs) const { return uid < rhs.uid; } bool deepEquals(const Require& rhs) const { return uid == rhs.uid && equalsVersion == rhs.equalsVersion && suggests == rhs.suggests; } QString uid; QString equalsVersion; QString suggests; }; using RequireSet = std::set; void parseIndex(const QJsonObject& obj, Index* ptr); void parseVersion(const QJsonObject& obj, Version* ptr); void parseVersionList(const QJsonObject& obj, VersionList* ptr); MetadataVersion parseFormatVersion(const QJsonObject& obj, bool required = true); void serializeFormatVersion(QJsonObject& obj, MetadataVersion version); // FIXME: this has a different shape than the others...FIX IT!? void parseRequires(const QJsonObject& obj, RequireSet* ptr, const char* keyName = "requires"); void serializeRequires(QJsonObject& objOut, RequireSet* ptr, const char* keyName = "requires"); MetadataVersion currentFormatVersion(); } // namespace Meta Q_DECLARE_METATYPE(std::set) PrismLauncher-11.0.3/launcher/meta/Index.cpp0000644000175100017510000001136215224505336020270 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Index.h" #include "JsonFormat.h" #include "QObjectPtr.h" #include "VersionList.h" #include "meta/BaseEntity.h" #include "tasks/SequentialTask.h" namespace Meta { Index::Index(QObject* parent) : QAbstractListModel(parent) {} Index::Index(const QList& lists, QObject* parent) : QAbstractListModel(parent), m_lists(lists) { for (int i = 0; i < m_lists.size(); ++i) { m_uids.insert(m_lists.at(i)->uid(), m_lists.at(i)); connectVersionList(i, m_lists.at(i)); } } QVariant Index::data(const QModelIndex& index, int role) const { if (index.parent().isValid() || index.row() < 0 || index.row() >= m_lists.size()) { return QVariant(); } VersionList::Ptr list = m_lists.at(index.row()); switch (role) { case Qt::DisplayRole: if (index.column() == 0) { return list->humanReadable(); } else { break; } case UidRole: return list->uid(); case NameRole: return list->name(); case ListPtrRole: return QVariant::fromValue(list); } return QVariant(); } int Index::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_lists.size(); } int Index::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : 1; } QVariant Index::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0) { return tr("Name"); } else { return QVariant(); } } bool Index::hasUid(const QString& uid) const { return m_uids.contains(uid); } VersionList::Ptr Index::get(const QString& uid) { VersionList::Ptr out = m_uids.value(uid, nullptr); if (!out) { out = std::make_shared(uid); m_uids[uid] = out; m_lists.append(out); } return out; } Version::Ptr Index::get(const QString& uid, const QString& version) { auto list = get(uid); return list->getVersion(version); } void Index::parse(const QJsonObject& obj) { parseIndex(obj, this); } void Index::merge(const std::shared_ptr& other) { const QList lists = other->m_lists; // initial load, no need to merge if (m_lists.isEmpty()) { beginResetModel(); m_lists = lists; for (int i = 0; i < lists.size(); ++i) { m_uids.insert(lists.at(i)->uid(), lists.at(i)); connectVersionList(i, lists.at(i)); } endResetModel(); } else { for (const VersionList::Ptr& list : lists) { if (m_uids.contains(list->uid())) { m_uids[list->uid()]->mergeFromIndex(list); } else { beginInsertRows(QModelIndex(), m_lists.size(), m_lists.size()); connectVersionList(m_lists.size(), list); m_lists.append(list); m_uids.insert(list->uid(), list); endInsertRows(); } } } } void Index::connectVersionList(const int row, const VersionList::Ptr& list) { connect(list.get(), &VersionList::nameChanged, this, [this, row] { emit dataChanged(index(row), index(row), { Qt::DisplayRole }); }); } Task::Ptr Index::loadVersion(const QString& uid, const QString& version, Net::Mode mode, bool force) { if (mode == Net::Mode::Offline) { return get(uid, version)->loadTask(mode); } auto versionList = get(uid); auto loadTask = makeShared(tr("Load meta for %1:%2", "This is for the task name that loads the meta index.").arg(uid, version)); if (status() != BaseEntity::LoadStatus::Remote || force) { loadTask->addTask(this->loadTask(mode)); } loadTask->addTask(versionList->loadTask(mode)); loadTask->addTask(versionList->getVersion(version)->loadTask(mode)); return loadTask; } Version::Ptr Index::getLoadedVersion(const QString& uid, const QString& version) { QEventLoop ev; auto task = loadVersion(uid, version); connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); task->start(); ev.exec(); return get(uid, version); } } // namespace Meta PrismLauncher-11.0.3/launcher/meta/Version.h0000644000175100017510000000531215224505336020311 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "../Version.h" #include "BaseVersion.h" #include #include #include #include #include "minecraft/VersionFile.h" #include "BaseEntity.h" #include "JsonFormat.h" namespace Meta { class Version : public QObject, public BaseVersion, public BaseEntity { Q_OBJECT public: using Ptr = std::shared_ptr; explicit Version(const QString& uid, const QString& version); virtual ~Version() = default; QString descriptor() const override; QString name() const override; QString typeString() const override; QString uid() const { return m_uid; } QString version() const { return m_version; } QString type() const { return m_type; } QDateTime time() const; qint64 rawTime() const { return m_time; } const Meta::RequireSet& requiredSet() const { return m_requires; } VersionFilePtr data() const { return m_data; } bool isRecommended() const { return m_recommended; } bool isLoaded() const { return m_data != nullptr && BaseEntity::isLoaded(); } void merge(const Version::Ptr& other); void mergeFromList(const Version::Ptr& other); void parse(const QJsonObject& obj) override; QString localFilename() const override; ::Version toComparableVersion() const; public: // for usage by format parsers only void setType(const QString& type); void setTime(qint64 time); void setRequires(const Meta::RequireSet& reqs, const Meta::RequireSet& conflicts); void setVolatile(bool volatile_); void setRecommended(bool recommended); void setProvidesRecommendations(); void setData(const VersionFilePtr& data); signals: void typeChanged(); void timeChanged(); void requiresChanged(); private: bool m_providesRecommendations = false; bool m_recommended = false; QString m_name; QString m_uid; QString m_version; QString m_type; qint64 m_time = 0; Meta::RequireSet m_requires; Meta::RequireSet m_conflicts; bool m_volatile = false; VersionFilePtr m_data; }; } // namespace Meta Q_DECLARE_METATYPE(Meta::Version::Ptr) PrismLauncher-11.0.3/launcher/meta/VersionList.cpp0000644000175100017510000002371215224505336021504 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "VersionList.h" #include #include #include "Application.h" #include "Index.h" #include "JsonFormat.h" #include "Version.h" #include "meta/BaseEntity.h" #include "net/Mode.h" #include "tasks/SequentialTask.h" namespace Meta { VersionList::VersionList(const QString& uid, QObject* parent) : BaseVersionList(parent), m_uid(uid) { setObjectName("Version list: " + uid); } Task::Ptr VersionList::getLoadTask(bool forceReload) { auto loadTask = makeShared(tr("Load meta for %1", "This is for the task name that loads the meta index.").arg(m_uid)); loadTask->addTask(APPLICATION->metadataIndex()->loadTask(Net::Mode::Online, forceReload)); loadTask->addTask(this->loadTask(Net::Mode::Online, forceReload)); return loadTask; } bool VersionList::isLoaded() { return BaseEntity::isLoaded(); } const BaseVersion::Ptr VersionList::at(int i) const { return m_versions.at(i); } int VersionList::count() const { return m_versions.size(); } void VersionList::sortVersions() { beginResetModel(); std::sort(m_versions.begin(), m_versions.end(), [](const Version::Ptr& a, const Version::Ptr& b) { return *a.get() < *b.get(); }); endResetModel(); } QVariant VersionList::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= m_versions.size() || index.parent().isValid()) { return QVariant(); } Version::Ptr version = m_versions.at(index.row()); switch (role) { case VersionPointerRole: return QVariant::fromValue(std::dynamic_pointer_cast(version)); case VersionRole: case VersionIdRole: return version->version(); case ParentVersionRole: { // FIXME: HACK: this should be generic and be replaced by something else. Anything that is a hard 'equals' dep is a 'parent // uid'. auto& reqs = version->requiredSet(); auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Require& req) { return req.uid == "net.minecraft"; }); if (iter != reqs.end()) { return (*iter).equalsVersion; } return QVariant(); } case TypeRole: return version->type(); case UidRole: return version->uid(); case TimeRole: return version->time(); case RequiresRole: return QVariant::fromValue(version->requiredSet()); case SortRole: return version->rawTime(); case VersionPtrRole: return QVariant::fromValue(version); case RecommendedRole: return version->isRecommended() || m_externalRecommendsVersions.contains(version->version()); case JavaMajorRole: { auto major = version->version(); if (major.startsWith("java")) { major = "Java " + major.mid(4); } return major; } // FIXME: this should be determined in whatever view/proxy is used... // case LatestRole: return version == getLatestStable(); default: return QVariant(); } } BaseVersionList::RoleList VersionList::providesRoles() const { return m_provided_roles; } void VersionList::setProvidedRoles(RoleList roles) { m_provided_roles = roles; }; QHash VersionList::roleNames() const { QHash roles = BaseVersionList::roleNames(); roles.insert(UidRole, "uid"); roles.insert(TimeRole, "time"); roles.insert(SortRole, "sort"); roles.insert(RequiresRole, "requires"); return roles; } QString VersionList::localFilename() const { return m_uid + "/index.json"; } QString VersionList::humanReadable() const { return m_name.isEmpty() ? m_uid : m_name; } Version::Ptr VersionList::getVersion(const QString& version) { Version::Ptr out = m_lookup.value(version, nullptr); if (!out) { out = std::make_shared(m_uid, version); m_lookup[version] = out; setupAddedVersion(m_versions.size(), out); m_versions.append(out); } return out; } bool VersionList::hasVersion(QString version) const { auto ver = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [version](Meta::Version::Ptr const& a) { return a->version() == version; }); return (ver != m_versions.constEnd()); } void VersionList::setName(const QString& name) { m_name = name; emit nameChanged(name); } void VersionList::setVersions(const QList& versions) { beginResetModel(); m_versions = versions; std::sort(m_versions.begin(), m_versions.end(), [](const Version::Ptr& a, const Version::Ptr& b) { return a->rawTime() > b->rawTime(); }); for (int i = 0; i < m_versions.size(); ++i) { m_lookup.insert(m_versions.at(i)->version(), m_versions.at(i)); setupAddedVersion(i, m_versions.at(i)); } // FIXME: this is dumb, we have 'recommended' as part of the metadata already... auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const Version::Ptr& ptr) { return ptr->type() == "release"; }); m_recommended = recommendedIt == m_versions.constEnd() ? nullptr : *recommendedIt; endResetModel(); } void VersionList::parse(const QJsonObject& obj) { parseVersionList(obj, this); } void VersionList::addExternalRecommends(const QStringList& recommends) { m_externalRecommendsVersions.append(recommends); } void VersionList::clearExternalRecommends() { m_externalRecommendsVersions.clear(); } // FIXME: this is dumb, we have 'recommended' as part of the metadata already... static const Meta::Version::Ptr& getBetterVersion(const Meta::Version::Ptr& a, const Meta::Version::Ptr& b) { if (!a) return b; if (!b) return a; if (a->type() == b->type()) { // newer of same type wins return (a->rawTime() > b->rawTime() ? a : b); } // 'release' type wins return (a->type() == "release" ? a : b); } void VersionList::mergeFromIndex(const VersionList::Ptr& other) { if (m_name != other->m_name) { setName(other->m_name); } if (!other->m_sha256.isEmpty()) { m_sha256 = other->m_sha256; } } void VersionList::merge(const VersionList::Ptr& other) { if (m_name != other->m_name) { setName(other->m_name); } if (!other->m_sha256.isEmpty()) { m_sha256 = other->m_sha256; } // TODO: do not reset the whole model. maybe? beginResetModel(); if (other->m_versions.isEmpty()) { qWarning() << "Empty list loaded ..."; } for (auto version : other->m_versions) { // we already have the version. merge the contents if (m_lookup.contains(version->version())) { auto existing = m_lookup.value(version->version()); existing->mergeFromList(version); version = existing; } else { m_lookup.insert(version->version(), version); // connect it. setupAddedVersion(m_versions.size(), version); m_versions.append(version); } m_recommended = getBetterVersion(m_recommended, version); } endResetModel(); } void VersionList::setupAddedVersion(const int row, const Version::Ptr& version) { disconnect(version.get(), &Version::requiresChanged, this, nullptr); disconnect(version.get(), &Version::timeChanged, this, nullptr); disconnect(version.get(), &Version::typeChanged, this, nullptr); connect(version.get(), &Version::requiresChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QList() << RequiresRole); }); connect(version.get(), &Version::timeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), { TimeRole, SortRole }); }); connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), { TypeRole }); }); } BaseVersion::Ptr VersionList::getRecommended() const { return m_recommended; } void VersionList::waitToLoad() { if (isLoaded()) return; QEventLoop ev; auto task = getLoadTask(); connect(task.get(), &Task::finished, &ev, &QEventLoop::quit); task->start(); ev.exec(); } Version::Ptr VersionList::getRecommendedForParent(const QString& uid, const QString& version) { auto foundExplicit = std::find_if(m_versions.begin(), m_versions.end(), [uid, version](Version::Ptr ver) -> bool { auto& reqs = ver->requiredSet(); auto parentReq = std::find_if(reqs.begin(), reqs.end(), [uid, version](const Require& req) -> bool { return req.uid == uid && req.equalsVersion == version; }); return parentReq != reqs.end() && ver->isRecommended(); }); if (foundExplicit != m_versions.end()) { return *foundExplicit; } return nullptr; } Version::Ptr VersionList::getLatestForParent(const QString& uid, const QString& version) { Version::Ptr latestCompat = nullptr; for (auto ver : m_versions) { auto& reqs = ver->requiredSet(); auto parentReq = std::find_if(reqs.begin(), reqs.end(), [uid, version](const Require& req) -> bool { return req.uid == uid && req.equalsVersion == version; }); if (parentReq != reqs.end()) { latestCompat = getBetterVersion(latestCompat, ver); } } return latestCompat; } } // namespace Meta PrismLauncher-11.0.3/launcher/meta/BaseEntity.h0000644000175100017510000000374615224505336020744 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "net/Mode.h" #include "net/NetJob.h" #include "tasks/Task.h" namespace Meta { class BaseEntityLoadTask; class BaseEntity { friend BaseEntityLoadTask; public: /* types */ using Ptr = std::shared_ptr; enum class LoadStatus { NotLoaded, Local, Remote }; public: virtual ~BaseEntity() = default; virtual QString localFilename() const = 0; virtual QUrl url() const; bool isLoaded() const; LoadStatus status() const; /* for parsers */ void setSha256(QString sha256); virtual void parse(const QJsonObject& obj) = 0; [[nodiscard]] Task::Ptr loadTask(Net::Mode loadType = Net::Mode::Online, bool forceReload = false); protected: QString m_sha256; // the expected sha256 QString m_file_sha256; // the file sha256 private: LoadStatus m_load_status = LoadStatus::NotLoaded; Task::Ptr m_task; }; class BaseEntityLoadTask : public Task { Q_OBJECT public: explicit BaseEntityLoadTask(BaseEntity* parent, Net::Mode mode, bool forceReload); ~BaseEntityLoadTask() override = default; virtual void executeTask() override; virtual bool canAbort() const override; virtual bool abort() override; private: BaseEntity* m_entity; Net::Mode m_mode; bool m_force_reload = false; NetJob::Ptr m_task; }; } // namespace Meta PrismLauncher-11.0.3/launcher/meta/Version.cpp0000644000175100017510000000567515224505336020660 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Version.h" #include #include "JsonFormat.h" Meta::Version::Version(const QString& uid, const QString& version) : BaseVersion(), m_uid(uid), m_version(version) {} QString Meta::Version::descriptor() const { return m_version; } QString Meta::Version::name() const { if (m_data) return m_data->name; return m_uid; } QString Meta::Version::typeString() const { return m_type; } QDateTime Meta::Version::time() const { return QDateTime::fromMSecsSinceEpoch(m_time * 1000, Qt::UTC); } void Meta::Version::parse(const QJsonObject& obj) { parseVersion(obj, this); } void Meta::Version::mergeFromList(const Meta::Version::Ptr& other) { if (other->m_providesRecommendations) { if (m_recommended != other->m_recommended) { setRecommended(other->m_recommended); } } if (m_type != other->m_type) { setType(other->m_type); } if (m_time != other->m_time) { setTime(other->m_time); } if (m_requires != other->m_requires) { m_requires = other->m_requires; } if (m_conflicts != other->m_conflicts) { m_conflicts = other->m_conflicts; } if (m_volatile != other->m_volatile) { setVolatile(other->m_volatile); } if (!other->m_sha256.isEmpty()) { m_sha256 = other->m_sha256; } } void Meta::Version::merge(const Version::Ptr& other) { mergeFromList(other); if (other->m_data) { setData(other->m_data); } } QString Meta::Version::localFilename() const { return m_uid + '/' + m_version + ".json"; } ::Version Meta::Version::toComparableVersion() const { return { descriptor() }; } void Meta::Version::setType(const QString& type) { m_type = type; emit typeChanged(); } void Meta::Version::setTime(const qint64 time) { m_time = time; emit timeChanged(); } void Meta::Version::setRequires(const Meta::RequireSet& reqs, const Meta::RequireSet& conflicts) { m_requires = reqs; m_conflicts = conflicts; emit requiresChanged(); } void Meta::Version::setVolatile(bool volatile_) { m_volatile = volatile_; } void Meta::Version::setData(const VersionFilePtr& data) { m_data = data; } void Meta::Version::setProvidesRecommendations() { m_providesRecommendations = true; } void Meta::Version::setRecommended(bool recommended) { m_recommended = recommended; } PrismLauncher-11.0.3/launcher/console/0000755000175100017510000000000015224505336017226 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/console/WindowsConsole.h0000644000175100017510000000231515224505336022355 0ustar runnerrunner// // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include namespace console { void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr); bool AttachWindowsConsole(); std::error_code EnableAnsiSupport(); void FreeWindowsConsole(); class WindowsConsoleGuard { public: WindowsConsoleGuard(); ~WindowsConsoleGuard(); private: bool m_consoleAttached; }; } // namespace console PrismLauncher-11.0.3/launcher/console/WindowsConsole.cpp0000644000175100017510000001470615224505336022717 0ustar runnerrunner/* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "WindowsConsole.h" #include #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include #include #include #include namespace console { void RedirectHandle(DWORD handle, FILE* stream, const char* mode) { HANDLE stdHandle = GetStdHandle(handle); if (stdHandle != INVALID_HANDLE_VALUE) { int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); if (fileDescriptor != -1) { FILE* file = _fdopen(fileDescriptor, mode); if (file != NULL) { int dup2Result = _dup2(_fileno(file), _fileno(stream)); if (dup2Result == 0) { setvbuf(stream, NULL, _IONBF, 0); } } } } } // taken from https://stackoverflow.com/a/25927081 // getting a proper output to console with redirection support on windows is apparently hell void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) { // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target // using the "_dup2" function. if (bindStdIn) { FILE* dummyFile; freopen_s(&dummyFile, "nul", "r", stdin); } if (bindStdOut) { FILE* dummyFile; freopen_s(&dummyFile, "nul", "w", stdout); } if (bindStdErr) { FILE* dummyFile; freopen_s(&dummyFile, "nul", "w", stderr); } // Redirect unbuffered stdin from the current standard input handle if (bindStdIn) { RedirectHandle(STD_INPUT_HANDLE, stdin, "r"); } // Redirect unbuffered stdout to the current standard output handle if (bindStdOut) { RedirectHandle(STD_OUTPUT_HANDLE, stdout, "w"); } // Redirect unbuffered stderr to the current standard error handle if (bindStdErr) { RedirectHandle(STD_ERROR_HANDLE, stderr, "w"); } // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything // has been read from or written to the targets or not. if (bindStdIn) { std::wcin.clear(); std::cin.clear(); } if (bindStdOut) { std::wcout.clear(); std::cout.clear(); } if (bindStdErr) { std::wcerr.clear(); std::cerr.clear(); } } bool AttachWindowsConsole() { auto stdinType = GetFileType(GetStdHandle(STD_INPUT_HANDLE)); auto stdoutType = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)); auto stderrType = GetFileType(GetStdHandle(STD_ERROR_HANDLE)); bool bindStdIn = false; bool bindStdOut = false; bool bindStdErr = false; if (stdinType == FILE_TYPE_CHAR || stdinType == FILE_TYPE_UNKNOWN) { bindStdIn = true; } if (stdoutType == FILE_TYPE_CHAR || stdoutType == FILE_TYPE_UNKNOWN) { bindStdOut = true; } if (stderrType == FILE_TYPE_CHAR || stderrType == FILE_TYPE_UNKNOWN) { bindStdErr = true; } if (AttachConsole(ATTACH_PARENT_PROCESS)) { BindCrtHandlesToStdHandles(bindStdIn, bindStdOut, bindStdErr); return true; } return false; } std::error_code EnableAnsiSupport() { // ref: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew // Using `CreateFileW("CONOUT$", ...)` to retrieve the console handle works correctly even if STDOUT and/or STDERR are redirected HANDLE console_handle = CreateFileW(L"CONOUT$", FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); if (console_handle == INVALID_HANDLE_VALUE) { return std::error_code(GetLastError(), std::system_category()); } // ref: https://docs.microsoft.com/en-us/windows/console/getconsolemode DWORD console_mode; if (0 == GetConsoleMode(console_handle, &console_mode)) { return std::error_code(GetLastError(), std::system_category()); } // VT processing not already enabled? if ((console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0) { // https://docs.microsoft.com/en-us/windows/console/setconsolemode if (0 == SetConsoleMode(console_handle, console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) { return std::error_code(GetLastError(), std::system_category()); } } return {}; } void FreeWindowsConsole() { fclose(stdout); fclose(stdin); fclose(stderr); FreeConsole(); } WindowsConsoleGuard::WindowsConsoleGuard() : m_consoleAttached(false) { if (console::AttachWindowsConsole()) { m_consoleAttached = true; if (auto err = console::EnableAnsiSupport(); err) { std::cout << "Error setting up ansi console" << err.message() << std::endl; } } } WindowsConsoleGuard::~WindowsConsoleGuard() { // Detach from Windows console if (m_consoleAttached) { console::FreeWindowsConsole(); } } } // namespace console PrismLauncher-11.0.3/launcher/console/Console.h0000644000175100017510000000107515224505336021004 0ustar runnerrunner#pragma once #include #include #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #else #include #include #endif namespace console { inline bool isConsole() { #if defined Q_OS_WIN32 DWORD procIDs[2]; DWORD maxCount = 2; DWORD result = GetConsoleProcessList((LPDWORD)procIDs, maxCount); return result > 1; #else if (isatty(fileno(stdout))) { return true; } return false; #endif } } // namespace console PrismLauncher-11.0.3/launcher/BaseVersionList.h0000644000175100017510000001047215224505336021015 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include "BaseVersion.h" #include "QObjectPtr.h" #include "tasks/Task.h" /*! * \brief Class that each instance type's version list derives from. * Version lists are the lists that keep track of the available game versions * for that instance. This list will not be loaded on startup. It will be loaded * when the list's load function is called. Before using the version list, you * should check to see if it has been loaded yet and if not, load the list. * * Note that this class also inherits from QAbstractListModel. Methods from that * class determine how this version list shows up in a list view. Said methods * all have a default implementation, but they can be overridden by plugins to * change the behavior of the list. */ class BaseVersionList : public QAbstractListModel { Q_OBJECT public: enum ModelRoles { VersionPointerRole = Qt::UserRole, VersionRole, VersionIdRole, ParentVersionRole, RecommendedRole, LatestRole, TypeRole, BranchRole, PathRole, JavaNameRole, JavaMajorRole, CPUArchitectureRole, SortRole }; using RoleList = QList; explicit BaseVersionList(QObject* parent = 0); /*! * \brief Gets a task that will reload the version list. * Simply execute the task to load the list. * The task returned by this function should reset the model when it's done. * \return A pointer to a task that reloads the version list. */ virtual Task::Ptr getLoadTask(bool forceReload = false) = 0; //! Checks whether or not the list is loaded. If this returns false, the list should be // loaded. virtual bool isLoaded() = 0; //! Gets the version at the given index. virtual const BaseVersion::Ptr at(int i) const = 0; //! Returns the number of versions in the list. virtual int count() const = 0; //////// List Model Functions //////// QVariant data(const QModelIndex& index, int role) const override; int rowCount(const QModelIndex& parent) const override; int columnCount(const QModelIndex& parent) const override; QHash roleNames() const override; //! which roles are provided by this version list? virtual RoleList providesRoles() const; /*! * \brief Finds a version by its descriptor. * \param descriptor The descriptor of the version to find. * \return A const pointer to the version with the given descriptor. NULL if * one doesn't exist. */ virtual BaseVersion::Ptr findVersion(const QString& descriptor); /*! * \brief Gets the recommended version from this list * If the list doesn't support recommended versions, this works exactly as getLatestStable */ virtual BaseVersion::Ptr getRecommended() const; /*! * Sorts the version list. */ virtual void sortVersions() = 0; protected slots: /*! * Updates this list with the given list of versions. * This is done by copying each version in the given list and inserting it * into this one. * We need to do this so that we can set the parents of the versions are set to this * version list. This can't be done in the load task, because the versions the load * task creates are on the load task's thread and Qt won't allow their parents * to be set to something created on another thread. * To get around that problem, we invoke this method on the GUI thread, which * then copies the versions and sets their parents correctly. * \param versions List of versions whose parents should be set. */ virtual void updateListData(QList versions) = 0; }; PrismLauncher-11.0.3/launcher/InstanceList.h0000644000175100017510000001631115224505336020337 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include "BaseInstance.h" class QFileSystemWatcher; class InstanceTask; struct InstanceName; using InstanceId = QString; using GroupId = QString; using InstanceLocator = std::pair; enum class InstCreateError { NoCreateError = 0, NoSuchVersion, UnknownCreateError, InstExists, CantCreateDir }; enum class GroupsState { NotLoaded, Steady, Dirty }; struct TrashShortcutItem { ShortcutData data; QString trashPath; }; struct TrashHistoryItem { QString id; QString path; QString trashPath; QString groupName; QList shortcuts; }; class InstanceList : public QAbstractListModel { Q_OBJECT public: explicit InstanceList(SettingsObject* settings, const QString& instDir, QObject* parent = 0); virtual ~InstanceList(); public: QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; bool setData(const QModelIndex& index, const QVariant& value, int role) override; enum AdditionalRoles { GroupRole = Qt::UserRole, InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance InstanceIDRole = 0x34B1CB49 ///< Return id if the instance }; /*! * \brief Error codes returned by functions in the InstanceList class. * NoError Indicates that no error occurred. * UnknownError indicates that an unspecified error occurred. */ enum InstListError { NoError = 0, UnknownError }; BaseInstance* at(int i) const { return m_instances.at(i).get(); } int count() const { return static_cast(m_instances.size()); } InstListError loadList(); void saveNow(); /* O(n) */ BaseInstance* getInstanceById(QString id) const; /* O(n) */ BaseInstance* getInstanceByManagedName(const QString& managed_name) const; QModelIndex getInstanceIndexById(const QString& id) const; QStringList getGroups(); bool isGroupCollapsed(const QString& groupName); GroupId getInstanceGroup(const InstanceId& id) const; void setInstanceGroup(const InstanceId& id, GroupId name); void deleteGroup(const GroupId& name); void renameGroup(const GroupId& src, const GroupId& dst); bool trashInstance(const InstanceId& id); bool trashedSomething() const; bool undoTrashInstance(); void deleteInstance(const InstanceId& id); // Wrap an instance creation task in some more task machinery and make it ready to be used Task* wrapInstanceTask(InstanceTask* task); /** * Create a new empty staging area for instance creation and @return a path/key top commit it later. * Used by instance manipulation tasks. */ QString getStagedInstancePath(); /** * Commit the staging area given by @keyPath to the provider - used when creation succeeds. * Used by instance manipulation tasks. * should_override is used when another similar instance already exists, and we want to override it * - for instance, when updating it. */ bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, QString groupName, const InstanceTask&); /** * Destroy a previously created staging area given by @keyPath - used when creation fails. * Used by instance manipulation tasks. */ bool destroyStagingPath(const QString& keyPath); int getTotalPlayTime(); Qt::DropActions supportedDragActions() const override; Qt::DropActions supportedDropActions() const override; bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const override; bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; QStringList mimeTypes() const override; QMimeData* mimeData(const QModelIndexList& indexes) const override; QStringList getLinkedInstancesById(const QString& id) const; signals: void dataIsInvalid(); void instancesChanged(); void instanceSelectRequest(QString instanceId); void groupsChanged(QSet groups); public slots: void on_InstFolderChanged(const Setting& setting, QVariant value); void on_GroupStateChanged(const QString& group, bool collapsed); private slots: void propertiesChanged(BaseInstance* inst); void providerUpdated(); void instanceDirContentsChanged(const QString& path); private: int getInstIndex(BaseInstance* inst) const; void updateTotalPlayTime(); void suspendWatch(); void resumeWatch(); void add(std::vector>& list); void loadGroupList(); void saveGroupList(); QList discoverInstances(); std::unique_ptr loadInstance(const InstanceId& id); void increaseGroupCount(const QString& group); void decreaseGroupCount(const QString& group); private: int m_watchLevel = 0; int totalPlayTime = 0; bool m_dirty = false; std::vector> m_instances; // id -> refs QMap m_groupNameCache; SettingsObject* m_globalSettings; QString m_instDir; QFileSystemWatcher* m_watcher; // FIXME: this is so inefficient that looking at it is almost painful. QSet m_collapsedGroups; QMap m_instanceGroupIndex; QSet instanceSet; bool m_groupsLoaded = false; bool m_instancesProbed = false; QStack m_trashHistory; }; PrismLauncher-11.0.3/launcher/VersionProxyModel.cpp0000644000175100017510000003562415224505336021752 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "VersionProxyModel.h" #include #include #include #include #include class VersionFilterModel : public QSortFilterProxyModel { Q_OBJECT public: VersionFilterModel(VersionProxyModel* parent) : QSortFilterProxyModel(parent) { m_parent = parent; setSortRole(BaseVersionList::SortRole); sort(0, Qt::DescendingOrder); } bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { const auto& filters = m_parent->filters(); const QString& search = m_parent->search(); const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive)) return false; for (auto it = filters.begin(); it != filters.end(); ++it) { auto data = sourceModel()->data(idx, it.key()); auto match = data.toString(); if (!it.value()(match)) { return false; } } return true; } void filterChanged() { invalidateFilter(); } private: VersionProxyModel* m_parent; }; VersionProxyModel::VersionProxyModel(QObject* parent) : QAbstractProxyModel(parent) { filterModel = new VersionFilterModel(this); connect(filterModel, &QAbstractItemModel::dataChanged, this, &VersionProxyModel::sourceDataChanged); connect(filterModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &VersionProxyModel::sourceRowsAboutToBeInserted); connect(filterModel, &QAbstractItemModel::rowsInserted, this, &VersionProxyModel::sourceRowsInserted); connect(filterModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &VersionProxyModel::sourceRowsAboutToBeRemoved); connect(filterModel, &QAbstractItemModel::rowsRemoved, this, &VersionProxyModel::sourceRowsRemoved); // FIXME: implement when needed /* connect(replacing, &QAbstractItemModel::rowsAboutToBeMoved, this, &VersionProxyModel::sourceRowsAboutToBeMoved); connect(replacing, &QAbstractItemModel::rowsMoved, this, &VersionProxyModel::sourceRowsMoved); connect(replacing, &QAbstractItemModel::layoutAboutToBeChanged, this, &VersionProxyModel::sourceLayoutAboutToBeChanged); connect(replacing, &QAbstractItemModel::layoutChanged, this, &VersionProxyModel::sourceLayoutChanged); */ connect(filterModel, &QAbstractItemModel::modelAboutToBeReset, this, &VersionProxyModel::sourceAboutToBeReset); connect(filterModel, &QAbstractItemModel::modelReset, this, &VersionProxyModel::sourceReset); QAbstractProxyModel::setSourceModel(filterModel); } QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, int role) const { if (section < 0 || section >= m_columns.size()) return QVariant(); if (orientation != Qt::Horizontal) return QVariant(); auto column = m_columns[section]; if (role == Qt::DisplayRole) { switch (column) { case Name: return tr("Version"); case ParentVersion: return tr("Minecraft"); // FIXME: this should come from metadata case Branch: return tr("Branch"); case Type: return tr("Type"); case CPUArchitecture: return tr("Architecture"); case Path: return tr("Path"); case JavaName: return tr("Java Name"); case JavaMajor: return tr("Major Version"); case Time: return tr("Released"); } } else if (role == Qt::ToolTipRole) { switch (column) { case Name: return tr("The name of the version."); case ParentVersion: return tr("Minecraft version"); // FIXME: this should come from metadata case Branch: return tr("The version's branch"); case Type: return tr("The version's type"); case CPUArchitecture: return tr("CPU Architecture"); case Path: return tr("Filesystem path to this version"); case JavaName: return tr("The alternative name of the Java version"); case JavaMajor: return tr("The Java major version"); case Time: return tr("Release date of this version"); } } return QVariant(); } QVariant VersionProxyModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } auto column = m_columns[index.column()]; auto parentIndex = mapToSource(index); switch (role) { case Qt::DisplayRole: { switch (column) { case Name: { QString version = sourceModel()->data(parentIndex, BaseVersionList::VersionRole).toString(); if (version == m_currentVersion) { return tr("%1 (installed)").arg(version); } return version; } case ParentVersion: return sourceModel()->data(parentIndex, BaseVersionList::ParentVersionRole); case Branch: return sourceModel()->data(parentIndex, BaseVersionList::BranchRole); case Type: return sourceModel()->data(parentIndex, BaseVersionList::TypeRole); case CPUArchitecture: return sourceModel()->data(parentIndex, BaseVersionList::CPUArchitectureRole); case Path: return sourceModel()->data(parentIndex, BaseVersionList::PathRole); case JavaName: return sourceModel()->data(parentIndex, BaseVersionList::JavaNameRole); case JavaMajor: return sourceModel()->data(parentIndex, BaseVersionList::JavaMajorRole); case Time: return sourceModel()->data(parentIndex, Meta::VersionList::TimeRole).toDate(); default: return QVariant(); } } case Qt::ToolTipRole: { if (column == Name && hasRecommended) { auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); if (value.toBool()) { return tr("Recommended"); } else if (hasLatest) { auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); if (latest.toBool()) { return tr("Latest"); } } } return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole); } case Qt::DecorationRole: { if (column == Name && hasRecommended) { auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole); if (recommenced.toBool()) { return QIcon::fromTheme("star"); } else if (hasLatest) { auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole); if (latest.toBool()) { return QIcon::fromTheme("bug"); } } QPixmap pixmap; QPixmapCache::find("placeholder", &pixmap); if (!pixmap) { QPixmap px(16, 16); px.fill(Qt::transparent); QPixmapCache::insert("placeholder", px); return px; } return pixmap; } return QVariant(); } default: { if (roles.contains((BaseVersionList::ModelRoles)role)) { return sourceModel()->data(parentIndex, role); } return QVariant(); } } } QModelIndex VersionProxyModel::parent([[maybe_unused]] const QModelIndex& child) const { return QModelIndex(); } QModelIndex VersionProxyModel::mapFromSource(const QModelIndex& sourceIndex) const { if (sourceIndex.isValid()) { return index(sourceIndex.row(), 0); } return QModelIndex(); } QModelIndex VersionProxyModel::mapToSource(const QModelIndex& proxyIndex) const { if (proxyIndex.isValid()) { return sourceModel()->index(proxyIndex.row(), 0); } return QModelIndex(); } QModelIndex VersionProxyModel::index(int row, int column, const QModelIndex& parent) const { // no trees here... shoo if (parent.isValid()) { return QModelIndex(); } if (row < 0 || row >= sourceModel()->rowCount()) return QModelIndex(); if (column < 0 || column >= columnCount()) return QModelIndex(); return QAbstractItemModel::createIndex(row, column); } int VersionProxyModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_columns.size(); } int VersionProxyModel::rowCount(const QModelIndex& parent) const { if (sourceModel()) { return sourceModel()->rowCount(parent); } return 0; } void VersionProxyModel::sourceDataChanged(const QModelIndex& source_top_left, const QModelIndex& source_bottom_right) { if (source_top_left.parent() != source_bottom_right.parent()) return; // whole row is getting changed auto topLeft = createIndex(source_top_left.row(), 0); auto bottomRight = createIndex(source_bottom_right.row(), columnCount() - 1); emit dataChanged(topLeft, bottomRight); } void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw) { auto replacing = dynamic_cast(replacingRaw); m_columns.clear(); if (!replacing) { roles.clear(); filterModel->setSourceModel(replacing); return; } roles = replacing->providesRoles(); if (roles.contains(BaseVersionList::VersionRole)) { m_columns.push_back(Name); } /* if(roles.contains(BaseVersionList::ParentVersionRole)) { m_columns.push_back(ParentVersion); } */ if (roles.contains(BaseVersionList::CPUArchitectureRole)) { m_columns.push_back(CPUArchitecture); } if (roles.contains(BaseVersionList::PathRole)) { m_columns.push_back(Path); } if (roles.contains(BaseVersionList::JavaNameRole)) { m_columns.push_back(JavaName); } if (roles.contains(BaseVersionList::JavaMajorRole)) { m_columns.push_back(JavaMajor); } if (roles.contains(Meta::VersionList::TimeRole)) { m_columns.push_back(Time); } if (roles.contains(BaseVersionList::BranchRole)) { m_columns.push_back(Branch); } if (roles.contains(BaseVersionList::TypeRole)) { m_columns.push_back(Type); } if (roles.contains(BaseVersionList::RecommendedRole)) { hasRecommended = true; } if (roles.contains(BaseVersionList::LatestRole)) { hasLatest = true; } filterModel->setSourceModel(replacing); } QModelIndex VersionProxyModel::getRecommended() const { if (!roles.contains(BaseVersionList::RecommendedRole)) { return index(0, 0); } int recommended = 0; for (int i = 0; i < rowCount(); i++) { auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::RecommendedRole); if (value.toBool()) { recommended = i; } } return index(recommended, 0); } QModelIndex VersionProxyModel::getVersion(const QString& version) const { int found = -1; for (int i = 0; i < rowCount(); i++) { auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::VersionRole); if (value.toString() == version) { found = i; } } if (found == -1) { return QModelIndex(); } return index(found, 0); } void VersionProxyModel::clearFilters() { m_filters.clear(); m_search.clear(); filterModel->filterChanged(); } void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filter f) { m_filters[column] = std::move(f); filterModel->filterChanged(); } void VersionProxyModel::setSearch(const QString& search) { m_search = search; filterModel->filterChanged(); } const VersionProxyModel::FilterMap& VersionProxyModel::filters() const { return m_filters; } const QString& VersionProxyModel::search() const { return m_search; } void VersionProxyModel::sourceAboutToBeReset() { beginResetModel(); } void VersionProxyModel::sourceReset() { endResetModel(); } void VersionProxyModel::sourceRowsAboutToBeInserted(const QModelIndex& parent, int first, int last) { beginInsertRows(parent, first, last); } void VersionProxyModel::sourceRowsInserted([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int first, [[maybe_unused]] int last) { endInsertRows(); } void VersionProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last) { beginRemoveRows(parent, first, last); } void VersionProxyModel::sourceRowsRemoved([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int first, [[maybe_unused]] int last) { endRemoveRows(); } void VersionProxyModel::setCurrentVersion(const QString& version) { m_currentVersion = version; } #include "VersionProxyModel.moc" PrismLauncher-11.0.3/launcher/minecraft/0000755000175100017510000000000015224505336017534 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/minecraft/MinecraftInstance.h0000644000175100017510000001415415224505336023307 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include "BaseInstance.h" #include "minecraft/launch/MinecraftTarget.h" #include "minecraft/mod/Mod.h" class ModFolderModel; class ResourceFolderModel; class ResourcePackFolderModel; class ShaderPackFolderModel; class TexturePackFolderModel; class WorldList; class LaunchStep; class LaunchProfile; class PackProfile; class MinecraftInstance : public BaseInstance { Q_OBJECT public: MinecraftInstance(SettingsObject* globalSettings, std::unique_ptr settings, const QString& rootDir); virtual ~MinecraftInstance(); virtual void saveNow() override; void loadSpecificSettings() override; // FIXME: remove QString typeName() const override; // FIXME: remove QSet traits() const override; bool canEdit() const override { return true; } bool canExport() const override { return true; } void populateLaunchMenu(QMenu* menu) override; ////// Directories and files ////// QString jarModsDir() const; QString resourcePacksDir() const; QString texturePacksDir() const; QString shaderPacksDir() const; QString modsRoot() const override; QString coreModsDir() const; QString nilModsDir() const; QString dataPacksDir(); QString modsCacheLocation() const; QString libDir() const; QString worldDir() const; QString resourcesDir() const; QDir jarmodsPath() const; QDir librariesPath() const; QDir versionsPath() const; QString instanceConfigFolder() const override; // Path to the instance's minecraft directory. QString gameRoot() const override; // Path to the instance's minecraft bin directory. QString binRoot() const; // where to put the natives during/before launch QString getNativePath() const; // where the instance-local libraries should be QString getLocalLibraryPath() const; /** Returns whether the instance, with its version, has support for demo mode. */ bool supportsDemo() const; void updateRuntimeContext() override; ////// Profile management ////// PackProfile* getPackProfile() const; ////// Mod Lists ////// ModFolderModel* loaderModList(); ModFolderModel* coreModList(); ModFolderModel* nilModList(); ResourcePackFolderModel* resourcePackList(); TexturePackFolderModel* texturePackList(); ShaderPackFolderModel* shaderPackList(); DataPackFolderModel* dataPackList(); QList resourceLists(); WorldList* worldList(); ////// Launch stuff ////// QList createUpdateTask() override; LaunchTask* createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) override; QStringList extraArguments() override; QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) override; QList getJarMods() const; QString createLaunchScript(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin); /// get arguments passed to java QStringList javaArguments(); QString getLauncher(); bool shouldApplyOnlineFixes(); /// get variables for launch command variable substitution/environment QMap getVariables() override; /// create an environment for launching processes QProcessEnvironment createEnvironment() override; QProcessEnvironment createLaunchEnvironment() override; QStringList getLogFileSearchPaths() override; QString getStatusbarDescription() override; // FIXME: remove virtual QStringList getClassPath(); // FIXME: remove virtual QStringList getNativeJars(); // FIXME: remove virtual QString getMainClass() const; // FIXME: remove virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) const; virtual JavaVersion getJavaVersion(); protected: QMap createCensorFilterFromSession(AuthSessionPtr session); QMap makeProfileVarMapping(std::shared_ptr profile) const; protected: // data std::unique_ptr m_components; std::unique_ptr m_loader_mod_list; std::unique_ptr m_core_mod_list; std::unique_ptr m_nil_mod_list; std::unique_ptr m_resource_pack_list; std::unique_ptr m_shader_pack_list; std::unique_ptr m_texture_pack_list; std::unique_ptr m_data_pack_list; std::unique_ptr m_world_list; }; PrismLauncher-11.0.3/launcher/minecraft/Rule.cpp0000644000175100017510000000521615224505336021153 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2025 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "Rule.h" Rule Rule::fromJson(const QJsonObject& object) { Rule result; if (object["action"] == "allow") result.m_action = Allow; else if (object["action"] == "disallow") result.m_action = Disallow; if (auto os = object["os"]; os.isObject()) { if (auto name = os["name"].toString(); !name.isNull()) { result.m_os = OS{ name, os["version"].toString(), }; } } return result; } QJsonObject Rule::toJson() { QJsonObject result; if (m_action == Allow) result["action"] = "allow"; else if (m_action == Disallow) result["action"] = "disallow"; if (m_os.has_value()) { QJsonObject os; os["name"] = m_os->name; if (!m_os->version.isEmpty()) os["version"] = m_os->version; result["os"] = os; } return result; } Rule::Action Rule::apply(const RuntimeContext& runtimeContext) { if (m_os.has_value() && !runtimeContext.classifierMatches(m_os->name)) return Defer; return m_action; } PrismLauncher-11.0.3/launcher/minecraft/auth/0000755000175100017510000000000015224505336020475 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/minecraft/auth/MinecraftAccount.h0000644000175100017510000001205715224505336024100 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include "AccountData.h" #include "AuthSession.h" #include "QObjectPtr.h" #include "Usable.h" #include "minecraft/auth/AuthFlow.h" class Task; class MinecraftAccount; using MinecraftAccountPtr = shared_qobject_ptr; Q_DECLARE_METATYPE(MinecraftAccountPtr) /** * A profile within someone's Mojang account. * * Currently, the profile system has not been implemented by Mojang yet, * but we might as well add some things for it in Prism Launcher right now so * we don't have to rip the code to pieces to add it later. */ struct AccountProfile { QString id; QString name; bool legacy; }; /** * Object that stores information about a certain Mojang account. * * Said information may include things such as that account's username, client token, and access * token if the user chose to stay logged in. */ class MinecraftAccount : public QObject, public Usable { Q_OBJECT public: /* construction */ //! Do not copy accounts. ever. explicit MinecraftAccount(const MinecraftAccount& other, QObject* parent) = delete; //! Default constructor explicit MinecraftAccount(QObject* parent = 0); static MinecraftAccountPtr createBlankMSA(); static MinecraftAccountPtr createOffline(const QString& username); static MinecraftAccountPtr loadFromJsonV3(const QJsonObject& json); static QUuid uuidFromUsername(QString username); //! Saves a MinecraftAccount to a JSON object and returns it. QJsonObject saveToJson() const; public: /* manipulation */ shared_qobject_ptr login(bool useDeviceCode = false); shared_qobject_ptr refresh(); shared_qobject_ptr currentTask(); public: /* queries */ QString internalId() const { return data.internalId; } QString accessToken() const { return data.accessToken(); } QString profileId() const { return data.profileId(); } QString profileName() const { return data.profileName(); } QString displayName() const; bool isActive() const; AccountType accountType() const noexcept { return data.type; } bool ownsMinecraft() const { return data.type != AccountType::Offline && data.minecraftEntitlement.ownsMinecraft; } bool hasProfile() const { return data.profileId().size() != 0; } QString typeString() const { switch (data.type) { case AccountType::MSA: { return "msa"; } break; case AccountType::Offline: { return "offline"; } break; default: { return "unknown"; } } } QPixmap getFace(int width = 64, int height = 64) const; //! Returns the current state of the account AccountState accountState() const; AccountData* accountData() { return &data; } bool shouldRefresh() const; void fillSession(AuthSessionPtr session); QString lastError() const { return data.lastError(); } signals: /** * This signal is emitted when the account changes */ void changed(); void activityChanged(bool active); // TODO: better signalling for the various possible state changes - especially errors protected: /* variables */ AccountData data; // current task we are executing here shared_qobject_ptr m_currentTask; protected: /* methods */ void incrementUses() override; void decrementUses() override; private slots: void authSucceeded(); void authFailed(QString reason); }; PrismLauncher-11.0.3/launcher/minecraft/auth/steps/0000755000175100017510000000000015224505336021633 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp0000644000175100017510000001747115224505336026706 0ustar runnerrunner#include "XboxAuthorizationStep.h" #include #include #include #include "Application.h" #include "Logging.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" #include "net/RawHeaderProxy.h" #include "net/Upload.h" XboxAuthorizationStep::XboxAuthorizationStep(AccountData* data, Token* token, QString relyingParty, QString authorizationKind) : AuthStep(data), m_token(token), m_relyingParty(std::move(relyingParty)), m_authorizationKind(std::move(authorizationKind)) {} QString XboxAuthorizationStep::describe() { return tr("Getting authorization to access %1 services.").arg(m_authorizationKind); } void XboxAuthorizationStep::perform() { const QString xboxAuthTemplate = R"XXX( { "Properties": { "SandboxId": "RETAIL", "UserTokens": [ "%1" ] }, "RelyingParty": "%2", "TokenType": "JWT" } )XXX"; const auto xboxAuthData = xboxAuthTemplate.arg(m_data->userToken.token, m_relyingParty); // http://xboxlive.com const QUrl url("https://xsts.auth.xboxlive.com/xsts/authorize"); auto headers = QList{ { .headerName = "Content-Type", .headerValue = "application/json" }, { .headerName = "Accept", .headerValue = "application/json" }, { .headerName = "x-xbl-contract-version", .headerValue = "1" } }; auto [request, response] = Net::Upload::makeByteArray(url, xboxAuthData.toUtf8()); m_request = request; m_request->addHeaderProxy(std::make_unique(headers)); m_request->enableAutoRetry(true); m_task.reset(new NetJob("XboxAuthorizationStep", APPLICATION->network())); m_task->setAskRetry(false); m_task->addNetAction(m_request); connect(m_task.get(), &Task::finished, this, [this, response] { onRequestDone(response); }); m_task->start(); qDebug() << "Getting authorization token for" << m_relyingParty; } void XboxAuthorizationStep::onRequestDone(QByteArray* response) { qCDebug(authCredentials()) << *response; if (m_request->error() != QNetworkReply::NoError) { qWarning() << "Reply error:" << m_request->error(); if (Net::isApplicationError(m_request->error()) && !Net::isServerError(m_request->error())) { if (processSTSError(*response)) { return; } emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Unknown STS error for %1 services: %2").arg(m_authorizationKind, m_request->errorString())); } else { m_data->networkError = m_request->error(); emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to get authorization for %1 services: %2").arg(m_authorizationKind, m_request->errorString())); } return; } Token temp; if (!Parsers::parseXTokenResponse(*response, temp, m_authorizationKind)) { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Could not parse authorization response for access to %1 services.").arg(m_authorizationKind)); return; } if (temp.extra["uhs"] != m_data->userToken.extra["uhs"]) { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Server has changed %1 authorization user hash in the reply. Something is wrong.").arg(m_authorizationKind)); return; } auto& token = *m_token; token = temp; emit finished(AccountTaskState::STATE_WORKING, tr("Got authorization to access %1").arg(m_relyingParty)); } bool XboxAuthorizationStep::processSTSError(const QByteArray& response) { if (m_request->error() == QNetworkReply::AuthenticationRequiredError) { QJsonParseError jsonError; const QJsonDocument doc = QJsonDocument::fromJson(response, &jsonError); if (jsonError.error != QJsonParseError::NoError) { qWarning() << "Cannot parse error XSTS response as JSON:" << jsonError.errorString(); emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Cannot parse %1 authorization error response as JSON: %2").arg(m_authorizationKind, jsonError.errorString())); return true; } int64_t errorCode = -1; auto obj = doc.object(); if (!Parsers::getNumber(obj.value("XErr"), errorCode)) { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XErr element is missing from %1 authorization error response.").arg(m_authorizationKind)); return true; } switch (errorCode) { case 2148916233: { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account does not have an Xbox Live profile. Buy the game on %1 first.") .arg("minecraft.net")); return true; } case 2148916235: { // NOTE: this is the Grulovia error emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Xbox Live is not available in your country. You've been blocked.")); return true; } case 2148916238: { emit finished( AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account is underaged and is not linked to a family.\n\nPlease set up your account according to %1.") .arg("help.minecraft.net")); return true; } // the following codes where copied from: https://github.com/PrismarineJS/prismarine-auth/pull/44 case 2148916236: { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account requires proof of age to play. Please login to %1 to provide proof of age.") .arg("login.live.com")); return true; } case 2148916237: emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account has reached its limit for playtime. This " "Microsoft account has been blocked from logging in.")); return true; case 2148916227: { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account was banned by Xbox for violating one or more " "Community Standards for Xbox and is unable to be used.")); return true; } case 2148916229: { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account is currently restricted and your guardian has not given you permission to play " "online. Login to %1 and have your guardian change your permissions.") .arg("account.microsoft.com")); return true; } case 2148916234: { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("This Microsoft account has not accepted Xbox's Terms of Service. Please login and accept them.")); return true; } default: { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XSTS authentication ended with unrecognized error(s):\n\n%1").arg(errorCode)); return true; } } } return false; } PrismLauncher-11.0.3/launcher/minecraft/auth/steps/MinecraftProfileStep.h0000644000175100017510000000077215224505336026077 0ustar runnerrunner#pragma once #include #include "minecraft/auth/AuthStep.h" #include "net/Download.h" #include "net/NetJob.h" class MinecraftProfileStep : public AuthStep { Q_OBJECT public: explicit MinecraftProfileStep(AccountData* data); virtual ~MinecraftProfileStep() noexcept = default; void perform() override; QString describe() override; private slots: void onRequestDone(QByteArray* response); private: Net::Download::Ptr m_request; NetJob::Ptr m_task; }; PrismLauncher-11.0.3/launcher/minecraft/auth/steps/GetSkinStep.h0000644000175100017510000000073715224505336024213 0ustar runnerrunner#pragma once #include #include "minecraft/auth/AuthStep.h" #include "net/Download.h" #include "net/NetJob.h" class GetSkinStep : public AuthStep { Q_OBJECT public: explicit GetSkinStep(AccountData* data); virtual ~GetSkinStep() noexcept = default; void perform() override; QString describe() override; private slots: void onRequestDone(QByteArray* response); private: Net::Download::Ptr m_request; NetJob::Ptr m_task; }; PrismLauncher-11.0.3/launcher/minecraft/auth/steps/LauncherLoginStep.cpp0000644000175100017510000000505615224505336025733 0ustar runnerrunner#include "LauncherLoginStep.h" #include #include #include "Application.h" #include "Logging.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" #include "net/RawHeaderProxy.h" #include "net/Upload.h" LauncherLoginStep::LauncherLoginStep(AccountData* data) : AuthStep(data) {} QString LauncherLoginStep::describe() { return tr("Fetching Minecraft access token"); } void LauncherLoginStep::perform() { QUrl url("https://api.minecraftservices.com/launcher/login"); auto uhs = m_data->mojangservicesToken.extra["uhs"].toString(); auto xToken = m_data->mojangservicesToken.token; QString mc_auth_template = R"XXX( { "xtoken": "XBL3.0 x=%1;%2", "platform": "PC_LAUNCHER" } )XXX"; auto requestBody = mc_auth_template.arg(uhs, xToken); auto headers = QList{ { "Content-Type", "application/json" }, { "Accept", "application/json" }, }; auto [request, response] = Net::Upload::makeByteArray(url, requestBody.toUtf8()); m_request = request; m_request->addHeaderProxy(std::make_unique(headers)); m_request->enableAutoRetry(true); m_task.reset(new NetJob("LauncherLoginStep", APPLICATION->network())); m_task->setAskRetry(false); m_task->addNetAction(m_request); connect(m_task.get(), &Task::finished, this, [this, response] { onRequestDone(response); }); m_task->start(); qDebug() << "Getting Minecraft access token..."; } void LauncherLoginStep::onRequestDone(QByteArray* response) { qCDebug(authCredentials()) << *response; if (m_request->error() != QNetworkReply::NoError) { qWarning() << "Reply error:" << m_request->error(); if (Net::isApplicationError(m_request->error()) && !Net::isServerError(m_request->error())) { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to get Minecraft access token: %1").arg(m_request->errorString())); } else { m_data->networkError = m_request->error(); emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to get Minecraft access token: %1").arg(m_request->errorString())); } return; } if (!Parsers::parseMojangResponse(*response, m_data->yggdrasilToken)) { qWarning() << "Could not parse login_with_xbox response..."; emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to parse the Minecraft access token response.")); return; } emit finished(AccountTaskState::STATE_WORKING, tr("Got Minecraft access token")); } PrismLauncher-11.0.3/launcher/minecraft/auth/steps/MSAStep.h0000644000175100017510000000373315224505336023266 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include "minecraft/auth/AuthStep.h" #include class MSAStep : public AuthStep { Q_OBJECT public: explicit MSAStep(AccountData* data, bool silent = false); virtual ~MSAStep() noexcept = default; void perform() override; QString describe() override; signals: void authorizeWithBrowser(const QUrl& url); private: bool m_silent; QString m_clientId; QOAuth2AuthorizationCodeFlow m_oauth2; }; PrismLauncher-11.0.3/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp0000644000175100017510000002427115224505336025534 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MSADeviceCodeStep.h" #include #include #include "Application.h" #include "Json.h" #include "net/RawHeaderProxy.h" // https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-device-code MSADeviceCodeStep::MSADeviceCodeStep(AccountData* data) : AuthStep(data) { m_clientId = APPLICATION->getMSAClientID(); connect(&m_expiration_timer, &QTimer::timeout, this, &MSADeviceCodeStep::abort); connect(&m_pool_timer, &QTimer::timeout, this, &MSADeviceCodeStep::authenticateUser); } QString MSADeviceCodeStep::describe() { return tr("Logging in with Microsoft account(device code)."); } void MSADeviceCodeStep::perform() { QUrlQuery data; data.addQueryItem("client_id", m_clientId); data.addQueryItem("scope", "XboxLive.SignIn XboxLive.offline_access"); auto payload = data.query(QUrl::FullyEncoded).toUtf8(); QUrl url("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"); auto headers = QList{ { "Content-Type", "application/x-www-form-urlencoded" }, { "Accept", "application/json" }, }; auto [request, response] = Net::Upload::makeByteArray(url, payload); m_request = request; m_request->addHeaderProxy(std::make_unique(headers)); m_request->enableAutoRetry(true); m_task.reset(new NetJob("MSADeviceCodeStep", APPLICATION->network())); m_task->setAskRetry(false); m_task->addNetAction(m_request); connect(m_task.get(), &Task::finished, this, [this, response] { deviceAuthorizationFinished(response); }); m_task->start(); } struct DeviceAuthorizationResponse { QString device_code; QString user_code; QString verification_uri; int expires_in; int interval; QString error; QString error_description; }; DeviceAuthorizationResponse parseDeviceAuthorizationResponse(const QByteArray& data) { QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { qWarning() << "Failed to parse device authorization response due to err:" << err.errorString(); return {}; } if (!doc.isObject()) { qWarning() << "Device authorization response is not an object"; return {}; } auto obj = doc.object(); return { obj["device_code"].toString(), obj["user_code"].toString(), obj["verification_uri"].toString(), obj["expires_in"].toInt(), obj["interval"].toInt(), obj["error"].toString(), obj["error_description"].toString(), }; } void MSADeviceCodeStep::deviceAuthorizationFinished(QByteArray* response) { if (!m_request->wasSuccessful() || m_request->error() != QNetworkReply::NoError) { qWarning() << "Device authorization failed:" << m_request->error() << m_request->errorString(); emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Device authorization failed: %1").arg(m_request->errorString())); return; } auto rsp = parseDeviceAuthorizationResponse(*response); if (!rsp.error.isEmpty() || !rsp.error_description.isEmpty()) { qWarning() << "Device authorization failed:" << rsp.error; emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Device authorization failed: %1").arg(rsp.error_description.isEmpty() ? rsp.error : rsp.error_description)); return; } if (rsp.device_code.isEmpty() || rsp.user_code.isEmpty() || rsp.verification_uri.isEmpty() || rsp.expires_in == 0) { qWarning() << "Device authorization failed: required fields missing"; emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Device authorization failed: required fields missing")); return; } if (rsp.interval != 0) { interval = rsp.interval; } m_device_code = rsp.device_code; emit authorizeWithBrowser(rsp.verification_uri, rsp.user_code, rsp.expires_in); m_expiration_timer.setTimerType(Qt::VeryCoarseTimer); m_expiration_timer.setInterval(rsp.expires_in * 1000); m_expiration_timer.setSingleShot(true); m_expiration_timer.start(); m_pool_timer.setTimerType(Qt::VeryCoarseTimer); m_pool_timer.setSingleShot(true); startPoolTimer(); } void MSADeviceCodeStep::abort() { m_expiration_timer.stop(); m_pool_timer.stop(); if (m_request) { m_request->abort(); } m_is_aborted = true; emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Task aborted")); } void MSADeviceCodeStep::startPoolTimer() { if (m_is_aborted) { return; } if (m_expiration_timer.remainingTime() < interval * 1000) { perform(); return; } m_pool_timer.setInterval(interval * 1000); m_pool_timer.start(); } void MSADeviceCodeStep::authenticateUser() { QUrlQuery data; data.addQueryItem("client_id", m_clientId); data.addQueryItem("grant_type", "urn:ietf:params:oauth:grant-type:device_code"); data.addQueryItem("device_code", m_device_code); auto payload = data.query(QUrl::FullyEncoded).toUtf8(); QUrl url("https://login.microsoftonline.com/consumers/oauth2/v2.0/token"); auto headers = QList{ { "Content-Type", "application/x-www-form-urlencoded" }, { "Accept", "application/json" }, }; auto [request, response] = Net::Upload::makeByteArray(url, payload); m_request = request; m_request->addHeaderProxy(std::make_unique(headers)); connect(m_request.get(), &Task::finished, this, [this, response] { authenticationFinished(response); }); m_request->setNetwork(APPLICATION->network()); m_request->start(); } struct AuthenticationResponse { QString access_token; QString token_type; QString refresh_token; int expires_in; QString error; QString error_description; QVariantMap extra; }; AuthenticationResponse parseAuthenticationResponse(const QByteArray& data) { QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { qWarning() << "Failed to parse device authorization response due to err:" << err.errorString(); return {}; } if (!doc.isObject()) { qWarning() << "Device authorization response is not an object"; return {}; } auto obj = doc.object(); return { obj["access_token"].toString(), obj["token_type"].toString(), obj["refresh_token"].toString(), obj["expires_in"].toInt(), obj["error"].toString(), obj["error_description"].toString(), obj.toVariantMap() }; } void MSADeviceCodeStep::authenticationFinished(QByteArray* response) { if (m_request->error() == QNetworkReply::TimeoutError) { // rfc8628#section-3.5 // "On encountering a connection timeout, clients MUST unilaterally // reduce their polling frequency before retrying. The use of an // exponential backoff algorithm to achieve this, such as doubling the // polling interval on each such connection timeout, is RECOMMENDED." interval *= 2; startPoolTimer(); return; } auto rsp = parseAuthenticationResponse(*response); if (rsp.error == "slow_down") { // rfc8628#section-3.5 // "A variant of 'authorization_pending', the authorization request is // still pending and polling should continue, but the interval MUST // be increased by 5 seconds for this and all subsequent requests." interval += 5; startPoolTimer(); return; } if (rsp.error == "authorization_pending") { // keep trying - rfc8628#section-3.5 // "The authorization request is still pending as the end user hasn't // yet completed the user-interaction steps (Section 3.3)." startPoolTimer(); return; } if (!rsp.error.isEmpty() || !rsp.error_description.isEmpty()) { qWarning() << "Device Access failed:" << rsp.error; emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Device Access failed: %1").arg(rsp.error_description.isEmpty() ? rsp.error : rsp.error_description)); return; } if (!m_request->wasSuccessful() || m_request->error() != QNetworkReply::NoError) { startPoolTimer(); // it failed so just try again without increasing the interval return; } m_expiration_timer.stop(); m_data->msaClientID = m_clientId; m_data->msaToken.issueInstant = QDateTime::currentDateTimeUtc(); m_data->msaToken.notAfter = QDateTime::currentDateTime().addSecs(rsp.expires_in); m_data->msaToken.extra = rsp.extra; m_data->msaToken.refresh_token = rsp.refresh_token; m_data->msaToken.token = rsp.access_token; emit finished(AccountTaskState::STATE_WORKING, tr("Got MSA token")); } PrismLauncher-11.0.3/launcher/minecraft/auth/steps/EntitlementsStep.h0000644000175100017510000000102515224505336025311 0ustar runnerrunner#pragma once #include #include "minecraft/auth/AuthStep.h" #include "net/Download.h" #include "net/NetJob.h" class EntitlementsStep : public AuthStep { Q_OBJECT public: explicit EntitlementsStep(AccountData* data); virtual ~EntitlementsStep() noexcept = default; void perform() override; QString describe() override; private slots: void onRequestDone(QByteArray* response); private: QString m_entitlements_request_id; Net::Download::Ptr m_request; NetJob::Ptr m_task; }; PrismLauncher-11.0.3/launcher/minecraft/auth/steps/MSADeviceCodeStep.h0000644000175100017510000000457015224505336025201 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "minecraft/auth/AuthStep.h" #include "net/NetJob.h" #include "net/Upload.h" class MSADeviceCodeStep : public AuthStep { Q_OBJECT public: explicit MSADeviceCodeStep(AccountData* data); virtual ~MSADeviceCodeStep() noexcept = default; void perform() override; QString describe() override; public slots: void abort() override; signals: void authorizeWithBrowser(QString url, QString code, int expiresIn); private slots: void deviceAuthorizationFinished(QByteArray* response); void startPoolTimer(); void authenticateUser(); void authenticationFinished(QByteArray* response); private: QString m_clientId; QString m_device_code; bool m_is_aborted = false; int interval = 5; QTimer m_pool_timer; QTimer m_expiration_timer; Net::Upload::Ptr m_request; NetJob::Ptr m_task; }; PrismLauncher-11.0.3/launcher/minecraft/auth/steps/XboxUserStep.cpp0000644000175100017510000000526515224505336024762 0ustar runnerrunner#include "XboxUserStep.h" #include #include "Application.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" #include "net/RawHeaderProxy.h" XboxUserStep::XboxUserStep(AccountData* data) : AuthStep(data) {} QString XboxUserStep::describe() { return tr("Logging in as an Xbox user."); } void XboxUserStep::perform() { QString xbox_auth_template = R"XXX( { "Properties": { "AuthMethod": "RPS", "SiteName": "user.auth.xboxlive.com", "RpsTicket": "d=%1" }, "RelyingParty": "http://auth.xboxlive.com", "TokenType": "JWT" } )XXX"; auto xbox_auth_data = xbox_auth_template.arg(m_data->msaToken.token); QUrl url("https://user.auth.xboxlive.com/user/authenticate"); auto headers = QList{ { "Content-Type", "application/json" }, { "Accept", "application/json" }, // set contract-version header (prevent err 400 bad-request?) // https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/live/rest/additional/httpstandardheaders { "x-xbl-contract-version", "1" } }; auto [request, response] = Net::Upload::makeByteArray(url, xbox_auth_data.toUtf8()); m_request = request; m_request->addHeaderProxy(std::make_unique(headers)); m_request->enableAutoRetry(true); m_task.reset(new NetJob("XboxUserStep", APPLICATION->network())); m_task->setAskRetry(false); m_task->addNetAction(m_request); connect(m_task.get(), &Task::finished, this, [this, response] { onRequestDone(response); }); m_task->start(); qDebug() << "First layer of Xbox auth ... commencing."; } void XboxUserStep::onRequestDone(QByteArray* response) { if (m_request->error() != QNetworkReply::NoError) { qWarning() << "Reply error:" << m_request->error(); if (Net::isApplicationError(m_request->error()) && !Net::isServerError(m_request->error())) { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Xbox user authentication failed: %1").arg(m_request->errorString())); } else { m_data->networkError = m_request->error(); emit finished(AccountTaskState::STATE_OFFLINE, tr("Xbox user authentication failed: %1").arg(m_request->errorString())); } return; } Token temp; if (!Parsers::parseXTokenResponse(*response, temp, "UToken")) { qWarning() << "Could not parse user authentication response..."; emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Xbox user authentication response could not be understood.")); return; } m_data->userToken = temp; emit finished(AccountTaskState::STATE_WORKING, tr("Got Xbox user token")); } PrismLauncher-11.0.3/launcher/minecraft/auth/steps/MSAStep.cpp0000644000175100017510000002014115224505336023611 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MSAStep.h" #include #include #include #include #include #include "Application.h" #include "BuildConfig.h" #include "FileSystem.h" #include #include #include bool isSchemeHandlerRegistered() { #ifdef Q_OS_LINUX QProcess process; process.start("xdg-mime", { "query", "default", "x-scheme-handler/" + BuildConfig.LAUNCHER_APP_BINARY_NAME }); process.waitForFinished(); QString output = process.readAllStandardOutput().trimmed(); return output.contains(APPLICATION->desktopFileName()); #elif defined(Q_OS_WIN) QString regPath = QString("HKEY_CURRENT_USER\\Software\\Classes\\%1").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); QSettings settings(regPath, QSettings::NativeFormat); const QString registeredRunCommand = settings.value("shell/open/command/.").toString().replace("\\", "/"); return registeredRunCommand.contains(QCoreApplication::applicationFilePath()); #endif return true; } class CustomOAuthOobReplyHandler : public QOAuthOobReplyHandler { Q_OBJECT public: explicit CustomOAuthOobReplyHandler(QObject* parent = nullptr) : QOAuthOobReplyHandler(parent) { connect(APPLICATION, &Application::oauthReplyRecieved, this, &QOAuthOobReplyHandler::callbackReceived); } ~CustomOAuthOobReplyHandler() override { disconnect(APPLICATION, &Application::oauthReplyRecieved, this, &QOAuthOobReplyHandler::callbackReceived); } QString callback() const override { return BuildConfig.LAUNCHER_APP_BINARY_NAME + "://oauth/microsoft"; } protected: void networkReplyFinished(QNetworkReply* reply) override { if (reply->error() != QNetworkReply::NoError) { qWarning() << "OAuth2 request failed:" << reply->readAll(); } QOAuthOobReplyHandler::networkReplyFinished(reply); } }; class LoggingOAuthHttpServerReplyHandler final : public QOAuthHttpServerReplyHandler { Q_OBJECT public: explicit LoggingOAuthHttpServerReplyHandler(QObject* parent = nullptr) : QOAuthHttpServerReplyHandler(parent) {} protected: void networkReplyFinished(QNetworkReply* reply) override { if (reply->error() != QNetworkReply::NoError) { qWarning() << "OAuth2 request failed:" << reply->readAll(); } QOAuthHttpServerReplyHandler::networkReplyFinished(reply); } }; MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(silent) { m_clientId = APPLICATION->getMSAClientID(); if (QCoreApplication::applicationFilePath().startsWith("/tmp/.mount_") || APPLICATION->isPortable() || !isSchemeHandlerRegistered()) { auto replyHandler = new LoggingOAuthHttpServerReplyHandler(this); replyHandler->setCallbackText(QString(R"XXX( Login Successful, redirecting... )XXX") .arg(BuildConfig.LOGIN_CALLBACK_URL)); m_oauth2.setReplyHandler(replyHandler); } else { m_oauth2.setReplyHandler(new CustomOAuthOobReplyHandler(this)); } m_oauth2.setAuthorizationUrl(QUrl("https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize")); m_oauth2.setAccessTokenUrl(QUrl("https://login.microsoftonline.com/consumers/oauth2/v2.0/token")); m_oauth2.setScope("XboxLive.SignIn XboxLive.offline_access"); m_oauth2.setClientIdentifier(m_clientId); m_oauth2.setNetworkAccessManager(APPLICATION->network()); connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::granted, this, [this] { m_data->msaClientID = m_oauth2.clientIdentifier(); m_data->msaToken.issueInstant = QDateTime::currentDateTimeUtc(); m_data->msaToken.notAfter = m_oauth2.expirationAt(); m_data->msaToken.extra = m_oauth2.extraTokens(); m_data->msaToken.refresh_token = m_oauth2.refreshToken(); m_data->msaToken.token = m_oauth2.token(); emit finished(AccountTaskState::STATE_WORKING, tr("Got MSA token")); }); connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, this, &MSAStep::authorizeWithBrowser); connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::requestFailed, this, [this, silent](const QAbstractOAuth2::Error err) { auto state = AccountTaskState::STATE_FAILED_HARD; if (m_oauth2.status() == QAbstractOAuth::Status::Granted || silent) { if (err == QAbstractOAuth2::Error::NetworkError) { state = AccountTaskState::STATE_OFFLINE; } else { state = AccountTaskState::STATE_FAILED_SOFT; } } auto message = tr("Microsoft user authentication failed."); if (silent) { message = tr("Failed to refresh token."); } qWarning() << message; emit finished(state, message); }); connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::error, this, [this](const QString& error, const QString& errorDescription, const QUrl& uri) { qWarning() << "Failed to login because" << error << errorDescription; emit finished(AccountTaskState::STATE_FAILED_HARD, errorDescription); }); connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::extraTokensChanged, this, [this](const QVariantMap& tokens) { m_data->msaToken.extra = tokens; }); connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::clientIdentifierChanged, this, [this](const QString& clientIdentifier) { m_data->msaClientID = clientIdentifier; }); } QString MSAStep::describe() { return tr("Logging in with Microsoft account."); } void MSAStep::perform() { if (m_silent) { if (m_data->msaClientID != m_clientId) { emit finished(AccountTaskState::STATE_DISABLED, tr("Microsoft user authentication failed - client identification has changed.")); return; } if (m_data->msaToken.refresh_token.isEmpty()) { emit finished(AccountTaskState::STATE_DISABLED, tr("Microsoft user authentication failed - refresh token is empty.")); return; } m_oauth2.setRefreshToken(m_data->msaToken.refresh_token); m_oauth2.refreshAccessToken(); } else { m_oauth2.setModifyParametersFunction( [](QAbstractOAuth::Stage stage, QMultiMap* map) { map->insert("prompt", "select_account"); }); *m_data = AccountData(); m_data->msaClientID = m_clientId; m_oauth2.grant(); } } #include "MSAStep.moc" PrismLauncher-11.0.3/launcher/minecraft/auth/steps/LauncherLoginStep.h0000644000175100017510000000075515224505336025401 0ustar runnerrunner#pragma once #include #include "minecraft/auth/AuthStep.h" #include "net/NetJob.h" #include "net/Upload.h" class LauncherLoginStep : public AuthStep { Q_OBJECT public: explicit LauncherLoginStep(AccountData* data); virtual ~LauncherLoginStep() noexcept = default; void perform() override; QString describe() override; private slots: void onRequestDone(QByteArray* response); private: Net::Upload::Ptr m_request; NetJob::Ptr m_task; }; PrismLauncher-11.0.3/launcher/minecraft/auth/steps/XboxAuthorizationStep.h0000644000175100017510000000131515224505336026341 0ustar runnerrunner#pragma once #include #include "minecraft/auth/AuthStep.h" #include "net/NetJob.h" #include "net/Upload.h" class XboxAuthorizationStep : public AuthStep { Q_OBJECT public: explicit XboxAuthorizationStep(AccountData* data, Token* token, QString relyingParty, QString authorizationKind); virtual ~XboxAuthorizationStep() noexcept = default; void perform() override; QString describe() override; private: bool processSTSError(const QByteArray& response); private slots: void onRequestDone(QByteArray* response); private: Token* m_token; QString m_relyingParty; QString m_authorizationKind; Net::Upload::Ptr m_request; NetJob::Ptr m_task; }; PrismLauncher-11.0.3/launcher/minecraft/auth/steps/XboxUserStep.h0000644000175100017510000000073615224505336024425 0ustar runnerrunner#pragma once #include #include "minecraft/auth/AuthStep.h" #include "net/NetJob.h" #include "net/Upload.h" class XboxUserStep : public AuthStep { Q_OBJECT public: explicit XboxUserStep(AccountData* data); virtual ~XboxUserStep() noexcept = default; void perform() override; QString describe() override; private slots: void onRequestDone(QByteArray* response); private: Net::Upload::Ptr m_request; NetJob::Ptr m_task; }; PrismLauncher-11.0.3/launcher/minecraft/auth/steps/MinecraftProfileStep.cpp0000644000175100017510000000562115224505336026430 0ustar runnerrunner#include "MinecraftProfileStep.h" #include #include "Application.h" #include "minecraft/auth/Parsers.h" #include "net/NetUtils.h" #include "net/RawHeaderProxy.h" MinecraftProfileStep::MinecraftProfileStep(AccountData* data) : AuthStep(data) {} QString MinecraftProfileStep::describe() { return tr("Fetching the Minecraft profile."); } void MinecraftProfileStep::perform() { QUrl url("https://api.minecraftservices.com/minecraft/profile"); auto headers = QList{ { "Content-Type", "application/json" }, { "Accept", "application/json" }, { "Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8() } }; auto [request, response] = Net::Download::makeByteArray(url); m_request = request; m_request->addHeaderProxy(std::make_unique(headers)); m_request->enableAutoRetry(true); m_task.reset(new NetJob("MinecraftProfileStep", APPLICATION->network())); m_task->setAskRetry(false); m_task->addNetAction(m_request); connect(m_task.get(), &Task::finished, this, [this, response] { onRequestDone(response); }); m_task->start(); } void MinecraftProfileStep::onRequestDone(QByteArray* response) { if (m_request->error() == QNetworkReply::ContentNotFoundError) { // NOTE: Succeed even if we do not have a profile. This is a valid account state. m_data->minecraftProfile = MinecraftProfile(); emit finished(AccountTaskState::STATE_WORKING, tr("Account has no Minecraft profile.")); return; } if (m_request->error() != QNetworkReply::NoError) { qWarning() << "Error getting profile:"; qWarning() << " HTTP Status :" << m_request->replyStatusCode(); qWarning() << " Internal error no.:" << m_request->error(); qWarning() << " Error string :" << m_request->errorString(); qWarning() << " Response:"; qWarning() << QString::fromUtf8(*response); if (Net::isApplicationError(m_request->error()) && !Net::isServerError(m_request->error())) { emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Minecraft Java profile acquisition failed: %1").arg(m_request->errorString())); } else { m_data->networkError = m_request->error(); emit finished(AccountTaskState::STATE_OFFLINE, tr("Minecraft Java profile acquisition failed: %1").arg(m_request->errorString())); } return; } if (!Parsers::parseMinecraftProfile(*response, m_data->minecraftProfile)) { m_data->minecraftProfile = MinecraftProfile(); emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Minecraft Java profile response could not be parsed")); return; } emit finished(AccountTaskState::STATE_WORKING, tr("Got Minecraft profile")); } PrismLauncher-11.0.3/launcher/minecraft/auth/steps/EntitlementsStep.cpp0000644000175100017510000000352315224505336025651 0ustar runnerrunner#include "EntitlementsStep.h" #include #include #include #include #include #include "Application.h" #include "Logging.h" #include "minecraft/auth/Parsers.h" #include "net/Download.h" #include "net/NetJob.h" #include "net/RawHeaderProxy.h" #include "tasks/Task.h" EntitlementsStep::EntitlementsStep(AccountData* data) : AuthStep(data) {} QString EntitlementsStep::describe() { return tr("Determining game ownership."); } void EntitlementsStep::perform() { m_entitlements_request_id = QUuid::createUuid().toString(QUuid::WithoutBraces); QUrl url("https://api.minecraftservices.com/entitlements/license?requestId=" + m_entitlements_request_id); auto headers = QList{ { "Content-Type", "application/json" }, { "Accept", "application/json" }, { "Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8() } }; auto [request, response] = Net::Download::makeByteArray(url); m_request = request; m_request->addHeaderProxy(std::make_unique(headers)); m_request->enableAutoRetry(true); m_task.reset(new NetJob("EntitlementsStep", APPLICATION->network())); m_task->setAskRetry(false); m_task->addNetAction(m_request); connect(m_task.get(), &Task::finished, this, [this, response] { onRequestDone(response); }); m_task->start(); qDebug() << "Getting entitlements..."; } void EntitlementsStep::onRequestDone(QByteArray* response) { qCDebug(authCredentials()) << *response; // TODO: check presence of same entitlementsRequestId? // TODO: validate JWTs? Parsers::parseMinecraftEntitlements(*response, m_data->minecraftEntitlement); emit finished(AccountTaskState::STATE_WORKING, tr("Got entitlements")); } PrismLauncher-11.0.3/launcher/minecraft/auth/steps/GetSkinStep.cpp0000644000175100017510000000163215224505336024541 0ustar runnerrunner #include "GetSkinStep.h" #include #include "Application.h" GetSkinStep::GetSkinStep(AccountData* data) : AuthStep(data) {} QString GetSkinStep::describe() { return tr("Getting skin."); } void GetSkinStep::perform() { QUrl url(m_data->minecraftProfile.skin.url); auto [request, response] = Net::Download::makeByteArray(url); m_request = request; m_request->enableAutoRetry(true); m_task.reset(new NetJob("GetSkinStep", APPLICATION->network())); m_task->setAskRetry(false); m_task->addNetAction(m_request); connect(m_task.get(), &Task::finished, this, [this, response] { onRequestDone(response); }); m_task->start(); } void GetSkinStep::onRequestDone(QByteArray* response) { if (m_request->error() == QNetworkReply::NoError) m_data->minecraftProfile.skin.data = *response; emit finished(AccountTaskState::STATE_WORKING, tr("Got skin")); } PrismLauncher-11.0.3/launcher/minecraft/auth/AccountList.cpp0000644000175100017510000005474015224505336023443 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AccountList.h" #include "AccountData.h" #include "tasks/Task.h" #include #include #include #include #include #include #include #include #include #include #include #include #include enum AccountListVersion { MojangMSA = 3 }; AccountList::AccountList(QObject* parent) : QAbstractListModel(parent) { m_refreshTimer = new QTimer(this); m_refreshTimer->setSingleShot(true); connect(m_refreshTimer, &QTimer::timeout, this, &AccountList::fillQueue); m_nextTimer = new QTimer(this); m_nextTimer->setSingleShot(true); connect(m_nextTimer, &QTimer::timeout, this, &AccountList::tryNext); } AccountList::~AccountList() noexcept {} int AccountList::findAccountByProfileId(const QString& profileId) const { for (int i = 0; i < count(); i++) { MinecraftAccountPtr account = at(i); if (account->profileId() == profileId) { return i; } } return -1; } MinecraftAccountPtr AccountList::getAccountByProfileName(const QString& profileName) const { for (int i = 0; i < count(); i++) { MinecraftAccountPtr account = at(i); if (account->profileName() == profileName) { return account; } } return nullptr; } const MinecraftAccountPtr AccountList::at(int i) const { return MinecraftAccountPtr(m_accounts.at(i)); } QStringList AccountList::profileNames() const { QStringList out; for (auto& account : m_accounts) { auto profileName = account->profileName(); if (profileName.isEmpty()) { continue; } out.append(profileName); } return out; } void AccountList::addAccount(const MinecraftAccountPtr account) { // NOTE: Do not allow adding something that's already there. We shouldn't let it continue // because of the signal / slot connections after this. if (m_accounts.contains(account)) { qDebug() << "Tried to add account that's already on the accounts list!"; return; } // hook up notifications for changes in the account connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged); connect(account.get(), &MinecraftAccount::activityChanged, this, &AccountList::accountActivityChanged); // override/replace existing account with the same profileId auto profileId = account->profileId(); if (profileId.size()) { auto existingAccount = findAccountByProfileId(profileId); if (existingAccount != -1) { qDebug() << "Replacing old account with a new one with the same profile ID!"; MinecraftAccountPtr existingAccountPtr = m_accounts[existingAccount]; m_accounts[existingAccount] = account; if (m_defaultAccount == existingAccountPtr) { m_defaultAccount = account; } // disconnect notifications for changes in the account being replaced existingAccountPtr->disconnect(this); emit dataChanged(index(existingAccount), index(existingAccount, columnCount(QModelIndex()) - 1)); onListChanged(); return; } } // if we don't have this profileId yet, add the account to the end int row = m_accounts.count(); qDebug() << "Inserting account at index" << row; beginInsertRows(QModelIndex(), row, row); m_accounts.append(account); endInsertRows(); onListChanged(); } void AccountList::removeAccount(QModelIndex index) { int row = index.row(); if (index.isValid() && row >= 0 && row < m_accounts.size()) { auto& account = m_accounts[row]; if (account == m_defaultAccount) { m_defaultAccount = nullptr; onDefaultAccountChanged(); } account->disconnect(this); beginRemoveRows(QModelIndex(), row, row); m_accounts.removeAt(index.row()); endRemoveRows(); onListChanged(); } } void AccountList::moveAccount(QModelIndex index, int delta) { const int row = index.row(); const int newRow = row + delta; if (index.isValid() && row < m_accounts.size() && newRow >= 0 && newRow < m_accounts.size()) { // Qt is stupid, https://doc.qt.io/qt-6/qabstractitemmodel.html#beginMoveRows const int modelDestinationRow = (newRow > row) ? newRow + 1 : newRow; if (beginMoveRows(QModelIndex(), row, row, QModelIndex(), modelDestinationRow)) { m_accounts.move(row, newRow); endMoveRows(); onListChanged(); } else { qCritical().noquote() << "AccountList: failed to move account from" << row << "to" << newRow << QString("(%1 accounts in total)").arg(this->count()); } } } MinecraftAccountPtr AccountList::defaultAccount() const { return m_defaultAccount; } void AccountList::setDefaultAccount(MinecraftAccountPtr newAccount) { if (!newAccount && m_defaultAccount) { int idx = 0; auto previousDefaultAccount = m_defaultAccount; m_defaultAccount = nullptr; for (MinecraftAccountPtr account : m_accounts) { if (account == previousDefaultAccount) { emit dataChanged(index(idx), index(idx, columnCount(QModelIndex()) - 1)); } idx++; } onDefaultAccountChanged(); } else { auto currentDefaultAccount = m_defaultAccount; int currentDefaultAccountIdx = -1; auto newDefaultAccount = m_defaultAccount; int newDefaultAccountIdx = -1; int idx = 0; for (MinecraftAccountPtr account : m_accounts) { if (account == newAccount) { newDefaultAccount = account; newDefaultAccountIdx = idx; } if (currentDefaultAccount == account) { currentDefaultAccountIdx = idx; } idx++; } if (currentDefaultAccount != newDefaultAccount) { emit dataChanged(index(currentDefaultAccountIdx), index(currentDefaultAccountIdx, columnCount(QModelIndex()) - 1)); emit dataChanged(index(newDefaultAccountIdx), index(newDefaultAccountIdx, columnCount(QModelIndex()) - 1)); m_defaultAccount = newDefaultAccount; onDefaultAccountChanged(); } } } void AccountList::accountChanged() { // the list changed. there is no doubt. onListChanged(); } void AccountList::accountActivityChanged(bool active) { MinecraftAccount* account = qobject_cast(sender()); bool found = false; for (int i = 0; i < count(); i++) { if (at(i).get() == account) { emit dataChanged(index(i), index(i, columnCount(QModelIndex()) - 1)); found = true; break; } } if (found) { emit listActivityChanged(); if (active) { beginActivity(); } else { endActivity(); } } } void AccountList::onListChanged() { if (m_autosave) // TODO: Alert the user if this fails. saveList(); emit listChanged(); } void AccountList::onDefaultAccountChanged() { if (m_autosave) saveList(); emit defaultAccountChanged(); } int AccountList::count() const { return m_accounts.count(); } QString getAccountStatus(AccountState status) { switch (status) { case AccountState::Unchecked: return QObject::tr("Unchecked", "Account status"); case AccountState::Offline: return QObject::tr("Offline", "Account status"); case AccountState::Online: return QObject::tr("Ready", "Account status"); case AccountState::Working: return QObject::tr("Working", "Account status"); case AccountState::Errored: return QObject::tr("Errored", "Account status"); case AccountState::Expired: return QObject::tr("Expired", "Account status"); case AccountState::Disabled: return QObject::tr("Disabled", "Account status"); case AccountState::Gone: return QObject::tr("Gone", "Account status"); default: return QObject::tr("Unknown", "Account status"); } } QVariant AccountList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() > count()) return QVariant(); MinecraftAccountPtr account = at(index.row()); switch (role) { case Qt::SizeHintRole: if (index.column() == ProfileNameColumn) { return QSize(0, 30); } return QVariant(); case Qt::DecorationRole: if (index.column() == ProfileNameColumn) { auto face = account->getFace(24, 24); if (!face.isNull()) { return face; } else { return QIcon::fromTheme("noaccount").pixmap(24, 24); } } return QVariant(); case Qt::DisplayRole: switch (index.column()) { case ProfileNameColumn: return account->profileName(); case TypeColumn: { switch (account->accountType()) { case AccountType::MSA: { return tr("MSA", "Account type"); } case AccountType::Offline: { return tr("Offline", "Account type"); } } return tr("Unknown", "Account type"); } case StatusColumn: return getAccountStatus(account->accountState()); default: return QVariant(); } case PointerRole: return QVariant::fromValue(account); case Qt::CheckStateRole: if (index.column() == ProfileNameColumn) return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked; return QVariant(); default: return QVariant(); } } QVariant AccountList::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { switch (role) { case Qt::DisplayRole: switch (section) { case ProfileNameColumn: return tr("Username"); case TypeColumn: return tr("Type"); case StatusColumn: return tr("Status"); default: return QVariant(); } case Qt::ToolTipRole: switch (section) { case ProfileNameColumn: return tr("Minecraft username associated with the account."); case TypeColumn: return tr("Type of the account (MSA or Offline)"); case StatusColumn: return tr("Current status of the account."); default: return QVariant(); } default: return QVariant(); } } int AccountList::rowCount(const QModelIndex& parent) const { // Return count return parent.isValid() ? 0 : count(); } int AccountList::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; } Qt::ItemFlags AccountList::flags(const QModelIndex& index) const { if (index.row() < 0 || index.row() >= rowCount(index.parent()) || !index.isValid()) { return Qt::NoItemFlags; } return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; } bool AccountList::setData(const QModelIndex& idx, const QVariant& value, int role) { if (idx.row() < 0 || idx.row() >= rowCount(idx.parent()) || !idx.isValid()) { return false; } if (role == Qt::CheckStateRole) { if (value == Qt::Checked) { MinecraftAccountPtr account = at(idx.row()); setDefaultAccount(account); } else if (m_defaultAccount == at(idx.row())) setDefaultAccount(nullptr); } emit dataChanged(idx, index(idx.row(), columnCount(QModelIndex()) - 1)); return true; } bool AccountList::loadList() { if (m_listFilePath.isEmpty()) { qCritical() << "Can't load Mojang account list. No file path given and no default set."; return false; } QFile file(m_listFilePath); // Try to open the file and fail if we can't. // TODO: We should probably report this error to the user. if (!file.open(QIODevice::ReadOnly)) { qCritical() << QString("Failed to read the account list file %1 (%2).").arg(m_listFilePath).arg(file.errorString()).toUtf8(); return false; } // Read the file and close it. QByteArray jsonData = file.readAll(); file.close(); QJsonParseError parseError; QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); // Fail if the JSON is invalid. if (parseError.error != QJsonParseError::NoError) { qCritical() << QString("Failed to parse account list file: %1 at offset %2") .arg(parseError.errorString(), QString::number(parseError.offset)) .toUtf8(); return false; } // Make sure the root is an object. if (!jsonDoc.isObject()) { qCritical() << "Invalid account list JSON: Root should be an array."; return false; } QJsonObject root = jsonDoc.object(); // Make sure the format version matches. auto listVersion = root.value("formatVersion").toVariant().toInt(); if (listVersion == AccountListVersion::MojangMSA) return loadV3(root); QString newName = "accounts-old.json"; qWarning() << "Unknown format version when loading account list. Existing one will be renamed to" << newName; // Attempt to rename the old version. file.rename(newName); return false; } bool AccountList::loadV3(QJsonObject& root) { beginResetModel(); QJsonArray accounts = root.value("accounts").toArray(); for (QJsonValue accountVal : accounts) { QJsonObject accountObj = accountVal.toObject(); MinecraftAccountPtr account = MinecraftAccount::loadFromJsonV3(accountObj); if (account.get() != nullptr) { auto profileId = account->profileId(); if (profileId.size()) { if (findAccountByProfileId(profileId) != -1) { continue; } } connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged); connect(account.get(), &MinecraftAccount::activityChanged, this, &AccountList::accountActivityChanged); m_accounts.append(account); if (accountObj.value("active").toBool(false)) { m_defaultAccount = account; } } else { qWarning() << "Failed to load an account."; } } endResetModel(); return true; } bool AccountList::saveList() { if (m_listFilePath.isEmpty()) { qCritical() << "Can't save Mojang account list. No file path given and no default set."; return false; } // make sure the parent folder exists if (!FS::ensureFilePathExists(m_listFilePath)) return false; // make sure the file wasn't overwritten with a folder before (fixes a bug) QFileInfo finfo(m_listFilePath); if (finfo.isDir()) { QDir badDir(m_listFilePath); badDir.removeRecursively(); } qDebug() << "Writing account list to" << m_listFilePath; qDebug() << "Building JSON data structure."; // Build the JSON document to write to the list file. QJsonObject root; root.insert("formatVersion", AccountListVersion::MojangMSA); // Build a list of accounts. qDebug() << "Building account array."; QJsonArray accounts; for (MinecraftAccountPtr account : m_accounts) { QJsonObject accountObj = account->saveToJson(); if (m_defaultAccount == account) { accountObj["active"] = true; } accounts.append(accountObj); } // Insert the account list into the root object. root.insert("accounts", accounts); // Create a JSON document object to convert our JSON to bytes. QJsonDocument doc(root); // Now that we're done building the JSON object, we can write it to the file. qDebug() << "Writing account list to file."; QSaveFile file(m_listFilePath); // Try to open the file and fail if we can't. // TODO: We should probably report this error to the user. if (!file.open(QIODevice::WriteOnly)) { qCritical() << QString("Failed to save the account list file %1 (%2).").arg(m_listFilePath).arg(file.errorString()).toUtf8(); return false; } // Write the JSON to the file. file.write(doc.toJson()); file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser); if (file.commit()) { qDebug() << "Saved account list to" << m_listFilePath; return true; } else { qDebug() << "Failed to save accounts to" << m_listFilePath << "error:" << file.errorString(); return false; } } void AccountList::setListFilePath(QString path, bool autosave) { m_listFilePath = path; m_autosave = autosave; } bool AccountList::anyAccountIsValid() { for (auto account : m_accounts) { if (account->ownsMinecraft()) { return true; } } return false; } void AccountList::fillQueue() { if (m_defaultAccount && m_defaultAccount->shouldRefresh()) { auto idToRefresh = m_defaultAccount->internalId(); m_refreshQueue.push_back(idToRefresh); qDebug() << "AccountList: Queued default account with internal ID" << idToRefresh << "to refresh first"; } for (int i = 0; i < count(); i++) { auto account = at(i); if (account == m_defaultAccount) { continue; } if (account->shouldRefresh()) { auto idToRefresh = account->internalId(); queueRefresh(idToRefresh); } } tryNext(); } void AccountList::requestRefresh(QString accountId) { auto index = m_refreshQueue.indexOf(accountId); if (index != -1) { m_refreshQueue.removeAt(index); } m_refreshQueue.push_front(accountId); qDebug() << "AccountList: Pushed account with internal ID" << accountId << "to the front of the queue"; if (!isActive()) { tryNext(); } } void AccountList::queueRefresh(QString accountId) { if (m_refreshQueue.indexOf(accountId) != -1) { return; } m_refreshQueue.push_back(accountId); qDebug() << "AccountList: Queued account with internal ID" << accountId << "to refresh"; } void AccountList::tryNext() { while (m_refreshQueue.length()) { auto accountId = m_refreshQueue.front(); m_refreshQueue.pop_front(); bool found = false; for (int i = 0; i < count(); i++) { auto account = at(i); if (account->internalId() == accountId) { found = true; if (!account->shouldRefresh()) { // Account no longer needs refreshing, skip it. qDebug() << "RefreshSchedule: Skipping account" << account->profileName() << "with internal ID" << accountId << "(no longer needs refresh)"; break; } m_currentTask = account->refresh(); if (m_currentTask) { connect(m_currentTask.get(), &Task::succeeded, this, &AccountList::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &AccountList::authFailed); m_currentTask->start(); qDebug() << "RefreshSchedule: Processing account" << account->profileName() << "with internal ID" << accountId; return; } break; } } if (!found) { qDebug() << "RefreshSchedule: Account with internal ID" << accountId << "not found."; } } // if we get here, no account needed refreshing. Schedule refresh in an hour. m_refreshTimer->start(1000 * 3600); } void AccountList::authSucceeded() { qDebug() << "RefreshSchedule: Background account refresh succeeded"; m_currentTask.reset(); m_nextTimer->start(1000 * 20); } void AccountList::authFailed(QString reason) { qDebug() << "RefreshSchedule: Background account refresh failed:" << reason; m_currentTask.reset(); m_nextTimer->start(1000 * 20); } bool AccountList::isActive() const { return m_activityCount != 0; } void AccountList::beginActivity() { bool activating = m_activityCount == 0; m_activityCount++; if (activating) { emit activityChanged(true); } } void AccountList::endActivity() { if (m_activityCount == 0) { qWarning() << "Activity count would become below zero"; return; } bool deactivating = m_activityCount == 1; m_activityCount--; if (deactivating) { emit activityChanged(false); } } PrismLauncher-11.0.3/launcher/minecraft/auth/Parsers.cpp0000644000175100017510000003526415224505336022632 0ustar runnerrunner#include "Parsers.h" #include "Json.h" #include "Logging.h" #include #include #include namespace Parsers { bool getDateTime(QJsonValue value, QDateTime& out) { if (!value.isString()) { return false; } out = QDateTime::fromString(value.toString(), Qt::ISODate); return out.isValid(); } bool getString(QJsonValue value, QString& out) { if (!value.isString()) { return false; } out = value.toString(); return true; } bool getNumber(QJsonValue value, double& out) { if (!value.isDouble()) { return false; } out = value.toDouble(); return true; } bool getNumber(QJsonValue value, int64_t& out) { if (!value.isDouble()) { return false; } out = (int64_t)value.toDouble(); return true; } bool getBool(QJsonValue value, bool& out) { if (!value.isBool()) { return false; } out = value.toBool(); return true; } /* { "IssueInstant":"2020-12-07T19:52:08.4463796Z", "NotAfter":"2020-12-21T19:52:08.4463796Z", "Token":"token", "DisplayClaims":{ "xui":[ { "uhs":"userhash" } ] } } */ // TODO: handle error responses ... /* { "Identity":"0", "XErr":2148916238, "Message":"", "Redirect":"https://start.ui.xboxlive.com/AddChildToFamily" } // 2148916233 = missing Xbox account // 2148916238 = child account not linked to a family */ bool parseXTokenResponse(QByteArray& data, Token& output, QString name) { qDebug() << "Parsing" << name << ":"; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error) { qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON:" << jsonError.errorString(); return false; } auto obj = doc.object(); if (!getDateTime(obj.value("IssueInstant"), output.issueInstant)) { qWarning() << "User IssueInstant is not a timestamp"; return false; } if (!getDateTime(obj.value("NotAfter"), output.notAfter)) { qWarning() << "User NotAfter is not a timestamp"; return false; } if (!getString(obj.value("Token"), output.token)) { qWarning() << "User Token is not a string"; return false; } auto arrayVal = obj.value("DisplayClaims").toObject().value("xui"); if (!arrayVal.isArray()) { qWarning() << "Missing xui claims array"; return false; } bool foundUHS = false; for (auto item : arrayVal.toArray()) { if (!item.isObject()) { continue; } auto obj_ = item.toObject(); if (obj_.contains("uhs")) { foundUHS = true; } else { continue; } // consume all 'display claims' ... whatever that means for (auto iter = obj_.begin(); iter != obj_.end(); iter++) { QString claim; if (!getString(obj_.value(iter.key()), claim)) { qWarning() << "display claim" << iter.key() << "is not a string..."; return false; } output.extra[iter.key()] = claim; } break; } if (!foundUHS) { qWarning() << "Missing uhs"; return false; } output.validity = Validity::Certain; qDebug() << name << "is valid."; return true; } bool parseMinecraftProfile(QByteArray& data, MinecraftProfile& output) { qDebug() << "Parsing Minecraft profile..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error) { qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON:" << jsonError.errorString(); return false; } auto obj = doc.object(); if (!getString(obj.value("id"), output.id)) { qWarning() << "Minecraft profile id is not a string"; return false; } if (!getString(obj.value("name"), output.name)) { qWarning() << "Minecraft profile name is not a string"; return false; } auto skinsArray = obj.value("skins").toArray(); for (auto skin : skinsArray) { auto skinObj = skin.toObject(); Skin skinOut; if (!getString(skinObj.value("id"), skinOut.id)) { continue; } QString state; if (!getString(skinObj.value("state"), state)) { continue; } if (state != "ACTIVE") { continue; } if (!getString(skinObj.value("url"), skinOut.url)) { continue; } skinOut.url.replace("http://textures.minecraft.net", "https://textures.minecraft.net"); if (!getString(skinObj.value("variant"), skinOut.variant)) { continue; } // we deal with only the active skin output.skin = skinOut; break; } auto capesArray = obj.value("capes").toArray(); QString currentCape; for (auto cape : capesArray) { auto capeObj = cape.toObject(); Cape capeOut; if (!getString(capeObj.value("id"), capeOut.id)) { continue; } QString state; if (!getString(capeObj.value("state"), state)) { continue; } if (state == "ACTIVE") { currentCape = capeOut.id; } if (!getString(capeObj.value("url"), capeOut.url)) { continue; } capeOut.url.replace("http://textures.minecraft.net", "https://textures.minecraft.net"); if (!getString(capeObj.value("alias"), capeOut.alias)) { continue; } output.capes[capeOut.id] = capeOut; } output.currentCape = currentCape; output.validity = Validity::Certain; return true; } namespace { // these skin URLs are for the MHF_Steve and MHF_Alex accounts (made by a Mojang employee) // they are needed because the session server doesn't return skin urls for default skins static const QString SKIN_URL_STEVE = "https://textures.minecraft.net/texture/1a4af718455d4aab528e7a61f86fa25e6a369d1768dcb13f7df319a713eb810b"; static const QString SKIN_URL_ALEX = "https://textures.minecraft.net/texture/83cee5ca6afcdb171285aa00e8049c297b2dbeba0efb8ff970a5677a1b644032"; bool isDefaultModelSteve(QString uuid) { // need to calculate *Java* hashCode of UUID // if number is even, skin/model is steve, otherwise it is alex // just in case dashes are in the id uuid.remove('-'); if (uuid.size() != 32) { return true; } // qulonglong is guaranteed to be 64 bits // we need to use unsigned numbers to guarantee truncation below qulonglong most = uuid.left(16).toULongLong(nullptr, 16); qulonglong least = uuid.right(16).toULongLong(nullptr, 16); qulonglong xored = most ^ least; return ((static_cast(xored >> 32)) ^ static_cast(xored)) % 2 == 0; } } // namespace /** Uses session server for skin/cape lookup instead of profile, because locked Mojang accounts cannot access profile endpoint (https://api.minecraftservices.com/minecraft/profile/) ref: https://wiki.vg/Mojang_API#UUID_to_Profile_and_Skin.2FCape { "id": "", "name": "", "properties": [ { "name": "textures", "value": "" } ] } decoded base64 "value": { "timestamp": , "profileId": "", "profileName": "", "textures": { "SKIN": { "url": "" }, "CAPE": { "url": "" } } } */ bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output) { qDebug() << "Parsing Minecraft profile..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error) { qWarning() << "Failed to parse response as JSON:" << jsonError.errorString(); return false; } auto obj = Json::requireObject(doc, "mojang minecraft profile"); if (!getString(obj.value("id"), output.id)) { qWarning() << "Minecraft profile id is not a string"; return false; } if (!getString(obj.value("name"), output.name)) { qWarning() << "Minecraft profile name is not a string"; return false; } auto propsArray = obj.value("properties").toArray(); QByteArray texturePayload; for (auto p : propsArray) { auto pObj = p.toObject(); auto name = pObj.value("name"); if (!name.isString() || name.toString() != "textures") { continue; } auto value = pObj.value("value"); if (value.isString()) { texturePayload = QByteArray::fromBase64(value.toString().toUtf8(), QByteArray::AbortOnBase64DecodingErrors); } if (!texturePayload.isEmpty()) { break; } } if (texturePayload.isNull()) { qWarning() << "No texture payload data"; return false; } doc = QJsonDocument::fromJson(texturePayload, &jsonError); if (jsonError.error) { qWarning() << "Failed to parse response as JSON:" << jsonError.errorString(); return false; } obj = Json::requireObject(doc, "session texture payload"); auto textures = obj.value("textures"); if (!textures.isObject()) { qWarning() << "No textures array in response"; return false; } Skin skinOut; // fill in default skin info ourselves, as this endpoint doesn't provide it bool steve = isDefaultModelSteve(output.id); skinOut.variant = steve ? "CLASSIC" : "SLIM"; skinOut.url = steve ? SKIN_URL_STEVE : SKIN_URL_ALEX; // sadly we can't figure this out, but I don't think it really matters... skinOut.id = "00000000-0000-0000-0000-000000000000"; Cape capeOut; auto tObj = textures.toObject(); for (auto idx = tObj.constBegin(); idx != tObj.constEnd(); ++idx) { if (idx->isObject()) { if (idx.key() == "SKIN") { auto skin = idx->toObject(); if (!getString(skin.value("url"), skinOut.url)) { qWarning() << "Skin url is not a string"; return false; } skinOut.url.replace("http://textures.minecraft.net", "https://textures.minecraft.net"); auto maybeMeta = skin.find("metadata"); if (maybeMeta != skin.end() && maybeMeta->isObject()) { auto meta = maybeMeta->toObject(); // might not be present getString(meta.value("model"), skinOut.variant); } } else if (idx.key() == "CAPE") { auto cape = idx->toObject(); if (!getString(cape.value("url"), capeOut.url)) { qWarning() << "Cape url is not a string"; return false; } capeOut.url.replace("http://textures.minecraft.net", "https://textures.minecraft.net"); // we don't know the cape ID as it is not returned from the session server // so just fake it - changing capes is probably locked anyway :( capeOut.alias = "cape"; } } } output.skin = skinOut; if (capeOut.alias == "cape") { output.capes = QMap({ { capeOut.alias, capeOut } }); output.currentCape = capeOut.alias; } output.validity = Validity::Certain; return true; } bool parseMinecraftEntitlements(QByteArray& data, MinecraftEntitlement& output) { qDebug() << "Parsing Minecraft entitlements..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error) { qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON:" << jsonError.errorString(); return false; } auto obj = doc.object(); output.canPlayMinecraft = false; output.ownsMinecraft = false; auto itemsArray = obj.value("items").toArray(); for (auto item : itemsArray) { auto itemObj = item.toObject(); QString name; if (!getString(itemObj.value("name"), name)) { continue; } if (name == "game_minecraft") { output.canPlayMinecraft = true; } if (name == "product_minecraft") { output.ownsMinecraft = true; } } output.validity = Validity::Certain; return true; } bool parseRolloutResponse(QByteArray& data, bool& result) { qDebug() << "Parsing Rollout response..."; qCDebug(authCredentials()) << data; QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error) { qWarning() << "Failed to parse response from https://api.minecraftservices.com/rollout/v1/msamigration as JSON: " << jsonError.errorString(); return false; } auto obj = doc.object(); QString feature; if (!getString(obj.value("feature"), feature)) { qWarning() << "Rollout feature is not a string"; return false; } if (feature != "msamigration") { qWarning() << "Rollout feature is not what we expected (msamigration), but is instead \"" << feature << "\""; return false; } if (!getBool(obj.value("rollout"), result)) { qWarning() << "Rollout feature is not a string"; return false; } return true; } bool parseMojangResponse(QByteArray& data, Token& output) { QJsonParseError jsonError; qDebug() << "Parsing Mojang response..."; qCDebug(authCredentials()) << data; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error) { qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON:" << jsonError.errorString(); return false; } auto obj = doc.object(); double expires_in = 0; if (!getNumber(obj.value("expires_in"), expires_in)) { qWarning() << "expires_in is not a valid number"; return false; } auto currentTime = QDateTime::currentDateTimeUtc(); output.issueInstant = currentTime; output.notAfter = currentTime.addSecs(expires_in); QString username; if (!getString(obj.value("username"), username)) { qWarning() << "username is not valid"; return false; } // TODO: it's a JWT... validate it? if (!getString(obj.value("access_token"), output.token)) { qWarning() << "access_token is not valid"; return false; } output.validity = Validity::Certain; qDebug() << "Mojang response is valid."; return true; } } // namespace Parsers PrismLauncher-11.0.3/launcher/minecraft/auth/Parsers.h0000644000175100017510000000135215224505336022266 0ustar runnerrunner#pragma once #include "AccountData.h" namespace Parsers { bool getDateTime(QJsonValue value, QDateTime& out); bool getString(QJsonValue value, QString& out); bool getNumber(QJsonValue value, double& out); bool getNumber(QJsonValue value, int64_t& out); bool getBool(QJsonValue value, bool& out); bool parseXTokenResponse(QByteArray& data, Token& output, QString name); bool parseMojangResponse(QByteArray& data, Token& output); bool parseMinecraftProfile(QByteArray& data, MinecraftProfile& output); bool parseMinecraftProfileMojang(QByteArray& data, MinecraftProfile& output); bool parseMinecraftEntitlements(QByteArray& data, MinecraftEntitlement& output); bool parseRolloutResponse(QByteArray& data, bool& result); } // namespace Parsers PrismLauncher-11.0.3/launcher/minecraft/auth/AuthFlow.h0000644000175100017510000000221415224505336022376 0ustar runnerrunner#pragma once #include #include #include #include #include "minecraft/auth/AccountData.h" #include "minecraft/auth/AuthStep.h" #include "tasks/Task.h" class AuthFlow : public Task { Q_OBJECT public: enum class Action { Refresh, Login, DeviceCode }; explicit AuthFlow(AccountData* data, Action action = Action::Refresh); virtual ~AuthFlow() = default; void executeTask() override; AccountTaskState taskState() { return m_taskState; } public slots: bool abort() override; signals: void authorizeWithBrowser(const QUrl& url); void authorizeWithBrowserWithExtra(QString url, QString code, int expiresIn); protected: void succeed(); void nextStep(); private slots: // NOTE: true -> non-terminal state, false -> terminal state bool changeState(AccountTaskState newState, QString reason = QString()); void stepFinished(AccountTaskState resultingState, QString message); private: AccountTaskState m_taskState = AccountTaskState::STATE_CREATED; QList m_steps; AuthStep::Ptr m_currentStep; AccountData* m_data = nullptr; }; PrismLauncher-11.0.3/launcher/minecraft/auth/AccountList.h0000644000175100017510000001314115224505336023076 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "MinecraftAccount.h" #include "minecraft/auth/AuthFlow.h" #include #include #include #include /*! * List of available Mojang accounts. * This should be loaded in the background by Prism Launcher on startup. */ class AccountList : public QAbstractListModel { Q_OBJECT public: enum ModelRoles { PointerRole = 0x34B1CB48 }; enum VListColumns { // TODO: Add icon column. ProfileNameColumn = 0, TypeColumn, StatusColumn, NUM_COLUMNS }; explicit AccountList(QObject* parent = 0); virtual ~AccountList() noexcept; const MinecraftAccountPtr at(int i) const; int count() const; //////// List Model Functions //////// QVariant data(const QModelIndex& index, int role) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; virtual int rowCount(const QModelIndex& parent) const override; virtual int columnCount(const QModelIndex& parent) const override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override; virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override; void addAccount(MinecraftAccountPtr account); void removeAccount(QModelIndex index); void moveAccount(QModelIndex index, int delta); int findAccountByProfileId(const QString& profileId) const; MinecraftAccountPtr getAccountByProfileName(const QString& profileName) const; QStringList profileNames() const; // requesting a refresh pushes it to the front of the queue void requestRefresh(QString accountId); // queuing a refresh will let it go to the back of the queue (unless it's somewhere inside the queue already) void queueRefresh(QString accountId); /*! * Sets the path to load/save the list file from/to. * If autosave is true, this list will automatically save to the given path whenever it changes. * THIS FUNCTION DOES NOT LOAD THE LIST. If you set autosave, be sure to call loadList() immediately * after calling this function to ensure an autosaved change doesn't overwrite the list you intended * to load. */ void setListFilePath(QString path, bool autosave = false); bool loadList(); bool loadV3(QJsonObject& root); bool saveList(); MinecraftAccountPtr defaultAccount() const; void setDefaultAccount(MinecraftAccountPtr profileId); bool anyAccountIsValid(); bool isActive() const; protected: void beginActivity(); void endActivity(); private: uint32_t m_activityCount = 0; signals: void listChanged(); void listActivityChanged(); void defaultAccountChanged(); void activityChanged(bool active); public slots: /** * This is called when one of the accounts changes and the list needs to be updated */ void accountChanged(); /** * This is called when a (refresh/login) task involving the account starts or ends */ void accountActivityChanged(bool active); /** * This is initially to run background account refresh tasks, or on a hourly timer */ void fillQueue(); private slots: void tryNext(); void authSucceeded(); void authFailed(QString reason); protected: QList m_refreshQueue; QTimer* m_refreshTimer; QTimer* m_nextTimer; shared_qobject_ptr m_currentTask; /*! * Called whenever the list changes. * This emits the listChanged() signal and autosaves the list (if autosave is enabled). */ void onListChanged(); /*! * Called whenever the active account changes. * Emits the defaultAccountChanged() signal and autosaves the list if enabled. */ void onDefaultAccountChanged(); QList m_accounts; MinecraftAccountPtr m_defaultAccount; //! Path to the account list file. Empty string if there isn't one. QString m_listFilePath; /*! * If true, the account list will automatically save to the account list path when it changes. * Ignored if m_listFilePath is blank. */ bool m_autosave = false; }; PrismLauncher-11.0.3/launcher/minecraft/auth/AuthStep.h0000644000175100017510000000234415224505336022406 0ustar runnerrunner#pragma once #include #include #include "QObjectPtr.h" #include "minecraft/auth/AccountData.h" /** * Enum for describing the state of the current task. * Used by the getStateMessage function to determine what the status message should be. */ enum class AccountTaskState { STATE_CREATED, STATE_WORKING, STATE_SUCCEEDED, STATE_DISABLED, //!< MSA Client ID has changed. Tell user to reloginn STATE_FAILED_SOFT, //!< soft failure. authentication went through partially STATE_FAILED_HARD, //!< hard failure. main tokens are invalid STATE_FAILED_GONE, //!< hard failure. main tokens are invalid, and the account no longer exists STATE_OFFLINE //!< soft failure. authentication failed in the first step in a 'soft' way }; class AuthStep : public QObject { Q_OBJECT public: using Ptr = shared_qobject_ptr; explicit AuthStep(AccountData* data) : QObject(nullptr), m_data(data) {}; virtual ~AuthStep() noexcept = default; virtual QString describe() = 0; public slots: virtual void perform() = 0; virtual void abort() {} signals: void finished(AccountTaskState resultingState, QString message); protected: AccountData* m_data; }; PrismLauncher-11.0.3/launcher/minecraft/auth/AuthFlow.cpp0000644000175100017510000001256615224505336022744 0ustar runnerrunner#include #include #include #include "minecraft/auth/AccountData.h" #include "minecraft/auth/steps/EntitlementsStep.h" #include "minecraft/auth/steps/GetSkinStep.h" #include "minecraft/auth/steps/LauncherLoginStep.h" #include "minecraft/auth/steps/MSADeviceCodeStep.h" #include "minecraft/auth/steps/MSAStep.h" #include "minecraft/auth/steps/MinecraftProfileStep.h" #include "minecraft/auth/steps/XboxAuthorizationStep.h" #include "minecraft/auth/steps/XboxUserStep.h" #include "tasks/Task.h" #include "AuthFlow.h" #include AuthFlow::AuthFlow(AccountData* data, Action action) : Task(), m_data(data) { if (data->type == AccountType::MSA) { if (action == Action::DeviceCode) { auto oauthStep = makeShared(m_data); connect(oauthStep.get(), &MSADeviceCodeStep::authorizeWithBrowser, this, &AuthFlow::authorizeWithBrowserWithExtra); connect(this, &Task::aborted, oauthStep.get(), &MSADeviceCodeStep::abort); m_steps.append(oauthStep); } else { auto oauthStep = makeShared(m_data, action == Action::Refresh); connect(oauthStep.get(), &MSAStep::authorizeWithBrowser, this, &AuthFlow::authorizeWithBrowser); m_steps.append(oauthStep); } m_steps.append(makeShared(m_data)); m_steps.append( makeShared(m_data, &m_data->mojangservicesToken, "rp://api.minecraftservices.com/", "Mojang")); m_steps.append(makeShared(m_data)); m_steps.append(makeShared(m_data)); m_steps.append(makeShared(m_data)); m_steps.append(makeShared(m_data)); } changeState(AccountTaskState::STATE_CREATED); } void AuthFlow::succeed() { m_data->validity_ = Validity::Certain; changeState(AccountTaskState::STATE_SUCCEEDED, tr("Finished all authentication steps")); } void AuthFlow::executeTask() { changeState(AccountTaskState::STATE_WORKING, tr("Initializing")); nextStep(); } void AuthFlow::nextStep() { if (!Task::isRunning()) { return; } if (m_steps.size() == 0) { // we got to the end without an incident... assume this is all. m_currentStep.reset(); succeed(); return; } m_currentStep = m_steps.front(); qDebug() << "AuthFlow:" << m_currentStep->describe(); setStatus(m_currentStep->describe()); m_steps.pop_front(); connect(m_currentStep.get(), &AuthStep::finished, this, &AuthFlow::stepFinished); m_currentStep->perform(); } void AuthFlow::stepFinished(AccountTaskState resultingState, QString message) { if (changeState(resultingState, message)) nextStep(); } bool AuthFlow::changeState(AccountTaskState newState, QString reason) { m_taskState = newState; setDetails(reason); switch (newState) { case AccountTaskState::STATE_CREATED: { setStatus(tr("Waiting...")); m_data->errorString.clear(); return true; } case AccountTaskState::STATE_WORKING: { if (!m_currentStep) { setStatus(tr("Preparing to log in...")); } m_data->accountState = AccountState::Working; return true; } case AccountTaskState::STATE_SUCCEEDED: { setStatus(tr("Authentication task succeeded.")); m_data->accountState = AccountState::Online; emitSucceeded(); return false; } case AccountTaskState::STATE_OFFLINE: { setStatus(tr("Failed to contact the authentication server.")); m_data->errorString = reason; m_data->accountState = AccountState::Offline; emitFailed(reason); return false; } case AccountTaskState::STATE_DISABLED: { setStatus(tr("Client ID has changed. New session needs to be created.")); m_data->errorString = reason; m_data->accountState = AccountState::Disabled; emitFailed(reason); return false; } case AccountTaskState::STATE_FAILED_SOFT: { setStatus(tr("Encountered an error during authentication.")); m_data->errorString = reason; m_data->accountState = AccountState::Errored; emitFailed(reason); return false; } case AccountTaskState::STATE_FAILED_HARD: { setStatus(tr("Failed to authenticate. The session has expired.")); m_data->errorString = reason; m_data->accountState = AccountState::Expired; emitFailed(reason); return false; } case AccountTaskState::STATE_FAILED_GONE: { setStatus(tr("Failed to authenticate. The account no longer exists.")); m_data->errorString = reason; m_data->accountState = AccountState::Gone; emitFailed(reason); return false; } default: { setStatus(tr("...")); QString error = tr("Unknown account task state: %1").arg(int(newState)); m_data->accountState = AccountState::Errored; emitFailed(error); return false; } } } bool AuthFlow::abort() { if (m_currentStep) m_currentStep->abort(); emitAborted(); return true; } PrismLauncher-11.0.3/launcher/minecraft/auth/MinecraftAccount.cpp0000644000175100017510000002344015224505336024431 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Authors: Orochimarufan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MinecraftAccount.h" #include #include #include #include #include #include #include #include #include #include #include "minecraft/auth/AccountData.h" #include "minecraft/auth/AuthFlow.h" MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) { data.internalId = QUuid::createUuid().toString(QUuid::Id128); } MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json) { MinecraftAccountPtr account(new MinecraftAccount()); if (account->data.resumeStateFromV3(json)) { return account; } return nullptr; } MinecraftAccountPtr MinecraftAccount::createBlankMSA() { MinecraftAccountPtr account(new MinecraftAccount()); account->data.type = AccountType::MSA; return account; } MinecraftAccountPtr MinecraftAccount::createOffline(const QString& username) { auto account = makeShared(); account->data.type = AccountType::Offline; account->data.yggdrasilToken.token = "0"; account->data.yggdrasilToken.validity = Validity::Certain; account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc(); account->data.yggdrasilToken.extra["userName"] = username; account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString(QUuid::Id128); account->data.minecraftProfile.id = uuidFromUsername(username).toString(QUuid::Id128); account->data.minecraftProfile.name = username; account->data.minecraftProfile.validity = Validity::Certain; return account; } QJsonObject MinecraftAccount::saveToJson() const { return data.saveState(); } AccountState MinecraftAccount::accountState() const { return data.accountState; } QPixmap MinecraftAccount::getFace(int width, int height) const { QPixmap skinTexture; if (!skinTexture.loadFromData(data.minecraftProfile.skin.data, "PNG")) { return QPixmap(); } QPixmap skin = QPixmap(8, 8); skin.fill(QColorConstants::Transparent); QPainter painter(&skin); painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8)); painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8)); return skin.scaled(width, height, Qt::KeepAspectRatio); } shared_qobject_ptr MinecraftAccount::login(bool useDeviceCode) { Q_ASSERT(m_currentTask.get() == nullptr); m_currentTask.reset(new AuthFlow(&data, useDeviceCode ? AuthFlow::Action::DeviceCode : AuthFlow::Action::Login)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } shared_qobject_ptr MinecraftAccount::refresh() { if (m_currentTask) { return m_currentTask; } m_currentTask.reset(new AuthFlow(&data, AuthFlow::Action::Refresh)); connect(m_currentTask.get(), &Task::succeeded, this, &MinecraftAccount::authSucceeded); connect(m_currentTask.get(), &Task::failed, this, &MinecraftAccount::authFailed); connect(m_currentTask.get(), &Task::aborted, this, [this] { authFailed(tr("Aborted")); }); emit activityChanged(true); return m_currentTask; } shared_qobject_ptr MinecraftAccount::currentTask() { return m_currentTask; } void MinecraftAccount::authSucceeded() { m_currentTask.reset(); emit changed(); emit activityChanged(false); } void MinecraftAccount::authFailed(QString reason) { switch (m_currentTask->taskState()) { case AccountTaskState::STATE_OFFLINE: case AccountTaskState::STATE_DISABLED: { // NOTE: user will need to fix this themselves. } case AccountTaskState::STATE_FAILED_SOFT: { // NOTE: this doesn't do much. There was an error of some sort. } break; case AccountTaskState::STATE_FAILED_HARD: { if (accountType() == AccountType::MSA) { data.msaToken.token = QString(); data.msaToken.refresh_token = QString(); data.msaToken.validity = Validity::None; data.validity_ = Validity::None; } else { data.yggdrasilToken.token = QString(); data.yggdrasilToken.validity = Validity::None; data.validity_ = Validity::None; } emit changed(); } break; case AccountTaskState::STATE_FAILED_GONE: { data.validity_ = Validity::None; emit changed(); } break; case AccountTaskState::STATE_WORKING: { data.accountState = AccountState::Unchecked; } break; case AccountTaskState::STATE_CREATED: case AccountTaskState::STATE_SUCCEEDED: { // Not reachable here, as they are not failures. } } m_currentTask.reset(); emit activityChanged(false); } QString MinecraftAccount::displayName() const { if (const QList validStates{ AccountState::Unchecked, AccountState::Working, AccountState::Offline, AccountState::Online }; !validStates.contains(accountState())) { return QString("âš  %1").arg(profileName()); } return profileName(); } bool MinecraftAccount::isActive() const { return !m_currentTask.isNull(); } bool MinecraftAccount::shouldRefresh() const { /* * Never refresh accounts that are being used by the game, it breaks the game session. * Always refresh accounts that have not been refreshed yet during this session. * Don't refresh broken accounts. * Refresh accounts that would expire in the next 12 hours (fresh token validity is 24 hours). */ if (isInUse()) { return false; } switch (data.validity_) { case Validity::Certain: { break; } case Validity::None: { return false; } case Validity::Assumed: { return true; } } auto now = QDateTime::currentDateTimeUtc(); auto issuedTimestamp = data.yggdrasilToken.issueInstant; auto expiresTimestamp = data.yggdrasilToken.notAfter; if (!expiresTimestamp.isValid()) { expiresTimestamp = issuedTimestamp.addSecs(24 * 3600); } if (now.secsTo(expiresTimestamp) < (12 * 3600)) { return true; } return false; } void MinecraftAccount::fillSession(AuthSessionPtr session) { // volatile auth token session->access_token = data.accessToken(); // profile name session->player_name = data.profileName(); // profile ID session->uuid = data.profileId(); if (session->uuid.isEmpty()) session->uuid = uuidFromUsername(session->player_name).toString(QUuid::Id128); // 'legacy' or 'mojang', depending on account type session->user_type = typeString(); if (!session->access_token.isEmpty()) { session->session = "token:" + data.accessToken() + ":" + data.profileId(); } else { session->session = "-"; } } void MinecraftAccount::decrementUses() { Usable::decrementUses(); if (!isInUse()) { emit changed(); // FIXME: we now need a better way to identify accounts... qWarning() << "Profile" << data.profileId() << "is no longer in use."; } } void MinecraftAccount::incrementUses() { bool wasInUse = isInUse(); Usable::incrementUses(); if (!wasInUse) { emit changed(); // FIXME: we now need a better way to identify accounts... qWarning() << "Profile" << data.profileId() << "is now in use."; } } QUuid MinecraftAccount::uuidFromUsername(QString username) { auto input = QString("OfflinePlayer:%1").arg(username).toUtf8(); // basically a reimplementation of Java's UUID#nameUUIDFromBytes QByteArray digest = QCryptographicHash::hash(input, QCryptographicHash::Md5); auto bOr = [](QByteArray& array, qsizetype index, uint8_t value) { array[index] |= value; }; auto bAnd = [](QByteArray& array, qsizetype index, uint8_t value) { array[index] &= value; }; bAnd(digest, 6, 0x0f); // clear version bOr(digest, 6, 0x30); // set to version 3 bAnd(digest, 8, 0x3f); // clear variant bOr(digest, 8, 0x80); // set to IETF variant return QUuid::fromRfc4122(digest); } PrismLauncher-11.0.3/launcher/minecraft/auth/AuthSession.cpp0000644000175100017510000000143515224505336023451 0ustar runnerrunner#include "AuthSession.h" #include #include #include #include QString AuthSession::serializeUserProperties() { QJsonObject userAttrs; /* for (auto key : u.properties.keys()) { auto array = QJsonArray::fromStringList(u.properties.values(key)); userAttrs.insert(key, array); } */ QJsonDocument value(userAttrs); return value.toJson(QJsonDocument::Compact); } bool AuthSession::MakeOffline(QString offline_playername) { session = "-"; access_token = "0"; player_name = offline_playername; return true; } void AuthSession::MakeDemo(QString name, QString u) { uuid = u; session = "-"; access_token = "0"; player_name = name; launchMode = LaunchMode::Demo; }; PrismLauncher-11.0.3/launcher/minecraft/auth/AuthSession.h0000644000175100017510000000121215224505336023107 0ustar runnerrunner#pragma once #include #include #include "LaunchMode.h" class MinecraftAccount; struct AuthSession { bool MakeOffline(QString offline_playername); void MakeDemo(QString name, QString uuid); QString serializeUserProperties(); // combined session ID QString session; // volatile auth token QString access_token; // profile name QString player_name; // profile ID QString uuid; // 'msa' or 'offline', depending on account type QString user_type; // the actual launch mode for this session LaunchMode launchMode; }; using AuthSessionPtr = std::shared_ptr; PrismLauncher-11.0.3/launcher/minecraft/auth/AccountData.h0000644000175100017510000000651115224505336023037 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include enum class Validity { None, Assumed, Certain }; struct Token { QDateTime issueInstant; QDateTime notAfter; QString token; QString refresh_token; QVariantMap extra; Validity validity = Validity::None; bool persistent = true; }; struct Skin { QString id; QString url; QString variant; QByteArray data; }; struct Cape { QString id; QString url; QString alias; QByteArray data; }; struct MinecraftEntitlement { bool ownsMinecraft = false; bool canPlayMinecraft = false; Validity validity = Validity::None; }; struct MinecraftProfile { QString id; QString name; Skin skin; QString currentCape; QMap capes; Validity validity = Validity::None; }; enum class AccountType { MSA, Offline }; enum class AccountState { Unchecked, Offline, Working, Online, Disabled, Errored, Expired, Gone }; struct AccountData { QJsonObject saveState() const; bool resumeStateFromV3(QJsonObject data); //! Yggdrasil access token, as passed to the game. QString accessToken() const; QString profileId() const; QString profileName() const; QString lastError() const; AccountType type = AccountType::MSA; QString msaClientID; Token msaToken; Token userToken; Token mojangservicesToken; Token yggdrasilToken; MinecraftProfile minecraftProfile; MinecraftEntitlement minecraftEntitlement; Validity validity_ = Validity::None; // runtime only information (not saved with the account) QString internalId; QString errorString; QNetworkReply::NetworkError networkError = QNetworkReply::NoError; AccountState accountState = AccountState::Unchecked; }; PrismLauncher-11.0.3/launcher/minecraft/auth/AccountData.cpp0000644000175100017510000002713515224505336023377 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AccountData.h" #include #include #include #include #include namespace { void tokenToJSONV3(QJsonObject& parent, const Token& t, const char* tokenName) { if (!t.persistent) { return; } QJsonObject out; if (t.issueInstant.isValid()) { out["iat"] = QJsonValue(t.issueInstant.toMSecsSinceEpoch() / 1000); } if (t.notAfter.isValid()) { out["exp"] = QJsonValue(t.notAfter.toMSecsSinceEpoch() / 1000); } bool save = false; if (!t.token.isEmpty()) { out["token"] = QJsonValue(t.token); save = true; } if (!t.refresh_token.isEmpty()) { out["refresh_token"] = QJsonValue(t.refresh_token); save = true; } if (t.extra.size()) { out["extra"] = QJsonObject::fromVariantMap(t.extra); save = true; } if (save) { parent[tokenName] = out; } } Token tokenFromJSONV3(const QJsonObject& parent, const char* tokenName) { Token out; auto tokenObject = parent.value(tokenName).toObject(); if (tokenObject.isEmpty()) { return out; } auto issueInstant = tokenObject.value("iat"); if (issueInstant.isDouble()) { out.issueInstant = QDateTime::fromMSecsSinceEpoch(((int64_t)issueInstant.toDouble()) * 1000); } auto notAfter = tokenObject.value("exp"); if (notAfter.isDouble()) { out.notAfter = QDateTime::fromMSecsSinceEpoch(((int64_t)notAfter.toDouble()) * 1000); } auto token = tokenObject.value("token"); if (token.isString()) { out.token = token.toString(); out.validity = Validity::Assumed; } auto refresh_token = tokenObject.value("refresh_token"); if (refresh_token.isString()) { out.refresh_token = refresh_token.toString(); } auto extra = tokenObject.value("extra"); if (extra.isObject()) { out.extra = extra.toObject().toVariantMap(); } return out; } void profileToJSONV3(QJsonObject& parent, MinecraftProfile p, const char* tokenName) { if (p.id.isEmpty()) { return; } QJsonObject out; out["id"] = QJsonValue(p.id); out["name"] = QJsonValue(p.name); if (!p.currentCape.isEmpty()) { out["cape"] = p.currentCape; } { QJsonObject skinObj; skinObj["id"] = p.skin.id; skinObj["url"] = p.skin.url; skinObj["variant"] = p.skin.variant; if (p.skin.data.size()) { skinObj["data"] = QString::fromLatin1(p.skin.data.toBase64()); } out["skin"] = skinObj; } QJsonArray capesArray; for (auto& cape : p.capes) { QJsonObject capeObj; capeObj["id"] = cape.id; capeObj["url"] = cape.url; capeObj["alias"] = cape.alias; if (cape.data.size()) { capeObj["data"] = QString::fromLatin1(cape.data.toBase64()); } capesArray.push_back(capeObj); } out["capes"] = capesArray; parent[tokenName] = out; } MinecraftProfile profileFromJSONV3(const QJsonObject& parent, const char* tokenName) { MinecraftProfile out; auto tokenObject = parent.value(tokenName).toObject(); if (tokenObject.isEmpty()) { return out; } { auto idV = tokenObject.value("id"); auto nameV = tokenObject.value("name"); if (!idV.isString() || !nameV.isString()) { qWarning() << "mandatory profile attributes are missing or of unexpected type"; return MinecraftProfile(); } out.name = nameV.toString(); out.id = idV.toString(); } { auto skinV = tokenObject.value("skin"); if (!skinV.isObject()) { qWarning() << "skin is missing"; return MinecraftProfile(); } auto skinObj = skinV.toObject(); auto idV = skinObj.value("id"); auto urlV = skinObj.value("url"); auto variantV = skinObj.value("variant"); if (!idV.isString() || !urlV.isString() || !variantV.isString()) { qWarning() << "mandatory skin attributes are missing or of unexpected type"; return MinecraftProfile(); } out.skin.id = idV.toString(); out.skin.url = urlV.toString(); out.skin.url.replace("http://textures.minecraft.net", "https://textures.minecraft.net"); out.skin.variant = variantV.toString(); // data for skin is optional auto dataV = skinObj.value("data"); if (dataV.isString()) { // TODO: validate base64 out.skin.data = QByteArray::fromBase64(dataV.toString().toLatin1()); } else if (!dataV.isUndefined()) { qWarning() << "skin data is something unexpected"; return MinecraftProfile(); } } { auto capesV = tokenObject.value("capes"); if (!capesV.isArray()) { qWarning() << "capes is not an array!"; return MinecraftProfile(); } auto capesArray = capesV.toArray(); for (auto capeV : capesArray) { if (!capeV.isObject()) { qWarning() << "cape is not an object!"; return MinecraftProfile(); } auto capeObj = capeV.toObject(); auto idV = capeObj.value("id"); auto urlV = capeObj.value("url"); auto aliasV = capeObj.value("alias"); if (!idV.isString() || !urlV.isString() || !aliasV.isString()) { qWarning() << "mandatory skin attributes are missing or of unexpected type"; return MinecraftProfile(); } Cape cape; cape.id = idV.toString(); cape.url = urlV.toString(); cape.url.replace("http://textures.minecraft.net", "https://textures.minecraft.net"); cape.alias = aliasV.toString(); // data for cape is optional. auto dataV = capeObj.value("data"); if (dataV.isString()) { // TODO: validate base64 cape.data = QByteArray::fromBase64(dataV.toString().toLatin1()); } else if (!dataV.isUndefined()) { qWarning() << "cape data is something unexpected"; return MinecraftProfile(); } out.capes[cape.id] = cape; } } // current cape { auto capeV = tokenObject.value("cape"); if (capeV.isString()) { auto currentCape = capeV.toString(); if (out.capes.contains(currentCape)) { out.currentCape = currentCape; } } } out.validity = Validity::Assumed; return out; } void entitlementToJSONV3(QJsonObject& parent, MinecraftEntitlement p) { if (p.validity == Validity::None) { return; } QJsonObject out; out["ownsMinecraft"] = QJsonValue(p.ownsMinecraft); out["canPlayMinecraft"] = QJsonValue(p.canPlayMinecraft); parent["entitlement"] = out; } bool entitlementFromJSONV3(const QJsonObject& parent, MinecraftEntitlement& out) { auto entitlementObject = parent.value("entitlement").toObject(); if (entitlementObject.isEmpty()) { return false; } { auto ownsMinecraftV = entitlementObject.value("ownsMinecraft"); auto canPlayMinecraftV = entitlementObject.value("canPlayMinecraft"); if (!ownsMinecraftV.isBool() || !canPlayMinecraftV.isBool()) { qWarning() << "mandatory attributes are missing or of unexpected type"; return false; } out.canPlayMinecraft = canPlayMinecraftV.toBool(false); out.ownsMinecraft = ownsMinecraftV.toBool(false); out.validity = Validity::Assumed; } return true; } } // namespace bool AccountData::resumeStateFromV3(QJsonObject data) { auto typeV = data.value("type"); if (!typeV.isString()) { qWarning() << "Failed to parse account data: type is missing."; return false; } auto typeS = typeV.toString(); if (typeS == "MSA") { type = AccountType::MSA; } else if (typeS == "Offline") { type = AccountType::Offline; } else { qWarning() << "Failed to parse account data: type is not recognized."; return false; } if (type == AccountType::MSA) { auto clientIDV = data.value("msa-client-id"); if (clientIDV.isString()) { msaClientID = clientIDV.toString(); } // leave msaClientID empty if it doesn't exist or isn't a string msaToken = tokenFromJSONV3(data, "msa"); userToken = tokenFromJSONV3(data, "utoken"); mojangservicesToken = tokenFromJSONV3(data, "xrp-mc"); } yggdrasilToken = tokenFromJSONV3(data, "ygg"); // versions before 7.2 used "offline" as the offline token if (yggdrasilToken.token == "offline") yggdrasilToken.token = "0"; minecraftProfile = profileFromJSONV3(data, "profile"); if (!entitlementFromJSONV3(data, minecraftEntitlement)) { if (minecraftProfile.validity != Validity::None) { minecraftEntitlement.canPlayMinecraft = true; minecraftEntitlement.ownsMinecraft = true; minecraftEntitlement.validity = Validity::Assumed; } } validity_ = minecraftProfile.validity; return true; } QJsonObject AccountData::saveState() const { QJsonObject output; if (type == AccountType::MSA) { output["type"] = "MSA"; output["msa-client-id"] = msaClientID; tokenToJSONV3(output, msaToken, "msa"); tokenToJSONV3(output, userToken, "utoken"); tokenToJSONV3(output, mojangservicesToken, "xrp-mc"); } else if (type == AccountType::Offline) { output["type"] = "Offline"; } tokenToJSONV3(output, yggdrasilToken, "ygg"); profileToJSONV3(output, minecraftProfile, "profile"); entitlementToJSONV3(output, minecraftEntitlement); return output; } QString AccountData::accessToken() const { return yggdrasilToken.token; } QString AccountData::profileId() const { return minecraftProfile.id; } QString AccountData::profileName() const { if (minecraftProfile.name.size() == 0) { return QObject::tr("No Minecraft profile"); } return minecraftProfile.name; } QString AccountData::lastError() const { return errorString; } PrismLauncher-11.0.3/launcher/minecraft/World.cpp0000644000175100017510000003543115224505336021335 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "World.h" #include #include #include #include #include #include #include #include #include #include #include "GZip.h" #include #include #include "FileSystem.h" #include "PSaveFile.h" #include "archive/ArchiveReader.h" using std::nullopt; using std::optional; GameType::GameType(std::optional original) : original(original) { if (!original) { return; } switch (*original) { case 0: type = GameType::Survival; break; case 1: type = GameType::Creative; break; case 2: type = GameType::Adventure; break; case 3: type = GameType::Spectator; break; default: break; } } QString GameType::toTranslatedString() const { switch (type) { case GameType::Survival: return QCoreApplication::translate("GameType", "Survival"); case GameType::Creative: return QCoreApplication::translate("GameType", "Creative"); case GameType::Adventure: return QCoreApplication::translate("GameType", "Adventure"); case GameType::Spectator: return QCoreApplication::translate("GameType", "Spectator"); default: break; } if (original) { return QCoreApplication::translate("GameType", "Unknown (%1)").arg(*original); } return QCoreApplication::translate("GameType", "Undefined"); } QString GameType::toLogString() const { switch (type) { case GameType::Survival: return "Survival"; case GameType::Creative: return "Creative"; case GameType::Adventure: return "Adventure"; case GameType::Spectator: return "Spectator"; default: break; } if (original) { return QString("Unknown (%1)").arg(*original); } return "Undefined"; } std::unique_ptr parseLevelDat(QByteArray data) { QByteArray output; if (!GZip::unzip(data, output)) { return nullptr; } std::istringstream foo(std::string(output.constData(), output.size())); try { auto pair = nbt::io::read_compound(foo); if (pair.first != "") return nullptr; if (pair.second == nullptr) return nullptr; return std::move(pair.second); } catch (const nbt::io::input_error& e) { qWarning() << "Unable to parse level.dat:" << e.what(); return nullptr; } } QByteArray serializeLevelDat(nbt::tag_compound* levelInfo) { std::ostringstream s; nbt::io::write_tag("", *levelInfo, s); QByteArray val(s.str().data(), (int)s.str().size()); return val; } QString getDatFromFS(const QFileInfo& root, QString file) { QDir worldDir(root.filePath()); if (!root.isDir() || !worldDir.exists(file)) { return QString(); } return worldDir.absoluteFilePath(file); } QString getLevelDatFromFS(const QFileInfo& file) { return getDatFromFS(file, "level.dat"); } QByteArray getDatDataFromFS(const QFileInfo& root, QString file) { auto fullFilePath = getDatFromFS(root, file); if (fullFilePath.isNull()) { return QByteArray(); } QFile f(fullFilePath); if (!f.open(QIODevice::ReadOnly)) { return QByteArray(); } return f.readAll(); } QByteArray getLevelDatDataFromFS(const QFileInfo& file) { return getDatDataFromFS(file, "level.dat"); } QByteArray getWorldGenDataFromFS(const QFileInfo& file) { return getDatDataFromFS(file, "data/minecraft/world_gen_settings.dat"); } bool putLevelDatDataToFS(const QFileInfo& file, QByteArray& data) { auto fullFilePath = getLevelDatFromFS(file); if (fullFilePath.isNull()) { return false; } PSaveFile f(fullFilePath); if (!f.open(QIODevice::WriteOnly)) { return false; } QByteArray compressed; if (!GZip::zip(data, compressed)) { return false; } if (f.write(compressed) != compressed.size()) { f.cancelWriting(); return false; } return f.commit(); } World::World(const QFileInfo& file) { repath(file); } void World::repath(const QFileInfo& file) { m_containerFile = file; m_folderName = file.fileName(); if (file.isFile() && file.suffix() == "zip") { m_iconFile = QString(); readFromZip(file); } else if (file.isDir()) { QFileInfo assumedIconPath(file.absoluteFilePath() + "/icon.png"); if (assumedIconPath.exists()) { m_iconFile = assumedIconPath.absoluteFilePath(); } readFromFS(file); } } bool World::resetIcon() { if (m_iconFile.isNull()) { return false; } if (QFile(m_iconFile).remove()) { m_iconFile = QString(); return true; } return false; } int64_t loadSeed(QByteArray data); void World::readFromFS(const QFileInfo& file) { auto bytes = getLevelDatDataFromFS(file); if (bytes.isEmpty()) { m_isValid = false; return; } loadFromLevelDat(bytes); m_levelDatTime = file.lastModified(); if (m_randomSeed == 0) { bytes = getWorldGenDataFromFS(file); if (!bytes.isEmpty()) { m_randomSeed = loadSeed(bytes); } } } void World::readFromZip(const QFileInfo& file) { MMCZip::ArchiveReader r(file.absoluteFilePath()); m_isValid = false; r.parse([this](MMCZip::ArchiveReader::File* file, bool& stop) { const QString levelDat = "level.dat"; auto filePath = file->filename(); QFileInfo fi(filePath); if (fi.fileName().compare(levelDat, Qt::CaseInsensitive) == 0) { m_containerOffsetPath = filePath.chopped(levelDat.length()); m_levelDatTime = file->dateTime(); loadFromLevelDat(file->readAll()); m_isValid = true; stop = true; } return true; }); } bool World::install(const QString& to, const QString& name) { auto finalPath = FS::PathCombine(to, FS::DirNameFromString(m_actualName, to)); if (!FS::ensureFolderPathExists(finalPath)) { return false; } bool ok = false; if (m_containerFile.isFile()) { MMCZip::ArchiveReader zip(m_containerFile.absoluteFilePath()); ok = !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath); } else if (m_containerFile.isDir()) { QString from = m_containerFile.filePath(); ok = FS::copy(from, finalPath)(); } if (ok && !name.isEmpty() && m_actualName != name) { QFileInfo finalPathInfo(finalPath); World newWorld(finalPathInfo); if (newWorld.isValid()) { newWorld.rename(name); } } return ok; } bool World::rename(const QString& newName) { if (m_containerFile.isFile()) { return false; } auto data = getLevelDatDataFromFS(m_containerFile); if (data.isEmpty()) { return false; } auto worldData = parseLevelDat(data); if (!worldData) { return false; } auto& val = worldData->at("Data"); if (val.get_type() != nbt::tag_type::Compound) { return false; } auto& dataCompound = val.as(); dataCompound.put("LevelName", nbt::value_initializer(newName.toUtf8().data())); data = serializeLevelDat(worldData.get()); putLevelDatDataToFS(m_containerFile, data); m_actualName = newName; QDir parentDir(m_containerFile.absoluteFilePath()); parentDir.cdUp(); QFile container(m_containerFile.absoluteFilePath()); auto dirName = FS::DirNameFromString(m_actualName, parentDir.absolutePath()); container.rename(parentDir.absoluteFilePath(dirName)); return true; } namespace { optional read_string(nbt::value& parent, const char* name) { try { auto& namedValue = parent.at(name); if (namedValue.get_type() != nbt::tag_type::String) { return nullopt; } auto& tag_str = namedValue.as(); return QString::fromUtf8(tag_str.get()); } catch ([[maybe_unused]] const std::out_of_range& e) { // fallback for old world formats qWarning() << "String NBT tag" << name << "could not be found."; return nullopt; } catch ([[maybe_unused]] const std::bad_cast& e) { // type mismatch qWarning() << "NBT tag" << name << "could not be converted to string."; return nullopt; } } optional read_long(nbt::value& parent, const char* name) { try { auto& namedValue = parent.at(name); if (namedValue.get_type() != nbt::tag_type::Long) { return nullopt; } auto& tag_str = namedValue.as(); return tag_str.get(); } catch ([[maybe_unused]] const std::out_of_range& e) { // fallback for old world formats qWarning() << "Long NBT tag" << name << "could not be found."; return nullopt; } catch ([[maybe_unused]] const std::bad_cast& e) { // type mismatch qWarning() << "NBT tag" << name << "could not be converted to long."; return nullopt; } } optional read_int(nbt::value& parent, const char* name) { try { auto& namedValue = parent.at(name); if (namedValue.get_type() != nbt::tag_type::Int) { return nullopt; } auto& tag_str = namedValue.as(); return tag_str.get(); } catch ([[maybe_unused]] const std::out_of_range& e) { // fallback for old world formats qWarning() << "Int NBT tag" << name << "could not be found."; return nullopt; } catch ([[maybe_unused]] const std::bad_cast& e) { // type mismatch qWarning() << "NBT tag" << name << "could not be converted to int."; return nullopt; } } GameType read_gametype(nbt::value& parent, const char* name) { return GameType(read_int(parent, name)); } } // namespace int64_t loadSeed(QByteArray data) { auto levelData = parseLevelDat(data); if (!levelData) { return 0; } nbt::value* valPtr = nullptr; try { valPtr = &levelData->at("data"); } catch (const std::out_of_range&) { return 0; } nbt::value& val = *valPtr; try { return read_long(val, "seed").value_or(0); } catch (const std::out_of_range&) { } return 0; } void World::loadFromLevelDat(QByteArray data) { auto levelData = parseLevelDat(data); if (!levelData) { m_isValid = false; return; } nbt::value* valPtr = nullptr; try { valPtr = &levelData->at("Data"); } catch (const std::out_of_range& e) { qWarning().nospace() << "Unable to read NBT tags from " << m_folderName << ": " << e.what(); m_isValid = false; return; } nbt::value& val = *valPtr; m_isValid = val.get_type() == nbt::tag_type::Compound; if (!m_isValid) return; auto name = read_string(val, "LevelName"); m_actualName = name ? *name : m_folderName; auto timestamp = read_long(val, "LastPlayed"); m_lastPlayed = timestamp ? QDateTime::fromMSecsSinceEpoch(*timestamp) : m_levelDatTime; m_gameType = read_gametype(val, "GameType"); optional randomSeed; try { auto& WorldGen_val = val.at("WorldGenSettings"); randomSeed = read_long(WorldGen_val, "seed"); } catch (const std::out_of_range&) { } if (!randomSeed) { randomSeed = read_long(val, "RandomSeed"); } m_randomSeed = randomSeed ? *randomSeed : 0; qDebug() << "World Name:" << m_actualName; qDebug() << "Last Played:" << m_lastPlayed.toString(); if (randomSeed) { qDebug() << "Seed:" << *randomSeed; } qDebug() << "Size:" << m_size; qDebug() << "GameType:" << m_gameType.toLogString(); } bool World::replace(World& with) { if (!destroy()) return false; bool success = FS::copy(with.m_containerFile.filePath(), m_containerFile.path())(); if (success) { m_folderName = with.m_folderName; m_containerFile.refresh(); } return success; } bool World::destroy() { if (!m_isValid) return false; if (FS::trash(m_containerFile.filePath())) return true; if (m_containerFile.isDir()) { QDir d(m_containerFile.filePath()); return d.removeRecursively(); } else if (m_containerFile.isFile()) { QFile file(m_containerFile.absoluteFilePath()); return file.remove(); } return true; } bool World::operator==(const World& other) const { return m_isValid == other.m_isValid && folderName() == other.folderName(); } bool World::isSymLinkUnder(const QString& instPath) const { if (isSymLink()) return true; auto instDir = QDir(instPath); auto relAbsPath = instDir.relativeFilePath(m_containerFile.absoluteFilePath()); auto relCanonPath = instDir.relativeFilePath(m_containerFile.canonicalFilePath()); return relAbsPath != relCanonPath; } bool World::isMoreThanOneHardLink() const { if (m_containerFile.isDir()) { return FS::hardLinkCount(QDir(m_containerFile.absoluteFilePath()).filePath("level.dat")) > 1; } return FS::hardLinkCount(m_containerFile.absoluteFilePath()) > 1; } void World::setSize(int64_t size) { m_size = size; } PrismLauncher-11.0.3/launcher/minecraft/launch/0000755000175100017510000000000015224505336021006 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/minecraft/launch/EnsureOfflineLibraries.h0000644000175100017510000000224015224505336025556 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2026 Octol1ttle * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "launch/LaunchStep.h" #include "minecraft/MinecraftInstance.h" class EnsureOfflineLibraries : public LaunchStep { Q_OBJECT public: explicit EnsureOfflineLibraries(LaunchTask* parent, MinecraftInstance* instance); ~EnsureOfflineLibraries() override = default; void executeTask() override; bool canAbort() const override { return false; } private: MinecraftInstance* m_instance; }; PrismLauncher-11.0.3/launcher/minecraft/launch/LauncherPartLaunch.h0000644000175100017510000000301515224505336024701 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include "MinecraftTarget.h" class LauncherPartLaunch : public LaunchStep { Q_OBJECT public: explicit LauncherPartLaunch(LaunchTask* parent); virtual ~LauncherPartLaunch() = default; virtual void executeTask(); virtual bool abort(); virtual void proceed(); virtual bool canAbort() const { return true; } void setWorkingDirectory(const QString& wd); void setAuthSession(AuthSessionPtr session) { m_session = session; } void setTargetToJoin(MinecraftTarget::Ptr targetToJoin) { m_targetToJoin = std::move(targetToJoin); } private slots: void on_state(LoggedProcess::State state); private: LoggedProcess m_process; QString m_command; AuthSessionPtr m_session; QString m_launchScript; MinecraftTarget::Ptr m_targetToJoin; bool mayProceed = false; }; PrismLauncher-11.0.3/launcher/minecraft/launch/MinecraftTarget.h0000644000175100017510000000155215224505336024241 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include struct MinecraftTarget { QString address; quint16 port; QString world; static MinecraftTarget parse(const QString& fullAddress, bool useWorld); using Ptr = std::shared_ptr; }; PrismLauncher-11.0.3/launcher/minecraft/launch/ReconstructAssets.h0000644000175100017510000000166015224505336024660 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include class ReconstructAssets : public LaunchStep { Q_OBJECT public: explicit ReconstructAssets(LaunchTask* parent) : LaunchStep(parent) {}; virtual ~ReconstructAssets() {}; void executeTask() override; bool canAbort() const override { return false; } }; PrismLauncher-11.0.3/launcher/minecraft/launch/VerifyJavaInstall.cpp0000644000175100017510000000720015224505336025106 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "VerifyJavaInstall.h" #include #include "Application.h" #include "MessageLevel.h" #include "java/JavaInstall.h" #include "java/JavaInstallList.h" #include "java/JavaVersion.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" void VerifyJavaInstall::executeTask() { auto instance = m_parent->instance(); auto packProfile = instance->getPackProfile(); auto settings = instance->settings(); auto storedVersion = settings->get("JavaVersion").toString(); auto ignoreCompatibility = settings->get("IgnoreJavaCompatibility").toBool(); auto javaArchitecture = settings->get("JavaArchitecture").toString(); auto maxMemAlloc = settings->get("MaxMemAlloc").toInt(); if (javaArchitecture == "32" && maxMemAlloc > 2048) { emit logLine(tr("Max memory allocation exceeds the supported value.\n" "The selected installation of Java is 32-bit and doesn't support more than 2048MiB of RAM.\n" "The instance may not start due to this."), MessageLevel::Error); } auto compatibleMajors = packProfile->getProfile()->getCompatibleJavaMajors(); JavaVersion javaVersion(storedVersion); if (compatibleMajors.isEmpty() || compatibleMajors.contains(javaVersion.major())) { emitSucceeded(); return; } if (ignoreCompatibility) { emit logLine(tr("Java major version is incompatible. Things might break.\n"), MessageLevel::Warning); emitSucceeded(); return; } emit logLine(tr("This instance is not compatible with Java version %1.\n" "Please switch to one of the following Java versions for this instance:") .arg(javaVersion.major()), MessageLevel::Error); for (auto major : compatibleMajors) { emit logLine(tr("Java version %1").arg(major), MessageLevel::Error); } emit logLine(tr("Go to instance Java settings to change your Java version or disable the Java compatibility check if you know what " "you're doing."), MessageLevel::Error); emitFailed(QString("Incompatible Java major version")); } PrismLauncher-11.0.3/launcher/minecraft/launch/CreateGameFolders.cpp0000644000175100017510000000151515224505336025030 0ustar runnerrunner#include "CreateGameFolders.h" #include "FileSystem.h" #include "launch/LaunchTask.h" #include "minecraft/MinecraftInstance.h" CreateGameFolders::CreateGameFolders(LaunchTask* parent) : LaunchStep(parent) {} void CreateGameFolders::executeTask() { auto instance = m_parent->instance(); if (!FS::ensureFolderPathExists(instance->gameRoot())) { emit logLine("Couldn't create the main game folder", MessageLevel::Error); emitFailed(tr("Couldn't create the main game folder")); return; } // HACK: this is a workaround for MCL-3732 - 'server-resource-packs' folder is created. if (!FS::ensureFolderPathExists(FS::PathCombine(instance->gameRoot(), "server-resource-packs"))) { emit logLine("Couldn't create the 'server-resource-packs' folder", MessageLevel::Error); } emitSucceeded(); } PrismLauncher-11.0.3/launcher/minecraft/launch/ModMinecraftJar.h0000644000175100017510000000177315224505336024174 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include class ModMinecraftJar : public LaunchStep { Q_OBJECT public: explicit ModMinecraftJar(LaunchTask* parent) : LaunchStep(parent) {}; virtual ~ModMinecraftJar() {}; virtual void executeTask() override; virtual bool canAbort() const override { return false; } void finalize() override; private: bool removeJar(); }; PrismLauncher-11.0.3/launcher/minecraft/launch/CreateGameFolders.h0000644000175100017510000000202615224505336024473 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include // Create the main .minecraft for the instance and any other necessary folders class CreateGameFolders : public LaunchStep { Q_OBJECT public: explicit CreateGameFolders(LaunchTask* parent); virtual ~CreateGameFolders() {}; virtual void executeTask(); virtual bool canAbort() const { return false; } }; PrismLauncher-11.0.3/launcher/minecraft/launch/ClaimAccount.cpp0000644000175100017510000000112215224505336024050 0ustar runnerrunner#include "ClaimAccount.h" #include #include "Application.h" #include "minecraft/auth/AccountList.h" ClaimAccount::ClaimAccount(LaunchTask* parent, AuthSessionPtr session) : LaunchStep(parent) { if (session->launchMode == LaunchMode::Normal) { auto accounts = APPLICATION->accounts(); m_account = accounts->getAccountByProfileName(session->player_name); } } void ClaimAccount::executeTask() { if (m_account) { lock.reset(new UseLock(m_account.get())); } emitSucceeded(); } void ClaimAccount::finalize() { lock.reset(); } PrismLauncher-11.0.3/launcher/minecraft/launch/ScanModFolders.h0000644000175100017510000000224615224505336024026 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include class ScanModFolders : public LaunchStep { Q_OBJECT public: explicit ScanModFolders(LaunchTask* parent) : LaunchStep(parent) {}; virtual ~ScanModFolders() {}; virtual void executeTask() override; virtual bool canAbort() const override { return false; } private slots: void coreModsDone(); void modsDone(); void nilModsDone(); private: void checkDone(); private: // DATA bool m_modsDone = false; bool m_nilModsDone = false; bool m_coreModsDone = false; }; PrismLauncher-11.0.3/launcher/minecraft/launch/ClaimAccount.h0000644000175100017510000000206415224505336023523 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include class ClaimAccount : public LaunchStep { Q_OBJECT public: explicit ClaimAccount(LaunchTask* parent, AuthSessionPtr session); virtual ~ClaimAccount() = default; void executeTask() override; void finalize() override; bool canAbort() const override { return false; } private: std::unique_ptr lock; MinecraftAccountPtr m_account; }; PrismLauncher-11.0.3/launcher/minecraft/launch/AutoInstallJava.h0000644000175100017510000000444015224505336024222 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "meta/Version.h" #include "minecraft/MinecraftInstance.h" #include "tasks/Task.h" class AutoInstallJava : public LaunchStep { Q_OBJECT public: explicit AutoInstallJava(LaunchTask* parent); ~AutoInstallJava() override = default; void executeTask() override; bool canAbort() const override { return m_current_task ? m_current_task->canAbort() : false; } bool abort() override; protected: void setJavaPath(QString path); void setJavaPathFromPartial(); void downloadJava(Meta::Version::Ptr version, QString javaName); void tryNextMajorJava(); private: MinecraftInstance* m_instance; Task::Ptr m_current_task; qsizetype m_majorJavaVersionIndex = 0; const QString m_supported_arch; }; PrismLauncher-11.0.3/launcher/minecraft/launch/EnsureAvailableMemory.cpp0000644000175100017510000001057015224505336025750 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2026 Octol1ttle * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "EnsureAvailableMemory.h" #include "HardwareInfo.h" #include "ui/dialogs/CustomMessageBox.h" EnsureAvailableMemory::EnsureAvailableMemory(LaunchTask* parent, MinecraftInstance* instance) : LaunchStep(parent), m_instance(instance) {} void EnsureAvailableMemory::executeTask() { #ifdef Q_OS_MACOS QString text; switch (MacOSHardwareInfo::memoryPressureLevel()) { case MacOSHardwareInfo::MemoryPressureLevel::Normal: emitSucceeded(); return; case MacOSHardwareInfo::MemoryPressureLevel::Warning: text = tr("The system is under increased memory pressure.\n" "This may lead to lag or slowdowns.\n" "If possible, close other applications before continuing.\n\n" "Launch anyway?"); break; case MacOSHardwareInfo::MemoryPressureLevel::Critical: text = tr("Your system is under critical memory pressure.\n" "This may lead to severe slowdowns, crashes or system instability.\n" "It is recommended to close other applications or restart your system.\n\n" "Launch anyway?"); break; } bool shouldAbort = false; if (m_instance->settings()->get("LowMemWarning").toBool()) { auto* dialog = CustomMessageBox::selectable(nullptr, tr("High memory pressure"), text, QMessageBox::Icon::Warning, QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::No); shouldAbort = dialog->exec() == QMessageBox::No; dialog->deleteLater(); } const auto message = tr("The system is under high memory pressure"); if (shouldAbort) { emit logLine(message, MessageLevel::Fatal); emitFailed(message); return; } emit logLine(message, MessageLevel::Warning); emitSucceeded(); #else const uint64_t available = HardwareInfo::availableRamMiB(); if (available == 0) { // could not read emitSucceeded(); return; } const uint64_t settingMin = m_instance->settings()->get("MinMemAlloc").toUInt(); const uint64_t settingMax = m_instance->settings()->get("MaxMemAlloc").toUInt(); const uint64_t max = std::max(settingMin, settingMax); if (static_cast(max) * 0.9 > static_cast(available)) { bool shouldAbort = false; if (m_instance->settings()->get("LowMemWarning").toBool()) { auto* dialog = CustomMessageBox::selectable( nullptr, tr("Low free memory"), tr("There might not be enough free RAM to launch this instance with the current memory settings.\n\n" "Maximum allocated: %1 MiB\nFree: %2 MiB (out of %3 MiB total)\n\n" "Launch anyway? This may cause slowdowns in the game and your system.") .arg(max) .arg(available) .arg(HardwareInfo::totalRamMiB()), QMessageBox::Icon::Warning, QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::No); shouldAbort = dialog->exec() == QMessageBox::No; dialog->deleteLater(); } const auto message = tr("Not enough RAM available to launch this instance"); if (shouldAbort) { emit logLine(message, MessageLevel::Fatal); emitFailed(message); return; } emit logLine(message, MessageLevel::Warning); } emitSucceeded(); #endif } PrismLauncher-11.0.3/launcher/minecraft/launch/ReconstructAssets.cpp0000644000175100017510000000226515224505336025215 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ReconstructAssets.h" #include "launch/LaunchTask.h" #include "minecraft/AssetsUtils.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" void ReconstructAssets::executeTask() { auto instance = m_parent->instance(); auto components = instance->getPackProfile(); auto profile = components->getProfile(); auto assets = profile->getMinecraftAssets(); if (!AssetsUtils::reconstructAssets(assets->id, instance->resourcesDir())) { emit logLine("Failed to reconstruct Minecraft assets.", MessageLevel::Error); } emitSucceeded(); } PrismLauncher-11.0.3/launcher/minecraft/launch/MinecraftTarget.cpp0000644000175100017510000000410515224505336024571 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MinecraftTarget.h" #include // FIXME: the way this is written, it can't ever do any sort of validation and can accept total junk MinecraftTarget MinecraftTarget::parse(const QString& fullAddress, bool useWorld) { if (useWorld) { MinecraftTarget target; target.world = fullAddress; return target; } QStringList split = fullAddress.split(":"); // The logic below replicates the exact logic minecraft uses for parsing server addresses. // While the conversion is not lossless and eats errors, it ensures the same behavior // within Minecraft and Prism Launcher when entering server addresses. if (fullAddress.startsWith("[")) { int bracket = fullAddress.indexOf("]"); if (bracket > 0) { QString ipv6 = fullAddress.mid(1, bracket - 1); QString port = fullAddress.mid(bracket + 1).trimmed(); if (port.startsWith(":") && !ipv6.isEmpty()) { port = port.mid(1); split = QStringList({ ipv6, port }); } else { split = QStringList({ ipv6 }); } } } if (split.size() > 2) { split = QStringList({ fullAddress }); } QString realAddress = split[0]; quint16 realPort = 25565; if (split.size() > 1) { bool ok; realPort = split[1].toUInt(&ok); if (!ok) { realPort = 25565; } } return MinecraftTarget{ realAddress, realPort }; } PrismLauncher-11.0.3/launcher/minecraft/launch/LauncherPartLaunch.cpp0000644000175100017510000002122115224505336025233 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "LauncherPartLaunch.h" #include #include #include "Application.h" #include "Commandline.h" #include "FileSystem.h" #include "launch/LaunchTask.h" #include "minecraft/MinecraftInstance.h" #ifdef Q_OS_LINUX #include "gamemode_client.h" #endif LauncherPartLaunch::LauncherPartLaunch(LaunchTask* parent) : LaunchStep(parent) , m_process(parent->instance()->getJavaVersion().defaultsToUtf8() ? QStringConverter::Utf8 : QStringConverter::System) { if (parent->instance()->settings()->get("CloseAfterLaunch").toBool()) { static const QRegularExpression s_settingUser(".*Setting user.+", QRegularExpression::CaseInsensitiveOption); std::shared_ptr connection{ new QMetaObject::Connection }; *connection = connect(&m_process, &LoggedProcess::log, this, [connection](const QStringList& lines, [[maybe_unused]] MessageLevel level) { qDebug() << lines; if (lines.filter(s_settingUser).length() != 0) { APPLICATION->closeAllWindows(); disconnect(*connection); } }); } connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines); connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state); } void LauncherPartLaunch::executeTask() { QString jarPath = APPLICATION->getJarPath("NewLaunch.jar"); if (jarPath.isEmpty()) { const char* reason = QT_TR_NOOP("Launcher library could not be found. Please check your installation."); emit logLine(tr(reason), MessageLevel::Fatal); emitFailed(tr(reason)); return; } auto instance = m_parent->instance(); QString legacyJarPath; if (instance->getLauncher() == "legacy" || instance->shouldApplyOnlineFixes()) { legacyJarPath = APPLICATION->getJarPath("NewLaunchLegacy.jar"); if (legacyJarPath.isEmpty()) { const char* reason = QT_TR_NOOP("Legacy launcher library could not be found. Please check your installation."); emit logLine(tr(reason), MessageLevel::Fatal); emitFailed(tr(reason)); return; } } m_launchScript = instance->createLaunchScript(m_session, m_targetToJoin); QStringList args = instance->javaArguments(); QString allArgs = args.join(" "); emit logLine("Java arguments:\n " + m_parent->censorPrivateInfo(allArgs) + "\n", MessageLevel::Launcher); auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString()); m_process.setProcessEnvironment(instance->createLaunchEnvironment()); // make detachable - this will keep the process running even if the object is destroyed m_process.setDetachable(true); auto classPath = instance->getClassPath(); classPath.prepend(jarPath); if (!legacyJarPath.isEmpty()) classPath.prepend(legacyJarPath); auto natPath = instance->getNativePath(); #ifdef Q_OS_WIN natPath = FS::getPathNameInLocal8bit(natPath); #endif args << "-Djava.library.path=" + natPath; args << "-cp"; #ifdef Q_OS_WIN QStringList processed; for (auto& item : classPath) { processed << FS::getPathNameInLocal8bit(item); } args << processed.join(';'); #else args << classPath.join(':'); #endif args << "org.prismlauncher.EntryPoint"; qDebug() << args.join(' '); QString wrapperCommandStr = instance->getWrapperCommand().trimmed(); if (!wrapperCommandStr.isEmpty()) { wrapperCommandStr = m_parent->substituteVariables(wrapperCommandStr); auto wrapperArgs = Commandline::splitArgs(wrapperCommandStr); auto wrapperCommand = wrapperArgs.takeFirst(); auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand); if (realWrapperCommand.isEmpty()) { const char* reason = QT_TR_NOOP("The wrapper command \"%1\" couldn't be found."); emit logLine(QString(reason).arg(wrapperCommand), MessageLevel::Fatal); emitFailed(tr(reason).arg(wrapperCommand)); return; } emit logLine("Wrapper command is:\n" + wrapperCommandStr + "\n\n", MessageLevel::Launcher); args.prepend(javaPath); m_process.start(wrapperCommand, wrapperArgs + args); } else { m_process.start(javaPath, args); } #ifdef Q_OS_LINUX if (instance->settings()->get("EnableFeralGamemode").toBool() && APPLICATION->capabilities() & Application::SupportsGameMode) { auto pid = m_process.processId(); if (pid) { gamemode_request_start_for(pid); } } #endif } void LauncherPartLaunch::on_state(LoggedProcess::State state) { switch (state) { case LoggedProcess::FailedToStart: { //: Error message displayed if instace can't start const char* reason = QT_TR_NOOP("Could not launch Minecraft: %1"); emit logLine(QString(reason).arg(m_process.errorString()), MessageLevel::Fatal); emitFailed(tr(reason).arg(m_process.errorString())); return; } case LoggedProcess::Aborted: case LoggedProcess::Crashed: { m_parent->setPid(-1); m_parent->instance()->setMinecraftRunning(false); emitFailed(tr("Game crashed.")); return; } case LoggedProcess::Finished: { auto instance = m_parent->instance(); if (instance->settings()->get("CloseAfterLaunch").toBool()) APPLICATION->showMainWindow(); m_parent->setPid(-1); m_parent->instance()->setMinecraftRunning(false); // if the exit code wasn't 0, report this as a crash auto exitCode = m_process.exitCode(); if (exitCode != 0) { emitFailed(tr("Game crashed.")); return; } // FIXME: make this work again // m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode)); // run post-exit emitSucceeded(); break; } case LoggedProcess::Running: emit logLine(QString("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::Launcher); m_parent->setPid(m_process.processId()); // send the launch script to the launcher part m_process.write(m_launchScript.toUtf8()); mayProceed = true; emit readyForLaunch(); break; default: break; } } void LauncherPartLaunch::setWorkingDirectory(const QString& wd) { m_process.setWorkingDirectory(wd); } void LauncherPartLaunch::proceed() { if (mayProceed) { m_parent->instance()->setMinecraftRunning(true); QString launchString("launch\n"); m_process.write(launchString.toUtf8()); mayProceed = false; } } bool LauncherPartLaunch::abort() { if (mayProceed) { mayProceed = false; QString launchString("abort\n"); m_process.write(launchString.toUtf8()); } else { auto state = m_process.state(); if (state == LoggedProcess::Running || state == LoggedProcess::Starting) { m_process.kill(); } } return true; } PrismLauncher-11.0.3/launcher/minecraft/launch/ScanModFolders.cpp0000644000175100017510000000525215224505336024361 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ScanModFolders.h" #include "FileSystem.h" #include "MMCZip.h" #include "launch/LaunchTask.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/mod/ModFolderModel.h" void ScanModFolders::executeTask() { auto m_inst = m_parent->instance(); auto loaders = m_inst->loaderModList(); connect(loaders, &ModFolderModel::updateFinished, this, &ScanModFolders::modsDone); if (!loaders->update()) { m_modsDone = true; } auto cores = m_inst->coreModList(); connect(cores, &ModFolderModel::updateFinished, this, &ScanModFolders::coreModsDone); if (!cores->update()) { m_coreModsDone = true; } auto nils = m_inst->nilModList(); connect(nils, &ModFolderModel::updateFinished, this, &ScanModFolders::nilModsDone); if (!nils->update()) { m_nilModsDone = true; } checkDone(); } void ScanModFolders::modsDone() { m_modsDone = true; checkDone(); } void ScanModFolders::coreModsDone() { m_coreModsDone = true; checkDone(); } void ScanModFolders::nilModsDone() { m_nilModsDone = true; checkDone(); } void ScanModFolders::checkDone() { if (m_modsDone && m_coreModsDone && m_nilModsDone) { emitSucceeded(); } } PrismLauncher-11.0.3/launcher/minecraft/launch/ExtractNatives.h0000644000175100017510000000174215224505336024127 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include // FIXME: temporary wrapper for existing task. class ExtractNatives : public LaunchStep { Q_OBJECT public: explicit ExtractNatives(LaunchTask* parent) : LaunchStep(parent) {}; virtual ~ExtractNatives() {}; void executeTask() override; bool canAbort() const override { return false; } void finalize() override; }; PrismLauncher-11.0.3/launcher/minecraft/launch/AutoInstallJava.cpp0000644000175100017510000002662315224505336024564 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AutoInstallJava.h" #include #include #include #include "Application.h" #include "FileSystem.h" #include "MessageLevel.h" #include "QObjectPtr.h" #include "SysInfo.h" #include "java/JavaInstall.h" #include "java/JavaInstallList.h" #include "java/JavaUtils.h" #include "java/JavaVersion.h" #include "java/download/ArchiveDownloadTask.h" #include "java/download/ManifestDownloadTask.h" #include "java/download/SymlinkTask.h" #include "meta/Index.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "net/Mode.h" #include "tasks/SequentialTask.h" AutoInstallJava::AutoInstallJava(LaunchTask* parent) : LaunchStep(parent), m_instance(m_parent->instance()), m_supported_arch(SysInfo::getSupportedJavaArchitecture()) {}; void AutoInstallJava::executeTask() { auto settings = m_instance->settings(); if (!APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() || (settings->get("OverrideJavaLocation").toBool() && QFileInfo::exists(settings->get("JavaPath").toString()))) { emitSucceeded(); return; } auto packProfile = m_instance->getPackProfile(); if (!APPLICATION->settings()->get("AutomaticJavaDownload").toBool()) { auto javas = APPLICATION->javalist(); m_current_task = javas->getLoadTask(); connect(m_current_task.get(), &Task::finished, this, [this, javas, packProfile] { for (auto i = 0; i < javas->count(); i++) { auto java = std::dynamic_pointer_cast(javas->at(i)); if (java && packProfile->getProfile()->getCompatibleJavaMajors().contains(java->id.major())) { if (!java->is_64bit) { emit logLine(tr("The automatic Java mechanism detected a 32-bit installation of Java."), MessageLevel::Launcher); } setJavaPath(java->path); return; } } emit logLine(tr("No compatible Java version was found. Using the default one."), MessageLevel::Warning); emitSucceeded(); }); connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress); connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress); connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus); connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails); emit progressReportingRequest(); return; } if (m_supported_arch.isEmpty()) { emit logLine(tr("Your system (%1-%2) is not compatible with automatic Java installation. Using the default Java path.") .arg(SysInfo::currentSystem(), SysInfo::useQTForArch()), MessageLevel::Warning); emitSucceeded(); return; } auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName(); if (wantedJavaName.isEmpty()) { emit logLine(tr("Your meta information is out of date or doesn't have the information necessary to determine what installation of " "Java should be used. " "Using the default Java path."), MessageLevel::Warning); emitSucceeded(); return; } QDir javaDir(APPLICATION->javaPath()); auto relativeBinary = FS::PathCombine(wantedJavaName, "bin", JavaUtils::javaExecutable); auto wantedJavaPath = javaDir.absoluteFilePath(relativeBinary); if (QFileInfo::exists(wantedJavaPath)) { setJavaPathFromPartial(); return; } auto versionList = APPLICATION->metadataIndex()->get("net.minecraft.java"); m_current_task = versionList->getLoadTask(); connect(m_current_task.get(), &Task::succeeded, this, &AutoInstallJava::tryNextMajorJava); connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::emitFailed); connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress); connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress); connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus); connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails); if (!m_current_task->isRunning()) { m_current_task->start(); } emit progressReportingRequest(); } void AutoInstallJava::setJavaPath(QString path) { auto settings = m_instance->settings(); settings->set("OverrideJavaLocation", true); settings->set("JavaPath", path); settings->set("AutomaticJava", true); emit logLine(tr("Compatible Java found at: %1.").arg(path), MessageLevel::Launcher); emitSucceeded(); } void AutoInstallJava::setJavaPathFromPartial() { auto packProfile = m_instance->getPackProfile(); auto javaName = packProfile->getProfile()->getCompatibleJavaName(); QDir javaDir(APPLICATION->javaPath()); // just checking if the executable is there should suffice // but if needed this can be achieved through refreshing the javalist // and retrieving the path that contains the java name auto relativeBinary = FS::PathCombine(javaName, "bin", JavaUtils::javaExecutable); auto finalPath = javaDir.absoluteFilePath(relativeBinary); if (QFileInfo::exists(finalPath)) { setJavaPath(finalPath); } else { emit logLine(tr("No compatible Java version was found (the binary file does not exist). Using the default one."), MessageLevel::Warning); emitSucceeded(); } return; } void AutoInstallJava::downloadJava(Meta::Version::Ptr version, QString javaName) { auto runtimes = version->data()->runtimes; for (auto java : runtimes) { if (java->runtimeOS == m_supported_arch && java->name() == javaName) { QDir javaDir(APPLICATION->javaPath()); auto final_path = javaDir.absoluteFilePath(java->m_name); auto deletePath = [final_path] { FS::deletePath(final_path); }; switch (java->downloadType) { case Java::DownloadType::Manifest: m_current_task = makeShared(java->url, final_path, java->checksumType, java->checksumHash); break; case Java::DownloadType::Archive: m_current_task = makeShared(java->url, final_path, java->checksumType, java->checksumHash); break; case Java::DownloadType::Unknown: deletePath(); emitFailed(tr("Could not determine Java download type!")); return; } #if defined(Q_OS_MACOS) auto seq = makeShared(tr("Install Java")); seq->addTask(m_current_task); seq->addTask(makeShared(final_path)); m_current_task = seq; #endif connect(m_current_task.get(), &Task::failed, this, [this, deletePath](QString reason) { deletePath(); emitFailed(reason); }); connect(m_current_task.get(), &Task::aborted, this, deletePath); connect(m_current_task.get(), &Task::succeeded, this, &AutoInstallJava::setJavaPathFromPartial); connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava); connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress); connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress); connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus); connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails); m_current_task->start(); return; } } tryNextMajorJava(); } void AutoInstallJava::tryNextMajorJava() { if (!isRunning()) return; auto versionList = APPLICATION->metadataIndex()->get("net.minecraft.java"); auto packProfile = m_instance->getPackProfile(); auto wantedJavaName = packProfile->getProfile()->getCompatibleJavaName(); auto majorJavaVersions = packProfile->getProfile()->getCompatibleJavaMajors(); if (m_majorJavaVersionIndex >= majorJavaVersions.length()) { emit logLine( tr("No versions of Java were found for your operating system: %1-%2").arg(SysInfo::currentSystem(), SysInfo::useQTForArch()), MessageLevel::Warning); emit logLine(tr("No compatible version of Java was found. Using the default one."), MessageLevel::Warning); emitSucceeded(); return; } auto majorJavaVersion = majorJavaVersions[m_majorJavaVersionIndex]; m_majorJavaVersionIndex++; auto javaMajor = versionList->getVersion(QString("java%1").arg(majorJavaVersion)); if (javaMajor->isLoaded()) { downloadJava(javaMajor, wantedJavaName); } else { m_current_task = APPLICATION->metadataIndex()->loadVersion("net.minecraft.java", javaMajor->version(), Net::Mode::Online); connect(m_current_task.get(), &Task::succeeded, this, [this, javaMajor, wantedJavaName] { downloadJava(javaMajor, wantedJavaName); }); connect(m_current_task.get(), &Task::failed, this, &AutoInstallJava::tryNextMajorJava); connect(m_current_task.get(), &Task::progress, this, &AutoInstallJava::setProgress); connect(m_current_task.get(), &Task::stepProgress, this, &AutoInstallJava::propagateStepProgress); connect(m_current_task.get(), &Task::status, this, &AutoInstallJava::setStatus); connect(m_current_task.get(), &Task::details, this, &AutoInstallJava::setDetails); if (!m_current_task->isRunning()) { m_current_task->start(); } } } bool AutoInstallJava::abort() { if (m_current_task && m_current_task->canAbort()) { auto status = m_current_task->abort(); emitAborted(); return status; } return Task::abort(); } PrismLauncher-11.0.3/launcher/minecraft/launch/EnsureAvailableMemory.h0000644000175100017510000000223515224505336025414 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2026 Octol1ttle * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "launch/LaunchStep.h" #include "minecraft/MinecraftInstance.h" class EnsureAvailableMemory : public LaunchStep { Q_OBJECT public: explicit EnsureAvailableMemory(LaunchTask* parent, MinecraftInstance* instance); ~EnsureAvailableMemory() override = default; void executeTask() override; bool canAbort() const override { return false; } private: MinecraftInstance* m_instance; }; PrismLauncher-11.0.3/launcher/minecraft/launch/EnsureOfflineLibraries.cpp0000644000175100017510000000366415224505336026124 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2026 Octol1ttle * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "EnsureOfflineLibraries.h" #include "minecraft/PackProfile.h" EnsureOfflineLibraries::EnsureOfflineLibraries(LaunchTask* parent, MinecraftInstance* instance) : LaunchStep(parent), m_instance(instance) {} void EnsureOfflineLibraries::executeTask() { const auto profile = m_instance->getPackProfile()->getProfile(); QStringList allJars; profile->getLibraryFiles(m_instance->runtimeContext(), allJars, allJars, m_instance->getLocalLibraryPath(), m_instance->binRoot(), false); QStringList missing; for (const auto& jar : allJars) { if (!QFileInfo::exists(jar)) { missing.append(jar); } } if (missing.isEmpty()) { emitSucceeded(); return; } emit logLine("Missing libraries:", MessageLevel::Error); for (const auto& jar : missing) { emit logLine(" " + jar, MessageLevel::Error); } emit logLine(tr("\nThis instance cannot be launched because some libraries are missing or have not been downloaded yet. Please " "try again in online mode with a working Internet connection"), MessageLevel::Fatal); emitFailed("Required libraries are missing"); } PrismLauncher-11.0.3/launcher/minecraft/launch/PrintInstanceInfo.cpp0000644000175100017510000000512415224505336025111 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "PrintInstanceInfo.h" #include "HardwareInfo.h" #if defined(Q_OS_FREEBSD) namespace { void runSysctlHwModel(QStringList& log) { char buff[512]; FILE* hwmodel = popen("sysctl hw.model", "r"); while (fgets(buff, 512, hwmodel) != NULL) { log << QString::fromUtf8(buff); break; } pclose(hwmodel); } void runPciconf(QStringList& log) { char buff[512]; std::string strcard; FILE* pciconf = popen("pciconf -lv -a vgapci0", "r"); while (fgets(buff, 512, pciconf) != NULL) { if (strncmp(buff, " vendor", 10) == 0) { std::string str(buff); strcard.append(str.substr(str.find_first_of("'") + 1, str.find_last_not_of("'") - (str.find_first_of("'") + 2))); strcard.append(" "); } else if (strncmp(buff, " device", 10) == 0) { std::string str2(buff); strcard.append(str2.substr(str2.find_first_of("'") + 1, str2.find_last_not_of("'") - (str2.find_first_of("'") + 2))); } log << QString::fromStdString(strcard); break; } pclose(pciconf); } } // namespace #endif void PrintInstanceInfo::executeTask() { auto instance = m_parent->instance(); QStringList log; log << ""; log << "OS: " + QString("%1 | %2 | %3").arg(QSysInfo::prettyProductName(), QSysInfo::kernelType(), QSysInfo::kernelVersion()); #ifdef Q_OS_FREEBSD ::runSysctlHwModel(log); ::runPciconf(log); #else log << "CPU: " + HardwareInfo::cpuInfo(); #ifdef Q_OS_MACOS log << "Memory pressure level: " + MacOSHardwareInfo::memoryPressureLevelName(); #else log << QString("RAM: %1 MiB (available: %2 MiB)").arg(HardwareInfo::totalRamMiB()).arg(HardwareInfo::availableRamMiB()); #endif #endif log.append(HardwareInfo::gpuInfo()); log << ""; logLines(log, MessageLevel::Launcher); logLines(instance->verboseDescription(m_session, m_targetToJoin), MessageLevel::Launcher); emitSucceeded(); } PrismLauncher-11.0.3/launcher/minecraft/launch/VerifyJavaInstall.h0000644000175100017510000000347515224505336024565 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include class VerifyJavaInstall : public LaunchStep { Q_OBJECT public: explicit VerifyJavaInstall(LaunchTask* parent) : LaunchStep(parent) {}; ~VerifyJavaInstall() override = default; void executeTask() override; bool canAbort() const override { return false; } }; PrismLauncher-11.0.3/launcher/minecraft/launch/PrintInstanceInfo.h0000644000175100017510000000236115224505336024556 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include "minecraft/auth/AuthSession.h" #include "minecraft/launch/MinecraftTarget.h" // FIXME: temporary wrapper for existing task. class PrintInstanceInfo : public LaunchStep { Q_OBJECT public: explicit PrintInstanceInfo(LaunchTask* parent, AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) : LaunchStep(parent), m_session(session), m_targetToJoin(targetToJoin) {}; virtual ~PrintInstanceInfo() = default; virtual void executeTask(); virtual bool canAbort() const { return false; } private: AuthSessionPtr m_session; MinecraftTarget::Ptr m_targetToJoin; }; PrismLauncher-11.0.3/launcher/minecraft/launch/ExtractNatives.cpp0000644000175100017510000000541215224505336024460 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ExtractNatives.h" #include #include #include #include "FileSystem.h" #include "archive/ArchiveReader.h" #include "archive/ArchiveWriter.h" #ifdef major #undef major #endif #ifdef minor #undef minor #endif static QString replaceSuffix(QString target, const QString& suffix, const QString& replacement) { if (!target.endsWith(suffix)) { return target; } target.resize(target.length() - suffix.length()); return target + replacement; } static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack) { MMCZip::ArchiveReader zip(source); QDir directory(targetFolder); auto extPtr = MMCZip::ArchiveWriter::createDiskWriter(); auto ext = extPtr.get(); return zip.parse([applyJnilibHack, directory, ext](MMCZip::ArchiveReader::File* f) { QString name = f->filename(); auto lowercase = name.toLower(); if (applyJnilibHack) { name = replaceSuffix(name, ".jnilib", ".dylib"); } QString absFilePath = directory.absoluteFilePath(name); return f->writeFile(ext, absFilePath, directory); }); } void ExtractNatives::executeTask() { auto instance = m_parent->instance(); auto toExtract = instance->getNativeJars(); if (toExtract.isEmpty()) { emitSucceeded(); return; } auto outputPath = instance->getNativePath(); FS::ensureFolderPathExists(outputPath); auto javaVersion = instance->getJavaVersion(); bool jniHackEnabled = javaVersion.major() >= 8; for (const auto& source : toExtract) { if (!unzipNatives(source, outputPath, jniHackEnabled)) { const char* reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'"); emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal); emitFailed(tr(reason).arg(source, outputPath)); return; } } emitSucceeded(); } void ExtractNatives::finalize() { auto instance = m_parent->instance(); QString target_dir = FS::PathCombine(instance->instanceRoot(), "natives/"); QDir dir(target_dir); dir.removeRecursively(); } PrismLauncher-11.0.3/launcher/minecraft/launch/ModMinecraftJar.cpp0000644000175100017510000000635515224505336024530 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ModMinecraftJar.h" #include "FileSystem.h" #include "MMCZip.h" #include "launch/LaunchTask.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" void ModMinecraftJar::executeTask() { auto m_inst = m_parent->instance(); if (!m_inst->getJarMods().size()) { emitSucceeded(); return; } // nuke obsolete stripped jar(s) if needed if (!FS::ensureFolderPathExists(m_inst->binRoot())) { emitFailed(tr("Couldn't create the bin folder for Minecraft.jar")); return; } auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar"); if (!removeJar()) { emitFailed(tr("Couldn't remove stale jar file: %1").arg(finalJarPath)); return; } // create temporary modded jar, if needed auto components = m_inst->getPackProfile(); auto profile = components->getProfile(); auto jarMods = m_inst->getJarMods(); if (jarMods.size()) { auto mainJar = profile->getMainJar(); QStringList jars, temp1, temp2, temp3, temp4; mainJar->getApplicableFiles(m_inst->runtimeContext(), jars, temp1, temp2, temp3, m_inst->getLocalLibraryPath()); auto sourceJarPath = jars[0]; if (!MMCZip::createModdedJar(sourceJarPath, finalJarPath, jarMods)) { emitFailed(tr("Failed to create the custom Minecraft jar file.")); return; } } emitSucceeded(); } void ModMinecraftJar::finalize() { removeJar(); } bool ModMinecraftJar::removeJar() { auto m_inst = m_parent->instance(); auto finalJarPath = QDir(m_inst->binRoot()).absoluteFilePath("minecraft.jar"); QFile finalJar(finalJarPath); if (finalJar.exists()) { if (!finalJar.remove()) { return false; } } return true; } PrismLauncher-11.0.3/launcher/minecraft/LaunchProfile.h0000644000175100017510000001355115224505336022445 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "Agent.h" #include "Library.h" class LaunchProfile : public ProblemProvider { public: virtual ~LaunchProfile() {} public: /* application of profile variables from patches */ void applyMinecraftVersion(const QString& id); void applyMainClass(const QString& mainClass); void applyAppletClass(const QString& appletClass); void applyMinecraftArguments(const QString& minecraftArguments); void applyAddnJvmArguments(const QStringList& minecraftArguments); void applyMinecraftVersionType(const QString& type); void applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets); void applyTraits(const QSet& traits); void applyTweakers(const QStringList& tweakers); void applyJarMods(const QList& jarMods); void applyMods(const QList& jarMods); void applyLibrary(LibraryPtr library, const RuntimeContext& runtimeContext); void applyMavenFile(LibraryPtr library, const RuntimeContext& runtimeContext); void applyAgent(const Agent& agent, const RuntimeContext& runtimeContext); void applyCompatibleJavaMajors(QList& javaMajor); void applyCompatibleJavaName(QString javaName); void applyMainJar(LibraryPtr jar); void applyProblemSeverity(ProblemSeverity severity); /// clear the profile void clear(); public: /* getters for profile variables */ QString getMinecraftVersion() const; QString getMainClass() const; QString getAppletClass() const; QString getMinecraftVersionType() const; MojangAssetIndexInfo::Ptr getMinecraftAssets() const; QString getMinecraftArguments() const; const QStringList& getAddnJvmArguments() const; const QSet& getTraits() const; const QStringList& getTweakers() const; const QList& getJarMods() const; const QList& getLibraries() const; const QList& getNativeLibraries() const; const QList& getMavenFiles() const; const QList& getAgents() const; const QList& getCompatibleJavaMajors() const; const QString getCompatibleJavaName() const; const LibraryPtr getMainJar() const; void getLibraryFiles(const RuntimeContext& runtimeContext, QStringList& jars, QStringList& nativeJars, const QString& overridePath, const QString& tempPath, bool addJarMods = true) const; bool hasTrait(const QString& trait) const; ProblemSeverity getProblemSeverity() const override; const QList getProblems() const override; private: /// the version of Minecraft - jar to use QString m_minecraftVersion; /// Release type - "release" or "snapshot" QString m_minecraftVersionType; /// Assets type - "legacy" or a version ID MojangAssetIndexInfo::Ptr m_minecraftAssets; /** * arguments that should be used for launching minecraft * * ex: "--username ${auth_player_name} --session ${auth_session} * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}" */ QString m_minecraftArguments; /** * Additional arguments to pass to the JVM in addition to those the user has configured, * memory settings, etc. */ QStringList m_addnJvmArguments; /// A list of all tweaker classes QStringList m_tweakers; /// The main class to load first QString m_mainClass; /// The applet class, for some very old minecraft releases QString m_appletClass; /// the list of libraries QList m_libraries; /// the list of maven files to be placed in the libraries folder, but not acted upon QList m_mavenFiles; /// the list of java agents to add to JVM arguments QList m_agents; /// the main jar LibraryPtr m_mainJar; /// the list of native libraries QList m_nativeLibraries; /// traits, collected from all the version files (version files can only add) QSet m_traits; /// A list of jar mods. version files can add those. QList m_jarMods; /// the list of mods QList m_mods; /// compatible java major versions QList m_compatibleJavaMajors; QString m_compatibleJavaName; ProblemSeverity m_problemSeverity = ProblemSeverity::None; }; PrismLauncher-11.0.3/launcher/minecraft/ComponentUpdateTask_p.h0000644000175100017510000000120415224505336024151 0ustar runnerrunner#pragma once #include #include #include #include "net/Mode.h" #include "tasks/Task.h" #include "minecraft/ComponentUpdateTask.h" class PackProfile; struct RemoteLoadStatus { enum class Type { Index, List, Version } type = Type::Version; size_t PackProfileIndex = 0; bool finished = false; bool succeeded = false; Task::Ptr task; }; struct ComponentUpdateTaskData { PackProfile* m_profile = nullptr; QList remoteLoadStatusList; bool remoteLoadSuccessful = true; size_t remoteTasksInProgress = 0; ComponentUpdateTask::Mode mode; Net::Mode netmode; }; PrismLauncher-11.0.3/launcher/minecraft/LaunchProfile.cpp0000644000175100017510000002345415224505336023003 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "LaunchProfile.h" #include void LaunchProfile::clear() { m_minecraftVersion.clear(); m_minecraftVersionType.clear(); m_minecraftAssets.reset(); m_minecraftArguments.clear(); m_addnJvmArguments.clear(); m_tweakers.clear(); m_mainClass.clear(); m_appletClass.clear(); m_libraries.clear(); m_mavenFiles.clear(); m_agents.clear(); m_traits.clear(); m_jarMods.clear(); m_mainJar.reset(); m_problemSeverity = ProblemSeverity::None; } static void applyString(const QString& from, QString& to) { if (from.isEmpty()) return; to = from; } void LaunchProfile::applyMinecraftVersion(const QString& id) { applyString(id, this->m_minecraftVersion); } void LaunchProfile::applyAppletClass(const QString& appletClass) { applyString(appletClass, this->m_appletClass); } void LaunchProfile::applyMainClass(const QString& mainClass) { applyString(mainClass, this->m_mainClass); } void LaunchProfile::applyMinecraftArguments(const QString& minecraftArguments) { applyString(minecraftArguments, this->m_minecraftArguments); } void LaunchProfile::applyAddnJvmArguments(const QStringList& addnJvmArguments) { this->m_addnJvmArguments.append(addnJvmArguments); } void LaunchProfile::applyMinecraftVersionType(const QString& type) { applyString(type, this->m_minecraftVersionType); } void LaunchProfile::applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets) { if (assets) { m_minecraftAssets = assets; } } void LaunchProfile::applyTraits(const QSet& traits) { this->m_traits.unite(traits); } void LaunchProfile::applyTweakers(const QStringList& tweakers) { // if the applied tweakers override an existing one, skip it. this effectively moves it later in the sequence QStringList newTweakers; for (auto& tweaker : m_tweakers) { if (tweakers.contains(tweaker)) { continue; } newTweakers.append(tweaker); } // then just append the new tweakers (or moved original ones) newTweakers += tweakers; m_tweakers = newTweakers; } void LaunchProfile::applyJarMods(const QList& jarMods) { this->m_jarMods.append(jarMods); } static int findLibraryByName(QList* haystack, const GradleSpecifier& needle) { int retval = -1; for (int i = 0; i < haystack->size(); ++i) { if (haystack->at(i)->rawName().matchName(needle)) { // only one is allowed. if (retval != -1) return -1; retval = i; } } return retval; } void LaunchProfile::applyMods(const QList& mods) { QList* list = &m_mods; for (auto& mod : mods) { auto modCopy = Library::limitedCopy(mod); // find the mod by name. const int index = findLibraryByName(list, mod->rawName()); // mod not found? just add it. if (index < 0) { list->append(modCopy); return; } auto existingLibrary = list->at(index); // if we are higher it means we should update if (Version(mod->version()) > Version(existingLibrary->version())) { list->replace(index, modCopy); } } } void LaunchProfile::applyCompatibleJavaMajors(QList& javaMajor) { m_compatibleJavaMajors.append(javaMajor); } void LaunchProfile::applyCompatibleJavaName(QString javaName) { if (!javaName.isEmpty()) m_compatibleJavaName = javaName; } void LaunchProfile::applyLibrary(LibraryPtr library, const RuntimeContext& runtimeContext) { if (!library->isActive(runtimeContext)) { return; } QList* list = &m_libraries; if (library->isNative()) { list = &m_nativeLibraries; } auto libraryCopy = Library::limitedCopy(library); // find the library by name. const int index = findLibraryByName(list, library->rawName()); // library not found? just add it. if (index < 0) { list->append(libraryCopy); return; } auto existingLibrary = list->at(index); // if we are higher it means we should update if (Version(library->version()) > Version(existingLibrary->version())) { list->replace(index, libraryCopy); } } void LaunchProfile::applyMavenFile(LibraryPtr mavenFile, const RuntimeContext& runtimeContext) { if (!mavenFile->isActive(runtimeContext)) { return; } if (mavenFile->isNative()) { return; } // unlike libraries, we do not keep only one version or try to dedupe them m_mavenFiles.append(Library::limitedCopy(mavenFile)); } void LaunchProfile::applyAgent(const Agent& agent, const RuntimeContext& runtimeContext) { auto lib = agent.library; if (!lib->isActive(runtimeContext)) { return; } if (lib->isNative()) { return; } m_agents.append(agent); } const LibraryPtr LaunchProfile::getMainJar() const { return m_mainJar; } void LaunchProfile::applyMainJar(LibraryPtr jar) { if (jar) { m_mainJar = jar; } } void LaunchProfile::applyProblemSeverity(ProblemSeverity severity) { if (m_problemSeverity < severity) { m_problemSeverity = severity; } } const QList LaunchProfile::getProblems() const { // FIXME: implement something that actually makes sense here return {}; } QString LaunchProfile::getMinecraftVersion() const { return m_minecraftVersion; } QString LaunchProfile::getAppletClass() const { return m_appletClass; } QString LaunchProfile::getMainClass() const { return m_mainClass; } const QSet& LaunchProfile::getTraits() const { return m_traits; } const QStringList& LaunchProfile::getTweakers() const { return m_tweakers; } bool LaunchProfile::hasTrait(const QString& trait) const { return m_traits.contains(trait); } ProblemSeverity LaunchProfile::getProblemSeverity() const { return m_problemSeverity; } QString LaunchProfile::getMinecraftVersionType() const { return m_minecraftVersionType; } std::shared_ptr LaunchProfile::getMinecraftAssets() const { if (!m_minecraftAssets) { return std::make_shared("legacy"); } return m_minecraftAssets; } QString LaunchProfile::getMinecraftArguments() const { return m_minecraftArguments; } const QStringList& LaunchProfile::getAddnJvmArguments() const { return m_addnJvmArguments; } const QList& LaunchProfile::getJarMods() const { return m_jarMods; } const QList& LaunchProfile::getLibraries() const { return m_libraries; } const QList& LaunchProfile::getNativeLibraries() const { return m_nativeLibraries; } const QList& LaunchProfile::getMavenFiles() const { return m_mavenFiles; } const QList& LaunchProfile::getAgents() const { return m_agents; } const QList& LaunchProfile::getCompatibleJavaMajors() const { return m_compatibleJavaMajors; } const QString LaunchProfile::getCompatibleJavaName() const { return m_compatibleJavaName; } void LaunchProfile::getLibraryFiles(const RuntimeContext& runtimeContext, QStringList& jars, QStringList& nativeJars, const QString& overridePath, const QString& tempPath, bool addJarMods) const { QStringList native32, native64; jars.clear(); nativeJars.clear(); for (auto lib : getLibraries()) { lib->getApplicableFiles(runtimeContext, jars, nativeJars, native32, native64, overridePath); } // NOTE: order is important here, add main jar last to the lists if (m_mainJar) { // FIXME: HACK!! jar modding is weird and unsystematic! if (m_jarMods.size() && addJarMods) { QDir tempDir(tempPath); jars.append(tempDir.absoluteFilePath("minecraft.jar")); } else { m_mainJar->getApplicableFiles(runtimeContext, jars, nativeJars, native32, native64, overridePath); } } for (auto lib : getNativeLibraries()) { lib->getApplicableFiles(runtimeContext, jars, nativeJars, native32, native64, overridePath); } if (runtimeContext.javaArchitecture == "32") { nativeJars.append(native32); } else if (runtimeContext.javaArchitecture == "64") { nativeJars.append(native64); } } PrismLauncher-11.0.3/launcher/minecraft/ShortcutUtils.cpp0000644000175100017510000002310615224505336023076 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * Copyright (C) 2025 Yihe Li * * parent program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * parent program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with parent program. If not, see . * * parent file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use parent file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ShortcutUtils.h" #include "FileSystem.h" #include #include #include #include #include namespace ShortcutUtils { bool createInstanceShortcut(const Shortcut& shortcut, const QString& filePath) { if (!shortcut.instance) return false; QString appPath = QApplication::applicationFilePath(); auto icon = APPLICATION->icons()->icon(shortcut.iconKey.isEmpty() ? shortcut.instance->iconKey() : shortcut.iconKey); if (icon == nullptr) { icon = APPLICATION->icons()->icon("grass"); } QString iconPath; QStringList args; #if defined(Q_OS_MACOS) if (appPath.startsWith("/private/var/")) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts.")); return false; } iconPath = FS::PathCombine(shortcut.instance->instanceRoot(), "Icon.icns"); QFile iconFile(iconPath); if (!iconFile.open(QFile::WriteOnly)) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for application: %1").arg(iconFile.errorString())); return false; } QIcon iconObj = icon->icon(); bool success = iconObj.pixmap(1024, 1024).save(iconPath, "ICNS"); iconFile.close(); if (!success) { iconFile.remove(); QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for application.")); return false; } #elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) if (appPath.startsWith("/tmp/.mount_")) { // AppImage! appPath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); if (appPath.isEmpty()) { QMessageBox::critical( shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Launcher is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); } else if (appPath.endsWith("/")) { appPath.chop(1); } } iconPath = FS::PathCombine(shortcut.instance->instanceRoot(), "icon.png"); QFile iconFile(iconPath); if (!iconFile.open(QFile::WriteOnly)) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut: %1").arg(iconFile.errorString())); return false; } bool success = icon->icon().pixmap(64, 64).save(&iconFile, "PNG"); iconFile.close(); if (!success) { iconFile.remove(); QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut.")); return false; } if (DesktopServices::isFlatpak()) { appPath = "flatpak"; args.append({ "run", BuildConfig.LAUNCHER_APPID }); } #elif defined(Q_OS_WIN) iconPath = FS::PathCombine(shortcut.instance->instanceRoot(), "icon.ico"); // part of fix for weird bug involving the window icon being replaced // dunno why it happens, but parent 2-line fix seems to be enough, so w/e auto appIcon = APPLICATION->logo(); QFile iconFile(iconPath); if (!iconFile.open(QFile::WriteOnly)) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut: %1").arg(iconFile.errorString())); return false; } bool success = icon->icon().pixmap(64, 64).save(&iconFile, "ICO"); iconFile.close(); // restore original window icon QGuiApplication::setWindowIcon(appIcon); if (!success) { iconFile.remove(); QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut.")); return false; } #else QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Not supported on your platform!")); return false; #endif args.append({ "--launch", shortcut.instance->id() }); args.append(shortcut.extraArgs); QString shortcutPath = FS::createShortcut(filePath, appPath, args, shortcut.name, iconPath); if (shortcutPath.isEmpty()) { #if not defined(Q_OS_MACOS) iconFile.remove(); #endif QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create %1 shortcut!").arg(shortcut.targetString)); return false; } shortcut.instance->registerShortcut({ shortcut.name, shortcutPath, shortcut.target }); return true; } bool createInstanceShortcutOnDesktop(const Shortcut& shortcut) { if (!shortcut.instance) return false; QString desktopDir = FS::getDesktopDir(); if (desktopDir.isEmpty()) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Couldn't find desktop?!")); return false; } QString shortcutFilePath = FS::PathCombine(desktopDir, FS::RemoveInvalidFilenameChars(shortcut.name)); if (!createInstanceShortcut(shortcut, shortcutFilePath)) return false; QMessageBox::information(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Created a shortcut to this %1 on your desktop!").arg(shortcut.targetString)); return true; } bool createInstanceShortcutInApplications(const Shortcut& shortcut) { if (!shortcut.instance) return false; QString applicationsDir = FS::getApplicationsDir(); if (applicationsDir.isEmpty()) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Couldn't find applications folder?!")); return false; } #if defined(Q_OS_MACOS) || defined(Q_OS_WIN) applicationsDir = FS::PathCombine(applicationsDir, BuildConfig.LAUNCHER_DISPLAYNAME + " Instances"); QDir applicationsDirQ(applicationsDir); if (!applicationsDirQ.mkpath(".")) { QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create instances folder in applications folder!")); return false; } #endif QString shortcutFilePath = FS::PathCombine(applicationsDir, FS::RemoveInvalidFilenameChars(shortcut.name)); if (!createInstanceShortcut(shortcut, shortcutFilePath)) return false; QMessageBox::information(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Created a shortcut to this %1 in your applications folder!").arg(shortcut.targetString)); return true; } bool createInstanceShortcutInOther(const Shortcut& shortcut) { if (!shortcut.instance) return false; QString defaultedDir = FS::getDesktopDir(); #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) QString extension = ".desktop"; #elif defined(Q_OS_WINDOWS) QString extension = ".lnk"; #else QString extension = ""; #endif QString shortcutFilePath = FS::PathCombine(defaultedDir, FS::RemoveInvalidFilenameChars(shortcut.name) + extension); QFileDialog fileDialog; // workaround to make sure the portal file dialog opens in the desktop directory fileDialog.setDirectoryUrl(defaultedDir); shortcutFilePath = fileDialog.getSaveFileName(shortcut.parent, QObject::tr("Create Shortcut"), shortcutFilePath, QObject::tr("Desktop Entries") + " (*" + extension + ")"); if (shortcutFilePath.isEmpty()) return false; // file dialog canceled by user if (shortcutFilePath.endsWith(extension)) shortcutFilePath = shortcutFilePath.mid(0, shortcutFilePath.length() - extension.length()); if (!createInstanceShortcut(shortcut, shortcutFilePath)) return false; QMessageBox::information(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Created a shortcut to this %1!").arg(shortcut.targetString)); return true; } } // namespace ShortcutUtils PrismLauncher-11.0.3/launcher/minecraft/Agent.h0000644000175100017510000000041215224505336020740 0ustar runnerrunner#pragma once #include #include "Library.h" struct Agent { /// The library pointing to the jar this Java agent is contained within LibraryPtr library; /// The argument to the Java agent, passed after an = if present QString argument; }; PrismLauncher-11.0.3/launcher/minecraft/MojangVersionFormat.cpp0000644000175100017510000003267115224505336024203 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MojangVersionFormat.h" #include "MojangDownloadInfo.h" #include "OneSixVersionFormat.h" #include "Json.h" using namespace Json; #include #include "ParseUtils.h" static const int CURRENT_MINIMUM_LAUNCHER_VERSION = 18; static MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject& obj); static MojangDownloadInfo::Ptr downloadInfoFromJson(const QJsonObject& obj); static MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson(const QJsonObject& libObj); static QJsonObject assetIndexToJson(MojangAssetIndexInfo::Ptr assetidxinfo); static QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo); static QJsonObject downloadInfoToJson(MojangDownloadInfo::Ptr info); namespace Bits { static void readString(const QJsonObject& root, const QString& key, QString& variable) { if (root.contains(key)) { variable = requireString(root.value(key)); } } static void readDownloadInfo(MojangDownloadInfo::Ptr out, const QJsonObject& obj) { // optional, not used readString(obj, "path", out->path); // required! out->sha1 = requireString(obj, "sha1"); out->url = requireString(obj, "url"); out->size = requireInteger(obj, "size"); } static void readAssetIndex(MojangAssetIndexInfo::Ptr out, const QJsonObject& obj) { out->totalSize = requireInteger(obj, "totalSize"); out->id = requireString(obj, "id"); // out->known = true; } } // namespace Bits MojangDownloadInfo::Ptr downloadInfoFromJson(const QJsonObject& obj) { auto out = std::make_shared(); Bits::readDownloadInfo(out, obj); return out; } MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject& obj) { auto out = std::make_shared(); Bits::readDownloadInfo(out, obj); Bits::readAssetIndex(out, obj); return out; } QJsonObject downloadInfoToJson(MojangDownloadInfo::Ptr info) { QJsonObject out; if (!info->path.isNull()) { out.insert("path", info->path); } out.insert("sha1", info->sha1); out.insert("size", info->size); out.insert("url", info->url); return out; } MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson(const QJsonObject& libObj) { auto out = std::make_shared(); auto dlObj = requireObject(libObj.value("downloads")); if (dlObj.contains("artifact")) { out->artifact = downloadInfoFromJson(requireObject(dlObj, "artifact")); } if (dlObj.contains("classifiers")) { auto classifiersObj = requireObject(dlObj, "classifiers"); for (auto iter = classifiersObj.begin(); iter != classifiersObj.end(); iter++) { auto classifier = iter.key(); auto classifierObj = requireObject(iter.value()); out->classifiers[classifier] = downloadInfoFromJson(classifierObj); } } return out; } QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo) { QJsonObject out; if (libinfo->artifact) { out.insert("artifact", downloadInfoToJson(libinfo->artifact)); } if (!libinfo->classifiers.isEmpty()) { QJsonObject classifiersOut; for (auto iter = libinfo->classifiers.begin(); iter != libinfo->classifiers.end(); iter++) { classifiersOut.insert(iter.key(), downloadInfoToJson(iter.value())); } out.insert("classifiers", classifiersOut); } return out; } QJsonObject assetIndexToJson(MojangAssetIndexInfo::Ptr info) { QJsonObject out; if (!info->path.isNull()) { out.insert("path", info->path); } out.insert("sha1", info->sha1); out.insert("size", info->size); out.insert("url", info->url); out.insert("totalSize", info->totalSize); out.insert("id", info->id); return out; } void MojangVersionFormat::readVersionProperties(const QJsonObject& in, VersionFile* out) { Bits::readString(in, "id", out->minecraftVersion); Bits::readString(in, "mainClass", out->mainClass); Bits::readString(in, "minecraftArguments", out->minecraftArguments); Bits::readString(in, "type", out->type); Bits::readString(in, "assets", out->assets); if (in.contains("assetIndex")) { out->mojangAssetIndex = assetIndexFromJson(requireObject(in, "assetIndex")); } else if (!out->assets.isNull()) { out->mojangAssetIndex = std::make_shared(out->assets); } out->releaseTime = timeFromS3Time(in.value("releaseTime").toString("")); out->updateTime = timeFromS3Time(in.value("time").toString("")); if (in.contains("minimumLauncherVersion")) { out->minimumLauncherVersion = requireInteger(in.value("minimumLauncherVersion")); if (out->minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION) { out->addProblem(ProblemSeverity::Warning, QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than " "supported by %3 (%2). It might not work properly!") .arg(out->minimumLauncherVersion) .arg(CURRENT_MINIMUM_LAUNCHER_VERSION) .arg(BuildConfig.LAUNCHER_DISPLAYNAME)); } } if (in.contains("compatibleJavaMajors")) { for (auto compatible : requireArray(in.value("compatibleJavaMajors"))) { out->compatibleJavaMajors.append(requireInteger(compatible)); } } if (in.contains("compatibleJavaName")) { out->compatibleJavaName = requireString(in.value("compatibleJavaName")); } if (in.contains("downloads")) { auto downloadsObj = requireObject(in, "downloads"); for (auto iter = downloadsObj.begin(); iter != downloadsObj.end(); iter++) { auto classifier = iter.key(); auto classifierObj = requireObject(iter.value()); out->mojangDownloads[classifier] = downloadInfoFromJson(classifierObj); } } } VersionFilePtr MojangVersionFormat::versionFileFromJson(const QJsonDocument& doc, const QString& filename) { VersionFilePtr out(new VersionFile()); if (doc.isEmpty() || doc.isNull()) { throw JSONValidationError(filename + " is empty or null"); } if (!doc.isObject()) { throw JSONValidationError(filename + " is not an object"); } QJsonObject root = doc.object(); readVersionProperties(root, out.get()); out->name = "Minecraft"; out->uid = "net.minecraft"; out->version = out->minecraftVersion; // out->filename = filename; if (root.contains("libraries")) { for (auto libVal : requireArray(root.value("libraries"))) { auto libObj = requireObject(libVal); auto lib = MojangVersionFormat::libraryFromJson(*out, libObj, filename); out->libraries.append(lib); } } return out; } void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObject& out) { writeString(out, "id", in->minecraftVersion); writeString(out, "mainClass", in->mainClass); writeString(out, "minecraftArguments", in->minecraftArguments); writeString(out, "type", in->type); if (!in->releaseTime.isNull()) { writeString(out, "releaseTime", timeToS3Time(in->releaseTime)); } if (!in->updateTime.isNull()) { writeString(out, "time", timeToS3Time(in->updateTime)); } if (in->minimumLauncherVersion != -1) { out.insert("minimumLauncherVersion", in->minimumLauncherVersion); } writeString(out, "assets", in->assets); if (in->mojangAssetIndex && in->mojangAssetIndex->known) { out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex)); } if (!in->mojangDownloads.isEmpty()) { QJsonObject downloadsOut; for (auto iter = in->mojangDownloads.begin(); iter != in->mojangDownloads.end(); iter++) { downloadsOut.insert(iter.key(), downloadInfoToJson(iter.value())); } out.insert("downloads", downloadsOut); } if (!in->compatibleJavaMajors.isEmpty()) { QJsonArray compatibleJavaMajorsOut; for (auto compatibleJavaMajor : in->compatibleJavaMajors) { compatibleJavaMajorsOut.append(compatibleJavaMajor); } out.insert("compatibleJavaMajors", compatibleJavaMajorsOut); } if (!in->compatibleJavaName.isEmpty()) { writeString(out, "compatibleJavaName", in->compatibleJavaName); } } QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr& patch) { QJsonObject root; writeVersionProperties(patch.get(), root); if (!patch->libraries.isEmpty()) { QJsonArray array; for (auto value : patch->libraries) { array.append(MojangVersionFormat::libraryToJson(value.get())); } root.insert("libraries", array); } // write the contents to a json document. { QJsonDocument out; out.setObject(root); return out; } } LibraryPtr MojangVersionFormat::libraryFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename) { LibraryPtr out(new Library()); if (!libObj.contains("name")) { throw JSONValidationError(filename + "contains a library that doesn't have a 'name' field"); } auto rawName = libObj.value("name").toString(); out->m_name = rawName; if (!out->m_name.valid()) { problems.addProblem(ProblemSeverity::Error, QObject::tr("Library %1 name is broken and cannot be processed.").arg(rawName)); } Bits::readString(libObj, "url", out->m_repositoryURL); if (libObj.contains("extract")) { out->m_hasExcludes = true; auto extractObj = requireObject(libObj.value("extract")); for (auto excludeVal : requireArray(extractObj.value("exclude"))) { out->m_extractExcludes.append(requireString(excludeVal)); } } if (libObj.contains("natives")) { QJsonObject nativesObj = requireObject(libObj.value("natives")); for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) { if (!it.value().isString()) { qWarning() << filename << "contains an invalid native (skipping)"; } // FIXME: Skip unknown platforms out->m_nativeClassifiers[it.key()] = it.value().toString(); } } if (libObj.contains("rules")) { out->applyRules = true; QJsonArray rulesArray = requireArray(libObj.value("rules")); for (auto rule : rulesArray) { out->m_rules.append(Rule::fromJson(requireObject(rule))); } } if (libObj.contains("downloads")) { out->m_mojangDownloads = libDownloadInfoFromJson(libObj); } return out; } QJsonObject MojangVersionFormat::libraryToJson(Library* library) { QJsonObject libRoot; libRoot.insert("name", library->m_name.serialize()); if (!library->m_repositoryURL.isEmpty()) { libRoot.insert("url", library->m_repositoryURL); } if (library->isNative()) { QJsonObject nativeList; auto iter = library->m_nativeClassifiers.begin(); while (iter != library->m_nativeClassifiers.end()) { nativeList.insert(iter.key(), iter.value()); iter++; } libRoot.insert("natives", nativeList); if (!library->m_extractExcludes.isEmpty()) { QJsonArray excludes; QJsonObject extract; for (auto exclude : library->m_extractExcludes) { excludes.append(exclude); } extract.insert("exclude", excludes); libRoot.insert("extract", extract); } } if (!library->m_rules.isEmpty()) { QJsonArray allRules; for (auto& rule : library->m_rules) { QJsonObject ruleObj = rule.toJson(); allRules.append(ruleObj); } libRoot.insert("rules", allRules); } if (library->m_mojangDownloads) { auto downloadsObj = libDownloadInfoToJson(library->m_mojangDownloads); libRoot.insert("downloads", downloadsObj); } return libRoot; } PrismLauncher-11.0.3/launcher/minecraft/PackProfile_p.h0000644000175100017510000000136315224505336022426 0ustar runnerrunner#pragma once #include #include #include #include "Component.h" #include "tasks/Task.h" class MinecraftInstance; using ComponentContainer = QList; using ComponentIndex = QMap; struct PackProfileData { // the instance this belongs to MinecraftInstance* m_instance; // the launch profile (volatile, temporary thing created on demand) std::shared_ptr m_profile; // persistent list of components and related machinery ComponentContainer components; ComponentIndex componentIndex; bool dirty = false; QTimer m_saveTimer; shared_qobject_ptr m_updateTask; bool loaded = false; bool interactionDisabled = true; }; PrismLauncher-11.0.3/launcher/minecraft/update/0000755000175100017510000000000015224505336021016 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/minecraft/update/LibrariesTask.h0000644000175100017510000000074115224505336023730 0ustar runnerrunner#pragma once #include "net/NetJob.h" #include "tasks/Task.h" class MinecraftInstance; class LibrariesTask : public Task { Q_OBJECT public: LibrariesTask(MinecraftInstance* inst); virtual ~LibrariesTask() = default; void executeTask() override; bool canAbort() const override; private slots: void jarlibFailed(QString reason); public slots: bool abort() override; private: MinecraftInstance* m_inst; NetJob::Ptr downloadJob; }; PrismLauncher-11.0.3/launcher/minecraft/update/LegacyFMLLibrariesTask.h0000644000175100017510000000122215224505336025407 0ustar runnerrunner#pragma once #include "minecraft/VersionFilterData.h" #include "net/NetJob.h" #include "tasks/Task.h" class MinecraftInstance; class LegacyFMLLibrariesTask : public Task { Q_OBJECT public: LegacyFMLLibrariesTask(MinecraftInstance* inst); virtual ~LegacyFMLLibrariesTask() = default; void executeTask() override; bool canAbort() const override; private slots: void fmllibsFinished(); void fmllibsFailed(QString reason); public slots: bool abort() override; private: static QString baseUrl(); private: MinecraftInstance* m_inst; NetJob::Ptr downloadJob; QList fmlLibsToProcess; }; PrismLauncher-11.0.3/launcher/minecraft/update/FoldersTask.cpp0000644000175100017510000000357215224505336023752 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FoldersTask.h" #include #include "minecraft/MinecraftInstance.h" FoldersTask::FoldersTask(MinecraftInstance* inst) { m_inst = inst; } void FoldersTask::executeTask() { // Make directories QDir mcDir(m_inst->gameRoot()); if (!mcDir.exists() && !mcDir.mkpath(".")) { emitFailed(tr("Failed to create folder for Minecraft binaries.")); return; } emitSucceeded(); } PrismLauncher-11.0.3/launcher/minecraft/update/LibrariesTask.cpp0000644000175100017510000000636215224505336024270 0ustar runnerrunner#include "LibrariesTask.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "Application.h" LibrariesTask::LibrariesTask(MinecraftInstance* inst) { m_inst = inst; } void LibrariesTask::executeTask() { setStatus(tr("Downloading required library files...")); qDebug() << m_inst->name() << ": downloading libraries"; MinecraftInstance* inst = (MinecraftInstance*)m_inst; // Build a list of URLs that will need to be downloaded. auto components = inst->getPackProfile(); auto profile = components->getProfile(); NetJob::Ptr job{ new NetJob(tr("Libraries for instance %1").arg(inst->name()), APPLICATION->network()) }; downloadJob.reset(job); auto metacache = APPLICATION->metacache(); auto processArtifactPool = [this, inst, metacache](const QList& pool, QStringList& errors, const QString& localPath) { for (auto lib : pool) { if (!lib) { emitFailed(tr("Null jar is specified in the metadata, aborting.")); return false; } auto dls = lib->getDownloads(inst->runtimeContext(), metacache, errors, localPath); for (auto dl : dls) { downloadJob->addNetAction(dl); } } return true; }; QStringList failedLocalLibraries; QList libArtifactPool; libArtifactPool.append(profile->getLibraries()); libArtifactPool.append(profile->getNativeLibraries()); libArtifactPool.append(profile->getMavenFiles()); for (const auto& agent : profile->getAgents()) { libArtifactPool.append(agent.library); } libArtifactPool.append(profile->getMainJar()); processArtifactPool(libArtifactPool, failedLocalLibraries, inst->getLocalLibraryPath()); QStringList failedLocalJarMods; processArtifactPool(profile->getJarMods(), failedLocalJarMods, inst->jarModsDir()); if (!failedLocalJarMods.empty() || !failedLocalLibraries.empty()) { downloadJob.reset(); QString failed_all = (failedLocalLibraries + failedLocalJarMods).join("\n"); emitFailed(tr("Some artifacts marked as 'local' are missing their files:\n%1\n\nYou need to either add the files, or removed the " "packages that require them.\nYou'll have to correct this problem manually.") .arg(failed_all)); return; } connect(downloadJob.get(), &NetJob::succeeded, this, &LibrariesTask::emitSucceeded); connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed); connect(downloadJob.get(), &NetJob::aborted, this, &LibrariesTask::emitAborted); connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress); connect(downloadJob.get(), &NetJob::stepProgress, this, &LibrariesTask::propagateStepProgress); downloadJob->start(); } bool LibrariesTask::canAbort() const { return true; } void LibrariesTask::jarlibFailed(QString reason) { emitFailed(tr("Game update failed: it was impossible to fetch the required libraries.\nReason:\n%1").arg(reason)); } bool LibrariesTask::abort() { if (downloadJob) { return downloadJob->abort(); } else { qWarning() << "Prematurely aborted LibrariesTask"; } return true; } PrismLauncher-11.0.3/launcher/minecraft/update/AssetUpdateTask.h0000644000175100017510000000113715224505336024236 0ustar runnerrunner#pragma once #include "net/NetJob.h" #include "tasks/Task.h" class MinecraftInstance; class AssetUpdateTask : public Task { Q_OBJECT public: AssetUpdateTask(MinecraftInstance* inst); virtual ~AssetUpdateTask() = default; void executeTask() override; bool canAbort() const override; public: static QString resourceUrl(); private slots: void assetIndexFinished(); void assetIndexFailed(QString reason); void assetsFailed(QString reason); public slots: bool abort() override; private: MinecraftInstance* m_inst; NetJob::Ptr downloadJob; }; PrismLauncher-11.0.3/launcher/minecraft/update/FoldersTask.h0000644000175100017510000000043315224505336023410 0ustar runnerrunner#pragma once #include "tasks/Task.h" class MinecraftInstance; class FoldersTask : public Task { Q_OBJECT public: FoldersTask(MinecraftInstance* inst); virtual ~FoldersTask() = default; void executeTask() override; private: MinecraftInstance* m_inst; }; PrismLauncher-11.0.3/launcher/minecraft/update/LegacyFMLLibrariesTask.cpp0000644000175100017510000001074315224505336025752 0ustar runnerrunner#include "LegacyFMLLibrariesTask.h" #include "FileSystem.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "minecraft/VersionFilterData.h" #include "Application.h" #include "BuildConfig.h" #include "net/ApiDownload.h" LegacyFMLLibrariesTask::LegacyFMLLibrariesTask(MinecraftInstance* inst) { m_inst = inst; } void LegacyFMLLibrariesTask::executeTask() { // Get the mod list MinecraftInstance* inst = (MinecraftInstance*)m_inst; auto components = inst->getPackProfile(); auto profile = components->getProfile(); if (!profile->hasTrait("legacyFML")) { emitSucceeded(); return; } QString version = components->getComponentVersion("net.minecraft"); auto& fmlLibsMapping = g_VersionFilterData.fmlLibsMapping; if (!fmlLibsMapping.contains(version)) { emitSucceeded(); return; } auto& libList = fmlLibsMapping[version]; // determine if we need some libs for FML or forge setStatus(tr("Checking for FML libraries...")); if (!components->getComponent("net.minecraftforge")) { emitSucceeded(); return; } // now check the lib folder inside the instance for files. for (auto& lib : libList) { QFileInfo libInfo(FS::PathCombine(inst->libDir(), lib.filename)); if (libInfo.exists()) continue; fmlLibsToProcess.append(lib); } // if everything is in place, there's nothing to do here... if (fmlLibsToProcess.isEmpty()) { emitSucceeded(); return; } // download missing libs to our place setStatus(tr("Downloading FML libraries...")); NetJob::Ptr dljob{ new NetJob("FML libraries", APPLICATION->network()) }; auto metacache = APPLICATION->metacache(); Net::Download::Options options = Net::Download::Option::MakeEternal; const QString base = baseUrl(); for (auto& lib : fmlLibsToProcess) { auto entry = metacache->resolveEntry("fmllibs", lib.filename); QString urlString = base + lib.filename; dljob->addNetAction(Net::ApiDownload::makeCached(QUrl(urlString), entry, options)); } connect(dljob.get(), &NetJob::succeeded, this, &LegacyFMLLibrariesTask::fmllibsFinished); connect(dljob.get(), &NetJob::failed, this, &LegacyFMLLibrariesTask::fmllibsFailed); connect(dljob.get(), &NetJob::aborted, this, &LegacyFMLLibrariesTask::emitAborted); connect(dljob.get(), &NetJob::progress, this, &LegacyFMLLibrariesTask::progress); connect(dljob.get(), &NetJob::stepProgress, this, &LegacyFMLLibrariesTask::propagateStepProgress); downloadJob.reset(dljob); downloadJob->start(); } bool LegacyFMLLibrariesTask::canAbort() const { return true; } void LegacyFMLLibrariesTask::fmllibsFinished() { downloadJob.reset(); if (!fmlLibsToProcess.isEmpty()) { setStatus(tr("Copying FML libraries into the instance...")); MinecraftInstance* inst = (MinecraftInstance*)m_inst; auto metacache = APPLICATION->metacache(); int index = 0; for (auto& lib : fmlLibsToProcess) { progress(index, fmlLibsToProcess.size()); auto entry = metacache->resolveEntry("fmllibs", lib.filename); auto path = FS::PathCombine(inst->libDir(), lib.filename); if (!FS::ensureFilePathExists(path)) { emitFailed(tr("Failed creating FML library folder inside the instance.")); return; } if (!QFile::copy(entry->getFullPath(), FS::PathCombine(inst->libDir(), lib.filename))) { emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename)); return; } index++; } progress(index, fmlLibsToProcess.size()); } emitSucceeded(); } void LegacyFMLLibrariesTask::fmllibsFailed(QString reason) { QStringList failed = downloadJob->getFailedFiles(); QString failed_all = failed.join("\n"); emitFailed(tr("Failed to download the following files:\n%1\n\nReason:%2\nPlease try again.").arg(failed_all, reason)); } bool LegacyFMLLibrariesTask::abort() { if (downloadJob) { return downloadJob->abort(); } else { qWarning() << "Prematurely aborted LegacyFMLLibrariesTask"; } return true; } QString LegacyFMLLibrariesTask::baseUrl() { if (const QString urlOverride = APPLICATION->settings()->get("LegacyFMLLibsURLOverride").toString(); !urlOverride.isEmpty()) { return urlOverride; } return BuildConfig.LEGACY_FMLLIBS_BASE_URL; } PrismLauncher-11.0.3/launcher/minecraft/update/AssetUpdateTask.cpp0000644000175100017510000001030315224505336024564 0ustar runnerrunner#include "AssetUpdateTask.h" #include "BuildConfig.h" #include "launch/LaunchStep.h" #include "minecraft/AssetsUtils.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "net/ChecksumValidator.h" #include "Application.h" #include "net/ApiDownload.h" AssetUpdateTask::AssetUpdateTask(MinecraftInstance* inst) { m_inst = inst; } void AssetUpdateTask::executeTask() { setStatus(tr("Updating assets index...")); auto components = m_inst->getPackProfile(); auto profile = components->getProfile(); auto assets = profile->getMinecraftAssets(); QUrl indexUrl = assets->url; QString localPath = assets->id + ".json"; auto job = makeShared(tr("Asset index for %1").arg(m_inst->name()), APPLICATION->network()); auto metacache = APPLICATION->metacache(); auto entry = metacache->resolveEntry("asset_indexes", localPath); entry->setStale(true); auto hexSha1 = assets->sha1.toLatin1(); qDebug() << "Asset index SHA1:" << hexSha1; auto dl = Net::ApiDownload::makeCached(indexUrl, entry); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, assets->sha1)); job->addNetAction(dl); downloadJob.reset(job); connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::assetIndexFinished); connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed); connect(downloadJob.get(), &NetJob::aborted, this, &AssetUpdateTask::emitAborted); connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propagateStepProgress); qDebug() << "Starting asset index download for" << m_inst->name(); downloadJob->start(); } bool AssetUpdateTask::canAbort() const { return true; } void AssetUpdateTask::assetIndexFinished() { AssetsIndex index; qDebug() << "Finished asset index download for" << m_inst->name(); auto components = m_inst->getPackProfile(); auto profile = components->getProfile(); auto assets = profile->getMinecraftAssets(); QString asset_fname = "assets/indexes/" + assets->id + ".json"; // FIXME: this looks like a job for a generic validator based on json schema? if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, index)) { auto metacache = APPLICATION->metacache(); auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json"); metacache->evictEntry(entry); emitFailed(tr("Failed to read the assets index!")); return; } auto job = index.getDownloadJob(); if (job) { QString resourceURL = resourceUrl(); QString source = tr("Mojang"); if (resourceURL != BuildConfig.DEFAULT_RESOURCE_BASE) { source = QUrl(resourceURL).host(); } setStatus(tr("Getting the asset files from %1...").arg(source)); downloadJob = job; connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::emitSucceeded); connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed); connect(downloadJob.get(), &NetJob::aborted, this, &AssetUpdateTask::emitAborted); connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propagateStepProgress); downloadJob->start(); return; } emitSucceeded(); } void AssetUpdateTask::assetIndexFailed(QString reason) { qDebug() << m_inst->name() << ": Failed asset index download"; emitFailed(tr("Failed to download the assets index:\n%1").arg(reason)); } void AssetUpdateTask::assetsFailed(QString reason) { emitFailed(tr("Failed to download assets:\n%1").arg(reason)); } bool AssetUpdateTask::abort() { if (downloadJob) { return downloadJob->abort(); } else { qWarning() << "Prematurely aborted AssetUpdateTask"; } return true; } QString AssetUpdateTask::resourceUrl() { if (const QString urlOverride = APPLICATION->settings()->get("ResourceURLOverride").toString(); !urlOverride.isEmpty()) { return urlOverride; } return BuildConfig.DEFAULT_RESOURCE_BASE; } PrismLauncher-11.0.3/launcher/minecraft/Library.cpp0000644000175100017510000003501715224505336021652 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Library.h" #include "MinecraftInstance.h" #include "net/NetRequest.h" #include #include #include #include /** * @brief Collect applicable files for the library. * * Depending on whether the library is native or not, it adds paths to the * appropriate lists for jar files, native libraries for 32-bit, and native * libraries for 64-bit. * * @param runtimeContext The current runtime context. * @param jar List to store paths for jar files. * @param native List to store paths for native libraries. * @param native32 List to store paths for 32-bit native libraries. * @param native64 List to store paths for 64-bit native libraries. * @param overridePath Optional path to override the default storage path. */ void Library::getApplicableFiles(const RuntimeContext& runtimeContext, QStringList& jar, QStringList& native, QStringList& native32, QStringList& native64, const QString& overridePath) const { bool local = isLocal(); // Lambda function to get the absolute file path auto actualPath = [this, local, overridePath](QString relPath) { relPath = FS::RemoveInvalidPathChars(relPath); QFileInfo out(FS::PathCombine(storagePrefix(), relPath)); if (local && !overridePath.isEmpty()) { QString fileName = out.fileName(); return QFileInfo(FS::PathCombine(overridePath, fileName)).absoluteFilePath(); } return out.absoluteFilePath(); }; QString raw_storage = storageSuffix(runtimeContext); if (isNative()) { if (raw_storage.contains("${arch}")) { auto nat32Storage = raw_storage; nat32Storage.replace("${arch}", "32"); auto nat64Storage = raw_storage; nat64Storage.replace("${arch}", "64"); native32 += actualPath(nat32Storage); native64 += actualPath(nat64Storage); } else { native += actualPath(raw_storage); } } else { jar += actualPath(raw_storage); } } /** * @brief Get download requests for the library files. * * Depending on whether the library is native or not, and the current runtime context, * this function prepares download requests for the necessary files. It handles both local * and remote files, checks for stale cache entries, and adds checksummed downloads. * * @param runtimeContext The current runtime context. * @param cache Pointer to the HTTP meta cache. * @param failedLocalFiles List to store paths for failed local files. * @param overridePath Optional path to override the default storage path. * @return QList List of download requests. */ QList Library::getDownloads(const RuntimeContext& runtimeContext, class HttpMetaCache* cache, QStringList& failedLocalFiles, const QString& overridePath) const { QList out; bool stale = isAlwaysStale(); bool local = isLocal(); // Lambda function to check if a local file exists auto check_local_file = [overridePath, &failedLocalFiles](QString storage) { QFileInfo fileinfo(storage); QString fileName = fileinfo.fileName(); auto fullPath = FS::PathCombine(overridePath, fileName); QFileInfo localFileInfo(fullPath); if (!localFileInfo.exists()) { failedLocalFiles.append(localFileInfo.filePath()); return false; } return true; }; // Lambda function to add a download request auto add_download = [this, local, check_local_file, cache, stale, &out](QString storage, QString url, QString sha1) { if (local) { return check_local_file(storage); } auto entry = cache->resolveEntry("libraries", storage); if (stale) { entry->setStale(true); } if (!entry->isStale()) return true; Net::Download::Options options; if (stale) { options |= Net::Download::Option::AcceptLocalFiles; } // Don't add a time limit for the libraries cache entry validity options |= Net::Download::Option::MakeEternal; if (sha1.size()) { auto dl = Net::ApiDownload::makeCached(url, entry, options); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, sha1)); qDebug() << "Checksummed Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url << "expected sha1:" << sha1; out.append(dl); } else { out.append(Net::ApiDownload::makeCached(url, entry, options)); qDebug() << "Download for:" << rawName().serialize() << "storage:" << storage << "url:" << url; } return true; }; QString raw_storage = storageSuffix(runtimeContext); if (m_mojangDownloads) { if (isNative()) { auto nativeClassifier = getCompatibleNative(runtimeContext); if (!nativeClassifier.isNull()) { if (nativeClassifier.contains("${arch}")) { auto nat32Classifier = nativeClassifier; nat32Classifier.replace("${arch}", "32"); auto nat64Classifier = nativeClassifier; nat64Classifier.replace("${arch}", "64"); auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier); if (nat32info) { auto cooked_storage = raw_storage; cooked_storage.replace("${arch}", "32"); add_download(cooked_storage, nat32info->url, nat32info->sha1); } auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier); if (nat64info) { auto cooked_storage = raw_storage; cooked_storage.replace("${arch}", "64"); add_download(cooked_storage, nat64info->url, nat64info->sha1); } } else { auto info = m_mojangDownloads->getDownloadInfo(nativeClassifier); if (info) { add_download(raw_storage, info->url, info->sha1); } } } else { qDebug() << "Ignoring native library" << m_name.serialize() << "because it has no classifier for current OS"; } } else { if (m_mojangDownloads->artifact) { auto artifact = m_mojangDownloads->artifact; add_download(raw_storage, artifact->url, artifact->sha1); } else { qDebug() << "Ignoring java library" << m_name.serialize() << "because it has no artifact"; } } } else { auto raw_dl = [this, raw_storage]() { if (!m_absoluteURL.isEmpty()) { return m_absoluteURL; } if (m_repositoryURL.isEmpty()) { return BuildConfig.LIBRARY_BASE + raw_storage; } if (m_repositoryURL.endsWith('/')) { return m_repositoryURL + raw_storage; } else { return m_repositoryURL + QChar('/') + raw_storage; } }(); if (raw_storage.contains("${arch}")) { QString cooked_storage = raw_storage; QString cooked_dl = raw_dl; add_download(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32"), QString()); cooked_storage = raw_storage; cooked_dl = raw_dl; add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64"), QString()); } else { add_download(raw_storage, raw_dl, QString()); } } return out; } /** * @brief Check if the library is active in the given runtime context. * * This function evaluates rules to determine if the library should be active, * considering both general rules and native compatibility. * * @param runtimeContext The current runtime context. * @return bool True if the library is active, false otherwise. */ bool Library::isActive(const RuntimeContext& runtimeContext) const { bool result = true; if (m_rules.empty()) { result = true; } else { Rule::Action ruleResult = Rule::Disallow; for (auto rule : m_rules) { Rule::Action temp = rule.apply(runtimeContext); if (temp != Rule::Defer) ruleResult = temp; } result = result && (ruleResult == Rule::Allow); } if (isNative()) { result = result && !getCompatibleNative(runtimeContext).isNull(); } return result; } /** * @brief Check if the library is considered local. * * @return bool True if the library is local, false otherwise. */ bool Library::isLocal() const { return m_hint == "local"; } /** * @brief Check if the library is always considered stale. * * @return bool True if the library is always stale, false otherwise. */ bool Library::isAlwaysStale() const { return m_hint == "always-stale"; } /** * @brief Get the compatible native classifier for the current runtime context. * * This function attempts to match the current runtime context with the appropriate * native classifier. * * @param runtimeContext The current runtime context. * @return QString The compatible native classifier, or an empty string if none is found. */ QString Library::getCompatibleNative(const RuntimeContext& runtimeContext) const { // try to match precise classifier "[os]-[arch]" auto entry = m_nativeClassifiers.constFind(runtimeContext.getClassifier()); // try to match imprecise classifier on legacy architectures "[os]" if (entry == m_nativeClassifiers.constEnd() && runtimeContext.isLegacyArch()) entry = m_nativeClassifiers.constFind(runtimeContext.system); if (entry == m_nativeClassifiers.constEnd()) return QString(); return entry.value(); } /** * @brief Set the storage prefix for the library. * * @param prefix The storage prefix to set. */ void Library::setStoragePrefix(QString prefix) { m_storagePrefix = prefix; } /** * @brief Get the default storage prefix for libraries. * * @return QString The default storage prefix. */ QString Library::defaultStoragePrefix() { return "libraries/"; } /** * @brief Get the current storage prefix for the library. * * @return QString The current storage prefix. */ QString Library::storagePrefix() const { if (m_storagePrefix.isEmpty()) { return defaultStoragePrefix(); } return m_storagePrefix; } /** * @brief Get the filename for the library in the current runtime context. * * This function determines the appropriate filename for the library, taking into * account native classifiers if applicable. * * @param runtimeContext The current runtime context. * @return QString The filename of the library. */ QString Library::filename(const RuntimeContext& runtimeContext) const { if (!m_filename.isEmpty()) { return m_filename; } // non-native? use only the gradle specifier if (!isNative()) { return m_name.getFileName(); } // otherwise native, override classifiers. Mojang HACK! GradleSpecifier nativeSpec = m_name; QString nativeClassifier = getCompatibleNative(runtimeContext); if (!nativeClassifier.isNull()) { nativeSpec.setClassifier(nativeClassifier); } else { nativeSpec.setClassifier("INVALID"); } return nativeSpec.getFileName(); } /** * @brief Get the display name for the library in the current runtime context. * * This function returns the display name for the library, defaulting to the filename * if no display name is set. * * @param runtimeContext The current runtime context. * @return QString The display name of the library. */ QString Library::displayName(const RuntimeContext& runtimeContext) const { if (!m_displayname.isEmpty()) return m_displayname; return filename(runtimeContext); } /** * @brief Get the storage suffix for the library in the current runtime context. * * This function determines the appropriate storage suffix for the library, taking into * account native classifiers if applicable. * * @param runtimeContext The current runtime context. * @return QString The storage suffix of the library. */ QString Library::storageSuffix(const RuntimeContext& runtimeContext) const { // non-native? use only the gradle specifier if (!isNative()) { return m_name.toPath(m_filename); } // otherwise native, override classifiers. Mojang HACK! GradleSpecifier nativeSpec = m_name; QString nativeClassifier = getCompatibleNative(runtimeContext); if (!nativeClassifier.isNull()) { nativeSpec.setClassifier(nativeClassifier); } else { nativeSpec.setClassifier("INVALID"); } return nativeSpec.toPath(m_filename); } PrismLauncher-11.0.3/launcher/minecraft/Logging.h0000644000175100017510000000164215224505336021276 0ustar runnerrunner // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include Q_DECLARE_LOGGING_CATEGORY(instanceProfileC) Q_DECLARE_LOGGING_CATEGORY(instanceProfileResolveC) PrismLauncher-11.0.3/launcher/minecraft/mod/0000755000175100017510000000000015224505336020313 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/minecraft/mod/DataPack.cpp0000644000175100017510000003072315224505336022474 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "DataPack.h" #include #include #include #include #include "MTPixmapCache.h" #include "Version.h" #include "minecraft/mod/tasks/LocalDataPackParseTask.h" // Values taken from: // https://minecraft.wiki/w/Pack_format#List_of_data_pack_formats static const QMap, std::pair> s_pack_format_versions = { { { 4, 0 }, { Version("1.13"), Version("1.14.4") } }, { { 5, 0 }, { Version("1.15"), Version("1.16.1") } }, { { 6, 0 }, { Version("1.16.2"), Version("1.16.5") } }, { { 7, 0 }, { Version("1.17"), Version("1.17.1") } }, { { 8, 0 }, { Version("1.18"), Version("1.18.1") } }, { { 9, 0 }, { Version("1.18.2"), Version("1.18.2") } }, { { 10, 0 }, { Version("1.19"), Version("1.19.3") } }, { { 11, 0 }, { Version("23w03a"), Version("23w05a") } }, { { 12, 0 }, { Version("1.19.4"), Version("1.19.4") } }, { { 13, 0 }, { Version("23w12a"), Version("23w14a") } }, { { 14, 0 }, { Version("23w16a"), Version("23w17a") } }, { { 15, 0 }, { Version("1.20"), Version("1.20.1") } }, { { 16, 0 }, { Version("23w31a"), Version("23w31a") } }, { { 17, 0 }, { Version("23w32a"), Version("23w35a") } }, { { 18, 0 }, { Version("1.20.2"), Version("1.20.2") } }, { { 19, 0 }, { Version("23w40a"), Version("23w40a") } }, { { 20, 0 }, { Version("23w41a"), Version("23w41a") } }, { { 21, 0 }, { Version("23w42a"), Version("23w42a") } }, { { 22, 0 }, { Version("23w43a"), Version("23w43b") } }, { { 23, 0 }, { Version("23w44a"), Version("23w44a") } }, { { 24, 0 }, { Version("23w45a"), Version("23w45a") } }, { { 25, 0 }, { Version("23w46a"), Version("23w46a") } }, { { 26, 0 }, { Version("1.20.3"), Version("1.20.4") } }, { { 27, 0 }, { Version("23w51a"), Version("23w51b") } }, { { 28, 0 }, { Version("24w03a"), Version("24w03b") } }, { { 29, 0 }, { Version("24w04a"), Version("24w04a") } }, { { 30, 0 }, { Version("24w05a"), Version("24w05b") } }, { { 31, 0 }, { Version("24w06a"), Version("24w06a") } }, { { 32, 0 }, { Version("24w07a"), Version("24w07a") } }, { { 33, 0 }, { Version("24w09a"), Version("24w09a") } }, { { 34, 0 }, { Version("24w10a"), Version("24w10a") } }, { { 35, 0 }, { Version("24w11a"), Version("24w11a") } }, { { 36, 0 }, { Version("24w12a"), Version("24w12a") } }, { { 37, 0 }, { Version("24w13a"), Version("24w13a") } }, { { 38, 0 }, { Version("24w14a"), Version("24w14a") } }, { { 39, 0 }, { Version("1.20.5-pre1"), Version("1.20.5-pre1") } }, { { 40, 0 }, { Version("1.20.5-pre2"), Version("1.20.5-pre2") } }, { { 41, 0 }, { Version("1.20.5"), Version("1.20.6") } }, { { 42, 0 }, { Version("24w18a"), Version("24w18a") } }, { { 43, 0 }, { Version("24w19a"), Version("24w19b") } }, { { 44, 0 }, { Version("24w20a"), Version("24w20a") } }, { { 45, 0 }, { Version("24w21a"), Version("24w21b") } }, { { 46, 0 }, { Version("1.21-pre1"), Version("1.21-pre1") } }, { { 47, 0 }, { Version("1.21-pre2"), Version("1.21-pre2") } }, { { 48, 0 }, { Version("1.21"), Version("1.21.1") } }, { { 49, 0 }, { Version("24w33a"), Version("24w33a") } }, { { 50, 0 }, { Version("24w34a"), Version("24w34a") } }, { { 51, 0 }, { Version("24w35a"), Version("24w35a") } }, { { 52, 0 }, { Version("24w36a"), Version("24w36a") } }, { { 53, 0 }, { Version("24w37a"), Version("24w37a") } }, { { 54, 0 }, { Version("24w38a"), Version("24w38a") } }, { { 55, 0 }, { Version("24w39a"), Version("24w39a") } }, { { 56, 0 }, { Version("24w40a"), Version("24w40a") } }, { { 57, 0 }, { Version("1.21.2"), Version("1.21.3") } }, { { 58, 0 }, { Version("24w44a"), Version("24w44a") } }, { { 59, 0 }, { Version("24w45a"), Version("24w45a") } }, { { 60, 0 }, { Version("24w46a"), Version("1.21.4-pre1") } }, { { 61, 0 }, { Version("1.21.4"), Version("1.21.4") } }, { { 62, 0 }, { Version("25w02a"), Version("25w02a") } }, { { 63, 0 }, { Version("25w03a"), Version("25w03a") } }, { { 64, 0 }, { Version("25w04a"), Version("25w04a") } }, { { 65, 0 }, { Version("25w05a"), Version("25w05a") } }, { { 66, 0 }, { Version("25w06a"), Version("25w06a") } }, { { 67, 0 }, { Version("25w07a"), Version("25w07a") } }, { { 68, 0 }, { Version("25w08a"), Version("25w08a") } }, { { 69, 0 }, { Version("25w09a"), Version("25w09b") } }, { { 70, 0 }, { Version("25w10a"), Version("1.21.5-pre1") } }, { { 71, 0 }, { Version("1.21.5"), Version("1.21.5") } }, { { 72, 0 }, { Version("25w15a"), Version("25w15a") } }, { { 73, 0 }, { Version("25w16a"), Version("25w16a") } }, { { 74, 0 }, { Version("25w17a"), Version("25w17a") } }, { { 75, 0 }, { Version("25w18a"), Version("25w18a") } }, { { 76, 0 }, { Version("25w19a"), Version("25w19a") } }, { { 77, 0 }, { Version("25w20a"), Version("25w20a") } }, { { 78, 0 }, { Version("25w21a"), Version("25w21a") } }, { { 79, 0 }, { Version("1.21.6-pre1"), Version("1.21.6-pre2") } }, { { 80, 0 }, { Version("1.21.6"), Version("1.21.6") } }, { { 81, 0 }, { Version("1.21.7"), Version("1.21.8") } }, { { 82, 0 }, { Version("25w31a"), Version("25w31a") } }, { { 83, 0 }, { Version("25w32a"), Version("25w32a") } }, { { 83, 1 }, { Version("25w33a"), Version("25w33a") } }, { { 84, 0 }, { Version("25w34a"), Version("25w34b") } }, { { 85, 0 }, { Version("25w35a"), Version("25w35a") } }, { { 86, 0 }, { Version("25w36a"), Version("25w36b") } }, { { 87, 0 }, { Version("25w37a"), Version("1.21.9-pre1") } }, { { 87, 1 }, { Version("1.21.9-pre1"), Version("1.21.9-pre1") } }, { { 88, 0 }, { Version("1.21.9"), Version("1.21.10") } }, { { 89, 0 }, { Version("25w41a"), Version("25w41a") } }, { { 90, 0 }, { Version("25w42a"), Version("25w42a") } }, { { 91, 0 }, { Version("25w43a"), Version("25w43a") } }, { { 92, 0 }, { Version("25w44a"), Version("25w44a") } }, { { 93, 0 }, { Version("25w45a"), Version("25w45a") } }, { { 93, 1 }, { Version("25w46a"), Version("25w46a") } }, { { 94, 0 }, { Version("1.21.11-pre1"), Version("1.21.11-pre3") } }, { { 94, 1 }, { Version("1.21.11-pre4"), Version("1.21.11") } }, { { 95, 0 }, { Version("26.1-snap1"), Version("26.1-snap1") } }, { { 96, 0 }, { Version("26.1-snap2"), Version("26.1-snap2") } }, { { 97, 0 }, { Version("26.1-snap3"), Version("26.1-snap3") } }, { { 97, 1 }, { Version("26.1-snap4"), Version("26.1-snap4") } }, { { 98, 0 }, { Version("26.1-snap5"), Version("26.1-snap5") } }, { { 99, 0 }, { Version("26.1-snap6"), Version("26.1-snap6") } }, { { 99, 1 }, { Version("26.1-snap7"), Version("26.1-snap7") } }, { { 99, 2 }, { Version("26.1-snap8"), Version("26.1-snap9") } }, { { 99, 3 }, { Version("26.1-snap10"), Version("26.1-snap10") } }, { { 100, 0 }, { Version("26.1-snap11"), Version("26.1-snap11") } }, }; void DataPack::setPackFormat(int new_format_id, std::pair min_format, std::pair max_format) { QMutexLocker locker(&m_data_lock); m_pack_format = new_format_id; m_min_format = min_format; m_max_format = max_format; } void DataPack::setDescription(QString new_description) { QMutexLocker locker(&m_data_lock); m_description = new_description; } void DataPack::setImage(QImage new_image) const { QMutexLocker locker(&m_data_lock); Q_ASSERT(!new_image.isNull()); if (m_pack_image_cache_key.key.isValid()) PixmapCache::instance().remove(m_pack_image_cache_key.key); // scale the image to avoid flooding the pixmapcache auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); m_pack_image_cache_key.key = PixmapCache::instance().insert(pixmap); m_pack_image_cache_key.was_ever_used = true; // This can happen if the pixmap is too big to fit in the cache :c if (!m_pack_image_cache_key.key.isValid()) { qWarning() << "Could not insert a image cache entry! Ignoring it."; m_pack_image_cache_key.was_ever_used = false; } } QPixmap DataPack::image(QSize size, Qt::AspectRatioMode mode) const { QPixmap cached_image; if (PixmapCache::instance().find(m_pack_image_cache_key.key, &cached_image)) { if (size.isNull()) return cached_image; return cached_image.scaled(size, mode, Qt::SmoothTransformation); } // No valid image we can get if (!m_pack_image_cache_key.was_ever_used) { return {}; } else { qDebug() << "Data Pack" << name() << "Had it's image evicted from the cache. reloading..."; PixmapCache::markCacheMissByEviciton(); } // Imaged got evicted from the cache. Re-process it and retry. DataPackUtils::processPackPNG(this); return image(size); } static std::pair map(std::pair format, const QMap, std::pair>& versions) { if (format.first == 0 || !versions.contains(format)) { return { {}, {} }; } return versions.constFind(format).value(); } static std::pair map(int format, const QMap, std::pair>& versions) { return map({ format, 0 }, versions); } int DataPack::compare(const Resource& other, SortType type) const { const auto& cast_other = static_cast(other); if (type == SortType::PACK_FORMAT) { auto this_ver = packFormat(); auto other_ver = cast_other.packFormat(); if (this_ver > other_ver) return 1; if (this_ver < other_ver) return -1; } else { return Resource::compare(other, type); } return 0; } bool DataPack::applyFilter(QRegularExpression filter) const { if (filter.match(description()).hasMatch()) { return true; } if (filter.match(QString::number(packFormat())).hasMatch()) { return true; } auto versions = { map(m_pack_format, mappings()), map(m_min_format, mappings()), map(m_max_format, mappings()) }; for (const auto& version : versions) { if (!version.first.isEmpty()) { if (filter.match(version.first.toString()).hasMatch()) { return true; } if (filter.match(version.second.toString()).hasMatch()) { return true; } } } return Resource::applyFilter(filter); } bool DataPack::valid() const { return m_pack_format != 0 || (m_min_format.first != 0 && m_max_format.first != 0); } QMap, std::pair> DataPack::mappings() const { return s_pack_format_versions; } QString DataPack::packFormatStr() const { if (m_pack_format != 0) { auto version_bounds = map(m_pack_format, mappings()); if (version_bounds.first.toString().isEmpty()) { return QString::number(m_pack_format); } return QString("%1 (%2 - %3)") .arg(QString::number(m_pack_format), version_bounds.first.toString(), version_bounds.second.toString()); } auto min_bound = map(m_min_format, mappings()); auto max_bound = map(m_max_format, mappings()); auto min_version = min_bound.first; auto max_version = max_bound.second; if (min_version.isEmpty() || max_version.isEmpty()) { return tr("Unrecognized"); } auto str = QString("[") + QString::number(m_min_format.first); if (m_min_format.second != 0) { str += "." + QString::number(m_min_format.second); } str += QString(" - ") + QString::number(m_max_format.first); if (m_max_format.second != 0) { str += "." + QString::number(m_max_format.second); } return str + QString(" (%2 - %3)").arg(min_version.toString(), max_version.toString()); } PrismLauncher-11.0.3/launcher/minecraft/mod/WorldSave.cpp0000644000175100017510000000241215224505336022724 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "WorldSave.h" #include "minecraft/mod/tasks/LocalWorldSaveParseTask.h" void WorldSave::setSaveFormat(WorldSaveFormat new_save_format) { QMutexLocker locker(&m_data_lock); m_save_format = new_save_format; } void WorldSave::setSaveDirName(QString dir_name) { QMutexLocker locker(&m_data_lock); m_save_dir_name = dir_name; } bool WorldSave::valid() const { return m_save_format != WorldSaveFormat::INVALID; } PrismLauncher-11.0.3/launcher/minecraft/mod/Resource.h0000644000175100017510000001670715224505336022266 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include "MetadataHandler.h" #include "QObjectPtr.h" class BaseInstance; enum class ResourceType { UNKNOWN, //!< Indicates an unspecified resource type. ZIPFILE, //!< The resource is a zip file containing the resource's class files. SINGLEFILE, //!< The resource is a single file (not a zip file). FOLDER, //!< The resource is in a folder on the filesystem. LITEMOD, //!< The resource is a litemod }; QDebug operator<<(QDebug debug, ResourceType type); enum class ResourceStatus { INSTALLED, // Both JAR and Metadata are present NOT_INSTALLED, // Only the Metadata is present NO_METADATA, // Only the JAR is present UNKNOWN, // Default status }; QDebug operator<<(QDebug debug, ResourceStatus status); enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER, SIZE, SIDE, MC_VERSIONS, LOADERS, RELEASE_TYPE, REQUIRES, REQUIRED_BY, }; enum class EnableAction { ENABLE, DISABLE, TOGGLE }; /** General class for managed resources. It mirrors a file in disk, with some more info * for display and house-keeping purposes. * * Subclass it to add additional data / behavior, such as Mods or Resource packs. */ class Resource : public QObject { Q_OBJECT Q_DISABLE_COPY(Resource) public: using Ptr = shared_qobject_ptr; using WeakPtr = QPointer; Resource(QObject* parent = nullptr); Resource(QFileInfo file_info); Resource(QString file_path) : Resource(QFileInfo(file_path)) {} ~Resource() override = default; void setFile(QFileInfo file_info); void parseFile(); auto fileinfo() const -> QFileInfo { return m_file_info; } auto dateTimeChanged() const -> QDateTime { return m_changed_date_time; } auto internal_id() const -> QString { return m_internal_id; } auto type() const -> ResourceType { return m_type; } bool enabled() const { return m_enabled; } auto getOriginalFileName() const -> QString; QString sizeStr() const { return m_size_str; } qint64 sizeInfo() const { return m_size_info; } virtual auto name() const -> QString; virtual bool valid() const { return m_type != ResourceType::UNKNOWN; } auto status() const -> ResourceStatus { return m_status; }; auto metadata() -> std::shared_ptr { return m_metadata; } auto metadata() const -> std::shared_ptr { return m_metadata; } auto provider() const -> QString; virtual auto homepage() const -> QString; void setStatus(ResourceStatus status) { m_status = status; } void setMetadata(std::shared_ptr&& metadata); void setMetadata(const Metadata::ModStruct& metadata) { setMetadata(std::make_shared(metadata)); } /** * Returns compatibility issues with the resource and the instance. * This is initially empty, and may be updated when calling updateIssues. */ QStringList issues() const; void updateIssues(const BaseInstance* inst); bool hasIssues() const { return !m_issues.empty(); } /** Compares two Resources, for sorting purposes, considering a ascending order, returning: * > 0: 'this' comes after 'other' * = 0: 'this' is equal to 'other' * < 0: 'this' comes before 'other' */ virtual int compare(const Resource& other, SortType type = SortType::NAME) const; /** Returns whether the given filter should filter out 'this' (false), * or if such filter includes the Resource (true). */ virtual bool applyFilter(QRegularExpression filter) const; /** Changes the enabled property, according to 'action'. * * Returns whether a change was applied to the Resource's properties. */ bool enable(EnableAction action); auto shouldResolve() const -> bool { return !m_is_resolving && !m_is_resolved; } auto isResolving() const -> bool { return m_is_resolving; } auto isResolved() const -> bool { return m_is_resolved; } auto resolutionTicket() const -> int { return m_resolution_ticket; } void setResolving(bool resolving, int resolutionTicket) { m_is_resolving = resolving; m_resolution_ticket = resolutionTicket; } // Delete all files of this resource. auto destroy(const QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool; // Delete the metadata only. auto destroyMetadata(const QDir& index_dir) -> void; auto isSymLink() const -> bool { return m_file_info.isSymLink(); } /** * @brief Take a instance path, checks if the file pointed to by the resource is a symlink or under a symlink in that instance * * @param instPath path to an instance directory * @return true * @return false */ bool isSymLinkUnder(const QString& instPath) const; bool isMoreThanOneHardLink() const; protected: /* The file corresponding to this resource. */ QFileInfo m_file_info; /* The cached date when this file was last changed. */ QDateTime m_changed_date_time; /* Internal ID for internal purposes. Properties such as human-readability should not be assumed. */ QString m_internal_id; /* Name as reported via the file name. In the absence of a better name, this is shown to the user. */ QString m_name; /* The type of file we're dealing with. */ ResourceType m_type = ResourceType::UNKNOWN; /* Installation status of the resource. */ ResourceStatus m_status = ResourceStatus::UNKNOWN; std::shared_ptr m_metadata = nullptr; /* Whether the resource is enabled (e.g. shows up in the game) or not. */ bool m_enabled = true; QList m_issues; /* Used to keep trach of pending / concluded actions on the resource. */ bool m_is_resolving = false; bool m_is_resolved = false; int m_resolution_ticket = 0; QString m_size_str; qint64 m_size_info; }; PrismLauncher-11.0.3/launcher/minecraft/mod/TexturePackFolderModel.cpp0000644000175100017510000001304015224505336025371 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "TexturePackFolderModel.h" #include "minecraft/mod/tasks/LocalTexturePackParseTask.h" #include "minecraft/mod/tasks/ResourceFolderLoadTask.h" TexturePackFolderModel::TexturePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) : ResourceFolderModel(QDir(dir), instance, is_indexed, create_dir, parent) { m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Provider", "Size" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Provider"), tr("Size") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE, SortType::PROVIDER, SortType::SIZE }; m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true, true }; } Task* TexturePackFolderModel::createParseTask(Resource& resource) { return new LocalTexturePackParseTask(m_next_resolution_ticket, static_cast(resource)); } QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const { if (!validateIndex(index)) return {}; int row = index.row(); int column = index.column(); switch (role) { case Qt::BackgroundRole: return rowBackground(row); case Qt::DecorationRole: { if (column == ImageColumn) { return at(row).image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); } break; } case Qt::SizeHintRole: if (column == ImageColumn) { return QSize(32, 32); } break; } // map the columns to the base equivilents QModelIndex mappedIndex; switch (column) { case ActiveColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::ActiveColumn); break; case NameColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::NameColumn); break; case DateColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::DateColumn); break; case ProviderColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::ProviderColumn); break; case SizeColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::SizeColumn); break; } if (mappedIndex.isValid()) { return ResourceFolderModel::data(mappedIndex, role); } return {}; } QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { switch (role) { case Qt::DisplayRole: switch (section) { case ActiveColumn: case NameColumn: case DateColumn: case ImageColumn: case ProviderColumn: case SizeColumn: return columnNames().at(section); default: return {}; } case Qt::ToolTipRole: { switch (section) { case ActiveColumn: return tr("Is the texture pack enabled?"); case NameColumn: return tr("The name of the texture pack."); case DateColumn: return tr("The date and time this texture pack was last changed (or added)."); case ProviderColumn: return tr("The source provider of the texture pack."); case SizeColumn: return tr("The size of the texture pack."); default: return {}; } } default: break; } return {}; } int TexturePackFolderModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; } PrismLauncher-11.0.3/launcher/minecraft/mod/TexturePack.h0000644000175100017510000000432515224505336022727 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "Resource.h" #include #include #include #include class Version; class TexturePack : public Resource { Q_OBJECT public: using Ptr = shared_qobject_ptr; TexturePack(QObject* parent = nullptr) : Resource(parent) {} TexturePack(QFileInfo file_info) : Resource(file_info) {} /** Gets the description of the texture pack. */ QString description() const { return m_description; } /** Gets the image of the texture pack, converted to a QPixmap for drawing, and scaled to size. */ QPixmap image(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const; /** Thread-safe. */ void setDescription(QString new_description); /** Thread-safe. */ void setImage(QImage new_image) const; bool valid() const override; protected: mutable QMutex m_data_lock; /** The texture pack's description, as defined in the pack.txt file. */ QString m_description; /** The texture pack's image file cache key, for access in the QPixmapCache global instance. * * The 'was_ever_used' state simply identifies whether the key was never inserted on the cache (true), * so as to tell whether a cache entry is inexistent or if it was just evicted from the cache. */ struct { QPixmapCache::Key key; bool was_ever_used = false; } mutable m_pack_image_cache_key; }; PrismLauncher-11.0.3/launcher/minecraft/mod/ModDetails.h0000644000175100017510000001150115224505336022507 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include struct ModLicense { QString name = {}; QString id = {}; QString url = {}; QString description = {}; ModLicense() {} ModLicense(const QString license) { // FIXME: come up with a better license parsing. // handle SPDX identifiers? https://spdx.org/licenses/ auto parts = license.split(' '); QStringList notNameParts = {}; for (auto part : parts) { auto _url = QUrl(part); if (part.startsWith("(") && part.endsWith(")")) _url = QUrl(part.mid(1, part.size() - 2)); if (_url.isValid() && !_url.scheme().isEmpty() && !_url.host().isEmpty()) { this->url = _url.toString(); notNameParts.append(part); continue; } } for (auto part : notNameParts) { parts.removeOne(part); } auto licensePart = parts.join(' '); this->name = licensePart; this->description = licensePart; if (parts.size() == 1) { this->id = parts.first(); } } ModLicense(const QString& name_, const QString& id_, const QString& url_, const QString& description_) : name(name_), id(id_), url(url_), description(description_) {} ModLicense(const ModLicense& other) : name(other.name), id(other.id), url(other.url), description(other.description) {} ModLicense& operator=(const ModLicense& other) { this->name = other.name; this->id = other.id; this->url = other.url; this->description = other.description; return *this; } ModLicense& operator=(const ModLicense&& other) { this->name = other.name; this->id = other.id; this->url = other.url; this->description = other.description; return *this; } bool isEmpty() { return this->name.isEmpty() && this->id.isEmpty() && this->url.isEmpty() && this->description.isEmpty(); } }; struct ModDetails { /* Mod ID as defined in the ModLoader-specific metadata */ QString mod_id = {}; /* Human-readable name */ QString name = {}; /* Human-readable mod version */ QString version = {}; /* Human-readable minecraft version */ QString mcversion = {}; /* URL for mod's home page */ QString homeurl = {}; /* Human-readable description */ QString description = {}; /* List of the author's names */ QStringList authors = {}; /* Issue Tracker URL */ QString issue_tracker = {}; /* License */ QList licenses = {}; /* Path of mod logo */ QString icon_file = {}; QStringList dependencies = {}; ModDetails() = default; /** Metadata should be handled manually to properly set the mod status. */ ModDetails(const ModDetails& other) : mod_id(other.mod_id) , name(other.name) , version(other.version) , mcversion(other.mcversion) , homeurl(other.homeurl) , description(other.description) , authors(other.authors) , issue_tracker(other.issue_tracker) , licenses(other.licenses) , icon_file(other.icon_file) , dependencies(other.dependencies) {} ModDetails& operator=(const ModDetails& other) = default; ModDetails& operator=(ModDetails&& other) = default; }; PrismLauncher-11.0.3/launcher/minecraft/mod/DataPack.h0000644000175100017510000000576615224505336022152 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "Resource.h" #include #include #include class Version; /* TODO: * * Store localized descriptions * */ class DataPack : public Resource { Q_OBJECT public: DataPack(QObject* parent = nullptr) : Resource(parent) {} DataPack(QFileInfo file_info) : Resource(file_info) {} /** Gets the numerical ID of the pack format. */ int packFormat() const { return m_pack_format; } /** Gets the description of the data pack. */ QString description() const { return m_description; } /** Gets the image of the data pack, converted to a QPixmap for drawing, and scaled to size. */ QPixmap image(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const; /** Thread-safe. */ void setPackFormat(int new_format_id, std::pair min_format, std::pair max_format); /** Thread-safe. */ void setDescription(QString new_description); /** Thread-safe. */ void setImage(QImage new_image) const; bool valid() const override; [[nodiscard]] int compare(const Resource& other, SortType type) const override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; QString packFormatStr() const; protected: virtual QMap, std::pair> mappings() const; protected: mutable QMutex m_data_lock; /* The 'version' of a data pack, as defined in the pack.mcmeta file. * See https://minecraft.wiki/w/Data_pack#pack.mcmeta */ int m_pack_format = 0; std::pair m_min_format; std::pair m_max_format; /** The data pack's description, as defined in the pack.mcmeta file. */ QString m_description; /** The data pack's image file cache key, for access in the QPixmapCache global instance. * * The 'was_ever_used' state simply identifies whether the key was never inserted on the cache (true), * so as to tell whether a cache entry is inexistent or if it was just evicted from the cache. */ struct { QPixmapCache::Key key; bool was_ever_used = false; } mutable m_pack_image_cache_key; }; PrismLauncher-11.0.3/launcher/minecraft/mod/DataPackFolderModel.h0000644000175100017510000000465015224505336024256 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "ResourceFolderModel.h" #include "DataPack.h" #include "ResourcePack.h" class DataPackFolderModel : public ResourceFolderModel { Q_OBJECT public: enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, NUM_COLUMNS }; explicit DataPackFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); virtual QString id() const override { return "datapacks"; } QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex& parent) const override; [[nodiscard]] Resource* createResource(const QFileInfo& file) override; [[nodiscard]] Task* createParseTask(Resource&) override; RESOURCE_HELPERS(DataPack) }; PrismLauncher-11.0.3/launcher/minecraft/mod/ResourceFolderModel.h0000644000175100017510000002762315224505336024402 0ustar runnerrunner#pragma once #include #include #include #include #include #include #include #include #include #include "Resource.h" #include "BaseInstance.h" #include "tasks/ConcurrentTask.h" #include "tasks/Task.h" class QSortFilterProxyModel; /* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */ #define RESOURCE_HELPERS(T) \ T& at(int index) \ { \ return *static_cast(m_resources[index].get()); \ } \ const T& at(int index) const \ { \ return *static_cast(m_resources.at(index).get()); \ } \ QList selected##T##s(const QModelIndexList& indexes) \ { \ QList result; \ for (const QModelIndex& index : indexes) { \ if (index.column() != 0) \ continue; \ \ result.append(&at(index.row())); \ } \ return result; \ } \ QList all##T##s() \ { \ QList result; \ result.reserve(m_resources.size()); \ \ for (const Resource::Ptr& resource : m_resources) \ result.append(static_cast(resource.get())); \ \ return result; \ } /** A basic model for external resources. * * This model manages a list of resources. As such, external users of such resources do not own them, * and the resource's lifetime is contingent on the model's lifetime. * * TODO: Make the resources unique pointers accessible through weak pointers. */ class ResourceFolderModel : public QAbstractListModel { Q_OBJECT public: ResourceFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); ~ResourceFolderModel() override; virtual QString id() const { return "resource"; } /** Starts watching the paths for changes. * * Returns whether starting to watch all the paths was successful. * If one or more fails, it returns false. */ bool startWatching(const QStringList& paths); /** Stops watching the paths for changes. * * Returns whether stopping to watch all the paths was successful. * If one or more fails, it returns false. */ bool stopWatching(const QStringList& paths); /* Helper methods for subclasses, using a predetermined list of paths. */ virtual bool startWatching() { return startWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); } virtual bool stopWatching() { return stopWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); } virtual QDir indexDir() const { return { QString("%1/.index").arg(dir().absolutePath()) }; } /** Given a path in the system, install that resource, moving it to its place in the * instance file hierarchy. * * Returns whether the installation was succcessful. */ virtual bool installResource(QString path); virtual void installResourceWithFlameMetadata(QString path, ModPlatform::IndexedVersion& vers); /** Uninstall (i.e. remove all data about it) a resource, given its file name. * * Returns whether the removal was successful. */ virtual bool uninstallResource(const QString& file_name, bool preserve_metadata = false); virtual bool deleteResources(const QModelIndexList&); virtual void deleteMetadata(const QModelIndexList&); /** Applies the given 'action' to the resources in 'indexes'. * * Returns whether the action was successfully applied to all resources. */ virtual bool setResourceEnabled(const QModelIndexList& indexes, EnableAction action); /** Creates a new update task and start it. Returns false if no update was done, like when an update is already underway. */ virtual bool update(); /** Creates a new parse task, if needed, for 'res' and start it.*/ virtual void resolveResource(Resource::Ptr res); qsizetype size() const { return m_resources.size(); } [[nodiscard]] bool empty() const { return size() == 0; } Resource& at(int index) { return *m_resources[index].get(); } const Resource& at(int index) const { return *m_resources.at(index).get(); } QList selectedResources(const QModelIndexList& indexes); QList allResources(); Resource::Ptr find(QString id); QDir const& dir() const { return m_dir; } /** Checks whether there's any parse tasks being done. * * Since they can be quite expensive, and are usually done in a separate thread, if we were to destroy the model while having * such tasks would introduce an undefined behavior, most likely resulting in a crash. */ bool hasPendingParseTasks() const; /* Qt behavior */ /* Basic columns */ enum Columns { ActiveColumn = 0, NameColumn, DateColumn, ProviderColumn, SizeColumn, NUM_COLUMNS }; QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; } int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast(size()); } int columnCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : NUM_COLUMNS; } Qt::DropActions supportedDropActions() const override; /// flags, mostly to support drag&drop Qt::ItemFlags flags(const QModelIndex& index) const override; QStringList mimeTypes() const override; [[nodiscard]] bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; [[nodiscard]] bool validateIndex(const QModelIndex& index) const; QBrush rowBackground(int row) const; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; void setupHeaderAction(QAction* act, int column); void saveColumns(QTreeView* tree); void loadColumns(QTreeView* tree); QMenu* createHeaderContextMenu(QTreeView* tree); /** This creates a proxy model to filter / sort the model for a UI. * * The actual comparisons and filtering are done directly by the Resource, so to modify behavior go there instead! */ QSortFilterProxyModel* createFilterProxyModel(QObject* parent = nullptr); SortType columnToSortKey(size_t column) const; QList columnResizeModes() const { return m_column_resize_modes; } class ProxyModel : public QSortFilterProxyModel { public: explicit ProxyModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent) {} protected: bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override; }; QString instDirPath() const; BaseInstance* instance() const { return m_instance; } signals: void updateFinished(); void parseFinished(); protected: [[nodiscard]] virtual Task* createPreUpdateTask() { return nullptr; } /** This creates a new update task to be executed by update(). * * The task should load and parse all resources necessary, and provide a way of accessing such results. * * This Task is normally executed when opening a page, so it shouldn't contain much heavy work. * If such work is needed, try using it in the Task create by createParseTask() instead! */ [[nodiscard]] Task* createUpdateTask(); [[nodiscard]] virtual Resource* createResource(const QFileInfo& info) { return new Resource(info); } /** This creates a new parse task to be executed by onUpdateSucceeded(). * * This task should load and parse all heavy info needed by a resource, such as parsing a manifest. It gets executed * in the background, so it slowly updates the UI as tasks get done. */ [[nodiscard]] virtual Task* createParseTask(Resource&) { return nullptr; } /** Standard implementation of the model update logic. * * It uses set operations to find differences between the current state and the updated state, * to act only on those disparities. * */ void applyUpdates(QSet& current_set, QSet& new_set, QMap& new_resources); protected slots: void directoryChanged(QString); /** Called when the update task is successful. * * This usually calls static_cast on the specific Task type returned by createUpdateTask, * so care must be taken in such cases. * TODO: Figure out a way to express this relationship better without templated classes (Q_OBJECT macro disallows that). */ virtual void onUpdateSucceeded(); virtual void onUpdateFailed() {} /** Called when the parse task with the given ticket is successful. * * This is just a simple reference implementation. You probably want to override it with your own logic in a subclass * if the resource is complex and has more stuff to parse. */ virtual void onParseSucceeded(int ticket, QString resource_id); virtual void onParseFailed(int ticket, QString resource_id); protected: // Represents the relationship between a column's index (represented by the list index), and it's sorting key. // As such, the order in with they appear is very important! QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::PROVIDER, SortType::SIZE }; QStringList m_column_names = { "Enable", "Name", "Last Modified", "Provider", "Size" }; QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Provider"), tr("Size") }; QList m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; QList m_columnsHideable = { false, false, true, true, true }; QDir m_dir; BaseInstance* m_instance; QFileSystemWatcher m_watcher; bool m_is_watching = false; bool m_is_indexed; bool m_first_folder_load = true; Task::Ptr m_current_update_task = nullptr; bool m_scheduled_update = false; QList m_resources; // Represents the relationship between a resource's internal ID and it's row position on the model. QMap m_resources_index; // Runs off-thread ConcurrentTask m_resourceResolver; bool m_resourceResolverRunning = false; QMap m_active_parse_tasks; std::atomic m_next_resolution_ticket = 0; }; PrismLauncher-11.0.3/launcher/minecraft/mod/WorldSave.h0000644000175100017510000000362615224505336022401 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "Resource.h" #include class Version; enum class WorldSaveFormat { SINGLE, MULTI, INVALID }; class WorldSave : public Resource { Q_OBJECT public: using Ptr = shared_qobject_ptr; WorldSave(QObject* parent = nullptr) : Resource(parent) {} WorldSave(QFileInfo file_info) : Resource(file_info) {} /** Gets the format of the save. */ WorldSaveFormat saveFormat() const { return m_save_format; } /** Gets the name of the save dir (first found in multi mode). */ QString saveDirName() const { return m_save_dir_name; } /** Thread-safe. */ void setSaveFormat(WorldSaveFormat new_save_format); /** Thread-safe. */ void setSaveDirName(QString dir_name); bool valid() const override; protected: mutable QMutex m_data_lock; /** The format in which the save file is in. * Since saves can be distributed in various slightly different ways, this allows us to treat them separately. */ WorldSaveFormat m_save_format = WorldSaveFormat::INVALID; QString m_save_dir_name; }; PrismLauncher-11.0.3/launcher/minecraft/mod/Mod.cpp0000644000175100017510000002121115224505336021533 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Mod.h" #include #include #include #include "MTPixmapCache.h" #include "MetadataHandler.h" #include "Resource.h" #include "Version.h" #include "minecraft/mod/ModDetails.h" #include "minecraft/mod/tasks/LocalModParseTask.h" #include "modplatform/ModIndex.h" Mod::Mod(const QFileInfo& file) : Resource(file), m_local_details() { m_enabled = (file.suffix() != "disabled"); } void Mod::setDetails(const ModDetails& details) { m_local_details = details; } int Mod::compare(const Resource& other, SortType type) const { auto cast_other = dynamic_cast(&other); if (!cast_other) return Resource::compare(other, type); switch (type) { default: case SortType::ENABLED: case SortType::NAME: case SortType::DATE: case SortType::SIZE: return Resource::compare(other, type); case SortType::VERSION: { auto this_ver = Version(version()); auto other_ver = Version(cast_other->version()); if (this_ver > other_ver) return 1; if (this_ver < other_ver) return -1; break; } case SortType::SIDE: { auto compare_result = QString::compare(side(), cast_other->side(), Qt::CaseInsensitive); if (compare_result != 0) return compare_result; break; } case SortType::MC_VERSIONS: { auto compare_result = QString::compare(mcVersions(), cast_other->mcVersions(), Qt::CaseInsensitive); if (compare_result != 0) return compare_result; break; } case SortType::LOADERS: { auto compare_result = QString::compare(loaders(), cast_other->loaders(), Qt::CaseInsensitive); if (compare_result != 0) return compare_result; break; } case SortType::RELEASE_TYPE: { auto compare_result = QString::compare(releaseType(), cast_other->releaseType(), Qt::CaseInsensitive); if (compare_result != 0) return compare_result; break; } case SortType::REQUIRED_BY: { if (requiredByCount() > cast_other->requiredByCount()) return 1; if (requiredByCount() < cast_other->requiredByCount()) return -1; break; } case SortType::REQUIRES: { if (requiresCount() > cast_other->requiresCount()) return 1; if (requiresCount() < cast_other->requiresCount()) return -1; break; } } return 0; } bool Mod::applyFilter(QRegularExpression filter) const { if (filter.match(description()).hasMatch()) return true; for (auto& author : authors()) { if (filter.match(author).hasMatch()) { return true; } } return Resource::applyFilter(filter); } auto Mod::details() const -> const ModDetails& { return m_local_details; } auto Mod::name() const -> QString { auto d_name = details().name; if (!d_name.isEmpty()) return d_name; return Resource::name(); } auto Mod::mod_id() const -> QString { auto d_mod_id = details().mod_id; if (!d_mod_id.isEmpty()) return d_mod_id; return Resource::name(); } auto Mod::version() const -> QString { return details().version; } auto Mod::homepage() const -> QString { QString metaUrl = Resource::homepage(); if (metaUrl.isEmpty()) return details().homeurl; else return metaUrl; } auto Mod::loaders() const -> QString { if (metadata()) { QStringList loaders; auto modLoaders = metadata()->loaders; for (auto loader : ModPlatform::modLoaderTypesToList(modLoaders)) { loaders << getModLoaderAsString(loader); } return loaders.join(", "); } return {}; } auto Mod::side() const -> QString { if (metadata()) return ModPlatform::SideUtils::toString(metadata()->side); return ModPlatform::SideUtils::toString(ModPlatform::Side::UniversalSide); } auto Mod::mcVersions() const -> QString { if (metadata()) return metadata()->mcVersions.join(", "); return {}; } auto Mod::releaseType() const -> QString { if (metadata()) return metadata()->releaseType.toString(); return ModPlatform::IndexedVersionType(ModPlatform::IndexedVersionType::Unknown).toString(); } auto Mod::description() const -> QString { return details().description; } auto Mod::authors() const -> QStringList { return details().authors; } void Mod::finishResolvingWithDetails(ModDetails&& details) { m_is_resolving = false; m_is_resolved = true; m_local_details = std::move(details); if (!iconPath().isEmpty()) { m_packImageCacheKey.wasReadAttempt = false; } } auto Mod::licenses() const -> const QList& { return details().licenses; } auto Mod::issueTracker() const -> QString { return details().issue_tracker; } QPixmap Mod::setIcon(QImage new_image) const { QMutexLocker locker(&m_data_lock); Q_ASSERT(!new_image.isNull()); if (m_packImageCacheKey.key.isValid()) PixmapCache::remove(m_packImageCacheKey.key); // scale the image to avoid flooding the pixmapcache auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); m_packImageCacheKey.key = PixmapCache::insert(pixmap); m_packImageCacheKey.wasEverUsed = true; m_packImageCacheKey.wasReadAttempt = true; return pixmap; } QPixmap Mod::icon(QSize size, Qt::AspectRatioMode mode) const { auto pixmap_transform = [&size, &mode](QPixmap pixmap) { if (size.isNull()) return pixmap; return pixmap.scaled(size, mode, Qt::SmoothTransformation); }; QPixmap cached_image; if (PixmapCache::find(m_packImageCacheKey.key, &cached_image)) { return pixmap_transform(cached_image); } // No valid image we can get if ((!m_packImageCacheKey.wasEverUsed && m_packImageCacheKey.wasReadAttempt) || iconPath().isEmpty()) return {}; if (m_packImageCacheKey.wasEverUsed) { qDebug() << "Mod" << name() << "Had it's icon evicted from the cache. reloading..."; PixmapCache::markCacheMissByEviciton(); } // Image got evicted from the cache or an attempt to load it has not been made. load it and retry. m_packImageCacheKey.wasReadAttempt = true; if (ModUtils::loadIconFile(*this, &cached_image)) { return pixmap_transform(cached_image); } // Image failed to load return {}; } bool Mod::valid() const { return !m_local_details.mod_id.isEmpty(); } QStringList Mod::dependencies() const { return details().dependencies; } int Mod::requiredByCount() const { return m_requiredByCount; } int Mod::requiresCount() const { return m_requiresCount; } void Mod::setRequiredByCount(int value) { m_requiredByCount = value; } void Mod::setRequiresCount(int value) { m_requiresCount = value; } PrismLauncher-11.0.3/launcher/minecraft/mod/TexturePack.cpp0000644000175100017510000000460515224505336023263 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "TexturePack.h" #include #include #include "MTPixmapCache.h" #include "minecraft/mod/tasks/LocalTexturePackParseTask.h" void TexturePack::setDescription(QString new_description) { QMutexLocker locker(&m_data_lock); m_description = new_description; } void TexturePack::setImage(QImage new_image) const { QMutexLocker locker(&m_data_lock); Q_ASSERT(!new_image.isNull()); if (m_pack_image_cache_key.key.isValid()) PixmapCache::remove(m_pack_image_cache_key.key); // scale the image to avoid flooding the pixmapcache auto pixmap = QPixmap::fromImage(new_image.scaled({ 64, 64 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); m_pack_image_cache_key.key = PixmapCache::insert(pixmap); m_pack_image_cache_key.was_ever_used = true; } QPixmap TexturePack::image(QSize size, Qt::AspectRatioMode mode) const { QPixmap cached_image; if (PixmapCache::find(m_pack_image_cache_key.key, &cached_image)) { if (size.isNull()) return cached_image; return cached_image.scaled(size, mode, Qt::SmoothTransformation); } // No valid image we can get if (!m_pack_image_cache_key.was_ever_used) { return {}; } else { qDebug() << "Texture Pack" << name() << "Had it's image evicted from the cache. reloading..."; PixmapCache::markCacheMissByEviciton(); } // Imaged got evicted from the cache. Re-process it and retry. TexturePackUtils::processPackPNG(*this); return image(size); } bool TexturePack::valid() const { return m_description != nullptr; } PrismLauncher-11.0.3/launcher/minecraft/mod/ResourcePack.h0000644000175100017510000000112415224505336023050 0ustar runnerrunner#pragma once #include "Resource.h" #include "minecraft/mod/DataPack.h" #include #include #include #include class Version; /* TODO: * * Store localized descriptions * */ class ResourcePack : public DataPack { Q_OBJECT public: ResourcePack(QObject* parent = nullptr) : DataPack(parent) {} ResourcePack(QFileInfo file_info) : DataPack(file_info) {} /** Gets, respectively, the lower and upper versions supported by the set pack format. */ QMap, std::pair> mappings() const override; }; PrismLauncher-11.0.3/launcher/minecraft/mod/MetadataHandler.h0000644000175100017510000000315015224505336023501 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "modplatform/packwiz/Packwiz.h" namespace Metadata { using ModStruct = Packwiz::V1::Mod; inline ModStruct create(const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) { return Packwiz::V1::createModFormat(index_dir, mod_pack, mod_version); } inline void update(const QDir& index_dir, ModStruct& mod) { Packwiz::V1::updateModIndex(index_dir, mod); } inline void remove(const QDir& index_dir, QString mod_slug) { Packwiz::V1::deleteModIndex(index_dir, mod_slug); } inline ModStruct get(const QDir& index_dir, QString mod_slug) { return Packwiz::V1::getIndexForMod(index_dir, std::move(mod_slug)); } inline ModStruct get(const QDir& index_dir, QVariant& mod_id) { return Packwiz::V1::getIndexForMod(index_dir, mod_id); } }; // namespace Metadata PrismLauncher-11.0.3/launcher/minecraft/mod/ResourcePackFolderModel.h0000644000175100017510000000171315224505336025171 0ustar runnerrunner#pragma once #include "ResourceFolderModel.h" #include "ResourcePack.h" class ResourcePackFolderModel : public ResourceFolderModel { Q_OBJECT public: enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, ProviderColumn, SizeColumn, NUM_COLUMNS }; explicit ResourcePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); QString id() const override { return "resourcepacks"; } QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex& parent) const override; [[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new ResourcePack(file); } [[nodiscard]] Task* createParseTask(Resource&) override; RESOURCE_HELPERS(ResourcePack) }; PrismLauncher-11.0.3/launcher/minecraft/mod/Mod.h0000644000175100017510000000752215224505336021211 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include "ModDetails.h" #include "Resource.h" class Mod : public Resource { Q_OBJECT public: using Ptr = shared_qobject_ptr; using WeakPtr = QPointer; Mod() = default; Mod(const QFileInfo& file); Mod(QString file_path) : Mod(QFileInfo(file_path)) {} auto details() const -> const ModDetails&; auto name() const -> QString override; auto mod_id() const -> QString; auto version() const -> QString; auto homepage() const -> QString override; auto description() const -> QString; auto authors() const -> QStringList; auto licenses() const -> const QList&; auto issueTracker() const -> QString; auto side() const -> QString; auto loaders() const -> QString; auto mcVersions() const -> QString; auto releaseType() const -> QString; QStringList dependencies() const; int requiredByCount() const; int requiresCount() const; void setRequiredByCount(int value); void setRequiresCount(int value); /** Get the intneral path to the mod's icon file*/ QString iconPath() const { return m_local_details.icon_file; } /** Gets the icon of the mod, converted to a QPixmap for drawing, and scaled to size. */ QPixmap icon(QSize size, Qt::AspectRatioMode mode = Qt::AspectRatioMode::IgnoreAspectRatio) const; /** Thread-safe. */ QPixmap setIcon(QImage new_image) const; void setDetails(const ModDetails& details); bool valid() const override; [[nodiscard]] int compare(const Resource& other, SortType type) const override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; // Delete all the files of this mod auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool; // Delete the metadata only void destroyMetadata(QDir& index_dir); void finishResolvingWithDetails(ModDetails&& details); protected: ModDetails m_local_details; mutable QMutex m_data_lock; struct { QPixmapCache::Key key; bool wasEverUsed = false; bool wasReadAttempt = false; } mutable m_packImageCacheKey; int m_requiredByCount = 0; int m_requiresCount = 0; }; PrismLauncher-11.0.3/launcher/minecraft/mod/TexturePackFolderModel.h0000644000175100017510000000470715224505336025050 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "ResourceFolderModel.h" #include "TexturePack.h" class TexturePackFolderModel : public ResourceFolderModel { Q_OBJECT public: enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, ProviderColumn, SizeColumn, NUM_COLUMNS }; explicit TexturePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); virtual QString id() const override { return "texturepacks"; } QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex& parent) const override; [[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new TexturePack(file); } [[nodiscard]] Task* createParseTask(Resource&) override; RESOURCE_HELPERS(TexturePack) }; PrismLauncher-11.0.3/launcher/minecraft/mod/ShaderPackFolderModel.h0000644000175100017510000000233515224505336024611 0ustar runnerrunner#pragma once #include "ResourceFolderModel.h" #include "minecraft/mod/ShaderPack.h" #include "minecraft/mod/tasks/LocalShaderPackParseTask.h" class ShaderPackFolderModel : public ResourceFolderModel { Q_OBJECT public: explicit ShaderPackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr) : ResourceFolderModel(dir, instance, is_indexed, create_dir, parent) {} virtual QString id() const override { return "shaderpacks"; } [[nodiscard]] Resource* createResource(const QFileInfo& info) override { return new ShaderPack(info); } [[nodiscard]] Task* createParseTask(Resource& resource) override { return new LocalShaderPackParseTask(m_next_resolution_ticket, static_cast(resource)); } QDir indexDir() const override { return m_dir; } Task* createPreUpdateTask() override; // avoid watching twice virtual bool startWatching() override { return ResourceFolderModel::startWatching({ m_dir.absolutePath() }); } virtual bool stopWatching() override { return ResourceFolderModel::stopWatching({ m_dir.absolutePath() }); } RESOURCE_HELPERS(ShaderPack); private: QMutex m_migrateLock; }; PrismLauncher-11.0.3/launcher/minecraft/mod/ResourceFolderModel.cpp0000644000175100017510000007627215224505336024741 0ustar runnerrunner#include "ResourceFolderModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "Application.h" #include "FileSystem.h" #include "minecraft/mod/tasks/ResourceFolderLoadTask.h" #include "Json.h" #include "minecraft/mod/tasks/LocalResourceUpdateTask.h" #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" #include "settings/Setting.h" #include "tasks/SequentialTask.h" #include "tasks/Task.h" #include "ui/dialogs/CustomMessageBox.h" ResourceFolderModel::ResourceFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) : QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this), m_is_indexed(is_indexed) { if (create_dir) { FS::ensureFolderPathExists(m_dir.absolutePath()); } m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged); connect(&m_resourceResolver, &ConcurrentTask::finished, this, [this] { m_resourceResolver.clear(); m_resourceResolverRunning = false; }); if (APPLICATION_DYN) { // in tests the application macro doesn't work m_resourceResolver.setMaxConcurrent(APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); } } ResourceFolderModel::~ResourceFolderModel() { while (!QThreadPool::globalInstance()->waitForDone(100)) QCoreApplication::processEvents(); } bool ResourceFolderModel::startWatching(const QStringList& paths) { // Remove orphaned metadata next time m_first_folder_load = true; if (m_is_watching) return false; auto couldnt_be_watched = m_watcher.addPaths(paths); for (auto path : paths) { if (couldnt_be_watched.contains(path)) qDebug() << "Failed to start watching" << path; else qDebug() << "Started watching" << path; } update(); m_is_watching = !m_is_watching; return m_is_watching; } bool ResourceFolderModel::stopWatching(const QStringList& paths) { if (!m_is_watching) return false; auto couldnt_be_stopped = m_watcher.removePaths(paths); for (auto path : paths) { if (couldnt_be_stopped.contains(path)) qDebug() << "Failed to stop watching" << path; else qDebug() << "Stopped watching" << path; } m_is_watching = !m_is_watching; return !m_is_watching; } bool ResourceFolderModel::installResource(QString original_path) { // NOTE: fix for GH-1178: remove trailing slash to avoid issues with using the empty result of QFileInfo::fileName original_path = FS::NormalizePath(original_path); QFileInfo file_info(original_path); if (!file_info.exists() || !file_info.isReadable()) { qWarning() << "Caught attempt to install non-existing file or file-like object:" << original_path; return false; } qDebug() << "Installing:" << file_info.absoluteFilePath(); Resource resource(file_info); if (!resource.valid()) { qWarning() << original_path << "is not a valid resource. Ignoring it."; return false; } auto new_path = FS::NormalizePath(m_dir.filePath(file_info.fileName())); if (original_path == new_path) { qWarning() << "Overwriting the mod (" << original_path << ") with itself makes no sense..."; return false; } switch (resource.type()) { case ResourceType::SINGLEFILE: case ResourceType::ZIPFILE: case ResourceType::LITEMOD: { if (QFile::exists(new_path) || QFile::exists(new_path + QString(".disabled"))) { if (!FS::deletePath(new_path)) { qCritical() << "Cleaning up new location (" << new_path << ") was unsuccessful!"; return false; } qDebug() << new_path << "has been deleted."; } if (!QFile::copy(original_path, new_path)) { qCritical() << "Copy from" << original_path << "to" << new_path << "has failed."; return false; } FS::updateTimestamp(new_path); QFileInfo new_path_file_info(new_path); resource.setFile(new_path_file_info); if (!m_is_watching) return update(); return true; } case ResourceType::FOLDER: { if (QFile::exists(new_path)) { qDebug() << "Ignoring folder '" << original_path << "', it would merge with" << new_path; return false; } if (!FS::copy(original_path, new_path)()) { qWarning() << "Copy of folder from" << original_path << "to" << new_path << "has (potentially partially) failed."; return false; } QFileInfo newpathInfo(new_path); resource.setFile(newpathInfo); if (!m_is_watching) return update(); return true; } default: break; } return false; } void ResourceFolderModel::installResourceWithFlameMetadata(QString path, ModPlatform::IndexedVersion& vers) { auto install = [this, path] { installResource(std::move(path)); }; if (vers.addonId.isValid()) { ModPlatform::IndexedPack pack{ vers.addonId, ModPlatform::ResourceProvider::FLAME, }; auto [job, response] = FlameAPI().getProject(vers.addonId.toString()); connect(job.get(), &Task::failed, this, install); connect(job.get(), &Task::aborted, this, install); connect(job.get(), &Task::succeeded, [response, this, &vers, install, &pack] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response for mod info at" << parse_error.offset << "reason:" << parse_error.errorString(); qDebug() << *response; return; } try { auto obj = Json::requireObject(Json::requireObject(doc), "data"); FlameMod::loadIndexedPack(pack, obj); } catch (const JSONValidationError& e) { qDebug() << doc; qWarning() << "Error while reading mod info:" << e.cause(); } LocalResourceUpdateTask update_metadata(indexDir(), pack, vers); connect(&update_metadata, &Task::finished, this, install); update_metadata.start(); }); job->start(); } else { install(); } } bool ResourceFolderModel::uninstallResource(const QString& file_name, bool preserve_metadata) { for (auto& resource : m_resources) { auto resourceFileInfo = resource->fileinfo(); auto resourceFileName = resource->fileinfo().fileName(); if (!resource->enabled() && resourceFileName.endsWith(".disabled")) { resourceFileName.chop(9); } if (resourceFileName == file_name) { auto res = resource->destroy(indexDir(), preserve_metadata, false); update(); return res; } } return false; } bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes) { if (indexes.isEmpty()) return true; for (auto i : indexes) { if (i.column() != 0) continue; auto& resource = m_resources.at(i.row()); resource->destroy(indexDir()); } update(); return true; } void ResourceFolderModel::deleteMetadata(const QModelIndexList& indexes) { if (indexes.isEmpty()) return; for (auto i : indexes) { if (i.column() != 0) continue; auto& resource = m_resources.at(i.row()); resource->destroyMetadata(indexDir()); } update(); } bool ResourceFolderModel::setResourceEnabled(const QModelIndexList& indexes, EnableAction action) { if (m_instance != nullptr && m_instance->isRunning()) { auto response = CustomMessageBox::selectable(nullptr, tr("Confirm toggle"), tr("If you enable/disable this resource while the game is running it may crash your game.\n" "Are you sure you want to do this?"), QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ->exec(); if (response != QMessageBox::Yes) return false; } if (indexes.isEmpty()) return true; bool succeeded = true; for (auto const& idx : indexes) { if (!validateIndex(idx) || idx.column() != 0) continue; int row = idx.row(); auto& resource = m_resources[row]; // Preserve the row, but change its ID auto old_id = resource->internal_id(); if (!resource->enable(action)) { succeeded = false; continue; } auto new_id = resource->internal_id(); m_resources_index.remove(old_id); m_resources_index[new_id] = row; emit dataChanged(index(row, 0), index(row, columnCount(QModelIndex()) - 1)); } return succeeded; } static QMutex s_update_task_mutex; bool ResourceFolderModel::update() { // We hold a lock here to prevent race conditions on the m_current_update_task reset. QMutexLocker lock(&s_update_task_mutex); // Already updating, so we schedule a future update and return. if (m_current_update_task) { m_scheduled_update = true; return false; } m_current_update_task.reset(createUpdateTask()); if (!m_current_update_task) return false; connect(m_current_update_task.get(), &Task::succeeded, this, &ResourceFolderModel::onUpdateSucceeded, Qt::ConnectionType::QueuedConnection); connect(m_current_update_task.get(), &Task::failed, this, &ResourceFolderModel::onUpdateFailed, Qt::ConnectionType::QueuedConnection); connect( m_current_update_task.get(), &Task::finished, this, [this] { m_current_update_task.reset(); if (m_scheduled_update) { m_scheduled_update = false; update(); } else { emit updateFinished(); } }, Qt::ConnectionType::QueuedConnection); Task::Ptr preUpdate{ createPreUpdateTask() }; if (preUpdate != nullptr) { auto task = new SequentialTask("ResourceFolderModel::update"); task->addTask(preUpdate); task->addTask(m_current_update_task); connect(task, &Task::finished, [task] { task->deleteLater(); }); QThreadPool::globalInstance()->start(task); } else { QThreadPool::globalInstance()->start(m_current_update_task.get()); } return true; } void ResourceFolderModel::resolveResource(Resource::Ptr res) { if (!res->shouldResolve()) { return; } Task::Ptr task{ createParseTask(*res) }; if (!task) return; int ticket = m_next_resolution_ticket.fetch_add(1); res->setResolving(true, ticket); m_active_parse_tasks.insert(ticket, task); connect( task.get(), &Task::succeeded, this, [this, ticket, res] { onParseSucceeded(ticket, res->internal_id()); }, Qt::ConnectionType::QueuedConnection); connect( task.get(), &Task::failed, this, [this, ticket, res] { onParseFailed(ticket, res->internal_id()); }, Qt::ConnectionType::QueuedConnection); connect( task.get(), &Task::finished, this, [this, ticket] { m_active_parse_tasks.remove(ticket); emit parseFinished(); }, Qt::ConnectionType::QueuedConnection); m_resourceResolver.addTask(task); if (!m_resourceResolverRunning) { QThreadPool::globalInstance()->start(&m_resourceResolver); m_resourceResolverRunning = true; } } void ResourceFolderModel::onUpdateSucceeded() { auto update_results = static_cast(m_current_update_task.get())->result(); auto& new_resources = update_results->resources; auto current_list = m_resources_index.keys(); QSet current_set(current_list.begin(), current_list.end()); auto new_list = new_resources.keys(); QSet new_set(new_list.begin(), new_list.end()); applyUpdates(current_set, new_set, new_resources); } void ResourceFolderModel::onParseSucceeded(int ticket, QString resource_id) { auto iter = m_active_parse_tasks.constFind(ticket); if (iter == m_active_parse_tasks.constEnd() || !m_resources_index.contains(resource_id)) return; int row = m_resources_index[resource_id]; emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1)); } Task* ResourceFolderModel::createUpdateTask() { auto index_dir = indexDir(); auto task = new ResourceFolderLoadTask(dir(), index_dir, m_is_indexed, m_first_folder_load, [this](const QFileInfo& file) { return createResource(file); }); m_first_folder_load = false; return task; } bool ResourceFolderModel::hasPendingParseTasks() const { return !m_active_parse_tasks.isEmpty(); } void ResourceFolderModel::directoryChanged(QString path) { update(); } Qt::DropActions ResourceFolderModel::supportedDropActions() const { // copy from outside, move from within and other resource lists return Qt::CopyAction | Qt::MoveAction; } Qt::ItemFlags ResourceFolderModel::flags(const QModelIndex& index) const { Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); auto flags = defaultFlags | Qt::ItemIsDropEnabled; if (index.isValid()) flags |= Qt::ItemIsUserCheckable; return flags; } QStringList ResourceFolderModel::mimeTypes() const { QStringList types; types << "text/uri-list"; return types; } bool ResourceFolderModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int, int, const QModelIndex&) { if (action == Qt::IgnoreAction) { return true; } // check if the action is supported if (!data || !(action & supportedDropActions())) { return false; } // files dropped from outside? if (data->hasUrls()) { auto urls = data->urls(); for (auto url : urls) { // only local files may be dropped... if (!url.isLocalFile()) { continue; } // TODO: implement not only copy, but also move // FIXME: handle errors here installResource(url.toLocalFile()); } return true; } return false; } bool ResourceFolderModel::validateIndex(const QModelIndex& index) const { if (!index.isValid()) return false; int row = index.row(); if (row < 0 || row >= m_resources.size()) return false; return true; } // HACK: all subclasses need to call this to have the whole row painted // and they only delegate to the superclass for compatible columns QBrush ResourceFolderModel::rowBackground(int row) const { if (APPLICATION->settings()->get("ShowModIncompat").toBool() && m_resources[row]->hasIssues()) { return { QColor(255, 0, 0, 40) }; } else { return {}; } } QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const { if (!validateIndex(index)) return {}; int row = index.row(); int column = index.column(); switch (role) { case Qt::BackgroundRole: return rowBackground(row); case Qt::DisplayRole: switch (column) { case NameColumn: return m_resources[row]->name(); case DateColumn: return m_resources[row]->dateTimeChanged(); case ProviderColumn: return m_resources[row]->provider(); case SizeColumn: return m_resources[row]->sizeStr(); default: return {}; } case Qt::ToolTipRole: { QString tooltip = m_resources[row]->internal_id(); if (column == NameColumn) { if (APPLICATION->settings()->get("ShowModIncompat").toBool()) { for (const QString& issue : at(row).issues()) { tooltip += "\n" + issue; } } if (at(row).isSymLinkUnder(instDirPath())) { tooltip += m_resources[row]->internal_id() + tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." "\nCanonical Path: %1") .arg(at(row).fileinfo().canonicalFilePath()); } if (at(row).isMoreThanOneHardLink()) { tooltip += tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original."); } } return tooltip; } case Qt::DecorationRole: { if (column == NameColumn) { if (APPLICATION->settings()->get("ShowModIncompat").toBool() && at(row).hasIssues()) { return QIcon::fromTheme("status-bad"); } else if (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink()) { return QIcon::fromTheme("status-yellow"); } } return {}; } case Qt::CheckStateRole: if (column == ActiveColumn) return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked; return {}; default: return {}; } } bool ResourceFolderModel::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role) { int row = index.row(); if (row < 0 || row >= rowCount(index.parent()) || !index.isValid()) return false; if (role == Qt::CheckStateRole) { return setResourceEnabled({ index }, EnableAction::TOGGLE); } return false; } QVariant ResourceFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { switch (role) { case Qt::DisplayRole: switch (section) { case ActiveColumn: case NameColumn: case DateColumn: case ProviderColumn: case SizeColumn: return columnNames().at(section); default: return {}; } case Qt::ToolTipRole: { //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. switch (section) { case ActiveColumn: return tr("Is the resource enabled?"); case NameColumn: return tr("The name of the resource."); case DateColumn: return tr("The date and time this resource was last changed (or added)."); case ProviderColumn: return tr("The source provider of the resource."); case SizeColumn: return tr("The size of the resource."); default: return {}; } } default: break; } return {}; } void ResourceFolderModel::setupHeaderAction(QAction* act, int column) { Q_ASSERT(act); act->setText(columnNames().at(column)); } void ResourceFolderModel::saveColumns(QTreeView* tree) { auto const stateSettingName = QString("UI/%1_Page/Columns").arg(id()); auto const overrideSettingName = QString("UI/%1_Page/ColumnsOverride").arg(id()); auto const visibilitySettingName = QString("UI/%1_Page/ColumnsVisibility").arg(id()); auto stateSetting = m_instance->settings()->getSetting(stateSettingName); stateSetting->set(QString::fromUtf8(tree->header()->saveState().toBase64())); // neither passthrough nor override settings works for this usecase as I need to only set the global when the gate is false auto settings = m_instance->settings(); if (!settings->get(overrideSettingName).toBool()) { settings = APPLICATION->settings(); } auto visibility = Json::toMap(settings->get(visibilitySettingName).toString()); for (auto i = 0; i < m_column_names.size(); ++i) { if (m_columnsHideable[i]) { auto name = m_column_names[i]; visibility[name] = !tree->isColumnHidden(i); } } settings->set(visibilitySettingName, Json::fromMap(visibility)); } void ResourceFolderModel::loadColumns(QTreeView* tree) { auto const stateSettingName = QString("UI/%1_Page/Columns").arg(id()); auto const overrideSettingName = QString("UI/%1_Page/ColumnsOverride").arg(id()); auto const visibilitySettingName = QString("UI/%1_Page/ColumnsVisibility").arg(id()); auto stateSetting = m_instance->settings()->getOrRegisterSetting(stateSettingName, ""); tree->header()->restoreState(QByteArray::fromBase64(stateSetting->get().toString().toUtf8())); auto setVisible = [this, tree](QVariant value) { auto visibility = Json::toMap(value.toString()); for (auto i = 0; i < m_column_names.size(); ++i) { if (m_columnsHideable[i]) { auto name = m_column_names[i]; tree->setColumnHidden(i, !visibility.value(name, false).toBool()); } } }; auto const defaultValue = Json::fromMap({ { "Image", true }, { "Version", true }, { "Last Modified", true }, { "Provider", true }, { "Pack Format", true }, }); // neither passthrough nor override settings works for this usecase as I need to only set the global when the gate is false auto settings = m_instance->settings(); if (!settings->getOrRegisterSetting(overrideSettingName, false)->get().toBool()) { settings = APPLICATION->settings(); } auto visibility = settings->getOrRegisterSetting(visibilitySettingName, defaultValue); setVisible(visibility->get()); // allways connect the signal in case the setting is toggled on and off auto gSetting = APPLICATION->settings()->getOrRegisterSetting(visibilitySettingName, defaultValue); connect(gSetting.get(), &Setting::SettingChanged, tree, [this, setVisible, overrideSettingName](const Setting&, QVariant value) { if (!m_instance->settings()->get(overrideSettingName).toBool()) { setVisible(value); } }); } QMenu* ResourceFolderModel::createHeaderContextMenu(QTreeView* tree) { auto menu = new QMenu(tree); { // action to decide if the visibility is per instance or not auto act = new QAction(tr("Override Columns Visibility"), menu); auto const overrideSettingName = QString("UI/%1_Page/ColumnsOverride").arg(id()); act->setCheckable(true); act->setChecked(m_instance->settings()->getOrRegisterSetting(overrideSettingName, false)->get().toBool()); connect(act, &QAction::toggled, tree, [this, tree, overrideSettingName](bool toggled) { m_instance->settings()->set(overrideSettingName, toggled); saveColumns(tree); }); menu->addAction(act); } menu->addSeparator()->setText(tr("Show / Hide Columns")); for (int col = 0; col < columnCount(); ++col) { // Skip creating actions for columns that should not be hidden if (!m_columnsHideable.at(col)) continue; auto act = new QAction(menu); setupHeaderAction(act, col); act->setCheckable(true); act->setChecked(!tree->isColumnHidden(col)); connect(act, &QAction::toggled, tree, [this, col, tree](bool toggled) { tree->setColumnHidden(col, !toggled); for (int c = 0; c < columnCount(); ++c) { if (m_column_resize_modes.at(c) == QHeaderView::ResizeToContents) tree->resizeColumnToContents(c); } saveColumns(tree); }); menu->addAction(act); } return menu; } QSortFilterProxyModel* ResourceFolderModel::createFilterProxyModel(QObject* parent) { return new ProxyModel(parent); } SortType ResourceFolderModel::columnToSortKey(size_t column) const { Q_ASSERT(m_column_sort_keys.size() == columnCount()); return m_column_sort_keys.at(column); } /* Standard Proxy Model for createFilterProxyModel */ bool ResourceFolderModel::ProxyModel::filterAcceptsRow(int source_row, [[maybe_unused]] const QModelIndex& source_parent) const { auto* model = qobject_cast(sourceModel()); if (!model) return true; const auto& resource = model->at(source_row); return resource.applyFilter(filterRegularExpression()); } bool ResourceFolderModel::ProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const { auto* model = qobject_cast(sourceModel()); if (!model || !source_left.isValid() || !source_right.isValid() || source_left.column() != source_right.column()) { return QSortFilterProxyModel::lessThan(source_left, source_right); } // we are now guaranteed to have two valid indexes in the same column... we love the provided invariants unconditionally and // proceed. auto column_sort_key = model->columnToSortKey(source_left.column()); auto const& resource_left = model->at(source_left.row()); auto const& resource_right = model->at(source_right.row()); auto compare_result = resource_left.compare(resource_right, column_sort_key); if (compare_result == 0) return QSortFilterProxyModel::lessThan(source_left, source_right); return compare_result < 0; } QString ResourceFolderModel::instDirPath() const { return QFileInfo(m_instance->instanceRoot()).absoluteFilePath(); } void ResourceFolderModel::onParseFailed(int ticket, QString resource_id) { auto iter = m_active_parse_tasks.constFind(ticket); if (iter == m_active_parse_tasks.constEnd() || !m_resources_index.contains(resource_id)) return; auto removed_index = m_resources_index[resource_id]; auto removed_it = m_resources.begin() + removed_index; Q_ASSERT(removed_it != m_resources.end()); beginRemoveRows(QModelIndex(), removed_index, removed_index); m_resources.erase(removed_it); // update index m_resources_index.clear(); int idx = 0; for (auto const& mod : qAsConst(m_resources)) { m_resources_index[mod->internal_id()] = idx; idx++; } endRemoveRows(); } void ResourceFolderModel::applyUpdates(QSet& current_set, QSet& new_set, QMap& new_resources) { // see if the kept resources changed in some way { QSet kept_set = current_set; kept_set.intersect(new_set); for (auto const& kept : kept_set) { auto row_it = m_resources_index.constFind(kept); Q_ASSERT(row_it != m_resources_index.constEnd()); auto row = row_it.value(); auto& new_resource = new_resources[kept]; auto const& current_resource = m_resources.at(row); if (new_resource->dateTimeChanged() == current_resource->dateTimeChanged()) { // no significant change bool hadIssues = !current_resource->hasIssues(); current_resource->updateIssues(m_instance); if (hadIssues != current_resource->hasIssues()) { emit dataChanged(index(row, 0), index(row, columnCount({}) - 1)); } continue; } // If the resource is resolving, but something about it changed, we don't want to // continue the resolving. if (current_resource->isResolving()) { auto ticket = current_resource->resolutionTicket(); if (m_active_parse_tasks.contains(ticket)) { auto task = (*m_active_parse_tasks.find(ticket)).get(); task->abort(); } } m_resources[row].reset(new_resource); new_resource->updateIssues(m_instance); resolveResource(m_resources.at(row)); emit dataChanged(index(row, 0), index(row, columnCount(QModelIndex()) - 1)); } } // remove resources no longer present { QSet removed_set = current_set; removed_set.subtract(new_set); QList removed_rows; for (auto& removed : removed_set) removed_rows.append(m_resources_index[removed]); std::sort(removed_rows.begin(), removed_rows.end(), std::greater()); for (auto& removed_index : removed_rows) { auto removed_it = m_resources.begin() + removed_index; Q_ASSERT(removed_it != m_resources.end()); if ((*removed_it)->isResolving()) { auto ticket = (*removed_it)->resolutionTicket(); if (m_active_parse_tasks.contains(ticket)) { auto task = (*m_active_parse_tasks.find(ticket)).get(); task->abort(); } } beginRemoveRows(QModelIndex(), removed_index, removed_index); m_resources.erase(removed_it); endRemoveRows(); } } // add new resources to the end { QSet added_set = new_set; added_set.subtract(current_set); // When you have a Qt build with assertions turned on, proceeding here will abort the application if (added_set.size() > 0) { beginInsertRows(QModelIndex(), static_cast(m_resources.size()), static_cast(m_resources.size() + added_set.size() - 1)); for (auto& added : added_set) { auto res = new_resources[added]; res->updateIssues(m_instance); m_resources.append(res); resolveResource(m_resources.last()); } endInsertRows(); } } // update index { m_resources_index.clear(); int idx = 0; for (auto const& mod : qAsConst(m_resources)) { m_resources_index[mod->internal_id()] = idx; idx++; } } } Resource::Ptr ResourceFolderModel::find(QString id) { auto iter = std::find_if(m_resources.constBegin(), m_resources.constEnd(), [&](Resource::Ptr const& r) { return r->internal_id() == id; }); if (iter == m_resources.constEnd()) return nullptr; return *iter; } QList ResourceFolderModel::allResources() { QList result; result.reserve(m_resources.size()); for (const Resource ::Ptr& resource : m_resources) result.append((resource.get())); return result; } QList ResourceFolderModel::selectedResources(const QModelIndexList& indexes) { QList result; for (const QModelIndex& index : indexes) { if (index.column() != 0) continue; result.append(&at(index.row())); } return result; } PrismLauncher-11.0.3/launcher/minecraft/mod/DataPackFolderModel.cpp0000644000175100017510000001445415224505336024614 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "DataPackFolderModel.h" #include #include #include "Version.h" #include "minecraft/mod/tasks/LocalDataPackParseTask.h" DataPackFolderModel::DataPackFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) : ResourceFolderModel(QDir(dir), instance, is_indexed, create_dir, parent) { m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true }; } QVariant DataPackFolderModel::data(const QModelIndex& index, int role) const { if (!validateIndex(index)) return {}; int row = index.row(); int column = index.column(); switch (role) { case Qt::BackgroundRole: return rowBackground(row); case Qt::DisplayRole: switch (column) { case PackFormatColumn: { const auto& resource = at(row); return resource.packFormatStr(); } } break; case Qt::DecorationRole: { if (column == ImageColumn) { return at(row).image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); } break; } case Qt::ToolTipRole: { if (column == PackFormatColumn) { //: The string being explained by this is in the format: ID (Lower version - Upper version) return tr("The data pack format ID, as well as the Minecraft versions it was designed for."); } break; } case Qt::SizeHintRole: if (column == ImageColumn) { return QSize(32, 32); } break; } // map the columns to the base equivilents QModelIndex mappedIndex; switch (column) { case ActiveColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::ActiveColumn); break; case NameColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::NameColumn); break; case DateColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::DateColumn); break; case ProviderColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::ProviderColumn); break; // FIXME: there is no size column due to an oversight } if (mappedIndex.isValid()) { return ResourceFolderModel::data(mappedIndex, role); } return {}; } QVariant DataPackFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { switch (role) { case Qt::DisplayRole: switch (section) { case ActiveColumn: case NameColumn: case PackFormatColumn: case DateColumn: case ImageColumn: return columnNames().at(section); default: return {}; } case Qt::ToolTipRole: switch (section) { case ActiveColumn: return tr("Is the data pack enabled? (Only valid for ZIPs)"); case NameColumn: return tr("The name of the data pack."); case PackFormatColumn: //: The string being explained by this is in the format: ID (Lower version - Upper version) return tr("The data pack format ID, as well as the Minecraft versions it was designed for."); case DateColumn: return tr("The date and time this data pack was last changed (or added)."); default: return {}; } case Qt::SizeHintRole: if (section == ImageColumn) { return QSize(64, 0); } return {}; default: return {}; } } int DataPackFolderModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; } Resource* DataPackFolderModel::createResource(const QFileInfo& file) { return new DataPack(file); } Task* DataPackFolderModel::createParseTask(Resource& resource) { return new LocalDataPackParseTask(m_next_resolution_ticket, static_cast(&resource)); } PrismLauncher-11.0.3/launcher/minecraft/mod/ShaderPack.cpp0000644000175100017510000000211615224505336023024 0ustar runnerrunner // SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ShaderPack.h" void ShaderPack::setPackFormat(ShaderPackFormat new_format) { QMutexLocker locker(&m_data_lock); m_pack_format = new_format; } bool ShaderPack::valid() const { return m_pack_format != ShaderPackFormat::INVALID; } PrismLauncher-11.0.3/launcher/minecraft/mod/ResourcePack.cpp0000644000175100017510000001314715224505336023413 0ustar runnerrunner#include "ResourcePack.h" #include #include #include #include #include "MTPixmapCache.h" #include "Version.h" // Values taken from: // https://minecraft.wiki/w/Pack_format#List_of_resource_pack_formats static const QMap, std::pair> s_pack_format_versions = { { { 1, 0 }, { Version("1.6.1"), Version("1.8.9") } }, { { 2, 0 }, { Version("1.9"), Version("1.10.2") } }, { { 3, 0 }, { Version("1.11"), Version("1.12.2") } }, { { 4, 0 }, { Version("1.13"), Version("1.14.4") } }, { { 5, 0 }, { Version("1.15"), Version("1.16.1") } }, { { 6, 0 }, { Version("1.16.2"), Version("1.16.5") } }, { { 7, 0 }, { Version("1.17"), Version("1.17.1") } }, { { 8, 0 }, { Version("1.18"), Version("1.18.2") } }, { { 9, 0 }, { Version("1.19"), Version("1.19.2") } }, { { 11, 0 }, { Version("22w42a"), Version("22w44a") } }, { { 12, 0 }, { Version("1.19.3"), Version("1.19.3") } }, { { 13, 0 }, { Version("1.19.4"), Version("1.19.4") } }, { { 14, 0 }, { Version("23w14a"), Version("23w16a") } }, { { 15, 0 }, { Version("1.20"), Version("1.20.1") } }, { { 16, 0 }, { Version("23w31a"), Version("23w31a") } }, { { 17, 0 }, { Version("23w32a"), Version("1.20.2-pre1") } }, { { 18, 0 }, { Version("1.20.2"), Version("1.20.2") } }, { { 19, 0 }, { Version("23w42a"), Version("23w42a") } }, { { 20, 0 }, { Version("23w43a"), Version("23w44a") } }, { { 21, 0 }, { Version("23w45a"), Version("23w46a") } }, { { 22, 0 }, { Version("1.20.3"), Version("1.20.4") } }, { { 24, 0 }, { Version("24w03a"), Version("24w04a") } }, { { 25, 0 }, { Version("24w05a"), Version("24w05b") } }, { { 26, 0 }, { Version("24w06a"), Version("24w07a") } }, { { 28, 0 }, { Version("24w09a"), Version("24w10a") } }, { { 29, 0 }, { Version("24w11a"), Version("24w11a") } }, { { 30, 0 }, { Version("24w12a"), Version("24w12a") } }, { { 31, 0 }, { Version("24w13a"), Version("1.20.5-pre3") } }, { { 32, 0 }, { Version("1.20.5"), Version("1.20.6") } }, { { 33, 0 }, { Version("24w18a"), Version("24w20a") } }, { { 34, 0 }, { Version("1.21"), Version("1.21.1") } }, { { 35, 0 }, { Version("24w33a"), Version("24w33a") } }, { { 36, 0 }, { Version("24w34a"), Version("24w35a") } }, { { 37, 0 }, { Version("24w36a"), Version("24w36a") } }, { { 38, 0 }, { Version("24w37a"), Version("24w37a") } }, { { 39, 0 }, { Version("24w38a"), Version("24w39a") } }, { { 40, 0 }, { Version("24w40a"), Version("24w40a") } }, { { 41, 0 }, { Version("1.21.2-pre1"), Version("1.21.2-pre2") } }, { { 42, 0 }, { Version("1.21.2"), Version("1.21.3") } }, { { 43, 0 }, { Version("24w44a"), Version("24w44a") } }, { { 44, 0 }, { Version("24w45a"), Version("24w45a") } }, { { 45, 0 }, { Version("24w46a"), Version("24w46a") } }, { { 46, 0 }, { Version("1.21.4"), Version("1.21.4") } }, { { 47, 0 }, { Version("25w02a"), Version("25w02a") } }, { { 48, 0 }, { Version("25w03a"), Version("25w03a") } }, { { 49, 0 }, { Version("25w04a"), Version("25w04a") } }, { { 50, 0 }, { Version("25w05a"), Version("25w05a") } }, { { 51, 0 }, { Version("25w06a"), Version("25w06a") } }, { { 52, 0 }, { Version("25w07a"), Version("25w07a") } }, { { 53, 0 }, { Version("25w08a"), Version("25w09b") } }, { { 54, 0 }, { Version("25w10a"), Version("25w10a") } }, { { 55, 0 }, { Version("1.21.5"), Version("1.21.5") } }, { { 56, 0 }, { Version("25w15a"), Version("25w15a") } }, { { 57, 0 }, { Version("25w16a"), Version("25w16a") } }, { { 58, 0 }, { Version("25w17a"), Version("25w17a") } }, { { 59, 0 }, { Version("25w18a"), Version("25w18a") } }, { { 60, 0 }, { Version("25w19a"), Version("25w19a") } }, { { 61, 0 }, { Version("25w20a"), Version("25w20a") } }, { { 62, 0 }, { Version("25w21a"), Version("25w21a") } }, { { 63, 0 }, { Version("1.21.6"), Version("1.21.6") } }, { { 64, 0 }, { Version("1.21.7"), Version("1.21.8") } }, { { 65, 0 }, { Version("25w31a"), Version("25w31a") } }, { { 65, 1 }, { Version("25w32a"), Version("25w32a") } }, { { 65, 2 }, { Version("25w33a"), Version("25w33a") } }, { { 66, 0 }, { Version("25w34a"), Version("25w34b") } }, { { 67, 0 }, { Version("25w35a"), Version("25w35a") } }, { { 68, 0 }, { Version("25w36a"), Version("25w36b") } }, { { 69, 0 }, { Version("1.21.9"), Version("1.21.10") } }, { { 70, 0 }, { Version("25w41a"), Version("25w41a") } }, { { 70, 1 }, { Version("25w42a"), Version("25w42a") } }, { { 71, 0 }, { Version("25w43a"), Version("25w43a") } }, { { 72, 0 }, { Version("25w44a"), Version("25w44a") } }, { { 73, 0 }, { Version("25w45a"), Version("25w45a") } }, { { 74, 0 }, { Version("25w46a"), Version("25w46a") } }, { { 75, 0 }, { Version("1.21.11"), Version("1.21.11") } }, { { 76, 0 }, { Version("26.1-snap1"), Version("26.1-snap1") } }, { { 77, 0 }, { Version("26.1-snap2"), Version("26.1-snap2") } }, { { 78, 0 }, { Version("26.1-snap3"), Version("26.1-snap3") } }, { { 78, 1 }, { Version("26.1-snap4"), Version("26.1-snap4") } }, { { 79, 0 }, { Version("26.1-snap5"), Version("26.1-snap5") } }, { { 80, 0 }, { Version("26.1-snap6"), Version("26.1-snap6") } }, { { 81, 0 }, { Version("26.1-snap7"), Version("26.1-snap7") } }, { { 81, 1 }, { Version("26.1-snap8"), Version("26.1-snap9") } }, { { 82, 0 }, { Version("26.1-snap10"), Version("26.1-snap10") } }, { { 83, 0 }, { Version("26.1-snap11"), Version("26.1-snap11") } }, }; QMap, std::pair> ResourcePack::mappings() const { return s_pack_format_versions; } PrismLauncher-11.0.3/launcher/minecraft/mod/ShaderPackFolderModel.cpp0000644000175100017510000000303415224505336025141 0ustar runnerrunner#include "ShaderPackFolderModel.h" #include "FileSystem.h" namespace { class ShaderPackIndexMigrateTask : public Task { Q_OBJECT public: ShaderPackIndexMigrateTask(QDir resourceDir, QDir indexDir) : m_resourceDir(std::move(resourceDir)), m_indexDir(std::move(indexDir)) {} void executeTask() override { if (!m_indexDir.exists()) { qDebug() << m_indexDir.absolutePath() << "does not exist; nothing to migrate"; emitSucceeded(); return; } QStringList pwFiles = m_indexDir.entryList({ "*.pw.toml" }, QDir::Files); bool movedAll = true; for (const auto& file : pwFiles) { QString src = m_indexDir.filePath(file); QString dest = m_resourceDir.filePath(file); if (FS::move(src, dest)) { qDebug() << "Moved" << src << "to" << dest; } else { movedAll = false; } } if (!movedAll) { // FIXME: not shown in the UI emitFailed(tr("Failed to migrate shaderpack metadata from .index")); return; } if (!FS::deletePath(m_indexDir.absolutePath())) { emitFailed(tr("Failed to remove old .index dir")); return; } emitSucceeded(); } private: QDir m_resourceDir, m_indexDir; }; } // namespace Task* ShaderPackFolderModel::createPreUpdateTask() { return new ShaderPackIndexMigrateTask(m_dir, ResourceFolderModel::indexDir()); } #include "ShaderPackFolderModel.moc" PrismLauncher-11.0.3/launcher/minecraft/mod/Resource.cpp0000644000175100017510000002131415224505336022607 0ustar runnerrunner#include "Resource.h" #include #include #include #include #include "FileSystem.h" #include "StringUtils.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" Resource::Resource(QObject* parent) : QObject(parent) {} Resource::Resource(QFileInfo file_info) : QObject() { setFile(file_info); } void Resource::setFile(QFileInfo file_info) { m_file_info = file_info; parseFile(); } static std::tuple calculateFileSize(const QFileInfo& file) { if (file.isDir()) { auto dir = QDir(file.absoluteFilePath()); dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); auto count = dir.count(); auto str = QObject::tr("item"); if (count != 1) str = QObject::tr("items"); return { QString("%1 %2").arg(QString::number(count), str), count }; } return { StringUtils::humanReadableFileSize(file.size(), true), file.size() }; } void Resource::parseFile() { QString file_name{ m_file_info.fileName() }; m_type = ResourceType::UNKNOWN; m_internal_id = file_name; std::tie(m_size_str, m_size_info) = calculateFileSize(m_file_info); if (m_file_info.isDir()) { m_type = ResourceType::FOLDER; m_name = file_name; } else if (m_file_info.isFile()) { if (file_name.endsWith(".disabled")) { file_name.chop(9); m_enabled = false; } if (file_name.endsWith(".zip") || file_name.endsWith(".jar")) { m_type = ResourceType::ZIPFILE; file_name.chop(4); } else if (file_name.endsWith(".nilmod")) { m_type = ResourceType::ZIPFILE; file_name.chop(7); } else if (file_name.endsWith(".litemod")) { m_type = ResourceType::LITEMOD; file_name.chop(8); } else { m_type = ResourceType::SINGLEFILE; } m_name = file_name; } m_changed_date_time = m_file_info.lastModified(); } auto Resource::name() const -> QString { if (metadata()) return metadata()->name; return m_name; } static void removeThePrefix(QString& string) { static const QRegularExpression s_regex(QStringLiteral("^(?:the|teh) +"), QRegularExpression::CaseInsensitiveOption); string.remove(s_regex); string = string.trimmed(); } auto Resource::provider() const -> QString { if (metadata()) return ModPlatform::ProviderCapabilities::readableName(metadata()->provider); return tr("Unknown"); } auto Resource::homepage() const -> QString { if (metadata()) return ModPlatform::getMetaURL(metadata()->provider, metadata()->project_id); return {}; } void Resource::setMetadata(std::shared_ptr&& metadata) { if (status() == ResourceStatus::NO_METADATA) setStatus(ResourceStatus::INSTALLED); m_metadata = metadata; } QStringList Resource::issues() const { QStringList result; result.reserve(m_issues.length()); for (const char* issue : m_issues) { result.append(tr(issue)); } return result; } void Resource::updateIssues(const BaseInstance* inst) { m_issues.clear(); if (m_metadata == nullptr) { return; } auto mcInst = dynamic_cast(inst); if (mcInst == nullptr) { return; } auto profile = mcInst->getPackProfile(); QString mcVersion = profile->getComponentVersion("net.minecraft"); if (!m_metadata->mcVersions.empty() && !m_metadata->mcVersions.contains(mcVersion)) { // delay translation until issues() is called m_issues.append(QT_TR_NOOP("Not marked as compatible with the instance's game version.")); } } int Resource::compare(const Resource& other, SortType type) const { switch (type) { default: case SortType::ENABLED: if (enabled() && !other.enabled()) return 1; if (!enabled() && other.enabled()) return -1; break; case SortType::NAME: { QString this_name{ name() }; QString other_name{ other.name() }; // TODO do we need this? it could result in 0 being returned removeThePrefix(this_name); removeThePrefix(other_name); return QString::compare(this_name, other_name, Qt::CaseInsensitive); } case SortType::DATE: if (dateTimeChanged() > other.dateTimeChanged()) return 1; if (dateTimeChanged() < other.dateTimeChanged()) return -1; break; case SortType::SIZE: { if (this->type() != other.type()) { if (this->type() == ResourceType::FOLDER) return -1; if (other.type() == ResourceType::FOLDER) return 1; } if (sizeInfo() > other.sizeInfo()) return 1; if (sizeInfo() < other.sizeInfo()) return -1; break; } case SortType::PROVIDER: { auto compare_result = QString::compare(provider(), other.provider(), Qt::CaseInsensitive); if (compare_result != 0) return compare_result; break; } } return 0; } bool Resource::applyFilter(QRegularExpression filter) const { return filter.match(name()).hasMatch(); } bool Resource::enable(EnableAction action) { if (m_type == ResourceType::UNKNOWN || m_type == ResourceType::FOLDER) return false; QString path = m_file_info.absoluteFilePath(); QFile file(path); bool enable = true; switch (action) { case EnableAction::ENABLE: enable = true; break; case EnableAction::DISABLE: enable = false; break; case EnableAction::TOGGLE: default: enable = !enabled(); break; } if (m_enabled == enable) return false; if (enable) { // m_enabled is false, but there's no '.disabled' suffix. // TODO: Report error? if (!path.endsWith(".disabled")) return false; path.chop(9); } else { path += ".disabled"; if (QFile::exists(path)) { path = FS::getUniqueResourceName(path); } } if (!file.rename(path)) return false; setFile(QFileInfo(path)); m_enabled = enable; return true; } auto Resource::destroy(const QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool { m_type = ResourceType::UNKNOWN; if (!preserve_metadata) { qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name()); destroyMetadata(index_dir); } return (attempt_trash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath()); } auto Resource::destroyMetadata(const QDir& index_dir) -> void { if (metadata()) { Metadata::remove(index_dir, metadata()->slug); } else { auto n = name(); Metadata::remove(index_dir, n); } m_metadata = nullptr; } bool Resource::isSymLinkUnder(const QString& instPath) const { if (isSymLink()) return true; auto instDir = QDir(instPath); auto relAbsPath = instDir.relativeFilePath(m_file_info.absoluteFilePath()); auto relCanonPath = instDir.relativeFilePath(m_file_info.canonicalFilePath()); return relAbsPath != relCanonPath; } bool Resource::isMoreThanOneHardLink() const { return FS::hardLinkCount(m_file_info.absoluteFilePath()) > 1; } auto Resource::getOriginalFileName() const -> QString { auto fileName = m_file_info.fileName(); if (!m_enabled) fileName.chop(9); return fileName; } QDebug operator<<(QDebug debug, ResourceType type) { switch (type) { case ResourceType::ZIPFILE: debug << "ZIPFILE"; break; case ResourceType::SINGLEFILE: debug << "SINGLEFILE"; break; case ResourceType::FOLDER: debug << "FOLDER"; break; case ResourceType::LITEMOD: debug << "LITEMOD"; break; case ResourceType::UNKNOWN: default: debug << "UNKNOWN"; break; }; return debug; } QDebug operator<<(QDebug debug, ResourceStatus status) { switch (status) { case ResourceStatus::INSTALLED: debug << "INSTALLED"; break; case ResourceStatus::NOT_INSTALLED: debug << "NOT_INSTALLED"; break; case ResourceStatus::NO_METADATA: debug << "NO_METADATA"; break; case ResourceStatus::UNKNOWN: default: debug << "UNKNOWN"; break; }; return debug; } PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/0000755000175100017510000000000015224505336021440 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalResourceUpdateTask.h0000644000175100017510000000266215224505336026347 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include "modplatform/ModIndex.h" #include "tasks/Task.h" class LocalResourceUpdateTask : public Task { Q_OBJECT public: using Ptr = shared_qobject_ptr; explicit LocalResourceUpdateTask(QDir index_dir, ModPlatform::IndexedPack& project, ModPlatform::IndexedVersion& version); auto canAbort() const -> bool override { return true; } auto abort() -> bool override; protected slots: //! Entry point for tasks. void executeTask() override; signals: void hasOldResource(QString name, QString filename); private: QDir m_index_dir; ModPlatform::IndexedPack m_project; ModPlatform::IndexedVersion m_version; }; PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalDataPackParseTask.h0000644000175100017510000000410715224505336026054 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include "minecraft/mod/DataPack.h" #include "tasks/Task.h" namespace DataPackUtils { enum class ProcessingLevel { Full, BasicInfoOnly }; bool process(DataPack* pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(DataPack* pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(DataPack* pack, ProcessingLevel level = ProcessingLevel::Full); bool processMCMeta(DataPack* pack, QByteArray&& raw_data); QString processComponent(const QJsonValue& value, bool strikethrough = false, bool underline = false); bool processPackPNG(const DataPack* pack, QByteArray&& raw_data); /// processes ONLY the pack.png (rest of the pack may be invalid) bool processPackPNG(const DataPack* pack); /** Checks whether a file is valid as a data pack or not. */ bool validate(QFileInfo file); /** Checks whether a file is valid as a resource pack or not. */ bool validateResourcePack(QFileInfo file); } // namespace DataPackUtils class LocalDataPackParseTask : public Task { Q_OBJECT public: LocalDataPackParseTask(int token, DataPack* dp); void executeTask() override; int token() const { return m_token; } private: int m_token; DataPack* m_data_pack; }; PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/GetModDependenciesTask.h0000644000175100017510000000651015224505336026124 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include #include #include #include "minecraft/mod/MetadataHandler.h" #include "minecraft/mod/ModFolderModel.h" #include "modplatform/ModIndex.h" #include "modplatform/ResourceAPI.h" #include "modplatform/flame/FlameAPI.h" #include "modplatform/modrinth/ModrinthAPI.h" #include "tasks/SequentialTask.h" #include "tasks/Task.h" #include "ui/pages/modplatform/ModModel.h" class GetModDependenciesTask : public SequentialTask { Q_OBJECT public: using Ptr = shared_qobject_ptr; struct PackDependency { ModPlatform::Dependency dependency; ModPlatform::IndexedPack::Ptr pack; ModPlatform::IndexedVersion version; PackDependency() = default; PackDependency(ModPlatform::IndexedPack::Ptr p, ModPlatform::IndexedVersion v) : pack(std::move(p)), version(std::move(v)) {} }; struct PackDependencyExtraInfo { bool maybe_installed{}; QStringList required_by; }; explicit GetModDependenciesTask(BaseInstance* instance, ModFolderModel* folder, QList> selected); auto getDependecies() const -> QList> { return m_pack_dependencies; } QHash getExtraInfo(); private: ResourceAPI* getAPI(ModPlatform::ResourceProvider provider) { if (provider == ModPlatform::ResourceProvider::FLAME) { return &m_flameAPI; } return &m_modrinthAPI; } protected slots: Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, ModPlatform::ResourceProvider, int); QList getDependenciesForVersion(const ModPlatform::IndexedVersion&, ModPlatform::ResourceProvider providerName); void prepare(); Task::Ptr getProjectInfoTask(std::shared_ptr pDep); ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, ModPlatform::ResourceProvider providerName); void removePack(const QVariant& addonId); bool isLocalyInstalled(std::shared_ptr pDep); bool maybeInstalled(std::shared_ptr pDep); private: QList> m_pack_dependencies; QList> m_mods; QList> m_selected; QStringList m_mods_file_names; Version m_version; ModPlatform::ModLoaderTypes m_loaderType; ModrinthAPI m_modrinthAPI; FlameAPI m_flameAPI; }; PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalShaderPackParseTask.h0000644000175100017510000000341115224505336026406 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include "minecraft/mod/ShaderPack.h" #include "tasks/Task.h" namespace ShaderPackUtils { enum class ProcessingLevel { Full, BasicInfoOnly }; bool process(ShaderPack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(ShaderPack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(ShaderPack& pack, ProcessingLevel level = ProcessingLevel::Full); /** Checks whether a file is valid as a shader pack or not. */ bool validate(QFileInfo file); } // namespace ShaderPackUtils class LocalShaderPackParseTask : public Task { Q_OBJECT public: LocalShaderPackParseTask(int token, ShaderPack& sp); bool canAbort() const override { return true; } bool abort() override; void executeTask() override; int token() const { return m_token; } private: int m_token; ShaderPack& m_shader_pack; bool m_aborted = false; }; PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.h0000644000175100017510000000535415224505336026166 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include "minecraft/mod/Mod.h" #include "tasks/Task.h" class ResourceFolderLoadTask : public Task { Q_OBJECT public: struct Result { QMap resources; }; using ResultPtr = std::shared_ptr; ResultPtr result() const { return m_result; } public: ResourceFolderLoadTask(const QDir& resource_dir, const QDir& index_dir, bool is_indexed, bool clean_orphan, std::function create_function); bool canAbort() const override { return true; } bool abort() override { m_aborted.store(true); return true; } void executeTask() override; private: void getFromMetadata(); private: QDir m_resource_dir, m_index_dir; bool m_is_indexed; bool m_clean_orphan; std::function m_create_func; ResultPtr m_result; std::atomic m_aborted = false; /** This is the thread in which we should put new mod objects */ QThread* m_thread_to_spawn_into; }; PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalResourceUpdateTask.cpp0000644000175100017510000000504715224505336026702 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "LocalResourceUpdateTask.h" #include "FileSystem.h" #include "minecraft/mod/MetadataHandler.h" #ifdef Q_OS_WIN32 #include #endif LocalResourceUpdateTask::LocalResourceUpdateTask(QDir index_dir, ModPlatform::IndexedPack& project, ModPlatform::IndexedVersion& version) : m_index_dir(index_dir), m_project(project), m_version(version) { // Ensure a '.index' folder exists in the mods folder, and create it if it does not if (!FS::ensureFolderPathExists(index_dir.path())) { emitFailed(QString("Unable to create index directory at %1!").arg(index_dir.absolutePath())); return; } #ifdef Q_OS_WIN32 std::wstring wpath = index_dir.path().toStdWString(); if (index_dir.dirName().startsWith('.')) { SetFileAttributesW(wpath.c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); } else { // fix shaderpacks folder being hidden by Prism Launcher 10.0.1 SetFileAttributesW(wpath.c_str(), FILE_ATTRIBUTE_NORMAL); } #endif } void LocalResourceUpdateTask::executeTask() { setStatus(tr("Updating index for resource:\n%1").arg(m_project.name)); auto old_metadata = Metadata::get(m_index_dir, m_project.addonId); if (old_metadata.isValid()) { emit hasOldResource(old_metadata.name, old_metadata.filename); if (m_project.slug.isEmpty()) m_project.slug = old_metadata.slug; } auto pw_mod = Metadata::create(m_index_dir, m_project, m_version); if (pw_mod.isValid()) { Metadata::update(m_index_dir, pw_mod); emitSucceeded(); } else { qCritical() << "Tried to update an invalid resource!"; emitFailed(tr("Invalid metadata")); } } auto LocalResourceUpdateTask::abort() -> bool { emitAborted(); return true; } PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp0000644000175100017510000003614015224505336026461 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "GetModDependenciesTask.h" #include #include #include #include "Json.h" #include "QObjectPtr.h" #include "minecraft/PackProfile.h" #include "minecraft/mod/MetadataHandler.h" #include "modplatform/ModIndex.h" #include "modplatform/ResourceAPI.h" #include "tasks/SequentialTask.h" #include "ui/pages/modplatform/ModModel.h" static Version mcVersion(BaseInstance* inst) { return static_cast(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion(); } static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst) { return static_cast(inst)->getPackProfile()->getSupportedModLoaders().value(); } static bool checkDependencies(std::shared_ptr sel, Version mcVersion, ModPlatform::ModLoaderTypes loaders) { return (sel->pack->versions.isEmpty() || sel->version.mcVersion.contains(mcVersion.toString())) && (!loaders || !sel->version.loaders || sel->version.loaders & loaders); } GetModDependenciesTask::GetModDependenciesTask(BaseInstance* instance, ModFolderModel* folder, QList> selected) : SequentialTask(tr("Get dependencies")), m_selected(selected), m_version(mcVersion(instance)), m_loaderType(mcLoaders(instance)) { for (auto mod : folder->allMods()) { m_mods_file_names << mod->fileinfo().fileName(); if (auto meta = mod->metadata(); meta) m_mods.append(meta); } prepare(); } void GetModDependenciesTask::prepare() { for (auto sel : m_selected) { if (checkDependencies(sel, m_version, m_loaderType)) for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) { addTask(prepareDependencyTask(dep, sel->pack->provider, 20)); } } } ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep, const ModPlatform::ResourceProvider providerName) { if (auto isQuilt = (m_loaderType & ModPlatform::Quilt) != 0U; isQuilt || (m_loaderType & ModPlatform::Fabric) != 0U) { auto overide = ModPlatform::getOverrideDeps(); auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](const auto& o) { return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt); }); if (over != overide.cend()) { return { .addonId = isQuilt ? over->quilt : over->fabric, .type = dep.type, .version = "" }; } } return dep; } QList GetModDependenciesTask::getDependenciesForVersion(const ModPlatform::IndexedVersion& version, const ModPlatform::ResourceProvider providerName) { QList c_dependencies; for (auto ver_dep : version.dependencies) { if (ver_dep.type != ModPlatform::DependencyType::REQUIRED) { continue; } ver_dep = getOverride(ver_dep, providerName); auto isOnlyVersion = providerName == ModPlatform::ResourceProvider::MODRINTH && ver_dep.addonId.toString().isEmpty(); if (auto dep = std::find_if(c_dependencies.begin(), c_dependencies.end(), [&ver_dep, isOnlyVersion](const ModPlatform::Dependency& i) { return isOnlyVersion ? i.version == ver_dep.version : i.addonId == ver_dep.addonId; }); dep != c_dependencies.end()) { continue; // check the current dependency list } if (auto dep = std::find_if(m_selected.begin(), m_selected.end(), [&ver_dep, providerName, isOnlyVersion](const std::shared_ptr& i) { return i->pack->provider == providerName && (isOnlyVersion ? i->version.version == ver_dep.version : i->pack->addonId == ver_dep.addonId); }); dep != m_selected.end()) { continue; // check the selected versions } if (auto dep = std::find_if(m_mods.begin(), m_mods.end(), [&ver_dep, providerName, isOnlyVersion](const std::shared_ptr& i) { return i->provider == providerName && (isOnlyVersion ? i->file_id == ver_dep.version : i->project_id == ver_dep.addonId); }); dep != m_mods.end()) { continue; // check the existing mods } if (auto dep = std::find_if(m_pack_dependencies.begin(), m_pack_dependencies.end(), [&ver_dep, providerName, isOnlyVersion](const std::shared_ptr& i) { return i->pack->provider == providerName && (isOnlyVersion ? i->version.version == ver_dep.addonId : i->pack->addonId == ver_dep.addonId); }); dep != m_pack_dependencies.end()) { // check loaded dependencies continue; } c_dependencies.append(ver_dep); } return c_dependencies; } Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr pDep) { auto provider = pDep->pack->provider; auto [info, responseInfo] = getAPI(provider)->getProject(pDep->pack->addonId.toString()); connect(info.get(), &NetJob::succeeded, [this, responseInfo, provider, pDep] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error); if (parse_error.error != QJsonParseError::NoError) { removePack(pDep->pack->addonId); qWarning() << "Error while parsing JSON response for mod info at" << parse_error.offset << "reason:" << parse_error.errorString(); qDebug() << *responseInfo; return; } try { auto obj = provider == ModPlatform::ResourceProvider::FLAME ? Json::requireObject(Json::requireObject(doc), "data") : Json::requireObject(doc); getAPI(provider)->loadIndexedPack(*pDep->pack, obj); } catch (const JSONValidationError& e) { removePack(pDep->pack->addonId); qDebug() << doc; qWarning() << "Error while reading mod info:" << e.cause(); } }); return info; } Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Dependency& dep, const ModPlatform::ResourceProvider providerName, int level) { auto pDep = std::make_shared(); pDep->dependency = dep; pDep->pack = std::make_shared(); pDep->pack->addonId = dep.addonId; pDep->pack->provider = providerName; m_pack_dependencies.append(pDep); auto provider = providerName; auto tasks = makeShared( QString("DependencyInfo: %1").arg(dep.addonId.toString().isEmpty() ? dep.version : dep.addonId.toString())); if (!dep.addonId.toString().isEmpty()) { tasks->addTask(getProjectInfoTask(pDep)); } ResourceAPI::DependencySearchArgs args = { .dependency = dep, .mcVersion = m_version, .loader = m_loaderType, .includeChangelog = true }; ResourceAPI::Callback callbacks; callbacks.on_fail = [](const QString& reason, int) { qCritical() << tr("A network error occurred. Could not load project dependencies:%1").arg(reason); }; callbacks.on_succeed = [dep, provider, pDep, level, this](auto& pack) { pDep->version = pack; if (!pDep->version.addonId.isValid()) { if (m_loaderType & ModPlatform::Quilt) { // falback for quilt auto overide = ModPlatform::getOverrideDeps(); auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, provider](const auto& o) { return o.provider == provider && dep.addonId == o.quilt; }); if (over != overide.cend()) { removePack(dep.addonId); addTask(prepareDependencyTask({ .addonId = over->fabric, .type = dep.type, .version = "" }, provider, level)); return; } } removePack(dep.addonId); return; } pDep->version.is_currently_selected = true; pDep->pack->versions = { pDep->version }; pDep->pack->versionsLoaded = true; if (level == 0) { removePack(dep.addonId); qWarning() << "Dependency cycle exceeded"; return; } if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) { pDep->pack->addonId = pDep->version.addonId; auto dep_ = getOverride({ .addonId = pDep->version.addonId, .type = pDep->dependency.type, .version = "" }, provider); if (dep_.addonId != pDep->version.addonId) { removePack(pDep->version.addonId); addTask(prepareDependencyTask(dep_, provider, level)); } else { addTask(getProjectInfoTask(pDep)); } } if (isLocalyInstalled(pDep)) { removePack(pDep->version.addonId); return; } for (const auto& dep_ : getDependenciesForVersion(pDep->version, provider)) { addTask(prepareDependencyTask(dep_, provider, level - 1)); } }; auto version = getAPI(provider)->getDependencyVersion(std::move(args), std::move(callbacks)); tasks->addTask(version); return tasks; } void GetModDependenciesTask::removePack(const QVariant& addonId) { auto pred = [addonId](const std::shared_ptr& v) { return v->pack->addonId == addonId; }; #if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) m_pack_dependencies.removeIf(pred); #else for (auto it = m_pack_dependencies.begin(); it != m_pack_dependencies.end();) if (pred(*it)) it = m_pack_dependencies.erase(it); else ++it; #endif } auto GetModDependenciesTask::getExtraInfo() -> QHash { QHash rby; auto fullList = m_selected + m_pack_dependencies; for (auto& mod : fullList) { auto addonId = mod->pack->addonId; auto provider = mod->pack->provider; auto version = mod->version.fileId; auto req = QStringList(); for (auto& smod : fullList) { if (provider != smod->pack->provider) continue; auto deps = smod->version.dependencies; if (auto dep = std::find_if(deps.begin(), deps.end(), [addonId, provider, version](const ModPlatform::Dependency& d) { return d.type == ModPlatform::DependencyType::REQUIRED && (provider == ModPlatform::ResourceProvider::MODRINTH && d.addonId.toString().isEmpty() ? version == d.version : d.addonId == addonId); }); dep != deps.end()) { req.append(smod->pack->name); } } rby[addonId.toString()] = { maybeInstalled(mod), req }; } return rby; } // super lax compare (but not fuzzy) // convert to lowercase // convert all speratores to whitespace // simplify sequence of internal whitespace to a single space // efectivly compare two strings ignoring all separators and case auto laxCompare = [](QString fsfilename, QString metadataFilename, bool excludeDigits = false) { // allowed character seperators QList allowedSeperators = { '-', '+', '.', '_' }; if (excludeDigits) allowedSeperators.append({ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }); // copy in lowercase auto fsName = fsfilename.toLower(); auto metaName = metadataFilename.toLower(); // replace all potential allowed seperatores with whitespace for (auto sep : allowedSeperators) { fsName = fsName.replace(sep, ' '); metaName = metaName.replace(sep, ' '); } // remove extraneous whitespace fsName = fsName.simplified(); metaName = metaName.simplified(); return fsName.compare(metaName) == 0; }; bool GetModDependenciesTask::isLocalyInstalled(std::shared_ptr pDep) { return pDep->version.fileName.isEmpty() || std::find_if(m_selected.begin(), m_selected.end(), [pDep](std::shared_ptr i) { return !i->version.fileName.isEmpty() && laxCompare(i->version.fileName, pDep->version.fileName); }) != m_selected.end() || // check the selected versions std::find_if(m_mods_file_names.begin(), m_mods_file_names.end(), [pDep](QString i) { return !i.isEmpty() && laxCompare(i, pDep->version.fileName); }) != m_mods_file_names.end() || // check the existing mods std::find_if(m_pack_dependencies.begin(), m_pack_dependencies.end(), [pDep](std::shared_ptr i) { return pDep->pack->addonId != i->pack->addonId && !i->version.fileName.isEmpty() && laxCompare(pDep->version.fileName, i->version.fileName); }) != m_pack_dependencies.end(); // check loaded dependencies } bool GetModDependenciesTask::maybeInstalled(std::shared_ptr pDep) { return std::find_if(m_mods_file_names.begin(), m_mods_file_names.end(), [pDep](QString i) { return !i.isEmpty() && laxCompare(i, pDep->version.fileName, true); }) != m_mods_file_names.end(); // check the existing mods } PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalWorldSaveParseTask.h0000644000175100017510000000327415224505336026316 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include "minecraft/mod/WorldSave.h" #include "tasks/Task.h" namespace WorldSaveUtils { enum class ProcessingLevel { Full, BasicInfoOnly }; bool process(WorldSave& save, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(WorldSave& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(WorldSave& pack, ProcessingLevel level = ProcessingLevel::Full); bool validate(QFileInfo file); } // namespace WorldSaveUtils class LocalWorldSaveParseTask : public Task { Q_OBJECT public: LocalWorldSaveParseTask(int token, WorldSave& save); bool canAbort() const override { return true; } bool abort() override; void executeTask() override; int token() const { return m_token; } private: int m_token; WorldSave& m_save; bool m_aborted = false; }; PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalWorldSaveParseTask.cpp0000644000175100017510000001335015224505336026645 0ustar runnerrunner // SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "LocalWorldSaveParseTask.h" #include "FileSystem.h" #include "archive/ArchiveReader.h" #include #include #include namespace WorldSaveUtils { bool process(WorldSave& pack, ProcessingLevel level) { switch (pack.type()) { case ResourceType::FOLDER: return WorldSaveUtils::processFolder(pack, level); case ResourceType::ZIPFILE: return WorldSaveUtils::processZIP(pack, level); default: qWarning() << "Invalid type for world save parse task!"; return false; } } /// @brief checks a folder structure to see if it contains a level.dat /// @param dir the path to check /// @param saves used in recursive call if a "saves" dir was found /// @return std::tuple of ( /// bool , /// QString , /// bool /// ) static std::tuple contains_level_dat(QDir dir, bool saves = false) { for (auto const& entry : dir.entryInfoList()) { if (!entry.isDir()) { continue; } if (!saves && entry.fileName() == "saves") { return contains_level_dat(QDir(entry.filePath()), true); } QFileInfo level_dat(FS::PathCombine(entry.filePath(), "level.dat")); if (level_dat.exists() && level_dat.isFile()) { return std::make_tuple(true, entry.fileName(), saves); } } return std::make_tuple(false, "", saves); } bool processFolder(WorldSave& save, ProcessingLevel level) { Q_ASSERT(save.type() == ResourceType::FOLDER); auto [found, save_dir_name, found_saves_dir] = contains_level_dat(QDir(save.fileinfo().filePath())); if (!found) { return false; } save.setSaveDirName(save_dir_name); if (found_saves_dir) { save.setSaveFormat(WorldSaveFormat::MULTI); } else { save.setSaveFormat(WorldSaveFormat::SINGLE); } if (level == ProcessingLevel::BasicInfoOnly) { return true; // only need basic info already checked } // reserved for more intensive processing return true; // all tests passed } /// @brief checks a folder structure to see if it contains a level.dat /// @param zip the zip file to check /// @return std::tuple of ( /// bool , /// QString , /// bool /// ) static std::tuple contains_level_dat(QString fileName) { MMCZip::ArchiveReader zip(fileName); if (!zip.collectFiles()) { return std::make_tuple(false, "", false); } bool saves = false; if (zip.exists("/saves")) { saves = true; } for (auto file : zip.getFiles()) { QString relativePath = file; if (saves) { if (!relativePath.startsWith("saves/", Qt::CaseInsensitive)) continue; relativePath = relativePath.mid(QString("saves/").length()); } if (!relativePath.endsWith("/level.dat", Qt::CaseInsensitive)) continue; int slashIndex = relativePath.indexOf('/'); if (slashIndex == -1) continue; // malformed: no slash between saves/ and level.dat QString worldName = relativePath.left(slashIndex); QString remaining = relativePath.mid(slashIndex + 1); // Check that there's nothing between worldName/ and level.dat if (remaining == "level.dat") { return std::make_tuple(true, worldName, saves); } } return std::make_tuple(false, "", saves); } bool processZIP(WorldSave& save, ProcessingLevel level) { Q_ASSERT(save.type() == ResourceType::ZIPFILE); auto [found, save_dir_name, found_saves_dir] = contains_level_dat(save.fileinfo().filePath()); if (!found) { return false; } if (save_dir_name.endsWith("/")) { save_dir_name.chop(1); } save.setSaveDirName(save_dir_name); if (found_saves_dir) { save.setSaveFormat(WorldSaveFormat::MULTI); } else { save.setSaveFormat(WorldSaveFormat::SINGLE); } if (level == ProcessingLevel::BasicInfoOnly) { return true; // only need basic info already checked } // reserved for more intensive processing return true; } bool validate(QFileInfo file) { WorldSave sp{ file }; return WorldSaveUtils::process(sp, ProcessingLevel::BasicInfoOnly) && sp.valid(); } } // namespace WorldSaveUtils LocalWorldSaveParseTask::LocalWorldSaveParseTask(int token, WorldSave& save) : Task(false), m_token(token), m_save(save) {} bool LocalWorldSaveParseTask::abort() { m_aborted = true; return true; } void LocalWorldSaveParseTask::executeTask() { if (!WorldSaveUtils::process(m_save)) { emitFailed("this is not a world"); return; } if (m_aborted) emitAborted(); else emitSucceeded(); } PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp0000644000175100017510000001453515224505336027204 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "LocalTexturePackParseTask.h" #include "FileSystem.h" #include "archive/ArchiveReader.h" #include namespace TexturePackUtils { bool process(TexturePack& pack, ProcessingLevel level) { switch (pack.type()) { case ResourceType::FOLDER: return TexturePackUtils::processFolder(pack, level); case ResourceType::ZIPFILE: return TexturePackUtils::processZIP(pack, level); default: qWarning() << "Invalid type for resource pack parse task!"; return false; } } bool processFolder(TexturePack& pack, ProcessingLevel level) { Q_ASSERT(pack.type() == ResourceType::FOLDER); QFileInfo mcmeta_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.txt")); if (mcmeta_file_info.isFile()) { QFile mcmeta_file(mcmeta_file_info.filePath()); if (!mcmeta_file.open(QIODevice::ReadOnly)) return false; auto data = mcmeta_file.readAll(); bool packTXT_result = TexturePackUtils::processPackTXT(pack, std::move(data)); mcmeta_file.close(); if (!packTXT_result) { return false; } } else { return false; } if (level == ProcessingLevel::BasicInfoOnly) return true; QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png")); if (image_file_info.isFile()) { QFile mcmeta_file(image_file_info.filePath()); if (!mcmeta_file.open(QIODevice::ReadOnly)) return false; auto data = mcmeta_file.readAll(); bool packPNG_result = TexturePackUtils::processPackPNG(pack, std::move(data)); mcmeta_file.close(); if (!packPNG_result) { return false; } } else { return false; } return true; } bool processZIP(TexturePack& pack, ProcessingLevel level) { Q_ASSERT(pack.type() == ResourceType::ZIPFILE); MMCZip::ArchiveReader zip(pack.fileinfo().filePath()); bool packProcessed = false; bool iconProcessed = false; return zip.parse([&packProcessed, &iconProcessed, &pack, level](MMCZip::ArchiveReader::File* file, bool& stop) { if (!packProcessed && file->filename() == "pack.txt") { packProcessed = true; auto data = file->readAll(); stop = packProcessed && (iconProcessed || level == ProcessingLevel::BasicInfoOnly); return TexturePackUtils::processPackTXT(pack, std::move(data)); } if (!iconProcessed && file->filename() == "pack.png") { iconProcessed = true; auto data = file->readAll(); stop = packProcessed && iconProcessed; return TexturePackUtils::processPackPNG(pack, std::move(data)); } file->skip(); return true; }); } bool processPackTXT(TexturePack& pack, QByteArray&& raw_data) { pack.setDescription(QString(raw_data)); return true; } bool processPackPNG(const TexturePack& pack, QByteArray&& raw_data) { auto img = QImage::fromData(raw_data); if (!img.isNull()) { pack.setImage(img); } else { qWarning() << "Failed to parse pack.png."; return false; } return true; } bool processPackPNG(const TexturePack& pack) { auto png_invalid = [&pack]() { qWarning() << "Texture pack at" << pack.fileinfo().filePath() << "does not have a valid pack.png"; return false; }; switch (pack.type()) { case ResourceType::FOLDER: { QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png")); if (image_file_info.exists() && image_file_info.isFile()) { QFile pack_png_file(image_file_info.filePath()); if (!pack_png_file.open(QIODevice::ReadOnly)) return png_invalid(); // can't open pack.png file auto data = pack_png_file.readAll(); bool pack_png_result = TexturePackUtils::processPackPNG(pack, std::move(data)); pack_png_file.close(); if (!pack_png_result) { return png_invalid(); // pack.png invalid } } else { return png_invalid(); // pack.png does not exists or is not a valid file. } return false; } case ResourceType::ZIPFILE: { MMCZip::ArchiveReader zip(pack.fileinfo().filePath()); auto file = zip.goToFile("pack.png"); if (file) { auto data = file->readAll(); bool pack_png_result = TexturePackUtils::processPackPNG(pack, std::move(data)); if (!pack_png_result) { return png_invalid(); // pack.png invalid } } return png_invalid(); // could not set pack.mcmeta as current file. } default: qWarning() << "Invalid type for resource pack parse task!"; return false; } } bool validate(QFileInfo file) { TexturePack rp{ file }; return TexturePackUtils::process(rp, ProcessingLevel::BasicInfoOnly) && rp.valid(); } } // namespace TexturePackUtils LocalTexturePackParseTask::LocalTexturePackParseTask(int token, TexturePack& rp) : Task(false), m_token(token), m_texture_pack(rp) {} bool LocalTexturePackParseTask::abort() { m_aborted = true; return true; } void LocalTexturePackParseTask::executeTask() { if (!TexturePackUtils::process(m_texture_pack)) { emitFailed("this is not a texture pack"); return; } if (m_aborted) emitAborted(); else emitSucceeded(); } PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalResourceParse.cpp0000644000175100017510000000505515224505336025706 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "LocalResourceParse.h" #include "LocalDataPackParseTask.h" #include "LocalModParseTask.h" #include "LocalShaderPackParseTask.h" #include "LocalTexturePackParseTask.h" #include "LocalWorldSaveParseTask.h" #include "modplatform/ResourceType.h" namespace ResourceUtils { ModPlatform::ResourceType identify(QFileInfo file) { if (file.exists() && file.isFile()) { if (ModUtils::validate(file)) { // mods can contain resource and data packs so they must be tested first qDebug() << file.fileName() << "is a mod"; return ModPlatform::ResourceType::Mod; } else if (DataPackUtils::validateResourcePack(file)) { qDebug() << file.fileName() << "is a resource pack"; return ModPlatform::ResourceType::ResourcePack; } else if (TexturePackUtils::validate(file)) { qDebug() << file.fileName() << "is a pre 1.6 texture pack"; return ModPlatform::ResourceType::TexturePack; } else if (DataPackUtils::validate(file)) { qDebug() << file.fileName() << "is a data pack"; return ModPlatform::ResourceType::DataPack; } else if (WorldSaveUtils::validate(file)) { qDebug() << file.fileName() << "is a world save"; return ModPlatform::ResourceType::World; } else if (ShaderPackUtils::validate(file)) { qDebug() << file.fileName() << "is a shader pack"; return ModPlatform::ResourceType::ShaderPack; } else { qDebug() << "Can't Identify" << file.fileName(); } } else { qDebug() << "Can't find" << file.absolutePath(); } return ModPlatform::ResourceType::Unknown; } } // namespace ResourceUtils PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalModParseTask.h0000644000175100017510000000303515224505336025122 0ustar runnerrunner#pragma once #include #include #include "minecraft/mod/Mod.h" #include "minecraft/mod/ModDetails.h" #include "tasks/Task.h" namespace ModUtils { ModDetails ReadFabricModInfo(QByteArray contents); ModDetails ReadQuiltModInfo(QByteArray contents); ModDetails ReadForgeInfo(QByteArray contents); ModDetails ReadLiteModInfo(QByteArray contents); enum class ProcessingLevel { Full, BasicInfoOnly }; bool process(Mod& mod, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(Mod& mod, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(Mod& mod, ProcessingLevel level = ProcessingLevel::Full); bool processLitemod(Mod& mod, ProcessingLevel level = ProcessingLevel::Full); /** Checks whether a file is valid as a mod or not. */ bool validate(QFileInfo file); bool processIconPNG(const Mod& mod, QByteArray&& raw_data, QPixmap* pixmap); bool loadIconFile(const Mod& mod, QPixmap* pixmap); } // namespace ModUtils class LocalModParseTask : public Task { Q_OBJECT public: struct Result { ModDetails details; }; using ResultPtr = std::shared_ptr; ResultPtr result() const { return m_result; } bool canAbort() const override { return true; } bool abort() override; LocalModParseTask(int token, ResourceType type, const QFileInfo& modFile); void executeTask() override; int token() const { return m_token; } private: int m_token; ResourceType m_type; QFileInfo m_modFile; ResultPtr m_result; std::atomic m_aborted = false; }; PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp0000644000175100017510000001314415224505336026515 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ResourceFolderLoadTask.h" #include "Application.h" #include "FileSystem.h" #include "minecraft/mod/MetadataHandler.h" #include ResourceFolderLoadTask::ResourceFolderLoadTask(const QDir& resource_dir, const QDir& index_dir, bool is_indexed, bool clean_orphan, std::function create_function) : Task(false) , m_resource_dir(resource_dir) , m_index_dir(index_dir) , m_is_indexed(is_indexed) , m_clean_orphan(clean_orphan) , m_create_func(create_function) , m_result(new Result()) , m_thread_to_spawn_into(thread()) {} void ResourceFolderLoadTask::executeTask() { if (thread() != m_thread_to_spawn_into) connect(this, &Task::finished, this->thread(), &QThread::quit); if (m_is_indexed) { // Read metadata first getFromMetadata(); } // Read JAR files that don't have metadata m_resource_dir.refresh(); for (auto entry : m_resource_dir.entryInfoList()) { auto filePath = entry.absoluteFilePath(); if (auto app = APPLICATION_DYN; app && app->checkQSavePath(filePath)) { continue; } auto newFilePath = FS::getUniqueResourceName(filePath); if (newFilePath != filePath) { FS::move(filePath, newFilePath); entry = QFileInfo(newFilePath); } Resource* resource = m_create_func(entry); if (resource->enabled()) { if (m_result->resources.contains(resource->internal_id())) { m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::INSTALLED); // Delete the object we just created, since a valid one is already in the mods list. delete resource; } else { m_result->resources[resource->internal_id()].reset(resource); m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::NO_METADATA); } } else { QString chopped_id = resource->internal_id().chopped(9); if (m_result->resources.contains(chopped_id)) { m_result->resources[resource->internal_id()].reset(resource); auto metadata = m_result->resources[chopped_id]->metadata(); if (metadata) { resource->setMetadata(*metadata); m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::INSTALLED); m_result->resources.remove(chopped_id); } } else { m_result->resources[resource->internal_id()].reset(resource); m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::NO_METADATA); } } } // Remove orphan metadata to prevent issues // See https://github.com/PolyMC/PolyMC/issues/996 if (m_clean_orphan) { QMutableMapIterator iter(m_result->resources); while (iter.hasNext()) { auto resource = iter.next().value(); if (resource->status() == ResourceStatus::NOT_INSTALLED) { resource->destroy(m_index_dir, false, false); iter.remove(); } } } for (auto mod : m_result->resources) mod->moveToThread(m_thread_to_spawn_into); if (m_aborted) emit finished(); else emitSucceeded(); } void ResourceFolderLoadTask::getFromMetadata() { m_index_dir.refresh(); for (auto entry : m_index_dir.entryList(QDir::Files)) { if (!entry.endsWith(".pw.toml")) { continue; } auto metadata = Metadata::get(m_index_dir, entry); if (!metadata.isValid()) continue; auto* resource = m_create_func(QFileInfo(m_resource_dir.filePath(metadata.filename))); resource->setMetadata(metadata); resource->setStatus(ResourceStatus::NOT_INSTALLED); m_result->resources[resource->internal_id()].reset(resource); } } PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalModParseTask.cpp0000644000175100017510000007403015224505336025460 0ustar runnerrunner#include "LocalModParseTask.h" #include #include #include #include #include #include #include #include #include "FileSystem.h" #include "Json.h" #include "archive/ArchiveReader.h" #include "minecraft/mod/ModDetails.h" #include "settings/INIFile.h" static const QRegularExpression s_newlineRegex("\r\n|\n|\r"); namespace ModUtils { // NEW format // https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/c8d8f1929aff9979e322af79a59ce81f3e02db6a // OLD format: // https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc ModDetails ReadMCModInfo(QByteArray contents) { auto getInfoFromArray = [](QJsonArray arr) -> ModDetails { if (!arr.at(0).isObject()) { return {}; } ModDetails details; auto firstObj = arr.at(0).toObject(); details.mod_id = firstObj.value("modid").toString(); auto name = firstObj.value("name").toString(); // NOTE: ignore stupid example mods copies where the author didn't even bother to change the name if (name != "Example Mod") { details.name = name; } details.version = firstObj.value("version").toString(); auto homeurl = firstObj.value("url").toString().trimmed(); if (!homeurl.isEmpty()) { // fix up url. if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://")) { homeurl.prepend("http://"); } } details.homeurl = homeurl; details.description = firstObj.value("description").toString(); QJsonArray authors = firstObj.value("authorList").toArray(); if (authors.size() == 0) { // FIXME: what is the format of this? is there any? authors = firstObj.value("authors").toArray(); } if (firstObj.contains("logoFile")) { details.icon_file = firstObj.value("logoFile").toString(); } for (auto author : authors) { details.authors.append(author.toString()); } if (details.mod_id.startsWith("mod_")) { details.mod_id = details.mod_id.mid(4); } auto addDep = [&details](QString dep) { if (dep == "mod_MinecraftForge" || dep == "Forge") return; if (dep.contains(":")) { dep = dep.section(":", 1); } if (dep.contains("@")) { dep = dep.section("@", 0, 0); } if (dep.startsWith("mod_")) { dep = dep.mid(4); } details.dependencies.append(dep); }; if (firstObj.contains("requiredMods")) { for (auto dep : firstObj.value("requiredMods").toArray()) { addDep(dep.toString()); } } else if (firstObj.contains("dependencies")) { for (auto dep : firstObj.value("dependencies").toArray()) { addDep(dep.toString()); } } return details; }; QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); // this is the very old format that had just the array if (jsonDoc.isArray()) { return getInfoFromArray(jsonDoc.array()); } else if (jsonDoc.isObject()) { auto val = jsonDoc.object().value("modinfoversion"); if (val.isUndefined()) { val = jsonDoc.object().value("modListVersion"); } int version = val.toInt(-1); // Some mods set the number with "", so it's a String instead if (version < 0) version = val.toString("").toInt(); if (version != 2) { qWarning() << QString(R"(The value of 'modListVersion' is "%1" (expected "2")! The file may be corrupted.)").arg(version); qWarning() << "The contents of 'mcmod.info' are as follows:"; qWarning() << contents; } auto arrVal = jsonDoc.object().value("modlist"); if (arrVal.isUndefined()) { arrVal = jsonDoc.object().value("modList"); } if (arrVal.isArray()) { return getInfoFromArray(arrVal.toArray()); } } return {}; } // https://github.com/MinecraftForge/Documentation/blob/5ab4ba6cf9abc0ac4c0abd96ad187461aefd72af/docs/gettingstarted/structuring.md ModDetails ReadMCModTOML(QByteArray contents) { ModDetails details; toml::table tomlData; #if TOML_EXCEPTIONS try { tomlData = toml::parse(contents.toStdString()); } catch ([[maybe_unused]] const toml::parse_error& err) { return {}; } #else toml::parse_result result = toml::parse(contents.toStdString()); if (!result) { return {}; } tomlData = result.table(); #endif // array defined by [[mods]] auto tomlModsArr = tomlData["mods"].as_array(); if (!tomlModsArr) { qWarning() << "Corrupted mods.toml? Couldn't find [[mods]] array!"; return {}; } // we only really care about the first element, since multiple mods in one file is not supported by us at the moment auto tomlModsTable0 = tomlModsArr->get(0); if (!tomlModsTable0) { qWarning() << "Corrupted mods.toml? [[mods]] didn't have an element at index 0!"; return {}; } auto modsTable = tomlModsTable0->as_table(); if (!modsTable) { qWarning() << "Corrupted mods.toml? [[mods]] was not a table!"; return {}; } // mandatory properties - always in [[mods]] if (auto modIdDatum = (*modsTable)["modId"].as_string()) { details.mod_id = QString::fromStdString(modIdDatum->get()); } if (auto versionDatum = (*modsTable)["version"].as_string()) { details.version = QString::fromStdString(versionDatum->get()); } if (auto displayNameDatum = (*modsTable)["displayName"].as_string()) { details.name = QString::fromStdString(displayNameDatum->get()); } if (auto descriptionDatum = (*modsTable)["description"].as_string()) { details.description = QString::fromStdString(descriptionDatum->get()); } // optional properties - can be in the root table or [[mods]] QString authors = ""; if (auto authorsDatum = tomlData["authors"].as_string()) { authors = QString::fromStdString(authorsDatum->get()); } else if (auto authorsDatumMods = (*modsTable)["authors"].as_string()) { authors = QString::fromStdString(authorsDatumMods->get()); } if (!authors.isEmpty()) { details.authors.append(authors); } QString homeurl = ""; if (auto homeurlDatum = tomlData["displayURL"].as_string()) { homeurl = QString::fromStdString(homeurlDatum->get()); } else if (auto homeurlDatumMods = (*modsTable)["displayURL"].as_string()) { homeurl = QString::fromStdString(homeurlDatumMods->get()); } // fix up url. if (!homeurl.isEmpty() && !homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://")) { homeurl.prepend("http://"); } details.homeurl = homeurl; QString issueTrackerURL = ""; if (auto issueTrackerURLDatum = tomlData["issueTrackerURL"].as_string()) { issueTrackerURL = QString::fromStdString(issueTrackerURLDatum->get()); } else if (auto issueTrackerURLDatumMods = (*modsTable)["issueTrackerURL"].as_string()) { issueTrackerURL = QString::fromStdString(issueTrackerURLDatumMods->get()); } details.issue_tracker = issueTrackerURL; QString license = ""; if (auto licenseDatum = tomlData["license"].as_string()) { license = QString::fromStdString(licenseDatum->get()); } else if (auto licenseDatumMods = (*modsTable)["license"].as_string()) { license = QString::fromStdString(licenseDatumMods->get()); } if (!license.isEmpty()) details.licenses.append(ModLicense(license)); QString logoFile = ""; if (auto logoFileDatum = tomlData["logoFile"].as_string()) { logoFile = QString::fromStdString(logoFileDatum->get()); } else if (auto logoFileDatumMods = (*modsTable)["logoFile"].as_string()) { logoFile = QString::fromStdString(logoFileDatumMods->get()); } details.icon_file = logoFile; auto parseDep = [&details](toml::array* dependencies) { static const QStringList ignoreModIds = { "", "forge", "neoforge", "minecraft" }; if (!dependencies) { return; } auto isNeoForgeDep = [](toml::table* t) { auto type = (*t)["type"].as_string(); return type && type->get() == "required"; }; auto isForgeDep = [](toml::table* t) { auto mandatory = (*t)["mandatory"].as_boolean(); return mandatory && mandatory->get(); }; for (auto& dep : *dependencies) { auto dep_table = dep.as_table(); if (!dep_table) { continue; } auto modId = (*dep_table)["modId"].as_string(); if (!modId || ignoreModIds.contains(QString::fromStdString(modId->get()))) { continue; } if (isNeoForgeDep(dep_table) || isForgeDep(dep_table)) { details.dependencies.append(QString::fromStdString(modId->get())); } } }; if (tomlData.contains("dependencies")) { auto depValue = tomlData["dependencies"]; if (auto array = depValue.as_array()) { parseDep(array); } else if (auto depTable = depValue.as_table()) { auto expectedKey = details.mod_id.toStdString(); if (!depTable->contains(expectedKey)) { if (auto it = depTable->begin(); it != depTable->end()) { expectedKey = it->first; } } if ((array = (*depTable)[expectedKey].as_array())) { parseDep(array); } } } return details; } // https://fabricmc.net/wiki/documentation:fabric_mod_json ModDetails ReadFabricModInfo(QByteArray contents) { QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); auto object = jsonDoc.object(); auto schemaVersion = object.contains("schemaVersion") ? object.value("schemaVersion").toInt(0) : 0; ModDetails details; details.mod_id = object.value("id").toString(); details.version = object.value("version").toString(); details.name = object.contains("name") ? object.value("name").toString() : details.mod_id; details.description = object.value("description").toString(); if (schemaVersion >= 1) { QJsonArray authors = object.value("authors").toArray(); for (auto author : authors) { if (author.isObject()) { details.authors.append(author.toObject().value("name").toString()); } else { details.authors.append(author.toString()); } } if (object.contains("contact")) { QJsonObject contact = object.value("contact").toObject(); if (contact.contains("homepage")) { details.homeurl = contact.value("homepage").toString(); } if (contact.contains("issues")) { details.issue_tracker = contact.value("issues").toString(); } } if (object.contains("license")) { auto license = object.value("license"); if (license.isArray()) { for (auto l : license.toArray()) { if (l.isString()) { details.licenses.append(ModLicense(l.toString())); } else if (l.isObject()) { auto obj = l.toObject(); details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(), obj.value("url").toString(), obj.value("description").toString())); } } } else if (license.isString()) { details.licenses.append(ModLicense(license.toString())); } else if (license.isObject()) { auto obj = license.toObject(); details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(), obj.value("url").toString(), obj.value("description").toString())); } } if (object.contains("icon")) { auto icon = object.value("icon"); if (icon.isObject()) { auto obj = icon.toObject(); // take the largest icon int largest = 0; for (auto key : obj.keys()) { auto size = key.split('x').first().toInt(); if (size > largest) { largest = size; } } if (largest > 0) { auto key = QString::number(largest) + "x" + QString::number(largest); details.icon_file = obj.value(key).toString(); } else { // parsing the sizes failed // take the first if (auto it = obj.begin(); it != obj.end()) { details.icon_file = it->toString(); } } } else if (icon.isString()) { details.icon_file = icon.toString(); } } if (object.contains("depends")) { auto depends = object.value("depends"); if (depends.isObject()) { auto obj = depends.toObject(); for (auto key : obj.keys()) { if (key != "fabricloader" && key != "minecraft" && !key.startsWith("fabric-")) { details.dependencies.append(key); } } } } } return details; } // https://github.com/QuiltMC/rfcs/blob/master/specification/0002-quilt.mod.json.md ModDetails ReadQuiltModInfo(QByteArray contents) { ModDetails details; try { QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); auto object = Json::requireObject(jsonDoc, "quilt.mod.json"); auto schemaVersion = object.value("schema_version").toInt(); // https://github.com/QuiltMC/rfcs/blob/be6ba280d785395fefa90a43db48e5bfc1d15eb4/specification/0002-quilt.mod.json.md if (schemaVersion == 1) { auto modInfo = Json::requireObject(object.value("quilt_loader"), "Quilt mod info"); details.mod_id = Json::requireString(modInfo.value("id"), "Mod ID"); details.version = Json::requireString(modInfo.value("version"), "Mod version"); auto modMetadata = modInfo.value("metadata").toObject(); details.name = modMetadata.value("name").toString(details.mod_id); details.description = modMetadata.value("description").toString(); auto modContributors = modMetadata.value("contributors").toObject(); // We don't really care about the role of a contributor here details.authors += modContributors.keys(); auto modContact = modMetadata.value("contact").toObject(); if (modContact.contains("homepage")) { details.homeurl = Json::requireString(modContact.value("homepage")); } if (modContact.contains("issues")) { details.issue_tracker = Json::requireString(modContact.value("issues")); } if (modMetadata.contains("license")) { auto license = modMetadata.value("license"); if (license.isArray()) { for (auto l : license.toArray()) { if (l.isString()) { details.licenses.append(ModLicense(l.toString())); } else if (l.isObject()) { auto obj = l.toObject(); details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(), obj.value("url").toString(), obj.value("description").toString())); } } } else if (license.isString()) { details.licenses.append(ModLicense(license.toString())); } else if (license.isObject()) { auto obj = license.toObject(); details.licenses.append(ModLicense(obj.value("name").toString(), obj.value("id").toString(), obj.value("url").toString(), obj.value("description").toString())); } } if (modMetadata.contains("icon")) { auto icon = modMetadata.value("icon"); if (icon.isObject()) { auto obj = icon.toObject(); // take the largest icon int largest = 0; for (auto key : obj.keys()) { auto size = key.split('x').first().toInt(); if (size > largest) { largest = size; } } if (largest > 0) { auto key = QString::number(largest) + "x" + QString::number(largest); details.icon_file = obj.value(key).toString(); } else { // parsing the sizes failed // take the first if (auto it = obj.begin(); it != obj.end()) { details.icon_file = it->toString(); } } } else if (icon.isString()) { details.icon_file = icon.toString(); } } if (object.contains("depends")) { auto depends = object.value("depends"); if (depends.isArray()) { auto array = depends.toArray(); for (auto obj : array) { QString modId; if (obj.isString()) { modId = obj.toString(); } else if (obj.isObject()) { auto objValue = obj.toObject(); modId = objValue.value("id").toString(); if (objValue.contains("optional") && objValue.value("optional").toBool()) { continue; } } else { continue; } if (modId != "minecraft" && !modId.startsWith("quilt_")) { details.dependencies.append(modId); } } } } } } catch (const Exception& e) { qWarning() << "Unable to parse mod info:" << e.cause(); } return details; } ModDetails ReadForgeInfo(QByteArray contents) { ModDetails details; // Read the data details.name = "Minecraft Forge"; details.mod_id = "Forge"; details.homeurl = "http://www.minecraftforge.net/forum/"; INIFile ini; if (!ini.loadFile(contents)) return details; QString major = ini.get("forge.major.number", "0").toString(); QString minor = ini.get("forge.minor.number", "0").toString(); QString revision = ini.get("forge.revision.number", "0").toString(); QString build = ini.get("forge.build.number", "0").toString(); details.version = major + "." + minor + "." + revision + "." + build; return details; } ModDetails ReadLiteModInfo(QByteArray contents) { ModDetails details; QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); auto object = jsonDoc.object(); if (object.contains("name")) { details.mod_id = details.name = object.value("name").toString(); } if (object.contains("version")) { details.version = object.value("version").toString(""); } else { details.version = object.value("revision").toString(""); } details.mcversion = object.value("mcversion").toString(); auto author = object.value("author").toString(); if (!author.isEmpty()) { details.authors.append(author); } details.description = object.value("description").toString(); details.homeurl = object.value("url").toString(); return details; } // https://git.sleeping.town/unascribed/NilLoader/src/commit/d7fc87b255fc31019ff90f80d45894927fac6efc/src/main/java/nilloader/api/NilMetadata.java#L64 ModDetails ReadNilModInfo(QByteArray contents, QString fname) { ModDetails details; QDCSS cssData = QDCSS(contents); auto name = cssData.get("@nilmod.name"); auto desc = cssData.get("@nilmod.description"); auto authors = cssData.get("@nilmod.authors"); if (name->has_value()) { details.name = name->value(); } if (desc->has_value()) { details.description = desc->value(); } if (authors->has_value()) { details.authors.append(authors->value()); } details.version = cssData.get("@nilmod.version")->value_or("?"); details.mod_id = fname.remove(".nilmod.css"); return details; } bool process(Mod& mod, ProcessingLevel level) { switch (mod.type()) { case ResourceType::FOLDER: return processFolder(mod, level); case ResourceType::ZIPFILE: return processZIP(mod, level); case ResourceType::LITEMOD: return processLitemod(mod); default: qWarning() << "Invalid type" << mod.type() << "for mod parse task!"; return false; } } bool processZIP(Mod& mod, [[maybe_unused]] ProcessingLevel level) { ModDetails details; MMCZip::ArchiveReader zip(mod.fileinfo().filePath()); bool baseForgePopulated = false; bool isNilMod = false; bool isValid = false; QString manifestVersion = {}; QByteArray nilData = {}; QString nilFilePath = {}; if (!zip.parse([&details, &baseForgePopulated, &manifestVersion, &isValid, &nilData, &isNilMod, &nilFilePath]( MMCZip::ArchiveReader::File* file, bool& stop) { auto filePath = file->filename(); if (filePath == "META-INF/mods.toml" || filePath == "META-INF/neoforge.mods.toml") { details = ReadMCModTOML(file->readAll()); isValid = true; if (details.version == "${file.jarVersion}" && !manifestVersion.isEmpty()) { details.version = manifestVersion; } stop = details.version != "${file.jarVersion}"; baseForgePopulated = true; return true; } if (filePath == "META-INF/MANIFEST.MF") { // quick and dirty line-by-line parser auto manifestLines = QString(file->readAll()).split(s_newlineRegex); manifestVersion = ""; for (auto& line : manifestLines) { if (line.startsWith("Implementation-Version: ", Qt::CaseInsensitive)) { manifestVersion = line.remove("Implementation-Version: ", Qt::CaseInsensitive); break; } } // some mods use ${projectversion} in their build.gradle, causing this mess to show up in MANIFEST.MF // also keep with forge's behavior of setting the version to "NONE" if none is found if (manifestVersion.contains("task ':jar' property 'archiveVersion'") || manifestVersion == "") { manifestVersion = "NONE"; } if (baseForgePopulated) { details.version = manifestVersion; stop = true; } return true; } if (filePath == "mcmod.info") { details = ReadMCModInfo(file->readAll()); isValid = true; stop = true; return true; } if (filePath == "quilt.mod.json") { details = ReadQuiltModInfo(file->readAll()); isValid = true; stop = true; return true; } if (filePath == "fabric.mod.json") { details = ReadFabricModInfo(file->readAll()); isValid = true; stop = true; return true; } if (filePath == "forgeversion.properties") { details = ReadForgeInfo(file->readAll()); isValid = true; stop = true; return true; } if (filePath == "META-INF/nil/mappings.json") { // nilloader uses the filename of the metadata file for the modid, so we can't know the exact filename // thankfully, there is a good file to use as a canary so we don't look for nil meta all the time isNilMod = true; stop = !nilFilePath.isEmpty(); file->skip(); return true; } // nilmods can shade nilloader to be able to run as a standalone agent - which includes nilloader's own meta file if (filePath.endsWith(".nilmod.css") && filePath != "nilloader.nilmod.css") { nilData = file->readAll(); nilFilePath = filePath; stop = isNilMod; return true; } file->skip(); return true; })) { return false; } if (isNilMod) { details = ReadNilModInfo(nilData, nilFilePath); isValid = true; } if (isValid) { mod.setDetails(details); return true; } return false; // no valid mod found in archive } bool processFolder(Mod& mod, [[maybe_unused]] ProcessingLevel level) { ModDetails details; QFileInfo mcmod_info(FS::PathCombine(mod.fileinfo().filePath(), "mcmod.info")); if (mcmod_info.exists() && mcmod_info.isFile()) { QFile mcmod(mcmod_info.filePath()); if (!mcmod.open(QIODevice::ReadOnly)) return false; auto data = mcmod.readAll(); if (data.isEmpty() || data.isNull()) return false; details = ReadMCModInfo(data); mod.setDetails(details); return true; } return false; // no valid mcmod.info file found } bool processLitemod(Mod& mod, [[maybe_unused]] ProcessingLevel level) { ModDetails details; MMCZip::ArchiveReader zip(mod.fileinfo().filePath()); if (auto file = zip.goToFile("litemod.json"); file) { details = ReadLiteModInfo(file->readAll()); mod.setDetails(details); return true; } return false; // no valid litemod.json found in archive } /** Checks whether a file is valid as a mod or not. */ bool validate(QFileInfo file) { Mod mod{ file }; return ModUtils::process(mod, ProcessingLevel::BasicInfoOnly) && mod.valid(); } bool processIconPNG(const Mod& mod, QByteArray&& raw_data, QPixmap* pixmap) { auto img = QImage::fromData(raw_data); if (!img.isNull()) { *pixmap = mod.setIcon(img); } else { qWarning() << "Failed to parse mod logo:" << mod.iconPath() << "from" << mod.name(); return false; } return true; } bool loadIconFile(const Mod& mod, QPixmap* pixmap) { if (mod.iconPath().isEmpty()) { qWarning() << "No Iconfile set, be sure to parse the mod first"; return false; } auto png_invalid = [&mod](const QString& reason) { qWarning() << "Mod at" << mod.fileinfo().filePath() << "does not have a valid icon:" << reason; return false; }; switch (mod.type()) { case ResourceType::FOLDER: { QFileInfo icon_info(FS::PathCombine(mod.fileinfo().filePath(), mod.iconPath())); if (icon_info.exists() && icon_info.isFile()) { QFile icon(icon_info.filePath()); if (!icon.open(QIODevice::ReadOnly)) { return png_invalid("failed to open file " + icon_info.filePath() + " " + icon.errorString()); } auto data = icon.readAll(); bool icon_result = ModUtils::processIconPNG(mod, std::move(data), pixmap); icon.close(); if (!icon_result) { return png_invalid("invalid png image"); // icon invalid } return true; } return png_invalid("file '" + icon_info.filePath() + "' does not exists or is not a file"); } case ResourceType::ZIPFILE: { MMCZip::ArchiveReader zip(mod.fileinfo().filePath()); auto file = zip.goToFile(mod.iconPath()); if (file) { auto data = file->readAll(); bool icon_result = ModUtils::processIconPNG(mod, std::move(data), pixmap); if (!icon_result) { return png_invalid("invalid png image"); // icon png invalid } return true; } return png_invalid("Failed to set '" + mod.iconPath() + "' as current file in zip archive"); // could not set icon as current file. } case ResourceType::LITEMOD: { return png_invalid("litemods do not have icons"); // can lightmods even have icons? } default: return png_invalid("Invalid type for mod, can not load icon."); } } } // namespace ModUtils LocalModParseTask::LocalModParseTask(int token, ResourceType type, const QFileInfo& modFile) : Task(false), m_token(token), m_type(type), m_modFile(modFile), m_result(new Result()) {} bool LocalModParseTask::abort() { m_aborted.store(true); return true; } void LocalModParseTask::executeTask() { Mod mod{ m_modFile }; ModUtils::process(mod, ModUtils::ProcessingLevel::Full); m_result->details = mod.details(); if (m_aborted) emitAborted(); else emitSucceeded(); } PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp0000644000175100017510000003144715224505336026416 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "LocalDataPackParseTask.h" #include "FileSystem.h" #include "Json.h" #include "archive/ArchiveReader.h" #include "minecraft/mod/ResourcePack.h" #include #include namespace DataPackUtils { bool process(DataPack* pack, ProcessingLevel level) { switch (pack->type()) { case ResourceType::FOLDER: return DataPackUtils::processFolder(pack, level); case ResourceType::ZIPFILE: return DataPackUtils::processZIP(pack, level); default: qWarning() << "Invalid type for data pack parse task!"; return false; } } bool processFolder(DataPack* pack, ProcessingLevel level) { Q_ASSERT(pack->type() == ResourceType::FOLDER); auto mcmeta_invalid = [&pack]() { qWarning() << "Data pack at" << pack->fileinfo().filePath() << "does not have a valid pack.mcmeta"; return false; // the mcmeta is not optional }; QFileInfo mcmeta_file_info(FS::PathCombine(pack->fileinfo().filePath(), "pack.mcmeta")); if (mcmeta_file_info.exists() && mcmeta_file_info.isFile()) { QFile mcmeta_file(mcmeta_file_info.filePath()); if (!mcmeta_file.open(QIODevice::ReadOnly)) return mcmeta_invalid(); // can't open mcmeta file auto data = mcmeta_file.readAll(); bool mcmeta_result = DataPackUtils::processMCMeta(pack, std::move(data)); mcmeta_file.close(); if (!mcmeta_result) { return mcmeta_invalid(); // mcmeta invalid } } else { return mcmeta_invalid(); // mcmeta file isn't a valid file } if (level == ProcessingLevel::BasicInfoOnly) { return true; // only need basic info already checked } auto png_invalid = [&pack]() { qWarning() << "Data pack at" << pack->fileinfo().filePath() << "does not have a valid pack.png"; return true; // the png is optional }; QFileInfo image_file_info(FS::PathCombine(pack->fileinfo().filePath(), "pack.png")); if (image_file_info.exists() && image_file_info.isFile()) { QFile pack_png_file(image_file_info.filePath()); if (!pack_png_file.open(QIODevice::ReadOnly)) return png_invalid(); // can't open pack.png file auto data = pack_png_file.readAll(); bool pack_png_result = DataPackUtils::processPackPNG(pack, std::move(data)); pack_png_file.close(); if (!pack_png_result) { return png_invalid(); // pack.png invalid } } else { return png_invalid(); // pack.png does not exists or is not a valid file. } return true; // all tests passed } bool processZIP(DataPack* pack, ProcessingLevel level) { Q_ASSERT(pack->type() == ResourceType::ZIPFILE); MMCZip::ArchiveReader zip(pack->fileinfo().filePath()); bool metaParsed = false; bool iconParsed = false; bool mcmeta_result = false; bool pack_png_result = false; if (!zip.parse( [&metaParsed, &iconParsed, &mcmeta_result, &pack_png_result, pack, level](MMCZip::ArchiveReader::File* f, bool& breakControl) { bool skip = true; if (!metaParsed && f->filename() == "pack.mcmeta") { metaParsed = true; skip = false; auto data = f->readAll(); mcmeta_result = DataPackUtils::processMCMeta(pack, std::move(data)); if (!mcmeta_result) { breakControl = true; return true; // mcmeta invalid } } if (!iconParsed && level != ProcessingLevel::BasicInfoOnly && f->filename() == "pack.png") { iconParsed = true; skip = false; auto data = f->readAll(); pack_png_result = DataPackUtils::processPackPNG(pack, std::move(data)); if (!pack_png_result) { breakControl = true; return true; // pack.png invalid } } if (skip) { f->skip(); } if (metaParsed && (level == ProcessingLevel::BasicInfoOnly || iconParsed)) { breakControl = true; } return true; })) { return false; // can't open zip file } if (!mcmeta_result) { qWarning() << "Data pack at" << pack->fileinfo().filePath() << "does not have a valid pack.mcmeta"; return false; // the mcmeta is not optional } if (level == ProcessingLevel::BasicInfoOnly) { return true; // only need basic info already checked } if (!pack_png_result) { qWarning() << "Data pack at" << pack->fileinfo().filePath() << "does not have a valid pack.png"; return true; // the png is optional } return true; } std::pair parseVersion(const QJsonValue& value) { if (value.isDouble()) { // Single integer -> [major, 0] return std::make_pair(value.toInt(), 0); } std::pair version; if (value.isArray()) { QJsonArray arr = value.toArray(); if (arr.size() >= 1) { version.first = arr.at(0).toInt(); } if (arr.size() >= 2) { version.second = arr.at(1).toInt(); } } return version; } // https://minecraft.wiki/w/Data_pack#pack.mcmeta // https://minecraft.wiki/w/Raw_JSON_text_format // https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta bool processMCMeta(DataPack* pack, QByteArray&& raw_data) { QJsonParseError parse_error; auto json_doc = Json::parseUntilGarbage(raw_data, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Failed to parse JSON:" << parse_error.errorString(); return false; } try { auto pack_obj = Json::requireObject(json_doc.object(), "pack", {}); int pack_format = 0; std::pair min_format; std::pair max_format; if (pack_obj.contains("pack_format")) { pack_format = pack_obj.value("pack_format").toInt(); } if (pack_obj.contains("min_format")) { min_format = parseVersion(pack_obj.value("min_format")); } if (pack_obj.contains("max_format")) { max_format = parseVersion(pack_obj.value("max_format")); } pack->setPackFormat(pack_format, min_format, max_format); pack->setDescription(DataPackUtils::processComponent(pack_obj.value("description"))); } catch (Json::JsonException& e) { qWarning() << "JsonException:" << e.what() << e.cause(); return false; } return true; } QString buildStyle(const QJsonObject& obj) { QStringList styles; if (auto color = obj["color"].toString(); !color.isEmpty()) { styles << QString("color: %1;").arg(color); } if (obj.contains("bold")) { QString weight = "normal"; if (obj["bold"].toBool()) { weight = "bold"; } styles << QString("font-weight: %1;").arg(weight); } if (obj.contains("italic")) { QString style = "normal"; if (obj["italic"].toBool()) { style = "italic"; } styles << QString("font-style: %1;").arg(style); } return styles.isEmpty() ? "" : QString("style=\"%1\"").arg(styles.join(" ")); } QString processComponent(const QJsonArray& value, bool strikethrough, bool underline) { QString result; for (auto current : value) result += processComponent(current, strikethrough, underline); return result; } QString processComponent(const QJsonObject& obj, bool strikethrough, bool underline) { underline = obj["underlined"].toBool(underline); strikethrough = obj["strikethrough"].toBool(strikethrough); QString result = obj["text"].toString(); if (underline) { result = QString("%1").arg(result); } if (strikethrough) { result = QString("%1").arg(result); } // the extra needs to be a array result += processComponent(obj["extra"].toArray(), strikethrough, underline); if (auto style = buildStyle(obj); !style.isEmpty()) { result = QString("%2").arg(style, result); } if (obj.contains("clickEvent")) { auto click_event = obj["clickEvent"].toObject(); auto action = click_event["action"].toString(); auto value = click_event["value"].toString(); if (action == "open_url" && !value.isEmpty()) { result = QString("%2").arg(value, result); } } return result; } QString processComponent(const QJsonValue& value, bool strikethrough, bool underline) { if (value.isString()) { return value.toString(); } if (value.isBool()) { return value.toBool() ? "true" : "false"; } if (value.isDouble()) { return QString::number(value.toDouble()); } if (value.isArray()) { return processComponent(value.toArray(), strikethrough, underline); } if (value.isObject()) { return processComponent(value.toObject(), strikethrough, underline); } qWarning() << "Invalid component type!"; return {}; } bool processPackPNG(const DataPack* pack, QByteArray&& raw_data) { auto img = QImage::fromData(raw_data); if (!img.isNull()) { pack->setImage(img); } else { qWarning() << "Failed to parse pack.png."; return false; } return true; } bool processPackPNG(const DataPack* pack) { auto png_invalid = [&pack]() { qWarning() << "Data pack at" << pack->fileinfo().filePath() << "does not have a valid pack.png"; return false; }; switch (pack->type()) { case ResourceType::FOLDER: { QFileInfo image_file_info(FS::PathCombine(pack->fileinfo().filePath(), "pack.png")); if (image_file_info.exists() && image_file_info.isFile()) { QFile pack_png_file(image_file_info.filePath()); if (!pack_png_file.open(QIODevice::ReadOnly)) return png_invalid(); // can't open pack.png file auto data = pack_png_file.readAll(); bool pack_png_result = DataPackUtils::processPackPNG(pack, std::move(data)); pack_png_file.close(); if (!pack_png_result) { return png_invalid(); // pack.png invalid } } else { return png_invalid(); // pack.png does not exists or is not a valid file. } return false; // not processed correctly; https://github.com/PrismLauncher/PrismLauncher/issues/1740 } case ResourceType::ZIPFILE: { MMCZip::ArchiveReader zip(pack->fileinfo().filePath()); auto f = zip.goToFile("pack.png"); if (!f) { return png_invalid(); } auto data = f->readAll(); bool pack_png_result = DataPackUtils::processPackPNG(pack, std::move(data)); if (!pack_png_result) { return png_invalid(); // pack.png invalid } return false; // not processed correctly; https://github.com/PrismLauncher/PrismLauncher/issues/1740 } default: qWarning() << "Invalid type for data pack parse task!"; return false; } } bool validate(QFileInfo file) { DataPack dp{ file }; return DataPackUtils::process(&dp, ProcessingLevel::BasicInfoOnly) && dp.valid(); } bool validateResourcePack(QFileInfo file) { ResourcePack rp{ file }; return DataPackUtils::process(&rp, ProcessingLevel::BasicInfoOnly) && rp.valid(); } } // namespace DataPackUtils LocalDataPackParseTask::LocalDataPackParseTask(int token, DataPack* dp) : Task(false), m_token(token), m_data_pack(dp) {} void LocalDataPackParseTask::executeTask() { if (!DataPackUtils::process(m_data_pack)) { emitFailed("process failed"); return; } emitSucceeded(); } PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalShaderPackParseTask.cpp0000644000175100017510000000756715224505336026761 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "LocalShaderPackParseTask.h" #include "FileSystem.h" #include "archive/ArchiveReader.h" namespace ShaderPackUtils { bool process(ShaderPack& pack, ProcessingLevel level) { switch (pack.type()) { case ResourceType::FOLDER: return ShaderPackUtils::processFolder(pack, level); case ResourceType::ZIPFILE: return ShaderPackUtils::processZIP(pack, level); default: qWarning() << "Invalid type for shader pack parse task!"; return false; } } bool processFolder(ShaderPack& pack, ProcessingLevel level) { Q_ASSERT(pack.type() == ResourceType::FOLDER); QFileInfo shaders_dir_info(FS::PathCombine(pack.fileinfo().filePath(), "shaders")); if (!shaders_dir_info.exists() || !shaders_dir_info.isDir()) { return false; // assets dir does not exists or isn't valid } pack.setPackFormat(ShaderPackFormat::VALID); if (level == ProcessingLevel::BasicInfoOnly) { return true; // only need basic info already checked } return true; // all tests passed } bool processZIP(ShaderPack& pack, ProcessingLevel level) { Q_ASSERT(pack.type() == ResourceType::ZIPFILE); MMCZip::ArchiveReader zip(pack.fileinfo().filePath()); if (!zip.collectFiles(false)) return false; // can't open zip file if (!zip.exists("/shaders")) { // assets dir does not exists at zip root, but shader packs // will sometimes be a zip file containing a folder with the // actual contents in it. This happens // e.g. when the shader pack is downloaded as code // from Github. so other than "/shaders", we // could also check for a "shaders" folder one level deep. QStringList files = zip.getFiles(); // the assumption here is that there is just one // folder with the "shader" subfolder. In case // there are multiple, the first one is picked. bool isShaderPresent = false; for (QString f : files) { if (f.contains("/shaders/", Qt::CaseInsensitive)) { isShaderPresent = true; break; } } if (!isShaderPresent) // assets dir does not exist. return false; } pack.setPackFormat(ShaderPackFormat::VALID); if (level == ProcessingLevel::BasicInfoOnly) { return true; // only need basic info already checked } return true; } bool validate(QFileInfo file) { ShaderPack sp{ file }; return ShaderPackUtils::process(sp, ProcessingLevel::BasicInfoOnly) && sp.valid(); } } // namespace ShaderPackUtils LocalShaderPackParseTask::LocalShaderPackParseTask(int token, ShaderPack& sp) : Task(false), m_token(token), m_shader_pack(sp) {} bool LocalShaderPackParseTask::abort() { m_aborted = true; return true; } void LocalShaderPackParseTask::executeTask() { if (!ShaderPackUtils::process(m_shader_pack)) { emitFailed("this is not a shader pack"); return; } if (m_aborted) emitAborted(); else emitSucceeded(); } PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalResourceParse.h0000644000175100017510000000200715224505336025345 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include "modplatform/ResourceType.h" namespace ResourceUtils { ModPlatform::ResourceType identify(QFileInfo file); } // namespace ResourceUtils PrismLauncher-11.0.3/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h0000644000175100017510000000372715224505336026652 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include "minecraft/mod/TexturePack.h" #include "tasks/Task.h" namespace TexturePackUtils { enum class ProcessingLevel { Full, BasicInfoOnly }; bool process(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processPackTXT(TexturePack& pack, QByteArray&& raw_data); bool processPackPNG(const TexturePack& pack, QByteArray&& raw_data); /// processes ONLY the pack.png (rest of the pack may be invalid) bool processPackPNG(const TexturePack& pack); /** Checks whether a file is valid as a texture pack or not. */ bool validate(QFileInfo file); } // namespace TexturePackUtils class LocalTexturePackParseTask : public Task { Q_OBJECT public: LocalTexturePackParseTask(int token, TexturePack& rp); bool canAbort() const override { return true; } bool abort() override; void executeTask() override; int token() const { return m_token; } private: int m_token; TexturePack& m_texture_pack; bool m_aborted = false; }; PrismLauncher-11.0.3/launcher/minecraft/mod/ModFolderModel.h0000644000175100017510000000702415224505336023323 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include "Mod.h" #include "ResourceFolderModel.h" #include "minecraft/Component.h" #include "minecraft/mod/Resource.h" class BaseInstance; class QFileSystemWatcher; /** * A legacy mod list. * Backed by a folder. */ class ModFolderModel : public ResourceFolderModel { Q_OBJECT public: enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, SizeColumn, SideColumn, LoadersColumn, McVersionsColumn, ReleaseTypeColumn, RequiresColumn, RequiredByColumn, NUM_COLUMNS }; ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); virtual QString id() const override { return "mods"; } QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex& parent) const override; [[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new Mod(file); } [[nodiscard]] Task* createParseTask(Resource&) override; bool isValid(); bool setResourceEnabled(const QModelIndexList& indexes, EnableAction action) override; bool deleteResources(const QModelIndexList& indexes) override; QModelIndexList getAffectedMods(const QModelIndexList& indexes, EnableAction action); RESOURCE_HELPERS(Mod) public: QStringList requiresList(QString id); QStringList requiredByList(QString id); private slots: void onParseSucceeded(int ticket, QString resource_id) override; void onParseFinished(); private: QHash> m_requiredBy; QHash> m_requires; }; PrismLauncher-11.0.3/launcher/minecraft/mod/ModFolderModel.cpp0000644000175100017510000004533515224505336023665 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ModFolderModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "minecraft/Component.h" #include "minecraft/mod/Resource.h" #include "minecraft/mod/ResourceFolderModel.h" #include "minecraft/mod/tasks/LocalModParseTask.h" #include "modplatform/ModIndex.h" #include "ui/dialogs/CustomMessageBox.h" ModFolderModel::ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) : ResourceFolderModel(QDir(dir), instance, is_indexed, create_dir, parent) { m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider", "Size", "Side", "Loaders", "Minecraft Versions", "Release Type", "Requires", "Required By" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider"), tr("Size"), tr("Side"), tr("Loaders"), tr("Minecraft Versions"), tr("Release Type"), tr("Requires"), tr("Required By") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER, SortType::SIZE, SortType::SIDE, SortType::LOADERS, SortType::MC_VERSIONS, SortType::RELEASE_TYPE, SortType::REQUIRES, SortType::REQUIRED_BY }; m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true, true, true, true, true, true, true, true, true }; connect(this, &ModFolderModel::parseFinished, this, &ModFolderModel::onParseFinished); } QVariant ModFolderModel::data(const QModelIndex& index, int role) const { if (!validateIndex(index)) return {}; int row = index.row(); int column = index.column(); switch (role) { case Qt::BackgroundRole: return rowBackground(row); case Qt::DisplayRole: switch (column) { case VersionColumn: { switch (at(row).type()) { case ResourceType::FOLDER: return tr("Folder"); case ResourceType::SINGLEFILE: return tr("File"); default: return at(row).version(); } } case SideColumn: { return at(row).side(); } case LoadersColumn: { return at(row).loaders(); } case McVersionsColumn: { return at(row).mcVersions(); } case ReleaseTypeColumn: { return at(row).releaseType(); } case RequiredByColumn: { return at(row).requiredByCount(); } case RequiresColumn: { return at(row).requiresCount(); } } break; case Qt::DecorationRole: { if (column == ImageColumn) { return at(row).icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); } break; } case Qt::SizeHintRole: if (column == ImageColumn) { return QSize(32, 32); } break; default: break; } // map the columns to the base equivilents QModelIndex mappedIndex; switch (column) { case ActiveColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::ActiveColumn); break; case NameColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::NameColumn); break; case DateColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::DateColumn); break; case ProviderColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::ProviderColumn); break; case SizeColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::SizeColumn); break; } if (mappedIndex.isValid()) { return ResourceFolderModel::data(mappedIndex, role); } return {}; } QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { switch (role) { case Qt::DisplayRole: switch (section) { case ActiveColumn: case NameColumn: case VersionColumn: case DateColumn: case ProviderColumn: case ImageColumn: case SideColumn: case LoadersColumn: case McVersionsColumn: case ReleaseTypeColumn: case SizeColumn: case RequiredByColumn: case RequiresColumn: return columnNames().at(section); default: return QVariant(); } case Qt::ToolTipRole: switch (section) { case ActiveColumn: return tr("Is the mod enabled?"); case NameColumn: return tr("The name of the mod."); case VersionColumn: return tr("The version of the mod."); case DateColumn: return tr("The date and time this mod was last changed (or added)."); case ProviderColumn: return tr("The source provider of the mod."); case SideColumn: return tr("On what environment the mod is running."); case LoadersColumn: return tr("The mod loader."); case McVersionsColumn: return tr("The supported minecraft versions."); case ReleaseTypeColumn: return tr("The release type."); case SizeColumn: return tr("The size of the mod."); case RequiredByColumn: return tr("For each mod, the number of other mods which depend on it."); case RequiresColumn: return tr("For each mod, the number of other mods it depends on."); default: return QVariant(); } default: return QVariant(); } } int ModFolderModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; } Task* ModFolderModel::createParseTask(Resource& resource) { return new LocalModParseTask(m_next_resolution_ticket, resource.type(), resource.fileinfo()); } bool ModFolderModel::isValid() { return m_dir.exists() && m_dir.isReadable(); } void ModFolderModel::onParseSucceeded(int ticket, QString mod_id) { auto iter = m_active_parse_tasks.constFind(ticket); if (iter == m_active_parse_tasks.constEnd()) return; int row = m_resources_index[mod_id]; auto parse_task = *iter; auto cast_task = static_cast(parse_task.get()); Q_ASSERT(cast_task->token() == ticket); auto resource = find(mod_id); auto result = cast_task->result(); if (result && resource) { auto* mod = static_cast(resource.get()); mod->finishResolvingWithDetails(std::move(result->details)); } emit dataChanged(index(row, RequiresColumn), index(row, RequiredByColumn)); } Mod* findById(QSet mods, QString modId) { auto found = std::find_if(mods.begin(), mods.end(), [modId](Mod* m) { return m->mod_id() == modId; }); return found != mods.end() ? *found : nullptr; } void ModFolderModel::onParseFinished() { if (hasPendingParseTasks()) { return; } auto modsList = allMods(); auto mods = QSet(modsList.begin(), modsList.end()); m_requires.clear(); m_requiredBy.clear(); auto findByProjectID = [mods](QVariant modId, ModPlatform::ResourceProvider provider) -> Mod* { auto found = std::find_if(mods.begin(), mods.end(), [modId, provider](Mod* m) { return m->metadata() && m->metadata()->provider == provider && m->metadata()->project_id == modId; }); return found != mods.end() ? *found : nullptr; }; for (auto mod : mods) { auto id = mod->mod_id(); for (auto dep : mod->dependencies()) { auto d = findById(mods, dep); if (d) { m_requires[id] << d; m_requiredBy[d->mod_id()] << mod; } } if (mod->metadata()) { for (auto dep : mod->metadata()->dependencies) { if (dep.type == ModPlatform::DependencyType::REQUIRED) { auto d = findByProjectID(dep.addonId, mod->metadata()->provider); if (d) { m_requires[id] << d; m_requiredBy[d->mod_id()] << mod; } } } } } for (auto mod : mods) { auto id = mod->mod_id(); if (mod->requiredByCount() != m_requiredBy[id].count() || mod->requiresCount() != m_requires[id].count()) { mod->setRequiredByCount(m_requiredBy[id].count()); mod->setRequiresCount(m_requires[id].count()); int row = m_resources_index[mod->internal_id()]; emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1)); } } } QSet collectMods(QSet mods, QHash> relation, std::set& seen, bool shouldBeEnabled) { QSet affectedList = {}; QSet needToCheck = {}; for (auto* mod : mods) { auto id = mod->mod_id(); if (!seen.contains(id)) { seen.insert(id); for (auto* affected : relation[id]) { auto affectedId = affected->mod_id(); if (findById(mods, affectedId) == nullptr && !seen.contains(affectedId)) { if (shouldBeEnabled != affected->enabled()) { affectedList << affected; } needToCheck << affected; } } } } // collect the affected mods until all of them are included in the list if (!needToCheck.isEmpty()) { affectedList += collectMods(needToCheck, relation, seen, shouldBeEnabled); } return affectedList; } QModelIndexList ModFolderModel::getAffectedMods(const QModelIndexList& indexes, EnableAction action) { if (indexes.isEmpty()) return {}; QModelIndexList affectedList = {}; auto affectedModsList = selectedMods(indexes); auto affectedMods = QSet(affectedModsList.begin(), affectedModsList.end()); std::set seen; switch (action) { case EnableAction::ENABLE: { affectedMods = collectMods(affectedMods, m_requires, seen, true); break; } case EnableAction::DISABLE: { affectedMods = collectMods(affectedMods, m_requiredBy, seen, false); break; } case EnableAction::TOGGLE: { return {}; // this function should not be called with TOGGLE } } for (auto affected : affectedMods) { auto affectedId = affected->mod_id(); auto row = m_resources_index[affected->internal_id()]; affectedList << index(row, 0); } return affectedList; } bool ModFolderModel::setResourceEnabled(const QModelIndexList& indexes, EnableAction action) { if (indexes.isEmpty()) return {}; auto indexedModsList = selectedMods(indexes); auto indexedMods = QSet(indexedModsList.begin(), indexedModsList.end()); QSet toEnable = {}; QSet toDisable = {}; std::set seen; switch (action) { case EnableAction::ENABLE: { toEnable = indexedMods; break; } case EnableAction::DISABLE: { toDisable = indexedMods; break; } case EnableAction::TOGGLE: { for (auto mod : indexedMods) { if (mod->enabled()) { toDisable << mod; } else { toEnable << mod; } } break; } } auto requiredToEnable = collectMods(toEnable, m_requires, seen, true); auto requiredToDisable = collectMods(toDisable, m_requiredBy, seen, false); toDisable.removeIf([toEnable](Mod* m) { return toEnable.contains(m); }); auto toList = [this](QSet mods) { QModelIndexList list; for (auto mod : mods) { auto row = m_resources_index[mod->internal_id()]; list << index(row, 0); } return list; }; if (requiredToEnable.size() > 0 || requiredToDisable.size() > 0) { QString title; QString message; QString noButton; QString yesButton; if (requiredToEnable.size() > 0 && requiredToDisable.size() > 0) { title = tr("Confirm toggle"); message = tr("Toggling these mod(s) will cause changes to other mods.\n") + tr("%n mod(s) will be enabled\n", "", requiredToEnable.size()) + tr("%n mod(s) will be disabled\n", "", requiredToDisable.size()) + tr("Do you want to automatically apply these related changes?\nIgnoring them may break the game."); noButton = tr("Only Toggle Selected"); yesButton = tr("Toggle Required Mods"); } else if (requiredToEnable.size() > 0) { title = tr("Confirm enable"); message = tr("The enabled mod(s) require %n mod(s).\n", "", requiredToEnable.size()) + tr("Would you like to enable them as well?\nIgnoring them may break the game."); noButton = tr("Only Enable Selected"); yesButton = tr("Enable Required"); } else { title = tr("Confirm disable"); message = tr("The disabled mod(s) are required by %n mod(s).\n", "", requiredToDisable.size()) + tr("Would you like to disable them as well?\nIgnoring them may break the game."); noButton = tr("Only Disable Selected"); yesButton = tr("Disable Required"); } auto box = CustomMessageBox::selectable(nullptr, title, message, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::No); box->button(QMessageBox::No)->setText(noButton); box->button(QMessageBox::Yes)->setText(yesButton); auto response = box->exec(); if (response == QMessageBox::Yes) { toEnable |= requiredToEnable; toDisable |= requiredToDisable; } else if (response == QMessageBox::Cancel) { return false; } } auto disableStatus = ResourceFolderModel::setResourceEnabled(toList(toDisable), EnableAction::DISABLE); auto enableStatus = ResourceFolderModel::setResourceEnabled(toList(toEnable), EnableAction::ENABLE); return disableStatus && enableStatus; } QStringList reqToList(QSet l) { QStringList req; for (auto m : l) { req << m->name(); } return req; } QStringList ModFolderModel::requiresList(QString id) { return reqToList(m_requires[id]); } QStringList ModFolderModel::requiredByList(QString id) { return reqToList(m_requiredBy[id]); } bool ModFolderModel::deleteResources(const QModelIndexList& indexes) { auto deleteInvalid = [](QSet& mods) { for (auto it = mods.begin(); it != mods.end();) { auto mod = *it; // the QFileInfo::exists is used instead of mod->fileinfo().exists // because the later somehow caches that the file exists if (!mod || !QFileInfo::exists(mod->fileinfo().absoluteFilePath())) { it = mods.erase(it); } else { ++it; } } }; auto rsp = ResourceFolderModel::deleteResources(indexes); for (auto mod : allMods()) { auto id = mod->mod_id(); deleteInvalid(m_requiredBy[id]); deleteInvalid(m_requires[id]); if (mod->requiredByCount() != m_requiredBy[id].count() || mod->requiresCount() != m_requires[id].count()) { mod->setRequiredByCount(m_requiredBy[id].count()); mod->setRequiresCount(m_requires[id].count()); int row = m_resources_index[mod->internal_id()]; emit dataChanged(index(row, RequiresColumn), index(row, RequiredByColumn)); } } return rsp; } PrismLauncher-11.0.3/launcher/minecraft/mod/ShaderPack.h0000644000175100017510000000377415224505336022504 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "Resource.h" /* Info: * Currently For Optifine / Iris shader packs, * could be expanded to support others should they exist? * * This class and enum are mostly here as placeholders for validating * that a shaderpack exists and is in the right format, * namely that they contain a folder named 'shaders'. * * In the technical sense it would be possible to parse files like `shaders/shaders.properties` * to get information like the available profiles but this is not all that useful without more knowledge of the * shader mod used to be able to change settings. */ #include enum class ShaderPackFormat { VALID, INVALID }; class ShaderPack : public Resource { Q_OBJECT public: using Ptr = shared_qobject_ptr; ShaderPackFormat packFormat() const { return m_pack_format; } ShaderPack(QObject* parent = nullptr) : Resource(parent) {} ShaderPack(QFileInfo file_info) : Resource(file_info) {} /** Thread-safe. */ void setPackFormat(ShaderPackFormat new_format); bool valid() const override; protected: mutable QMutex m_data_lock; ShaderPackFormat m_pack_format = ShaderPackFormat::INVALID; }; PrismLauncher-11.0.3/launcher/minecraft/mod/ResourcePackFolderModel.cpp0000644000175100017510000001521515224505336025526 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ResourcePackFolderModel.h" #include #include #include "Version.h" #include "minecraft/mod/tasks/LocalDataPackParseTask.h" ResourcePackFolderModel::ResourcePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) : ResourceFolderModel(dir, instance, is_indexed, create_dir, parent) { m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified", "Provider", "Size" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Provider"), tr("Size") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE, SortType::PROVIDER, SortType::SIZE }; m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true, true, true }; } QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const { if (!validateIndex(index)) return {}; int row = index.row(); int column = index.column(); switch (role) { case Qt::BackgroundRole: return rowBackground(row); case Qt::DisplayRole: { if (column == PackFormatColumn) { const auto& resource = at(row); return resource.packFormatStr(); } break; } case Qt::DecorationRole: { if (column == ImageColumn) { return at(row).image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); } break; } case Qt::ToolTipRole: { if (column == PackFormatColumn) { //: The string being explained by this is in the format: ID (Lower version - Upper version) return tr("The resource pack format ID, as well as the Minecraft versions it was designed for."); } break; } case Qt::SizeHintRole: if (column == ImageColumn) { return QSize(32, 32); } break; } // map the columns to the base equivilents QModelIndex mappedIndex; switch (column) { case ActiveColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::ActiveColumn); break; case NameColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::NameColumn); break; case DateColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::DateColumn); break; case ProviderColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::ProviderColumn); break; case SizeColumn: mappedIndex = index.siblingAtColumn(ResourceFolderModel::SizeColumn); break; } if (mappedIndex.isValid()) { return ResourceFolderModel::data(mappedIndex, role); } return {}; } QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { switch (role) { case Qt::DisplayRole: switch (section) { case ActiveColumn: case NameColumn: case PackFormatColumn: case DateColumn: case ImageColumn: case ProviderColumn: case SizeColumn: return columnNames().at(section); default: return {}; } case Qt::ToolTipRole: switch (section) { case ActiveColumn: return tr("Is the resource pack enabled?"); case NameColumn: return tr("The name of the resource pack."); case PackFormatColumn: //: The string being explained by this is in the format: ID (Lower version - Upper version) return tr("The resource pack format ID, as well as the Minecraft versions it was designed for."); case DateColumn: return tr("The date and time this resource pack was last changed (or added)."); case ProviderColumn: return tr("The source provider of the resource pack."); case SizeColumn: return tr("The size of the resource pack."); default: return {}; } case Qt::SizeHintRole: if (section == ImageColumn) { return QSize(64, 0); } return {}; default: return {}; } } int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; } Task* ResourcePackFolderModel::createParseTask(Resource& resource) { return new LocalDataPackParseTask(m_next_resolution_ticket, dynamic_cast(&resource)); } PrismLauncher-11.0.3/launcher/minecraft/ComponentUpdateTask.h0000644000175100017510000000201715224505336023635 0ustar runnerrunner#pragma once #include "minecraft/Component.h" #include "net/Mode.h" #include "tasks/Task.h" #include class PackProfile; struct ComponentUpdateTaskData; class ComponentUpdateTask : public Task { Q_OBJECT public: enum class Mode { Launch, Resolution }; public: explicit ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfile* list); virtual ~ComponentUpdateTask(); bool canAbort() const override; bool abort() override; Net::Mode netMode(); protected: void executeTask() override; private: void loadComponents(); /// collects components that are dependent on or dependencies of the component QList collectTreeLinked(const QString& uid); void resolveDependencies(bool checkOnly); void performUpdateActions(); void finalizeComponents(); void remoteLoadSucceeded(size_t index); void remoteLoadFailed(size_t index, const QString& msg); void checkIfAllFinished(); private: std::unique_ptr d; }; PrismLauncher-11.0.3/launcher/minecraft/ProfileUtils.h0000644000175100017510000000424215224505336022330 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "Library.h" #include "VersionFile.h" namespace ProfileUtils { using PatchOrder = QStringList; /// Read and parse a OneSix format order file bool readOverrideOrders(QString path, PatchOrder& order); /// Write a OneSix format order file bool writeOverrideOrders(QString path, const PatchOrder& order); /// Parse a version file in JSON format VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, bool requireOrder); /// Save a JSON file (in any format) bool saveJsonFile(const QJsonDocument& doc, const QString& filename); /// Remove LWJGL from a patch file. This is applied to all Mojang-like profile files. void removeLwjglFromPatch(VersionFilePtr patch); } // namespace ProfileUtils PrismLauncher-11.0.3/launcher/minecraft/WorldList.h0000644000175100017510000000634615224505336021641 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include "BaseInstance.h" #include "minecraft/World.h" class QFileSystemWatcher; class WorldList : public QAbstractListModel { Q_OBJECT public: enum Columns { NameColumn, GameModeColumn, LastPlayedColumn, SizeColumn, InfoColumn }; enum Roles { ObjectRole = Qt::UserRole + 1, FolderRole, SeedRole, NameRole, GameModeRole, LastPlayedRole, SizeRole, IconFileRole }; WorldList(const QString& dir, BaseInstance* instance); virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const { return parent.isValid() ? 0 : static_cast(size()); }; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual int columnCount(const QModelIndex& parent) const; size_t size() const { return m_worlds.size(); }; bool empty() const { return size() == 0; } World& operator[](size_t index) { return m_worlds[index]; } /// Reloads the mod list and returns true if the list changed. virtual bool update(); /// Install a world from location void installWorld(QFileInfo filename); /// Deletes the mod at the given index. virtual bool deleteWorld(int index); /// Removes the world icon, if any virtual bool resetIcon(int index); /// Deletes all the selected mods virtual bool deleteWorlds(int first, int last); /// flags, mostly to support drag&drop virtual Qt::ItemFlags flags(const QModelIndex& index) const; /// get data for drag action virtual QMimeData* mimeData(const QModelIndexList& indexes) const; /// get the supported mime types virtual QStringList mimeTypes() const; /// process data from drop action virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); /// what drag actions do we support? virtual Qt::DropActions supportedDragActions() const; /// what drop actions do we support? virtual Qt::DropActions supportedDropActions() const; void startWatching(); void stopWatching(); virtual bool isValid(); QDir dir() const { return m_dir; } QString instDirPath() const; const QList& allWorlds() const { return m_worlds; } private slots: void directoryChanged(QString path); void loadWorldsAsync(); signals: void changed(); protected: BaseInstance* m_instance; QFileSystemWatcher* m_watcher; bool m_isWatching; QDir m_dir; QList m_worlds; }; PrismLauncher-11.0.3/launcher/minecraft/VersionFilterData.cpp0000644000175100017510000000774415224505336023641 0ustar runnerrunner#include "VersionFilterData.h" #include "ParseUtils.h" VersionFilterData g_VersionFilterData = VersionFilterData(); VersionFilterData::VersionFilterData() { // 1.3.* auto libs13 = QList{ { "argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b" }, { "guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f" }, { "asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82" } }; fmlLibsMapping["1.3.2"] = libs13; // 1.4.* auto libs14 = QList{ { "argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b" }, { "guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f" }, { "asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82" }, { "bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb" } }; fmlLibsMapping["1.4"] = libs14; fmlLibsMapping["1.4.1"] = libs14; fmlLibsMapping["1.4.2"] = libs14; fmlLibsMapping["1.4.3"] = libs14; fmlLibsMapping["1.4.4"] = libs14; fmlLibsMapping["1.4.5"] = libs14; fmlLibsMapping["1.4.6"] = libs14; fmlLibsMapping["1.4.7"] = libs14; // 1.5 fmlLibsMapping["1.5"] = QList{ { "argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51" }, { "guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a" }, { "asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58" }, { "bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65" }, { "deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8" }, { "scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85" } }; // 1.5.1 fmlLibsMapping["1.5.1"] = QList{ { "argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51" }, { "guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a" }, { "asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58" }, { "bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65" }, { "deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6" }, { "scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85" } }; // 1.5.2 fmlLibsMapping["1.5.2"] = QList{ { "argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51" }, { "guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a" }, { "asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58" }, { "bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65" }, { "deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9" }, { "scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85" } }; // don't use installers for those. forgeInstallerBlacklist = QSet({ "1.5.2" }); // FIXME: remove, used for deciding when core mods should display legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00"); lwjglWhitelist = QSet{ "net.java.jinput:jinput", "net.java.jinput:jinput-platform", "net.java.jutils:jutils", "org.lwjgl.lwjgl:lwjgl", "org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform" }; java8BeginsDate = timeFromS3Time("2017-03-30T09:32:19+00:00"); java16BeginsDate = timeFromS3Time("2021-05-12T11:19:15+00:00"); java17BeginsDate = timeFromS3Time("2021-11-16T17:04:48+00:00"); } PrismLauncher-11.0.3/launcher/minecraft/VersionFilterData.h0000644000175100017510000000171615224505336023277 0ustar runnerrunner#pragma once #include #include #include #include struct FMLlib { QString filename; QString checksum; }; struct VersionFilterData { VersionFilterData(); // mapping between minecraft versions and FML libraries required QMap> fmlLibsMapping; // set of minecraft versions for which using forge installers is blacklisted QSet forgeInstallerBlacklist; // no new versions below this date will be accepted from Mojang servers QDateTime legacyCutoffDate; // Libraries that belong to LWJGL QSet lwjglWhitelist; // release date of first version to require Java 8 (17w13a) QDateTime java8BeginsDate; // release data of first version to require Java 16 (21w19a) QDateTime java16BeginsDate; // release data of first version to require Java 17 (1.18 Pre Release 2) QDateTime java17BeginsDate; }; extern VersionFilterData g_VersionFilterData; PrismLauncher-11.0.3/launcher/minecraft/Component.cpp0000644000175100017510000003041015224505336022200 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Component.h" #include #include #include #include "Application.h" #include "FileSystem.h" #include "OneSixVersionFormat.h" #include "VersionFile.h" #include "meta/Version.h" #include "minecraft/Component.h" #include "minecraft/PackProfile.h" #include const QMap Component::KNOWN_MODLOADERS = { { "net.neoforged", { ModPlatform::NeoForge, { "net.minecraftforge", "net.fabricmc.fabric-loader", "org.quiltmc.quilt-loader" } } }, { "net.minecraftforge", { ModPlatform::Forge, { "net.neoforged", "net.fabricmc.fabric-loader", "org.quiltmc.quilt-loader" } } }, { "net.fabricmc.fabric-loader", { ModPlatform::Fabric, { "net.minecraftforge", "net.neoforged", "org.quiltmc.quilt-loader" } } }, { "org.quiltmc.quilt-loader", { ModPlatform::Quilt, { "net.minecraftforge", "net.neoforged", "net.fabricmc.fabric-loader" } } }, { "com.mumfrey.liteloader", { ModPlatform::LiteLoader, {} } } }; Component::Component(PackProfile* parent, const QString& uid) { assert(parent); m_parent = parent; m_uid = uid; } Component::Component(PackProfile* parent, const QString& uid, std::shared_ptr file) { assert(parent); m_parent = parent; m_file = file; m_uid = uid; m_cachedVersion = m_file->version; m_cachedName = m_file->name; m_loaded = true; } std::shared_ptr Component::getMeta() { return m_metaVersion; } void Component::applyTo(LaunchProfile* profile) { // do not apply disabled components if (!isEnabled()) { return; } auto vfile = getVersionFile(); if (vfile) { vfile->applyTo(profile, m_parent->runtimeContext()); } else { profile->applyProblemSeverity(getProblemSeverity()); } } std::shared_ptr Component::getVersionFile() const { if (m_metaVersion) { return m_metaVersion->data(); } else { return m_file; } } std::shared_ptr Component::getVersionList() const { // FIXME: what if the metadata index isn't loaded yet? if (APPLICATION->metadataIndex()->hasUid(m_uid)) { return APPLICATION->metadataIndex()->get(m_uid); } return nullptr; } int Component::getOrder() { if (m_orderOverride) return m_order; auto vfile = getVersionFile(); if (vfile) { return vfile->order; } return 0; } void Component::setOrder(int order) { m_orderOverride = true; m_order = order; } QString Component::getID() { return m_uid; } QString Component::getName() { if (!m_cachedName.isEmpty()) return m_cachedName; return m_uid; } QString Component::getVersion() { return m_cachedVersion; } QString Component::getFilename() { return m_parent->patchFilePathForUid(m_uid); } QDateTime Component::getReleaseDateTime() { if (m_metaVersion) { return m_metaVersion->time(); } auto vfile = getVersionFile(); if (vfile) { return vfile->releaseTime; } // FIXME: fake return QDateTime::currentDateTime(); } bool Component::isEnabled() { return !canBeDisabled() || !m_disabled; } bool Component::canBeDisabled() { return isRemovable() && !m_dependencyOnly; } bool Component::setEnabled(bool state) { bool intendedDisabled = !state; if (!canBeDisabled()) { intendedDisabled = false; } if (intendedDisabled != m_disabled) { m_disabled = intendedDisabled; emit dataChanged(); return true; } return false; } bool Component::isCustom() { return m_file != nullptr; } bool Component::isCustomizable() { return m_metaVersion && getVersionFile(); } bool Component::isRemovable() { return !m_important; } bool Component::isRevertible() { if (isCustom()) { if (APPLICATION->metadataIndex()->hasUid(m_uid)) { return true; } } return false; } bool Component::isMoveable() { // HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'. return true; } bool Component::isVersionChangeable(bool wait) { auto list = getVersionList(); if (list) { if (wait) list->waitToLoad(); return list->count() != 0; } return false; } bool Component::isKnownModloader() { auto iter = KNOWN_MODLOADERS.find(m_uid); return iter != KNOWN_MODLOADERS.cend(); } QStringList Component::knownConflictingComponents() { auto iter = KNOWN_MODLOADERS.find(m_uid); if (iter != KNOWN_MODLOADERS.cend()) { return (*iter).knownConflictingComponents; } else { return {}; } } void Component::setImportant(bool state) { if (m_important != state) { m_important = state; emit dataChanged(); } } ProblemSeverity Component::getProblemSeverity() const { auto file = getVersionFile(); if (file) { auto severity = file->getProblemSeverity(); return m_componentProblemSeverity > severity ? m_componentProblemSeverity : severity; } return ProblemSeverity::Error; } const QList Component::getProblems() const { auto file = getVersionFile(); if (file) { auto problems = file->getProblems(); problems.append(m_componentProblems); return problems; } return { { ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.") } }; } void Component::addComponentProblem(ProblemSeverity severity, const QString& description) { if (severity > m_componentProblemSeverity) { m_componentProblemSeverity = severity; } m_componentProblems.append({ severity, description }); emit dataChanged(); } void Component::resetComponentProblems() { m_componentProblems.clear(); m_componentProblemSeverity = ProblemSeverity::None; emit dataChanged(); } void Component::setVersion(const QString& version) { if (version == m_version) { return; } m_version = version; if (m_loaded) { // we are loaded and potentially have state to invalidate if (m_file) { // we have a file... explicit version has been changed and there is nothing else to do. } else { // we don't have a file, therefore we are loaded with metadata m_cachedVersion = version; // see if the meta version is loaded auto metaVersion = APPLICATION->metadataIndex()->get(m_uid, version); if (metaVersion->isLoaded()) { // if yes, we can continue with that. m_metaVersion = metaVersion; } else { // if not, we need loading m_metaVersion.reset(); m_loaded = false; } updateCachedData(); } } else { // not loaded... assume it will be sorted out later by the update task } emit dataChanged(); } bool Component::customize() { if (isCustom()) { return false; } auto filename = getFilename(); if (!FS::ensureFilePathExists(filename)) { return false; } // FIXME: get rid of this try-catch. try { QSaveFile jsonFile(filename); if (!jsonFile.open(QIODevice::WriteOnly)) { return false; } auto vfile = getVersionFile(); if (!vfile) { return false; } auto document = OneSixVersionFormat::versionFileToJson(vfile); jsonFile.write(document.toJson()); if (!jsonFile.commit()) { return false; } m_file = vfile; m_metaVersion.reset(); emit dataChanged(); } catch (const Exception& error) { qWarning() << "Version could not be loaded:" << error.cause(); } return true; } bool Component::revert() { if (!isCustom()) { // already not custom return true; } auto filename = getFilename(); bool result = true; // just kill the file and reload if (QFile::exists(filename)) { result = FS::deletePath(filename); } if (result) { // file gone... m_file.reset(); // check local cache for metadata... auto version = APPLICATION->metadataIndex()->get(m_uid, m_version); if (version->isLoaded()) { m_metaVersion = version; } else { m_metaVersion.reset(); m_loaded = false; } emit dataChanged(); } return result; } /** * deep inspecting compare for requirement sets * By default, only uids are compared for set operations. * This compares all fields of the Require structs in the sets. */ static bool deepCompare(const std::set& a, const std::set& b) { // NOTE: this needs to be rewritten if the type of Meta::RequireSet changes if (a.size() != b.size()) { return false; } for (const auto& reqA : a) { const auto& iter2 = b.find(reqA); if (iter2 == b.cend()) { return false; } const auto& reqB = *iter2; if (!reqA.deepEquals(reqB)) { return false; } } return true; } void Component::updateCachedData() { auto file = getVersionFile(); if (file) { bool changed = false; if (m_cachedName != file->name) { m_cachedName = file->name; changed = true; } if (m_cachedVersion != file->version) { m_cachedVersion = file->version; changed = true; } if (m_cachedVolatile != file->m_volatile) { m_cachedVolatile = file->m_volatile; changed = true; } if (!deepCompare(m_cachedRequires, file->m_requires)) { m_cachedRequires = file->m_requires; changed = true; } if (!deepCompare(m_cachedConflicts, file->conflicts)) { m_cachedConflicts = file->conflicts; changed = true; } if (changed) { emit dataChanged(); } } else { // in case we removed all the metadata m_cachedRequires.clear(); m_cachedConflicts.clear(); emit dataChanged(); } } void Component::waitLoadMeta() { if (!m_loaded) { if (!m_metaVersion || !m_metaVersion->isLoaded()) { // wait for the loaded version from meta m_metaVersion = APPLICATION->metadataIndex()->getLoadedVersion(m_uid, m_version); } m_loaded = true; updateCachedData(); } } void Component::setUpdateAction(const UpdateAction& action) { m_updateAction = action; } UpdateAction Component::getUpdateAction() { return m_updateAction; } void Component::clearUpdateAction() { m_updateAction = UpdateAction{ UpdateActionNone{} }; } QDebug operator<<(QDebug d, const Component& comp) { QDebugStateSaver saver(d); d.nospace() << "Component(" << comp.m_uid << " : " << comp.m_cachedVersion << ")"; return d; } PrismLauncher-11.0.3/launcher/minecraft/PackProfile.cpp0000644000175100017510000010541215224505336022442 0ustar runnerrunner// SPDX-FileCopyrightText: 2022-2023 Sefa Eyeoglu // // SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022-2023 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "Application.h" #include "Exception.h" #include "FileSystem.h" #include "Json.h" #include "meta/Index.h" #include "meta/JsonFormat.h" #include "minecraft/Component.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/OneSixVersionFormat.h" #include "minecraft/ProfileUtils.h" #include "ComponentUpdateTask.h" #include "PackProfile.h" #include "PackProfile_p.h" #include "modplatform/ModIndex.h" #include "minecraft/Logging.h" #include "ui/dialogs/CustomMessageBox.h" PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel() { d.reset(new PackProfileData); d->m_instance = instance; d->m_saveTimer.setSingleShot(true); d->m_saveTimer.setInterval(5000); d->interactionDisabled = instance->isRunning(); connect(d->m_instance, &BaseInstance::runningStatusChanged, this, &PackProfile::disableInteraction); connect(&d->m_saveTimer, &QTimer::timeout, this, &PackProfile::save_internal); } PackProfile::~PackProfile() { saveNow(); } // BEGIN: component file format static const int currentComponentsFileVersion = 1; static QJsonObject componentToJsonV1(ComponentPtr component) { QJsonObject obj; // critical obj.insert("uid", component->m_uid); if (!component->m_version.isEmpty()) { obj.insert("version", component->m_version); } if (component->m_dependencyOnly) { obj.insert("dependencyOnly", true); } if (component->m_important) { obj.insert("important", true); } if (component->m_disabled) { obj.insert("disabled", true); } // cached if (!component->m_cachedVersion.isEmpty()) { obj.insert("cachedVersion", component->m_cachedVersion); } if (!component->m_cachedName.isEmpty()) { obj.insert("cachedName", component->m_cachedName); } Meta::serializeRequires(obj, &component->m_cachedRequires, "cachedRequires"); Meta::serializeRequires(obj, &component->m_cachedConflicts, "cachedConflicts"); if (component->m_cachedVolatile) { obj.insert("cachedVolatile", true); } return obj; } static ComponentPtr componentFromJsonV1(PackProfile* parent, const QString& componentJsonPattern, const QJsonObject& obj) { // critical auto uid = Json::requireString(obj.value("uid")); auto filePath = componentJsonPattern.arg(uid); auto component = makeShared(parent, uid); component->m_version = obj.value("version").toString(); component->m_dependencyOnly = obj.value("dependencyOnly").toBool(); component->m_important = obj.value("important").toBool(); // cached // TODO @RESILIENCE: ignore invalid values/structure here? component->m_cachedVersion = obj.value("cachedVersion").toString(); component->m_cachedName = obj.value("cachedName").toString(); Meta::parseRequires(obj, &component->m_cachedRequires, "cachedRequires"); Meta::parseRequires(obj, &component->m_cachedConflicts, "cachedConflicts"); component->m_cachedVolatile = obj.value("volatile").toBool(); bool disabled = obj.value("disabled").toBool(); component->setEnabled(!disabled); return component; } // Save the given component container data to a file static bool savePackProfile(const QString& filename, const ComponentContainer& container) { QJsonObject obj; obj.insert("formatVersion", currentComponentsFileVersion); QJsonArray orderArray; for (auto component : container) { orderArray.append(componentToJsonV1(component)); } obj.insert("components", orderArray); QSaveFile outFile(filename); if (!outFile.open(QFile::WriteOnly)) { qCCritical(instanceProfileC) << "Couldn't open" << outFile.fileName() << "for writing:" << outFile.errorString(); return false; } auto data = QJsonDocument(obj).toJson(QJsonDocument::Indented); if (outFile.write(data) != data.size()) { qCCritical(instanceProfileC) << "Couldn't write all the data into" << outFile.fileName() << "because:" << outFile.errorString(); return false; } if (!outFile.commit()) { qCCritical(instanceProfileC) << "Couldn't save" << outFile.fileName() << "because:" << outFile.errorString(); return false; } return true; } // Read the given file into component containers static PackProfile::Result loadPackProfile(PackProfile* parent, const QString& filename, const QString& componentJsonPattern, ComponentContainer& container) { QFile componentsFile(filename); if (!componentsFile.exists()) { auto message = QObject::tr("Components file %1 doesn't exist. This should never happen.").arg(filename); qCWarning(instanceProfileC) << message; return PackProfile::Result::Error(message); } if (!componentsFile.open(QFile::ReadOnly)) { auto message = QObject::tr("Couldn't open %1 for reading: %2").arg(componentsFile.fileName(), componentsFile.errorString()); qCCritical(instanceProfileC) << message; qCWarning(instanceProfileC) << "Ignoring overridden order"; return PackProfile::Result::Error(message); } // and it's valid JSON QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(componentsFile.readAll(), &error); if (error.error != QJsonParseError::NoError) { auto message = QObject::tr("Couldn't parse %1 as json: %2").arg(componentsFile.fileName(), error.errorString()); qCCritical(instanceProfileC) << message; qCWarning(instanceProfileC) << "Ignoring overridden order"; return PackProfile::Result::Error(message); } // and then read it and process it if all above is true. try { auto obj = Json::requireObject(doc); // check order file version. auto version = Json::requireInteger(obj.value("formatVersion")); if (version != currentComponentsFileVersion) { throw JSONValidationError(QObject::tr("Invalid component file version, expected %1").arg(currentComponentsFileVersion)); } auto orderArray = Json::requireArray(obj.value("components")); for (auto item : orderArray) { auto comp_obj = Json::requireObject(item, "Component must be an object."); container.append(componentFromJsonV1(parent, componentJsonPattern, comp_obj)); } } catch ([[maybe_unused]] const JSONValidationError& err) { auto message = QObject::tr("Couldn't parse %1 : bad file format").arg(componentsFile.fileName()); qCCritical(instanceProfileC) << message; qCWarning(instanceProfileC) << "error:" << err.what(); container.clear(); return PackProfile::Result::Error(message); } return PackProfile::Result::Success(); } // END: component file format // BEGIN: save/load logic void PackProfile::saveNow() { if (saveIsScheduled() && save_internal()) { d->m_saveTimer.stop(); } } bool PackProfile::saveIsScheduled() const { return d->dirty; } void PackProfile::buildingFromScratch() { d->loaded = true; d->dirty = true; } void PackProfile::scheduleSave() { if (!d->loaded) { qDebug() << d->m_instance->name() << "|" << "Component list should never save if it didn't successfully load"; return; } if (!d->dirty) { d->dirty = true; qDebug() << d->m_instance->name() << "|" << "Component list save is scheduled"; } d->m_saveTimer.start(); } RuntimeContext PackProfile::runtimeContext() { return d->m_instance->runtimeContext(); } QString PackProfile::componentsFilePath() const { return FS::PathCombine(d->m_instance->instanceRoot(), "mmc-pack.json"); } QString PackProfile::patchesPattern() const { return FS::PathCombine(d->m_instance->instanceRoot(), "patches", "%1.json"); } QString PackProfile::patchFilePathForUid(const QString& uid) const { return patchesPattern().arg(uid); } bool PackProfile::save_internal() { qDebug() << d->m_instance->name() << "|" << "Component list save performed now"; auto filename = componentsFilePath(); if (savePackProfile(filename, d->components)) { d->dirty = false; return true; } return false; } PackProfile::Result PackProfile::load() { auto filename = componentsFilePath(); // load the new component list and swap it with the current one... ComponentContainer newComponents; if (auto result = loadPackProfile(this, filename, patchesPattern(), newComponents); !result) { qCritical() << d->m_instance->name() << "|" << "Failed to load the component config"; return result; } // FIXME: actually use fine-grained updates, not this... beginResetModel(); // disconnect all the old components for (auto component : d->components) { disconnect(component.get(), &Component::dataChanged, this, &PackProfile::componentDataChanged); } d->components.clear(); d->componentIndex.clear(); for (auto component : newComponents) { if (d->componentIndex.contains(component->m_uid)) { qWarning() << d->m_instance->name() << "|" << "Ignoring duplicate component entry" << component->m_uid; continue; } connect(component.get(), &Component::dataChanged, this, &PackProfile::componentDataChanged); d->components.append(component); d->componentIndex[component->m_uid] = component; } endResetModel(); d->loaded = true; return Result::Success(); } PackProfile::Result PackProfile::reload(Net::Mode netmode) { // Do not reload when the update/resolve task is running. It is in control. if (d->m_updateTask) { if (d->m_updateTask->netMode() == netmode) { return Result::Success(); } // https://github.com/PrismLauncher/PrismLauncher/issues/5209 // FIXME: HACK HACK HACK disconnect(d->m_updateTask.get(), &ComponentUpdateTask::aborted, nullptr, nullptr); d->m_updateTask->abort(); d->m_updateTask.reset(); } // flush any scheduled saves to not lose state saveNow(); // FIXME: differentiate when a reapply is required by propagating state from components invalidateLaunchProfile(); if (auto result = load(); !result) { return result; } resolve(netmode); return Result::Success(); } Task::Ptr PackProfile::getCurrentTask() { return d->m_updateTask; } void PackProfile::resolve(Net::Mode netmode) { auto updateTask = new ComponentUpdateTask(ComponentUpdateTask::Mode::Resolution, netmode, this); d->m_updateTask.reset(updateTask); connect(updateTask, &ComponentUpdateTask::succeeded, this, &PackProfile::updateSucceeded); connect(updateTask, &ComponentUpdateTask::failed, this, &PackProfile::updateFailed); connect(updateTask, &ComponentUpdateTask::aborted, this, [this] { updateFailed(tr("Aborted")); }); d->m_updateTask->start(); } void PackProfile::updateSucceeded() { qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Component list update/resolve task succeeded"; d->m_updateTask.reset(); invalidateLaunchProfile(); } void PackProfile::updateFailed(const QString& error) { qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Component list update/resolve task failed. Reason:" << error; d->m_updateTask.reset(); invalidateLaunchProfile(); } // END: save/load void PackProfile::appendComponent(ComponentPtr component) { insertComponent(d->components.size(), component); } void PackProfile::insertComponent(size_t index, ComponentPtr component) { auto id = component->getID(); if (id.isEmpty()) { qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Attempt to add a component with empty ID!"; return; } if (d->componentIndex.contains(id)) { qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Attempt to add a component that is already present!"; return; } beginInsertRows(QModelIndex(), static_cast(index), static_cast(index)); d->components.insert(index, component); d->componentIndex[id] = component; endInsertRows(); connect(component.get(), &Component::dataChanged, this, &PackProfile::componentDataChanged); scheduleSave(); } void PackProfile::componentDataChanged() { auto objPtr = qobject_cast(sender()); if (!objPtr) { qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "PackProfile got dataChanged signal from a non-Component!"; return; } if (objPtr->getID() == "net.minecraft") { emit minecraftChanged(); } // figure out which one is it... in a seriously dumb way. int index = 0; for (auto component : d->components) { if (component.get() == objPtr) { emit dataChanged(createIndex(index, 0), createIndex(index, columnCount(QModelIndex()) - 1)); scheduleSave(); return; } index++; } qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "PackProfile got dataChanged signal from a Component which does not belong to it!"; } bool PackProfile::remove(const int index) { auto patch = getComponent(index); if (!patch->isRemovable()) { qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "is non-removable"; return false; } if (!removeComponent_internal(patch)) { qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "could not be removed"; return false; } beginRemoveRows(QModelIndex(), index, index); d->components.removeAt(index); d->componentIndex.remove(patch->getID()); endRemoveRows(); invalidateLaunchProfile(); scheduleSave(); return true; } bool PackProfile::remove(const QString& id) { int i = 0; for (auto patch : d->components) { if (patch->getID() == id) { return remove(i); } i++; } return false; } bool PackProfile::customize(int index) { auto patch = getComponent(index); if (!patch->isCustomizable()) { qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "is not customizable"; return false; } if (!patch->customize()) { qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "could not be customized"; return false; } invalidateLaunchProfile(); scheduleSave(); return true; } bool PackProfile::revertToBase(int index) { auto patch = getComponent(index); if (!patch->isRevertible()) { qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "is not revertible"; return false; } if (!patch->revert()) { qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Patch" << patch->getID() << "could not be reverted"; return false; } invalidateLaunchProfile(); scheduleSave(); return true; } ComponentPtr PackProfile::getComponent(const QString& id) { auto iter = d->componentIndex.find(id); if (iter == d->componentIndex.end()) { return nullptr; } return (*iter); } ComponentPtr PackProfile::getComponent(size_t index) { if (index >= static_cast(d->components.size())) { return nullptr; } return d->components[index]; } QVariant PackProfile::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); int row = index.row(); int column = index.column(); if (row < 0 || row >= d->components.size()) return QVariant(); auto patch = d->components.at(row); switch (role) { case Qt::CheckStateRole: { if (column == NameColumn) return patch->isEnabled() ? Qt::Checked : Qt::Unchecked; return QVariant(); } case Qt::DisplayRole: { switch (column) { case NameColumn: return patch->getName(); case VersionColumn: { if (patch->isCustom()) { return QString("%1 (Custom)").arg(patch->getVersion()); } else { return patch->getVersion(); } } default: return QVariant(); } } case Qt::DecorationRole: { if (column == NameColumn) { auto severity = patch->getProblemSeverity(); switch (severity) { case ProblemSeverity::Warning: return "warning"; case ProblemSeverity::Error: return "error"; default: return QVariant(); } } return QVariant(); } } return QVariant(); } bool PackProfile::setData(const QModelIndex& index, [[maybe_unused]] const QVariant& value, int role) { if (!index.isValid() || index.row() < 0 || index.row() >= rowCount(index.parent())) { return false; } if (role == Qt::CheckStateRole) { auto component = d->components[index.row()]; if (component->setEnabled(!component->isEnabled())) { return true; } } return false; } QVariant PackProfile::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { switch (section) { case NameColumn: return tr("Name"); case VersionColumn: return tr("Version"); default: return QVariant(); } } } return QVariant(); } // FIXME: zero precision mess Qt::ItemFlags PackProfile::flags(const QModelIndex& index) const { if (!index.isValid()) { return Qt::NoItemFlags; } Qt::ItemFlags outFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; int row = index.row(); if (row < 0 || row >= d->components.size()) { return Qt::NoItemFlags; } auto patch = d->components.at(row); // TODO: this will need fine-tuning later... if (patch->canBeDisabled() && !d->interactionDisabled) { outFlags |= Qt::ItemIsUserCheckable; } return outFlags; } int PackProfile::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : d->components.size(); } int PackProfile::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : NUM_COLUMNS; } void PackProfile::move(const int index, const MoveDirection direction) { int theirIndex; if (direction == MoveUp) { theirIndex = index - 1; } else { theirIndex = index + 1; } if (index < 0 || index >= d->components.size()) return; if (theirIndex >= rowCount()) theirIndex = rowCount() - 1; if (theirIndex == -1) theirIndex = rowCount() - 1; if (index == theirIndex) return; int togap = theirIndex > index ? theirIndex + 1 : theirIndex; auto from = getComponent(index); auto to = getComponent(theirIndex); if (!from || !to || !to->isMoveable() || !from->isMoveable()) { return; } beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap); d->components.swapItemsAt(index, theirIndex); endMoveRows(); invalidateLaunchProfile(); scheduleSave(); } void PackProfile::invalidateLaunchProfile() { d->m_profile.reset(); } void PackProfile::installJarMods(QStringList selectedFiles) { // FIXME: get rid of _internal installJarMods_internal(selectedFiles); } void PackProfile::installCustomJar(QString selectedFile) { // FIXME: get rid of _internal installCustomJar_internal(selectedFile); } bool PackProfile::installComponents(QStringList selectedFiles) { const QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); if (!FS::ensureFolderPathExists(patchDir)) return false; bool result = true; for (const QString& source : selectedFiles) { const QFileInfo sourceInfo(source); auto versionFile = ProfileUtils::parseJsonFile(sourceInfo, false); const QString target = FS::PathCombine(patchDir, versionFile->uid + ".json"); if (!QFile::copy(source, target)) { qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Component" << source << "could not be copied to target" << target; result = false; continue; } appendComponent(makeShared(this, versionFile->uid, versionFile)); } scheduleSave(); invalidateLaunchProfile(); return result; } void PackProfile::installAgents(QStringList selectedFiles) { // FIXME: get rid of _internal installAgents_internal(selectedFiles); } bool PackProfile::installEmpty(const QString& uid, const QString& name) { QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); if (!FS::ensureFolderPathExists(patchDir)) { return false; } auto f = std::make_shared(); f->name = name; f->uid = uid; f->version = "1"; QString patchFileName = FS::PathCombine(patchDir, uid + ".json"); QFile file(patchFileName); if (!file.open(QFile::WriteOnly)) { qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Error opening" << file.fileName() << "for reading:" << file.errorString(); return false; } file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); file.close(); appendComponent(makeShared(this, f->uid, f)); scheduleSave(); invalidateLaunchProfile(); return true; } bool PackProfile::removeComponent_internal(ComponentPtr patch) { bool ok = true; // first, remove the patch file. this ensures it's not used anymore auto fileName = patch->getFilename(); if (fileName.size()) { QFile patchFile(fileName); if (patchFile.exists() && !patchFile.remove()) { qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "File" << fileName << "could not be removed because:" << patchFile.errorString(); return false; } } // FIXME: we need a generic way of removing local resources, not just jar mods... auto preRemoveJarMod = [this](LibraryPtr jarMod) -> bool { if (!jarMod->isLocal()) { return true; } QStringList jar, temp1, temp2, temp3; jarMod->getApplicableFiles(d->m_instance->runtimeContext(), jar, temp1, temp2, temp3, d->m_instance->jarmodsPath().absolutePath()); QFileInfo finfo(jar[0]); if (finfo.exists()) { QFile jarModFile(jar[0]); if (!jarModFile.remove()) { qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "File" << jar[0] << "could not be removed because:" << jarModFile.errorString(); return false; } return true; } return true; }; auto vFile = patch->getVersionFile(); if (vFile) { auto& jarMods = vFile->jarMods; for (auto& jarmod : jarMods) { ok &= preRemoveJarMod(jarmod); } } return ok; } bool PackProfile::installJarMods_internal(QStringList filepaths) { QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); if (!FS::ensureFolderPathExists(patchDir)) { return false; } if (!FS::ensureFolderPathExists(d->m_instance->jarModsDir())) { return false; } for (auto filepath : filepaths) { QFileInfo sourceInfo(filepath); QString id = QUuid::createUuid().toString(QUuid::WithoutBraces); QString target_filename = id + ".jar"; QString target_id = "custom.jarmod." + id; QString target_name = sourceInfo.completeBaseName() + " (jar mod)"; QString finalPath = FS::PathCombine(d->m_instance->jarModsDir(), target_filename); QFileInfo targetInfo(finalPath); Q_ASSERT(!targetInfo.exists()); if (!QFile::copy(sourceInfo.absoluteFilePath(), QFileInfo(finalPath).absoluteFilePath())) { return false; } auto f = std::make_shared(); auto jarMod = std::make_shared(); jarMod->setRawName(GradleSpecifier("custom.jarmods:" + id + ":1")); jarMod->setFilename(target_filename); jarMod->setDisplayName(sourceInfo.completeBaseName()); jarMod->setHint("local"); f->jarMods.append(jarMod); f->name = target_name; f->uid = target_id; QString patchFileName = FS::PathCombine(patchDir, target_id + ".json"); QFile file(patchFileName); if (!file.open(QFile::WriteOnly)) { qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Error opening" << file.fileName() << "for reading:" << file.errorString(); return false; } file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); file.close(); appendComponent(makeShared(this, f->uid, f)); } scheduleSave(); invalidateLaunchProfile(); return true; } bool PackProfile::installCustomJar_internal(QString filepath) { QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); if (!FS::ensureFolderPathExists(patchDir)) { return false; } QString libDir = d->m_instance->getLocalLibraryPath(); if (!FS::ensureFolderPathExists(libDir)) { return false; } auto specifier = GradleSpecifier("custom:customjar:1"); QFileInfo sourceInfo(filepath); QString target_filename = specifier.getFileName(); QString target_id = specifier.artifactId(); QString target_name = sourceInfo.completeBaseName() + " (custom jar)"; QString finalPath = FS::PathCombine(libDir, target_filename); QFileInfo jarInfo(finalPath); if (jarInfo.exists()) { if (!FS::deletePath(finalPath)) { return false; } } if (!QFile::copy(filepath, finalPath)) { return false; } auto f = std::make_shared(); auto jarMod = std::make_shared(); jarMod->setRawName(specifier); jarMod->setDisplayName(sourceInfo.completeBaseName()); jarMod->setHint("local"); f->mainJar = jarMod; f->name = target_name; f->uid = target_id; QString patchFileName = FS::PathCombine(patchDir, target_id + ".json"); QFile file(patchFileName); if (!file.open(QFile::WriteOnly)) { qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Error opening" << file.fileName() << "for reading:" << file.errorString(); return false; } file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); file.close(); appendComponent(makeShared(this, f->uid, f)); scheduleSave(); invalidateLaunchProfile(); return true; } bool PackProfile::installAgents_internal(QStringList filepaths) { // FIXME code duplication const QString patchDir = FS::PathCombine(d->m_instance->instanceRoot(), "patches"); if (!FS::ensureFolderPathExists(patchDir)) return false; const QString libDir = d->m_instance->getLocalLibraryPath(); if (!FS::ensureFolderPathExists(libDir)) return false; for (const QString& source : filepaths) { const QFileInfo sourceInfo(source); const QString id = QUuid::createUuid().toString(QUuid::WithoutBraces); const QString targetBaseName = id + ".jar"; const QString targetId = "custom.agent." + id; const QString targetName = sourceInfo.completeBaseName() + " (agent)"; const QString target = FS::PathCombine(d->m_instance->getLocalLibraryPath(), targetBaseName); const QFileInfo targetInfo(target); Q_ASSERT(!targetInfo.exists()); if (!QFile::copy(source, target)) return false; auto versionFile = std::make_shared(); auto agent = std::make_shared(); agent->setRawName("custom.agents:" + id + ":1"); agent->setFilename(targetBaseName); agent->setDisplayName(sourceInfo.completeBaseName()); agent->setHint("local"); versionFile->agents.append(Agent{agent, QString()}); versionFile->name = targetName; versionFile->uid = targetId; QFile patchFile(FS::PathCombine(patchDir, targetId + ".json")); if (!patchFile.open(QFile::WriteOnly)) { qCCritical(instanceProfileC) << d->m_instance->name() << "|" << "Error opening" << patchFile.fileName() << "for reading:" << patchFile.errorString(); return false; } patchFile.write(OneSixVersionFormat::versionFileToJson(versionFile).toJson()); patchFile.close(); appendComponent(makeShared(this, versionFile->uid, versionFile)); } scheduleSave(); invalidateLaunchProfile(); return true; } std::shared_ptr PackProfile::getProfile() const { if (!d->m_profile) { try { auto profile = std::make_shared(); for (auto file : d->components) { qCDebug(instanceProfileC) << d->m_instance->name() << "|" << "Applying" << file->getID() << (file->getProblemSeverity() == ProblemSeverity::Error ? "ERROR" : "GOOD"); file->applyTo(profile.get()); } d->m_profile = profile; } catch (const Exception& error) { qCWarning(instanceProfileC) << d->m_instance->name() << "|" << "Couldn't apply profile patches because:" << error.cause(); } } return d->m_profile; } bool PackProfile::setComponentVersion(const QString& uid, const QString& version, bool important) { auto iter = d->componentIndex.find(uid); if (iter != d->componentIndex.end()) { ComponentPtr component = *iter; // set existing if (component->revert()) { // set new version auto oldVersion = component->getVersion(); component->setVersion(version); component->setImportant(important); if (important) { component->setUpdateAction(UpdateAction{ UpdateActionImportantChanged{ oldVersion } }); resolve(Net::Mode::Online); } return true; } return false; } else { // add new auto component = makeShared(this, uid); component->m_version = version; component->m_important = important; appendComponent(component); return true; } } QString PackProfile::getComponentVersion(const QString& uid) const { const auto iter = d->componentIndex.find(uid); if (iter != d->componentIndex.end()) { return (*iter)->getVersion(); } return QString(); } void PackProfile::disableInteraction(bool disable) { if (d->interactionDisabled != disable) { d->interactionDisabled = disable; auto size = d->components.size(); if (size) { emit dataChanged(index(0), index(size - 1)); } } } std::optional PackProfile::getModLoaders() { ModPlatform::ModLoaderTypes result; bool has_any_loader = false; QMapIterator i(Component::KNOWN_MODLOADERS); while (i.hasNext()) { i.next(); if (auto c = getComponent(i.key()); c != nullptr && c->isEnabled()) { result |= i.value().type; has_any_loader = true; } } if (!has_any_loader) return {}; return result; } std::optional PackProfile::getSupportedModLoaders() { auto loadersOpt = getModLoaders(); if (!loadersOpt.has_value()) return loadersOpt; auto loaders = loadersOpt.value(); // TODO: remove this or add version condition once Quilt drops official Fabric support if (loaders & ModPlatform::Quilt) loaders |= ModPlatform::Fabric; if (getComponentVersion("net.minecraft") == "1.20.1" && (loaders & ModPlatform::NeoForge)) loaders |= ModPlatform::Forge; return loaders; } QList PackProfile::getModLoadersList() { QList result; for (auto c : d->components) { if (c->isEnabled() && Component::KNOWN_MODLOADERS.contains(c->getID())) { result.append(Component::KNOWN_MODLOADERS[c->getID()].type); } } // TODO: remove this or add version condition once Quilt drops official Fabric support if (result.contains(ModPlatform::Quilt) && !result.contains(ModPlatform::Fabric)) { result.append(ModPlatform::Fabric); } if (getComponentVersion("net.minecraft") == "1.20.1" && result.contains(ModPlatform::NeoForge) && !result.contains(ModPlatform::Forge)) { result.append(ModPlatform::Forge); } return result; } PrismLauncher-11.0.3/launcher/minecraft/AssetsUtils.h0000644000175100017510000000271615224505336022176 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "net/NetJob.h" #include "net/NetRequest.h" struct AssetObject { QString getRelPath(); QUrl getUrl(); QString getLocalPath(); Net::NetRequest::Ptr getDownloadAction(); QString hash; qint64 size; }; struct AssetsIndex { NetJob::Ptr getDownloadJob(); QString id; QMap objects; bool isVirtual = false; bool mapToResources = false; }; /// FIXME: this is absolutely horrendous. REDO!!!! namespace AssetsUtils { bool loadAssetsIndexJson(const QString& id, const QString& file, AssetsIndex& index); QDir getAssetsDir(const QString& assetsId, const QString& resourcesFolder); /// Reconstruct a virtual assets folder for the given assets ID and return the folder bool reconstructAssets(QString assetsId, QString resourcesFolder); } // namespace AssetsUtils PrismLauncher-11.0.3/launcher/minecraft/GradleSpecifier.h0000644000175100017510000001152015224505336022734 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include struct GradleSpecifier { GradleSpecifier() { m_valid = false; } GradleSpecifier(const QString& value) { /* org.gradle.test.classifiers : service : 1.0 : jdk15 @ jar 0 "org.gradle.test.classifiers:service:1.0:jdk15@jar" 1 "org.gradle.test.classifiers" 2 "service" 3 "1.0" 4 "jdk15" 5 "jar" */ static const QRegularExpression s_matcher( QRegularExpression::anchoredPattern("([^:@]+):([^:@]+):([^:@]+)" "(?::([^:@]+))?" "(?:@([^:@]+))?")); QRegularExpressionMatch match = s_matcher.match(value); m_valid = match.hasMatch(); if (!m_valid) { m_invalidValue = value; return; } auto elements = match.captured(); m_groupId = match.captured(1); m_artifactId = match.captured(2); m_version = match.captured(3); m_classifier = match.captured(4); if (match.lastCapturedIndex() >= 5) { m_extension = match.captured(5); } } QString serialize() const { if (!m_valid) { return m_invalidValue; } QString retval = m_groupId + ":" + m_artifactId + ":" + m_version; if (!m_classifier.isEmpty()) { retval += ":" + m_classifier; } if (m_extension.has_value()) { retval += "@" + m_extension.value(); } return retval; } QString getFileName() const { if (!m_valid) { return QString(); } QString filename = m_artifactId + '-' + m_version; if (!m_classifier.isEmpty()) { filename += "-" + m_classifier; } filename += "." + m_extension.value_or("jar"); return filename; } QString toPath(const QString& filenameOverride = QString()) const { if (!m_valid) { return QString(); } QString filename; if (filenameOverride.isEmpty()) { filename = getFileName(); } else { filename = filenameOverride; } QString path = m_groupId; path.replace('.', '/'); path += '/' + m_artifactId + '/' + m_version + '/' + filename; return path; } inline bool valid() const { return m_valid; } inline QString version() const { return m_version; } inline QString groupId() const { return m_groupId; } inline QString artifactId() const { return m_artifactId; } inline void setClassifier(const QString& classifier) { m_classifier = classifier; } inline QString classifier() const { return m_classifier; } inline std::optional extension() const { return m_extension; } inline QString artifactPrefix() const { return m_groupId + ":" + m_artifactId; } bool matchName(const GradleSpecifier& other) const { return other.artifactId() == artifactId() && other.groupId() == groupId() && other.classifier() == classifier(); } bool operator ==(const GradleSpecifier &other) const = default; private: QString m_invalidValue; QString m_groupId; QString m_artifactId; QString m_version; QString m_classifier; std::optional m_extension; bool m_valid = false; }; PrismLauncher-11.0.3/launcher/minecraft/Logging.cpp0000644000175100017510000000170715224505336021633 0ustar runnerrunner // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "minecraft/Logging.h" Q_LOGGING_CATEGORY(instanceProfileC, "launcher.instance.profile") Q_LOGGING_CATEGORY(instanceProfileResolveC, "launcher.instance.profile.resolve") PrismLauncher-11.0.3/launcher/minecraft/MinecraftInstance.cpp0000644000175100017510000013506215224505336023644 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Jamie Mansfield * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MinecraftInstance.h" #include "Application.h" #include "BuildConfig.h" #include "Json.h" #include "QObjectPtr.h" #include "settings/Setting.h" #include "settings/SettingsObject.h" #include "FileSystem.h" #include "MMCTime.h" #include "java/JavaVersion.h" #include "launch/LaunchTask.h" #include "launch/TaskStepWrapper.h" #include "launch/steps/CheckJava.h" #include "launch/steps/LookupServerAddress.h" #include "launch/steps/PostLaunchCommand.h" #include "launch/steps/PreLaunchCommand.h" #include "launch/steps/QuitAfterGameStop.h" #include "launch/steps/TextPrint.h" #include "minecraft/launch/AutoInstallJava.h" #include "minecraft/launch/ClaimAccount.h" #include "minecraft/launch/CreateGameFolders.h" #include "minecraft/launch/EnsureAvailableMemory.h" #include "minecraft/launch/EnsureOfflineLibraries.h" #include "minecraft/launch/ExtractNatives.h" #include "minecraft/launch/LauncherPartLaunch.h" #include "minecraft/launch/ModMinecraftJar.h" #include "minecraft/launch/PrintInstanceInfo.h" #include "minecraft/launch/ReconstructAssets.h" #include "minecraft/launch/ScanModFolders.h" #include "minecraft/launch/VerifyJavaInstall.h" #include "minecraft/update/AssetUpdateTask.h" #include "minecraft/update/FoldersTask.h" #include "minecraft/update/LegacyFMLLibrariesTask.h" #include "minecraft/update/LibrariesTask.h" #include "java/JavaUtils.h" #include "icons/IconList.h" #include "mod/ModFolderModel.h" #include "mod/ResourcePackFolderModel.h" #include "mod/ShaderPackFolderModel.h" #include "mod/TexturePackFolderModel.h" #include "WorldList.h" #include "AssetsUtils.h" #include "MinecraftLoadAndCheck.h" #include "PackProfile.h" #include "tools/BaseProfiler.h" #include #include #include #include #include #ifdef Q_OS_LINUX #include "LibraryUtils.h" #endif #ifdef WITH_QTDBUS #include #endif #define IBUS "@im=ibus" [[maybe_unused]] static bool switcherooSetupGPU(QProcessEnvironment& env) { #ifdef WITH_QTDBUS if (!QDBusConnection::systemBus().isConnected()) return false; QDBusInterface switcheroo("net.hadess.SwitcherooControl", "/net/hadess/SwitcherooControl", "org.freedesktop.DBus.Properties", QDBusConnection::systemBus()); if (!switcheroo.isValid()) return false; QDBusReply reply = switcheroo.call(QStringLiteral("Get"), QStringLiteral("net.hadess.SwitcherooControl"), QStringLiteral("GPUs")); if (!reply.isValid()) return false; QDBusArgument arg = qvariant_cast(reply.value().variant()); QList gpus; arg >> gpus; for (const auto& gpu : gpus) { QString name = qvariant_cast(gpu[QStringLiteral("Name")]); bool defaultGpu = qvariant_cast(gpu[QStringLiteral("Default")]); bool discrete = qvariant_cast(gpu.value(QStringLiteral("Discrete"), !defaultGpu)); if (discrete) { QStringList envList = qvariant_cast(gpu[QStringLiteral("Environment")]); for (int i = 0; i + 1 < envList.size(); i += 2) { env.insert(envList[i], envList[i + 1]); } return true; } } #endif return false; } // all of this because keeping things compatible with deprecated old settings // if either of the settings {a, b} is true, this also resolves to true class OrSetting : public Setting { Q_OBJECT public: OrSetting(QString id, std::shared_ptr a, std::shared_ptr b) : Setting({ id }, false), m_a(a), m_b(b) {} virtual QVariant get() const { bool a = m_a->get().toBool(); bool b = m_b->get().toBool(); return a || b; } virtual void reset() {} virtual void set(QVariant value) {} private: std::shared_ptr m_a; std::shared_ptr m_b; }; MinecraftInstance::MinecraftInstance(SettingsObject* globalSettings, std::unique_ptr settings, const QString& rootDir) : BaseInstance(globalSettings, std::move(settings), rootDir) { m_components.reset(new PackProfile(this)); } MinecraftInstance::~MinecraftInstance() {} void MinecraftInstance::saveNow() { m_components->saveNow(); } void MinecraftInstance::loadSpecificSettings() { if (isSpecificSettingsLoaded()) return; // Java Settings auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false); auto argsOverride = m_settings->registerSetting("OverrideJavaArgs", false); m_settings->registerSetting("AutomaticJava", false); if (auto global_settings = globalSettings()) { m_settings->registerOverride(global_settings->getSetting("JavaPath"), locationOverride); m_settings->registerOverride(global_settings->getSetting("JvmArgs"), argsOverride); m_settings->registerOverride(global_settings->getSetting("IgnoreJavaCompatibility"), locationOverride); // special! m_settings->registerPassthrough(global_settings->getSetting("JavaSignature"), locationOverride); m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), locationOverride); m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), locationOverride); m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), locationOverride); m_settings->registerPassthrough(global_settings->getSetting("JavaVendor"), locationOverride); // Window Size auto windowSetting = m_settings->registerSetting("OverrideWindow", false); m_settings->registerOverride(global_settings->getSetting("LaunchMaximized"), windowSetting); m_settings->registerOverride(global_settings->getSetting("MinecraftWinWidth"), windowSetting); m_settings->registerOverride(global_settings->getSetting("MinecraftWinHeight"), windowSetting); // Memory auto memorySetting = m_settings->registerSetting("OverrideMemory", false); m_settings->registerOverride(global_settings->getSetting("MinMemAlloc"), memorySetting); m_settings->registerOverride(global_settings->getSetting("MaxMemAlloc"), memorySetting); m_settings->registerOverride(global_settings->getSetting("PermGen"), memorySetting); m_settings->registerOverride(global_settings->getSetting("LowMemWarning"), memorySetting); // Native library workarounds auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false); m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride); m_settings->registerOverride(global_settings->getSetting("CustomOpenALPath"), nativeLibraryWorkaroundsOverride); m_settings->registerOverride(global_settings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride); m_settings->registerOverride(global_settings->getSetting("CustomGLFWPath"), nativeLibraryWorkaroundsOverride); // Performance related options auto performanceOverride = m_settings->registerSetting("OverridePerformance", false); m_settings->registerOverride(global_settings->getSetting("EnableFeralGamemode"), performanceOverride); m_settings->registerOverride(global_settings->getSetting("EnableMangoHud"), performanceOverride); m_settings->registerOverride(global_settings->getSetting("UseDiscreteGpu"), performanceOverride); m_settings->registerOverride(global_settings->getSetting("UseZink"), performanceOverride); // Miscellaneous auto miscellaneousOverride = m_settings->registerSetting("OverrideMiscellaneous", false); m_settings->registerOverride(global_settings->getSetting("CloseAfterLaunch"), miscellaneousOverride); m_settings->registerOverride(global_settings->getSetting("QuitAfterGameStop"), miscellaneousOverride); // Legacy-related options auto legacySettings = m_settings->registerSetting("OverrideLegacySettings", false); m_settings->registerOverride(global_settings->getSetting("OnlineFixes"), legacySettings); auto envSetting = m_settings->registerSetting("OverrideEnv", false); m_settings->registerOverride(global_settings->getSetting("Env"), envSetting); m_settings->set("InstanceType", "OneSix"); } // Join server on launch, this does not have a global override m_settings->registerSetting("JoinServerOnLaunch", false); m_settings->registerSetting("JoinServerOnLaunchAddress", ""); m_settings->registerSetting("JoinWorldOnLaunch", ""); // Use account for instance, this does not have a global override m_settings->registerSetting("UseAccountForInstance", false); m_settings->registerSetting("InstanceAccountId", ""); m_settings->registerSetting("ExportName", ""); m_settings->registerSetting("ExportVersion", "1.0.0"); m_settings->registerSetting("ExportSummary", ""); m_settings->registerSetting("ExportAuthor", ""); m_settings->registerSetting("ExportOptionalFiles", true); m_settings->registerSetting("ExportRecommendedRAM"); auto dataPacksEnabled = m_settings->registerSetting("GlobalDataPacksEnabled", false); auto dataPacksPath = m_settings->registerSetting("GlobalDataPacksPath", ""); connect(dataPacksEnabled.get(), &Setting::SettingChanged, this, [this] { m_data_pack_list.reset(); }); connect(dataPacksPath.get(), &Setting::SettingChanged, this, [this] { m_data_pack_list.reset(); }); // Join server on launch, this does not have a global override m_settings->registerSetting("OverrideModDownloadLoaders", false); m_settings->registerSetting("ModDownloadLoaders", "[]"); qDebug() << "Instance-type specific settings were loaded!"; setSpecificSettingsLoaded(true); updateRuntimeContext(); } void MinecraftInstance::updateRuntimeContext() { m_runtimeContext.updateFromInstanceSettings(m_settings.get()); m_components->invalidateLaunchProfile(); } QString MinecraftInstance::typeName() const { return "Minecraft"; } PackProfile* MinecraftInstance::getPackProfile() const { return m_components.get(); } QSet MinecraftInstance::traits() const { auto components = getPackProfile(); if (!components) { return { "version-incomplete" }; } auto profile = components->getProfile(); if (!profile) { return { "version-incomplete" }; } return profile->getTraits(); } // FIXME: move UI code out of MinecraftInstance void MinecraftInstance::populateLaunchMenu(QMenu* menu) { QAction* normalLaunch = menu->addAction(tr("&Launch")); normalLaunch->setShortcut(QKeySequence::Open); QAction* normalLaunchOffline = menu->addAction(tr("Launch &Offline")); normalLaunchOffline->setShortcut(QKeySequence(tr("Ctrl+Shift+O"))); QAction* normalLaunchDemo = menu->addAction(tr("Launch &Demo")); normalLaunchDemo->setShortcut(QKeySequence(tr("Ctrl+Alt+O"))); normalLaunchDemo->setEnabled(supportsDemo()); connect(normalLaunch, &QAction::triggered, [this] { APPLICATION->launch(this); }); connect(normalLaunchOffline, &QAction::triggered, [this] { APPLICATION->launch(this, LaunchMode::Offline); }); connect(normalLaunchDemo, &QAction::triggered, [this] { APPLICATION->launch(this, LaunchMode::Demo); }); QString profilersTitle = tr("Profilers"); menu->addSeparator()->setText(profilersTitle); auto profilers = new QActionGroup(menu); profilers->setExclusive(true); connect(profilers, &QActionGroup::triggered, [this](QAction* action) { settings()->set("Profiler", action->data()); emit profilerChanged(); }); QAction* noProfilerAction = menu->addAction(tr("&No Profiler")); noProfilerAction->setData(""); noProfilerAction->setCheckable(true); noProfilerAction->setChecked(true); profilers->addAction(noProfilerAction); for (auto profiler = APPLICATION->profilers().begin(); profiler != APPLICATION->profilers().end(); profiler++) { QAction* profilerAction = menu->addAction(profiler.value()->name()); profilers->addAction(profilerAction); profilerAction->setData(profiler.key()); profilerAction->setCheckable(true); profilerAction->setChecked(settings()->get("Profiler").toString() == profiler.key()); QString error; profilerAction->setEnabled(profiler.value()->check(&error)); } } QString MinecraftInstance::gameRoot() const { QFileInfo mcDir(FS::PathCombine(instanceRoot(), "minecraft")); QFileInfo dotMCDir(FS::PathCombine(instanceRoot(), ".minecraft")); if (dotMCDir.exists() && !mcDir.exists()) return dotMCDir.filePath(); else return mcDir.filePath(); } QString MinecraftInstance::binRoot() const { return FS::PathCombine(gameRoot(), "bin"); } QString MinecraftInstance::getNativePath() const { QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/")); return natives_dir.absolutePath(); } QString MinecraftInstance::getLocalLibraryPath() const { QDir libraries_dir(FS::PathCombine(instanceRoot(), "libraries/")); return libraries_dir.absolutePath(); } bool MinecraftInstance::supportsDemo() const { Version instance_ver{ getPackProfile()->getComponentVersion("net.minecraft") }; // Demo mode was introduced in 1.3.1: https://minecraft.wiki/w/Demo_mode#History // FIXME: Due to Version constraints atm, this can't handle well non-release versions return instance_ver >= Version("1.3.1"); } QString MinecraftInstance::jarModsDir() const { QDir jarmods_dir(FS::PathCombine(instanceRoot(), "jarmods/")); return jarmods_dir.absolutePath(); } QString MinecraftInstance::modsRoot() const { return FS::PathCombine(gameRoot(), "mods"); } QString MinecraftInstance::modsCacheLocation() const { return FS::PathCombine(instanceRoot(), "mods.cache"); } QString MinecraftInstance::coreModsDir() const { return FS::PathCombine(gameRoot(), "coremods"); } QString MinecraftInstance::nilModsDir() const { return FS::PathCombine(gameRoot(), "nilmods"); } QString MinecraftInstance::dataPacksDir() { QString relativePath = settings()->get("GlobalDataPacksPath").toString(); if (relativePath.isEmpty()) relativePath = "datapacks"; return QDir(gameRoot()).filePath(relativePath); } QString MinecraftInstance::resourcePacksDir() const { return FS::PathCombine(gameRoot(), "resourcepacks"); } QString MinecraftInstance::texturePacksDir() const { return FS::PathCombine(gameRoot(), "texturepacks"); } QString MinecraftInstance::shaderPacksDir() const { return FS::PathCombine(gameRoot(), "shaderpacks"); } QString MinecraftInstance::instanceConfigFolder() const { return FS::PathCombine(gameRoot(), "config"); } QString MinecraftInstance::libDir() const { return FS::PathCombine(gameRoot(), "lib"); } QString MinecraftInstance::worldDir() const { return FS::PathCombine(gameRoot(), "saves"); } QString MinecraftInstance::resourcesDir() const { return FS::PathCombine(gameRoot(), "resources"); } QDir MinecraftInstance::librariesPath() const { return QDir::current().absoluteFilePath("libraries"); } QDir MinecraftInstance::jarmodsPath() const { return QDir(jarModsDir()); } QDir MinecraftInstance::versionsPath() const { return QDir::current().absoluteFilePath("versions"); } QStringList MinecraftInstance::getClassPath() { QStringList jars, nativeJars; auto profile = m_components->getProfile(); profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot()); return jars; } QString MinecraftInstance::getMainClass() const { auto profile = m_components->getProfile(); return profile->getMainClass(); } QStringList MinecraftInstance::getNativeJars() { QStringList jars, nativeJars; auto profile = m_components->getProfile(); profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot()); return nativeJars; } static QString replaceTokensIn(const QString& text, const QMap& with) { // TODO: does this still work?? QString result; static const QRegularExpression s_token_regexp("\\$\\{(.+)\\}", QRegularExpression::InvertedGreedinessOption); QStringList list; QRegularExpressionMatchIterator i = s_token_regexp.globalMatch(text); int lastCapturedEnd = 0; while (i.hasNext()) { QRegularExpressionMatch match = i.next(); result.append(text.mid(lastCapturedEnd, match.capturedStart())); QString key = match.captured(1); auto iter = with.find(key); if (iter != with.end()) { result.append(*iter); } lastCapturedEnd = match.capturedEnd(); } result.append(text.mid(lastCapturedEnd)); return result; } QStringList MinecraftInstance::extraArguments() { auto list = BaseInstance::extraArguments(); auto version = getPackProfile(); if (!version) return list; auto jarMods = getJarMods(); if (!jarMods.isEmpty()) { list.append({ "-Dfml.ignoreInvalidMinecraftCertificates=true", "-Dfml.ignorePatchDiscrepancies=true" }); } auto addn = m_components->getProfile()->getAddnJvmArguments(); if (!addn.isEmpty()) { QMap tokenMapping = makeProfileVarMapping(m_components->getProfile()); for (const QString& item : addn) { list.append(replaceTokensIn(item, tokenMapping)); } } auto agents = m_components->getProfile()->getAgents(); for (const auto& agent : agents) { QStringList jar, temp1, temp2, temp3; agent.library->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, getLocalLibraryPath()); list.append("-javaagent:" + jar[0] + (agent.argument.isEmpty() ? "" : "=" + agent.argument)); } { QString openALPath; QString glfwPath; if (settings()->get("UseNativeOpenAL").toBool()) { openALPath = APPLICATION->m_detectedOpenALPath; auto customPath = settings()->get("CustomOpenALPath").toString(); if (!customPath.isEmpty()) openALPath = customPath; } if (settings()->get("UseNativeGLFW").toBool()) { glfwPath = APPLICATION->m_detectedGLFWPath; auto customPath = settings()->get("CustomGLFWPath").toString(); if (!customPath.isEmpty()) glfwPath = customPath; } QFileInfo openALInfo(openALPath); QFileInfo glfwInfo(glfwPath); if (!openALPath.isEmpty() && openALInfo.exists()) list.append("-Dorg.lwjgl.openal.libname=" + openALInfo.absoluteFilePath()); if (!glfwPath.isEmpty() && glfwInfo.exists()) list.append("-Dorg.lwjgl.glfw.libname=" + glfwInfo.absoluteFilePath()); } return list; } QStringList MinecraftInstance::javaArguments() { QStringList args; args << "-Duser.language=en"; // custom args go first. we want to override them if we have our own here. args.append(extraArguments()); // OSX dock icon and name #ifdef Q_OS_MAC args << "-Xdock:icon=icon.png"; args << QString("-Xdock:name=\"%1\"").arg(windowTitle()); #endif auto traits_ = traits(); // HACK: fix issues on macOS with 1.13 snapshots // NOTE: Oracle Java option. if there are alternate jvm implementations, this would be the place to customize this for them #ifdef Q_OS_MAC if (traits_.contains("FirstThreadOnMacOS")) { args << QString("-XstartOnFirstThread"); } #endif // HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767 #ifdef Q_OS_WIN32 args << QString( "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_" "minecraft.exe.heapdump"); #endif // LWJGL2 reads `LWJGL_DISABLE_XRANDR` to force disable xrandr usage and fall back to xf86videomode. // It *SHOULD* check for the executable to exist before trying to use it for queries but it doesnt, // so WE can and force disable xrandr if it is not available. #ifdef Q_OS_LINUX // LWJGL2 is "org.lwjgl" LWJGL3 is "org.lwjgl3" if (m_components->getComponent("org.lwjgl") != nullptr && QStandardPaths::findExecutable("xrandr").isEmpty()) { args << QString("-DLWJGL_DISABLE_XRANDR=true"); } #endif int min = settings()->get("MinMemAlloc").toInt(); int max = settings()->get("MaxMemAlloc").toInt(); if (min < max) { args << QString("-Xms%1m").arg(min); args << QString("-Xmx%1m").arg(max); } else { args << QString("-Xms%1m").arg(max); args << QString("-Xmx%1m").arg(min); } // No PermGen in newer java. JavaVersion javaVersion = getJavaVersion(); if (javaVersion.requiresPermGen()) { auto permgen = settings()->get("PermGen").toInt(); if (permgen != 64) { args << QString("-XX:PermSize=%1m").arg(permgen); } } if (javaVersion.isModular() && shouldApplyOnlineFixes()) // allow reflective access to java.net - required by the skin fix args << "--add-opens" << "java.base/java.net=ALL-UNNAMED"; return args; } QString MinecraftInstance::getLauncher() { // use legacy launcher if the traits are set if (isLegacy()) return "legacy"; return "standard"; } bool MinecraftInstance::shouldApplyOnlineFixes() { return traits().contains("legacyServices") && settings()->get("OnlineFixes").toBool(); } QMap MinecraftInstance::getVariables() { QMap out; out.insert("INST_NAME", name()); out.insert("INST_ID", id()); out.insert("INST_DIR", QDir::toNativeSeparators(QDir(instanceRoot()).absolutePath())); out.insert("INST_MC_DIR", QDir::toNativeSeparators(QDir(gameRoot()).absolutePath())); out.insert("INST_JAVA", QDir::toNativeSeparators(QDir(settings()->get("JavaPath").toString()).absolutePath())); out.insert("INST_JAVA_ARGS", javaArguments().join(' ')); out.insert("NO_COLOR", "1"); #ifdef Q_OS_MACOS // get library for Steam overlay support QString steamDyldInsertLibraries = qEnvironmentVariable("STEAM_DYLD_INSERT_LIBRARIES"); if (!steamDyldInsertLibraries.isEmpty()) { out.insert("DYLD_INSERT_LIBRARIES", steamDyldInsertLibraries); } #endif return out; } QProcessEnvironment MinecraftInstance::createEnvironment() { // prepare the process environment QProcessEnvironment env = CleanEnviroment(); // export some infos auto variables = getVariables(); for (auto it = variables.begin(); it != variables.end(); ++it) { env.insert(it.key(), it.value()); } // custom env auto insertEnv = [&env](QString value) { auto envMap = Json::toMap(value); if (envMap.isEmpty()) return; for (auto iter = envMap.begin(); iter != envMap.end(); iter++) env.insert(iter.key(), iter.value().toString()); }; insertEnv(settings()->get("Env").toString()); return env; } QProcessEnvironment MinecraftInstance::createLaunchEnvironment() { // prepare the process environment QProcessEnvironment env = createEnvironment(); #ifdef Q_OS_LINUX if (settings()->get("EnableMangoHud").toBool() && APPLICATION->capabilities() & Application::SupportsMangoHud) { QStringList preloadList; if (auto value = env.value("LD_PRELOAD"); !value.isEmpty()) preloadList = value.split(QLatin1String(":")); auto mangoHudLibString = LibraryUtils::findMangoHud(); if (!mangoHudLibString.isEmpty()) { QFileInfo mangoHudLib(mangoHudLibString); QString libPath = mangoHudLib.absolutePath(); auto appendLib = [libPath, &preloadList](QString fileName) { if (QFileInfo(FS::PathCombine(libPath, fileName)).exists()) preloadList << FS::PathCombine(libPath, fileName); }; // dlsym variant is only needed for OpenGL and not included in the vulkan layer appendLib("libMangoHud_dlsym.so"); appendLib("libMangoHud_opengl.so"); appendLib("libMangoHud_shim.so"); preloadList << mangoHudLibString; } env.insert("LD_PRELOAD", preloadList.join(QLatin1String(":"))); env.insert("MANGOHUD", "1"); } if (settings()->get("UseDiscreteGpu").toBool()) { if (!switcherooSetupGPU(env)) { // Open Source Drivers env.insert("DRI_PRIME", "1"); // Proprietary Nvidia Drivers env.insert("__NV_PRIME_RENDER_OFFLOAD", "1"); env.insert("__VK_LAYER_NV_optimus", "NVIDIA_only"); env.insert("__GLX_VENDOR_LIBRARY_NAME", "nvidia"); } } if (settings()->get("UseZink").toBool()) { // taken from https://wiki.archlinux.org/title/OpenGL#OpenGL_over_Vulkan_(Zink) env.insert("__GLX_VENDOR_LIBRARY_NAME", "mesa"); env.insert("MESA_LOADER_DRIVER_OVERRIDE", "zink"); env.insert("GALLIUM_DRIVER", "zink"); env.insert("LIBGL_KOPPER_DRI2", "1"); } #endif return env; } QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) const { auto profile = m_components->getProfile(); auto args = profile->getMinecraftArguments().split(' ', Qt::SkipEmptyParts); for (auto tweaker : profile->getTweakers()) { args << "--tweakClass" << tweaker; } if (targetToJoin) { if (!targetToJoin->address.isEmpty()) { if (profile->hasTrait("feature:is_quick_play_multiplayer")) { args << "--quickPlayMultiplayer" << targetToJoin->address + ':' + QString::number(targetToJoin->port); } else { args << "--server" << targetToJoin->address; args << "--port" << QString::number(targetToJoin->port); } } else if (!targetToJoin->world.isEmpty() && profile->hasTrait("feature:is_quick_play_singleplayer")) { args << "--quickPlaySingleplayer" << targetToJoin->world; } } QMap tokenMapping = makeProfileVarMapping(profile); // yggdrasil! if (session) { // token_mapping["auth_username"] = session->username; tokenMapping["auth_session"] = session->session; tokenMapping["auth_access_token"] = session->access_token; tokenMapping["auth_player_name"] = session->player_name; tokenMapping["auth_uuid"] = session->uuid; tokenMapping["user_properties"] = session->serializeUserProperties(); tokenMapping["user_type"] = session->user_type; if (session->launchMode == LaunchMode::Demo) { args << "--demo"; } } for (int i = 0; i < args.length(); i++) { args[i] = replaceTokensIn(args[i], tokenMapping); } return args; } QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) { QString launchScript; if (!m_components) return QString(); auto profile = m_components->getProfile(); if (!profile) return QString(); auto mainClass = getMainClass(); if (!mainClass.isEmpty()) { launchScript += "mainClass " + mainClass + "\n"; } auto appletClass = profile->getAppletClass(); if (!appletClass.isEmpty()) { launchScript += "appletClass " + appletClass + "\n"; } if (targetToJoin) { if (!targetToJoin->address.isEmpty()) { launchScript += "serverAddress " + targetToJoin->address + "\n"; launchScript += "serverPort " + QString::number(targetToJoin->port) + "\n"; } else if (!targetToJoin->world.isEmpty()) { launchScript += "worldName " + targetToJoin->world + "\n"; } } // generic minecraft params for (auto param : processMinecraftArgs(session, nullptr /* When using a launch script, the server parameters are handled by it*/ )) { launchScript += "param " + param + "\n"; } // window size, title and state, legacy { QString windowParams; if (settings()->get("LaunchMaximized").toBool()) { // FIXME doesn't support maximisation if (!isLegacy()) { auto screen = QGuiApplication::primaryScreen(); auto screenGeometry = screen->availableSize(); // small hack to get the widow decorations for (auto w : QApplication::topLevelWidgets()) { auto mainWindow = qobject_cast(w); if (mainWindow) { auto m = mainWindow->windowHandle()->frameMargins(); screenGeometry = screenGeometry.shrunkBy(m); break; } } windowParams = QString("%1x%2").arg(screenGeometry.width()).arg(screenGeometry.height()); } else { windowParams = "maximized"; } } else { windowParams = QString("%1x%2").arg(settings()->get("MinecraftWinWidth").toInt()).arg(settings()->get("MinecraftWinHeight").toInt()); } launchScript += "windowTitle " + windowTitle() + "\n"; launchScript += "windowParams " + windowParams + "\n"; } // launcher info { launchScript += "launcherBrand " + BuildConfig.LAUNCHER_NAME + "\n"; launchScript += "launcherVersion " + BuildConfig.printableVersionString() + "\n"; } // instance info { launchScript += "instanceName " + name() + "\n"; launchScript += "instanceIconKey " + name() + "\n"; launchScript += "instanceIconPath icon.png\n"; // we already save a copy here } // legacy auth if (session) { launchScript += "userName " + session->player_name + "\n"; launchScript += "sessionId " + session->session + "\n"; } for (auto trait : profile->getTraits()) { launchScript += "traits " + trait + "\n"; } if (shouldApplyOnlineFixes()) launchScript += "onlineFixes true\n"; launchScript += "launcher " + getLauncher() + "\n"; // qDebug() << "Generated launch script:" << launchScript; return launchScript; } QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) { constexpr auto indent = " "; constexpr auto emptyLine = ""; QStringList out; out << "Components:"; for (int i = 0; i < m_components->rowCount(); ++i) { const auto& component = m_components->getComponent(i); out << indent + QString("%1) %2 (%3) %4").arg(QString::number(i + 1), component->getName(), component->getID(), component->getVersion()); } out << emptyLine; out << "Launcher: " + getLauncher(); out << "Main class: " + getMainClass() << emptyLine; auto profile = m_components->getProfile(); // mods and core mods auto printModList = [&out](const QString& label, ModFolderModel& model) { if (model.size()) { out << QString("%1:").arg(label); auto modList = model.allMods(); std::sort(modList.begin(), modList.end(), [](auto a, auto b) { auto aName = a->fileinfo().completeBaseName(); auto bName = b->fileinfo().completeBaseName(); return aName.localeAwareCompare(bName) < 0; }); for (auto mod : modList) { if (mod->type() == ResourceType::FOLDER) { out << u8" [🖿] " + mod->fileinfo().completeBaseName() + " (folder)"; continue; } if (mod->enabled()) { out << u8" [✔] " + mod->fileinfo().completeBaseName(); } else { out << u8" [✘] " + mod->fileinfo().completeBaseName() + " (disabled)"; } } out << emptyLine; } }; printModList("Mods", *loaderModList()); printModList("Core Mods", *coreModList()); // jar mods auto& jarMods = profile->getJarMods(); if (jarMods.size()) { out << "Jar Mods:"; for (auto& jarmod : jarMods) { auto displayname = jarmod->displayName(runtimeContext()); auto realname = jarmod->filename(runtimeContext()); if (displayname != realname) { out << indent + displayname + " (" + realname + ")"; } else { out << indent + realname; } } out << emptyLine; } // traits auto alltraits = traits(); if (alltraits.size()) { out << "Traits:"; for (auto trait : alltraits) { out << indent + trait; } out << emptyLine; } // native libraries auto settings = this->settings(); bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool(); bool nativeGLFW = settings->get("UseNativeGLFW").toBool(); if (nativeOpenAL || nativeGLFW) { if (nativeOpenAL) out << "Using system OpenAL."; if (nativeGLFW) out << "Using system GLFW."; out << emptyLine; } // libraries and class path. { out << "Libraries:"; QStringList jars, nativeJars; profile->getLibraryFiles(runtimeContext(), jars, nativeJars, getLocalLibraryPath(), binRoot()); for (auto file : jars) { out << indent + file; } out << emptyLine; out << "Native libraries:"; for (auto file : nativeJars) { out << indent + file; } out << emptyLine; } out << "Natives path:" << indent + getNativePath() << emptyLine; // minecraft arguments auto params = processMinecraftArgs(nullptr, targetToJoin); out << "Minecraft arguments:"; out << indent + params.join(' '); out << emptyLine; // window size QString windowParams; if (settings->get("LaunchMaximized").toBool()) { out << "Window size: max (if available)"; } else { auto width = settings->get("MinecraftWinWidth").toInt(); auto height = settings->get("MinecraftWinHeight").toInt(); out << "Window size: " + QString::number(width) + " x " + QString::number(height); } out << emptyLine; // environment variables const QString env = settings->get("Env").toString(); if (auto envMap = Json::toMap(env); !envMap.isEmpty()) { out << "Custom environment variables:"; for (auto [key, value] : envMap.asKeyValueRange()) { out << indent + key + "=" + value.toString(); } out << emptyLine; } return out; } QMap MinecraftInstance::createCensorFilterFromSession(AuthSessionPtr session) { if (!session) { return QMap(); } auto& sessionRef = *session.get(); QMap filter; auto addToFilter = [&filter](QString key, QString value) { if (key.trimmed().size()) { filter[key] = value; } }; if (sessionRef.session != "-") { addToFilter(sessionRef.session, tr("")); } if (sessionRef.access_token != "0") { addToFilter(sessionRef.access_token, tr("")); } addToFilter(sessionRef.uuid, tr("")); return filter; } QMap MinecraftInstance::makeProfileVarMapping(std::shared_ptr profile) const { QMap result; result["profile_name"] = name(); result["version_name"] = profile->getMinecraftVersion(); result["version_type"] = profile->getMinecraftVersionType(); QString absRootDir = QDir(gameRoot()).absolutePath(); result["game_directory"] = absRootDir; QString absAssetsDir = QDir("assets/").absolutePath(); auto assets = profile->getMinecraftAssets(); result["game_assets"] = AssetsUtils::getAssetsDir(assets->id, resourcesDir()).absolutePath(); // 1.7.3+ assets tokens result["assets_root"] = absAssetsDir; result["assets_index_name"] = assets->id; result["library_directory"] = APPLICATION->metacache()->getBasePath("libraries"); return result; } QStringList MinecraftInstance::getLogFileSearchPaths() { return { FS::PathCombine(gameRoot(), "crash-reports"), FS::PathCombine(gameRoot(), "logs"), gameRoot() }; } QString MinecraftInstance::getStatusbarDescription() { QStringList traits; if (hasVersionBroken()) { traits.append(tr("broken")); } QString mcVersion = m_components->getComponentVersion("net.minecraft"); if (mcVersion.isEmpty()) { // Load component info if needed m_components->reload(Net::Mode::Offline); mcVersion = m_components->getComponentVersion("net.minecraft"); } QString description; description.append(tr("Minecraft %1").arg(mcVersion)); if (m_settings->get("ShowGameTime").toBool()) { if (lastTimePlayed() > 0 && lastLaunch() > 0) { QDateTime lastLaunchTime = QDateTime::fromMSecsSinceEpoch(lastLaunch()); description.append( tr(", last played on %1 for %2") .arg(QLocale().toString(lastLaunchTime, QLocale::ShortFormat)) .arg(Time::prettifyDuration(lastTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool()))); } if (totalTimePlayed() > 0) { description.append( tr(", total played for %1") .arg(Time::prettifyDuration(totalTimePlayed(), APPLICATION->settings()->get("ShowGameTimeWithoutDays").toBool()))); } } if (hasCrashed()) { description.append(tr(", has crashed.")); } return description; } QList MinecraftInstance::createUpdateTask() { return { // create folders makeShared(this), // libraries download makeShared(this), // FML libraries download and copy into the instance makeShared(this), // assets update makeShared(this), }; } LaunchTask* MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) { updateRuntimeContext(); auto process = LaunchTask::create(this); auto pptr = process.get(); APPLICATION->icons()->saveIcon(iconKey(), FS::PathCombine(gameRoot(), "icon.png"), "PNG"); // print a header { process->appendStep(makeShared(pptr, "Minecraft folder is:\n " + gameRoot() + "\n", MessageLevel::Launcher)); } // create the .minecraft folder and server-resource-packs (workaround for Minecraft bug MCL-3732) { process->appendStep(makeShared(pptr)); } if (!targetToJoin && settings()->get("JoinServerOnLaunch").toBool()) { QString fullAddress = settings()->get("JoinServerOnLaunchAddress").toString(); if (!fullAddress.isEmpty()) { targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(fullAddress, false))); } else { QString world = settings()->get("JoinWorldOnLaunch").toString(); if (!world.isEmpty()) { targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(world, true))); } } } if (targetToJoin && targetToJoin->port == 25565) { // Resolve server address to join on launch auto step = makeShared(pptr); step->setLookupAddress(targetToJoin->address); step->setOutputAddressPtr(targetToJoin); process->appendStep(step); } // load meta { auto mode = session->launchMode != LaunchMode::Offline ? Net::Mode::Online : Net::Mode::Offline; process->appendStep(makeShared(pptr, makeShared(this, mode))); } // check java { process->appendStep(makeShared(pptr)); process->appendStep(makeShared(pptr)); // verify that minimum Java requirements are met process->appendStep(makeShared(pptr)); } // run pre-launch command if that's needed if (getPreLaunchCommand().size()) { auto step = makeShared(pptr); step->setWorkingDirectory(gameRoot()); process->appendStep(step); } // if we aren't in offline mode if (session->launchMode != LaunchMode::Offline) { process->appendStep(makeShared(pptr, session)); for (auto t : createUpdateTask()) { process->appendStep(makeShared(pptr, t)); } } else { process->appendStep(makeShared(pptr, this)); } // if there are any jar mods { process->appendStep(makeShared(pptr)); } // Scan mods folders for mods { process->appendStep(makeShared(pptr)); } // make sure we have enough RAM, warn the user if we don't { process->appendStep(makeShared(pptr, this)); } // print some instance info here... { process->appendStep(makeShared(pptr, session, targetToJoin)); } // extract native jars if needed { process->appendStep(makeShared(pptr)); } // reconstruct assets if needed { process->appendStep(makeShared(pptr)); } { // actually launch the game auto step = makeShared(pptr); step->setWorkingDirectory(gameRoot()); step->setAuthSession(session); step->setTargetToJoin(targetToJoin); process->appendStep(step); } // run post-exit command if that's needed if (getPostExitCommand().size()) { auto step = makeShared(pptr); step->setWorkingDirectory(gameRoot()); process->appendStep(step); } if (session) { process->setCensorFilter(createCensorFilterFromSession(session)); } if (m_settings->get("QuitAfterGameStop").toBool()) { process->appendStep(makeShared(pptr)); } m_launchProcess = std::move(process); emit launchTaskChanged(m_launchProcess.get()); return m_launchProcess.get(); } JavaVersion MinecraftInstance::getJavaVersion() { return JavaVersion(settings()->get("JavaVersion").toString()); } ModFolderModel* MinecraftInstance::loaderModList() { if (!m_loader_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed, true)); } return m_loader_mod_list.get(); } ModFolderModel* MinecraftInstance::coreModList() { if (!m_core_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed, true)); } return m_core_mod_list.get(); } ModFolderModel* MinecraftInstance::nilModList() { if (!m_nil_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), this, is_indexed, false)); } return m_nil_mod_list.get(); } ResourcePackFolderModel* MinecraftInstance::resourcePackList() { if (!m_resource_pack_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), this, is_indexed, true)); } return m_resource_pack_list.get(); } TexturePackFolderModel* MinecraftInstance::texturePackList() { if (!m_texture_pack_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), this, is_indexed, true)); } return m_texture_pack_list.get(); } ShaderPackFolderModel* MinecraftInstance::shaderPackList() { if (!m_shader_pack_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), this, is_indexed, true)); } return m_shader_pack_list.get(); } DataPackFolderModel* MinecraftInstance::dataPackList() { if (!m_data_pack_list && settings()->get("GlobalDataPacksEnabled").toBool()) { bool isIndexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); m_data_pack_list.reset(new DataPackFolderModel(dataPacksDir(), this, isIndexed, true)); } return m_data_pack_list.get(); } QList MinecraftInstance::resourceLists() { return { loaderModList(), coreModList(), nilModList(), resourcePackList(), texturePackList(), shaderPackList(), dataPackList() }; } WorldList* MinecraftInstance::worldList() { if (!m_world_list) { m_world_list.reset(new WorldList(worldDir(), this)); } return m_world_list.get(); } QList MinecraftInstance::getJarMods() const { auto profile = m_components->getProfile(); QList mods; for (auto jarmod : profile->getJarMods()) { QStringList jar, temp1, temp2, temp3; jarmod->getApplicableFiles(runtimeContext(), jar, temp1, temp2, temp3, jarmodsPath().absolutePath()); // QString filePath = jarmodsPath().absoluteFilePath(jarmod->filename(currentSystem)); mods.push_back(new Mod(QFileInfo(jar[0]))); } return mods; } #include "MinecraftInstance.moc" PrismLauncher-11.0.3/launcher/minecraft/MojangVersionFormat.h0000644000175100017510000000153715224505336023645 0ustar runnerrunner#pragma once #include #include #include #include class MojangVersionFormat { friend class OneSixVersionFormat; protected: // does not include libraries static void readVersionProperties(const QJsonObject& in, VersionFile* out); // does not include libraries static void writeVersionProperties(const VersionFile* in, QJsonObject& out); public: // version files / profile patches static VersionFilePtr versionFileFromJson(const QJsonDocument& doc, const QString& filename); static QJsonDocument versionFileToJson(const VersionFilePtr& patch); // libraries static LibraryPtr libraryFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename); static QJsonObject libraryToJson(Library* library); }; PrismLauncher-11.0.3/launcher/minecraft/MinecraftLoadAndCheck.h0000644000175100017510000000212415224505336023775 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "net/Mode.h" #include "tasks/Task.h" class MinecraftInstance; class MinecraftLoadAndCheck : public Task { Q_OBJECT public: explicit MinecraftLoadAndCheck(MinecraftInstance* inst, Net::Mode netmode); virtual ~MinecraftLoadAndCheck() = default; void executeTask() override; bool canAbort() const override; public slots: bool abort() override; private: MinecraftInstance* m_inst = nullptr; Task::Ptr m_task; Net::Mode m_netmode; }; PrismLauncher-11.0.3/launcher/minecraft/VanillaInstanceCreationTask.h0000644000175100017510000000116615224505336025274 0ustar runnerrunner#pragma once #include "InstanceCreationTask.h" #include class VanillaCreationTask final : public InstanceCreationTask { Q_OBJECT public: VanillaCreationTask(BaseVersion::Ptr version) : InstanceCreationTask(), m_version(std::move(version)) {} VanillaCreationTask(BaseVersion::Ptr version, QString loader, BaseVersion::Ptr loader_version); std::unique_ptr createInstance() override; private: // Version to update to / create of the instance. BaseVersion::Ptr m_version; bool m_using_loader = false; QString m_loader; BaseVersion::Ptr m_loader_version; }; PrismLauncher-11.0.3/launcher/minecraft/OneSixVersionFormat.h0000644000175100017510000000257115224505336023636 0ustar runnerrunner#pragma once #include #include #include #include #include class OneSixVersionFormat { public: // version files / profile patches static VersionFilePtr versionFileFromJson(const QJsonDocument& doc, const QString& filename, bool requireOrder); static QJsonDocument versionFileToJson(const VersionFilePtr& patch); // libraries static LibraryPtr libraryFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename); static QJsonObject libraryToJson(Library* library); // DEPRECATED: old 'plus' jar mods generated by the application static LibraryPtr plusJarModFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename, const QString& originalName); // new jar mods derived from libraries static LibraryPtr jarModFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename); static QJsonObject jarModtoJson(Library* jarmod); // mods, also derived from libraries static LibraryPtr modFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename); static QJsonObject modtoJson(Library* jarmod); }; PrismLauncher-11.0.3/launcher/minecraft/skins/0000755000175100017510000000000015224505336020663 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/minecraft/skins/SkinList.h0000644000175100017510000000520515224505336022576 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include "QObjectPtr.h" #include "SkinModel.h" #include "minecraft/auth/MinecraftAccount.h" class SkinList : public QAbstractListModel { Q_OBJECT public: explicit SkinList(QObject* parent, QString path, MinecraftAccountPtr acct); virtual ~SkinList() { save(); }; int getSkinIndex(const QString& key) const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex& idx, const QVariant& value, int role) override; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual QStringList mimeTypes() const override; virtual Qt::DropActions supportedDropActions() const override; virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override; bool deleteSkin(const QString& key, bool trash); void installSkins(const QStringList& iconFiles); QString installSkin(const QString& file, const QString& name = {}); const SkinModel* skin(const QString& key) const; SkinModel* skin(const QString& key); void startWatching(); void stopWatching(); QString getDir() const { return m_dir.absolutePath(); } void save(); int getSelectedAccountSkin(); void updateSkin(SkinModel* s); private: // hide copy constructor SkinList(const SkinList&) = delete; // hide assign op SkinList& operator=(const SkinList&) = delete; protected slots: void directoryChanged(const QString& path); void fileChanged(const QString& path); bool update(); private: shared_qobject_ptr m_watcher; bool m_isWatching; QList m_skinList; QDir m_dir; MinecraftAccountPtr m_acct; }; PrismLauncher-11.0.3/launcher/minecraft/skins/SkinUpload.cpp0000644000175100017510000000577615224505336023457 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "SkinUpload.h" #include #include "FileSystem.h" #include "net/DummySink.h" #include "net/RawHeaderProxy.h" SkinUpload::SkinUpload(QString path, QString variant) : NetRequest(), m_path(path), m_variant(variant) { logCat = taskMCSkinsLogC; } QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) { QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this); QHttpPart skin; skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png")); skin.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\"skin.png\"")); skin.setBody(FS::read(m_path)); QHttpPart model; model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"variant\"")); model.setBody(m_variant.toUtf8()); multiPart->append(skin); multiPart->append(model); setStatus(tr("Uploading skin")); return m_network->post(request, multiPart); } SkinUpload::Ptr SkinUpload::make(QString token, QString path, QString variant) { auto up = makeShared(path, variant); up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins"); up->setObjectName(QString("BYTES:") + up->m_url.toString()); up->m_sink.reset(new Net::DummySink()); up->addHeaderProxy(std::make_unique(QList{ { "Authorization", QString("Bearer %1").arg(token).toLocal8Bit() }, })); return up; } PrismLauncher-11.0.3/launcher/minecraft/skins/CapeChange.cpp0000644000175100017510000000505315224505336023350 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "CapeChange.h" #include #include #include "net/RawHeaderProxy.h" CapeChange::CapeChange(QString cape) : NetRequest(), m_capeId(cape) { logCat = taskMCSkinsLogC; } QNetworkReply* CapeChange::getReply(QNetworkRequest& request) { if (m_capeId.isEmpty()) { setStatus(tr("Removing cape")); return m_network->deleteResource(request); } else { setStatus(tr("Equipping cape")); return m_network->put(request, QString("{\"capeId\":\"%1\"}").arg(m_capeId).toUtf8()); } } CapeChange::Ptr CapeChange::make(QString token, QString capeId) { auto up = makeShared(capeId); up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active"); up->setObjectName(QString("BYTES:") + up->m_url.toString()); up->m_sink.reset(new Net::DummySink()); up->addHeaderProxy(std::make_unique(QList{ { "Authorization", QString("Bearer %1").arg(token).toLocal8Bit() }, })); return up; } PrismLauncher-11.0.3/launcher/minecraft/skins/CapeChange.h0000644000175100017510000000220415224505336023010 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "net/NetRequest.h" class CapeChange : public Net::NetRequest { Q_OBJECT public: using Ptr = shared_qobject_ptr; CapeChange(QString capeId); virtual ~CapeChange() = default; static CapeChange::Ptr make(QString token, QString capeId); protected: virtual QNetworkReply* getReply(QNetworkRequest&) override; private: QString m_capeId; }; PrismLauncher-11.0.3/launcher/minecraft/skins/SkinDelete.h0000644000175100017510000000210315224505336023057 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "net/NetRequest.h" class SkinDelete : public Net::NetRequest { Q_OBJECT public: using Ptr = shared_qobject_ptr; SkinDelete(); virtual ~SkinDelete() = default; static SkinDelete::Ptr make(QString token); protected: virtual QNetworkReply* getReply(QNetworkRequest&) override; }; PrismLauncher-11.0.3/launcher/minecraft/skins/SkinModel.h0000644000175100017510000000334715224505336022730 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include class SkinModel { public: enum Model { CLASSIC, SLIM }; SkinModel() = default; SkinModel(QString path); SkinModel(QDir skinDir, QJsonObject obj); virtual ~SkinModel() = default; QString name() const; QString getModelString() const; bool isValid() const; QString getPath() const { return m_path; } QImage getTexture() const { return m_texture; } QImage getPreview() const { return m_preview; } QString getCapeId() const { return m_capeId; } Model getModel() const { return m_model; } QString getURL() const { return m_url; } bool rename(QString newName); void setCapeId(QString capeID) { m_capeId = capeID; } void setModel(Model model); void setURL(QString url) { m_url = url; } void refresh(); QJsonObject toJSON() const; private: QString m_path; QImage m_texture; QImage m_preview; QString m_capeId; Model m_model; QString m_url; }; PrismLauncher-11.0.3/launcher/minecraft/skins/SkinUpload.h0000644000175100017510000000235415224505336023111 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "net/NetRequest.h" class SkinUpload : public Net::NetRequest { Q_OBJECT public: using Ptr = shared_qobject_ptr; // Note this class takes ownership of the file. SkinUpload(QString path, QString variant); virtual ~SkinUpload() = default; static SkinUpload::Ptr make(QString token, QString path, QString variant); protected: virtual QNetworkReply* getReply(QNetworkRequest&) override; private: QString m_path; QString m_variant; }; PrismLauncher-11.0.3/launcher/minecraft/skins/SkinModel.cpp0000644000175100017510000001663415224505336023266 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2025 Trial97 * Copyright (c) 2025 Rinth, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "SkinModel.h" #include #include #include "FileSystem.h" static void setAlpha(QImage& image, const QRect& region, const int alpha) { for (int y = region.top(); y < region.bottom(); ++y) { QRgb* line = reinterpret_cast(image.scanLine(y)); for (int x = region.left(); x < region.right(); ++x) { QRgb pixel = line[x]; line[x] = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), alpha); } } } static void doNotchTransparencyHack(QImage& image) { for (int y = 0; y < 32; y++) { QRgb* line = reinterpret_cast(image.scanLine(y)); for (int x = 32; x < 64; x++) { if (qAlpha(line[x]) < 128) { return; } } } setAlpha(image, { 32, 0, 32, 32 }, 0); } static QImage improveSkin(QImage skin) { int height = skin.height(); int width = skin.width(); if (width != 64 || (height != 32 && height != 64)) { // this is no minecraft skin return skin; } // It seems some older skins may use this format, which can't be drawn onto // https://github.com/PrismLauncher/PrismLauncher/issues/4032 // https://doc.qt.io/qt-6/qpainter.html#begin if (skin.format() <= QImage::Format_Indexed8 || !skin.hasAlphaChannel()) { skin = skin.convertToFormat(QImage::Format_ARGB32); } auto isLegacy = height == 32; // old format if (isLegacy) { auto newSkin = QImage(QSize(64, 64), skin.format()); newSkin.fill(Qt::transparent); QPainter p(&newSkin); p.drawImage(0, 0, skin); auto copyRect = [&p, &newSkin](int startX, int startY, int offsetX, int offsetY, int sizeX, int sizeY) { QImage region = newSkin.copy(startX, startY, sizeX, sizeY); region = region.mirrored(true, false); p.drawImage(startX + offsetX, startY + offsetY, region); }; static const struct { int x; int y; int offsetX; int offsetY; int width; int height; } faces[] = { { 4, 16, 16, 32, 4, 4 }, { 8, 16, 16, 32, 4, 4 }, { 0, 20, 24, 32, 4, 12 }, { 4, 20, 16, 32, 4, 12 }, { 8, 20, 8, 32, 4, 12 }, { 12, 20, 16, 32, 4, 12 }, { 44, 16, -8, 32, 4, 4 }, { 48, 16, -8, 32, 4, 4 }, { 40, 20, 0, 32, 4, 12 }, { 44, 20, -8, 32, 4, 12 }, { 48, 20, -16, 32, 4, 12 }, { 52, 20, -8, 32, 4, 12 }, }; for (const auto& face : faces) { copyRect(face.x, face.y, face.offsetX, face.offsetY, face.width, face.height); } doNotchTransparencyHack(newSkin); skin = newSkin; } static const QRect opaqueParts[] = { { 0, 0, 32, 16 }, { 0, 16, 64, 16 }, { 16, 48, 32, 16 }, }; for (const auto& p : opaqueParts) { setAlpha(skin, p, 255); } return skin; } static QImage getSkin(const QString path) { return improveSkin(QImage(path)); } static QImage generatePreviews(QImage texture, bool slim) { QImage preview(36, 36, QImage::Format_ARGB32); preview.fill(Qt::transparent); QPainter paint(&preview); // head paint.drawImage(4, 2, texture.copy(8, 8, 8, 8)); paint.drawImage(4, 2, texture.copy(40, 8, 8, 8)); // torso paint.drawImage(4, 10, texture.copy(20, 20, 8, 12)); paint.drawImage(4, 10, texture.copy(20, 36, 8, 12)); // right leg paint.drawImage(4, 22, texture.copy(4, 20, 4, 12)); paint.drawImage(4, 22, texture.copy(4, 36, 4, 12)); // left leg paint.drawImage(8, 22, texture.copy(20, 52, 4, 12)); paint.drawImage(8, 22, texture.copy(4, 52, 4, 12)); auto armWidth = slim ? 3 : 4; auto armPosX = slim ? 1 : 0; // right arm paint.drawImage(armPosX, 10, texture.copy(44, 20, armWidth, 12)); paint.drawImage(armPosX, 10, texture.copy(44, 36, armWidth, 12)); // left arm paint.drawImage(12, 10, texture.copy(36, 52, armWidth, 12)); paint.drawImage(12, 10, texture.copy(52, 52, armWidth, 12)); // back // head paint.drawImage(24, 2, texture.copy(24, 8, 8, 8)); paint.drawImage(24, 2, texture.copy(56, 8, 8, 8)); // torso paint.drawImage(24, 10, texture.copy(32, 20, 8, 12)); paint.drawImage(24, 10, texture.copy(32, 36, 8, 12)); // right leg paint.drawImage(24, 22, texture.copy(12, 20, 4, 12)); paint.drawImage(24, 22, texture.copy(12, 36, 4, 12)); // left leg paint.drawImage(28, 22, texture.copy(28, 52, 4, 12)); paint.drawImage(28, 22, texture.copy(12, 52, 4, 12)); // right arm paint.drawImage(armPosX + 20, 10, texture.copy(48 + armWidth, 20, armWidth, 12)); paint.drawImage(armPosX + 20, 10, texture.copy(48 + armWidth, 36, armWidth, 12)); // left arm paint.drawImage(32, 10, texture.copy(40 + armWidth, 52, armWidth, 12)); paint.drawImage(32, 10, texture.copy(56 + armWidth, 52, armWidth, 12)); return preview; } SkinModel::SkinModel(QString path) : m_path(path), m_texture(getSkin(path)), m_model(Model::CLASSIC) { m_preview = generatePreviews(m_texture, false); } SkinModel::SkinModel(QDir skinDir, QJsonObject obj) : m_capeId(obj["capeId"].toString()), m_model(Model::CLASSIC), m_url(obj["url"].toString()) { auto name = obj["name"].toString(); if (auto model = obj["model"].toString(); model == "SLIM") { m_model = Model::SLIM; } m_path = skinDir.absoluteFilePath(name) + ".png"; m_texture = getSkin(m_path); m_preview = generatePreviews(m_texture, m_model == Model::SLIM); } QString SkinModel::name() const { return QFileInfo(m_path).completeBaseName(); } bool SkinModel::rename(QString newName) { auto info = QFileInfo(m_path); auto new_path = FS::PathCombine(info.absolutePath(), newName + ".png"); if (QFileInfo::exists(new_path)) { return false; } m_path = new_path; return FS::move(info.absoluteFilePath(), m_path); } QJsonObject SkinModel::toJSON() const { QJsonObject obj; obj["name"] = name(); obj["capeId"] = m_capeId; obj["url"] = m_url; obj["model"] = getModelString(); return obj; } QString SkinModel::getModelString() const { switch (m_model) { case CLASSIC: return "CLASSIC"; case SLIM: return "SLIM"; } return {}; } bool SkinModel::isValid() const { return !m_texture.isNull() && (m_texture.size().height() == 32 || m_texture.size().height() == 64) && m_texture.size().width() == 64; } void SkinModel::refresh() { m_texture = getSkin(m_path); m_preview = generatePreviews(m_texture, m_model == Model::SLIM); } void SkinModel::setModel(Model model) { m_model = model; m_preview = generatePreviews(m_texture, m_model == Model::SLIM); } PrismLauncher-11.0.3/launcher/minecraft/skins/SkinDelete.cpp0000644000175100017510000000434515224505336023424 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "SkinDelete.h" #include #include "net/RawHeaderProxy.h" SkinDelete::SkinDelete() : NetRequest() { logCat = taskMCSkinsLogC; } QNetworkReply* SkinDelete::getReply(QNetworkRequest& request) { setStatus(tr("Deleting skin")); return m_network->deleteResource(request); } SkinDelete::Ptr SkinDelete::make(QString token) { auto up = makeShared(); up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active"); up->m_sink.reset(new Net::DummySink()); up->addHeaderProxy(std::make_unique(QList{ { "Authorization", QString("Bearer %1").arg(token).toLocal8Bit() }, })); return up; } PrismLauncher-11.0.3/launcher/minecraft/skins/SkinList.cpp0000644000175100017510000002761415224505336023141 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "SkinList.h" #include #include #include "FileSystem.h" #include "Json.h" #include "minecraft/skins/SkinModel.h" SkinList::SkinList(QObject* parent, QString path, MinecraftAccountPtr acct) : QAbstractListModel(parent), m_acct(acct) { FS::ensureFolderPathExists(m_dir.absolutePath()); m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); m_watcher.reset(new QFileSystemWatcher(this)); m_isWatching = false; connect(m_watcher.get(), &QFileSystemWatcher::directoryChanged, this, &SkinList::directoryChanged); connect(m_watcher.get(), &QFileSystemWatcher::fileChanged, this, &SkinList::fileChanged); directoryChanged(path); } void SkinList::startWatching() { if (m_isWatching) { return; } update(); m_isWatching = m_watcher->addPath(m_dir.absolutePath()); if (m_isWatching) { qDebug() << "Started watching" << m_dir.absolutePath(); } else { qDebug() << "Failed to start watching" << m_dir.absolutePath(); } } void SkinList::stopWatching() { save(); if (!m_isWatching) { return; } m_isWatching = !m_watcher->removePath(m_dir.absolutePath()); if (!m_isWatching) { qDebug() << "Stopped watching" << m_dir.absolutePath(); } else { qDebug() << "Failed to stop watching" << m_dir.absolutePath(); } } bool SkinList::update() { QList newSkins; m_dir.refresh(); auto manifestInfo = QFileInfo(m_dir.absoluteFilePath("index.json")); if (manifestInfo.exists()) { try { auto doc = Json::requireDocument(manifestInfo.absoluteFilePath(), "SkinList JSON file"); const auto root = doc.object(); auto skins = root["skins"].toArray(); for (auto jSkin : skins) { SkinModel s(m_dir, jSkin.toObject()); if (s.isValid()) { newSkins << s; } } } catch (const Exception& e) { qCritical() << "Couldn't load skins json:" << e.cause(); } } bool needsSave = false; const auto& skin = m_acct->accountData()->minecraftProfile.skin; if (!skin.url.isEmpty() && !skin.data.isEmpty()) { QPixmap skinTexture; SkinModel* nskin = nullptr; for (auto i = 0; i < newSkins.size(); i++) { if (newSkins[i].getURL() == skin.url) { nskin = &newSkins[i]; break; } } if (!nskin) { auto name = m_acct->profileName() + ".png"; if (QFileInfo(m_dir.absoluteFilePath(name)).exists()) { name = QUrl(skin.url).fileName() + ".png"; } auto path = m_dir.absoluteFilePath(name); if (skinTexture.loadFromData(skin.data, "PNG") && skinTexture.save(path)) { SkinModel s(path); s.setModel(skin.variant.toUpper() == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); s.setCapeId(m_acct->accountData()->minecraftProfile.currentCape); s.setURL(skin.url); newSkins << s; needsSave = true; } } else { nskin->setCapeId(m_acct->accountData()->minecraftProfile.currentCape); nskin->setModel(skin.variant.toUpper() == "SLIM" ? SkinModel::SLIM : SkinModel::CLASSIC); } } auto folderContents = m_dir.entryInfoList(); // if there are any untracked files... for (QFileInfo entry : folderContents) { if (!entry.isFile() || entry.suffix() != "png") continue; SkinModel w(entry.absoluteFilePath()); if (w.isValid()) { auto add = true; for (auto s : newSkins) { if (s.name() == w.name()) { add = false; break; } } if (add) { newSkins.append(w); needsSave = true; } } } std::sort(newSkins.begin(), newSkins.end(), [](const SkinModel& a, const SkinModel& b) { return a.getPath().localeAwareCompare(b.getPath()) < 0; }); beginResetModel(); m_skinList.swap(newSkins); endResetModel(); if (needsSave) save(); return true; } void SkinList::directoryChanged(const QString& path) { QDir new_dir(path); if (!new_dir.exists()) if (!FS::ensureFolderPathExists(new_dir.absolutePath())) return; if (m_dir.absolutePath() != new_dir.absolutePath()) { m_dir.setPath(path); m_dir.refresh(); if (m_isWatching) stopWatching(); startWatching(); } update(); } void SkinList::fileChanged(const QString& path) { qDebug() << "Checking" << path; QFileInfo checkfile(path); if (!checkfile.exists()) return; for (int i = 0; i < m_skinList.count(); i++) { if (m_skinList[i].getPath() == checkfile.absoluteFilePath()) { m_skinList[i].refresh(); dataChanged(index(i), index(i)); break; } } } QStringList SkinList::mimeTypes() const { return { "text/uri-list" }; } Qt::DropActions SkinList::supportedDropActions() const { return Qt::CopyAction; } bool SkinList::dropMimeData(const QMimeData* data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex& parent) { if (action == Qt::IgnoreAction) return true; // check if the action is supported if (!data || !(action & supportedDropActions())) return false; // files dropped from outside? if (data->hasUrls()) { auto urls = data->urls(); QStringList skinFiles; for (auto url : urls) { // only local files may be dropped... if (!url.isLocalFile()) continue; skinFiles << url.toLocalFile(); } installSkins(skinFiles); return true; } return false; } Qt::ItemFlags SkinList::flags(const QModelIndex& index) const { Qt::ItemFlags f = Qt::ItemIsDropEnabled | QAbstractListModel::flags(index); if (index.isValid()) { f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); } return f; } QVariant SkinList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); int row = index.row(); if (row < 0 || row >= m_skinList.size()) return QVariant(); auto skin = m_skinList[row]; switch (role) { case Qt::DecorationRole: { auto preview = skin.getPreview(); if (preview.isNull()) { preview = skin.getTexture(); } return preview; } case Qt::DisplayRole: return skin.name(); case Qt::UserRole: return skin.name(); case Qt::EditRole: return skin.name(); default: return QVariant(); } } int SkinList::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_skinList.size(); } void SkinList::installSkins(const QStringList& iconFiles) { for (QString file : iconFiles) installSkin(file); } QString getUniqueFile(const QString& root, const QString& file) { auto result = FS::PathCombine(root, file); if (!QFileInfo::exists(result)) { return result; } QString baseName = QFileInfo(file).completeBaseName(); QString extension = QFileInfo(file).suffix(); int tries = 0; while (QFileInfo::exists(result)) { if (++tries > 256) return {}; QString key = QString("%1%2.%3").arg(baseName).arg(tries).arg(extension); result = FS::PathCombine(root, key); } return result; } QString SkinList::installSkin(const QString& file, const QString& name) { if (file.isEmpty()) return tr("Path is empty."); QFileInfo fileinfo(file); if (!fileinfo.exists()) return tr("File doesn't exist."); if (!fileinfo.isFile()) return tr("Not a file."); if (!fileinfo.isReadable()) return tr("File is not readable."); if (fileinfo.suffix() != "png" && !SkinModel(fileinfo.absoluteFilePath()).isValid()) return tr("Skin images must be 64x64 or 64x32 pixel PNG files."); QString target = getUniqueFile(m_dir.absolutePath(), name.isEmpty() ? fileinfo.fileName() : name); return QFile::copy(file, target) ? "" : tr("Unable to copy file"); } int SkinList::getSkinIndex(const QString& key) const { for (int i = 0; i < m_skinList.count(); i++) { if (m_skinList[i].name() == key) { return i; } } return -1; } const SkinModel* SkinList::skin(const QString& key) const { int idx = getSkinIndex(key); if (idx == -1) return nullptr; return &m_skinList[idx]; } SkinModel* SkinList::skin(const QString& key) { int idx = getSkinIndex(key); if (idx == -1) return nullptr; return &m_skinList[idx]; } bool SkinList::deleteSkin(const QString& key, bool trash) { int idx = getSkinIndex(key); if (idx != -1) { auto s = m_skinList[idx]; if (trash) { if (FS::trash(s.getPath(), nullptr)) { m_skinList.remove(idx); save(); return true; } } else if (QFile::remove(s.getPath())) { m_skinList.remove(idx); save(); return true; } } return false; } void SkinList::save() { QJsonObject doc; QJsonArray arr; for (auto s : m_skinList) { arr << s.toJSON(); } doc["skins"] = arr; try { Json::write(doc, m_dir.absoluteFilePath("index.json")); } catch (const FS::FileSystemException& e) { qCritical() << "Failed to write skin index file :" << e.cause(); } } int SkinList::getSelectedAccountSkin() { const auto& skin = m_acct->accountData()->minecraftProfile.skin; for (int i = 0; i < m_skinList.count(); i++) { if (m_skinList[i].getURL() == skin.url) { return i; } } return -1; } bool SkinList::setData(const QModelIndex& idx, const QVariant& value, int role) { if (!idx.isValid() || role != Qt::EditRole) { return false; } int row = idx.row(); if (row < 0 || row >= m_skinList.size()) return false; auto& skin = m_skinList[row]; auto newName = value.toString(); if (skin.name() != newName) { if (!skin.rename(newName)) return false; save(); } return true; } void SkinList::updateSkin(SkinModel* s) { auto done = false; for (auto i = 0; i < m_skinList.size(); i++) { if (m_skinList[i].getPath() == s->getPath()) { m_skinList[i].setCapeId(s->getCapeId()); m_skinList[i].setModel(s->getModel()); m_skinList[i].setURL(s->getURL()); done = true; break; } } if (!done) { beginInsertRows(QModelIndex(), m_skinList.count(), m_skinList.count() + 1); m_skinList.append(*s); endInsertRows(); } save(); } PrismLauncher-11.0.3/launcher/minecraft/VersionFile.cpp0000644000175100017510000000634015224505336022470 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "ParseUtils.h" #include "minecraft/Library.h" #include "minecraft/PackProfile.h" #include "minecraft/VersionFile.h" #include static bool isMinecraftVersion(const QString& uid) { return uid == "net.minecraft"; } void VersionFile::applyTo(LaunchProfile* profile, const RuntimeContext& runtimeContext) { // Only real Minecraft can set those. Don't let anything override them. if (isMinecraftVersion(uid)) { profile->applyMinecraftVersion(version); profile->applyMinecraftVersionType(type); // HACK: ignore assets from other version files than Minecraft // workaround for stupid assets issue caused by amazon: // https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/ profile->applyMinecraftAssets(mojangAssetIndex); } profile->applyMainJar(mainJar); profile->applyMainClass(mainClass); profile->applyAppletClass(appletClass); profile->applyMinecraftArguments(minecraftArguments); profile->applyAddnJvmArguments(addnJvmArguments); profile->applyTweakers(addTweakers); profile->applyJarMods(jarMods); profile->applyMods(mods); profile->applyTraits(traits); profile->applyCompatibleJavaMajors(compatibleJavaMajors); profile->applyCompatibleJavaName(compatibleJavaName); for (auto library : libraries) { profile->applyLibrary(library, runtimeContext); } for (auto mavenFile : mavenFiles) { profile->applyMavenFile(mavenFile, runtimeContext); } for (auto agent : agents) { profile->applyAgent(agent, runtimeContext); } profile->applyProblemSeverity(getProblemSeverity()); } PrismLauncher-11.0.3/launcher/minecraft/OneSixVersionFormat.cpp0000644000175100017510000003626215224505336024175 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "OneSixVersionFormat.h" #include #include #include #include "java/JavaMetadata.h" #include "minecraft/Agent.h" #include "minecraft/ParseUtils.h" #include using namespace Json; static void readString(const QJsonObject& root, const QString& key, QString& variable) { if (root.contains(key)) { variable = requireString(root.value(key)); } } LibraryPtr OneSixVersionFormat::libraryFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename) { LibraryPtr out = MojangVersionFormat::libraryFromJson(problems, libObj, filename); readString(libObj, "MMC-hint", out->m_hint); readString(libObj, "MMC-absulute_url", out->m_absoluteURL); readString(libObj, "MMC-absoluteUrl", out->m_absoluteURL); readString(libObj, "MMC-filename", out->m_filename); readString(libObj, "MMC-displayname", out->m_displayname); return out; } QJsonObject OneSixVersionFormat::libraryToJson(Library* library) { QJsonObject libRoot = MojangVersionFormat::libraryToJson(library); if (!library->m_absoluteURL.isEmpty()) libRoot.insert("MMC-absoluteUrl", library->m_absoluteURL); if (!library->m_hint.isEmpty()) libRoot.insert("MMC-hint", library->m_hint); if (!library->m_filename.isEmpty()) libRoot.insert("MMC-filename", library->m_filename); if (!library->m_displayname.isEmpty()) libRoot.insert("MMC-displayname", library->m_displayname); return libRoot; } VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument& doc, const QString& filename, const bool requireOrder) { VersionFilePtr out(new VersionFile()); if (doc.isEmpty() || doc.isNull()) { throw JSONValidationError(filename + " is empty or null"); } if (!doc.isObject()) { throw JSONValidationError(filename + " is not an object"); } QJsonObject root = doc.object(); Meta::MetadataVersion formatVersion = Meta::parseFormatVersion(root, false); switch (formatVersion) { case Meta::MetadataVersion::InitialRelease: break; case Meta::MetadataVersion::Invalid: throw JSONValidationError(filename + " does not contain a recognizable version of the metadata format."); } if (requireOrder) { if (root.contains("order")) { out->order = requireInteger(root.value("order")); } else { // FIXME: evaluate if we don't want to throw exceptions here instead qCritical() << filename << "doesn't contain an order field"; } } out->name = root.value("name").toString(); if (root.contains("uid")) { out->uid = root.value("uid").toString(); } else { out->uid = root.value("fileId").toString(); } static const QRegularExpression s_validUidRegex{ QRegularExpression::anchoredPattern( QStringLiteral(R"([a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]+)*)")) }; if (!s_validUidRegex.match(out->uid).hasMatch()) { qCritical() << "The component's 'uid' contains illegal characters! UID:" << out->uid; out->addProblem(ProblemSeverity::Error, QObject::tr("The component's 'uid' contains illegal characters! This can cause security issues.")); } out->version = root.value("version").toString(); MojangVersionFormat::readVersionProperties(root, out.get()); // added for legacy Minecraft window embedding, TODO: remove readString(root, "appletClass", out->appletClass); if (root.contains("+tweakers")) { for (auto tweakerVal : requireArray(root.value("+tweakers"))) { out->addTweakers.append(requireString(tweakerVal)); } } if (root.contains("+traits")) { for (auto tweakerVal : requireArray(root.value("+traits"))) { out->traits.insert(requireString(tweakerVal)); } } if (root.contains("+jvmArgs")) { for (auto arg : requireArray(root.value("+jvmArgs"))) { out->addnJvmArguments.append(requireString(arg)); } } if (root.contains("jarMods")) { for (auto libVal : requireArray(root.value("jarMods"))) { QJsonObject libObj = requireObject(libVal); // parse the jarmod auto lib = OneSixVersionFormat::jarModFromJson(*out, libObj, filename); // and add to jar mods out->jarMods.append(lib); } } else if (root.contains("+jarMods")) // DEPRECATED: old style '+jarMods' are only here for backwards compatibility { for (auto libVal : requireArray(root.value("+jarMods"))) { QJsonObject libObj = requireObject(libVal); // parse the jarmod auto lib = OneSixVersionFormat::plusJarModFromJson(*out, libObj, filename, out->name); // and add to jar mods out->jarMods.append(lib); } } if (root.contains("mods")) { for (auto libVal : requireArray(root.value("mods"))) { QJsonObject libObj = requireObject(libVal); // parse the jarmod auto lib = OneSixVersionFormat::modFromJson(*out, libObj, filename); // and add to jar mods out->mods.append(lib); } } auto readLibs = [&root, &out, &filename](const char* which, QList& outList) { for (auto libVal : requireArray(root.value(which))) { QJsonObject libObj = requireObject(libVal); // parse the library auto lib = libraryFromJson(*out, libObj, filename); outList.append(lib); } }; bool hasPlusLibs = root.contains("+libraries"); bool hasLibs = root.contains("libraries"); if (hasPlusLibs && hasLibs) { out->addProblem(ProblemSeverity::Warning, QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported.")); readLibs("libraries", out->libraries); readLibs("+libraries", out->libraries); } else if (hasLibs) { readLibs("libraries", out->libraries); } else if (hasPlusLibs) { readLibs("+libraries", out->libraries); } if (root.contains("mavenFiles")) { readLibs("mavenFiles", out->mavenFiles); } if (root.contains("+agents")) { for (auto agentVal : requireArray(root.value("+agents"))) { QJsonObject agentObj = requireObject(agentVal); auto lib = libraryFromJson(*out, agentObj, filename); QString arg = ""; readString(agentObj, "argument", arg); out->agents.append(Agent{ lib, arg }); } } // if we have mainJar, just use it if (root.contains("mainJar")) { QJsonObject libObj = requireObject(root, "mainJar"); out->mainJar = libraryFromJson(*out, libObj, filename); } // else reconstruct it from downloads and id ... if that's available else if (!out->minecraftVersion.isEmpty()) { auto lib = std::make_shared(); lib->setRawName(GradleSpecifier(QString("com.mojang:minecraft:%1:client").arg(out->minecraftVersion))); // we have a reliable client download, use it. if (out->mojangDownloads.contains("client")) { auto LibDLInfo = std::make_shared(); LibDLInfo->artifact = out->mojangDownloads["client"]; lib->setMojangDownloadInfo(LibDLInfo); } // we got nothing... else { out->addProblem( ProblemSeverity::Error, QObject::tr("URL for the main jar could not be determined - Mojang removed the server that we used as fallback.")); } out->mainJar = lib; } if (root.contains("requires")) { Meta::parseRequires(root, &out->m_requires); } QString dependsOnMinecraftVersion = root.value("mcVersion").toString(); if (!dependsOnMinecraftVersion.isEmpty()) { Meta::Require mcReq; mcReq.uid = "net.minecraft"; mcReq.equalsVersion = dependsOnMinecraftVersion; if (out->m_requires.count(mcReq) == 0) { out->m_requires.insert(mcReq); } } if (root.contains("conflicts")) { Meta::parseRequires(root, &out->conflicts); } if (root.contains("volatile")) { out->m_volatile = requireBoolean(root, "volatile"); } if (root.contains("runtimes")) { out->runtimes = {}; for (auto runtime : root["runtimes"].toArray()) { out->runtimes.append(Java::parseJavaMeta(runtime.toObject())); } } /* removed features that shouldn't be used */ if (root.contains("tweakers")) { out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'")); } if (root.contains("-libraries")) { out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-libraries'")); } if (root.contains("-tweakers")) { out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-tweakers'")); } if (root.contains("-minecraftArguments")) { out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-minecraftArguments'")); } if (root.contains("+minecraftArguments")) { out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '+minecraftArguments'")); } return out; } QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr& patch) { QJsonObject root; writeString(root, "name", patch->name); writeString(root, "uid", patch->uid); writeString(root, "version", patch->version); Meta::serializeFormatVersion(root, Meta::MetadataVersion::InitialRelease); MojangVersionFormat::writeVersionProperties(patch.get(), root); if (patch->mainJar) { root.insert("mainJar", libraryToJson(patch->mainJar.get())); } writeString(root, "appletClass", patch->appletClass); writeStringList(root, "+tweakers", patch->addTweakers); writeStringList(root, "+traits", patch->traits.values()); writeStringList(root, "+jvmArgs", patch->addnJvmArguments); if (!patch->agents.isEmpty()) { QJsonArray array; for (const auto& value : patch->agents) { QJsonObject agentOut = OneSixVersionFormat::libraryToJson(value.library.get()); if (!value.argument.isEmpty()) agentOut.insert("argument", value.argument); array.append(agentOut); } root.insert("+agents", array); } if (!patch->libraries.isEmpty()) { QJsonArray array; for (auto value : patch->libraries) { array.append(OneSixVersionFormat::libraryToJson(value.get())); } root.insert("libraries", array); } if (!patch->mavenFiles.isEmpty()) { QJsonArray array; for (auto value : patch->mavenFiles) { array.append(OneSixVersionFormat::libraryToJson(value.get())); } root.insert("mavenFiles", array); } if (!patch->jarMods.isEmpty()) { QJsonArray array; for (auto value : patch->jarMods) { array.append(OneSixVersionFormat::jarModtoJson(value.get())); } root.insert("jarMods", array); } if (!patch->mods.isEmpty()) { QJsonArray array; for (auto value : patch->jarMods) { array.append(OneSixVersionFormat::modtoJson(value.get())); } root.insert("mods", array); } if (!patch->m_requires.empty()) { Meta::serializeRequires(root, &patch->m_requires, "requires"); } if (!patch->conflicts.empty()) { Meta::serializeRequires(root, &patch->conflicts, "conflicts"); } if (patch->m_volatile) { root.insert("volatile", true); } // write the contents to a json document. { QJsonDocument out; out.setObject(root); return out; } } LibraryPtr OneSixVersionFormat::plusJarModFromJson([[maybe_unused]] ProblemContainer& problems, const QJsonObject& libObj, const QString& filename, const QString& originalName) { LibraryPtr out(new Library()); if (!libObj.contains("name")) { throw JSONValidationError(filename + "contains a jarmod that doesn't have a 'name' field"); } // just make up something unique on the spot for the library name. QString id = QUuid::createUuid().toString(QUuid::WithoutBraces); out->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1")); // filename override is the old name out->setFilename(libObj.value("name").toString()); // it needs to be local, it is stored in the instance jarmods folder out->setHint("local"); // read the original name if present - some versions did not set it // it is the original jar mod filename before it got renamed at the point of addition auto displayName = libObj.value("originalName").toString(); if (displayName.isEmpty()) { auto fixed = originalName; fixed.remove(" (jar mod)"); out->setDisplayName(fixed); } else { out->setDisplayName(displayName); } return out; } LibraryPtr OneSixVersionFormat::jarModFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename) { return libraryFromJson(problems, libObj, filename); } QJsonObject OneSixVersionFormat::jarModtoJson(Library* jarmod) { return libraryToJson(jarmod); } LibraryPtr OneSixVersionFormat::modFromJson(ProblemContainer& problems, const QJsonObject& libObj, const QString& filename) { return libraryFromJson(problems, libObj, filename); } QJsonObject OneSixVersionFormat::modtoJson(Library* jarmod) { return libraryToJson(jarmod); } PrismLauncher-11.0.3/launcher/minecraft/World.h0000644000175100017510000000604415224505336021000 0ustar runnerrunner/* Copyright 2015-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include struct GameType { GameType() = default; GameType(std::optional original); QString toTranslatedString() const; QString toLogString() const; enum { Unknown = -1, Survival, Creative, Adventure, Spectator } type = Unknown; std::optional original; }; class World { public: World(const QFileInfo& file); QString folderName() const { return m_folderName; } QString name() const { return m_actualName; } QString iconFile() const { return m_iconFile; } int64_t bytes() const { return m_size; } QDateTime lastPlayed() const { return m_lastPlayed; } GameType gameType() const { return m_gameType; } int64_t seed() const { return m_randomSeed; } bool isValid() const { return m_isValid; } bool isOnFS() const { return m_containerFile.isDir(); } QFileInfo container() const { return m_containerFile; } // delete all the files of this world bool destroy(); // replace this world with a copy of the other bool replace(World& with); // change the world's filesystem path (used by world lists for *MAGIC* purposes) void repath(const QFileInfo& file); // remove the icon file, if any bool resetIcon(); bool rename(const QString& to); bool install(const QString& to, const QString& name = QString()); void setSize(int64_t size); // WEAK compare operator - used for replacing worlds bool operator==(const World& other) const; auto isSymLink() const -> bool { return m_containerFile.isSymLink(); } /** * @brief Take a instance path, checks if the file pointed to by the resource is a symlink or under a symlink in that instance * * @param instPath path to an instance directory * @return true * @return false */ bool isSymLinkUnder(const QString& instPath) const; bool isMoreThanOneHardLink() const; QString canonicalFilePath() const { return m_containerFile.canonicalFilePath(); } private: void readFromZip(const QFileInfo& file); void readFromFS(const QFileInfo& file); void loadFromLevelDat(QByteArray data); protected: QFileInfo m_containerFile; QString m_containerOffsetPath; QString m_folderName; QString m_actualName; QString m_iconFile; QDateTime m_levelDatTime; QDateTime m_lastPlayed; int64_t m_size = 0; int64_t m_randomSeed = 0; GameType m_gameType; bool m_isValid = false; }; PrismLauncher-11.0.3/launcher/minecraft/Component.h0000644000175100017510000001141615224505336021652 0ustar runnerrunner#pragma once #include #include #include #include #include #include #include "ProblemProvider.h" #include "QObjectPtr.h" #include "meta/JsonFormat.h" #include "modplatform/ModIndex.h" class PackProfile; class LaunchProfile; namespace Meta { class Version; class VersionList; } // namespace Meta class VersionFile; struct UpdateActionChangeVersion { /// version to change to QString targetVersion; }; struct UpdateActionLatestRecommendedCompatible { /// Parent uid QString parentUid; QString parentName; /// Parent version QString version; /// }; struct UpdateActionRemove {}; struct UpdateActionImportantChanged { QString oldVersion; }; using UpdateActionNone = std::monostate; using UpdateAction = std::variant; struct ModloaderMapEntry { ModPlatform::ModLoaderType type; QStringList knownConflictingComponents; }; class Component : public QObject, public ProblemProvider { Q_OBJECT public: Component(PackProfile* parent, const QString& uid); // DEPRECATED: remove these constructors? Component(PackProfile* parent, const QString& uid, std::shared_ptr file); virtual ~Component() {} static const QMap KNOWN_MODLOADERS; void applyTo(LaunchProfile* profile); bool isEnabled(); bool setEnabled(bool state); bool canBeDisabled(); bool isMoveable(); bool isCustomizable(); bool isRevertible(); bool isRemovable(); bool isCustom(); bool isVersionChangeable(bool wait = true); bool isKnownModloader(); QStringList knownConflictingComponents(); // DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code void setOrder(int order); int getOrder(); QString getID(); QString getName(); QString getVersion(); std::shared_ptr getMeta(); QDateTime getReleaseDateTime(); QString getFilename(); std::shared_ptr getVersionFile() const; std::shared_ptr getVersionList() const; void setImportant(bool state); const QList getProblems() const override; ProblemSeverity getProblemSeverity() const override; void addComponentProblem(ProblemSeverity severity, const QString& description); void resetComponentProblems(); void setVersion(const QString& version); bool customize(); bool revert(); void updateCachedData(); void waitLoadMeta(); void setUpdateAction(const UpdateAction& action); void clearUpdateAction(); UpdateAction getUpdateAction(); signals: void dataChanged(); public: /* data */ PackProfile* m_parent; // BEGIN: persistent component list properties /// ID of the component QString m_uid; /// version of the component - when there's a custom json override, this is also the version the component reverts to QString m_version; /// if true, this has been added automatically to satisfy dependencies and may be automatically removed bool m_dependencyOnly = false; /// if true, the component is either the main component of the instance, or otherwise important and cannot be removed. bool m_important = false; /// if true, the component is disabled bool m_disabled = false; /// cached name for display purposes, taken from the version file (meta or local override) QString m_cachedName; /// cached version for display AND other purposes, taken from the version file (meta or local override) QString m_cachedVersion; /// cached set of requirements, taken from the version file (meta or local override) Meta::RequireSet m_cachedRequires; Meta::RequireSet m_cachedConflicts; /// if true, the component is volatile and may be automatically removed when no longer needed bool m_cachedVolatile = false; // END: persistent component list properties // DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code bool m_orderOverride = false; int m_order = 0; // load state std::shared_ptr m_metaVersion; std::shared_ptr m_file; bool m_loaded = false; private: QList m_componentProblems; ProblemSeverity m_componentProblemSeverity = ProblemSeverity::None; UpdateAction m_updateAction = UpdateAction{ UpdateActionNone{} }; }; using ComponentPtr = shared_qobject_ptr; PrismLauncher-11.0.3/launcher/minecraft/ComponentUpdateTask.cpp0000644000175100017510000010467215224505336024202 0ustar runnerrunner#include "ComponentUpdateTask.h" #include #include "Component.h" #include "ComponentUpdateTask_p.h" #include "PackProfile.h" #include "PackProfile_p.h" #include "ProblemProvider.h" #include "Version.h" #include "cassert" #include "meta/Index.h" #include "meta/Version.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/OneSixVersionFormat.h" #include "minecraft/ProfileUtils.h" #include "net/Mode.h" #include "Application.h" #include "tasks/Task.h" #include "minecraft/Logging.h" /* * This is responsible for loading the components of a component list AND resolving dependency issues between them */ /* * FIXME: the 'one shot async task' nature of this does not fit the intended usage * Really, it should be a reactor/state machine that receives input from the application * and dynamically adapts to changing requirements... * * The reactor should be the only entry into manipulating the PackProfile. * See: https://en.wikipedia.org/wiki/Reactor_pattern */ /* * Or make this operate on a snapshot of the PackProfile state, then merge results in as long as the snapshot and PackProfile didn't change? * If the component list changes, start over. */ /* * TODO: This task launches multiple other tasks. As such it should be converted to a ConcurrentTask */ ComponentUpdateTask::ComponentUpdateTask(Mode mode, Net::Mode netmode, PackProfile* list) : Task() { d.reset(new ComponentUpdateTaskData); d->m_profile = list; d->mode = mode; d->netmode = netmode; } ComponentUpdateTask::~ComponentUpdateTask() {} bool ComponentUpdateTask::canAbort() const { for (const auto& status : d->remoteLoadStatusList) { if (status.task && !status.task->canAbort()) { return false; } } return true; } bool ComponentUpdateTask::abort() { bool aborted = true; for (const auto& status : d->remoteLoadStatusList) { if (status.task && !status.task->abort()) { aborted = false; } } return aborted; } Net::Mode ComponentUpdateTask::netMode() { return d->netmode; } void ComponentUpdateTask::executeTask() { qCDebug(instanceProfileResolveC) << "Loading components"; setStatus(tr("Loading components")); loadComponents(); } namespace { enum class LoadResult { LoadedLocal, RequiresRemote, Failed }; LoadResult composeLoadResult(LoadResult a, LoadResult b) { if (a < b) { return b; } return a; } static LoadResult loadComponent(ComponentPtr component, Task::Ptr& loadTask, Net::Mode netmode) { if (component->m_loaded) { qCDebug(instanceProfileResolveC) << component->getName() << "is already loaded"; return LoadResult::LoadedLocal; } LoadResult result = LoadResult::Failed; auto customPatchFilename = component->getFilename(); if (QFile::exists(customPatchFilename)) { // if local file exists... // check for uid problems inside... bool fileChanged = false; auto file = ProfileUtils::parseJsonFile(QFileInfo(customPatchFilename), false); if (file->uid != component->m_uid) { file->uid = component->m_uid; fileChanged = true; } if (fileChanged) { // FIXME: @QUALITY do not ignore return value ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), customPatchFilename); } component->m_file = file; component->m_loaded = true; result = LoadResult::LoadedLocal; } else { auto metaVersion = APPLICATION->metadataIndex()->get(component->m_uid, component->m_version); component->m_metaVersion = metaVersion; if (metaVersion->isLoaded()) { component->m_loaded = true; result = LoadResult::LoadedLocal; } else { loadTask = APPLICATION->metadataIndex()->loadVersion(component->m_uid, component->m_version, netmode); loadTask->start(); if (netmode == Net::Mode::Online) result = LoadResult::RequiresRemote; else if (metaVersion->isLoaded()) result = LoadResult::LoadedLocal; else result = LoadResult::Failed; } } return result; } // FIXME: dead code. determine if this can still be useful? /* static LoadResult loadPackProfile(ComponentPtr component, Task::Ptr& loadTask, Net::Mode netmode) { if(component->m_loaded) { qDebug() << component->getName() << "is already loaded"; return LoadResult::LoadedLocal; } LoadResult result = LoadResult::Failed; auto metaList = APPLICATION->metadataIndex()->get(component->m_uid); if(metaList->isLoaded()) { component->m_loaded = true; result = LoadResult::LoadedLocal; } else { metaList->load(netmode); loadTask = metaList->getCurrentTask(); result = LoadResult::RequiresRemote; } return result; } */ } // namespace void ComponentUpdateTask::loadComponents() { LoadResult result = LoadResult::LoadedLocal; size_t taskIndex = 0; size_t componentIndex = 0; d->remoteLoadSuccessful = true; // load all the components OR their lists... for (auto component : d->m_profile->d->components) { Task::Ptr loadTask; LoadResult singleResult; RemoteLoadStatus::Type loadType; component->resetComponentProblems(); // FIXME: to do this right, we need to load the lists and decide on which versions to use during dependency resolution. For now, // ignore all that... #if 0 switch(d->mode) { case Mode::Launch: { singleResult = loadComponent(component, loadTask, d->netmode); loadType = RemoteLoadStatus::Type::Version; break; } case Mode::Resolution: { singleResult = loadPackProfile(component, loadTask, d->netmode); loadType = RemoteLoadStatus::Type::List; break; } } #else singleResult = loadComponent(component, loadTask, d->netmode); loadType = RemoteLoadStatus::Type::Version; #endif if (singleResult == LoadResult::LoadedLocal) { component->updateCachedData(); } result = composeLoadResult(result, singleResult); if (loadTask) { qCDebug(instanceProfileResolveC) << d->m_profile->d->m_instance->name() << "|" << "Remote loading is being run for" << component->getName(); connect(loadTask.get(), &Task::succeeded, this, [this, taskIndex]() { remoteLoadSucceeded(taskIndex); }); connect(loadTask.get(), &Task::failed, this, [this, taskIndex](const QString& error) { remoteLoadFailed(taskIndex, error); }); connect(loadTask.get(), &Task::aborted, this, [this, taskIndex]() { remoteLoadFailed(taskIndex, tr("Aborted")); }); RemoteLoadStatus status; status.type = loadType; status.PackProfileIndex = componentIndex; status.task = loadTask; d->remoteLoadStatusList.append(status); taskIndex++; } componentIndex++; } d->remoteTasksInProgress = taskIndex; m_progressTotal = static_cast(taskIndex); switch (result) { case LoadResult::LoadedLocal: { // Everything got loaded. Advance to dependency resolution. performUpdateActions(); resolveDependencies(d->mode == Mode::Launch || d->netmode == Net::Mode::Offline); return; } case LoadResult::RequiresRemote: { // we wait for signals. break; } case LoadResult::Failed: { emitFailed(tr("Some component metadata load tasks failed.")); return; } } setDetails(tr("Downloading metadata for %1 components").arg(taskIndex)); } namespace { struct RequireEx : public Meta::Require { size_t indexOfFirstDependee = 0; }; struct RequireCompositionResult { bool ok; RequireEx outcome; }; using RequireExSet = std::set; } // namespace static RequireCompositionResult composeRequirement(const RequireEx& a, const RequireEx& b) { assert(a.uid == b.uid); RequireEx out; out.uid = a.uid; out.indexOfFirstDependee = std::min(a.indexOfFirstDependee, b.indexOfFirstDependee); if (a.equalsVersion.isEmpty()) { out.equalsVersion = b.equalsVersion; } else if (b.equalsVersion.isEmpty()) { out.equalsVersion = a.equalsVersion; } else if (a.equalsVersion == b.equalsVersion) { out.equalsVersion = a.equalsVersion; } else { // FIXME: mark error as explicit version conflict return { false, out }; } if (a.suggests.isEmpty()) { out.suggests = b.suggests; } else if (b.suggests.isEmpty()) { out.suggests = a.suggests; } else { Version aVer(a.suggests); Version bVer(b.suggests); out.suggests = (aVer < bVer ? b.suggests : a.suggests); } return { true, out }; } // gather the requirements from all components, finding any obvious conflicts static bool gatherRequirementsFromComponents(const ComponentContainer& input, RequireExSet& output) { bool succeeded = true; size_t componentNum = 0; for (auto component : input) { auto& componentRequires = component->m_cachedRequires; for (const auto& componentRequire : componentRequires) { auto found = std::find_if(output.cbegin(), output.cend(), [componentRequire](const Meta::Require& req) { return req.uid == componentRequire.uid; }); RequireEx componenRequireEx; componenRequireEx.uid = componentRequire.uid; componenRequireEx.suggests = componentRequire.suggests; componenRequireEx.equalsVersion = componentRequire.equalsVersion; componenRequireEx.indexOfFirstDependee = componentNum; if (found != output.cend()) { // found... process it further auto result = composeRequirement(componenRequireEx, *found); if (result.ok) { output.erase(componenRequireEx); output.insert(result.outcome); } else { qCCritical(instanceProfileResolveC) << "Conflicting requirements:" << componentRequire.uid << "versions:" << componentRequire.equalsVersion << ";" << (*found).equalsVersion; } succeeded &= result.ok; } else { // not found, accumulate output.insert(componenRequireEx); } } componentNum++; } return succeeded; } /// Get list of uids that can be trivially removed because nothing is depending on them anymore (and they are installed as deps) static void getTrivialRemovals(const ComponentContainer& components, const RequireExSet& reqs, QStringList& toRemove) { for (const auto& component : components) { if (!component->m_dependencyOnly) continue; if (!component->m_cachedVolatile) continue; RequireEx reqNeedle; reqNeedle.uid = component->m_uid; const auto iter = reqs.find(reqNeedle); if (iter == reqs.cend()) { toRemove.append(component->m_uid); } } } /** * handles: * - trivial addition (there is an unmet requirement and it can be trivially met by adding something) * - trivial version conflict of dependencies == explicit version required and installed is different * * toAdd - set of requirements than mean adding a new component * toChange - set of requirements that mean changing version of an existing component */ static bool getTrivialComponentChanges(const ComponentIndex& index, const RequireExSet& input, RequireExSet& toAdd, RequireExSet& toChange) { enum class Decision { Undetermined, Met, Missing, VersionNotSame, LockedVersionNotSame } decision = Decision::Undetermined; QString reqStr; bool succeeded = true; // list the composed requirements and say if they are met or unmet for (auto& req : input) { do { if (req.equalsVersion.isEmpty()) { reqStr = QString("Req: %1").arg(req.uid); if (index.contains(req.uid)) { decision = Decision::Met; } else { toAdd.insert(req); decision = Decision::Missing; } break; } else { reqStr = QString("Req: %1 == %2").arg(req.uid, req.equalsVersion); const auto& compIter = index.find(req.uid); if (compIter == index.cend()) { toAdd.insert(req); decision = Decision::Missing; break; } auto& comp = (*compIter); if (comp->getVersion() != req.equalsVersion) { if (comp->isCustom()) { decision = Decision::LockedVersionNotSame; } else { if (comp->m_dependencyOnly) { decision = Decision::VersionNotSame; } else { decision = Decision::LockedVersionNotSame; } } break; } decision = Decision::Met; } } while (false); switch (decision) { case Decision::Undetermined: qCCritical(instanceProfileResolveC) << "No decision for" << reqStr; succeeded = false; break; case Decision::Met: qCDebug(instanceProfileResolveC) << reqStr << "Is met."; break; case Decision::Missing: qCDebug(instanceProfileResolveC) << reqStr << "Is missing and should be added at" << req.indexOfFirstDependee; toAdd.insert(req); break; case Decision::VersionNotSame: qCDebug(instanceProfileResolveC) << reqStr << "already has different version that can be changed."; toChange.insert(req); break; case Decision::LockedVersionNotSame: qCDebug(instanceProfileResolveC) << reqStr << "already has different version that cannot be changed."; succeeded = false; break; } } return succeeded; } ComponentContainer ComponentUpdateTask::collectTreeLinked(const QString& uid) { ComponentContainer linked; auto& components = d->m_profile->d->components; auto& componentIndex = d->m_profile->d->componentIndex; auto& instance = d->m_profile->d->m_instance; for (auto comp : components) { qCDebug(instanceProfileResolveC) << instance->name() << "|" << "scanning" << comp->getID() << ":" << comp->getVersion() << "for tree link"; auto dep = std::find_if(comp->m_cachedRequires.cbegin(), comp->m_cachedRequires.cend(), [uid](const Meta::Require& req) -> bool { return req.uid == uid; }); if (dep != comp->m_cachedRequires.cend()) { qCDebug(instanceProfileResolveC) << instance->name() << "|" << comp->getID() << ":" << comp->getVersion() << "depends on" << uid; linked.append(comp); } } auto iter = componentIndex.find(uid); if (iter != componentIndex.end()) { ComponentPtr comp = *iter; comp->updateCachedData(); qCDebug(instanceProfileResolveC) << instance->name() << "|" << comp->getID() << ":" << comp->getVersion() << "has" << comp->m_cachedRequires.size() << "dependencies"; for (auto dep : comp->m_cachedRequires) { qCDebug(instanceProfileC) << instance->name() << "|" << uid << "depends on" << dep.uid; auto found = componentIndex.find(dep.uid); if (found != componentIndex.end()) { qCDebug(instanceProfileC) << instance->name() << "|" << (*found)->getID() << "is present"; linked.append(*found); } } } return linked; } // FIXME, TODO: decouple dependency resolution from loading // FIXME: This works directly with the PackProfile internals. It shouldn't! It needs richer data types than PackProfile uses. // FIXME: throw all this away and use a graph void ComponentUpdateTask::resolveDependencies(bool checkOnly) { qCDebug(instanceProfileResolveC) << "Resolving dependencies"; /* * this is a naive dependency resolving algorithm. all it does is check for following conditions and react in simple ways: * 1. There are conflicting dependencies on the same uid with different exact version numbers * -> hard error * 2. A dependency has non-matching exact version number * -> hard error * 3. A dependency is entirely missing and needs to be injected before the dependee(s) * -> requirements are injected * * NOTE: this is a placeholder and should eventually be replaced with something 'serious' */ auto& components = d->m_profile->d->components; auto& componentIndex = d->m_profile->d->componentIndex; RequireExSet allRequires; QStringList toRemove; do { allRequires.clear(); toRemove.clear(); if (!gatherRequirementsFromComponents(components, allRequires)) { finalizeComponents(); emitFailed(tr("Conflicting requirements detected during dependency checking!")); return; } getTrivialRemovals(components, allRequires, toRemove); if (!toRemove.isEmpty()) { qCDebug(instanceProfileResolveC) << "Removing obsolete components..."; for (auto& remove : toRemove) { qCDebug(instanceProfileResolveC) << "Removing" << remove; d->m_profile->remove(remove); } } } while (!toRemove.isEmpty()); RequireExSet toAdd; RequireExSet toChange; bool succeeded = getTrivialComponentChanges(componentIndex, allRequires, toAdd, toChange); if (!succeeded) { finalizeComponents(); emitFailed(tr("Instance has conflicting dependencies.")); return; } if (checkOnly) { finalizeComponents(); if (toAdd.size() || toChange.size()) { emitFailed(tr("Instance has unresolved dependencies while loading/checking for launch.")); } else { emitSucceeded(); } return; } bool recursionNeeded = false; if (toAdd.size()) { // add stuff... for (auto& add : toAdd) { auto component = makeShared(d->m_profile, add.uid); if (!add.equalsVersion.isEmpty()) { // exact version qCDebug(instanceProfileResolveC) << "Adding" << add.uid << "version" << add.equalsVersion << "at position" << add.indexOfFirstDependee; component->m_version = add.equalsVersion; } else { // version needs to be decided qCDebug(instanceProfileResolveC) << "Adding" << add.uid << "at position" << add.indexOfFirstDependee; // ############################################################################################################ // HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded. if (!add.suggests.isEmpty()) { component->m_version = add.suggests; } else { if (add.uid == "org.lwjgl") { component->m_version = "2.9.1"; } else if (add.uid == "org.lwjgl3") { component->m_version = "3.1.2"; } else if (add.uid == "net.fabricmc.intermediary" || add.uid == "org.quiltmc.hashed") { auto minecraft = std::find_if(components.begin(), components.end(), [](ComponentPtr& cmp) { return cmp->getID() == "net.minecraft"; }); if (minecraft != components.end()) { component->m_version = (*minecraft)->getVersion(); } } } // HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded. // ############################################################################################################ } component->m_dependencyOnly = true; // FIXME: this should not work directly with the component list d->m_profile->insertComponent(add.indexOfFirstDependee, component); componentIndex[add.uid] = component; } recursionNeeded = true; } if (toChange.size()) { // change a version of something that exists for (auto& change : toChange) { // FIXME: this should not work directly with the component list qCDebug(instanceProfileResolveC) << "Setting version of" << change.uid << "to" << change.equalsVersion; auto component = componentIndex[change.uid]; component->setVersion(change.equalsVersion); } recursionNeeded = true; } if (recursionNeeded) { loadComponents(); } else { finalizeComponents(); emitSucceeded(); } } // Variant visitation via lambda template struct overload : Ts... { using Ts::operator()...; }; template overload(Ts...) -> overload; void ComponentUpdateTask::performUpdateActions() { auto& instance = d->m_profile->d->m_instance; bool addedActions; QStringList toRemove; do { addedActions = false; toRemove.clear(); auto& components = d->m_profile->d->components; auto& componentIndex = d->m_profile->d->componentIndex; for (auto component : components) { if (!component) { continue; } auto action = component->getUpdateAction(); auto visitor = overload{ [](const UpdateActionNone&) { // noop }, [&component, &instance](const UpdateActionChangeVersion& cv) { qCDebug(instanceProfileResolveC) << instance->name() << "|" << "UpdateActionChangeVersion" << component->getID() << ":" << component->getVersion() << "change to" << cv.targetVersion; component->setVersion(cv.targetVersion); component->waitLoadMeta(); }, [&component, &instance](const UpdateActionLatestRecommendedCompatible& lrc) { qCDebug(instanceProfileResolveC) << instance->name() << "|" << "UpdateActionLatestRecommendedCompatible" << component->getID() << ":" << component->getVersion() << "updating to latest recommend or compatible with" << lrc.parentUid << lrc.version; auto versionList = APPLICATION->metadataIndex()->get(component->getID()); if (versionList) { versionList->waitToLoad(); auto recommended = versionList->getRecommendedForParent(lrc.parentUid, lrc.version); if (!recommended) { recommended = versionList->getLatestForParent(lrc.parentUid, lrc.version); } if (recommended) { component->setVersion(recommended->version()); component->waitLoadMeta(); return; } else { component->addComponentProblem(ProblemSeverity::Error, QObject::tr("No compatible version of %1 found for %2 %3") .arg(component->getName(), lrc.parentName, lrc.version)); } } else { component->addComponentProblem( ProblemSeverity::Error, QObject::tr("No version list in metadata index for %1").arg(component->getID())); } }, [&component, &instance, &toRemove](const UpdateActionRemove&) { qCDebug(instanceProfileResolveC) << instance->name() << "|" << "UpdateActionRemove" << component->getID() << ":" << component->getVersion() << "removing"; toRemove.append(component->getID()); }, [this, &component, &instance, &addedActions, &componentIndex](const UpdateActionImportantChanged& ic) { qCDebug(instanceProfileResolveC) << instance->name() << "|" << "UpdateImportantChanged" << component->getID() << ":" << component->getVersion() << "was changed from" << ic.oldVersion << "updating linked components"; auto oldVersion = APPLICATION->metadataIndex()->getLoadedVersion(component->getID(), ic.oldVersion); for (auto oldReq : oldVersion->requiredSet()) { auto currentlyRequired = component->m_cachedRequires.find(oldReq); if (currentlyRequired == component->m_cachedRequires.cend()) { auto oldReqComp = componentIndex.find(oldReq.uid); if (oldReqComp != componentIndex.cend()) { (*oldReqComp)->setUpdateAction(UpdateAction{ UpdateActionRemove{} }); addedActions = true; } } } auto linked = collectTreeLinked(component->getID()); for (auto comp : linked) { if (comp->isCustom()) { continue; } auto compUid = comp->getID(); auto parentReq = std::find_if(component->m_cachedRequires.begin(), component->m_cachedRequires.end(), [compUid](const Meta::Require& req) { return req.uid == compUid; }); if (parentReq != component->m_cachedRequires.end()) { auto newVersion = parentReq->equalsVersion.isEmpty() ? parentReq->suggests : parentReq->equalsVersion; if (!newVersion.isEmpty()) { comp->setUpdateAction(UpdateAction{ UpdateActionChangeVersion{ newVersion } }); } else { comp->setUpdateAction(UpdateAction{ UpdateActionLatestRecommendedCompatible{ component->getID(), component->getName(), component->getVersion(), } }); } } else { comp->setUpdateAction(UpdateAction{ UpdateActionLatestRecommendedCompatible{ component->getID(), component->getName(), component->getVersion(), } }); } addedActions = true; } } }; std::visit(visitor, action); component->clearUpdateAction(); for (auto uid : toRemove) { d->m_profile->remove(uid); } } } while (addedActions); } void ComponentUpdateTask::finalizeComponents() { auto& components = d->m_profile->d->components; auto& componentIndex = d->m_profile->d->componentIndex; for (auto component : components) { for (auto req : component->m_cachedRequires) { auto found = componentIndex.find(req.uid); if (found == componentIndex.cend()) { component->addComponentProblem( ProblemSeverity::Error, QObject::tr("%1 is missing requirement %2 %3") .arg(component->getName(), req.uid, req.equalsVersion.isEmpty() ? req.suggests : req.equalsVersion)); } else { auto reqComp = *found; if (!reqComp->getProblems().isEmpty()) { component->addComponentProblem( reqComp->getProblemSeverity(), QObject::tr("%1, a dependency of this component, has reported issues").arg(reqComp->getName())); } if (!req.equalsVersion.isEmpty() && req.equalsVersion != reqComp->getVersion()) { component->addComponentProblem(ProblemSeverity::Error, QObject::tr("%1, a dependency of this component, is not the required version %2") .arg(reqComp->getName(), req.equalsVersion)); } else if (!req.suggests.isEmpty() && req.suggests != reqComp->getVersion()) { component->addComponentProblem(ProblemSeverity::Warning, QObject::tr("%1, a dependency of this component, is not the suggested version %2") .arg(reqComp->getName(), req.suggests)); } } } for (auto conflict : component->knownConflictingComponents()) { auto found = componentIndex.find(conflict); if (found != componentIndex.cend()) { auto foundComp = *found; if (foundComp->isCustom()) { continue; } component->addComponentProblem( ProblemSeverity::Warning, QObject::tr("%1 and %2 are known to not work together. It is recommended to remove one of them.") .arg(component->getName(), foundComp->getName())); } } } } void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex) { if (static_cast(d->remoteLoadStatusList.size()) < taskIndex) { qCWarning(instanceProfileResolveC) << "Got task index outside of results" << taskIndex; return; } auto& taskSlot = d->remoteLoadStatusList[taskIndex]; disconnect(taskSlot.task.get(), &Task::succeeded, this, nullptr); disconnect(taskSlot.task.get(), &Task::failed, this, nullptr); disconnect(taskSlot.task.get(), &Task::aborted, this, nullptr); if (taskSlot.finished) { qCWarning(instanceProfileResolveC) << "Got multiple results from remote load task" << taskIndex; return; } qCDebug(instanceProfileResolveC) << "Remote task" << taskIndex << "succeeded"; taskSlot.succeeded = false; taskSlot.finished = true; d->remoteTasksInProgress--; // update the cached data of the component from the downloaded version file. if (taskSlot.type == RemoteLoadStatus::Type::Version) { auto component = d->m_profile->getComponent(taskSlot.PackProfileIndex); component->m_loaded = true; component->updateCachedData(); } checkIfAllFinished(); } void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg) { if (static_cast(d->remoteLoadStatusList.size()) < taskIndex) { qCWarning(instanceProfileResolveC) << "Got task index outside of results" << taskIndex; return; } auto& taskSlot = d->remoteLoadStatusList[taskIndex]; disconnect(taskSlot.task.get(), &Task::succeeded, this, nullptr); disconnect(taskSlot.task.get(), &Task::failed, this, nullptr); disconnect(taskSlot.task.get(), &Task::aborted, this, nullptr); if (taskSlot.finished) { qCWarning(instanceProfileResolveC) << "Got multiple results from remote load task" << taskIndex; return; } qCDebug(instanceProfileResolveC) << "Remote task" << taskIndex << "failed:" << msg; d->remoteLoadSuccessful = false; taskSlot.succeeded = false; taskSlot.finished = true; d->remoteTasksInProgress--; checkIfAllFinished(); } void ComponentUpdateTask::checkIfAllFinished() { setProgress(m_progress + 1, m_progressTotal); if (d->remoteTasksInProgress) { // not yet... return; } if (d->remoteLoadSuccessful) { // nothing bad happened... clear the temp load status and proceed with looking at dependencies d->remoteLoadStatusList.clear(); performUpdateActions(); resolveDependencies(d->mode == Mode::Launch); } else { // remote load failed... report error and bail QStringList allErrorsList; for (auto& item : d->remoteLoadStatusList) { if (!item.succeeded) { const ComponentPtr component = d->m_profile->getComponent(item.PackProfileIndex); allErrorsList.append(tr("Could not download metadata for %1 %2. Please change the version or try again later.") .arg(component->getName(), component->m_version)); } } d->remoteLoadStatusList.clear(); auto allErrors = allErrorsList.join("\n"); emitFailed(tr("Component metadata update task failed while downloading from remote server:\n%1").arg(allErrors)); } } PrismLauncher-11.0.3/launcher/minecraft/VanillaInstanceCreationTask.cpp0000644000175100017510000000235515224505336025630 0ustar runnerrunner#include "VanillaInstanceCreationTask.h" #include #include "FileSystem.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "settings/INISettingsObject.h" VanillaCreationTask::VanillaCreationTask(BaseVersion::Ptr version, QString loader, BaseVersion::Ptr loader_version) : InstanceCreationTask() , m_version(std::move(version)) , m_using_loader(true) , m_loader(std::move(loader)) , m_loader_version(std::move(loader_version)) {} std::unique_ptr VanillaCreationTask::createInstance() { setStatus(tr("Creating instance from version %1").arg(m_version->name())); auto inst = std::make_unique(m_globalSettings, std::make_unique(FS::PathCombine(m_stagingPath, "instance.cfg")), m_stagingPath); SettingsObject::Lock lock(inst->settings()); auto components = inst->getPackProfile(); components->buildingFromScratch(); components->setComponentVersion("net.minecraft", m_version->descriptor(), true); if (m_using_loader) components->setComponentVersion(m_loader, m_loader_version->descriptor()); inst->setName(name()); inst->setIconKey(m_instIcon); return inst; } PrismLauncher-11.0.3/launcher/minecraft/WorldList.cpp0000644000175100017510000003127715224505336022175 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "WorldList.h" #include #include #include #include #include #include #include #include #include #include WorldList::WorldList(const QString& dir, BaseInstance* instance) : QAbstractListModel(), m_instance(instance), m_dir(dir) { FS::ensureFolderPathExists(m_dir.absolutePath()); m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); m_watcher = new QFileSystemWatcher(this); m_isWatching = false; connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &WorldList::directoryChanged); } void WorldList::startWatching() { if (m_isWatching) { return; } update(); m_isWatching = m_watcher->addPath(m_dir.absolutePath()); if (m_isWatching) { qDebug() << "Started watching" << m_dir.absolutePath(); } else { qDebug() << "Failed to start watching" << m_dir.absolutePath(); } } void WorldList::stopWatching() { if (!m_isWatching) { return; } m_isWatching = !m_watcher->removePath(m_dir.absolutePath()); if (!m_isWatching) { qDebug() << "Stopped watching" << m_dir.absolutePath(); } else { qDebug() << "Failed to stop watching" << m_dir.absolutePath(); } } bool WorldList::update() { if (!isValid()) return false; QList newWorlds; m_dir.refresh(); auto folderContents = m_dir.entryInfoList(); // if there are any untracked files... for (QFileInfo entry : folderContents) { if (!entry.isDir()) continue; World w(entry); if (w.isValid()) { newWorlds.append(w); } } beginResetModel(); m_worlds.swap(newWorlds); endResetModel(); loadWorldsAsync(); return true; } void WorldList::directoryChanged(QString) { update(); } bool WorldList::isValid() { return m_dir.exists() && m_dir.isReadable(); } QString WorldList::instDirPath() const { return QFileInfo(m_instance->instanceRoot()).absoluteFilePath(); } bool WorldList::deleteWorld(int index) { if (index >= m_worlds.size() || index < 0) return false; World& m = m_worlds[index]; if (m.destroy()) { beginRemoveRows(QModelIndex(), index, index); m_worlds.removeAt(index); endRemoveRows(); emit changed(); return true; } return false; } bool WorldList::deleteWorlds(int first, int last) { for (int i = first; i <= last; i++) { World& m = m_worlds[i]; m.destroy(); } beginRemoveRows(QModelIndex(), first, last); m_worlds.erase(m_worlds.begin() + first, m_worlds.begin() + last + 1); endRemoveRows(); emit changed(); return true; } bool WorldList::resetIcon(int row) { if (row >= m_worlds.size() || row < 0) return false; World& m = m_worlds[row]; if (m.resetIcon()) { QModelIndex modelIndex = index(row, NameColumn); emit dataChanged(modelIndex, modelIndex, { WorldList::IconFileRole }); return true; } return false; } int WorldList::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : 5; } QVariant WorldList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); int row = index.row(); int column = index.column(); if (row < 0 || row >= m_worlds.size()) return QVariant(); QLocale locale; auto& world = m_worlds[row]; switch (role) { case Qt::DisplayRole: switch (column) { case NameColumn: return world.name(); case GameModeColumn: return world.gameType().toTranslatedString(); case LastPlayedColumn: return world.lastPlayed(); case SizeColumn: return locale.formattedDataSize(world.bytes()); case InfoColumn: if (world.isSymLinkUnder(instDirPath())) { return tr("This world is symbolically linked from elsewhere."); } if (world.isMoreThanOneHardLink()) { return tr("\nThis world is hard linked elsewhere."); } return ""; default: return QVariant(); } case Qt::UserRole: if (column == SizeColumn) return QVariant::fromValue(world.bytes()); return data(index, Qt::DisplayRole); case Qt::ToolTipRole: { if (column == InfoColumn) { if (world.isSymLinkUnder(instDirPath())) { return tr("Warning: This world is symbolically linked from elsewhere. Editing it will also change the original." "\nCanonical Path: %1") .arg(world.canonicalFilePath()); } if (world.isMoreThanOneHardLink()) { return tr("Warning: This world is hard linked elsewhere. Editing it will also change the original."); } } return world.folderName(); } case ObjectRole: { return QVariant::fromValue((void*)&world); } case FolderRole: { return QDir::toNativeSeparators(dir().absoluteFilePath(world.folderName())); } case SeedRole: { return QVariant::fromValue(world.seed()); } case NameRole: { return world.name(); } case LastPlayedRole: { return world.lastPlayed(); } case SizeRole: { return QVariant::fromValue(world.bytes()); } case IconFileRole: { return world.iconFile(); } default: return QVariant(); } } QVariant WorldList::headerData(int section, [[maybe_unused]] Qt::Orientation orientation, int role) const { switch (role) { case Qt::DisplayRole: switch (section) { case NameColumn: return tr("Name"); case GameModeColumn: return tr("Game Mode"); case LastPlayedColumn: return tr("Last Played"); case SizeColumn: //: World size on disk return tr("Size"); case InfoColumn: //: special warnings? return tr("Info"); default: return QVariant(); } case Qt::ToolTipRole: switch (section) { case NameColumn: return tr("The name of the world."); case GameModeColumn: return tr("Game mode of the world."); case LastPlayedColumn: return tr("Date and time the world was last played."); case SizeColumn: return tr("Size of the world on disk."); case InfoColumn: return tr("Information and warnings about the world."); default: return QVariant(); } default: return QVariant(); } } QStringList WorldList::mimeTypes() const { QStringList types; types << "text/uri-list"; return types; } QMimeData* WorldList::mimeData(const QModelIndexList& indexes) const { QList urls; for (auto idx : indexes) { if (idx.column() != 0) continue; int row = idx.row(); if (row < 0 || row >= this->m_worlds.size()) continue; const World& world = m_worlds[row]; if (!world.isValid() || !world.isOnFS()) continue; QString worldPath = world.container().absoluteFilePath(); qDebug() << worldPath; urls.append(QUrl::fromLocalFile(worldPath)); } auto result = new QMimeData(); result->setUrls(urls); return result; } Qt::ItemFlags WorldList::flags(const QModelIndex& index) const { Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); if (index.isValid()) return Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; else return Qt::ItemIsDropEnabled | defaultFlags; } Qt::DropActions WorldList::supportedDragActions() const { // move to other mod lists or VOID return Qt::MoveAction; } Qt::DropActions WorldList::supportedDropActions() const { // copy from outside, move from within and other mod lists return Qt::CopyAction | Qt::MoveAction; } void WorldList::installWorld(QFileInfo filename) { qDebug() << "installing:" << filename.absoluteFilePath(); World w(filename); if (!w.isValid()) { return; } w.install(m_dir.absolutePath()); } bool WorldList::dropMimeData(const QMimeData* data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex& parent) { if (action == Qt::IgnoreAction) return true; // check if the action is supported if (!data || !(action & supportedDropActions())) return false; // files dropped from outside? if (data->hasUrls()) { bool was_watching = m_isWatching; if (was_watching) stopWatching(); auto urls = data->urls(); for (auto url : urls) { // only local files may be dropped... if (!url.isLocalFile()) continue; QString filename = url.toLocalFile(); QFileInfo worldInfo(filename); if (!m_dir.entryInfoList().contains(worldInfo)) { installWorld(worldInfo); } } if (was_watching) startWatching(); return true; } return false; } int64_t calculateWorldSize(const QFileInfo& file) { if (file.isFile() && file.suffix() == "zip") { return file.size(); } else if (file.isDir()) { QDirIterator it(file.absoluteFilePath(), QDir::Files, QDirIterator::Subdirectories); int64_t total = 0; while (it.hasNext()) { it.next(); total += it.fileInfo().size(); } return total; } return -1; } void WorldList::loadWorldsAsync() { for (int i = 0; i < m_worlds.size(); ++i) { auto file = m_worlds.at(i).container(); int row = i; QThreadPool::globalInstance()->start([this, file, row]() mutable { auto size = calculateWorldSize(file); QMetaObject::invokeMethod( this, [this, size, row, file]() { if (row < m_worlds.size() && m_worlds[row].container() == file) { m_worlds[row].setSize(size); // Notify views QModelIndex modelIndex = index(row, SizeColumn); emit dataChanged(modelIndex, modelIndex, { SizeRole }); } }, Qt::QueuedConnection); }); } } PrismLauncher-11.0.3/launcher/minecraft/AssetsUtils.cpp0000644000175100017510000002371115224505336022527 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include "AssetsUtils.h" #include "BuildConfig.h" #include "FileSystem.h" #include "net/ApiDownload.h" #include "net/ChecksumValidator.h" #include "net/Download.h" #include "Application.h" #include "net/NetRequest.h" #include "update/AssetUpdateTask.h" namespace { QSet collectPathsFromDir(QString dirPath) { QFileInfo dirInfo(dirPath); if (!dirInfo.exists()) { return {}; } QSet out; QDirIterator iter(dirPath, QDirIterator::Subdirectories); while (iter.hasNext()) { QString value = iter.next(); QFileInfo info(value); if (info.isFile()) { out.insert(value); qDebug() << value; } } return out; } } // namespace namespace AssetsUtils { /* * Returns true on success, with index populated * index is undefined otherwise */ bool loadAssetsIndexJson(const QString& assetsId, const QString& path, AssetsIndex& index) { /* { "objects": { "icons/icon_16x16.png": { "hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a", "size": 3665 }, ... } } } */ QFile file(path); // Try to open the file and fail if we can't. // TODO: We should probably report this error to the user. if (!file.open(QIODevice::ReadOnly)) { qCritical() << "Failed to read assets index file" << path << "error:" << file.errorString(); return false; } index.id = assetsId; // Read the file and close it. QByteArray jsonData = file.readAll(); file.close(); QJsonParseError parseError; QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); // Fail if the JSON is invalid. if (parseError.error != QJsonParseError::NoError) { qCritical() << "Failed to parse assets index file:" << parseError.errorString() << "at offset " << QString::number(parseError.offset); return false; } // Make sure the root is an object. if (!jsonDoc.isObject()) { qCritical() << "Invalid assets index JSON: Root should be an array."; return false; } QJsonObject root = jsonDoc.object(); QJsonValue isVirtual = root.value("virtual"); if (!isVirtual.isUndefined()) { index.isVirtual = isVirtual.toBool(false); } QJsonValue mapToResources = root.value("map_to_resources"); if (!mapToResources.isUndefined()) { index.mapToResources = mapToResources.toBool(false); } QJsonValue objects = root.value("objects"); QVariantMap map = objects.toVariant().toMap(); for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) { // qDebug() << iter.key(); QVariant variant = iter.value(); QVariantMap nested_objects = variant.toMap(); AssetObject object; for (QVariantMap::const_iterator nested_iter = nested_objects.begin(); nested_iter != nested_objects.end(); ++nested_iter) { // qDebug() << nested_iter.key() << nested_iter.value().toString(); QString key = nested_iter.key(); QVariant value = nested_iter.value(); if (key == "hash") { object.hash = value.toString(); } else if (key == "size") { object.size = value.toLongLong(); } } index.objects.insert(iter.key(), object); } return true; } // FIXME: ugly code duplication QDir getAssetsDir(const QString& assetsId, const QString& resourcesFolder) { QDir assetsDir = QDir("assets/"); QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes")); QDir objectDir = QDir(FS::PathCombine(assetsDir.path(), "objects")); QDir virtualDir = QDir(FS::PathCombine(assetsDir.path(), "virtual")); QString indexPath = FS::PathCombine(indexDir.path(), assetsId + ".json"); QFile indexFile(indexPath); QDir virtualRoot(FS::PathCombine(virtualDir.path(), assetsId)); if (!indexFile.exists()) { qCritical() << "No assets index file" << indexPath << "; can't determine assets path!"; return virtualRoot; } AssetsIndex index; if (!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index)) { qCritical() << "Failed to load asset index file" << indexPath << "; can't determine assets path!"; return virtualRoot; } QString targetPath; if (index.isVirtual) { return virtualRoot; } else if (index.mapToResources) { return QDir(resourcesFolder); } return virtualRoot; } // FIXME: ugly code duplication bool reconstructAssets(QString assetsId, QString resourcesFolder) { QDir assetsDir = QDir("assets/"); QDir indexDir = QDir(FS::PathCombine(assetsDir.path(), "indexes")); QDir objectDir = QDir(FS::PathCombine(assetsDir.path(), "objects")); QDir virtualDir = QDir(FS::PathCombine(assetsDir.path(), "virtual")); QString indexPath = FS::PathCombine(indexDir.path(), assetsId + ".json"); QFile indexFile(indexPath); QDir virtualRoot(FS::PathCombine(virtualDir.path(), assetsId)); if (!indexFile.exists()) { qCritical() << "No assets index file" << indexPath << "; can't reconstruct assets!"; return false; } qDebug() << "reconstructAssets" << assetsDir.path() << indexDir.path() << objectDir.path() << virtualDir.path() << virtualRoot.path(); AssetsIndex index; if (!AssetsUtils::loadAssetsIndexJson(assetsId, indexPath, index)) { qCritical() << "Failed to load asset index file" << indexPath << "; can't reconstruct assets!"; return false; } QString targetPath; bool removeLeftovers = false; if (index.isVirtual) { targetPath = virtualRoot.path(); removeLeftovers = true; qDebug() << "Reconstructing virtual assets folder at" << targetPath; } else if (index.mapToResources) { targetPath = resourcesFolder; qDebug() << "Reconstructing resources folder at" << targetPath; } if (!targetPath.isNull()) { auto presentFiles = collectPathsFromDir(targetPath); for (QString map : index.objects.keys()) { AssetObject asset_object = index.objects.value(map); QString target_path = FS::PathCombine(targetPath, map); QFile target(target_path); QString tlk = asset_object.hash.left(2); QString original_path = FS::PathCombine(objectDir.path(), tlk, asset_object.hash); QFile original(original_path); if (!original.exists()) continue; presentFiles.remove(target_path); if (!target.exists()) { QFileInfo info(target_path); QDir target_dir = info.dir(); qDebug() << target_dir.path(); FS::ensureFolderPathExists(target_dir.path()); bool couldCopy = original.copy(target_path); qDebug() << " Copying" << original_path << "to" << target_path << QString::number(couldCopy); } } // TODO: Write last used time to virtualRoot/.lastused if (removeLeftovers) { for (auto& file : presentFiles) { qDebug() << "Would remove" << file; } } } return true; } } // namespace AssetsUtils Net::NetRequest::Ptr AssetObject::getDownloadAction() { QFileInfo objectFile(getLocalPath()); if ((!objectFile.isFile()) || (objectFile.size() != size)) { auto objectDL = Net::ApiDownload::makeFile(getUrl(), objectFile.filePath()); if (hash.size()) { objectDL->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, hash)); } objectDL->setProgress(objectDL->getProgress(), size); return objectDL; } return nullptr; } QString AssetObject::getLocalPath() { return "assets/objects/" + getRelPath(); } QUrl AssetObject::getUrl() { auto resourceURL = AssetUpdateTask::resourceUrl(); return resourceURL + getRelPath(); } QString AssetObject::getRelPath() { return hash.left(2) + "/" + hash; } NetJob::Ptr AssetsIndex::getDownloadJob() { auto job = makeShared(QObject::tr("Assets for %1").arg(id), APPLICATION->network()); for (auto& object : objects.values()) { auto dl = object.getDownloadAction(); if (dl) { job->addNetAction(dl); } } if (job->size()) return job; return nullptr; } PrismLauncher-11.0.3/launcher/minecraft/MojangDownloadInfo.h0000644000175100017510000000352115224505336023425 0ustar runnerrunner#pragma once #include #include #include struct MojangDownloadInfo { // types using Ptr = std::shared_ptr; // data /// Local filesystem path. WARNING: not used, only here so we can pass through mojang files unmolested! QString path; /// absolute URL of this file QString url; /// sha-1 checksum of the file QString sha1; /// size of the file in bytes int size; }; struct MojangLibraryDownloadInfo { MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact_) : artifact(artifact_) {} MojangLibraryDownloadInfo() {} // types using Ptr = std::shared_ptr; // methods MojangDownloadInfo* getDownloadInfo(QString classifier) { if (classifier.isNull()) { return artifact.get(); } return classifiers[classifier].get(); } // data MojangDownloadInfo::Ptr artifact; QMap classifiers; }; struct MojangAssetIndexInfo : public MojangDownloadInfo { // types using Ptr = std::shared_ptr; // methods MojangAssetIndexInfo() {} MojangAssetIndexInfo(QString id_) { this->id = id_; // HACK: ignore assets from other version files than Minecraft // workaround for stupid assets issue caused by amazon: // https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/ if (id_ == "legacy") { url = "https://piston-meta.mojang.com/mc/assets/legacy/c0fd82e8ce9fbc93119e40d96d5a4e62cfa3f729/legacy.json"; } // HACK else { url = "https://s3.amazonaws.com/Minecraft.Download/indexes/" + id_ + ".json"; } known = false; } // data int totalSize; QString id; bool known = true; }; PrismLauncher-11.0.3/launcher/minecraft/ShortcutUtils.h0000644000175100017510000000475215224505336022551 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * Copyright (C) 2025 Yihe Li * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "Application.h" #include "BaseInstance.h" #include #include namespace ShortcutUtils { /// A struct to hold parameters for creating a shortcut struct Shortcut { BaseInstance* instance; QString name; QString targetString; QWidget* parent = nullptr; QStringList extraArgs = {}; QString iconKey = ""; ShortcutTarget target; }; /// Create an instance shortcut on the specified file path bool createInstanceShortcut(const Shortcut& shortcut, const QString& filePath); /// Create an instance shortcut on the desktop bool createInstanceShortcutOnDesktop(const Shortcut& shortcut); /// Create an instance shortcut in the Applications directory bool createInstanceShortcutInApplications(const Shortcut& shortcut); /// Create an instance shortcut in other directories bool createInstanceShortcutInOther(const Shortcut& shortcut); } // namespace ShortcutUtils PrismLauncher-11.0.3/launcher/minecraft/PackProfile.h0000644000175100017510000001473415224505336022115 0ustar runnerrunner// SPDX-FileCopyrightText: 2022-2023 Sefa Eyeoglu // // SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022-2023 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include "Component.h" #include "LaunchProfile.h" #include "modplatform/ModIndex.h" #include "net/Mode.h" class MinecraftInstance; struct PackProfileData; class ComponentUpdateTask; class PackProfile : public QAbstractListModel { Q_OBJECT friend ComponentUpdateTask; public: enum Columns { NameColumn = 0, VersionColumn, NUM_COLUMNS }; struct Result { bool success; QString error; // Implicit conversion to bool operator bool() const { return success; } // Factory methods for convenience static Result Success() { return { true, "" }; } static Result Error(const QString& errorMessage) { return { false, errorMessage }; } }; explicit PackProfile(MinecraftInstance* instance); virtual ~PackProfile(); virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex& parent) const override; virtual Qt::ItemFlags flags(const QModelIndex& index) const override; /// call this to explicitly mark the component list as loaded - this is used to build a new component list from scratch. void buildingFromScratch(); /// install more jar mods void installJarMods(QStringList selectedFiles); /// install a jar/zip as a replacement for the main jar void installCustomJar(QString selectedFile); /// install MMC/Prism component files bool installComponents(QStringList selectedFiles); /// install Java agent files void installAgents(QStringList selectedFiles); enum MoveDirection { MoveUp, MoveDown }; /// move component file # up or down the list void move(int index, MoveDirection direction); /// remove component file # - including files/records bool remove(int index); /// remove component file by id - including files/records bool remove(const QString& id); bool customize(int index); bool revertToBase(int index); /// reload the list, reload all components, resolve dependencies Result reload(Net::Mode netmode); // reload all components, resolve dependencies void resolve(Net::Mode netmode); /// get current running task... Task::Ptr getCurrentTask(); std::shared_ptr getProfile() const; // NOTE: used ONLY by MinecraftInstance to provide legacy version mappings from instance config void setOldConfigVersion(const QString& uid, const QString& version); QString getComponentVersion(const QString& uid) const; bool setComponentVersion(const QString& uid, const QString& version, bool important = false); bool installEmpty(const QString& uid, const QString& name); QString patchFilePathForUid(const QString& uid) const; /// if there is a save scheduled, do it now. void saveNow(); /// helper method, returns RuntimeContext of instance RuntimeContext runtimeContext(); signals: void minecraftChanged(); public: /// get the profile component by id ComponentPtr getComponent(const QString& id); /// get the profile component by index ComponentPtr getComponent(size_t index); /// Add the component to the internal list of patches // todo(merged): is this the best approach void appendComponent(ComponentPtr component); std::optional getModLoaders(); // this returns aditional loaders(Quilt supports fabric and NeoForge supports Forge) std::optional getSupportedModLoaders(); QList getModLoadersList(); /// apply the component patches. Catches all the errors and returns true/false for success/failure void invalidateLaunchProfile(); private: void scheduleSave(); bool saveIsScheduled() const; /// insert component so that its index is ideally the specified one (returns real index) void insertComponent(size_t index, ComponentPtr component); QString componentsFilePath() const; QString patchesPattern() const; private slots: bool save_internal(); void updateSucceeded(); void updateFailed(const QString& error); void componentDataChanged(); void disableInteraction(bool disable); private: Result load(); bool installJarMods_internal(QStringList filepaths); bool installCustomJar_internal(QString filepath); bool installAgents_internal(QStringList filepaths); bool removeComponent_internal(ComponentPtr patch); private: /* data */ std::unique_ptr d; }; PrismLauncher-11.0.3/launcher/minecraft/ProfileUtils.cpp0000644000175100017510000001434415224505336022667 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ProfileUtils.h" #include #include "Json.h" #include "minecraft/OneSixVersionFormat.h" #include "minecraft/VersionFilterData.h" #include #include #include namespace ProfileUtils { static const int currentOrderFileVersion = 1; bool readOverrideOrders(QString path, PatchOrder& order) { QFile orderFile(path); if (!orderFile.exists()) { qWarning() << "Order file doesn't exist. Ignoring."; return false; } if (!orderFile.open(QFile::ReadOnly)) { qCritical() << "Couldn't open" << orderFile.fileName() << "for reading:" << orderFile.errorString(); qWarning() << "Ignoring overridden order"; return false; } // and it's valid JSON QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error); if (error.error != QJsonParseError::NoError) { qCritical() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString(); qWarning() << "Ignoring overridden order"; return false; } // and then read it and process it if all above is true. try { auto obj = Json::requireObject(doc); // check order file version. auto version = Json::requireInteger(obj.value("version")); if (version != currentOrderFileVersion) { throw JSONValidationError(QObject::tr("Invalid order file version, expected %1").arg(currentOrderFileVersion)); } auto orderArray = Json::requireArray(obj.value("order")); for (auto item : orderArray) { order.append(Json::requireString(item)); } } catch ([[maybe_unused]] const JSONValidationError& err) { qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format"; qWarning() << "Ignoring overridden order"; order.clear(); return false; } return true; } static VersionFilePtr createErrorVersionFile(QString fileId, QString filepath, QString error) { auto outError = std::make_shared(); outError->uid = outError->name = fileId; // outError->filename = filepath; outError->addProblem(ProblemSeverity::Error, error); return outError; } static VersionFilePtr guardedParseJson(const QJsonDocument& doc, const QString& fileId, const QString& filepath, const bool& requireOrder) { try { return OneSixVersionFormat::versionFileFromJson(doc, filepath, requireOrder); } catch (const Exception& e) { return createErrorVersionFile(fileId, filepath, e.cause()); } } VersionFilePtr parseJsonFile(const QFileInfo& fileInfo, const bool requireOrder) { QFile file(fileInfo.absoluteFilePath()); if (!file.open(QFile::ReadOnly)) { auto errorStr = QObject::tr("Unable to open the version file %1: %2.").arg(fileInfo.fileName(), file.errorString()); return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr); } QJsonParseError error; auto data = file.readAll(); QJsonDocument doc = QJsonDocument::fromJson(data, &error); file.close(); if (error.error != QJsonParseError::NoError) { int line = 1; int column = 0; for (int i = 0; i < error.offset; i++) { if (data[i] == '\n') { line++; column = 0; continue; } column++; } auto errorStr = QObject::tr("Unable to process the version file %1: %2 at line %3 column %4.") .arg(fileInfo.fileName(), error.errorString()) .arg(line) .arg(column); return createErrorVersionFile(fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), errorStr); } return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder); } bool saveJsonFile(const QJsonDocument& doc, const QString& filename) { auto data = doc.toJson(); QSaveFile jsonFile(filename); if (!jsonFile.open(QIODevice::WriteOnly)) { qWarning() << "Couldn't open" << filename << "for writing:" << jsonFile.errorString(); jsonFile.cancelWriting(); return false; } jsonFile.write(data); if (!jsonFile.commit()) { qWarning() << "Couldn't save" << filename << "error:" << jsonFile.errorString(); return false; } return true; } void removeLwjglFromPatch(VersionFilePtr patch) { auto filter = [](QList& libs) { QList filteredLibs; for (auto lib : libs) { if (!g_VersionFilterData.lwjglWhitelist.contains(lib->artifactPrefix())) { filteredLibs.append(lib); } } libs = filteredLibs; }; filter(patch->libraries); } } // namespace ProfileUtils PrismLauncher-11.0.3/launcher/minecraft/MinecraftLoadAndCheck.cpp0000644000175100017510000000217715224505336024340 0ustar runnerrunner#include "MinecraftLoadAndCheck.h" #include "MinecraftInstance.h" #include "PackProfile.h" MinecraftLoadAndCheck::MinecraftLoadAndCheck(MinecraftInstance* inst, Net::Mode netmode) : m_inst(inst), m_netmode(netmode) {} void MinecraftLoadAndCheck::executeTask() { // add offline metadata load task auto components = m_inst->getPackProfile(); if (auto result = components->reload(m_netmode); !result) { emitFailed(result.error); return; } m_task = components->getCurrentTask(); if (!m_task) { emitSucceeded(); return; } connect(m_task.get(), &Task::succeeded, this, &MinecraftLoadAndCheck::emitSucceeded); connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::emitFailed); connect(m_task.get(), &Task::aborted, this, &MinecraftLoadAndCheck::emitAborted); propagateFromOther(m_task.get()); } bool MinecraftLoadAndCheck::canAbort() const { if (m_task) { return m_task->canAbort(); } return true; } bool MinecraftLoadAndCheck::abort() { if (m_task && m_task->canAbort()) { return m_task->abort(); } return Task::abort(); } PrismLauncher-11.0.3/launcher/minecraft/ParseUtils.cpp0000644000175100017510000000157415224505336022342 0ustar runnerrunner#include "ParseUtils.h" #include #include #include #include QDateTime timeFromS3Time(QString str) { return QDateTime::fromString(str, Qt::ISODate); } QString timeToS3Time(QDateTime time) { // this all because Qt can't format timestamps right. int offsetRaw = time.offsetFromUtc(); bool negative = offsetRaw < 0; int offsetAbs = std::abs(offsetRaw); int offsetSeconds = offsetAbs % 60; offsetAbs -= offsetSeconds; int offsetMinutes = offsetAbs % 3600; offsetAbs -= offsetMinutes; offsetMinutes /= 60; int offsetHours = offsetAbs / 3600; QString raw = time.toString("yyyy-MM-ddTHH:mm:ss"); raw += (negative ? QChar('-') : QChar('+')); raw += QString("%1").arg(offsetHours, 2, 10, QChar('0')); raw += ":"; raw += QString("%1").arg(offsetMinutes, 2, 10, QChar('0')); return raw; } PrismLauncher-11.0.3/launcher/minecraft/ParseUtils.h0000644000175100017510000000036515224505336022004 0ustar runnerrunner#pragma once #include #include /// take the timestamp used by S3 and turn it into QDateTime QDateTime timeFromS3Time(QString str); /// take a timestamp and convert it into an S3 timestamp QString timeToS3Time(QDateTime); PrismLauncher-11.0.3/launcher/minecraft/VersionFile.h0000644000175100017510000001236015224505336022134 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include "Agent.h" #include "Library.h" #include "ProblemProvider.h" #include "java/JavaMetadata.h" #include "minecraft/Rule.h" class PackProfile; class VersionFile; class LaunchProfile; struct MojangDownloadInfo; struct MojangAssetIndexInfo; using VersionFilePtr = std::shared_ptr; class VersionFile : public ProblemContainer { friend class MojangVersionFormat; friend class OneSixVersionFormat; public: /* methods */ void applyTo(LaunchProfile* profile, const RuntimeContext& runtimeContext); public: /* data */ /// Prism Launcher: order hint for this version file if no explicit order is set int order = 0; /// Prism Launcher: human readable name of this package QString name; /// Prism Launcher: package ID of this package QString uid; /// Prism Launcher: version of this package QString version; /// Prism Launcher: DEPRECATED dependency on a Minecraft version QString dependsOnMinecraftVersion; /// Mojang: DEPRECATED used to version the Mojang version format int minimumLauncherVersion = -1; /// Mojang: DEPRECATED version of Minecraft this is QString minecraftVersion; /// Mojang: class to launch Minecraft with QString mainClass; /// Prism Launcher: class to launch legacy Minecraft with (embed in a custom window) QString appletClass; /// Mojang: Minecraft launch arguments (may contain placeholders for variable substitution) QString minecraftArguments; /// Prism Launcher: Additional JVM launch arguments QStringList addnJvmArguments; /// Mojang: list of compatible java majors QList compatibleJavaMajors; /// Mojang: the name of recommended java version QString compatibleJavaName; /// Mojang: type of the Minecraft version QString type; /// Mojang: the time this version was actually released by Mojang QDateTime releaseTime; /// Mojang: DEPRECATED the time this version was last updated by Mojang QDateTime updateTime; /// Mojang: DEPRECATED asset group to be used with Minecraft QString assets; /// Prism Launcher: list of tweaker mod arguments for launchwrapper QStringList addTweakers; /// Mojang: list of libraries to add to the version QList libraries; /// Prism Launcher: list of maven files to put in the libraries folder, but not in classpath QList mavenFiles; /// Prism Launcher: list of agents to add to JVM arguments QList agents; /// The main jar (Minecraft version library, normally) LibraryPtr mainJar; /// Prism Launcher: list of attached traits of this version file - used to enable features QSet traits; /// Prism Launcher: list of jar mods added to this version QList jarMods; /// Prism Launcher: list of mods added to this version QList mods; /** * Prism Launcher: set of packages this depends on * NOTE: this is shared with the meta format!!! */ Meta::RequireSet m_requires; /** * Prism Launcher: set of packages this conflicts with * NOTE: this is shared with the meta format!!! */ Meta::RequireSet conflicts; /// is volatile -- may be removed as soon as it is no longer needed by something else bool m_volatile = false; QList runtimes; public: // Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more. QMap> mojangDownloads; // Mojang: extended asset index download information std::shared_ptr mojangAssetIndex; }; PrismLauncher-11.0.3/launcher/minecraft/Rule.h0000644000175100017510000000407715224505336020624 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2025 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include "RuntimeContext.h" class Library; class Rule { public: enum Action { Allow, Disallow, Defer }; static Rule fromJson(const QJsonObject& json); QJsonObject toJson(); Action apply(const RuntimeContext& runtimeContext); private: struct OS { QString name; // FIXME: unsupported // retained to avoid information being lost from files QString version; }; Action m_action = Defer; std::optional m_os; }; PrismLauncher-11.0.3/launcher/minecraft/Library.h0000644000175100017510000001644215224505336021320 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include "GradleSpecifier.h" #include "MojangDownloadInfo.h" #include "Rule.h" #include "RuntimeContext.h" #include "net/NetRequest.h" class Library; class MinecraftInstance; using LibraryPtr = std::shared_ptr; class Library { friend class OneSixVersionFormat; friend class MojangVersionFormat; friend class LibraryTest; public: Library() {} Library(const QString& name) { m_name = name; } /// limited copy without some data. TODO: why? static LibraryPtr limitedCopy(LibraryPtr base) { auto newlib = std::make_shared(); newlib->m_name = base->m_name; newlib->m_repositoryURL = base->m_repositoryURL; newlib->m_hint = base->m_hint; newlib->m_absoluteURL = base->m_absoluteURL; newlib->m_extractExcludes = base->m_extractExcludes; newlib->m_nativeClassifiers = base->m_nativeClassifiers; newlib->m_rules = base->m_rules; newlib->m_storagePrefix = base->m_storagePrefix; newlib->m_mojangDownloads = base->m_mojangDownloads; newlib->m_filename = base->m_filename; return newlib; } public: /* methods */ /// Returns the raw name field const GradleSpecifier& rawName() const { return m_name; } void setRawName(const GradleSpecifier& spec) { m_name = spec; } void setClassifier(const QString& spec) { m_name.setClassifier(spec); } /// returns the full group and artifact prefix QString artifactPrefix() const { return m_name.artifactPrefix(); } /// get the artifact ID QString artifactId() const { return m_name.artifactId(); } /// get the artifact version QString version() const { return m_name.version(); } /// Returns true if the library is native bool isNative() const { return m_nativeClassifiers.size() != 0; } void setStoragePrefix(QString prefix = QString()); /// Set the url base for downloads void setRepositoryURL(const QString& base_url) { m_repositoryURL = base_url; } void getApplicableFiles(const RuntimeContext& runtimeContext, QStringList& jar, QStringList& native, QStringList& native32, QStringList& native64, const QString& overridePath) const; void setAbsoluteUrl(const QString& absolute_url) { m_absoluteURL = absolute_url; } void setFilename(const QString& filename) { m_filename = filename; } /// Get the file name of the library QString filename(const RuntimeContext& runtimeContext) const; // DEPRECATED: set a display name, used by jar mods only void setDisplayName(const QString& displayName) { m_displayname = displayName; } /// Get the file name of the library QString displayName(const RuntimeContext& runtimeContext) const; void setMojangDownloadInfo(MojangLibraryDownloadInfo::Ptr info) { m_mojangDownloads = info; } void setHint(const QString& hint) { m_hint = hint; } /// Set the load rules void setRules(QList rules) { m_rules = rules; } /// Returns true if the library should be loaded (or extracted, in case of natives) bool isActive(const RuntimeContext& runtimeContext) const; /// Returns true if the library is contained in an instance and false if it is shared bool isLocal() const; /// Returns true if the library is to always be checked for updates bool isAlwaysStale() const; /// Return true if the library requires forge XZ hacks bool isForge() const; // Get a list of downloads for this library QList getDownloads(const RuntimeContext& runtimeContext, class HttpMetaCache* cache, QStringList& failedLocalFiles, const QString& overridePath) const; QString getCompatibleNative(const RuntimeContext& runtimeContext) const; private: /* methods */ /// the default storage prefix used by Prism Launcher static QString defaultStoragePrefix(); /// Get the prefix - root of the storage to be used QString storagePrefix() const; /// Get the relative file path where the library should be saved QString storageSuffix(const RuntimeContext& runtimeContext) const; QString hint() const { return m_hint; } protected: /* data */ /// the basic gradle dependency specifier. GradleSpecifier m_name; /// DEPRECATED URL prefix of the maven repo where the file can be downloaded QString m_repositoryURL; /// DEPRECATED: Prism Launcher-specific absolute URL. takes precedence over the implicit maven repo URL, if defined QString m_absoluteURL; /// Prism Launcher extension - filename override QString m_filename; /// DEPRECATED Prism Launcher extension - display name QString m_displayname; /** * Prism Launcher-specific type hint - modifies how the library is treated */ QString m_hint; /** * storage - by default the local libraries folder in Prism Launcher, but could be elsewhere * Prism Launcher specific, because of FTB. */ QString m_storagePrefix; /// true if the library had an extract/excludes section (even empty) bool m_hasExcludes = false; /// a list of files that shouldn't be extracted from the library QStringList m_extractExcludes; /// native suffixes per OS QMap m_nativeClassifiers; /// true if the library had a rules section (even empty) bool applyRules = false; /// rules associated with the library QList m_rules; /// MOJANG: container with Mojang style download info MojangLibraryDownloadInfo::Ptr m_mojangDownloads; }; PrismLauncher-11.0.3/launcher/ResourceDownloadTask.h0000644000175100017510000000447015224505336022044 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022-2023 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "net/NetJob.h" #include "tasks/SequentialTask.h" #include "minecraft/mod/tasks/LocalResourceUpdateTask.h" #include "modplatform/ModIndex.h" class ResourceFolderModel; class ResourceDownloadTask : public SequentialTask { Q_OBJECT public: explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion version, ResourceFolderModel* packs, bool isIndexed = true, QString downloadReason = "standalone"); const QString& getFilename() const { return m_pack_version.fileName; } const QVariant& getVersionID() const { return m_pack_version.fileId; } const ModPlatform::IndexedVersion& getVersion() const { return m_pack_version; } const ModPlatform::ResourceProvider& getProvider() const { return m_pack->provider; } const QString& getName() const { return m_pack->name; } ModPlatform::IndexedPack::Ptr getPack() { return m_pack; } private: ModPlatform::IndexedPack::Ptr m_pack; ModPlatform::IndexedVersion m_pack_version; ResourceFolderModel* m_pack_model; NetJob::Ptr m_filesNetJob; LocalResourceUpdateTask::Ptr m_update_task; void downloadProgressChanged(qint64 current, qint64 total); void downloadFailed(QString reason); void downloadSucceeded(); std::tuple to_delete{ "", "" }; private slots: void hasOldResource(const QString& name, const QString& filename); }; PrismLauncher-11.0.3/launcher/tools/0000755000175100017510000000000015224505336016724 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/tools/BaseExternalTool.cpp0000644000175100017510000000144115224505336022643 0ustar runnerrunner#include "BaseExternalTool.h" #include #include #ifdef Q_OS_WIN #include #endif #include "BaseInstance.h" BaseExternalTool::BaseExternalTool(SettingsObject* settings, BaseInstance* instance, QObject* parent) : QObject(parent), m_instance(instance), globalSettings(settings) {} BaseExternalTool::~BaseExternalTool() {} BaseDetachedTool::BaseDetachedTool(SettingsObject* settings, BaseInstance* instance, QObject* parent) : BaseExternalTool(settings, instance, parent) {} void BaseDetachedTool::run() { runImpl(); } BaseExternalToolFactory::~BaseExternalToolFactory() {} BaseDetachedTool* BaseDetachedToolFactory::createDetachedTool(BaseInstance* instance, QObject* parent) { return qobject_cast(createTool(instance, parent)); } PrismLauncher-11.0.3/launcher/tools/GenericProfiler.h0000644000175100017510000000237215224505336022160 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "BaseProfiler.h" class GenericProfilerFactory : public BaseProfilerFactory { public: QString name() const override { return "Generic"; } void registerSettings([[maybe_unused]] SettingsObject* settings) override {}; BaseExternalTool* createTool(BaseInstance* instance, QObject* parent = 0) override; bool check([[maybe_unused]] QString* error) override { return true; }; bool check([[maybe_unused]] const QString& path, [[maybe_unused]] QString* error) override { return true; }; }; PrismLauncher-11.0.3/launcher/tools/MCEditTool.h0000644000175100017510000000052115224505336021036 0ustar runnerrunner#pragma once #include #include "settings/SettingsObject.h" class MCEditTool { public: MCEditTool(SettingsObject* settings); void setPath(QString& path); QString path() const; bool check(const QString& toolPath, QString& error); QString getProgramPath(); private: SettingsObject* m_settings; }; PrismLauncher-11.0.3/launcher/tools/JProfiler.cpp0000644000175100017510000000560315224505336021330 0ustar runnerrunner#include "JProfiler.h" #include #include "BaseInstance.h" #include "launch/LaunchTask.h" #include "settings/SettingsObject.h" class JProfiler : public BaseProfiler { Q_OBJECT public: JProfiler(SettingsObject* settings, BaseInstance* instance, QObject* parent = 0); private slots: void profilerStarted(); void profilerFinished(int exit, QProcess::ExitStatus status); protected: void beginProfilingImpl(LaunchTask* process); private: int listeningPort = 0; }; JProfiler::JProfiler(SettingsObject* settings, BaseInstance* instance, QObject* parent) : BaseProfiler(settings, instance, parent) {} void JProfiler::profilerStarted() { emit readyToLaunch(tr("Listening on port: %1").arg(listeningPort)); } void JProfiler::profilerFinished([[maybe_unused]] int exit, QProcess::ExitStatus status) { if (status == QProcess::CrashExit) { emit abortLaunch(tr("Profiler aborted")); } if (m_profilerProcess) { m_profilerProcess->deleteLater(); m_profilerProcess = 0; } } void JProfiler::beginProfilingImpl(LaunchTask* process) { listeningPort = globalSettings->get("JProfilerPort").toInt(); QProcess* profiler = new QProcess(this); QStringList profilerArgs = { "-d", QString::number(process->pid()), "--gui", "-p", QString::number(listeningPort) }; auto basePath = globalSettings->get("JProfilerPath").toString(); #ifdef Q_OS_WIN QString profilerProgram = QDir(basePath).absoluteFilePath("bin/jpenable.exe"); #else QString profilerProgram = QDir(basePath).absoluteFilePath("bin/jpenable"); #endif profiler->setArguments(profilerArgs); profiler->setProgram(profilerProgram); connect(profiler, &QProcess::started, this, &JProfiler::profilerStarted); connect(profiler, &QProcess::finished, this, &JProfiler::profilerFinished); m_profilerProcess = profiler; profiler->start(); } void JProfilerFactory::registerSettings(SettingsObject* settings) { settings->registerSetting("JProfilerPath"); settings->registerSetting("JProfilerPort", 42042); globalSettings = settings; } BaseExternalTool* JProfilerFactory::createTool(BaseInstance* instance, QObject* parent) { return new JProfiler(globalSettings, instance, parent); } bool JProfilerFactory::check(QString* error) { return check(globalSettings->get("JProfilerPath").toString(), error); } bool JProfilerFactory::check(const QString& path, QString* error) { if (path.isEmpty()) { *error = QObject::tr("Empty path"); return false; } QDir dir(path); if (!dir.exists()) { *error = QObject::tr("Path does not exist"); return false; } if (!dir.exists("bin") || !(dir.exists("bin/jprofiler") || dir.exists("bin/jprofiler.exe")) || !dir.exists("bin/agent.jar")) { *error = QObject::tr("Invalid JProfiler install"); return false; } return true; } #include "JProfiler.moc" PrismLauncher-11.0.3/launcher/tools/JVisualVM.cpp0000644000175100017510000000503715224505336021255 0ustar runnerrunner#include "JVisualVM.h" #include #include #include "BaseInstance.h" #include "launch/LaunchTask.h" #include "settings/SettingsObject.h" class JVisualVM : public BaseProfiler { Q_OBJECT public: JVisualVM(SettingsObject* settings, BaseInstance* instance, QObject* parent = 0); private slots: void profilerStarted(); void profilerFinished(int exit, QProcess::ExitStatus status); protected: void beginProfilingImpl(LaunchTask* process); }; JVisualVM::JVisualVM(SettingsObject* settings, BaseInstance* instance, QObject* parent) : BaseProfiler(settings, instance, parent) {} void JVisualVM::profilerStarted() { emit readyToLaunch(tr("VisualVM started")); } void JVisualVM::profilerFinished([[maybe_unused]] int exit, QProcess::ExitStatus status) { if (status == QProcess::CrashExit) { emit abortLaunch(tr("Profiler aborted")); } if (m_profilerProcess) { m_profilerProcess->deleteLater(); m_profilerProcess = 0; } } void JVisualVM::beginProfilingImpl(LaunchTask* process) { QProcess* profiler = new QProcess(this); QStringList profilerArgs = { "--openpid", QString::number(process->pid()) }; auto programPath = globalSettings->get("JVisualVMPath").toString(); profiler->setArguments(profilerArgs); profiler->setProgram(programPath); connect(profiler, &QProcess::started, this, &JVisualVM::profilerStarted); connect(profiler, &QProcess::finished, this, &JVisualVM::profilerFinished); profiler->start(); m_profilerProcess = profiler; } void JVisualVMFactory::registerSettings(SettingsObject* settings) { QString defaultValue = QStandardPaths::findExecutable("jvisualvm"); if (defaultValue.isNull()) { defaultValue = QStandardPaths::findExecutable("visualvm"); } settings->registerSetting("JVisualVMPath", defaultValue); globalSettings = settings; } BaseExternalTool* JVisualVMFactory::createTool(BaseInstance* instance, QObject* parent) { return new JVisualVM(globalSettings, instance, parent); } bool JVisualVMFactory::check(QString* error) { return check(globalSettings->get("JVisualVMPath").toString(), error); } bool JVisualVMFactory::check(const QString& path, QString* error) { if (path.isEmpty()) { *error = QObject::tr("Empty path"); return false; } QFileInfo finfo(path); if (!finfo.isExecutable() || !finfo.fileName().contains("visualvm")) { *error = QObject::tr("Invalid path to VisualVM"); return false; } return true; } #include "JVisualVM.moc" PrismLauncher-11.0.3/launcher/tools/BaseProfiler.cpp0000644000175100017510000000142215224505336022004 0ustar runnerrunner#include "BaseProfiler.h" #include "QObjectPtr.h" #include BaseProfiler::BaseProfiler(SettingsObject* settings, BaseInstance* instance, QObject* parent) : BaseExternalTool(settings, instance, parent) {} void BaseProfiler::beginProfiling(LaunchTask* process) { beginProfilingImpl(process); } void BaseProfiler::abortProfiling() { abortProfilingImpl(); } void BaseProfiler::abortProfilingImpl() { if (!m_profilerProcess) { return; } m_profilerProcess->terminate(); m_profilerProcess->deleteLater(); m_profilerProcess = 0; emit abortLaunch(tr("Profiler aborted")); } BaseProfiler* BaseProfilerFactory::createProfiler(BaseInstance* instance, QObject* parent) { return qobject_cast(createTool(instance, parent)); } PrismLauncher-11.0.3/launcher/tools/BaseExternalTool.h0000644000175100017510000000240615224505336022312 0ustar runnerrunner#pragma once #include #include class BaseInstance; class SettingsObject; class QProcess; class BaseExternalTool : public QObject { Q_OBJECT public: explicit BaseExternalTool(SettingsObject* settings, BaseInstance* instance, QObject* parent = 0); virtual ~BaseExternalTool(); protected: BaseInstance* m_instance; SettingsObject* globalSettings; }; class BaseDetachedTool : public BaseExternalTool { Q_OBJECT public: explicit BaseDetachedTool(SettingsObject* settings, BaseInstance* instance, QObject* parent = 0); public slots: void run(); protected: virtual void runImpl() = 0; }; class BaseExternalToolFactory { public: virtual ~BaseExternalToolFactory(); virtual QString name() const = 0; virtual void registerSettings(SettingsObject* settings) = 0; virtual BaseExternalTool* createTool(BaseInstance* instance, QObject* parent = 0) = 0; virtual bool check(QString* error) = 0; virtual bool check(const QString& path, QString* error) = 0; protected: SettingsObject* globalSettings; }; class BaseDetachedToolFactory : public BaseExternalToolFactory { public: virtual BaseDetachedTool* createDetachedTool(BaseInstance* instance, QObject* parent = 0); }; PrismLauncher-11.0.3/launcher/tools/JVisualVM.h0000644000175100017510000000064315224505336020720 0ustar runnerrunner#pragma once #include "BaseProfiler.h" class JVisualVMFactory : public BaseProfilerFactory { public: QString name() const override { return "VisualVM"; } void registerSettings(SettingsObject* settings) override; BaseExternalTool* createTool(BaseInstance* instance, QObject* parent = 0) override; bool check(QString* error) override; bool check(const QString& path, QString* error) override; }; PrismLauncher-11.0.3/launcher/tools/GenericProfiler.cpp0000644000175100017510000000306115224505336022507 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "GenericProfiler.h" #include "BaseInstance.h" #include "launch/LaunchTask.h" #include "settings/SettingsObject.h" class GenericProfiler : public BaseProfiler { Q_OBJECT public: GenericProfiler(SettingsObject* settings, BaseInstance* instance, QObject* parent = 0); protected: void beginProfilingImpl(LaunchTask* process); }; GenericProfiler::GenericProfiler(SettingsObject* settings, BaseInstance* instance, QObject* parent) : BaseProfiler(settings, instance, parent) {} void GenericProfiler::beginProfilingImpl(LaunchTask* process) { emit readyToLaunch(tr("Started process: %1").arg(process->pid())); } BaseExternalTool* GenericProfilerFactory::createTool(BaseInstance* instance, QObject* parent) { return new GenericProfiler(globalSettings, instance, parent); } #include "GenericProfiler.moc" PrismLauncher-11.0.3/launcher/tools/MCEditTool.cpp0000644000175100017510000000341115224505336021372 0ustar runnerrunner#include "MCEditTool.h" #include #include #include #include "BaseInstance.h" #include "minecraft/MinecraftInstance.h" #include "settings/SettingsObject.h" MCEditTool::MCEditTool(SettingsObject* settings) { settings->registerSetting("MCEditPath"); m_settings = settings; } void MCEditTool::setPath(QString& path) { m_settings->set("MCEditPath", path); } QString MCEditTool::path() const { return m_settings->get("MCEditPath").toString(); } bool MCEditTool::check(const QString& toolPath, QString& error) { if (toolPath.isEmpty()) { error = QObject::tr("Path is empty"); return false; } const QDir dir(toolPath); if (!dir.exists()) { error = QObject::tr("Path does not exist"); return false; } if (!dir.exists("mcedit.sh") && !dir.exists("mcedit.py") && !dir.exists("mcedit.exe") && !dir.exists("Contents") && !dir.exists("mcedit2.exe")) { error = QObject::tr("Path does not seem to be a MCEdit path"); return false; } return true; } QString MCEditTool::getProgramPath() { #ifdef Q_OS_MACOS return path(); #else const QString mceditPath = path(); QDir mceditDir(mceditPath); #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) if (mceditDir.exists("mcedit.sh")) { return mceditDir.absoluteFilePath("mcedit.sh"); } else if (mceditDir.exists("mcedit.py")) { return mceditDir.absoluteFilePath("mcedit.py"); } return QString(); #elif defined(Q_OS_WIN32) if (mceditDir.exists("mcedit.exe")) { return mceditDir.absoluteFilePath("mcedit.exe"); } else if (mceditDir.exists("mcedit2.exe")) { return mceditDir.absoluteFilePath("mcedit2.exe"); } return QString(); #endif #endif } PrismLauncher-11.0.3/launcher/tools/BaseProfiler.h0000644000175100017510000000147515224505336021461 0ustar runnerrunner#pragma once #include "BaseExternalTool.h" #include "QObjectPtr.h" class BaseInstance; class SettingsObject; class LaunchTask; class QProcess; class BaseProfiler : public BaseExternalTool { Q_OBJECT public: explicit BaseProfiler(SettingsObject* settings, BaseInstance* instance, QObject* parent = 0); public slots: void beginProfiling(LaunchTask* process); void abortProfiling(); protected: QProcess* m_profilerProcess; virtual void beginProfilingImpl(LaunchTask* process) = 0; virtual void abortProfilingImpl(); signals: void readyToLaunch(const QString& message); void abortLaunch(const QString& message); }; class BaseProfilerFactory : public BaseExternalToolFactory { public: virtual BaseProfiler* createProfiler(BaseInstance* instance, QObject* parent = 0); }; PrismLauncher-11.0.3/launcher/tools/JProfiler.h0000644000175100017510000000064415224505336020775 0ustar runnerrunner#pragma once #include "BaseProfiler.h" class JProfilerFactory : public BaseProfilerFactory { public: QString name() const override { return "JProfiler"; } void registerSettings(SettingsObject* settings) override; BaseExternalTool* createTool(BaseInstance* instance, QObject* parent = 0) override; bool check(QString* error) override; bool check(const QString& path, QString* error) override; }; PrismLauncher-11.0.3/launcher/logs/0000755000175100017510000000000015224505336016530 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/logs/AnonymizeLog.cpp0000644000175100017510000000601715224505336021653 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2025 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AnonymizeLog.h" #include struct RegReplace { RegReplace(QRegularExpression r, QString w) : reg(r), with(w) { reg.optimize(); } QRegularExpression reg; QString with; }; static const QVector anonymizeRules = { RegReplace(QRegularExpression("C:\\\\Users\\\\([^\\\\]+)\\\\", QRegularExpression::CaseInsensitiveOption), "C:\\Users\\********\\"), // windows RegReplace(QRegularExpression("C:\\/Users\\/([^\\/]+)\\/", QRegularExpression::CaseInsensitiveOption), "C:/Users/********/"), // windows with forward slashes RegReplace(QRegularExpression("(?)"), // SESSION_TOKEN RegReplace(QRegularExpression("new refresh token: \"[^\"]+\"", QRegularExpression::CaseInsensitiveOption), "new refresh token: \"\""), // refresh token RegReplace(QRegularExpression("\"device_code\" : \"[^\"]+\"", QRegularExpression::CaseInsensitiveOption), "\"device_code\" : \"\""), // device code }; void anonymizeLog(QString& log) { for (auto rule : anonymizeRules) { log.replace(rule.reg, rule.with); } } PrismLauncher-11.0.3/launcher/logs/LogParser.h0000644000175100017510000000402415224505336020577 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2025 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include #include #include #include #include #include #include #include #include "MessageLevel.h" class LogParser { public: struct LogEntry { QString logger; MessageLevel level; QString levelText; QDateTime timestamp; QString thread; QString message; }; struct Partial { QString data; }; struct PlainText { QString message; }; struct Error { QString errMessage; QXmlStreamReader::Error error; }; using ParsedItem = std::variant; public: LogParser() = default; void appendLine(QAnyStringView data); std::optional parseNext(); QList parseAvailable(); std::optional getError(); /// guess log level from a line of game log static MessageLevel guessLevel(const QString& line, MessageLevel previous); protected: std::optional parseAttributes(); void setError(); void clearError(); std::optional parseLog4J(); private: QString m_buffer; QString m_partialData; QXmlStreamReader m_parser; std::optional m_error; }; PrismLauncher-11.0.3/launcher/logs/LogParser.cpp0000644000175100017510000002652615224505336021145 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2025 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "LogParser.h" #include #include "MessageLevel.h" using namespace Qt::Literals::StringLiterals; void LogParser::appendLine(QAnyStringView data) { if (!m_partialData.isEmpty()) { m_buffer = QString(m_partialData); m_buffer.append("\n"); m_partialData.clear(); } m_buffer.append(data.toString()); } std::optional LogParser::getError() { return m_error; } std::optional LogParser::parseAttributes() { LogParser::LogEntry entry{ "", MessageLevel::Info, }; auto attributes = m_parser.attributes(); for (const auto& attr : attributes) { auto name = attr.name(); auto value = attr.value(); if (name == "logger"_L1) { entry.logger = value.trimmed().toString(); } else if (name == "timestamp"_L1) { if (value.trimmed().isEmpty()) { m_parser.raiseError("log4j:Event Missing required attribute: timestamp"); return {}; } entry.timestamp = QDateTime::fromSecsSinceEpoch(value.trimmed().toLongLong()); } else if (name == "level"_L1) { entry.levelText = value.trimmed().toString(); entry.level = MessageLevel::fromName(entry.levelText); } else if (name == "thread"_L1) { entry.thread = value.trimmed().toString(); } } if (entry.logger.isEmpty()) { m_parser.raiseError("log4j:Event Missing required attribute: logger"); return {}; } return entry; } void LogParser::setError() { m_error = { m_parser.errorString(), m_parser.error(), }; } void LogParser::clearError() { m_error = {}; // clear previous error } bool isPotentialLog4JStart(QStringView buffer) { static QString target = QStringLiteral(" LogParser::parseNext() { clearError(); if (m_buffer.isEmpty()) { return {}; } if (m_buffer.trimmed().isEmpty()) { auto text = QString(m_buffer); m_buffer.clear(); return LogParser::PlainText{ text }; } // check if we have a full xml log4j event bool isCompleteLog4j = false; m_parser.clear(); m_parser.setNamespaceProcessing(false); m_parser.addData(m_buffer); if (m_parser.readNextStartElement()) { if (m_parser.qualifiedName().compare("log4j:Event"_L1, Qt::CaseInsensitive) == 0) { int depth = 1; bool eod = false; while (depth > 0 && !eod) { auto tok = m_parser.readNext(); switch (tok) { case QXmlStreamReader::TokenType::StartElement: { depth += 1; } break; case QXmlStreamReader::TokenType::EndElement: { depth -= 1; } break; case QXmlStreamReader::TokenType::EndDocument: { eod = true; // break outer while loop } break; default: { // no op } } if (m_parser.hasError()) { break; } } isCompleteLog4j = depth == 0; } } if (isCompleteLog4j) { return parseLog4J(); } else { if (isPotentialLog4JStart(m_buffer)) { m_partialData = QString(m_buffer); return LogParser::Partial{ QString(m_buffer) }; } int start = 0; auto bufView = QStringView(m_buffer); while (start < bufView.length()) { if (qsizetype pos = bufView.right(bufView.length() - start).indexOf('<'); pos != -1) { auto slicestart = start + pos; auto slice = bufView.right(bufView.length() - slicestart); if (isPotentialLog4JStart(slice)) { if (slicestart > 0) { auto text = m_buffer.left(slicestart); m_buffer = m_buffer.right(m_buffer.length() - slicestart); if (!text.trimmed().isEmpty()) { return LogParser::PlainText{ text }; } } m_partialData = QString(m_buffer); return LogParser::Partial{ QString(m_buffer) }; } start = slicestart + 1; } else { break; } } // no log4j found, all plain text auto text = QString(m_buffer); m_buffer.clear(); return LogParser::PlainText{ text }; } } QList LogParser::parseAvailable() { QList items; bool doNext = true; while (doNext) { auto item_ = parseNext(); if (m_error.has_value()) { return {}; } if (item_.has_value()) { auto item = item_.value(); if (std::holds_alternative(item)) { break; } else { items.push_back(item); } } else { doNext = false; } } return items; } std::optional LogParser::parseLog4J() { m_parser.clear(); m_parser.setNamespaceProcessing(false); m_parser.addData(m_buffer); m_parser.readNextStartElement(); if (m_parser.qualifiedName().compare("log4j:Event"_L1, Qt::CaseInsensitive) == 0) { auto entry_ = parseAttributes(); if (!entry_.has_value()) { setError(); return {}; } auto entry = entry_.value(); bool foundMessage = false; int depth = 1; enum parseOp { noOp, entryReady, parseError }; auto foundStart = [&]() -> parseOp { depth += 1; if (m_parser.qualifiedName().compare("log4j:Message"_L1, Qt::CaseInsensitive) == 0) { QString message; bool messageComplete = false; while (!messageComplete) { auto tok = m_parser.readNext(); switch (tok) { case QXmlStreamReader::TokenType::Characters: { message.append(m_parser.text()); } break; case QXmlStreamReader::TokenType::EndElement: { if (m_parser.qualifiedName().compare("log4j:Message"_L1, Qt::CaseInsensitive) == 0) { messageComplete = true; } } break; case QXmlStreamReader::TokenType::EndDocument: { return parseError; // parse fail } break; default: { // no op } } if (m_parser.hasError()) { return parseError; } } entry.message = message; foundMessage = true; depth -= 1; } return noOp; }; auto foundEnd = [&]() -> parseOp { depth -= 1; if (depth == 0 && m_parser.qualifiedName().compare("log4j:Event"_L1, Qt::CaseInsensitive) == 0) { if (foundMessage) { auto consumed = m_parser.characterOffset(); if (consumed > 0 && consumed <= m_buffer.length()) { m_buffer = m_buffer.right(m_buffer.length() - consumed); // potential whitespace preserved for next item } clearError(); return entryReady; } m_parser.raiseError("log4j:Event Missing required attribute: message"); setError(); return parseError; } return noOp; }; while (!m_parser.atEnd()) { auto tok = m_parser.readNext(); parseOp op = noOp; switch (tok) { case QXmlStreamReader::TokenType::StartElement: { op = foundStart(); } break; case QXmlStreamReader::TokenType::EndElement: { op = foundEnd(); } break; case QXmlStreamReader::TokenType::EndDocument: { return {}; } break; default: { // no op } } switch (op) { case parseError: return {}; // parse fail or error case entryReady: return entry; case noOp: default: { // no op } } if (m_parser.hasError()) { return {}; } } } throw std::runtime_error("unreachable: already verified this was a complete log4j:Event"); } MessageLevel LogParser::guessLevel(const QString& line, MessageLevel previous) { static const QRegularExpression LINE_WITH_LEVEL("^\\[(?[0-9:]+)\\] \\[[^/]+/(?[^\\]]+)\\]"); auto match = LINE_WITH_LEVEL.match(line); if (match.hasMatch()) { // New style logs from log4j QString timestamp = match.captured("timestamp"); QString levelStr = match.captured("level"); return MessageLevel::fromName(levelStr); } else { // Old style forge logs if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") || line.contains("[FINEST]")) return MessageLevel::Info; if (line.contains("[SEVERE]") || line.contains("[STDERR]")) return MessageLevel::Error; if (line.contains("[WARNING]")) return MessageLevel::Warning; if (line.contains("[DEBUG]")) return MessageLevel::Debug; } if (line.contains("Exception: ") || line.contains("Throwable: ")) return MessageLevel::Error; if (line.startsWith("Caused by: ") || line.startsWith("Exception in thread")) return MessageLevel::Error; if (line.contains("overwriting existing")) return MessageLevel::Fatal; if (line.startsWith("\t") || line.startsWith(" ")) return previous; return MessageLevel::Unknown; } PrismLauncher-11.0.3/launcher/logs/AnonymizeLog.h0000644000175100017510000000303315224505336021313 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2025 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include void anonymizeLog(QString& log); PrismLauncher-11.0.3/launcher/VersionProxyModel.h0000644000175100017510000000454515224505336021415 0ustar runnerrunner#pragma once #include #include "BaseVersionList.h" #include class VersionFilterModel; class VersionProxyModel : public QAbstractProxyModel { Q_OBJECT public: enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName, JavaMajor }; using FilterMap = QHash; public: VersionProxyModel(QObject* parent = 0); virtual ~VersionProxyModel() {}; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override; virtual QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; virtual QModelIndex parent(const QModelIndex& child) const override; virtual void setSourceModel(QAbstractItemModel* sourceModel) override; const FilterMap& filters() const; const QString& search() const; void setFilter(BaseVersionList::ModelRoles column, Filter filter); void setSearch(const QString& search); void clearFilters(); QModelIndex getRecommended() const; QModelIndex getVersion(const QString& version) const; void setCurrentVersion(const QString& version); private slots: void sourceDataChanged(const QModelIndex& source_top_left, const QModelIndex& source_bottom_right); void sourceAboutToBeReset(); void sourceReset(); void sourceRowsAboutToBeInserted(const QModelIndex& parent, int first, int last); void sourceRowsInserted(const QModelIndex& parent, int first, int last); void sourceRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last); void sourceRowsRemoved(const QModelIndex& parent, int first, int last); private: QList m_columns; FilterMap m_filters; QString m_search; BaseVersionList::RoleList roles; VersionFilterModel* filterModel; bool hasRecommended = false; bool hasLatest = false; QString m_currentVersion; }; PrismLauncher-11.0.3/launcher/LibraryUtils.cpp0000644000175100017510000001212515224505336020716 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * PrismLauncher - Minecraft Launcher * Copyright (C) 2022 Jan Drögehoff * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "FileSystem.h" #include "Json.h" #include "LibraryUtils.h" #ifdef __GLIBC__ #ifndef _GNU_SOURCE #define _GNU_SOURCE #define UNDEF_GNU_SOURCE #endif #include #include #endif namespace LibraryUtils { QString findMangoHud() { /** * Guess MangoHud install location by searching for vulkan layers in this order: * * $VK_LAYER_PATH * $XDG_DATA_DIRS (/usr/local/share/:/usr/share/) * $XDG_DATA_HOME (~/.local/share) * /etc * $XDG_CONFIG_DIRS (/etc/xdg) * $XDG_CONFIG_HOME (~/.config) * * @returns Absolute path of libMangoHud.so if found and empty QString otherwise. */ QStringList vkLayerList; { QString home = QDir::homePath(); QString vkLayerPath = qEnvironmentVariable("VK_LAYER_PATH"); if (!vkLayerPath.isEmpty()) { vkLayerList << vkLayerPath; } QStringList xdgDataDirs = qEnvironmentVariable("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/").split(QLatin1String(":")); for (QString dir : xdgDataDirs) { vkLayerList << FS::PathCombine(dir, "vulkan", "implicit_layer.d"); } QString xdgDataHome = qEnvironmentVariable("XDG_DATA_HOME"); if (xdgDataHome.isEmpty()) { xdgDataHome = FS::PathCombine(home, ".local", "share"); } vkLayerList << FS::PathCombine(xdgDataHome, "vulkan", "implicit_layer.d"); vkLayerList << "/etc"; QStringList xdgConfigDirs = qEnvironmentVariable("XDG_CONFIG_DIRS", "/etc/xdg").split(QLatin1String(":")); for (QString dir : xdgConfigDirs) { vkLayerList << FS::PathCombine(dir, "vulkan", "implicit_layer.d"); } QString xdgConfigHome = qEnvironmentVariable("XDG_CONFIG_HOME"); if (xdgConfigHome.isEmpty()) { xdgConfigHome = FS::PathCombine(home, ".config"); } vkLayerList << FS::PathCombine(xdgConfigHome, "vulkan", "implicit_layer.d"); } for (const QString& vkLayer : vkLayerList) { // prefer to use architecture specific vulkan layers QString currentArch = QSysInfo::currentCpuArchitecture(); if (currentArch == "arm64") { currentArch = "aarch64"; } QStringList manifestNames = { QString("MangoHud.%1.json").arg(currentArch), "MangoHud.json" }; QString filePath{}; for (const QString& manifestName : manifestNames) { QString tryPath = FS::PathCombine(vkLayer, manifestName); if (QFile::exists(tryPath)) { filePath = tryPath; break; } } if (filePath.isEmpty()) { continue; } try { auto conf = Json::requireDocument(filePath, vkLayer); auto confObject = Json::requireObject(conf, vkLayer); auto layer = confObject["layer"].toObject(); QString libraryName = layer["library_path"].toString(); if (libraryName.isEmpty()) { continue; } if (QFileInfo(libraryName).isAbsolute()) { return libraryName; } #ifdef __GLIBC__ // Check whether mangohud is usable on a glibc based system QString libraryPath = find(libraryName); if (!libraryPath.isEmpty()) { return libraryPath; } #else // Without glibc return recorded shared library as-is. return libraryName; #endif } catch (const Exception& e) { } } return {}; } QString find(QString libName) { #ifdef __GLIBC__ const char* library = libName.toLocal8Bit().constData(); void* handle = dlopen(library, RTLD_NOW); if (!handle) { qCritical() << "dlopen() failed:" << dlerror(); return {}; } char path[PATH_MAX]; if (dlinfo(handle, RTLD_DI_ORIGIN, path) == -1) { qCritical() << "dlinfo() failed:" << dlerror(); dlclose(handle); return {}; } auto fullPath = FS::PathCombine(QString(path), libName); dlclose(handle); return fullPath; #else qWarning() << "LibraryUtils::find is not implemented on this platform"; return {}; #endif } } // namespace LibraryUtils #ifdef UNDEF_GNU_SOURCE #undef _GNU_SOURCE #undef UNDEF_GNU_SOURCE #endif PrismLauncher-11.0.3/launcher/DesktopServices.h0000644000175100017510000000206215224505336021052 0ustar runnerrunner#pragma once #include #include class QFileInfo; /** * This wraps around QDesktopServices and adds workarounds where needed * Use this instead of QDesktopServices! */ namespace DesktopServices { /** * Open a path in whatever application is applicable. * @param ensureFolderPathExists Make sure the path exists */ bool openPath(const QFileInfo& path, bool ensureFolderPathExists = false); /** * Open a path in whatever application is applicable. * @param ensureFolderPathExists Make sure the path exists */ bool openPath(const QString& path, bool ensureFolderPathExists = false); /** * Run an application */ bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0); /** * Open the URL, most likely in a browser. Maybe. */ bool openUrl(const QUrl& url); /** * Determine whether the launcher is running in a Flatpak environment */ bool isFlatpak(); /** * Determine whether the launcher is running in a Snap environment */ bool isSnap(); } // namespace DesktopServices PrismLauncher-11.0.3/launcher/MMCZip.cpp0000644000175100017510000002747515224505336017406 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MMCZip.h" #include #include "FileSystem.h" #include "archive/ArchiveReader.h" #include "archive/ArchiveWriter.h" #include #include #include #include #include namespace MMCZip { // ours using FilterFunction = std::function; #if defined(LAUNCHER_APPLICATION) bool mergeZipFiles(ArchiveWriter& into, QFileInfo from, QSet& contained, const FilterFunction& filter = nullptr) { ArchiveReader r(from.absoluteFilePath()); return r.parse([&into, &contained, &filter, from](ArchiveReader::File* f) { auto filename = f->filename(); if (filter && !filter(filename)) { qDebug() << "Skipping file" << filename << "from" << from.fileName() << "- filtered"; f->skip(); return true; } if (contained.contains(filename)) { qDebug() << "Skipping already contained file" << filename << "from" << from.fileName(); f->skip(); return true; } contained.insert(filename); if (!into.addFile(f)) { qCritical() << "Failed to copy data of" << filename << "into the jar"; return false; } return true; }); } bool compressDirFiles(ArchiveWriter& zip, QString dir, QFileInfoList files) { QDir directory(dir); if (!directory.exists()) return false; for (auto e : files) { auto filePath = directory.relativeFilePath(e.absoluteFilePath()); auto srcPath = e.absoluteFilePath(); if (!zip.addFile(srcPath, filePath)) return false; } return true; } // ours bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods) { ArchiveWriter zipOut(targetJarPath); if (!zipOut.open()) { FS::deletePath(targetJarPath); qCritical() << "Failed to open the minecraft.jar for modding"; return false; } // Files already added to the jar. // These files will be skipped. QSet addedFiles; // Modify the jar // This needs to be done in reverse-order to ensure we respect the loading order of components for (auto i = mods.crbegin(); i != mods.crend(); i++) { const auto* mod = *i; // do not merge disabled mods. if (!mod->enabled()) continue; if (mod->type() == ResourceType::ZIPFILE) { if (!mergeZipFiles(zipOut, mod->fileinfo(), addedFiles)) { zipOut.close(); FS::deletePath(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } } else if (mod->type() == ResourceType::SINGLEFILE) { // FIXME: buggy - does not work with addedFiles auto filename = mod->fileinfo(); if (!zipOut.addFile(filename.absoluteFilePath(), filename.fileName())) { zipOut.close(); FS::deletePath(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } addedFiles.insert(filename.fileName()); } else if (mod->type() == ResourceType::FOLDER) { // untested, but seems to be unused / not possible to reach // FIXME: buggy - does not work with addedFiles auto filename = mod->fileinfo(); QString what_to_zip = filename.absoluteFilePath(); QDir dir(what_to_zip); dir.cdUp(); QString parent_dir = dir.absolutePath(); auto files = QFileInfoList(); collectFileListRecursively(what_to_zip, nullptr, &files, nullptr); for (auto e : files) { if (addedFiles.contains(e.filePath())) files.removeAll(e); } if (!compressDirFiles(zipOut, parent_dir, files)) { zipOut.close(); FS::deletePath(targetJarPath); qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar."; return false; } qDebug() << "Adding folder" << filename.fileName() << "from" << filename.absoluteFilePath(); } else { // Make sure we do not continue launching when something is missing or undefined... zipOut.close(); FS::deletePath(targetJarPath); qCritical() << "Failed to add unknown mod type" << mod->fileinfo().fileName() << "to the jar."; return false; } } if (!mergeZipFiles(zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key) { return !key.contains("META-INF"); })) { zipOut.close(); FS::deletePath(targetJarPath); qCritical() << "Failed to insert minecraft.jar contents."; return false; } // Recompress the jar if (!zipOut.close()) { FS::deletePath(targetJarPath); qCritical() << "Failed to finalize minecraft.jar!"; return false; } return true; } #endif // ours std::optional extractSubDir(ArchiveReader* zip, const QString& subdir, const QString& target) { auto target_top_dir = QUrl::fromLocalFile(target); QStringList extracted; qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target; if (!zip->collectFiles()) { qWarning() << "Failed to enumerate files in archive"; return std::nullopt; } if (zip->getFiles().isEmpty()) { qDebug() << "Extracting empty archives seems odd..."; return extracted; } auto extPtr = ArchiveWriter::createDiskWriter(); auto ext = extPtr.get(); if (!zip->parse([&subdir, &target, &target_top_dir, ext, &extracted](ArchiveReader::File* f) { QString file_name = f->filename(); file_name = FS::RemoveInvalidPathChars(file_name); if (!file_name.startsWith(subdir)) { f->skip(); return true; } auto relative_file_name = QDir::fromNativeSeparators(file_name.mid(subdir.size())); auto original_name = relative_file_name; // Fix subdirs/files ending with a / getting transformed into absolute paths if (relative_file_name.startsWith('/')) relative_file_name = relative_file_name.mid(1); // Fix weird "folders with a single file get squashed" thing QString sub_path; if (relative_file_name.contains('/') && !relative_file_name.endsWith('/')) { sub_path = relative_file_name.section('/', 0, -2) + '/'; FS::ensureFolderPathExists(FS::PathCombine(target, sub_path)); relative_file_name = relative_file_name.split('/').last(); } QString target_file_path; if (relative_file_name.isEmpty()) { target_file_path = target + '/'; } else { target_file_path = FS::PathCombine(target_top_dir.toLocalFile(), sub_path, relative_file_name); if (relative_file_name.endsWith('/') && !target_file_path.endsWith('/')) target_file_path += '/'; } if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) { qWarning() << "Extracting" << relative_file_name << "was cancelled, because it was effectively outside of the target path" << target; return false; } if (!f->writeFile(ext, target_file_path, target)) { qWarning() << "Failed to extract file" << original_name << "to" << target_file_path; return false; } extracted.append(target_file_path); qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; return true; })) { qWarning() << "Failed to parse file" << zip->getZipName(); FS::removeFiles(extracted); return std::nullopt; } return extracted; } // ours std::optional extractDir(QString fileCompressed, QString dir) { // check if this is a minimum size empty zip file... QFileInfo fileInfo(fileCompressed); if (fileInfo.size() == 22) { return QStringList(); } ArchiveReader zip(fileCompressed); return extractSubDir(&zip, "", dir); } // ours std::optional extractDir(QString fileCompressed, QString subdir, QString dir) { // check if this is a minimum size empty zip file... QFileInfo fileInfo(fileCompressed); if (fileInfo.size() == 22) { return QStringList(); } ArchiveReader zip(fileCompressed); return extractSubDir(&zip, subdir, dir); } // ours bool extractFile(QString fileCompressed, QString file, QString target) { // check if this is a minimum size empty zip file... QFileInfo fileInfo(fileCompressed); if (fileInfo.size() == 22) { return true; } ArchiveReader zip(fileCompressed); auto f = zip.goToFile(file); if (!f) { return false; } auto extPtr = ArchiveWriter::createDiskWriter(); auto ext = extPtr.get(); return f->writeFile(ext, target); } bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFileFunction excludeFilter) { QDir rootDirectory(rootDir); if (!rootDirectory.exists()) return false; QDir directory; if (subDir == nullptr) directory = rootDirectory; else directory = QDir(subDir); if (!directory.exists()) return false; // shouldn't ever happen // recurse directories QFileInfoList entries = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden); for (const auto& e : entries) { if (!collectFileListRecursively(rootDir, e.filePath(), files, excludeFilter)) return false; } // collect files entries = directory.entryInfoList(QDir::Files); for (const auto& e : entries) { if (excludeFilter && excludeFilter(e)) { QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath()); qDebug() << "Skipping file" << relativeFilePath; continue; } files->append(e); // we want the original paths for compressDirFiles } return true; } } // namespace MMCZip PrismLauncher-11.0.3/launcher/launch/0000755000175100017510000000000015224505336017036 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/launch/LaunchTask.cpp0000644000175100017510000002446015224505336021605 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Authors: Orochimarufan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "launch/LaunchTask.h" #include #include #include #include #include #include #include #include "MessageLevel.h" #include "tasks/Task.h" void LaunchTask::init() { m_instance->setRunning(true); } std::unique_ptr LaunchTask::create(MinecraftInstance* inst) { auto task = std::unique_ptr(new LaunchTask(inst)); task->init(); return task; } LaunchTask::LaunchTask(MinecraftInstance* instance) : m_instance(instance) {} void LaunchTask::appendStep(shared_qobject_ptr step) { m_steps.append(step); } void LaunchTask::prependStep(shared_qobject_ptr step) { m_steps.prepend(step); } void LaunchTask::executeTask() { m_instance->setCrashed(false); if (!m_steps.size()) { state = LaunchTask::Finished; emitSucceeded(); return; } state = LaunchTask::Running; onStepFinished(); } void LaunchTask::onReadyForLaunch() { state = LaunchTask::Waiting; emit readyForLaunch(); } void LaunchTask::onStepFinished() { // initial -> just start the first step if (currentStep == -1) { currentStep++; m_steps[currentStep]->start(); return; } auto step = m_steps[currentStep]; if (step->wasSuccessful()) { // end? if (currentStep == m_steps.size() - 1) { finalizeSteps(true, QString()); } else { currentStep++; step = m_steps[currentStep]; step->start(); } } else { finalizeSteps(false, step->failReason()); } } void LaunchTask::finalizeSteps(bool successful, const QString& error) { for (auto step = currentStep; step >= 0; step--) { m_steps[step]->finalize(); } if (successful) { emitSucceeded(); } else { emitFailed(error); } } void LaunchTask::onProgressReportingRequested() { state = LaunchTask::Waiting; emit requestProgress(m_steps[currentStep].get()); } void LaunchTask::setCensorFilter(QMap filter) { m_censorFilter = filter; } QString LaunchTask::censorPrivateInfo(QString in) { auto iter = m_censorFilter.begin(); while (iter != m_censorFilter.end()) { in.replace(iter.key(), iter.value()); iter++; } return in; } void LaunchTask::proceed() { if (state != LaunchTask::Waiting) { return; } m_steps[currentStep]->proceed(); } bool LaunchTask::canAbort() const { switch (state) { case LaunchTask::Aborted: case LaunchTask::Failed: case LaunchTask::Finished: return false; case LaunchTask::NotStarted: return true; case LaunchTask::Running: case LaunchTask::Waiting: { auto step = m_steps[currentStep]; return step->canAbort(); } } return false; } bool LaunchTask::abort() { switch (state) { case LaunchTask::Aborted: case LaunchTask::Failed: case LaunchTask::Finished: return true; case LaunchTask::NotStarted: { state = LaunchTask::Aborted; emitAborted(); return true; } case LaunchTask::Running: case LaunchTask::Waiting: { auto step = m_steps[currentStep]; if (!step->canAbort()) { return false; } if (step->abort()) { state = LaunchTask::Aborted; return true; } } default: break; } return false; } shared_qobject_ptr LaunchTask::getLogModel() { if (!m_logModel) { m_logModel.reset(new LogModel()); m_logModel->setMaxLines(getConsoleMaxLines(m_instance->settings())); m_logModel->setStopOnOverflow(shouldStopOnConsoleOverflow(m_instance->settings())); // FIXME: should this really be here? m_logModel->setOverflowMessage(tr("Stopped watching the game log because the log length surpassed %1 lines.\n" "You may have to fix your mods because the game is still logging to files and" " likely wasting harddrive space at an alarming rate!") .arg(m_logModel->getMaxLines())); } return m_logModel; } bool LaunchTask::parseXmlLogs(QString const& line, MessageLevel level) { LogParser* parser; switch (static_cast(level)) { case MessageLevel::StdErr: parser = &m_stderrParser; break; case MessageLevel::StdOut: parser = &m_stdoutParser; break; default: return false; } parser->appendLine(line); auto items = parser->parseAvailable(); if (auto err = parser->getError(); err.has_value()) { auto& model = *getLogModel(); model.append(MessageLevel::Error, tr("[Log4j Parse Error] Failed to parse log4j log event: %1").arg(err.value().errMessage)); return false; } if (items.isEmpty()) return true; auto model = getLogModel(); for (auto const& item : items) { if (std::holds_alternative(item)) { auto entry = std::get(item); auto msg = QString("[%1] [%2/%3] [%4]: %5") .arg(entry.timestamp.toString("HH:mm:ss")) .arg(entry.thread) .arg(entry.levelText) .arg(entry.logger) .arg(entry.message); msg = censorPrivateInfo(msg); model->append(entry.level, msg); } else if (std::holds_alternative(item)) { auto msg = std::get(item).message; MessageLevel newLevel = MessageLevel::takeFromLine(msg); if (newLevel == MessageLevel::Unknown) newLevel = LogParser::guessLevel(line, model->previousLevel()); msg = censorPrivateInfo(msg); model->append(newLevel, msg); } } return true; } void LaunchTask::onLogLines(const QStringList& lines, MessageLevel defaultLevel) { for (auto& line : lines) { onLogLine(line, defaultLevel); } } void LaunchTask::onLogLine(QString line, MessageLevel level) { if (parseXmlLogs(line, level)) { return; } // censor private user info line = censorPrivateInfo(line); getLogModel()->append(level, line); } void LaunchTask::emitSucceeded() { m_instance->setRunning(false); Task::emitSucceeded(); } void LaunchTask::emitFailed(QString reason) { m_instance->setRunning(false); m_instance->setCrashed(true); Task::emitFailed(reason); } QString expandVariables(const QString& input, QProcessEnvironment dict) { QString result = input; enum { base, maybeBrace, variable, brace } state = base; int startIdx = -1; for (int i = 0; i < result.length();) { QChar c = result.at(i++); switch (state) { case base: if (c == '$') state = maybeBrace; break; case maybeBrace: if (c == '{') { state = brace; startIdx = i; } else if (c.isLetterOrNumber() || c == '_') { state = variable; startIdx = i - 1; } else { state = base; } break; case brace: if (c == '}') { const auto res = dict.value(result.mid(startIdx, i - 1 - startIdx), ""); if (!res.isEmpty()) { result.replace(startIdx - 2, i - startIdx + 2, res); i = startIdx - 2 + res.length(); } state = base; } break; case variable: if (!c.isLetterOrNumber() && c != '_') { const auto res = dict.value(result.mid(startIdx, i - startIdx - 1), ""); if (!res.isEmpty()) { result.replace(startIdx - 1, i - startIdx, res); i = startIdx - 1 + res.length(); } state = base; } break; } } if (state == variable) { if (const auto res = dict.value(result.mid(startIdx), ""); !res.isEmpty()) result.replace(startIdx - 1, result.length() - startIdx + 1, res); } return result; } QString LaunchTask::substituteVariables(QString& cmd, bool isLaunch) const { return expandVariables(cmd, isLaunch ? m_instance->createLaunchEnvironment() : m_instance->createEnvironment()); } PrismLauncher-11.0.3/launcher/launch/steps/0000755000175100017510000000000015224505336020174 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/launch/steps/PostLaunchCommand.cpp0000644000175100017510000000667315224505336024273 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "PostLaunchCommand.h" #include PostLaunchCommand::PostLaunchCommand(LaunchTask* parent) : LaunchStep(parent) { auto instance = m_parent->instance(); m_command = instance->getPostExitCommand(); m_process.setProcessEnvironment(instance->createEnvironment()); connect(&m_process, &LoggedProcess::log, this, &PostLaunchCommand::logLines); connect(&m_process, &LoggedProcess::stateChanged, this, &PostLaunchCommand::on_state); } void PostLaunchCommand::executeTask() { auto cmd = m_parent->substituteVariables(m_command); emit logLine(tr("Running Post-Launch command: %1").arg(cmd), MessageLevel::Launcher); auto args = QProcess::splitCommand(cmd); const QString program = args.takeFirst(); m_process.start(program, args); } void PostLaunchCommand::on_state(LoggedProcess::State state) { auto getError = [this]() { return tr("Post-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); }; switch (state) { case LoggedProcess::Aborted: case LoggedProcess::Crashed: case LoggedProcess::FailedToStart: { auto error = getError(); emit logLine(error, MessageLevel::Fatal); emitFailed(error); return; } case LoggedProcess::Finished: { if (m_process.exitCode() != 0) { auto error = getError(); emit logLine(error, MessageLevel::Fatal); emitFailed(error); } else { emit logLine(tr("Post-Launch command ran successfully.\n\n"), MessageLevel::Launcher); emitSucceeded(); } } default: break; } } void PostLaunchCommand::setWorkingDirectory(const QString& wd) { m_process.setWorkingDirectory(wd); } bool PostLaunchCommand::abort() { auto state = m_process.state(); if (state == LoggedProcess::Running || state == LoggedProcess::Starting) { m_process.kill(); } return true; } PrismLauncher-11.0.3/launcher/launch/steps/PreLaunchCommand.cpp0000644000175100017510000000665715224505336024076 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "PreLaunchCommand.h" #include PreLaunchCommand::PreLaunchCommand(LaunchTask* parent) : LaunchStep(parent) { auto instance = m_parent->instance(); m_command = instance->getPreLaunchCommand(); m_process.setProcessEnvironment(instance->createEnvironment()); connect(&m_process, &LoggedProcess::log, this, &PreLaunchCommand::logLines); connect(&m_process, &LoggedProcess::stateChanged, this, &PreLaunchCommand::on_state); } void PreLaunchCommand::executeTask() { auto cmd = m_parent->substituteVariables(m_command); emit logLine(tr("Running Pre-Launch command: %1").arg(cmd), MessageLevel::Launcher); auto args = QProcess::splitCommand(cmd); const QString program = args.takeFirst(); m_process.start(program, args); } void PreLaunchCommand::on_state(LoggedProcess::State state) { auto getError = [this]() { return tr("Pre-Launch command failed with code %1.\n\n").arg(m_process.exitCode()); }; switch (state) { case LoggedProcess::Aborted: case LoggedProcess::Crashed: case LoggedProcess::FailedToStart: { auto error = getError(); emit logLine(error, MessageLevel::Fatal); emitFailed(error); return; } case LoggedProcess::Finished: { if (m_process.exitCode() != 0) { auto error = getError(); emit logLine(error, MessageLevel::Fatal); emitFailed(error); } else { emit logLine(tr("Pre-Launch command ran successfully.\n\n"), MessageLevel::Launcher); emitSucceeded(); } } default: break; } } void PreLaunchCommand::setWorkingDirectory(const QString& wd) { m_process.setWorkingDirectory(wd); } bool PreLaunchCommand::abort() { auto state = m_process.state(); if (state == LoggedProcess::Running || state == LoggedProcess::Starting) { m_process.kill(); } return true; } PrismLauncher-11.0.3/launcher/launch/steps/LookupServerAddress.cpp0000644000175100017510000000605515224505336024654 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "LookupServerAddress.h" #include LookupServerAddress::LookupServerAddress(LaunchTask* parent) : LaunchStep(parent), m_dnsLookup(new QDnsLookup(this)) { connect(m_dnsLookup, &QDnsLookup::finished, this, &LookupServerAddress::on_dnsLookupFinished); m_dnsLookup->setType(QDnsLookup::SRV); } void LookupServerAddress::setLookupAddress(const QString& lookupAddress) { m_lookupAddress = lookupAddress; m_dnsLookup->setName(QString("_minecraft._tcp.%1").arg(lookupAddress)); } void LookupServerAddress::setOutputAddressPtr(MinecraftTarget::Ptr output) { m_output = std::move(output); } bool LookupServerAddress::abort() { m_dnsLookup->abort(); emitAborted(); return true; } void LookupServerAddress::executeTask() { m_dnsLookup->lookup(); } void LookupServerAddress::on_dnsLookupFinished() { if (isFinished()) { // Aborted return; } if (m_dnsLookup->error() != QDnsLookup::NoError) { emit logLine(QString("Failed to resolve server address (this is NOT an error!) %1: %2\n") .arg(m_dnsLookup->name(), m_dnsLookup->errorString()), MessageLevel::Launcher); resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch // and leave it up to minecraft to fail (or maybe not) when connecting return; } const auto records = m_dnsLookup->serviceRecords(); if (records.empty()) { emit logLine(QString("Failed to resolve server address %1: the DNS lookup succeeded, but no records were returned.\n") .arg(m_dnsLookup->name()), MessageLevel::Warning); resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch // and leave it up to minecraft to fail (or maybe not) when connecting return; } const auto& firstRecord = records.at(0); quint16 port = firstRecord.port(); emit logLine( QString("Resolved server address %1 to %2 with port %3\n").arg(m_dnsLookup->name(), firstRecord.target(), QString::number(port)), MessageLevel::Launcher); resolve(firstRecord.target(), port); } void LookupServerAddress::resolve(const QString& address, quint16 port) { m_output->address = address; m_output->port = port; m_dnsLookup->deleteLater(); emitSucceeded(); } PrismLauncher-11.0.3/launcher/launch/steps/CheckJava.h0000644000175100017510000000251315224505336022165 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include class CheckJava : public LaunchStep { Q_OBJECT public: explicit CheckJava(LaunchTask* parent) : LaunchStep(parent) {}; virtual ~CheckJava() = default; virtual void executeTask(); virtual bool canAbort() const { return false; } private slots: void checkJavaFinished(const JavaChecker::Result& result); private: void printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString& vendor); void printSystemInfo(bool javaIsKnown, bool javaIs64bit); private: QString m_javaPath; QString m_javaSignature; JavaChecker::Ptr m_JavaChecker; }; PrismLauncher-11.0.3/launcher/launch/steps/TextPrint.h0000644000175100017510000000222715224505336022311 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include /* * FIXME: maybe do not export */ class TextPrint : public LaunchStep { Q_OBJECT public: explicit TextPrint(LaunchTask* parent, const QStringList& lines, MessageLevel level); explicit TextPrint(LaunchTask* parent, const QString& line, MessageLevel level); virtual ~TextPrint() {}; virtual void executeTask(); virtual bool canAbort() const; virtual bool abort(); private: QStringList m_lines; MessageLevel m_level; }; PrismLauncher-11.0.3/launcher/launch/steps/PreLaunchCommand.h0000644000175100017510000000215015224505336023523 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "LoggedProcess.h" #include "launch/LaunchStep.h" class PreLaunchCommand : public LaunchStep { Q_OBJECT public: explicit PreLaunchCommand(LaunchTask* parent); virtual ~PreLaunchCommand() {}; virtual void executeTask(); virtual bool abort(); virtual bool canAbort() const { return true; } void setWorkingDirectory(const QString& wd); private slots: void on_state(LoggedProcess::State state); private: LoggedProcess m_process; QString m_command; }; PrismLauncher-11.0.3/launcher/launch/steps/LookupServerAddress.h0000644000175100017510000000252315224505336024315 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include "minecraft/launch/MinecraftTarget.h" class LookupServerAddress : public LaunchStep { Q_OBJECT public: explicit LookupServerAddress(LaunchTask* parent); virtual ~LookupServerAddress() = default; virtual void executeTask(); virtual bool abort(); virtual bool canAbort() const { return true; } void setLookupAddress(const QString& lookupAddress); void setOutputAddressPtr(MinecraftTarget::Ptr output); private slots: void on_dnsLookupFinished(); private: void resolve(const QString& address, quint16 port); QDnsLookup* m_dnsLookup; QString m_lookupAddress; MinecraftTarget::Ptr m_output; }; PrismLauncher-11.0.3/launcher/launch/steps/PrintServers.h0000644000175100017510000000226115224505336023014 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2024 Leia uwu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include class PrintServers : public LaunchStep { Q_OBJECT public: PrintServers(LaunchTask* parent, const QStringList& servers); virtual void executeTask(); virtual bool canAbort() const; private: void resolveServer(const QHostInfo& host_info); QMap m_server_to_address; QStringList m_servers; }; PrismLauncher-11.0.3/launcher/launch/steps/QuitAfterGameStop.h0000644000175100017510000000205015224505336023706 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 dada513 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include class QuitAfterGameStop : public LaunchStep { Q_OBJECT public: explicit QuitAfterGameStop(LaunchTask* parent) : LaunchStep(parent) {}; virtual ~QuitAfterGameStop() = default; virtual void executeTask(); virtual bool canAbort() const { return false; } }; PrismLauncher-11.0.3/launcher/launch/steps/PostLaunchCommand.h0000644000175100017510000000215315224505336023725 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include class PostLaunchCommand : public LaunchStep { Q_OBJECT public: explicit PostLaunchCommand(LaunchTask* parent); virtual ~PostLaunchCommand() {}; virtual void executeTask(); virtual bool abort(); virtual bool canAbort() const { return true; } void setWorkingDirectory(const QString& wd); private slots: void on_state(LoggedProcess::State state); private: LoggedProcess m_process; QString m_command; }; PrismLauncher-11.0.3/launcher/launch/steps/CheckJava.cpp0000644000175100017510000001552215224505336022524 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "CheckJava.h" #include #include #include #include #include #include "java/JavaUtils.h" void CheckJava::executeTask() { auto instance = m_parent->instance(); auto settings = instance->settings(); QString javaPathSetting = settings->get("JavaPath").toString(); m_javaPath = FS::ResolveExecutable(javaPathSetting); bool perInstance = settings->get("OverrideJava").toBool() || settings->get("OverrideJavaLocation").toBool(); auto realJavaPath = QStandardPaths::findExecutable(m_javaPath); if (realJavaPath.isEmpty()) { if (perInstance) { emit logLine(QString("The Java binary \"%1\" couldn't be found. Please fix the Java path " "override in the instance's settings or disable it.") .arg(javaPathSetting), MessageLevel::Warning); } else { emit logLine(QString("The Java binary \"%1\" couldn't be found. Please set up Java in " "the settings.") .arg(javaPathSetting), MessageLevel::Warning); } emitFailed(QString("Java path is not valid.")); return; } else { emit logLine("Java path is:\n " + m_javaPath, MessageLevel::Launcher); } if (JavaUtils::getJavaCheckPath().isEmpty()) { const char* reason = QT_TR_NOOP("Java checker library could not be found. Please check your installation."); emit logLine(tr(reason), MessageLevel::Fatal); emitFailed(tr(reason)); return; } QFileInfo javaInfo(realJavaPath); qint64 javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); auto storedSignature = settings->get("JavaSignature").toString(); auto storedArchitecture = settings->get("JavaArchitecture").toString(); auto storedRealArchitecture = settings->get("JavaRealArchitecture").toString(); auto storedVersion = settings->get("JavaVersion").toString(); auto storedVendor = settings->get("JavaVendor").toString(); QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(QByteArray::number(javaUnixTime)); hash.addData(m_javaPath.toUtf8()); m_javaSignature = hash.result().toHex(); // if timestamps are not the same, or something is missing, check! if (m_javaSignature != storedSignature || storedVersion.size() == 0 || storedArchitecture.size() == 0 || storedRealArchitecture.size() == 0 || storedVendor.size() == 0) { m_JavaChecker.reset(new JavaChecker(realJavaPath, "", 0, 0, 0, 0)); emit logLine(QString("Checking Java version..."), MessageLevel::Launcher); connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished); m_JavaChecker->start(); return; } else { auto verString = instance->settings()->get("JavaVersion").toString(); auto archString = instance->settings()->get("JavaArchitecture").toString(); auto realArchString = settings->get("JavaRealArchitecture").toString(); auto vendorString = instance->settings()->get("JavaVendor").toString(); printJavaInfo(verString, archString, realArchString, vendorString); } m_parent->instance()->updateRuntimeContext(); emitSucceeded(); } void CheckJava::checkJavaFinished(const JavaChecker::Result& result) { switch (result.validity) { case JavaChecker::Result::Validity::Errored: { // Error message displayed if java can't start emit logLine(QString("Could not start java:"), MessageLevel::Error); emit logLines(result.errorLog.split('\n'), MessageLevel::Error); emit logLine(QString("\nCheck your Java settings."), MessageLevel::Launcher); emitFailed(QString("Could not start java!")); return; } case JavaChecker::Result::Validity::ReturnedInvalidData: { emit logLine(QString("Java checker returned some invalid data we don't understand:"), MessageLevel::Error); emit logLines(result.outLog.split('\n'), MessageLevel::Warning); emit logLine("\nMinecraft might not start properly.", MessageLevel::Launcher); m_parent->instance()->updateRuntimeContext(); emitSucceeded(); return; } case JavaChecker::Result::Validity::Valid: { auto instance = m_parent->instance(); printJavaInfo(result.javaVersion.toString(), result.mojangPlatform, result.realPlatform, result.javaVendor); instance->settings()->set("JavaVersion", result.javaVersion.toString()); instance->settings()->set("JavaArchitecture", result.mojangPlatform); instance->settings()->set("JavaRealArchitecture", result.realPlatform); instance->settings()->set("JavaVendor", result.javaVendor); instance->settings()->set("JavaSignature", m_javaSignature); m_parent->instance()->updateRuntimeContext(); emitSucceeded(); return; } } } void CheckJava::printJavaInfo(const QString& version, const QString& architecture, const QString& realArchitecture, const QString& vendor) { emit logLine( QString("Java is version %1, using %2 (%3) architecture, from %4").arg(version, architecture, realArchitecture, vendor), MessageLevel::Launcher); } PrismLauncher-11.0.3/launcher/launch/steps/TextPrint.cpp0000644000175100017510000000104115224505336022635 0ustar runnerrunner#include "TextPrint.h" TextPrint::TextPrint(LaunchTask* parent, const QStringList& lines, MessageLevel level) : LaunchStep(parent) { m_lines = lines; m_level = level; } TextPrint::TextPrint(LaunchTask* parent, const QString& line, MessageLevel level) : LaunchStep(parent) { m_lines.append(line); m_level = level; } void TextPrint::executeTask() { emit logLines(m_lines, m_level); emitSucceeded(); } bool TextPrint::canAbort() const { return true; } bool TextPrint::abort() { emitAborted(); return true; } PrismLauncher-11.0.3/launcher/launch/steps/QuitAfterGameStop.cpp0000644000175100017510000000157715224505336024256 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 dada513 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "QuitAfterGameStop.h" #include #include "Application.h" void QuitAfterGameStop::executeTask() { APPLICATION->quit(); } PrismLauncher-11.0.3/launcher/launch/steps/PrintServers.cpp0000644000175100017510000000366015224505336023353 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2024 Leia uwu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "PrintServers.h" #include "QHostInfo" PrintServers::PrintServers(LaunchTask* parent, const QStringList& servers) : LaunchStep(parent) { m_servers = servers; } void PrintServers::executeTask() { for (QString server : m_servers) { QHostInfo::lookupHost(server, this, &PrintServers::resolveServer); } } void PrintServers::resolveServer(const QHostInfo& host_info) { QString server = host_info.hostName(); QString addresses = server + " resolves to:\n "; if (!host_info.addresses().isEmpty()) { for (QHostAddress address : host_info.addresses()) { addresses += address.toString(); if (!host_info.addresses().endsWith(address)) { addresses += ", "; } } } else { addresses += "N/A"; } addresses += "\n"; m_server_to_address.insert(server, addresses); // print server info in order once all servers are resolved if (m_server_to_address.size() >= m_servers.size()) { for (QString serv : m_servers) { emit logLine(m_server_to_address.value(serv), MessageLevel::Launcher); } emitSucceeded(); } } bool PrintServers::canAbort() const { return true; } PrismLauncher-11.0.3/launcher/launch/LaunchTask.h0000644000175100017510000000770515224505336021255 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Authors: Orochimarufan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include "LaunchStep.h" #include "LogModel.h" #include "MessageLevel.h" #include "logs/LogParser.h" class LaunchTask : public Task { Q_OBJECT protected: explicit LaunchTask(MinecraftInstance* instance); void init(); public: enum State { NotStarted, Running, Waiting, Failed, Aborted, Finished }; public: /* methods */ static std::unique_ptr create(MinecraftInstance* inst); virtual ~LaunchTask() = default; void appendStep(shared_qobject_ptr step); void prependStep(shared_qobject_ptr step); void setCensorFilter(QMap filter); MinecraftInstance* instance() { return m_instance; } void setPid(qint64 pid) { m_pid = pid; } qint64 pid() { return m_pid; } /** * @brief prepare the process for launch (for multi-stage launch) */ virtual void executeTask() override; /** * @brief launch the armed instance */ void proceed(); /** * @brief abort launch */ bool abort() override; bool canAbort() const override; shared_qobject_ptr getLogModel(); public: QString substituteVariables(QString& cmd, bool isLaunch = false) const; QString censorPrivateInfo(QString in); protected: /* methods */ virtual void emitFailed(QString reason) override; virtual void emitSucceeded() override; signals: /** * @brief emitted when the launch preparations are done */ void readyForLaunch(); void requestProgress(Task* task); void requestLogging(); public slots: void onLogLines(const QStringList& lines, MessageLevel defaultLevel = MessageLevel::Launcher); void onLogLine(QString line, MessageLevel defaultLevel = MessageLevel::Launcher); void onReadyForLaunch(); void onStepFinished(); void onProgressReportingRequested(); private: /*methods */ void finalizeSteps(bool successful, const QString& error); protected: bool parseXmlLogs(QString const& line, MessageLevel level); protected: /* data */ MinecraftInstance* m_instance; shared_qobject_ptr m_logModel; QList> m_steps; QMap m_censorFilter; int currentStep = -1; State state = NotStarted; qint64 m_pid = -1; LogParser m_stdoutParser; LogParser m_stderrParser; }; PrismLauncher-11.0.3/launcher/launch/LaunchStep.cpp0000644000175100017510000000220115224505336021603 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "LaunchStep.h" #include "LaunchTask.h" LaunchStep::LaunchStep(LaunchTask* parent) : Task(), m_parent(parent) { connect(this, &LaunchStep::readyForLaunch, parent, &LaunchTask::onReadyForLaunch); connect(this, &LaunchStep::logLine, parent, &LaunchTask::onLogLine); connect(this, &LaunchStep::logLines, parent, &LaunchTask::onLogLines); connect(this, &LaunchStep::finished, parent, &LaunchTask::onStepFinished); connect(this, &LaunchStep::progressReportingRequest, parent, &LaunchTask::onProgressReportingRequested); } PrismLauncher-11.0.3/launcher/launch/TaskStepWrapper.h0000644000175100017510000000225515224505336022312 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include class TaskStepWrapper : public LaunchStep { Q_OBJECT public: explicit TaskStepWrapper(LaunchTask* parent, Task::Ptr task) : LaunchStep(parent), m_task(task) {}; virtual ~TaskStepWrapper() = default; void executeTask() override; bool canAbort() const override; void proceed() override; public slots: bool abort() override; private slots: void updateFinished(); private: Task::Ptr m_task; }; PrismLauncher-11.0.3/launcher/launch/LaunchStep.h0000644000175100017510000000244215224505336021257 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "MessageLevel.h" #include "tasks/Task.h" #include class LaunchTask; class LaunchStep : public Task { Q_OBJECT public: /* methods */ explicit LaunchStep(LaunchTask* parent); virtual ~LaunchStep() = default; signals: void logLines(QStringList lines, MessageLevel level); void logLine(QString line, MessageLevel level); void readyForLaunch(); void progressReportingRequest(); public slots: virtual void proceed() {}; // called in the opposite order than the Task launch(), used to clean up or otherwise undo things after the launch ends virtual void finalize() {}; protected: /* data */ LaunchTask* m_parent; }; PrismLauncher-11.0.3/launcher/launch/LogModel.cpp0000644000175100017510000000775115224505336021256 0ustar runnerrunner#include "LogModel.h" LogModel::LogModel(QObject* parent) : QAbstractListModel(parent) { m_content.resize(m_maxLines); } int LogModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return m_numLines; } QVariant LogModel::data(const QModelIndex& index, int role) const { if (index.row() < 0 || index.row() >= m_numLines) return QVariant(); auto row = index.row(); auto realRow = (row + m_firstLine) % m_maxLines; if (role == Qt::DisplayRole || role == Qt::EditRole) { return m_content[realRow].line; } if (role == LevelRole) { return static_cast(m_content[realRow].level); } return QVariant(); } void LogModel::append(MessageLevel level, QString line) { if (m_suspended) { return; } int lineNum = (m_firstLine + m_numLines) % m_maxLines; // overflow if (m_numLines == m_maxLines) { if (m_stopOnOverflow) { // nothing more to do, the buffer is full return; } beginRemoveRows(QModelIndex(), 0, 0); m_firstLine = (m_firstLine + 1) % m_maxLines; m_numLines--; endRemoveRows(); } else if (m_numLines == m_maxLines - 1 && m_stopOnOverflow) { level = MessageLevel::Fatal; line = m_overflowMessage; } beginInsertRows(QModelIndex(), m_numLines, m_numLines); m_numLines++; m_content[lineNum].level = level; m_content[lineNum].line = line; endInsertRows(); } void LogModel::suspend(bool suspend) { m_suspended = suspend; } bool LogModel::suspended() { return m_suspended; } void LogModel::clear() { beginResetModel(); m_firstLine = 0; m_numLines = 0; endResetModel(); } QString LogModel::toPlainText() { QString out; out.reserve(m_numLines * 80); for (int i = 0; i < m_numLines; i++) { QString& line = m_content[(m_firstLine + i) % m_maxLines].line; out.append(line + '\n'); } out.squeeze(); return out; } void LogModel::setMaxLines(int maxLines) { // no-op if (maxLines == m_maxLines) { return; } // if it all still fits in the buffer, just resize it if (m_firstLine + m_numLines < m_maxLines) { m_maxLines = maxLines; m_content.resize(maxLines); return; } // otherwise, we need to reorganize the data because it crosses the wrap boundary QList newContent; newContent.resize(maxLines); if (m_numLines <= maxLines) { // if it all fits in the new buffer, just copy it over for (int i = 0; i < m_numLines; i++) { newContent[i] = m_content[(m_firstLine + i) % m_maxLines]; } m_content.swap(newContent); } else { // if it doesn't fit, part of the data needs to be thrown away (the oldest log messages) int lead = m_numLines - maxLines; beginRemoveRows(QModelIndex(), 0, lead - 1); for (int i = 0; i < maxLines; i++) { newContent[i] = m_content[(m_firstLine + lead + i) % m_maxLines]; } m_numLines = m_maxLines; m_content.swap(newContent); endRemoveRows(); } m_firstLine = 0; m_maxLines = maxLines; } int LogModel::getMaxLines() { return m_maxLines; } void LogModel::setStopOnOverflow(bool stop) { m_stopOnOverflow = stop; } void LogModel::setOverflowMessage(const QString& overflowMessage) { m_overflowMessage = overflowMessage; } void LogModel::setLineWrap(bool state) { if (m_lineWrap != state) { m_lineWrap = state; } } bool LogModel::wrapLines() const { return m_lineWrap; } void LogModel::setColorLines(bool state) { if (m_colorLines != state) { m_colorLines = state; } } bool LogModel::colorLines() const { return m_colorLines; } bool LogModel::isOverFlow() { return m_numLines >= m_maxLines && m_stopOnOverflow; } MessageLevel LogModel::previousLevel() { if (m_numLines > 0) { return m_content[m_numLines - 1].level; } return MessageLevel::Unknown; } PrismLauncher-11.0.3/launcher/launch/LogModel.h0000644000175100017510000000263015224505336020712 0ustar runnerrunner#pragma once #include #include #include "MessageLevel.h" class LogModel : public QAbstractListModel { Q_OBJECT public: explicit LogModel(QObject* parent = 0); int rowCount(const QModelIndex& parent = QModelIndex()) const; QVariant data(const QModelIndex& index, int role) const; void append(MessageLevel, QString line); void clear(); void suspend(bool suspend); bool suspended(); QString toPlainText(); int getMaxLines(); void setMaxLines(int maxLines); void setStopOnOverflow(bool stop); void setOverflowMessage(const QString& overflowMessage); bool isOverFlow(); void setLineWrap(bool state); bool wrapLines() const; void setColorLines(bool state); bool colorLines() const; MessageLevel previousLevel(); enum Roles { LevelRole = Qt::UserRole }; private /* types */: struct entry { MessageLevel level = MessageLevel::Unknown; QString line; }; private: /* data */ QList m_content; int m_maxLines = 1000; // first line in the circular buffer int m_firstLine = 0; // number of lines occupied in the circular buffer int m_numLines = 0; bool m_stopOnOverflow = false; QString m_overflowMessage = "OVERFLOW"; bool m_suspended = false; bool m_lineWrap = true; bool m_colorLines = true; private: Q_DISABLE_COPY(LogModel) }; PrismLauncher-11.0.3/launcher/launch/TaskStepWrapper.cpp0000644000175100017510000000314415224505336022643 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "TaskStepWrapper.h" #include "tasks/Task.h" void TaskStepWrapper::executeTask() { if (m_state == Task::State::AbortedByUser) { emitFailed(tr("Task aborted.")); return; } connect(m_task.get(), &Task::finished, this, &TaskStepWrapper::updateFinished); propagateFromOther(m_task.get()); emit progressReportingRequest(); } void TaskStepWrapper::proceed() { m_task->start(); } void TaskStepWrapper::updateFinished() { if (m_task->wasSuccessful()) { m_task.reset(); emitSucceeded(); } else { QString reason = tr("Instance update failed because: %1\n\n").arg(m_task->failReason()); m_task.reset(); emit logLine(reason, MessageLevel::Fatal); emitFailed(reason); } } bool TaskStepWrapper::canAbort() const { if (m_task) { return m_task->canAbort(); } return true; } bool TaskStepWrapper::abort() { if (m_task && m_task->canAbort()) { return m_task->abort(); } return Task::abort(); } PrismLauncher-11.0.3/launcher/Filter.h0000644000175100017510000000263615224505336017171 0ustar runnerrunner#pragma once #include #include using Filter = std::function; namespace Filters { inline Filter inverse(Filter filter) { return [filter = std::move(filter)](const QString& src) { return !filter(src); }; } inline Filter any(QList filters) { return [filters = std::move(filters)](const QString& src) { for (auto& filter : filters) if (filter(src)) return true; return false; }; } inline Filter equals(QString pattern) { return [pattern = std::move(pattern)](const QString& src) { return src == pattern; }; } inline Filter equalsAny(QStringList patterns = {}) { return [patterns = std::move(patterns)](const QString& src) { return patterns.isEmpty() || patterns.contains(src); }; } inline Filter equalsOrEmpty(QString pattern) { return [pattern = std::move(pattern)](const QString& src) { return src.isEmpty() || src == pattern; }; } inline Filter contains(QString pattern) { return [pattern = std::move(pattern)](const QString& src) { return src.contains(pattern); }; } inline Filter startsWith(QString pattern) { return [pattern = std::move(pattern)](const QString& src) { return src.startsWith(pattern); }; } inline Filter regexp(QRegularExpression pattern) { return [pattern = std::move(pattern)](const QString& src) { return pattern.match(src).hasMatch(); }; } } // namespace Filters PrismLauncher-11.0.3/launcher/InstanceImportTask.h0000644000175100017510000000514015224505336021517 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include "InstanceTask.h" class InstanceImportTask : public InstanceTask { Q_OBJECT public: explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap&& extra_info = {}); virtual ~InstanceImportTask() = default; bool abort() override; protected: //! Entry point for tasks. virtual void executeTask() override; private: void processMultiMC(); void processTechnic(); void processFlame(); void processModrinth(); private slots: void processZipPack(); void extractFinished(); private: /* data */ QUrl m_sourceUrl; QString m_archivePath; Task::Ptr m_task; enum class ModpackType { Unknown, MultiMC, Technic, Flame, Modrinth, } m_modpackType = ModpackType::Unknown; // Extra info we might need, that's available before, but can't be derived from // the source URL / the resource it points to alone. QMap m_extra_info; // FIXME: nuke QWidget* m_parent; void downloadFromUrl(); }; PrismLauncher-11.0.3/launcher/ProblemProvider.h0000644000175100017510000000171615224505336021055 0ustar runnerrunner#pragma once #include #include enum class ProblemSeverity { None, Warning, Error }; struct PatchProblem { ProblemSeverity m_severity; QString m_description; }; class ProblemProvider { public: virtual ~ProblemProvider() {} virtual const QList getProblems() const = 0; virtual ProblemSeverity getProblemSeverity() const = 0; }; class ProblemContainer : public ProblemProvider { public: const QList getProblems() const override { return m_problems; } ProblemSeverity getProblemSeverity() const override { return m_problemSeverity; } virtual void addProblem(ProblemSeverity severity, const QString& description) { if (severity > m_problemSeverity) { m_problemSeverity = severity; } m_problems.append({ severity, description }); } private: QList m_problems; ProblemSeverity m_problemSeverity = ProblemSeverity::None; }; PrismLauncher-11.0.3/launcher/DesktopServices.cpp0000644000175100017510000000520115224505336021403 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 dada513 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2022 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "DesktopServices.h" #include #include #include #include #include "FileSystem.h" namespace DesktopServices { bool openPath(const QFileInfo& path, bool ensureFolderPathExists) { qDebug() << "Opening path" << path; if (ensureFolderPathExists) { FS::ensureFolderPathExists(path); } return openUrl(QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath())); } bool openPath(const QString& path, bool ensureFolderPathExists) { return openPath(QFileInfo(path), ensureFolderPathExists); } bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid) { qDebug() << "Running" << application << "with args" << args.join(' '); return QProcess::startDetached(application, args, workingDirectory, pid); } bool openUrl(const QUrl& url) { qDebug() << "Opening URL" << url.toString(); return QDesktopServices::openUrl(url); } bool isFlatpak() { #ifdef Q_OS_LINUX return QFile::exists("/.flatpak-info"); #else return false; #endif } bool isSnap() { #ifdef Q_OS_LINUX return getenv("SNAP"); #else return false; #endif } } // namespace DesktopServices PrismLauncher-11.0.3/launcher/InstanceCopyPrefs.h0000644000175100017510000000332215224505336021334 0ustar runnerrunner// // Created by marcelohdez on 10/22/22. // #pragma once #include struct InstanceCopyPrefs { public: bool allTrue() const; QString getSelectedFiltersAsRegex() const; QString getSelectedFiltersAsRegex(const QStringList& additionalFilters) const; // Getters bool isCopySavesEnabled() const; bool isKeepPlaytimeEnabled() const; bool isCopyGameOptionsEnabled() const; bool isCopyResourcePacksEnabled() const; bool isCopyShaderPacksEnabled() const; bool isCopyServersEnabled() const; bool isCopyModsEnabled() const; bool isCopyScreenshotsEnabled() const; bool isUseSymLinksEnabled() const; bool isLinkRecursivelyEnabled() const; bool isUseHardLinksEnabled() const; bool isDontLinkSavesEnabled() const; bool isUseCloneEnabled() const; // Setters void enableCopySaves(bool b); void enableKeepPlaytime(bool b); void enableCopyGameOptions(bool b); void enableCopyResourcePacks(bool b); void enableCopyShaderPacks(bool b); void enableCopyServers(bool b); void enableCopyMods(bool b); void enableCopyScreenshots(bool b); void enableUseSymLinks(bool b); void enableLinkRecursively(bool b); void enableUseHardLinks(bool b); void enableDontLinkSaves(bool b); void enableUseClone(bool b); protected: // data bool copySaves = true; bool keepPlaytime = true; bool copyGameOptions = true; bool copyResourcePacks = true; bool copyShaderPacks = true; bool copyServers = true; bool copyMods = true; bool copyScreenshots = true; bool useSymLinks = false; bool linkRecursively = false; bool useHardLinks = false; bool dontLinkSaves = false; bool useClone = false; }; PrismLauncher-11.0.3/launcher/InstanceTask.h0000644000175100017510000000446115224505336020331 0ustar runnerrunner#pragma once #include "settings/SettingsObject.h" #include "tasks/Task.h" /* Helpers */ enum class InstanceNameChange { ShouldChange, ShouldKeep }; [[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name); enum class ShouldUpdate { Update, SkipUpdating, Cancel }; [[nodiscard]] ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name); enum class ShouldDeleteSaves { NotAsked, Yes, No }; [[nodiscard]] ShouldDeleteSaves askIfShouldDeleteSaves(QWidget* parent); struct InstanceName { public: InstanceName() = default; InstanceName(QString name, QString version) : m_original_name(std::move(name)), m_original_version(std::move(version)) {} QString modifiedName() const; QString originalName() const; QString name() const; QString version() const; void setName(QString name) { m_modified_name = name; } void setName(InstanceName& other); protected: QString m_original_name; QString m_original_version; QString m_modified_name; }; class InstanceTask : public Task, public InstanceName { Q_OBJECT public: InstanceTask(); ~InstanceTask() override = default; void setParentSettings(SettingsObject* settings) { m_globalSettings = settings; } void setStagingPath(const QString& stagingPath) { m_stagingPath = stagingPath; } void setIcon(const QString& icon) { m_instIcon = icon; } void setGroup(const QString& group) { m_instGroup = group; } QString group() const { return m_instGroup; } bool shouldConfirmUpdate() const { return m_confirm_update; } void setConfirmUpdate(bool confirm) { m_confirm_update = confirm; } bool shouldOverride() const { return m_override_existing; } QString originalInstanceID() const { return m_original_instance_id; }; protected: void setOverride(bool override, QString instance_id_to_override = {}) { m_override_existing = override; if (!instance_id_to_override.isEmpty()) m_original_instance_id = instance_id_to_override; } protected: /* data */ SettingsObject* m_globalSettings; QString m_instIcon; QString m_instGroup; QString m_stagingPath; bool m_override_existing = false; bool m_confirm_update = true; QString m_original_instance_id; }; PrismLauncher-11.0.3/launcher/ExponentialSeries.h0000644000175100017510000000142315224505336021376 0ustar runnerrunner #pragma once template inline void clamp(T& current, T min, T max) { if (current < min) { current = min; } else if (current > max) { current = max; } } // List of numbers from min to max. Next is exponent times bigger than previous. class ExponentialSeries { public: ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2) { m_current = m_min = min; m_max = max; m_exponent = exponent; } void reset() { m_current = m_min; } unsigned operator()() { unsigned retval = m_current; m_current *= m_exponent; clamp(m_current, m_min, m_max); return retval; } unsigned m_current; unsigned m_min; unsigned m_max; unsigned m_exponent; }; PrismLauncher-11.0.3/launcher/RWStorage.h0000644000175100017510000000240115224505336017607 0ustar runnerrunner#pragma once #include #include #include #include template class RWStorage { public: void add(K key, V value) { QWriteLocker l(&lock); cache[key] = value; stale_entries.remove(key); } V get(K key) { QReadLocker l(&lock); if (cache.contains(key)) { return cache[key]; } else return V(); } bool get(K key, V& value) { QReadLocker l(&lock); if (cache.contains(key)) { value = cache[key]; return true; } else return false; } bool has(K key) { QReadLocker l(&lock); return cache.contains(key); } bool stale(K key) { QReadLocker l(&lock); if (!cache.contains(key)) return true; return stale_entries.contains(key); } void setStale(K key) { QWriteLocker l(&lock); if (cache.contains(key)) { stale_entries.insert(key); } } void clear() { QWriteLocker l(&lock); cache.clear(); stale_entries.clear(); } private: QReadWriteLock lock; QMap cache; QSet stale_entries; }; PrismLauncher-11.0.3/launcher/qtlogging.ini0000644000175100017510000000111615224505336020257 0ustar runnerrunner[Rules] *.debug=true # prevent log spam and strange bugs # qt.qpa.drawing in particular causes theme artifacts on MacOS qt.*.debug=false # supress image format noise kf.imageformats.plugins.hdr=false kf.imageformats.plugins.xcf=false # don't log credentials by default launcher.auth.credentials.debug=false # remove the debug lines, other log levels still get through launcher.task.net.download.debug=false # enable or disable whole catageries launcher.task.net=true launcher.task=false launcher.task.net.upload=true launcher.task.net.metacache=false launcher.task.net.metacache.http=true PrismLauncher-11.0.3/launcher/FileIgnoreProxy.cpp0000644000175100017510000002363515224505336021366 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FileIgnoreProxy.h" #include #include #include #include #include "FileSystem.h" #include "SeparatorPrefixTree.h" #include "StringUtils.h" FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), m_root(root) {} // NOTE: Sadly, we have to do sorting ourselves. bool FileIgnoreProxy::lessThan(const QModelIndex& left, const QModelIndex& right) const { QFileSystemModel* fsm = qobject_cast(sourceModel()); if (!fsm) { return QSortFilterProxyModel::lessThan(left, right); } bool asc = sortOrder() == Qt::AscendingOrder ? true : false; QFileInfo leftFileInfo = fsm->fileInfo(left); QFileInfo rightFileInfo = fsm->fileInfo(right); if (!leftFileInfo.isDir() && rightFileInfo.isDir()) { return !asc; } if (leftFileInfo.isDir() && !rightFileInfo.isDir()) { return asc; } // sort and proxy model breaks the original model... if (sortColumn() == 0) { return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(), Qt::CaseInsensitive) < 0; } if (sortColumn() == 1) { auto leftSize = leftFileInfo.size(); auto rightSize = rightFileInfo.size(); if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir())) { return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(), Qt::CaseInsensitive) < 0 ? asc : !asc; } return leftSize < rightSize; } return QSortFilterProxyModel::lessThan(left, right); } Qt::ItemFlags FileIgnoreProxy::flags(const QModelIndex& index) const { if (!index.isValid()) return Qt::NoItemFlags; auto sourceIndex = mapToSource(index); Qt::ItemFlags flags = sourceIndex.flags(); if (index.column() == 0) { flags |= Qt::ItemIsUserCheckable; if (sourceIndex.model()->hasChildren(sourceIndex)) { flags |= Qt::ItemIsAutoTristate; } } return flags; } QVariant FileIgnoreProxy::data(const QModelIndex& index, int role) const { QModelIndex sourceIndex = mapToSource(index); if (index.column() == 0 && role == Qt::CheckStateRole) { QFileSystemModel* fsm = qobject_cast(sourceModel()); auto blockedPath = relPath(fsm->filePath(sourceIndex)); auto cover = m_blocked.cover(blockedPath); if (!cover.isNull()) { return QVariant(Qt::Unchecked); } else if (m_blocked.exists(blockedPath)) { return QVariant(Qt::PartiallyChecked); } else { return QVariant(Qt::Checked); } } return sourceIndex.data(role); } bool FileIgnoreProxy::setData(const QModelIndex& index, const QVariant& value, int role) { if (index.column() == 0 && role == Qt::CheckStateRole) { Qt::CheckState state = static_cast(value.toInt()); return setFilterState(index, state); } QModelIndex sourceIndex = mapToSource(index); return QSortFilterProxyModel::sourceModel()->setData(sourceIndex, value, role); } QString FileIgnoreProxy::relPath(const QString& path) const { return QDir(m_root).relativeFilePath(path); } bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state) { QFileSystemModel* fsm = qobject_cast(sourceModel()); if (!fsm) { return false; } QModelIndex sourceIndex = mapToSource(index); auto blockedPath = relPath(fsm->filePath(sourceIndex)); bool changed = false; if (state == Qt::Unchecked) { // blocking a path auto& node = m_blocked.insert(blockedPath); // get rid of all blocked nodes below node.clear(); changed = true; } else if (state == Qt::Checked || state == Qt::PartiallyChecked) { if (!m_blocked.remove(blockedPath)) { auto cover = m_blocked.cover(blockedPath); qDebug() << "Blocked by cover" << cover; // uncover m_blocked.remove(cover); // block all contents, except for any cover QModelIndex rootIndex = fsm->index(FS::PathCombine(m_root, cover)); QModelIndex doing = rootIndex; int row = 0; QStack todo; while (1) { auto node = fsm->index(row, 0, doing); if (!node.isValid()) { if (!todo.size()) { break; } else { doing = todo.pop(); row = 0; continue; } } auto relpath = relPath(fsm->filePath(node)); if (blockedPath.startsWith(relpath)) // cover found? { // continue processing cover later todo.push(node); } else { // or just block this one. m_blocked.insert(relpath); } row++; } } changed = true; } if (changed) { // update the thing emit dataChanged(index, index, { Qt::CheckStateRole }); // update everything above index QModelIndex up = index.parent(); while (1) { if (!up.isValid()) break; emit dataChanged(up, up, { Qt::CheckStateRole }); up = up.parent(); } // and everything below the index QModelIndex doing = index; int row = 0; QStack todo; while (1) { auto node = this->index(row, 0, doing); if (!node.isValid()) { if (!todo.size()) { break; } else { doing = todo.pop(); row = 0; continue; } } emit dataChanged(node, node, { Qt::CheckStateRole }); todo.push(node); row++; } // siblings and unrelated nodes are ignored } return true; } bool FileIgnoreProxy::shouldExpand(QModelIndex index) { QModelIndex sourceIndex = mapToSource(index); QFileSystemModel* fsm = qobject_cast(sourceModel()); if (!fsm) { return false; } auto blockedPath = relPath(fsm->filePath(sourceIndex)); auto found = m_blocked.find(blockedPath); if (found) { return !found->leaf(); } return false; } void FileIgnoreProxy::setBlockedPaths(QStringList paths) { beginResetModel(); m_blocked.clear(); m_blocked.insert(paths); endResetModel(); } bool FileIgnoreProxy::filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const { Q_UNUSED(source_parent) // adjust the columns you want to filter out here // return false for those that will be hidden if (source_column == 2 || source_column == 3) return false; return true; } bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); QFileSystemModel* fsm = qobject_cast(sourceModel()); auto fileInfo = fsm->fileInfo(index); return !ignoreFile(fileInfo); } bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const { if (m_ignoreFiles.contains(fileInfo.fileName())) { return true; } for (const auto& suffix : m_ignoreFilesSuffixes) { if (fileInfo.fileName().endsWith(suffix)) { return true; } } if (m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()))) { return true; } return false; } bool FileIgnoreProxy::filterFile(const QFileInfo& file) const { return m_blocked.covers(relPath(file.absoluteFilePath())) || ignoreFile(file); } void FileIgnoreProxy::loadBlockedPathsFromFile(const QString& fileName) { QFile ignoreFile(fileName); if (!ignoreFile.open(QIODevice::ReadOnly)) { return; } auto ignoreData = ignoreFile.readAll(); auto string = QString::fromUtf8(ignoreData); setBlockedPaths(string.split('\n', Qt::SkipEmptyParts)); } void FileIgnoreProxy::saveBlockedPathsToFile(const QString& fileName) { auto ignoreData = blockedPaths().toStringList().join('\n').toUtf8(); try { FS::write(fileName, ignoreData); } catch (const Exception& e) { qWarning() << e.cause(); } } PrismLauncher-11.0.3/launcher/LibraryUtils.h0000644000175100017510000000162415224505336020365 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * PrismLauncher - Minecraft Launcher * Copyright (C) 2022 Jan Drögehoff * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include namespace LibraryUtils { QString findMangoHud(); QString find(QString libName); } // namespace LibraryUtils PrismLauncher-11.0.3/launcher/MMCTime.cpp0000644000175100017510000000604615224505336017531 0ustar runnerrunner/* * Copyright 2015 Petr Mrazek * Copyright 2021 Jamie Mansfield * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include QString Time::prettifyDuration(int64_t duration, bool noDays) { int seconds = (int)(duration % 60); duration /= 60; int minutes = (int)(duration % 60); duration /= 60; int hours = (int)(noDays ? duration : (duration % 24)); int days = (int)(noDays ? 0 : (duration / 24)); if ((hours == 0) && (days == 0)) { return QObject::tr("%1min %2s").arg(minutes).arg(seconds); } if (days == 0) { return QObject::tr("%1h %2min").arg(hours).arg(minutes); } return QObject::tr("%1d %2h %3min").arg(days).arg(hours).arg(minutes); } QString Time::humanReadableDuration(double duration, int precision) { using days = std::chrono::duration>; QString outStr; QTextStream os(&outStr); bool neg = false; if (duration < 0) { neg = true; // flag duration *= -1; // invert } auto std_duration = std::chrono::duration(duration); auto d = std::chrono::duration_cast(std_duration); std_duration -= d; auto h = std::chrono::duration_cast(std_duration); std_duration -= h; auto m = std::chrono::duration_cast(std_duration); std_duration -= m; auto s = std::chrono::duration_cast(std_duration); std_duration -= s; auto ms = std::chrono::duration_cast(std_duration); auto dc = d.count(); auto hc = h.count(); auto mc = m.count(); auto sc = s.count(); auto msc = ms.count(); if (neg) { os << '-'; } if (dc) { os << dc << QObject::tr("days"); } if (hc) { if (dc) os << " "; os << qSetFieldWidth(2) << hc << QObject::tr("h"); // hours } if (mc) { if (dc || hc) os << " "; os << qSetFieldWidth(2) << mc << QObject::tr("m"); // minutes } if (dc || hc || mc || sc) { if (dc || hc || mc) os << " "; os << qSetFieldWidth(2) << sc << QObject::tr("s"); // seconds } if ((msc && (precision > 0)) || !(dc || hc || mc || sc)) { if (dc || hc || mc || sc) os << " "; os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms"); // miliseconds } os.flush(); return outStr; } PrismLauncher-11.0.3/launcher/ResourceDownloadTask.cpp0000644000175100017510000001376115224505336022402 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022-2023 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ResourceDownloadTask.h" #include #include "Application.h" #include "FileSystem.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "minecraft/mod/ResourceFolderModel.h" #include "minecraft/mod/ShaderPackFolderModel.h" #include "modplatform/ModIndex.h" #include "modplatform/helpers/HashUtils.h" #include "net/ApiDownload.h" #include "net/ChecksumValidator.h" namespace { Net::ModrinthDownloadMeta createModrinthMeta(BaseInstance* instance, QString reason) { auto* mcInstance = dynamic_cast(instance); if (!mcInstance) { return {}; } auto* profile = mcInstance->getPackProfile(); if (!profile) { return {}; } auto loaders = profile->getModLoadersList(); return { .reason = std::move(reason), .gameVersion = profile->getComponentVersion("net.minecraft"), .loader = !loaders.isEmpty() ? ModPlatform::getModLoaderAsString(loaders.first()) : "", }; } } // namespace ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion version, ResourceFolderModel* packs, bool isIndexed, QString downloadReason) : m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs) { if (isIndexed) { m_update_task.reset(new LocalResourceUpdateTask(m_pack_model->indexDir(), *m_pack, m_pack_version)); connect(m_update_task.get(), &LocalResourceUpdateTask::hasOldResource, this, &ResourceDownloadTask::hasOldResource); addTask(m_update_task); } m_filesNetJob.reset(new NetJob(tr("Resource download"), APPLICATION->network())); m_filesNetJob->setStatus(tr("Downloading resource:\n%1").arg(m_pack_version.downloadUrl)); auto action = Net::ApiDownload::makeFile(m_pack_version.downloadUrl, m_pack_model->dir().absoluteFilePath(getFilename()), Net::Download::Option::NoOptions, createModrinthMeta(m_pack_model->instance(), std::move(downloadReason))); if (!m_pack_version.hash_type.isEmpty() && !m_pack_version.hash.isEmpty()) { switch (Hashing::algorithmFromString(m_pack_version.hash_type)) { case Hashing::Algorithm::Md4: action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Md4, m_pack_version.hash)); break; case Hashing::Algorithm::Md5: action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Md5, m_pack_version.hash)); break; case Hashing::Algorithm::Sha1: action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha1, m_pack_version.hash)); break; case Hashing::Algorithm::Sha256: action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha256, m_pack_version.hash)); break; case Hashing::Algorithm::Sha512: action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Sha512, m_pack_version.hash)); break; default: break; } } m_filesNetJob->addNetAction(action); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged); connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress); connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed); addTask(m_filesNetJob); } void ResourceDownloadTask::downloadSucceeded() { m_filesNetJob.reset(); auto oldName = std::get<0>(to_delete); auto oldFilename = std::get<1>(to_delete); if (oldName.isEmpty() || oldFilename == m_pack_version.fileName) { return; } m_pack_model->uninstallResource(oldFilename, true); // also rename the shader config file if (dynamic_cast(m_pack_model) != nullptr) { QFileInfo oldConfig(m_pack_model->dir(), oldFilename + ".txt"); QFileInfo newConfig(m_pack_model->dir(), getFilename() + ".txt"); if (oldConfig.exists() && !newConfig.exists()) { bool success = FS::move(oldConfig.filePath(), newConfig.filePath()); if (!success) { emit logWarning(tr("Failed to rename shader config from '%1' to '%2'").arg(oldConfig.fileName(), newConfig.fileName())); } } } } void ResourceDownloadTask::downloadFailed(QString reason) { m_filesNetJob.reset(); emitFailed(std::move(reason)); } void ResourceDownloadTask::downloadProgressChanged(qint64 current, qint64 total) { emit progress(current, total); } // This indirection is done so that we don't delete a mod before being sure it was // downloaded successfully! void ResourceDownloadTask::hasOldResource(const QString& name, const QString& filename) { to_delete = { name, filename }; } PrismLauncher-11.0.3/launcher/StringUtils.h0000644000175100017510000000571115224505336020230 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include namespace StringUtils { #if defined Q_OS_WIN32 using string = std::wstring; inline string toStdString(QString s) { return s.toStdWString(); } inline QString fromStdString(string s) { return QString::fromStdWString(s); } #else using string = std::string; inline string toStdString(QString s) { return s.toStdString(); } inline QString fromStdString(string s) { return QString::fromStdString(s); } #endif int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs); /** * @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path * @param url Url to truncate * @param max_len max length of url in characters * @param hard_limit if truncating the path can't get the url short enough, truncate it normally. */ QString truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit = false); QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1); QString getRandomAlphaNumeric(); QPair splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs = Qt::CaseSensitive); QPair splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs = Qt::CaseSensitive); QPair splitFirst(const QString& s, const QRegularExpression& re); QString htmlListPatch(QString htmlStr); } // namespace StringUtils PrismLauncher-11.0.3/launcher/FastFileIconProvider.cpp0000644000175100017510000000271015224505336022311 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "FastFileIconProvider.h" #include #include QIcon FastFileIconProvider::icon(const QFileInfo& info) const { #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) bool link = info.isSymbolicLink() || info.isAlias() || info.isShortcut(); #else // in versions prior to 6.4 we don't have access to isAlias bool link = info.isSymLink(); #endif QStyle::StandardPixmap icon; if (info.isDir()) { if (link) icon = QStyle::SP_DirLinkIcon; else icon = QStyle::SP_DirIcon; } else { if (link) icon = QStyle::SP_FileLinkIcon; else icon = QStyle::SP_FileIcon; } return QApplication::style()->standardIcon(icon); } PrismLauncher-11.0.3/launcher/InstanceCreationTask.cpp0000644000175100017510000000736115224505336022353 0ustar runnerrunner#include "InstanceCreationTask.h" #include #include #include "InstanceTask.h" #include "minecraft/MinecraftLoadAndCheck.h" #include "tasks/SequentialTask.h" bool InstanceCreationTask::abort() { if (!canAbort()) { return false; } m_abort = true; if (m_gameFilesTask) { return m_gameFilesTask->abort(); } return InstanceTask::abort(); } void InstanceCreationTask::executeTask() { setAbortable(true); if (updateInstance()) { emitSucceeded(); return; } // When the user aborted in the update stage. if (m_abort) { emitAborted(); return; } m_instance = createInstance(); if (!m_instance) { if (m_abort) return; qWarning() << "Instance creation failed!"; if (!m_error_message.isEmpty()) { qWarning() << "Reason:" << m_error_message; emitFailed(tr("Error while creating new instance:\n%1").arg(m_error_message)); } else { emitFailed(tr("Error while creating new instance.")); } return; } // If this is set, it means we're updating an instance. So, we now need to remove the // files scheduled to, and we'd better not let the user abort in the middle of it, since it'd // put the instance in an invalid state. if (shouldOverride()) { bool deleteFailed = false; setAbortable(false); setStatus(tr("Removing old conflicting files...")); qDebug() << "Removing old files"; for (const QString& path : m_filesToRemove) { if (!QFile::exists(path)) continue; qDebug() << "Removing" << path; if (!QFile::remove(path)) { qCritical() << "Could not remove" << path; deleteFailed = true; } } if (deleteFailed) { emitFailed(tr("Failed to remove old conflicting files.")); return; } } if (!m_abort) { setAbortable(true); setAbortButtonText(tr("Skip")); qDebug() << "Downloading game files"; auto updateTasks = m_instance->createUpdateTask(); if (updateTasks.isEmpty()) { emitSucceeded(); return; } auto task = makeShared(); task->addTask(makeShared(m_instance.get(), Net::Mode::Online)); for (const auto& t : updateTasks) { task->addTask(t); } connect(task.get(), &Task::finished, this, [this, task] { if (task->wasSuccessful() || m_abort) { emitSucceeded(); } else { emitFailed(tr("Could not download game files: %1").arg(task->failReason())); } }); propagateFromOther(task.get()); setDetails(tr("Downloading game files")); m_gameFilesTask = task; m_gameFilesTask->start(); } } void InstanceCreationTask::scheduleToDelete(QWidget* parent, QDir dir, QString path, bool checkDisabled) { if (path.isEmpty()) { return; } if (path.startsWith("saves/")) { if (m_shouldDeleteSaves == ShouldDeleteSaves::NotAsked) { m_shouldDeleteSaves = askIfShouldDeleteSaves(parent); } if (m_shouldDeleteSaves == ShouldDeleteSaves::No) { return; } } qDebug() << "Scheduling" << path << "for removal"; m_filesToRemove.append(dir.absoluteFilePath(path)); if (checkDisabled) { if (path.endsWith(".disabled")) { // remove it if it was enabled/disabled by user m_filesToRemove.append(dir.absoluteFilePath(path.chopped(9))); } else { m_filesToRemove.append(dir.absoluteFilePath(path + ".disabled")); } } } PrismLauncher-11.0.3/launcher/BaseInstance.cpp0000644000175100017510000003537115224505336020640 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "BaseInstance.h" #include #include #include #include #include #include "Application.h" #include "Json.h" #include "launch/LaunchTask.h" #include "settings/INISettingsObject.h" #include "settings/OverrideSetting.h" #include "settings/Setting.h" #include "BuildConfig.h" #include "Commandline.h" #include "FileSystem.h" int getConsoleMaxLines(SettingsObject* settings) { auto lineSetting = settings->getSetting("ConsoleMaxLines"); bool conversionOk = false; int maxLines = lineSetting->get().toInt(&conversionOk); if (!conversionOk) { maxLines = lineSetting->defValue().toInt(); qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines; } return maxLines; } bool shouldStopOnConsoleOverflow(SettingsObject* settings) { return settings->get("ConsoleOverflowStop").toBool(); } BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptr settings, const QString& rootDir) : QObject() { m_settings = std::move(settings); m_global_settings = globalSettings; m_rootDir = rootDir; m_settings->registerSetting("name", "Unnamed Instance"); m_settings->registerSetting("iconKey", "default"); m_settings->registerSetting("notes", ""); m_settings->registerSetting("lastLaunchTime", 0); m_settings->registerSetting("totalTimePlayed", 0); if (m_settings->get("totalTimePlayed").toLongLong() < 0) m_settings->reset("totalTimePlayed"); m_settings->registerSetting("lastTimePlayed", 0); m_settings->registerSetting("linkedInstances", "[]"); m_settings->registerSetting("shortcuts", QString()); // Game time override auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false); m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride); m_settings->registerOverride(globalSettings->getSetting("RecordGameTime"), gameTimeOverride); // NOTE: Sometimees InstanceType is already registered, as it was used to identify the type of // a locally stored instance if (!m_settings->getSetting("InstanceType")) m_settings->registerSetting("InstanceType", ""); // Custom Commands auto commandSetting = m_settings->registerSetting({ "OverrideCommands", "OverrideLaunchCmd" }, false); m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting); m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting); m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting); // Console auto consoleSetting = m_settings->registerSetting("OverrideConsole", false); m_settings->registerOverride(globalSettings->getSetting("ShowConsole"), consoleSetting); m_settings->registerOverride(globalSettings->getSetting("AutoCloseConsole"), consoleSetting); m_settings->registerOverride(globalSettings->getSetting("ShowConsoleOnError"), consoleSetting); m_settings->registerOverride(globalSettings->getSetting("LogPrePostOutput"), consoleSetting); m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr); m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr); // Managed Packs m_settings->registerSetting("ManagedPack", false); m_settings->registerSetting("ManagedPackType", ""); m_settings->registerSetting("ManagedPackID", ""); m_settings->registerSetting("ManagedPackName", ""); m_settings->registerSetting("ManagedPackVersionID", ""); m_settings->registerSetting("ManagedPackVersionName", ""); m_settings->registerSetting("ManagedPackURL", ""); m_settings->registerSetting("Profiler", ""); } BaseInstance::~BaseInstance() {} QString BaseInstance::getPreLaunchCommand() { return settings()->get("PreLaunchCommand").toString(); } QString BaseInstance::getWrapperCommand() { return settings()->get("WrapperCommand").toString(); } QString BaseInstance::getPostExitCommand() { return settings()->get("PostExitCommand").toString(); } bool BaseInstance::isManagedPack() const { return m_settings->get("ManagedPack").toBool(); } QString BaseInstance::getManagedPackType() const { return m_settings->get("ManagedPackType").toString(); } QString BaseInstance::getManagedPackID() const { return m_settings->get("ManagedPackID").toString(); } QString BaseInstance::getManagedPackName() const { return m_settings->get("ManagedPackName").toString(); } QString BaseInstance::getManagedPackVersionID() const { return m_settings->get("ManagedPackVersionID").toString(); } QString BaseInstance::getManagedPackVersionName() const { return m_settings->get("ManagedPackVersionName").toString(); } void BaseInstance::setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version) { m_settings->set("ManagedPack", true); m_settings->set("ManagedPackType", type); m_settings->set("ManagedPackID", id); m_settings->set("ManagedPackName", name); m_settings->set("ManagedPackVersionID", versionId); m_settings->set("ManagedPackVersionName", version); } void BaseInstance::copyManagedPack(BaseInstance& other) { m_settings->set("ManagedPack", other.isManagedPack()); m_settings->set("ManagedPackType", other.getManagedPackType()); m_settings->set("ManagedPackID", other.getManagedPackID()); m_settings->set("ManagedPackName", other.getManagedPackName()); m_settings->set("ManagedPackVersionID", other.getManagedPackVersionID()); m_settings->set("ManagedPackVersionName", other.getManagedPackVersionName()); if (APPLICATION->settings()->get("AutomaticJavaSwitch").toBool() && m_settings->get("AutomaticJava").toBool() && m_settings->get("OverrideJavaLocation").toBool()) { m_settings->set("OverrideJavaLocation", false); m_settings->set("JavaPath", ""); } } QStringList BaseInstance::getLinkedInstances() const { auto setting = m_settings->get("linkedInstances").toString(); return Json::toStringList(setting); } void BaseInstance::setLinkedInstances(const QStringList& list) { m_settings->set("linkedInstances", Json::fromStringList(list)); } void BaseInstance::addLinkedInstanceId(const QString& id) { auto linkedInstances = getLinkedInstances(); linkedInstances.append(id); setLinkedInstances(linkedInstances); } bool BaseInstance::removeLinkedInstanceId(const QString& id) { auto linkedInstances = getLinkedInstances(); int numRemoved = linkedInstances.removeAll(id); setLinkedInstances(linkedInstances); return numRemoved > 0; } bool BaseInstance::isLinkedToInstanceId(const QString& id) const { auto linkedInstances = getLinkedInstances(); return linkedInstances.contains(id); } void BaseInstance::iconUpdated(QString key) { if (iconKey() == key) { emit propertiesChanged(this); } } void BaseInstance::invalidate() { changeStatus(Status::Gone); qDebug() << "Instance" << id() << "has been invalidated."; } void BaseInstance::changeStatus(BaseInstance::Status newStatus) { Status status = currentStatus(); if (status != newStatus) { m_status = newStatus; emit statusChanged(status, newStatus); } } BaseInstance::Status BaseInstance::currentStatus() const { return m_status; } QString BaseInstance::id() const { return QFileInfo(instanceRoot()).fileName(); } bool BaseInstance::isRunning() const { return m_isRunning; } void BaseInstance::setRunning(bool running) { if (running == m_isRunning) return; m_isRunning = running; emit runningStatusChanged(running); } void BaseInstance::setMinecraftRunning(bool running) { if (!settings()->get("RecordGameTime").toBool()) { return; } if (running) { m_timeStarted = QDateTime::currentDateTime(); setLastLaunch(m_timeStarted.toMSecsSinceEpoch()); } else { QDateTime timeEnded = QDateTime::currentDateTime(); qint64 current = settings()->get("totalTimePlayed").toLongLong(); settings()->set("totalTimePlayed", current + m_timeStarted.secsTo(timeEnded)); settings()->set("lastTimePlayed", m_timeStarted.secsTo(timeEnded)); emit propertiesChanged(this); } } int64_t BaseInstance::totalTimePlayed() const { qint64 current = m_settings->get("totalTimePlayed").toLongLong(); if (m_isRunning) { QDateTime timeNow = QDateTime::currentDateTime(); return current + m_timeStarted.secsTo(timeNow); } return current; } int64_t BaseInstance::lastTimePlayed() const { if (m_isRunning) { QDateTime timeNow = QDateTime::currentDateTime(); return m_timeStarted.secsTo(timeNow); } return m_settings->get("lastTimePlayed").toLongLong(); } void BaseInstance::resetTimePlayed() { settings()->reset("totalTimePlayed"); settings()->reset("lastTimePlayed"); } QString BaseInstance::instanceType() const { return m_settings->get("InstanceType").toString(); } QString BaseInstance::instanceRoot() const { return m_rootDir; } SettingsObject* BaseInstance::settings() { loadSpecificSettings(); return m_settings.get(); } bool BaseInstance::canLaunch() const { return (!hasVersionBroken() && !isRunning()); } bool BaseInstance::reloadSettings() { return m_settings->reload(); } qint64 BaseInstance::lastLaunch() const { return m_settings->get("lastLaunchTime").value(); } void BaseInstance::setLastLaunch(qint64 val) { // FIXME: if no change, do not set. setting involves saving a file. m_settings->set("lastLaunchTime", val); emit propertiesChanged(this); } void BaseInstance::setNotes(QString val) { // FIXME: if no change, do not set. setting involves saving a file. m_settings->set("notes", val); } QString BaseInstance::notes() const { return m_settings->get("notes").toString(); } void BaseInstance::setIconKey(QString val) { // FIXME: if no change, do not set. setting involves saving a file. m_settings->set("iconKey", val); emit propertiesChanged(this); } QString BaseInstance::iconKey() const { return m_settings->get("iconKey").toString(); } void BaseInstance::setName(QString val) { // FIXME: if no change, do not set. setting involves saving a file. m_settings->set("name", val); emit propertiesChanged(this); } bool BaseInstance::syncInstanceDirName(const QString& newRoot) const { auto oldRoot = instanceRoot(); return oldRoot == newRoot || QFile::rename(oldRoot, newRoot); } void BaseInstance::registerShortcut(const ShortcutData& data) { auto currentShortcuts = shortcuts(); currentShortcuts.append(data); qDebug() << "Registering shortcut for instance" << id() << "with name" << data.name << "and path" << data.filePath; setShortcuts(currentShortcuts); } void BaseInstance::setShortcuts(const QList& shortcuts) { // FIXME: if no change, do not set. setting involves saving a file. QJsonArray array; for (const auto& elem : shortcuts) { array.append(QJsonObject{ { "name", elem.name }, { "filePath", elem.filePath }, { "target", static_cast(elem.target) } }); } QJsonDocument document; document.setArray(array); m_settings->set("shortcuts", QString::fromUtf8(document.toJson(QJsonDocument::Compact))); } QList BaseInstance::shortcuts() const { auto data = m_settings->get("shortcuts").toString().toUtf8(); QJsonParseError parseError; auto document = QJsonDocument::fromJson(data, &parseError); if (parseError.error != QJsonParseError::NoError || !document.isArray()) return {}; QList results; for (const auto& elem : document.array()) { if (!elem.isObject()) continue; auto dict = elem.toObject(); if (!dict.contains("name") || !dict.contains("filePath") || !dict.contains("target")) continue; int value = dict["target"].toInt(-1); if (!dict["name"].isString() || !dict["filePath"].isString() || value < 0 || value >= 3) continue; QString shortcutName = dict["name"].toString(); QString filePath = dict["filePath"].toString(); if (!QDir(filePath).exists()) { qWarning() << "Shortcut" << shortcutName << "for instance" << name() << "have non-existent path" << filePath; continue; } results.append({ shortcutName, filePath, static_cast(value) }); } return results; } QString BaseInstance::name() const { return m_settings->get("name").toString(); } QString BaseInstance::windowTitle() const { return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name(); } // FIXME: why is this here? move it to MinecraftInstance!!! QStringList BaseInstance::extraArguments() { return Commandline::splitArgs(settings()->get("JvmArgs").toString()); } LaunchTask* BaseInstance::getLaunchTask() { return m_launchProcess.get(); } void BaseInstance::updateRuntimeContext() { // NOOP } bool BaseInstance::isLegacy() { return traits().contains("legacyLaunch") || traits().contains("alphaLaunch"); } PrismLauncher-11.0.3/launcher/WatchLock.h0000644000175100017510000000056315224505336017620 0ustar runnerrunner #pragma once #include #include struct WatchLock { WatchLock(QFileSystemWatcher* watcher, const QString& directory) : m_watcher(watcher), m_directory(directory) { m_watcher->removePath(m_directory); } ~WatchLock() { m_watcher->addPath(m_directory); } QFileSystemWatcher* m_watcher; QString m_directory; }; PrismLauncher-11.0.3/launcher/DataMigrationTask.h0000644000175100017510000000165115224505336021306 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu // // SPDX-License-Identifier: GPL-3.0-only #pragma once #include "FileSystem.h" #include "Filter.h" #include "tasks/Task.h" #include #include /* * Migrate existing data from other MMC-like launchers. */ class DataMigrationTask : public Task { Q_OBJECT public: explicit DataMigrationTask(const QString& sourcePath, const QString& targetPath, Filter pathmatcher); ~DataMigrationTask() override = default; protected: virtual void executeTask() override; protected slots: void dryRunFinished(); void dryRunAborted(); void copyFinished(); void copyAborted(); private: const QString& m_sourcePath; const QString& m_targetPath; const Filter m_pathMatcher; FS::copy m_copy; int m_toCopy = 0; QFuture m_copyFuture; QFutureWatcher m_copyFutureWatcher; }; PrismLauncher-11.0.3/launcher/NullInstance.h0000644000175100017510000000650415224505336020341 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "BaseInstance.h" #include "launch/LaunchTask.h" class NullInstance : public BaseInstance { Q_OBJECT public: NullInstance(SettingsObject* globalSettings, std::unique_ptr settings, const QString& rootDir) : BaseInstance(globalSettings, std::move(settings), rootDir) { setVersionBroken(true); } virtual ~NullInstance() = default; void saveNow() override {} void loadSpecificSettings() override { setSpecificSettingsLoaded(true); } QString getStatusbarDescription() override { return tr("Unknown instance type"); }; QSet traits() const override { return {}; }; QString instanceConfigFolder() const override { return instanceRoot(); }; LaunchTask* createLaunchTask(AuthSessionPtr, MinecraftTarget::Ptr) override { return nullptr; } QList createUpdateTask() override { return {}; } QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); } QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); } QMap getVariables() override { return QMap(); } QStringList getLogFileSearchPaths() override { return {}; } QString typeName() const override { return "Null"; } bool canExport() const override { return false; } bool canEdit() const override { return false; } bool canLaunch() const override { return false; } void populateLaunchMenu(QMenu* menu) override {} QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) override { QStringList out; out << "Null instance - placeholder."; return out; } QString modsRoot() const override { return QString(); } void updateRuntimeContext() override { // NOOP } }; PrismLauncher-11.0.3/launcher/main.cpp0000644000175100017510000000525515224505336017223 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "Application.h" #if defined Q_OS_WIN32 #include "console/WindowsConsole.h" #endif int main(int argc, char* argv[]) { #if defined Q_OS_WIN32 // used on Windows to attach the standard IO streams console::WindowsConsoleGuard _consoleGuard; #endif // initialize Qt Application app(argc, argv); switch (app.status()) { case Application::StartingUp: case Application::Initialized: { Q_INIT_RESOURCE(multimc); Q_INIT_RESOURCE(backgrounds); Q_INIT_RESOURCE(documents); Q_INIT_RESOURCE(prismlauncher); Q_INIT_RESOURCE(pe_dark); Q_INIT_RESOURCE(pe_light); Q_INIT_RESOURCE(pe_blue); Q_INIT_RESOURCE(pe_colored); Q_INIT_RESOURCE(breeze_dark); Q_INIT_RESOURCE(breeze_light); Q_INIT_RESOURCE(OSX); Q_INIT_RESOURCE(iOS); Q_INIT_RESOURCE(flat); Q_INIT_RESOURCE(flat_white); Q_INIT_RESOURCE(shaders); return app.exec(); } case Application::Failed: return 1; case Application::Succeeded: return 0; default: return -1; } } PrismLauncher-11.0.3/launcher/GZip.cpp0000644000175100017510000001466715224505336017157 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "GZip.h" #include #include #include #include bool GZip::unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes) { if (compressedBytes.size() == 0) { uncompressedBytes = compressedBytes; return true; } unsigned uncompLength = compressedBytes.size(); uncompressedBytes.clear(); uncompressedBytes.resize(uncompLength); z_stream strm; memset(&strm, 0, sizeof(strm)); strm.next_in = (Bytef*)compressedBytes.data(); strm.avail_in = compressedBytes.size(); bool done = false; if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) { return false; } int err = Z_OK; while (!done) { // If our output buffer is too small if (strm.total_out >= uncompLength) { uncompressedBytes.resize(uncompLength * 2); uncompLength *= 2; } strm.next_out = reinterpret_cast((uncompressedBytes.data() + strm.total_out)); strm.avail_out = uncompLength - strm.total_out; // Inflate another chunk. err = inflate(&strm, Z_SYNC_FLUSH); if (err == Z_STREAM_END) done = true; else if (err != Z_OK) { break; } } if (inflateEnd(&strm) != Z_OK || !done) { return false; } uncompressedBytes.resize(strm.total_out); return true; } bool GZip::zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes) { if (uncompressedBytes.size() == 0) { compressedBytes = uncompressedBytes; return true; } unsigned compLength = qMin(uncompressedBytes.size(), 16); compressedBytes.clear(); compressedBytes.resize(compLength); z_stream zs; memset(&zs, 0, sizeof(zs)); if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK) { return false; } zs.next_in = (Bytef*)uncompressedBytes.data(); zs.avail_in = uncompressedBytes.size(); int ret; compressedBytes.resize(uncompressedBytes.size()); unsigned offset = 0; unsigned temp = 0; do { auto remaining = compressedBytes.size() - offset; if (remaining < 1) { compressedBytes.resize(compressedBytes.size() * 2); } zs.next_out = reinterpret_cast((compressedBytes.data() + offset)); temp = zs.avail_out = compressedBytes.size() - offset; ret = deflate(&zs, Z_FINISH); offset += temp - zs.avail_out; } while (ret == Z_OK); compressedBytes.resize(offset); if (deflateEnd(&zs) != Z_OK) { return false; } if (ret != Z_STREAM_END) { return false; } return true; } int inf(QFile* source, std::function handleBlock) { constexpr auto CHUNK = 16384; int ret; unsigned have; z_stream strm; memset(&strm, 0, sizeof(strm)); char in[CHUNK]; unsigned char out[CHUNK]; ret = inflateInit2(&strm, (16 + MAX_WBITS)); if (ret != Z_OK) return ret; /* decompress until deflate stream ends or end of file */ do { strm.avail_in = source->read(in, CHUNK); if (source->error()) { (void)inflateEnd(&strm); return Z_ERRNO; } if (strm.avail_in == 0) break; strm.next_in = reinterpret_cast(in); /* run inflate() on input until output buffer not full */ do { strm.avail_out = CHUNK; strm.next_out = out; ret = inflate(&strm, Z_NO_FLUSH); assert(ret != Z_STREAM_ERROR); /* state not clobbered */ switch (ret) { case Z_NEED_DICT: ret = Z_DATA_ERROR; [[fallthrough]]; case Z_DATA_ERROR: case Z_MEM_ERROR: (void)inflateEnd(&strm); return ret; } have = CHUNK - strm.avail_out; if (!handleBlock(QByteArray(reinterpret_cast(out), have))) { (void)inflateEnd(&strm); return Z_OK; } } while (strm.avail_out == 0); /* done when inflate() says it's done */ } while (ret != Z_STREAM_END); /* clean up and return */ (void)inflateEnd(&strm); return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; } QString zerr(int ret) { switch (ret) { case Z_ERRNO: return QObject::tr("error handling file"); case Z_STREAM_ERROR: return QObject::tr("invalid compression level"); case Z_DATA_ERROR: return QObject::tr("invalid or incomplete deflate data"); case Z_MEM_ERROR: return QObject::tr("out of memory"); case Z_VERSION_ERROR: return QObject::tr("zlib version mismatch!"); } return {}; } QString GZip::readGzFileByBlocks(QFile* source, std::function handleBlock) { auto ret = inf(source, handleBlock); return zerr(ret); } PrismLauncher-11.0.3/launcher/CMakeLists.txt0000644000175100017510000014512515224505336020334 0ustar runnerrunnerproject(application) ################################ FILES ################################ ######## Sources and headers ######## set(CORE_SOURCES # LOGIC - Base classes and infrastructure BaseInstaller.h BaseInstaller.cpp BaseVersionList.h BaseVersionList.cpp InstanceList.h InstanceList.cpp InstanceTask.h InstanceTask.cpp LoggedProcess.h LoggedProcess.cpp MessageLevel.cpp MessageLevel.h BaseVersion.h BaseInstance.h BaseInstance.cpp InstanceDirUpdate.h InstanceDirUpdate.cpp NullInstance.h MMCZip.h MMCZip.cpp archive/ArchiveReader.cpp archive/ArchiveReader.h archive/ArchiveWriter.cpp archive/ArchiveWriter.h archive/ExportToZipTask.cpp archive/ExportToZipTask.h archive/ExtractZipTask.cpp archive/ExtractZipTask.h StringUtils.h StringUtils.cpp QVariantUtils.h RuntimeContext.h PSaveFile.h # Basic instance manipulation tasks (derived from InstanceTask) InstanceCreationTask.h InstanceCreationTask.cpp InstanceCopyPrefs.h InstanceCopyPrefs.cpp InstanceCopyTask.h InstanceCopyTask.cpp InstanceImportTask.h InstanceImportTask.cpp # Resource downloading task ResourceDownloadTask.h ResourceDownloadTask.cpp # Use tracking separate from memory management Usable.h # Prefix tree where node names are strings between separators SeparatorPrefixTree.h # String filters Filter.h # JSON parsing helpers Json.h Json.cpp FileSystem.h FileSystem.cpp Exception.h # RW lock protected map RWStorage.h # a smart pointer wrapper intended for safer use with Qt signal/slot mechanisms QObjectPtr.h # Compression support GZip.h GZip.cpp # Command line parameter parsing Commandline.h Commandline.cpp # Version number string support Version.h Version.cpp # A Recursive file system watcher RecursiveFileSystemWatcher.h RecursiveFileSystemWatcher.cpp # Time MMCTime.h MMCTime.cpp MTPixmapCache.h # Assertion helper AssertHelpers.h ) if (UNIX AND NOT CYGWIN AND NOT APPLE) set(CORE_SOURCES ${CORE_SOURCES} # LibraryUtils LibraryUtils.h LibraryUtils.cpp ) endif() set(NET_SOURCES # network stuffs net/ByteArraySink.h net/ChecksumValidator.h net/Download.cpp net/Download.h net/DummySink.h net/FileSink.cpp net/FileSink.h net/HttpMetaCache.cpp net/HttpMetaCache.h net/MetaCacheSink.cpp net/MetaCacheSink.h net/Logging.h net/Logging.cpp net/NetJob.cpp net/NetJob.h net/NetUtils.h net/PasteUpload.cpp net/PasteUpload.h net/Sink.h net/Validator.h net/Upload.cpp net/Upload.h net/HeaderProxy.h net/RawHeaderProxy.h net/ApiHeaderProxy.h net/ApiDownload.h net/ApiDownload.cpp net/ApiUpload.cpp net/ApiUpload.h net/NetRequest.cpp net/NetRequest.h ) # Game launch logic set(LAUNCH_SOURCES launch/steps/CheckJava.cpp launch/steps/CheckJava.h launch/steps/LookupServerAddress.cpp launch/steps/LookupServerAddress.h launch/steps/PostLaunchCommand.cpp launch/steps/PostLaunchCommand.h launch/steps/PreLaunchCommand.cpp launch/steps/PreLaunchCommand.h launch/steps/TextPrint.cpp launch/steps/TextPrint.h launch/steps/QuitAfterGameStop.cpp launch/steps/QuitAfterGameStop.h launch/steps/PrintServers.cpp launch/steps/PrintServers.h launch/LaunchStep.cpp launch/LaunchStep.h launch/LaunchTask.cpp launch/LaunchTask.h launch/LogModel.cpp launch/LogModel.h launch/TaskStepWrapper.cpp launch/TaskStepWrapper.h logs/LogParser.cpp logs/LogParser.h ) # Old update system set(UPDATE_SOURCES updater/ExternalUpdater.h ) set(MAC_UPDATE_SOURCES updater/MacSparkleUpdater.h updater/MacSparkleUpdater.mm ) set(PRISM_UPDATE_SOURCES updater/PrismExternalUpdater.h updater/PrismExternalUpdater.cpp ) # Backend for the news bar... there's usually no news. set(NEWS_SOURCES # News System news/NewsChecker.h news/NewsChecker.cpp news/NewsEntry.h news/NewsEntry.cpp ) # Icon interface set(ICONS_SOURCES # Icons System and related code icons/IconUtils.h icons/IconUtils.cpp ) # Support for Minecraft instances and launch set(MINECRAFT_SOURCES # Logging minecraft/Logging.h minecraft/Logging.cpp # Minecraft support minecraft/auth/AccountData.cpp minecraft/auth/AccountData.h minecraft/auth/AccountList.cpp minecraft/auth/AccountList.h minecraft/auth/AuthSession.cpp minecraft/auth/AuthSession.h minecraft/auth/AuthStep.h minecraft/auth/MinecraftAccount.cpp minecraft/auth/MinecraftAccount.h minecraft/auth/Parsers.cpp minecraft/auth/Parsers.h minecraft/auth/AuthFlow.cpp minecraft/auth/AuthFlow.h minecraft/auth/steps/EntitlementsStep.cpp minecraft/auth/steps/EntitlementsStep.h minecraft/auth/steps/GetSkinStep.cpp minecraft/auth/steps/GetSkinStep.h minecraft/auth/steps/LauncherLoginStep.cpp minecraft/auth/steps/LauncherLoginStep.h minecraft/auth/steps/MinecraftProfileStep.cpp minecraft/auth/steps/MinecraftProfileStep.h minecraft/auth/steps/MSADeviceCodeStep.cpp minecraft/auth/steps/MSADeviceCodeStep.h minecraft/auth/steps/MSAStep.cpp minecraft/auth/steps/MSAStep.h minecraft/auth/steps/XboxAuthorizationStep.cpp minecraft/auth/steps/XboxAuthorizationStep.h minecraft/auth/steps/XboxUserStep.cpp minecraft/auth/steps/XboxUserStep.h minecraft/update/AssetUpdateTask.h minecraft/update/AssetUpdateTask.cpp minecraft/update/LegacyFMLLibrariesTask.cpp minecraft/update/LegacyFMLLibrariesTask.h minecraft/update/FoldersTask.cpp minecraft/update/FoldersTask.h minecraft/update/LibrariesTask.cpp minecraft/update/LibrariesTask.h minecraft/launch/ClaimAccount.cpp minecraft/launch/ClaimAccount.h minecraft/launch/CreateGameFolders.cpp minecraft/launch/CreateGameFolders.h minecraft/launch/EnsureAvailableMemory.cpp minecraft/launch/EnsureAvailableMemory.h minecraft/launch/EnsureOfflineLibraries.cpp minecraft/launch/EnsureOfflineLibraries.h minecraft/launch/ModMinecraftJar.cpp minecraft/launch/ModMinecraftJar.h minecraft/launch/ExtractNatives.cpp minecraft/launch/ExtractNatives.h minecraft/launch/LauncherPartLaunch.cpp minecraft/launch/LauncherPartLaunch.h minecraft/launch/MinecraftTarget.cpp minecraft/launch/MinecraftTarget.h minecraft/launch/PrintInstanceInfo.cpp minecraft/launch/PrintInstanceInfo.h minecraft/launch/ReconstructAssets.cpp minecraft/launch/ReconstructAssets.h minecraft/launch/ScanModFolders.cpp minecraft/launch/ScanModFolders.h minecraft/launch/VerifyJavaInstall.cpp minecraft/launch/VerifyJavaInstall.h minecraft/launch/AutoInstallJava.cpp minecraft/launch/AutoInstallJava.h minecraft/GradleSpecifier.h minecraft/MinecraftInstance.cpp minecraft/MinecraftInstance.h minecraft/LaunchProfile.cpp minecraft/LaunchProfile.h minecraft/Component.cpp minecraft/Component.h minecraft/PackProfile.cpp minecraft/PackProfile.h minecraft/ComponentUpdateTask.cpp minecraft/ComponentUpdateTask.h minecraft/MinecraftLoadAndCheck.h minecraft/MinecraftLoadAndCheck.cpp minecraft/MojangVersionFormat.cpp minecraft/MojangVersionFormat.h minecraft/Rule.cpp minecraft/Rule.h minecraft/OneSixVersionFormat.cpp minecraft/OneSixVersionFormat.h minecraft/ParseUtils.cpp minecraft/ParseUtils.h minecraft/ProfileUtils.cpp minecraft/ProfileUtils.h minecraft/ShortcutUtils.cpp minecraft/ShortcutUtils.h minecraft/Library.cpp minecraft/Library.h minecraft/MojangDownloadInfo.h minecraft/VanillaInstanceCreationTask.cpp minecraft/VanillaInstanceCreationTask.h minecraft/VersionFile.cpp minecraft/VersionFile.h minecraft/VersionFilterData.h minecraft/VersionFilterData.cpp minecraft/World.h minecraft/World.cpp minecraft/WorldList.h minecraft/WorldList.cpp minecraft/mod/MetadataHandler.h minecraft/mod/Mod.h minecraft/mod/Mod.cpp minecraft/mod/ModDetails.h minecraft/mod/ModFolderModel.h minecraft/mod/ModFolderModel.cpp minecraft/mod/Resource.h minecraft/mod/Resource.cpp minecraft/mod/ResourceFolderModel.h minecraft/mod/ResourceFolderModel.cpp minecraft/mod/DataPack.h minecraft/mod/DataPack.cpp minecraft/mod/DataPackFolderModel.h minecraft/mod/DataPackFolderModel.cpp minecraft/mod/ResourcePack.h minecraft/mod/ResourcePack.cpp minecraft/mod/ResourcePackFolderModel.h minecraft/mod/ResourcePackFolderModel.cpp minecraft/mod/TexturePack.h minecraft/mod/TexturePack.cpp minecraft/mod/ShaderPack.h minecraft/mod/ShaderPack.cpp minecraft/mod/WorldSave.h minecraft/mod/WorldSave.cpp minecraft/mod/TexturePackFolderModel.h minecraft/mod/TexturePackFolderModel.cpp minecraft/mod/ShaderPackFolderModel.h minecraft/mod/ShaderPackFolderModel.cpp minecraft/mod/tasks/ResourceFolderLoadTask.h minecraft/mod/tasks/ResourceFolderLoadTask.cpp minecraft/mod/tasks/LocalModParseTask.h minecraft/mod/tasks/LocalModParseTask.cpp minecraft/mod/tasks/LocalResourceUpdateTask.h minecraft/mod/tasks/LocalResourceUpdateTask.cpp minecraft/mod/tasks/LocalDataPackParseTask.h minecraft/mod/tasks/LocalDataPackParseTask.cpp minecraft/mod/tasks/LocalTexturePackParseTask.h minecraft/mod/tasks/LocalTexturePackParseTask.cpp minecraft/mod/tasks/LocalShaderPackParseTask.h minecraft/mod/tasks/LocalShaderPackParseTask.cpp minecraft/mod/tasks/LocalWorldSaveParseTask.h minecraft/mod/tasks/LocalWorldSaveParseTask.cpp minecraft/mod/tasks/LocalResourceParse.h minecraft/mod/tasks/LocalResourceParse.cpp minecraft/mod/tasks/GetModDependenciesTask.h minecraft/mod/tasks/GetModDependenciesTask.cpp # Assets minecraft/AssetsUtils.h minecraft/AssetsUtils.cpp # Minecraft skins minecraft/skins/CapeChange.cpp minecraft/skins/CapeChange.h minecraft/skins/SkinUpload.cpp minecraft/skins/SkinUpload.h minecraft/skins/SkinDelete.cpp minecraft/skins/SkinDelete.h minecraft/skins/SkinModel.cpp minecraft/skins/SkinModel.h minecraft/skins/SkinList.cpp minecraft/skins/SkinList.h minecraft/Agent.h) # the screenshots feature set(SCREENSHOTS_SOURCES screenshots/Screenshot.h screenshots/ImgurUpload.h screenshots/ImgurUpload.cpp screenshots/ImgurAlbumCreation.h screenshots/ImgurAlbumCreation.cpp ) set(TASKS_SOURCES # Tasks tasks/Task.h tasks/Task.cpp tasks/ConcurrentTask.h tasks/ConcurrentTask.cpp tasks/SequentialTask.h tasks/SequentialTask.cpp tasks/MultipleOptionsTask.h tasks/MultipleOptionsTask.cpp ) set(SETTINGS_SOURCES # Settings settings/INIFile.cpp settings/INIFile.h settings/INISettingsObject.cpp settings/INISettingsObject.h settings/OverrideSetting.cpp settings/OverrideSetting.h settings/PassthroughSetting.cpp settings/PassthroughSetting.h settings/Setting.cpp settings/Setting.h settings/SettingsObject.cpp settings/SettingsObject.h ) set(JAVA_SOURCES java/JavaChecker.h java/JavaChecker.cpp java/JavaInstall.h java/JavaInstall.cpp java/JavaInstallList.h java/JavaInstallList.cpp java/JavaUtils.h java/JavaUtils.cpp java/JavaVersion.h java/JavaVersion.cpp java/JavaMetadata.h java/JavaMetadata.cpp java/download/ArchiveDownloadTask.cpp java/download/ArchiveDownloadTask.h java/download/ManifestDownloadTask.cpp java/download/ManifestDownloadTask.h java/download/SymlinkTask.cpp java/download/SymlinkTask.h ui/java/InstallJavaDialog.h ui/java/InstallJavaDialog.cpp ui/java/VersionList.h ui/java/VersionList.cpp ) set(TRANSLATIONS_SOURCES translations/TranslationsModel.h translations/TranslationsModel.cpp translations/POTranslator.h translations/POTranslator.cpp ) set(TOOLS_SOURCES # Tools tools/BaseExternalTool.cpp tools/BaseExternalTool.h tools/BaseProfiler.cpp tools/BaseProfiler.h tools/JProfiler.cpp tools/JProfiler.h tools/JVisualVM.cpp tools/JVisualVM.h tools/MCEditTool.cpp tools/MCEditTool.h tools/GenericProfiler.cpp tools/GenericProfiler.h ) set(META_SOURCES # Metadata sources meta/JsonFormat.cpp meta/JsonFormat.h meta/BaseEntity.cpp meta/BaseEntity.h meta/VersionList.cpp meta/VersionList.h meta/Version.cpp meta/Version.h meta/Index.cpp meta/Index.h ) set(API_SOURCES modplatform/ModIndex.h modplatform/ModIndex.cpp modplatform/ResourceType.h modplatform/ResourceType.cpp modplatform/ResourceAPI.h modplatform/ResourceAPI.cpp modplatform/EnsureMetadataTask.h modplatform/EnsureMetadataTask.cpp modplatform/CheckUpdateTask.h modplatform/flame/FlameAPI.h modplatform/flame/FlameAPI.cpp modplatform/modrinth/ModrinthAPI.h modplatform/modrinth/ModrinthAPI.cpp modplatform/helpers/HashUtils.h modplatform/helpers/HashUtils.cpp modplatform/helpers/OverrideUtils.h modplatform/helpers/OverrideUtils.cpp modplatform/helpers/ExportToModList.h modplatform/helpers/ExportToModList.cpp ) set(FTB_SOURCES modplatform/legacy_ftb/PackFetchTask.h modplatform/legacy_ftb/PackFetchTask.cpp modplatform/legacy_ftb/PackInstallTask.h modplatform/legacy_ftb/PackInstallTask.cpp modplatform/legacy_ftb/PrivatePackManager.h modplatform/legacy_ftb/PrivatePackManager.cpp modplatform/legacy_ftb/PackHelpers.h modplatform/import_ftb/PackInstallTask.h modplatform/import_ftb/PackInstallTask.cpp modplatform/import_ftb/PackHelpers.h modplatform/import_ftb/PackHelpers.cpp modplatform/ftb/FTBPackInstallTask.h modplatform/ftb/FTBPackInstallTask.cpp modplatform/ftb/FTBPackManifest.h modplatform/ftb/FTBPackManifest.cpp ) set(FLAME_SOURCES # Flame modplatform/flame/FlameModIndex.cpp modplatform/flame/FlameModIndex.h modplatform/flame/PackManifest.h modplatform/flame/PackManifest.cpp modplatform/flame/FileResolvingTask.h modplatform/flame/FileResolvingTask.cpp modplatform/flame/FlameCheckUpdate.cpp modplatform/flame/FlameCheckUpdate.h modplatform/flame/FlameInstanceCreationTask.h modplatform/flame/FlameInstanceCreationTask.cpp modplatform/flame/FlamePackExportTask.h modplatform/flame/FlamePackExportTask.cpp ) set(MODRINTH_SOURCES modplatform/modrinth/ModrinthPackIndex.cpp modplatform/modrinth/ModrinthPackIndex.h modplatform/modrinth/ModrinthCheckUpdate.cpp modplatform/modrinth/ModrinthCheckUpdate.h modplatform/modrinth/ModrinthInstanceCreationTask.cpp modplatform/modrinth/ModrinthInstanceCreationTask.h modplatform/modrinth/ModrinthPackExportTask.cpp modplatform/modrinth/ModrinthPackExportTask.h ) set(PACKWIZ_SOURCES modplatform/packwiz/Packwiz.h modplatform/packwiz/Packwiz.cpp ) set(TECHNIC_SOURCES modplatform/technic/SingleZipPackInstallTask.h modplatform/technic/SingleZipPackInstallTask.cpp modplatform/technic/SolderPackInstallTask.h modplatform/technic/SolderPackInstallTask.cpp modplatform/technic/SolderPackManifest.h modplatform/technic/SolderPackManifest.cpp modplatform/technic/TechnicPackProcessor.h modplatform/technic/TechnicPackProcessor.cpp ) set(ATLAUNCHER_SOURCES modplatform/atlauncher/ATLPackIndex.cpp modplatform/atlauncher/ATLPackIndex.h modplatform/atlauncher/ATLPackInstallTask.cpp modplatform/atlauncher/ATLPackInstallTask.h modplatform/atlauncher/ATLPackManifest.cpp modplatform/atlauncher/ATLPackManifest.h modplatform/atlauncher/ATLShareCode.cpp modplatform/atlauncher/ATLShareCode.h ) set(LINKEXE_SOURCES console/WindowsConsole.h console/WindowsConsole.cpp filelink/FileLink.h filelink/FileLink.cpp FileSystem.h FileSystem.cpp Exception.h StringUtils.h StringUtils.cpp DesktopServices.h DesktopServices.cpp ) set(PRISMUPDATER_SOURCES updater/prismupdater/PrismUpdater.h updater/prismupdater/PrismUpdater.cpp updater/prismupdater/UpdaterDialogs.h updater/prismupdater/UpdaterDialogs.cpp updater/prismupdater/GitHubRelease.h updater/prismupdater/GitHubRelease.cpp Json.h Json.cpp FileSystem.h FileSystem.cpp StringUtils.h StringUtils.cpp DesktopServices.h DesktopServices.cpp Version.h Version.cpp Markdown.h Markdown.cpp # Zip MMCZip.h MMCZip.cpp archive/ArchiveReader.cpp archive/ArchiveReader.h archive/ArchiveWriter.cpp archive/ArchiveWriter.h # Time MMCTime.h MMCTime.cpp net/ByteArraySink.h net/ChecksumValidator.h net/Download.cpp net/Download.h net/FileSink.cpp net/FileSink.h net/HttpMetaCache.cpp net/HttpMetaCache.h net/Logging.h net/Logging.cpp net/NetRequest.cpp net/NetRequest.h net/NetJob.cpp net/NetJob.h net/NetUtils.h net/Sink.h net/Validator.h net/HeaderProxy.h net/RawHeaderProxy.h ui/dialogs/ProgressDialog.cpp ui/dialogs/ProgressDialog.h ui/widgets/SubTaskProgressBar.h ui/widgets/SubTaskProgressBar.cpp ) if(WIN32) set(PRISMUPDATER_SOURCES console/WindowsConsole.h console/WindowsConsole.cpp ${PRISMUPDATER_SOURCES} ) endif() ######## Logging categories ######## ecm_qt_declare_logging_category(CORE_SOURCES HEADER Logging.h IDENTIFIER authCredentials CATEGORY_NAME "launcher.auth.credentials" DEFAULT_SEVERITY Warning DESCRIPTION "Secrets and credentials for debugging purposes" EXPORT "${Launcher_Name}" ) ecm_qt_export_logging_category( IDENTIFIER instanceProfileC CATEGORY_NAME "launcher.instance.profile" DEFAULT_SEVERITY Debug DESCRIPTION "Profile actions" EXPORT "${Launcher_Name}" ) ecm_qt_export_logging_category( IDENTIFIER instanceProfileResolveC CATEGORY_NAME "launcher.instance.profile.resolve" DEFAULT_SEVERITY Debug DESCRIPTION "Profile component resolusion actions" EXPORT "${Launcher_Name}" ) ecm_qt_export_logging_category( IDENTIFIER taskLogC CATEGORY_NAME "launcher.task" DEFAULT_SEVERITY Debug DESCRIPTION "Task actions" EXPORT "${Launcher_Name}" ) ecm_qt_export_logging_category( IDENTIFIER taskNetLogC CATEGORY_NAME "launcher.task.net" DEFAULT_SEVERITY Debug DESCRIPTION "Task network action" EXPORT "${Launcher_Name}" ) ecm_qt_export_logging_category( IDENTIFIER taskDownloadLogC CATEGORY_NAME "launcher.task.net.download" DEFAULT_SEVERITY Debug DESCRIPTION "Task network download actions" EXPORT "${Launcher_Name}" ) ecm_qt_export_logging_category( IDENTIFIER taskUploadLogC CATEGORY_NAME "launcher.task.net.upload" DEFAULT_SEVERITY Debug DESCRIPTION "Task network upload actions" EXPORT "${Launcher_Name}" ) ecm_qt_export_logging_category( IDENTIFIER taskMetaCacheLogC CATEGORY_NAME "launcher.task.net.metacache" DEFAULT_SEVERITY Debug DESCRIPTION "task network meta-cache actions" EXPORT "${Launcher_Name}" ) ecm_qt_export_logging_category( IDENTIFIER taskHttpMetaCacheLogC CATEGORY_NAME "launcher.task.net.metacache.http" DEFAULT_SEVERITY Debug DESCRIPTION "task network http meta-cache actions" EXPORT "${Launcher_Name}" ) if(KDE_INSTALL_LOGGINGCATEGORIESDIR) # only install if there is a standard path for this ecm_qt_install_logging_categories( EXPORT "${Launcher_Name}" DESTINATION "${KDE_INSTALL_LOGGINGCATEGORIESDIR}" ) endif() ################################ COMPILE ################################ set(LOGIC_SOURCES ${CORE_SOURCES} ${NET_SOURCES} ${LAUNCH_SOURCES} ${UPDATE_SOURCES} ${NEWS_SOURCES} ${MINECRAFT_SOURCES} ${SCREENSHOTS_SOURCES} ${TASKS_SOURCES} ${SETTINGS_SOURCES} ${JAVA_SOURCES} ${TRANSLATIONS_SOURCES} ${TOOLS_SOURCES} ${META_SOURCES} ${ICONS_SOURCES} ${API_SOURCES} ${FTB_SOURCES} ${FLAME_SOURCES} ${MODRINTH_SOURCES} ${PACKWIZ_SOURCES} ${TECHNIC_SOURCES} ${ATLAUNCHER_SOURCES} ) if(APPLE AND Launcher_ENABLE_UPDATER) set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES}) else() set (LOGIC_SOURCES ${LOGIC_SOURCES} ${PRISM_UPDATE_SOURCES}) endif() SET(LAUNCHER_SOURCES # Application base Application.h Application.cpp DataMigrationTask.h DataMigrationTask.cpp ApplicationMessage.h ApplicationMessage.cpp SysInfo.h SysInfo.cpp HardwareInfo.cpp HardwareInfo.h # console utils console/Console.h # GUI - general utilities DesktopServices.h DesktopServices.cpp VersionProxyModel.h VersionProxyModel.cpp Markdown.h Markdown.cpp # Super secret! KonamiCode.h KonamiCode.cpp # Icons icons/MMCIcon.h icons/MMCIcon.cpp icons/IconList.h icons/IconList.cpp # log utils logs/AnonymizeLog.cpp logs/AnonymizeLog.h # GUI - windows ui/GuiUtil.h ui/GuiUtil.cpp ui/MainWindow.h ui/MainWindow.cpp ui/InstanceWindow.h ui/InstanceWindow.cpp ui/ViewLogWindow.h ui/ViewLogWindow.cpp ui/ToolTipFilter.h ui/ToolTipFilter.cpp # FIXME: maybe find a better home for this. FileIgnoreProxy.cpp FileIgnoreProxy.h FastFileIconProvider.cpp FastFileIconProvider.h # GUI - setup wizard ui/setupwizard/SetupWizard.h ui/setupwizard/SetupWizard.cpp ui/setupwizard/BaseWizardPage.h ui/setupwizard/JavaWizardPage.cpp ui/setupwizard/JavaWizardPage.h ui/setupwizard/LanguageWizardPage.cpp ui/setupwizard/LanguageWizardPage.h ui/setupwizard/PasteWizardPage.cpp ui/setupwizard/PasteWizardPage.h ui/setupwizard/ThemeWizardPage.h ui/setupwizard/AutoJavaWizardPage.cpp ui/setupwizard/AutoJavaWizardPage.h ui/setupwizard/LoginWizardPage.cpp ui/setupwizard/LoginWizardPage.h # GUI - themes ui/themes/FusionTheme.cpp ui/themes/FusionTheme.h ui/themes/BrightTheme.cpp ui/themes/BrightTheme.h ui/themes/CustomTheme.cpp ui/themes/CustomTheme.h ui/themes/DarkTheme.cpp ui/themes/DarkTheme.h ui/themes/ITheme.cpp ui/themes/ITheme.h ui/themes/HintOverrideProxyStyle.cpp ui/themes/HintOverrideProxyStyle.h ui/themes/SystemTheme.cpp ui/themes/SystemTheme.h ui/themes/IconTheme.cpp ui/themes/IconTheme.h ui/themes/ThemeManager.cpp ui/themes/ThemeManager.h ui/themes/CatPack.cpp ui/themes/CatPack.h ui/themes/CatPainter.cpp ui/themes/CatPainter.h # Processes LaunchMode.h LaunchController.h LaunchController.cpp # page provider for instances InstancePageProvider.h # Common java checking UI JavaCommon.h JavaCommon.cpp # GUI - paged dialog base ui/pages/BasePage.h ui/pages/BasePageContainer.h ui/pages/BasePageProvider.h # GUI - instance pages ui/pages/instance/ExternalResourcesPage.cpp ui/pages/instance/ExternalResourcesPage.h ui/pages/instance/VersionPage.cpp ui/pages/instance/VersionPage.h ui/pages/instance/ManagedPackPage.cpp ui/pages/instance/ManagedPackPage.h ui/pages/instance/DataPackPage.h ui/pages/instance/DataPackPage.cpp ui/pages/instance/TexturePackPage.h ui/pages/instance/TexturePackPage.cpp ui/pages/instance/ResourcePackPage.h ui/pages/instance/ResourcePackPage.cpp ui/pages/instance/ShaderPackPage.h ui/pages/instance/ShaderPackPage.cpp ui/pages/instance/ModFolderPage.cpp ui/pages/instance/ModFolderPage.h ui/pages/instance/NotesPage.cpp ui/pages/instance/NotesPage.h ui/pages/instance/LogPage.cpp ui/pages/instance/LogPage.h ui/pages/instance/InstanceSettingsPage.h ui/pages/instance/ScreenshotsPage.cpp ui/pages/instance/ScreenshotsPage.h ui/pages/instance/OtherLogsPage.cpp ui/pages/instance/OtherLogsPage.h ui/pages/instance/ServersPage.cpp ui/pages/instance/ServersPage.h ui/pages/instance/WorldListPage.cpp ui/pages/instance/WorldListPage.h ui/pages/instance/McClient.cpp ui/pages/instance/McClient.h ui/pages/instance/McResolver.cpp ui/pages/instance/McResolver.h ui/pages/instance/ServerPingTask.cpp ui/pages/instance/ServerPingTask.h # GUI - global settings pages ui/pages/global/AccountListPage.cpp ui/pages/global/AccountListPage.h ui/pages/global/ExternalToolsPage.cpp ui/pages/global/ExternalToolsPage.h ui/pages/global/JavaPage.cpp ui/pages/global/JavaPage.h ui/pages/global/LanguagePage.cpp ui/pages/global/LanguagePage.h ui/pages/global/MinecraftPage.h ui/pages/global/LauncherPage.cpp ui/pages/global/LauncherPage.h ui/pages/global/AppearancePage.h ui/pages/global/ProxyPage.cpp ui/pages/global/ProxyPage.h ui/pages/global/APIPage.cpp ui/pages/global/APIPage.h # GUI - platform pages ui/pages/modplatform/CustomPage.cpp ui/pages/modplatform/CustomPage.h ui/pages/modplatform/ResourcePage.cpp ui/pages/modplatform/ResourcePage.h ui/pages/modplatform/ResourceModel.cpp ui/pages/modplatform/ResourceModel.h ui/pages/modplatform/ModPage.cpp ui/pages/modplatform/ModPage.h ui/pages/modplatform/ModModel.cpp ui/pages/modplatform/ModModel.h ui/pages/modplatform/ResourcePackPage.cpp ui/pages/modplatform/ResourcePackModel.cpp # Needed for MOC to find them without a corresponding .cpp ui/pages/modplatform/TexturePackPage.h ui/pages/modplatform/TexturePackModel.cpp ui/pages/modplatform/ShaderPackPage.cpp ui/pages/modplatform/ShaderPackModel.cpp ui/pages/modplatform/DataPackPage.cpp ui/pages/modplatform/DataPackModel.cpp ui/pages/modplatform/ModpackProviderBasePage.h ui/pages/modplatform/atlauncher/AtlFilterModel.cpp ui/pages/modplatform/atlauncher/AtlFilterModel.h ui/pages/modplatform/atlauncher/AtlListModel.cpp ui/pages/modplatform/atlauncher/AtlListModel.h ui/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h ui/pages/modplatform/atlauncher/AtlPage.cpp ui/pages/modplatform/atlauncher/AtlPage.h ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h ui/pages/modplatform/ftb/FtbFilterModel.cpp ui/pages/modplatform/ftb/FtbFilterModel.h ui/pages/modplatform/ftb/FtbListModel.cpp ui/pages/modplatform/ftb/FtbListModel.h ui/pages/modplatform/ftb/FtbPage.cpp ui/pages/modplatform/ftb/FtbPage.h ui/pages/modplatform/legacy_ftb/Page.cpp ui/pages/modplatform/legacy_ftb/Page.h ui/pages/modplatform/legacy_ftb/ListModel.h ui/pages/modplatform/legacy_ftb/ListModel.cpp ui/pages/modplatform/import_ftb/ImportFTBPage.cpp ui/pages/modplatform/import_ftb/ImportFTBPage.h ui/pages/modplatform/import_ftb/ListModel.h ui/pages/modplatform/import_ftb/ListModel.cpp ui/pages/modplatform/flame/FlameModel.cpp ui/pages/modplatform/flame/FlameModel.h ui/pages/modplatform/flame/FlamePage.cpp ui/pages/modplatform/flame/FlamePage.h ui/pages/modplatform/flame/FlameResourceModels.cpp ui/pages/modplatform/flame/FlameResourceModels.h ui/pages/modplatform/flame/FlameResourcePages.cpp ui/pages/modplatform/flame/FlameResourcePages.h ui/pages/modplatform/modrinth/ModrinthPage.cpp ui/pages/modplatform/modrinth/ModrinthPage.h ui/pages/modplatform/modrinth/ModrinthModel.cpp ui/pages/modplatform/modrinth/ModrinthModel.h ui/pages/modplatform/technic/TechnicModel.cpp ui/pages/modplatform/technic/TechnicModel.h ui/pages/modplatform/technic/TechnicPage.cpp ui/pages/modplatform/technic/TechnicPage.h ui/pages/modplatform/ImportPage.cpp ui/pages/modplatform/ImportPage.h ui/pages/modplatform/OptionalModDialog.cpp ui/pages/modplatform/OptionalModDialog.h ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp ui/pages/modplatform/modrinth/ModrinthResourcePages.h # GUI - dialogs ui/dialogs/AboutDialog.cpp ui/dialogs/AboutDialog.h ui/dialogs/ProfileSelectDialog.cpp ui/dialogs/ProfileSelectDialog.h ui/dialogs/ProfileSetupDialog.cpp ui/dialogs/ProfileSetupDialog.h ui/dialogs/CopyInstanceDialog.cpp ui/dialogs/CopyInstanceDialog.h ui/dialogs/CreateShortcutDialog.cpp ui/dialogs/CreateShortcutDialog.h ui/dialogs/CustomMessageBox.cpp ui/dialogs/CustomMessageBox.h ui/dialogs/ExportInstanceDialog.cpp ui/dialogs/ExportInstanceDialog.h ui/dialogs/ExportPackDialog.cpp ui/dialogs/ExportPackDialog.h ui/dialogs/ExportToModListDialog.cpp ui/dialogs/ExportToModListDialog.h ui/dialogs/IconPickerDialog.cpp ui/dialogs/IconPickerDialog.h ui/dialogs/ImportResourceDialog.cpp ui/dialogs/ImportResourceDialog.h ui/dialogs/MSALoginDialog.cpp ui/dialogs/MSALoginDialog.h ui/dialogs/NetworkJobFailedDialog.cpp ui/dialogs/NetworkJobFailedDialog.h ui/dialogs/NewComponentDialog.cpp ui/dialogs/NewComponentDialog.h ui/dialogs/NewInstanceDialog.cpp ui/dialogs/NewInstanceDialog.h ui/dialogs/NewsDialog.cpp ui/dialogs/NewsDialog.h ui/pagedialog/PageDialog.cpp ui/pagedialog/PageDialog.h ui/dialogs/ProgressDialog.cpp ui/dialogs/ProgressDialog.h ui/dialogs/ReviewMessageBox.cpp ui/dialogs/ReviewMessageBox.h ui/dialogs/VersionSelectDialog.cpp ui/dialogs/VersionSelectDialog.h ui/dialogs/ResourceDownloadDialog.cpp ui/dialogs/ResourceDownloadDialog.h ui/dialogs/ScrollMessageBox.cpp ui/dialogs/ScrollMessageBox.h ui/dialogs/BlockedModsDialog.cpp ui/dialogs/BlockedModsDialog.h ui/dialogs/ChooseProviderDialog.h ui/dialogs/ChooseProviderDialog.cpp ui/dialogs/ResourceUpdateDialog.cpp ui/dialogs/ResourceUpdateDialog.h ui/dialogs/InstallLoaderDialog.cpp ui/dialogs/InstallLoaderDialog.h ui/dialogs/ChooseOfflineNameDialog.cpp ui/dialogs/ChooseOfflineNameDialog.h ui/dialogs/skins/SkinManageDialog.cpp ui/dialogs/skins/SkinManageDialog.h ui/dialogs/skins/draw/SkinOpenGLWindow.h ui/dialogs/skins/draw/SkinOpenGLWindow.cpp ui/dialogs/skins/draw/Scene.h ui/dialogs/skins/draw/Scene.cpp ui/dialogs/skins/draw/BoxGeometry.h ui/dialogs/skins/draw/BoxGeometry.cpp # GUI - widgets ui/widgets/CheckComboBox.cpp ui/widgets/CheckComboBox.h ui/widgets/Common.cpp ui/widgets/Common.h ui/widgets/CustomCommands.cpp ui/widgets/CustomCommands.h ui/widgets/EnvironmentVariables.cpp ui/widgets/EnvironmentVariables.h ui/widgets/IconLabel.cpp ui/widgets/IconLabel.h ui/widgets/JavaWizardWidget.cpp ui/widgets/JavaWizardWidget.h ui/widgets/LabeledToolButton.cpp ui/widgets/LabeledToolButton.h ui/widgets/LanguageSelectionWidget.cpp ui/widgets/LanguageSelectionWidget.h ui/widgets/LogView.cpp ui/widgets/LogView.h ui/widgets/InfoFrame.cpp ui/widgets/InfoFrame.h ui/widgets/ModFilterWidget.cpp ui/widgets/ModFilterWidget.h ui/widgets/ModListView.cpp ui/widgets/ModListView.h ui/widgets/PageContainer.cpp ui/widgets/PageContainer.h ui/widgets/PageContainer_p.h ui/widgets/ProjectDescriptionPage.h ui/widgets/ProjectDescriptionPage.cpp ui/widgets/VariableSizedImageObject.h ui/widgets/VariableSizedImageObject.cpp ui/widgets/ProjectItem.h ui/widgets/ProjectItem.cpp ui/widgets/SubTaskProgressBar.h ui/widgets/SubTaskProgressBar.cpp ui/widgets/VersionListView.cpp ui/widgets/VersionListView.h ui/widgets/VersionSelectWidget.cpp ui/widgets/VersionSelectWidget.h ui/widgets/ProgressWidget.h ui/widgets/ProgressWidget.cpp ui/widgets/WideBar.h ui/widgets/WideBar.cpp ui/widgets/AppearanceWidget.h ui/widgets/AppearanceWidget.cpp ui/widgets/MinecraftSettingsWidget.h ui/widgets/MinecraftSettingsWidget.cpp ui/widgets/JavaSettingsWidget.h ui/widgets/JavaSettingsWidget.cpp # GUI - instance group view ui/instanceview/InstanceProxyModel.cpp ui/instanceview/InstanceProxyModel.h ui/instanceview/AccessibleInstanceView.cpp ui/instanceview/AccessibleInstanceView.h ui/instanceview/AccessibleInstanceView_p.h ui/instanceview/InstanceView.cpp ui/instanceview/InstanceView.h ui/instanceview/InstanceDelegate.cpp ui/instanceview/InstanceDelegate.h ui/instanceview/VisualGroup.cpp ui/instanceview/VisualGroup.h ) if (APPLE) set(LAUNCHER_SOURCES ${LAUNCHER_SOURCES} ui/themes/ThemeManager.mm ) endif() if (NOT Apple) set(LAUNCHER_SOURCES ${LAUNCHER_SOURCES} ui/dialogs/UpdateAvailableDialog.h ui/dialogs/UpdateAvailableDialog.cpp ) endif() if (APPLE) set(LAUNCHER_SOURCES ${LAUNCHER_SOURCES} macsandbox/SecurityBookmarkFileAccess.h macsandbox/SecurityBookmarkFileAccess.mm ) endif() if(WIN32) set(LAUNCHER_SOURCES console/WindowsConsole.h console/WindowsConsole.cpp ${LAUNCHER_SOURCES} ) endif() qt_wrap_ui(LAUNCHER_UI ui/MainWindow.ui ui/setupwizard/PasteWizardPage.ui ui/setupwizard/AutoJavaWizardPage.ui ui/setupwizard/LoginWizardPage.ui ui/pages/global/AccountListPage.ui ui/pages/global/JavaPage.ui ui/pages/global/LauncherPage.ui ui/pages/global/APIPage.ui ui/pages/global/ProxyPage.ui ui/pages/global/ExternalToolsPage.ui ui/pages/instance/ExternalResourcesPage.ui ui/pages/instance/NotesPage.ui ui/pages/instance/LogPage.ui ui/pages/instance/ServersPage.ui ui/pages/instance/OtherLogsPage.ui ui/pages/instance/VersionPage.ui ui/pages/instance/ManagedPackPage.ui ui/pages/instance/WorldListPage.ui ui/pages/instance/ScreenshotsPage.ui ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui ui/pages/modplatform/atlauncher/AtlPage.ui ui/pages/modplatform/CustomPage.ui ui/pages/modplatform/ResourcePage.ui ui/pages/modplatform/flame/FlamePage.ui ui/pages/modplatform/legacy_ftb/Page.ui ui/pages/modplatform/import_ftb/ImportFTBPage.ui ui/pages/modplatform/ImportPage.ui ui/pages/modplatform/OptionalModDialog.ui ui/pages/modplatform/ftb/FtbPage.ui ui/pages/modplatform/modrinth/ModrinthPage.ui ui/pages/modplatform/technic/TechnicPage.ui ui/widgets/CustomCommands.ui ui/widgets/EnvironmentVariables.ui ui/widgets/InfoFrame.ui ui/widgets/ModFilterWidget.ui ui/widgets/SubTaskProgressBar.ui ui/widgets/AppearanceWidget.ui ui/widgets/MinecraftSettingsWidget.ui ui/widgets/JavaSettingsWidget.ui ui/dialogs/CopyInstanceDialog.ui ui/dialogs/CreateShortcutDialog.ui ui/dialogs/ProfileSetupDialog.ui ui/dialogs/ProgressDialog.ui ui/dialogs/NewInstanceDialog.ui ui/dialogs/NetworkJobFailedDialog.ui ui/dialogs/NewComponentDialog.ui ui/dialogs/NewsDialog.ui ui/dialogs/ProfileSelectDialog.ui ui/dialogs/ExportInstanceDialog.ui ui/dialogs/ExportPackDialog.ui ui/dialogs/ExportToModListDialog.ui ui/dialogs/IconPickerDialog.ui ui/dialogs/ImportResourceDialog.ui ui/dialogs/MSALoginDialog.ui ui/dialogs/AboutDialog.ui ui/dialogs/ReviewMessageBox.ui ui/dialogs/ScrollMessageBox.ui ui/dialogs/BlockedModsDialog.ui ui/dialogs/ChooseProviderDialog.ui ui/dialogs/skins/SkinManageDialog.ui ui/dialogs/ChooseOfflineNameDialog.ui ) qt_wrap_ui(PRISM_UPDATE_UI ui/dialogs/UpdateAvailableDialog.ui ) if (NOT Apple) set (LAUNCHER_UI ${LAUNCHER_UI} ${PRISM_UPDATE_UI}) endif() qt_add_resources(LAUNCHER_RESOURCES resources/backgrounds/backgrounds.qrc resources/multimc/multimc.qrc resources/pe_dark/pe_dark.qrc resources/pe_light/pe_light.qrc resources/pe_colored/pe_colored.qrc resources/pe_blue/pe_blue.qrc resources/breeze_dark/breeze_dark.qrc resources/breeze_light/breeze_light.qrc resources/OSX/OSX.qrc resources/iOS/iOS.qrc resources/flat/flat.qrc resources/flat_white/flat_white.qrc resources/documents/documents.qrc resources/shaders/shaders.qrc "${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_LogoQRC}" ) qt_wrap_ui(PRISMUPDATER_UI updater/prismupdater/SelectReleaseDialog.ui ui/widgets/SubTaskProgressBar.ui ui/dialogs/ProgressDialog.ui ) ######## Windows resource files ######## if(WIN32) set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC}) endif() ######## Precompiled Headers ########### if(${Launcher_USE_PCH}) message(STATUS "Using precompiled headers for applicable launcher targets") set(PRECOMPILED_HEADERS include/base.pch.hpp include/qtcore.pch.hpp include/qtgui.pch.hpp ) endif() ####### Targets ######## # Add executable add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES}) target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION) if(${Launcher_USE_PCH}) target_precompile_headers(Launcher_logic PRIVATE ${PRECOMPILED_HEADERS}) endif() target_link_libraries(Launcher_logic Launcher_murmur2 nbt++ ${ZLIB_LIBRARIES} qdcss BuildConfig Qt${QT_VERSION_MAJOR}::Widgets ) if(TARGET PkgConfig::libqrencode) target_link_libraries(Launcher_logic PkgConfig::libqrencode) else() target_include_directories(Launcher_logic PRIVATE ${LIBQRENCODE_INCLUDE_DIR}) target_link_libraries(Launcher_logic ${LIBQRENCODE_LIBRARIES}) endif() if(TARGET PkgConfig::tomlplusplus) target_link_libraries(Launcher_logic PkgConfig::tomlplusplus) else() target_link_libraries(Launcher_logic tomlplusplus::tomlplusplus) endif() if(TARGET PkgConfig::libarchive) target_link_libraries(Launcher_logic PkgConfig::libarchive) else() target_link_libraries(Launcher_logic LibArchive::LibArchive) endif() if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(Launcher_logic gamemode ) endif() target_link_libraries(Launcher_logic Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Xml Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Concurrent Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::NetworkAuth Qt${QT_VERSION_MAJOR}::OpenGL ${Launcher_QT_DBUS} ${Launcher_QT_LIBS} ) target_link_libraries(Launcher_logic cmark::cmark LocalPeer Launcher_rainbow ) if (TARGET ${Launcher_QT_DBUS}) add_compile_definitions(WITH_QTDBUS) endif() if(APPLE) set(CMAKE_MACOSX_RPATH 1) set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/") if(Launcher_ENABLE_UPDATER) file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256}) file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle) find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle") add_compile_definitions(SPARKLE_ENABLED) endif() target_link_libraries(Launcher_logic "-framework AppKit" "-framework Carbon" "-framework Foundation" "-framework ApplicationServices" ) if(Launcher_ENABLE_UPDATER) target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK}) endif() endif() add_executable(${Launcher_Name} MACOSX_BUNDLE WIN32 main.cpp ${LAUNCHER_RCS}) if(${Launcher_USE_PCH}) target_precompile_headers(${Launcher_Name} REUSE_FROM Launcher_logic) endif() target_link_libraries(${Launcher_Name} Launcher_logic) if(DEFINED Launcher_APP_BINARY_NAME) set_target_properties(${Launcher_Name} PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}") endif() if(DEFINED Launcher_BINARY_RPATH) SET_TARGET_PROPERTIES(${Launcher_Name} PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}") endif() if(DEFINED Launcher_APP_BINARY_DEFS) target_compile_definitions(${Launcher_Name} PRIVATE ${Launcher_APP_BINARY_DEFS}) target_compile_definitions(Launcher_logic PRIVATE ${Launcher_APP_BINARY_DEFS}) endif() install(TARGETS ${Launcher_Name} RUNTIME_DEPENDENCY_SET LAUNCHER_DEPENDENCY_SET BUNDLE DESTINATION "." COMPONENT Runtime LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime ) # Deploy PDBs if(CMAKE_CXX_LINKER_SUPPORTS_PDB) install(FILES $ DESTINATION ${BINARY_DEST_DIR} OPTIONAL) endif() if(Launcher_BUILD_UPDATER) # Updater add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI}) target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) if(${Launcher_USE_PCH}) target_precompile_headers(prism_updater_logic PRIVATE ${PRECOMPILED_HEADERS}) endif() target_link_libraries(prism_updater_logic ${ZLIB_LIBRARIES} BuildConfig Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network ${Launcher_QT_LIBS} cmark::cmark ) if(TARGET PkgConfig::libarchive) target_link_libraries(prism_updater_logic PkgConfig::libarchive) else() target_link_libraries(prism_updater_logic LibArchive::LibArchive) endif() add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp) target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest) target_link_libraries("${Launcher_Name}_updater" prism_updater_logic) if(${Launcher_USE_PCH}) target_precompile_headers("${Launcher_Name}_updater" REUSE_FROM prism_updater_logic) endif() if(DEFINED Launcher_APP_BINARY_NAME) set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater") endif() if(DEFINED Launcher_BINARY_RPATH) SET_TARGET_PROPERTIES("${Launcher_Name}_updater" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}") endif() install(TARGETS "${Launcher_Name}_updater" BUNDLE DESTINATION "." COMPONENT Runtime LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime ) # Deploy PDBs if(CMAKE_CXX_LINKER_SUPPORTS_PDB) install(FILES $ DESTINATION ${BINARY_DEST_DIR} OPTIONAL) endif() endif() if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER)) # File link add_library(filelink_logic STATIC ${LINKEXE_SOURCES}) target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) if(${Launcher_USE_PCH}) target_precompile_headers(filelink_logic PRIVATE ${PRECOMPILED_HEADERS}) endif() target_link_libraries(filelink_logic BuildConfig Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network # Qt${QT_VERSION_MAJOR}::Concurrent ${Launcher_QT_LIBS} ) add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp) target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest) if(${Launcher_USE_PCH}) target_precompile_headers("${Launcher_Name}_filelink" REUSE_FROM filelink_logic) endif() # HACK: Fix manifest issues with Ninja in release mode (and only release mode) and MSVC # I have no idea why this works or why it's needed. UPDATE THIS IF YOU EDIT THE MANIFEST!!! -@getchoo # Thank you 2018 CMake mailing list thread https://cmake.cmake.narkive.com/LnotZXus/conflicting-msvc-manifests if(MSVC) set_property(TARGET "${Launcher_Name}_filelink" PROPERTY LINK_FLAGS "/MANIFESTUAC:level='requireAdministrator'") endif() target_link_libraries("${Launcher_Name}_filelink" filelink_logic) if(DEFINED Launcher_APP_BINARY_NAME) set_target_properties("${Launcher_Name}_filelink" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_filelink") endif() if(DEFINED Launcher_BINARY_RPATH) SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}") endif() install(TARGETS "${Launcher_Name}_filelink" BUNDLE DESTINATION "." COMPONENT Runtime LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime ) # Deploy PDBs if(CMAKE_CXX_LINKER_SUPPORTS_PDB) install(FILES $ DESTINATION ${BINARY_DEST_DIR} OPTIONAL) endif() endif() if (UNIX AND APPLE AND Launcher_ENABLE_UPDATER) # Add Sparkle updater # It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of # the framework aren't installed install(DIRECTORY ${MACOSX_SPARKLE_DIR}/Sparkle.framework DESTINATION ${FRAMEWORK_DEST_DIR} USE_SOURCE_PERMISSIONS) endif() # Set basic compiler warning/error flags for all targets get_property(Launcher_TARGETS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY BUILDSYSTEM_TARGETS) foreach(target ${Launcher_TARGETS}) message(STATUS "Enabling all warnings as errors for target '${target}'") if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") target_compile_options(${target} PRIVATE /W4 /WX /permissive-) else() target_compile_options(${target} PRIVATE -Wall -Wextra -Wpedantic -Werror) endif() endforeach() # Disable some warnings in main launcher target due to being present in a lot of places. TODO: Fix them. if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") target_compile_options(Launcher_logic PRIVATE /wd4100) # C4100 - unused parameter target_compile_options(${Launcher_Name} PRIVATE /wd4100) # C4100 - unused parameter else() # sfinae-incomplete is a new GCC warning and triggers in Qt headers # no-unknown-warning-option so that compilers that don't have sfinae-incomplete don't error target_compile_options(Launcher_logic PRIVATE -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unknown-warning-option -Wno-sfinae-incomplete) target_compile_options(${Launcher_Name} PRIVATE -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unknown-warning-option -Wno-sfinae-incomplete) endif() #### The bundle mess! #### # Bundle utilities are used to complete packages for different platforms - they add all the libraries that would otherwise be missing on the target system. # NOTE: it seems that this absolutely has to be here, and nowhere else. if(WIN32 OR (UNIX AND APPLE)) if(WIN32) set(QT_DEPLOY_TOOL_OPTIONS "--no-opengl-sw --no-quick-import --no-system-d3d-compiler --no-system-dxc-compiler --skip-plugin-types generic,networkinformation") endif() qt_generate_deploy_script( TARGET ${Launcher_Name} OUTPUT_SCRIPT QT_DEPLOY_SCRIPT CONTENT " qt_deploy_runtime_dependencies( EXECUTABLE ${BINARY_DEST_DIR}/$ BIN_DIR ${BINARY_DEST_DIR} LIBEXEC_DIR ${LIBRARY_DEST_DIR} LIB_DIR ${LIBRARY_DEST_DIR} PLUGINS_DIR ${PLUGIN_DEST_DIR} NO_OVERWRITE NO_TRANSLATIONS NO_COMPILER_RUNTIME DEPLOY_TOOL_OPTIONS ${QT_DEPLOY_TOOL_OPTIONS} )" ) # Bundle our linked dependencies install( RUNTIME_DEPENDENCY_SET LAUNCHER_DEPENDENCY_SET COMPONENT bundle DIRECTORIES ${CMAKE_SYSTEM_LIBRARY_PATH} ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} PRE_EXCLUDE_REGEXES "^(api-ms-win|ext-ms)-.*\\.dll$" # FIXME: Why aren't these caught by the below regex??? "^azure.*\\.dll$" "^vcruntime.*\\.dll$" POST_EXCLUDE_REGEXES "system32" LIBRARY DESTINATION ${LIBRARY_DEST_DIR} RUNTIME DESTINATION ${BINARY_DEST_DIR} FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} ) # Deploy Qt plugins install( SCRIPT ${QT_DEPLOY_SCRIPT} COMPONENT bundle ) # FIXME: remove this crap once we stop using msys2 if(MINGW) # i've not found a solution better than injecting the config vars like this... # with install(CODE" for everything everything just breaks install(CODE " set(QT_PLUGINS_DIR \"${QT_PLUGINS_DIR}\") set(QT_LIBS_DIR \"${QT_LIBS_DIR}\") set(QT_LIBEXECS_DIR \"${QT_LIBEXECS_DIR}\") set(CMAKE_SYSTEM_LIBRARY_PATH \"${CMAKE_SYSTEM_LIBRARY_PATH}\") set(CMAKE_INSTALL_PREFIX \"${CMAKE_INSTALL_PREFIX}\") " COMPONENT bundle) install(CODE [[ file(GLOB QT_IMAGEFORMAT_DLLS "${QT_PLUGINS_DIR}/imageformats/*.dll") set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL objdump) file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR imageformatdeps LIBRARIES ${QT_IMAGEFORMAT_DLLS} DIRECTORIES ${CMAKE_SYSTEM_LIBRARY_PATH} ${QT_PLUGINS_DIR} ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} PRE_EXCLUDE_REGEXES "^(api-ms-win|ext-ms)-.*\\.dll$" # FIXME: Why aren't these caught by the below regex??? "^azure.*\\.dll$" "^vcruntime.*\\.dll$" POST_EXCLUDE_REGEXES "system32" ) foreach(_lib ${imageformatdeps}) file(INSTALL DESTINATION ${CMAKE_INSTALL_PREFIX} TYPE SHARED_LIBRARY FOLLOW_SYMLINK_CHAIN FILES ${_lib} ) endforeach() ]] COMPONENT bundle) endif() # Add qt.conf - this makes Qt stop looking for things outside the bundle install( CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}/qt.conf\" \" \")" COMPONENT bundle ) # Add qtlogging.ini as a config file install( FILES "qtlogging.ini" DESTINATION ${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR} COMPONENT bundle ) endif() find_program(CLANG_FORMAT clang-format OPTIONAL) if(CLANG_FORMAT) message(STATUS "Creating clang-format target") add_custom_target( clang-format COMMAND ${CLANG_FORMAT} -i --style=file:${CMAKE_SOURCE_DIR}/.clang-format ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${PRISMUPDATER_SOURCES} ${LINKEXE_SOURCES} ${PRECOMPILED_HEADERS} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} ) else() message(WARNING "Unable to find `clang-format`. Not creating custom target") endif() PrismLauncher-11.0.3/launcher/RuntimeContext.h0000644000175100017510000000450315224505336020727 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include "SysInfo.h" #include "settings/SettingsObject.h" struct RuntimeContext { QString javaArchitecture; QString javaRealArchitecture; QString system = SysInfo::currentSystem(); QString mappedJavaRealArchitecture() const { if (javaRealArchitecture == "amd64") return "x86_64"; if (javaRealArchitecture == "i386" || javaRealArchitecture == "i686") return "x86"; if (javaRealArchitecture == "aarch64") return "arm64"; if (javaRealArchitecture == "arm" || javaRealArchitecture == "armhf") return "arm32"; return javaRealArchitecture; } void updateFromInstanceSettings(SettingsObject* instanceSettings) { javaArchitecture = instanceSettings->get("JavaArchitecture").toString(); javaRealArchitecture = instanceSettings->get("JavaRealArchitecture").toString(); } QString getClassifier() const { return system + "-" + mappedJavaRealArchitecture(); } // "Legacy" refers to the fact that Mojang assumed that these are the only two architectures bool isLegacyArch() const { const QString mapped = mappedJavaRealArchitecture(); return mapped == "x86_64" || mapped == "x86"; } bool classifierMatches(QString target) const { // try to match precise classifier "[os]-[arch]" bool x = target == getClassifier(); // try to match imprecise classifier on legacy architectures "[os]" if (!x && isLegacyArch()) x = target == system; return x; } }; PrismLauncher-11.0.3/launcher/StringUtils.cpp0000644000175100017510000002106015224505336020556 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "StringUtils.h" #include #include #include /// If you're wondering where these came from exactly, then know you're not the only one =D /// TAKEN FROM Qt, because it doesn't expose it intelligently static inline QChar getNextChar(const QString& s, int location) { return (location < s.length()) ? s.at(location) : QChar(); } /// TAKEN FROM Qt, because it doesn't expose it intelligently int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs) { int l1 = 0, l2 = 0; while (l1 <= s1.size() && l2 <= s2.size()) { // skip spaces, tabs and 0's QChar c1 = getNextChar(s1, l1); while (c1.isSpace()) c1 = getNextChar(s1, ++l1); QChar c2 = getNextChar(s2, l2); while (c2.isSpace()) c2 = getNextChar(s2, ++l2); if (c1.isDigit() && c2.isDigit()) { while (c1.digitValue() == 0) c1 = getNextChar(s1, ++l1); while (c2.digitValue() == 0) c2 = getNextChar(s2, ++l2); int lookAheadLocation1 = l1; int lookAheadLocation2 = l2; int currentReturnValue = 0; // find the last digit, setting currentReturnValue as we go if it isn't equal for (QChar lookAhead1 = c1, lookAhead2 = c2; (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length()); lookAhead1 = getNextChar(s1, ++lookAheadLocation1), lookAhead2 = getNextChar(s2, ++lookAheadLocation2)) { bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit(); bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit(); if (!is1ADigit && !is2ADigit) break; if (!is1ADigit) return -1; if (!is2ADigit) return 1; if (currentReturnValue == 0) { if (lookAhead1 < lookAhead2) { currentReturnValue = -1; } else if (lookAhead1 > lookAhead2) { currentReturnValue = 1; } } } if (currentReturnValue != 0) return currentReturnValue; } if (cs == Qt::CaseInsensitive) { if (!c1.isLower()) c1 = c1.toLower(); if (!c2.isLower()) c2 = c2.toLower(); } int r = QString::localeAwareCompare(c1, c2); if (r < 0) return -1; if (r > 0) return 1; l1 += 1; l2 += 1; } // The two strings are the same (02 == 2) so fall back to the normal sort return QString::compare(s1, s2, cs); } QString StringUtils::truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit) { auto display_options = QUrl::RemoveUserInfo | QUrl::RemoveFragment | QUrl::NormalizePathSegments; auto str_url = url.toDisplayString(display_options); if (str_url.length() <= max_len) return str_url; auto url_path_parts = url.path().split('/'); QString last_path_segment = url_path_parts.takeLast(); if (url_path_parts.size() >= 1 && url_path_parts.first().isEmpty()) url_path_parts.removeFirst(); // drop empty first segment (from leading / ) if (url_path_parts.size() >= 1) url_path_parts.removeLast(); // drop the next to last path segment auto url_template = QStringLiteral("%1://%2/%3%4"); auto url_compact = url_path_parts.isEmpty() ? url_template.arg(url.scheme(), url.host(), QStringList({ "...", last_path_segment }).join('/'), url.query()) : url_template.arg(url.scheme(), url.host(), QStringList({ url_path_parts.join('/'), "...", last_path_segment }).join('/'), url.query()); // remove url parts one by one if it's still too long while (url_compact.length() > max_len && url_path_parts.size() >= 1) { url_path_parts.removeLast(); // drop the next to last path segment url_compact = url_path_parts.isEmpty() ? url_template.arg(url.scheme(), url.host(), QStringList({ "...", last_path_segment }).join('/'), url.query()) : url_template.arg(url.scheme(), url.host(), QStringList({ url_path_parts.join('/'), "...", last_path_segment }).join('/'), url.query()); } if ((url_compact.length() >= max_len) && hard_limit) { // still too long, truncate normally url_compact = QString(str_url); auto to_remove = url_compact.length() - max_len + 3; url_compact.remove(url_compact.length() - to_remove - 1, to_remove); url_compact.append("..."); } return url_compact; } static const QStringList s_units_si{ "KB", "MB", "GB", "TB" }; static const QStringList s_units_kibi{ "KiB", "MiB", "GiB", "TiB" }; QString StringUtils::humanReadableFileSize(double bytes, bool use_si, int decimal_points) { const QStringList units = use_si ? s_units_si : s_units_kibi; const int scale = use_si ? 1000 : 1024; int u = -1; double r = pow(10, decimal_points); do { bytes /= scale; u++; } while (round(abs(bytes) * r) / r >= scale && u < units.length() - 1); return QString::number(bytes, 'f', 2) + " " + units[u]; } QString StringUtils::getRandomAlphaNumeric() { return QUuid::createUuid().toString(QUuid::Id128); } QPair StringUtils::splitFirst(const QString& s, const QString& sep, Qt::CaseSensitivity cs) { QString left, right; auto index = s.indexOf(sep, 0, cs); left = s.mid(0, index); right = s.mid(index + sep.length()); return qMakePair(left, right); } QPair StringUtils::splitFirst(const QString& s, QChar sep, Qt::CaseSensitivity cs) { QString left, right; auto index = s.indexOf(sep, 0, cs); left = s.mid(0, index); right = s.mid(left.length() + 1); return qMakePair(left, right); } QPair StringUtils::splitFirst(const QString& s, const QRegularExpression& re) { QString left, right; QRegularExpressionMatch match; auto index = s.indexOf(re, 0, &match); left = s.mid(0, index); auto end = match.hasMatch() ? left.length() + match.capturedLength() : left.length() + 1; right = s.mid(end); return qMakePair(left, right); } QString StringUtils::htmlListPatch(QString htmlStr) { static const QRegularExpression s_ulMatcher("<\\s*/\\s*ul\\s*>"); int pos = htmlStr.indexOf(s_ulMatcher); int imgPos; while (pos != -1) { pos = htmlStr.indexOf(">", pos) + 1; // Get the size of the tag. Add one for zeroeth index imgPos = htmlStr.indexOf(""); pos = htmlStr.indexOf(s_ulMatcher, pos); } return htmlStr; } PrismLauncher-11.0.3/launcher/BaseInstaller.cpp0000644000175100017510000000271115224505336021021 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "BaseInstaller.h" #include "FileSystem.h" #include "minecraft/MinecraftInstance.h" BaseInstaller::BaseInstaller() {} bool BaseInstaller::isApplied(MinecraftInstance* on) { return QFile::exists(filename(on->instanceRoot())); } bool BaseInstaller::add(MinecraftInstance* to) { if (!patchesDir(to->instanceRoot()).exists()) { QDir(to->instanceRoot()).mkdir("patches"); } if (isApplied(to)) { if (!remove(to)) { return false; } } return true; } bool BaseInstaller::remove(MinecraftInstance* from) { return FS::deletePath(filename(from->instanceRoot())); } QString BaseInstaller::filename(const QString& root) const { return patchesDir(root).absoluteFilePath(id() + ".json"); } QDir BaseInstaller::patchesDir(const QString& root) const { return QDir(root + "/patches/"); } PrismLauncher-11.0.3/launcher/FastFileIconProvider.h0000644000175100017510000000161715224505336021763 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include class FastFileIconProvider : public QFileIconProvider { public: QIcon icon(const QFileInfo& info) const override; }; PrismLauncher-11.0.3/launcher/MMCZip.h0000644000175100017510000000770415224505336017044 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include #include "archive/ArchiveReader.h" #if defined(LAUNCHER_APPLICATION) #include "minecraft/mod/Mod.h" #endif namespace MMCZip { using FilterFileFunction = std::function; #if defined(LAUNCHER_APPLICATION) /** * take a source jar, add mods to it, resulting in target jar */ bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList& mods); #endif /** * Extract a subdirectory from an archive */ std::optional extractSubDir(ArchiveReader* zip, const QString& subdir, const QString& target); /** * Extract a whole archive. * * \param fileCompressed The name of the archive. * \param dir The directory to extract to, the current directory if left empty. * \return The list of the full paths of the files extracted, empty on failure. */ std::optional extractDir(QString fileCompressed, QString dir); /** * Extract a subdirectory from an archive * * \param fileCompressed The name of the archive. * \param subdir The directory within the archive to extract * \param dir The directory to extract to, the current directory if left empty. * \return The list of the full paths of the files extracted, empty on failure. */ std::optional extractDir(QString fileCompressed, QString subdir, QString dir); /** * Extract a single file from an archive into a directory * * \param fileCompressed The name of the archive. * \param file The file within the archive to extract * \param dir The directory to extract to, the current directory if left empty. * \return true for success or false for failure */ bool extractFile(QString fileCompressed, QString file, QString dir); /** * Populate a QFileInfoList with a directory tree recursively, while allowing to excludeFilter what shouldn't be included. * \param rootDir directory to start off * \param subDir subdirectory, should be nullptr for first invocation * \param files resulting list of QFileInfo * \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude) * \return true for success or false for failure */ bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFileFunction excludeFilter); } // namespace MMCZip PrismLauncher-11.0.3/launcher/Markdown.h0000644000175100017510000000151315224505336017517 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Joshua Goins * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include QString markdownToHTML(const QString& markdown); PrismLauncher-11.0.3/launcher/modplatform/0000755000175100017510000000000015224505336020110 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/modplatform/ResourceAPI.h0000644000175100017510000001376315224505336022414 0ustar runnerrunner// SPDX-FileCopyrightText: 2023 flowln // // SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2023-2025 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include "../Version.h" #include "modplatform/ModIndex.h" #include "modplatform/ResourceType.h" #include "tasks/Task.h" /* Simple class with a common interface for interacting with APIs */ class ResourceAPI { public: virtual ~ResourceAPI() = default; struct SortingMethod { // The index of the sorting method. Used to allow for arbitrary ordering in the list of methods. // Used by Flame in the API request. unsigned int index; // The real name of the sorting, as used in the respective API specification. // Used by Modrinth in the API request. QString name; // The human-readable name of the sorting, used for display in the UI. QString readable_name; }; template struct Callback { std::function on_succeed; std::function on_fail; std::function on_abort; }; struct SearchArgs { ModPlatform::ResourceType type{}; int offset = 0; std::optional search; std::optional sorting; std::optional loaders; std::optional> versions; std::optional side; std::optional categoryIds; bool openSource{}; }; struct VersionSearchArgs { ModPlatform::IndexedPack::Ptr pack; std::optional> mcVersions; std::optional loaders; ModPlatform::ResourceType resourceType; bool includeChangelog{}; }; struct ProjectInfoArgs { ModPlatform::IndexedPack::Ptr pack; }; struct DependencySearchArgs { ModPlatform::Dependency dependency; Version mcVersion; ModPlatform::ModLoaderTypes loader; bool includeChangelog{}; }; public: /** Gets a list of available sorting methods for this API. */ virtual auto getSortingMethods() const -> QList = 0; public slots: virtual Task::Ptr searchProjects(SearchArgs&&, Callback>&&) const; virtual std::pair getProject(QString addonId, bool askRetry = true) const; virtual std::pair getProjects(QStringList addonIds) const = 0; virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, Callback&&, bool askRetry = true) const; Task::Ptr getProjectVersions(VersionSearchArgs&& args, Callback>&& callbacks) const; virtual Task::Ptr getDependencyVersion(DependencySearchArgs&&, Callback&&) const; protected: inline QString debugName() const { return "External resource API"; } QString mapMCVersionToModrinth(Version v) const; QString getGameVersionsString(std::vector mcVersions) const; public: virtual auto getSearchURL(const SearchArgs& args) const -> std::optional = 0; virtual auto getInfoURL(const QString& id) const -> std::optional = 0; virtual auto getVersionsURL(const VersionSearchArgs& args) const -> std::optional = 0; virtual auto getDependencyURL(const DependencySearchArgs& args) const -> std::optional = 0; /** Functions to load data into a pack. * * Those are needed for the same reason as documentToArray, and NEED to be re-implemented in the same way. */ virtual void loadIndexedPack(ModPlatform::IndexedPack&, QJsonObject&) const = 0; virtual ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, ModPlatform::ResourceType) const = 0; /** Converts a JSON document to a common array format. * * This is needed so that different providers, with different JSON structures, can be parsed * uniformally. You NEED to re-implement this if you intend on using the default callbacks. */ virtual QJsonArray documentToArray(QJsonDocument& obj) const = 0; /** Functions to load data into a pack. * * Those are needed for the same reason as documentToArray, and NEED to be re-implemented in the same way. */ virtual void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&) const = 0; }; PrismLauncher-11.0.3/launcher/modplatform/EnsureMetadataTask.cpp0000644000175100017510000004172015224505336024345 0ustar runnerrunner#include "EnsureMetadataTask.h" #include #include #include "Application.h" #include "Json.h" #include "QObjectPtr.h" #include "minecraft/mod/Mod.h" #include "minecraft/mod/tasks/LocalResourceUpdateTask.h" #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" #include "modplatform/helpers/HashUtils.h" #include "modplatform/modrinth/ModrinthAPI.h" #include "modplatform/modrinth/ModrinthPackIndex.h" static ModrinthAPI modrinth_api; static FlameAPI flame_api; EnsureMetadataTask::EnsureMetadataTask(Resource* resource, QDir dir, ModPlatform::ResourceProvider prov) : Task(), m_indexDir(dir), m_provider(prov), m_hashingTask(nullptr), m_currentTask(nullptr) { auto hashTask = createNewHash(resource); if (!hashTask) return; connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, resource](QString hash) { m_resources.insert(hash, resource); }); connect(hashTask.get(), &Task::failed, [this, resource] { emitFail(resource, "", RemoveFromList::No); }); m_hashingTask = hashTask; } EnsureMetadataTask::EnsureMetadataTask(QList& resources, QDir dir, ModPlatform::ResourceProvider prov) : Task(), m_indexDir(dir), m_provider(prov), m_currentTask(nullptr) { auto hashTask = makeShared("MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); m_hashingTask = hashTask; for (auto* resource : resources) { auto hash_task = createNewHash(resource); if (!hash_task) continue; connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, resource](QString hash) { m_resources.insert(hash, resource); }); connect(hash_task.get(), &Task::failed, [this, resource] { emitFail(resource, "", RemoveFromList::No); }); hashTask->addTask(hash_task); } } EnsureMetadataTask::EnsureMetadataTask(QHash& resources, QDir dir, ModPlatform::ResourceProvider prov) : Task(), m_resources(resources), m_indexDir(dir), m_provider(prov), m_currentTask(nullptr) {} Hashing::Hasher::Ptr EnsureMetadataTask::createNewHash(Resource* resource) { if (!resource || !resource->valid() || resource->type() == ResourceType::FOLDER) return nullptr; return Hashing::createHasher(resource->fileinfo().absoluteFilePath(), m_provider); } QString EnsureMetadataTask::getExistingHash(Resource* resource) { // Check for already computed hashes // (linear on the number of mods vs. linear on the size of the mod's JAR) auto it = m_resources.keyValueBegin(); while (it != m_resources.keyValueEnd()) { if ((*it).second == resource) break; it++; } // We already have the hash computed if (it != m_resources.keyValueEnd()) { return (*it).first; } // No existing hash return {}; } bool EnsureMetadataTask::abort() { // Prevent sending signals to a dead object disconnect(this, 0, 0, 0); if (m_currentTask) return m_currentTask->abort(); return true; } void EnsureMetadataTask::executeTask() { setStatus(tr("Checking if resources have metadata...")); for (auto* resource : m_resources) { if (!resource->valid()) { qDebug() << "Resource" << resource->name() << "is invalid!"; emitFail(resource); continue; } // They already have the right metadata :o if (resource->status() != ResourceStatus::NO_METADATA && resource->metadata() && resource->metadata()->provider == m_provider) { qDebug() << "Resource" << resource->name() << "already has metadata!"; emitReady(resource); continue; } // Folders don't have metadata if (resource->type() == ResourceType::FOLDER) { emitReady(resource); } } Task::Ptr version_task; switch (m_provider) { case (ModPlatform::ResourceProvider::MODRINTH): version_task = modrinthVersionsTask(); break; case (ModPlatform::ResourceProvider::FLAME): version_task = flameVersionsTask(); break; } auto invalidade_leftover = [this] { for (auto resource = m_resources.constBegin(); resource != m_resources.constEnd(); resource++) emitFail(resource.value(), resource.key(), RemoveFromList::No); m_resources.clear(); emitSucceeded(); }; connect(version_task.get(), &Task::finished, this, [this, invalidade_leftover] { Task::Ptr project_task; switch (m_provider) { case (ModPlatform::ResourceProvider::MODRINTH): project_task = modrinthProjectsTask(); break; case (ModPlatform::ResourceProvider::FLAME): project_task = flameProjectsTask(); break; } if (!project_task) { invalidade_leftover(); return; } connect(project_task.get(), &Task::finished, this, [this, invalidade_leftover, project_task] { invalidade_leftover(); project_task->deleteLater(); if (m_currentTask) m_currentTask.reset(); }); connect(project_task.get(), &Task::failed, this, &EnsureMetadataTask::emitFailed); m_currentTask = project_task; project_task->start(); }); if (m_resources.size() > 1) setStatus(tr("Requesting metadata information from %1...").arg(ModPlatform::ProviderCapabilities::readableName(m_provider))); else if (!m_resources.empty()) setStatus(tr("Requesting metadata information from %1 for '%2'...") .arg(ModPlatform::ProviderCapabilities::readableName(m_provider), m_resources.begin().value()->name())); m_currentTask = version_task; version_task->start(); } void EnsureMetadataTask::emitReady(Resource* resource, QString key, RemoveFromList remove) { if (!resource) { qCritical() << "Tried to mark a null resource as ready."; if (!key.isEmpty()) m_resources.remove(key); return; } qDebug() << QString("Generated metadata for %1").arg(resource->name()); emit metadataReady(resource); if (remove == RemoveFromList::Yes) { if (key.isEmpty()) key = getExistingHash(resource); m_resources.remove(key); } } void EnsureMetadataTask::emitFail(Resource* resource, QString key, RemoveFromList remove) { if (!resource) { qCritical() << "Tried to mark a null resource as failed."; if (!key.isEmpty()) m_resources.remove(key); return; } qDebug() << QString("Failed to generate metadata for %1").arg(resource->name()); emit metadataFailed(resource); if (remove == RemoveFromList::Yes) { if (key.isEmpty()) key = getExistingHash(resource); m_resources.remove(key); } } // Modrinth Task::Ptr EnsureMetadataTask::modrinthVersionsTask() { auto hash_type = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first(); auto [ver_task, response] = modrinth_api.currentVersions(m_resources.keys(), hash_type); // Prevents unfortunate timings when aborting the task if (!ver_task) return Task::Ptr{ nullptr }; connect(ver_task.get(), &Task::succeeded, this, [this, response] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Modrinth::CurrentVersions at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; failed(parse_error.errorString()); return; } try { auto entries = Json::requireObject(doc); for (auto& hash : m_resources.keys()) { auto resource = m_resources.find(hash).value(); try { auto entry = Json::requireObject(entries, hash); setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(resource->name())); qDebug() << "Getting version for" << resource->name() << "from Modrinth"; m_tempVersions.insert(hash, Modrinth::loadIndexedPackVersion(entry)); } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << entries; emitFail(resource); } } } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << doc; } }); return ver_task; } Task::Ptr EnsureMetadataTask::modrinthProjectsTask() { QHash addonIds; for (auto const& data : m_tempVersions) addonIds.insert(data.addonId.toString(), data.hash); Task::Ptr proj_task; QByteArray* response; if (addonIds.isEmpty()) { qWarning() << "No addonId found!"; } else if (addonIds.size() == 1) { std::tie(proj_task, response) = modrinth_api.getProject(*addonIds.keyBegin()); } else { std::tie(proj_task, response) = modrinth_api.getProjects(addonIds.keys()); } // Prevents unfortunate timings when aborting the task if (!proj_task) return Task::Ptr{ nullptr }; connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] { QJsonParseError parse_error{}; auto doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Modrinth projects task at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; return; } QJsonArray entries; try { if (addonIds.size() == 1) entries = { doc.object() }; else entries = Json::requireArray(doc); } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << doc; } for (auto entry : entries) { ModPlatform::IndexedPack pack; try { auto entry_obj = Json::requireObject(entry); Modrinth::loadIndexedPack(pack, entry_obj); } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << doc; // Skip this entry, since it has problems continue; } auto hash = addonIds.find(pack.addonId.toString()).value(); auto resource_iter = m_resources.find(hash); if (resource_iter == m_resources.end()) { qWarning() << "Invalid project id from the API response."; continue; } auto* resource = resource_iter.value(); setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(resource->name())); updateMetadata(pack, m_tempVersions.find(hash).value(), resource); } }); return proj_task; } // Flame Task::Ptr EnsureMetadataTask::flameVersionsTask() { QList fingerprints; for (auto& murmur : m_resources.keys()) { fingerprints.push_back(murmur.toUInt()); } auto [ver_task, response] = flame_api.matchFingerprints(fingerprints); connect(ver_task.get(), &Task::succeeded, this, [this, response] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Flame::CurrentVersions at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; failed(parse_error.errorString()); return; } try { auto doc_obj = Json::requireObject(doc); auto data_obj = Json::requireObject(doc_obj, "data"); auto data_arr = Json::requireArray(data_obj, "exactMatches"); if (data_arr.isEmpty()) { qWarning() << "No matches found for fingerprint search!"; return; } for (auto match : data_arr) { auto match_obj = match.toObject(); auto file_obj = match_obj["file"].toObject(); if (match_obj.isEmpty() || file_obj.isEmpty()) { qWarning() << "Fingerprint match is empty!"; return; } auto fingerprint = QString::number(file_obj["fileFingerprint"].toInteger()); auto resource = m_resources.find(fingerprint); if (resource == m_resources.end()) { qWarning() << "Invalid fingerprint from the API response."; continue; } setStatus(tr("Parsing API response from CurseForge for '%1'...").arg((*resource)->name())); m_tempVersions.insert(fingerprint, FlameMod::loadIndexedPackVersion(file_obj)); } } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << doc; } }); return ver_task; } Task::Ptr EnsureMetadataTask::flameProjectsTask() { QHash addonIds; for (auto const& hash : m_resources.keys()) { if (m_tempVersions.contains(hash)) { auto data = m_tempVersions.find(hash).value(); auto id_str = data.addonId.toString(); if (!id_str.isEmpty()) addonIds.insert(data.addonId.toString(), hash); } } Task::Ptr proj_task; QByteArray* response; if (addonIds.isEmpty()) { qWarning() << "No addonId found!"; } else if (addonIds.size() == 1) { std::tie(proj_task, response) = flame_api.getProject(*addonIds.keyBegin()); } else { std::tie(proj_task, response) = flame_api.getProjects(addonIds.keys()); } // Prevents unfortunate timings when aborting the task if (!proj_task) return Task::Ptr{ nullptr }; connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] { QJsonParseError parse_error{}; auto doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Flame projects task at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; return; } try { QJsonArray entries; if (addonIds.size() == 1) entries = { Json::requireObject(Json::requireObject(doc), "data") }; else entries = Json::requireArray(Json::requireObject(doc), "data"); for (auto entry : entries) { auto entry_obj = Json::requireObject(entry); auto id = QString::number(Json::requireInteger(entry_obj, "id")); auto hash = addonIds.find(id).value(); auto resource = m_resources.find(hash).value(); ModPlatform::IndexedPack pack; try { setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(resource->name())); FlameMod::loadIndexedPack(pack, entry_obj); } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << entries; emitFail(resource); } updateMetadata(pack, m_tempVersions.find(hash).value(), resource); } } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << doc; } }); return proj_task; } void EnsureMetadataTask::updateMetadata(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Resource* resource) { try { // Prevent file name mismatch ver.fileName = resource->fileinfo().fileName(); if (ver.fileName.endsWith(".disabled")) ver.fileName.chop(9); auto task = makeShared(m_indexDir, pack, ver); connect(task.get(), &Task::finished, this, [this, &pack, resource] { updateMetadataCallback(pack, resource); }); m_updateMetadataTasks[ModPlatform::ProviderCapabilities::name(pack.provider) + pack.addonId.toString()] = task; task->start(); } catch (Json::JsonException& e) { qDebug() << e.cause(); emitFail(resource); } } void EnsureMetadataTask::updateMetadataCallback(ModPlatform::IndexedPack& pack, Resource* resource) { QDir tmpIndexDir(m_indexDir); auto metadata = Metadata::get(tmpIndexDir, pack.slug); if (!metadata.isValid()) { qCritical() << "Failed to generate metadata at last step!"; emitFail(resource); return; } resource->setMetadata(metadata); emitReady(resource); } PrismLauncher-11.0.3/launcher/modplatform/ResourceType.h0000644000175100017510000000271015224505336022712 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include namespace ModPlatform { enum class ResourceType { Mod, ResourcePack, ShaderPack, Modpack, DataPack, World, Screenshots, TexturePack, Unknown }; namespace ResourceTypeUtils { static const std::set VALID_RESOURCES = { ResourceType::DataPack, ResourceType::ResourcePack, ResourceType::TexturePack, ResourceType::ShaderPack, ResourceType::World, ResourceType::Mod }; QString getName(ResourceType type); } // namespace ResourceTypeUtils } // namespace ModPlatform PrismLauncher-11.0.3/launcher/modplatform/ResourceType.cpp0000644000175100017510000000356715224505336023260 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ResourceType.h" namespace ModPlatform { static const QMap s_packedTypeNames = { { ResourceType::ResourcePack, QObject::tr("resource pack") }, { ResourceType::TexturePack, QObject::tr("texture pack") }, { ResourceType::DataPack, QObject::tr("data pack") }, { ResourceType::ShaderPack, QObject::tr("shader pack") }, { ResourceType::World, QObject::tr("world save") }, { ResourceType::Mod, QObject::tr("mod") }, { ResourceType::Unknown, QObject::tr("unknown") } }; namespace ResourceTypeUtils { QString getName(ResourceType type) { return s_packedTypeNames.constFind(type).value(); } } // namespace ResourceTypeUtils } // namespace ModPlatform PrismLauncher-11.0.3/launcher/modplatform/technic/0000755000175100017510000000000015224505336021525 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/modplatform/technic/SolderPackManifest.h0000644000175100017510000000233115224505336025413 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include namespace TechnicSolder { struct Pack { QString recommended; QString latest; QList builds; }; void loadPack(Pack& v, QJsonObject& obj); struct PackBuildMod { QString name; QString version; QString md5; QString url; }; struct PackBuild { QString minecraft; QList mods; }; void loadPackBuild(PackBuild& v, QJsonObject& obj); } // namespace TechnicSolder PrismLauncher-11.0.3/launcher/modplatform/technic/SolderPackInstallTask.cpp0000644000175100017510000002007315224505336026434 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2021-2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "SolderPackInstallTask.h" #include #include #include #include #include "SolderPackManifest.h" #include "TechnicPackProcessor.h" #include "net/ApiDownload.h" #include "net/ChecksumValidator.h" Technic::SolderPackInstallTask::SolderPackInstallTask(QNetworkAccessManager* network, const QUrl& solderUrl, const QString& pack, const QString& version, const QString& minecraftVersion) { m_solderUrl = solderUrl; m_pack = pack; m_version = version; m_network = network; m_minecraftVersion = minecraftVersion; } bool Technic::SolderPackInstallTask::abort() { if (m_abortable) { return m_filesNetJob->abort(); } return false; } void Technic::SolderPackInstallTask::executeTask() { setStatus(tr("Resolving modpack files")); m_filesNetJob.reset(new NetJob(tr("Resolving modpack files"), m_network)); auto sourceUrl = QString("%1/modpack/%2/%3").arg(m_solderUrl.toString(), m_pack, m_version); auto [action, response] = Net::ApiDownload::makeByteArray(sourceUrl); m_filesNetJob->addNetAction(action); auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, [this, response] { fileListSucceeded(response); }); connect(job, &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed); connect(job, &NetJob::aborted, this, &Technic::SolderPackInstallTask::downloadAborted); m_filesNetJob->start(); } void Technic::SolderPackInstallTask::fileListSucceeded(QByteArray* response) { setStatus(tr("Downloading modpack")); QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Solder at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; return; } auto obj = doc.object(); TechnicSolder::PackBuild build; try { TechnicSolder::loadPackBuild(build, obj); } catch (const JSONValidationError& e) { m_filesNetJob.reset(); emitFailed(tr("Could not understand pack manifest:\n") + e.cause()); return; } if (!build.minecraft.isEmpty()) m_minecraftVersion = build.minecraft; m_filesNetJob.reset(new NetJob(tr("Downloading modpack"), m_network)); int i = 0; for (const auto& mod : build.mods) { auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); auto dl = Net::ApiDownload::makeFile(mod.url, path); if (!mod.md5.isEmpty()) { dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5)); } m_filesNetJob->addNetAction(dl); i++; } m_modCount = build.mods.size(); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged); connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propagateStepProgress); connect(m_filesNetJob.get(), &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed); connect(m_filesNetJob.get(), &NetJob::aborted, this, &Technic::SolderPackInstallTask::downloadAborted); m_filesNetJob->start(); } void Technic::SolderPackInstallTask::downloadSucceeded() { m_abortable = false; setStatus(tr("Extracting modpack")); m_filesNetJob.reset(); m_extractFuture = QtConcurrent::run([this]() { int i = 0; QString extractDir = FS::PathCombine(m_stagingPath, "minecraft"); FS::ensureFolderPathExists(extractDir); while (m_modCount > i) { auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); if (!MMCZip::extractDir(path, extractDir)) { return false; } i++; } return true; }); connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, &Technic::SolderPackInstallTask::extractFinished); connect(&m_extractFutureWatcher, &QFutureWatcher::canceled, this, &Technic::SolderPackInstallTask::extractAborted); m_extractFutureWatcher.setFuture(m_extractFuture); } void Technic::SolderPackInstallTask::downloadFailed(QString reason) { m_abortable = false; m_filesNetJob.reset(); emitFailed(reason); } void Technic::SolderPackInstallTask::downloadProgressChanged(qint64 current, qint64 total) { m_abortable = true; setProgress(current / 2, total); } void Technic::SolderPackInstallTask::downloadAborted() { m_filesNetJob.reset(); emitAborted(); } void Technic::SolderPackInstallTask::extractFinished() { if (!m_extractFuture.result()) { emitFailed(tr("Failed to extract modpack")); return; } QDir extractDir(m_stagingPath); qDebug() << "Fixing permissions for extracted pack files..."; QDirIterator it(extractDir, QDirIterator::Subdirectories); while (it.hasNext()) { auto filepath = it.next(); QFileInfo file(filepath); auto permissions = QFile::permissions(filepath); auto origPermissions = permissions; if (file.isDir()) { // Folder +rwx for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; } else { // File +rw for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; } if (origPermissions != permissions) { if (!QFile::setPermissions(filepath, permissions)) { logWarning(tr("Could not fix permissions for %1").arg(filepath)); } else { qDebug() << "Fixed" << filepath; } } } auto packProcessor = makeShared(); connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &Technic::SolderPackInstallTask::emitSucceeded); connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &Technic::SolderPackInstallTask::emitFailed); packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath, m_minecraftVersion, true); } void Technic::SolderPackInstallTask::extractAborted() { emitFailed(tr("Instance import has been aborted.")); } PrismLauncher-11.0.3/launcher/modplatform/technic/TechnicPackProcessor.h0000644000175100017510000000231215224505336025750 0ustar runnerrunner/* Copyright 2020-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include "settings/SettingsObject.h" namespace Technic { // not exporting it, only used in SingleZipPackInstallTask, InstanceImportTask and SolderPackInstallTask class TechnicPackProcessor : public QObject { Q_OBJECT signals: void succeeded(); void failed(QString reason); public: void run(SettingsObject* globalSettings, const QString& instName, const QString& instIcon, const QString& stagingPath, const QString& minecraftVersion = QString(), bool isSolder = false); }; } // namespace Technic PrismLauncher-11.0.3/launcher/modplatform/technic/SolderPackInstallTask.h0000644000175100017510000000543215224505336026103 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2021-2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include namespace Technic { class SolderPackInstallTask : public InstanceTask { Q_OBJECT public: explicit SolderPackInstallTask(QNetworkAccessManager* network, const QUrl& solderUrl, const QString& pack, const QString& version, const QString& minecraftVersion); bool canAbort() const override { return true; } bool abort() override; protected: //! Entry point for tasks. virtual void executeTask() override; private slots: void fileListSucceeded(QByteArray* response); void downloadSucceeded(); void downloadFailed(QString reason); void downloadProgressChanged(qint64 current, qint64 total); void downloadAborted(); void extractFinished(); void extractAborted(); private: bool m_abortable = false; QNetworkAccessManager* m_network; NetJob::Ptr m_filesNetJob; QUrl m_solderUrl; QString m_pack; QString m_version; QString m_minecraftVersion; QTemporaryDir m_outputDir; int m_modCount; QFuture m_extractFuture; QFutureWatcher m_extractFutureWatcher; }; } // namespace Technic PrismLauncher-11.0.3/launcher/modplatform/technic/SingleZipPackInstallTask.h0000644000175100017510000000322415224505336026554 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "InstanceTask.h" #include "archive/ArchiveReader.h" #include "net/NetJob.h" #include #include #include #include namespace Technic { class SingleZipPackInstallTask : public InstanceTask { Q_OBJECT public: SingleZipPackInstallTask(const QUrl& sourceUrl, const QString& minecraftVersion); bool canAbort() const override { return true; } bool abort() override; protected: void executeTask() override; private slots: void downloadSucceeded(); void downloadFailed(QString reason); void downloadProgressChanged(qint64 current, qint64 total); void extractFinished(); void extractAborted(); private: bool m_abortable = false; QUrl m_sourceUrl; QString m_minecraftVersion; QString m_archivePath; NetJob::Ptr m_filesNetJob; std::unique_ptr m_packZip; QFuture> m_extractFuture; QFutureWatcher> m_extractFutureWatcher; }; } // namespace Technic PrismLauncher-11.0.3/launcher/modplatform/technic/TechnicPackProcessor.cpp0000644000175100017510000002147415224505336026315 0ustar runnerrunner/* Copyright 2020-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "TechnicPackProcessor.h" #include #include #include #include #include #include #include "archive/ArchiveReader.h" void Technic::TechnicPackProcessor::run(SettingsObject* globalSettings, const QString& instName, const QString& instIcon, const QString& stagingPath, const QString& minecraftVersion, [[maybe_unused]] const bool isSolder) { QString minecraftPath = FS::PathCombine(stagingPath, "minecraft"); QString configPath = FS::PathCombine(stagingPath, "instance.cfg"); auto instanceSettings = std::make_unique(configPath); MinecraftInstance instance(globalSettings, std::move(instanceSettings), stagingPath); instance.setName(instName); if (instIcon != "default") { instance.setIconKey(instIcon); } auto components = instance.getPackProfile(); components->buildingFromScratch(); QByteArray data; QString modpackJar = FS::PathCombine(minecraftPath, "bin", "modpack.jar"); QString versionJson = FS::PathCombine(minecraftPath, "bin", "version.json"); QString fmlMinecraftVersion; if (QFile::exists(modpackJar)) { MMCZip::ArchiveReader zipFile(modpackJar); if (!zipFile.collectFiles()) { emit failed(tr("Unable to open \"bin/modpack.jar\" file!")); return; } if (zipFile.exists("/version.json")) { if (zipFile.exists("/fmlversion.properties")) { auto file = zipFile.goToFile("fmlversion.properties"); if (!file) { emit failed(tr("Unable to open \"fmlversion.properties\"!")); return; } QByteArray fmlVersionData = file->readAll(); INIFile iniFile; iniFile.loadFile(fmlVersionData); // If not present, this evaluates to a null string fmlMinecraftVersion = iniFile["fmlbuild.mcversion"].toString(); } auto file = zipFile.goToFile("version.json"); if (!file) { emit failed(tr("Unable to open \"version.json\"!")); return; } data = file->readAll(); } else { if (minecraftVersion.isEmpty()) { emit failed(tr("Could not find \"version.json\" inside \"bin/modpack.jar\", but Minecraft version is unknown")); return; } components->setComponentVersion("net.minecraft", minecraftVersion, true); components->installJarMods({ modpackJar }); // Forge for 1.4.7 and for 1.5.2 require extra libraries. // Figure out the forge version and add it as a component // (the code still comes from the jar mod installed above) if (zipFile.exists("/forgeversion.properties")) { auto file = zipFile.goToFile("forgeversion.properties"); if (!file) { // Really shouldn't happen, but error handling shall not be forgotten emit failed(tr("Unable to open \"forgeversion.properties\"")); return; } auto forgeVersionData = file->readAll(); INIFile iniFile; iniFile.loadFile(forgeVersionData); QString major, minor, revision, build; major = iniFile["forge.major.number"].toString(); minor = iniFile["forge.minor.number"].toString(); revision = iniFile["forge.revision.number"].toString(); build = iniFile["forge.build.number"].toString(); if (major.isEmpty() || minor.isEmpty() || revision.isEmpty() || build.isEmpty()) { emit failed(tr("Invalid \"forgeversion.properties\"!")); return; } components->setComponentVersion("net.minecraftforge", major + '.' + minor + '.' + revision + '.' + build); } components->saveNow(); emit succeeded(); return; } } else if (QFile::exists(versionJson)) { QFile file(versionJson); if (!file.open(QIODevice::ReadOnly)) { emit failed(tr("Unable to open \"version.json\": %1").arg(file.errorString())); return; } data = file.readAll(); file.close(); } else { // This is the "Vanilla" modpack, excluded by the search code components->setComponentVersion("net.minecraft", minecraftVersion, true); components->saveNow(); emit succeeded(); return; } try { QJsonDocument doc = Json::requireDocument(data); QJsonObject root = Json::requireObject(doc, "version.json"); QString packMinecraftVersion = root["inheritsFrom"].toString(); if (packMinecraftVersion.isEmpty()) { if (fmlMinecraftVersion.isEmpty()) { emit failed(tr("Could not understand \"version.json\":\ninheritsFrom is missing")); return; } packMinecraftVersion = fmlMinecraftVersion; } components->setComponentVersion("net.minecraft", packMinecraftVersion, true); for (auto library : root["libraries"].toArray()) { if (!library.isObject()) { continue; } auto libraryObject = library.toObject(); auto libraryName = libraryObject["name"].toString(); if (libraryName.startsWith("net.neoforged.fancymodloader:")) { // it is neoforge // no easy way to get the version from the libs so use the arguments auto arguments = root["arguments"].toObject(); bool isVersionArg = false; QString neoforgeVersion; for (auto arg : arguments["game"].toArray()) { auto argument = arg.toString(""); if (isVersionArg) { neoforgeVersion = argument; break; } else { isVersionArg = "--fml.neoForgeVersion" == argument || "--fml.forgeVersion" == argument; } } if (!neoforgeVersion.isEmpty()) { components->setComponentVersion("net.neoforged", neoforgeVersion); } break; } else if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) && libraryName.contains('-')) { QString libraryVersion = libraryName.section(':', 2); if (!libraryVersion.startsWith("1.7.10-")) { components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1)); } else { // 1.7.10 versions sometimes look like 1.7.10-10.13.4.1614-1.7.10, this filters out the 10.13.4.1614 part components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1, 1)); } break; } else { // -> static QMap loaderMap{ { "net.minecraftforge:minecraftforge:", "net.minecraftforge" }, { "net.fabricmc:fabric-loader:", "net.fabricmc.fabric-loader" }, { "org.quiltmc:quilt-loader:", "org.quiltmc.quilt-loader" } }; for (const auto& loader : loaderMap.keys()) { if (libraryName.startsWith(loader)) { components->setComponentVersion(loaderMap.value(loader), libraryName.section(':', 2)); break; } } } } } catch (const JSONValidationError& e) { emit failed(tr("Could not understand \"version.json\":\n") + e.cause()); return; } components->saveNow(); emit succeeded(); } PrismLauncher-11.0.3/launcher/modplatform/technic/SingleZipPackInstallTask.cpp0000644000175100017510000001176315224505336027116 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "SingleZipPackInstallTask.h" #include #include "FileSystem.h" #include "MMCZip.h" #include "TechnicPackProcessor.h" #include "Application.h" #include "net/ApiDownload.h" Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl& sourceUrl, const QString& minecraftVersion) { m_sourceUrl = sourceUrl; m_minecraftVersion = minecraftVersion; } bool Technic::SingleZipPackInstallTask::abort() { if (m_abortable) { return m_filesNetJob->abort(); } return false; } void Technic::SingleZipPackInstallTask::executeTask() { setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path(); auto entry = APPLICATION->metacache()->resolveEntry("general", path); entry->setStale(true); m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network())); m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry)); m_archivePath = entry->getFullPath(); auto job = m_filesNetJob.get(); connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded); connect(job, &NetJob::progress, this, &Technic::SingleZipPackInstallTask::downloadProgressChanged); connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propagateStepProgress); connect(job, &NetJob::failed, this, &Technic::SingleZipPackInstallTask::downloadFailed); m_filesNetJob->start(); } void Technic::SingleZipPackInstallTask::downloadSucceeded() { m_abortable = false; setStatus(tr("Extracting modpack")); QDir extractDir(FS::PathCombine(m_stagingPath, "minecraft")); qDebug() << "Attempting to create instance from" << m_archivePath; // open the zip and find relevant files in it m_packZip.reset(new MMCZip::ArchiveReader(m_archivePath)); m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), QString(""), extractDir.absolutePath()); connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, &Technic::SingleZipPackInstallTask::extractFinished); connect(&m_extractFutureWatcher, &QFutureWatcher::canceled, this, &Technic::SingleZipPackInstallTask::extractAborted); m_extractFutureWatcher.setFuture(m_extractFuture); m_filesNetJob.reset(); } void Technic::SingleZipPackInstallTask::downloadFailed(QString reason) { m_abortable = false; m_filesNetJob.reset(); emitFailed(reason); } void Technic::SingleZipPackInstallTask::downloadProgressChanged(qint64 current, qint64 total) { m_abortable = true; setProgress(current / 2, total); } void Technic::SingleZipPackInstallTask::extractFinished() { m_packZip.reset(); if (!m_extractFuture.result()) { emitFailed(tr("Failed to extract modpack")); return; } QDir extractDir(m_stagingPath); qDebug() << "Fixing permissions for extracted pack files..."; QDirIterator it(extractDir, QDirIterator::Subdirectories); while (it.hasNext()) { auto filepath = it.next(); QFileInfo file(filepath); auto permissions = QFile::permissions(filepath); auto origPermissions = permissions; if (file.isDir()) { // Folder +rwx for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; } else { // File +rw for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; } if (origPermissions != permissions) { if (!QFile::setPermissions(filepath, permissions)) { logWarning(tr("Could not fix permissions for %1").arg(filepath)); } else { qDebug() << "Fixed" << filepath; } } } auto packProcessor = makeShared(); connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &Technic::SingleZipPackInstallTask::emitSucceeded); connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &Technic::SingleZipPackInstallTask::emitFailed); packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath, m_minecraftVersion); } void Technic::SingleZipPackInstallTask::extractAborted() { emitFailed(tr("Instance import has been aborted.")); } PrismLauncher-11.0.3/launcher/modplatform/technic/SolderPackManifest.cpp0000644000175100017510000000341215224505336025747 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "SolderPackManifest.h" #include "Json.h" namespace TechnicSolder { void loadPack(Pack& v, QJsonObject& obj) { v.recommended = Json::requireString(obj, "recommended"); v.latest = Json::requireString(obj, "latest"); auto builds = Json::requireArray(obj, "builds"); for (const auto buildRaw : builds) { auto build = Json::requireString(buildRaw); v.builds.append(build); } } static void loadPackBuildMod(PackBuildMod& b, QJsonObject& obj) { b.name = Json::requireString(obj, "name"); b.version = obj["version"].toString(""); b.md5 = Json::requireString(obj, "md5"); b.url = Json::requireString(obj, "url"); } void loadPackBuild(PackBuild& v, QJsonObject& obj) { v.minecraft = Json::requireString(obj, "minecraft"); auto mods = Json::requireArray(obj, "mods"); for (const auto modRaw : mods) { auto modObj = Json::requireObject(modRaw); PackBuildMod mod; loadPackBuildMod(mod, modObj); v.mods.append(mod); } } } // namespace TechnicSolder PrismLauncher-11.0.3/launcher/modplatform/flame/0000755000175100017510000000000015224505336021174 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/modplatform/flame/FileResolvingTask.cpp0000644000175100017510000002545515224505336025306 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "FileResolvingTask.h" #include #include "Json.h" #include "modplatform/ModIndex.h" #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" #include "modplatform/modrinth/ModrinthAPI.h" #include "modplatform/modrinth/ModrinthPackIndex.h" #include "net/NetJob.h" #include "tasks/Task.h" #include "Application.h" static const FlameAPI flameAPI; static ModrinthAPI modrinthAPI; Flame::FileResolvingTask::FileResolvingTask(Flame::Manifest& toProcess) : m_manifest(toProcess) {} bool Flame::FileResolvingTask::abort() { bool aborted = true; if (m_task) { aborted = m_task->abort(); } return aborted ? Task::abort() : false; } void Flame::FileResolvingTask::executeTask() { if (m_manifest.files.isEmpty()) { // no file to resolve so leave it empty and emit success immediately emitSucceeded(); return; } setStatus(tr("Resolving mod IDs...")); setProgress(0, 3); QStringList fileIds; for (auto file : m_manifest.files) { fileIds.push_back(QString::number(file.fileId)); } auto [task, response] = flameAPI.getFiles(fileIds); m_task = task; auto step_progress = std::make_shared(); connect(m_task.get(), &Task::succeeded, this, [this, response, step_progress]() { step_progress->state = TaskStepState::Succeeded; stepProgress(*step_progress); netJobFinished(response); }); connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) { step_progress->state = TaskStepState::Failed; stepProgress(*step_progress); emitFailed(reason); }); connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress); connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) { qDebug() << "Resolve slug progress" << current << total; step_progress->update(current, total); stepProgress(*step_progress); }); connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) { step_progress->status = status; stepProgress(*step_progress); }); m_task->start(); } ModPlatform::ResourceType getResourceType(int classId) { switch (classId) { case 17: // Worlds return ModPlatform::ResourceType::World; case 6: // Mods return ModPlatform::ResourceType::Mod; case 12: // Resource Packs // return ModPlatform::ResourceType::ResourcePack; // not really a resourcepack /* fallthrough */ case 4546: // Customization // return ModPlatform::ResourceType::ShaderPack; // not really a shaderPack /* fallthrough */ case 4471: // Modpacks /* fallthrough */ case 5: // Bukkit Plugins /* fallthrough */ case 4559: // Addons /* fallthrough */ default: return ModPlatform::ResourceType::Unknown; } } void Flame::FileResolvingTask::netJobFinished(QByteArray* response) { setProgress(1, 3); // job to check modrinth for blocked projects QJsonDocument doc; QJsonArray array; try { doc = Json::requireDocument(*response); array = Json::requireArray(doc.object()["data"]); } catch (Json::JsonException& e) { qCritical() << "Non-JSON data returned from the CF API"; qCritical() << e.cause(); emitFailed(tr("Invalid data returned from the API.")); return; } QStringList hashes; for (QJsonValueRef file : array) { try { auto obj = Json::requireObject(file); auto version = FlameMod::loadIndexedPackVersion(obj); auto fileid = version.fileId.toInt(); Q_ASSERT(fileid != 0); Q_ASSERT(m_manifest.files.contains(fileid)); m_manifest.files[fileid].version = version; auto url = QUrl(version.downloadUrl, QUrl::TolerantMode); if (!url.isValid() && "sha1" == version.hash_type && !version.hash.isEmpty()) { hashes.push_back(version.hash); } } catch (Json::JsonException& e) { qCritical() << "Non-JSON data returned from the CF API"; qCritical() << e.cause(); emitFailed(tr("Invalid data returned from the API.")); return; } } if (hashes.isEmpty()) { getFlameProjects(); return; } auto [modrinthTask, modrinthResponse] = modrinthAPI.currentVersions(hashes, "sha1"); m_task = modrinthTask; (dynamic_cast(m_task.get()))->setAskRetry(false); auto step_progress = std::make_shared(); connect(m_task.get(), &Task::succeeded, this, [this, modrinthResponse, step_progress]() { step_progress->state = TaskStepState::Succeeded; stepProgress(*step_progress); QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*modrinthResponse, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Modrinth::CurrentVersions at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *modrinthResponse; getFlameProjects(); return; } if (APPLICATION->settings()->get("FallbackMRBlockedMods").toBool()){ try { auto entries = Json::requireObject(doc); for (auto& out : m_manifest.files) { auto url = QUrl(out.version.downloadUrl, QUrl::TolerantMode); if (!url.isValid() && "sha1" == out.version.hash_type && !out.version.hash.isEmpty()) { try { auto entry = Json::requireObject(entries, out.version.hash); auto file = Modrinth::loadIndexedPackVersion(entry); out.version.downloadUrl = file.downloadUrl; qDebug() << "Found alternative on modrinth" << out.version.fileName; } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << entries; } } } } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << doc; } } getFlameProjects(); }); connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) { step_progress->state = TaskStepState::Failed; stepProgress(*step_progress); getFlameProjects(); }); connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress); connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) { qDebug() << "Resolve slug progress" << current << total; step_progress->update(current, total); stepProgress(*step_progress); }); connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) { step_progress->status = status; stepProgress(*step_progress); }); m_task->start(); } void Flame::FileResolvingTask::getFlameProjects() { setProgress(2, 3); QStringList addonIds; for (auto file : m_manifest.files) { addonIds.push_back(QString::number(file.projectId)); } auto [task, response] = flameAPI.getProjects(addonIds); m_task = task; auto step_progress = std::make_shared(); connect(m_task.get(), &Task::succeeded, this, [this, response, step_progress] { QJsonParseError parse_error{}; auto doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Modrinth projects task at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; return; } try { QJsonArray entries; entries = Json::requireArray(Json::requireObject(doc), "data"); for (auto entry : entries) { auto entry_obj = Json::requireObject(entry); auto id = Json::requireInteger(entry_obj, "id"); auto file = std::find_if(m_manifest.files.begin(), m_manifest.files.end(), [id](const Flame::File& file) { return file.projectId == id; }); if (file == m_manifest.files.end()) { continue; } setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(file->version.fileName)); FlameMod::loadIndexedPack(file->pack, entry_obj); file->resourceType = getResourceType(Json::requireInteger(entry_obj, "classId", "modClassId")); if (file->resourceType == ModPlatform::ResourceType::World) { file->targetFolder = "saves"; } } } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << doc; } step_progress->state = TaskStepState::Succeeded; stepProgress(*step_progress); emitSucceeded(); }); connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) { step_progress->state = TaskStepState::Failed; stepProgress(*step_progress); emitFailed(reason); }); connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress); connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) { qDebug() << "Resolve slug progress" << current << total; step_progress->update(current, total); stepProgress(*step_progress); }); connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) { step_progress->status = status; stepProgress(*step_progress); }); m_task->start(); } PrismLauncher-11.0.3/launcher/modplatform/flame/PackManifest.h0000644000175100017510000000500015224505336023705 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include "modplatform/ModIndex.h" #include "modplatform/ResourceType.h" namespace Flame { struct File { int projectId = 0; int fileId = 0; // NOTE: the opposite to 'optional' bool required = true; ModPlatform::IndexedPack pack; ModPlatform::IndexedVersion version; // our QString targetFolder = QStringLiteral("mods"); ModPlatform::ResourceType resourceType; }; struct Modloader { QString id; bool primary = false; }; struct Minecraft { QString version; QString libraries; QList modLoaders; int recommendedRAM; }; struct Manifest { QString manifestType; int manifestVersion = 0; Flame::Minecraft minecraft; QString name; QString version; QString author; // File id -> File QMap files; QString overrides; bool is_loaded = false; }; void loadManifest(Flame::Manifest& m, const QString& filepath); } // namespace Flame PrismLauncher-11.0.3/launcher/modplatform/flame/FlameModIndex.h0000644000175100017510000000101015224505336024011 0ustar runnerrunner// // Created by timoreo on 16/01/2022. // #pragma once #include "modplatform/ModIndex.h" #include "BaseInstance.h" namespace FlameMod { void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj); void loadURLs(ModPlatform::IndexedPack& m, QJsonObject& obj); void loadBody(ModPlatform::IndexedPack& m); void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr); ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false); } // namespace FlameMod PrismLauncher-11.0.3/launcher/modplatform/flame/FlameInstanceCreationTask.h0000644000175100017510000000632115224505336026370 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "InstanceCreationTask.h" #include #include "minecraft/MinecraftInstance.h" #include "modplatform/flame/FileResolvingTask.h" #include "net/NetJob.h" #include "ui/dialogs/BlockedModsDialog.h" class FlameCreationTask final : public InstanceCreationTask { Q_OBJECT public: FlameCreationTask(const QString& staging_path, SettingsObject* global_settings, QWidget* parent, QString id, QString version_id, QString original_instance_id = {}) : InstanceCreationTask(), m_parent(parent), m_managedId(std::move(id)), m_managedVersionId(std::move(version_id)) { setStagingPath(staging_path); setParentSettings(global_settings); m_original_instance_id = std::move(original_instance_id); } bool abort() override; bool updateInstance() override; std::unique_ptr createInstance() override; private slots: void idResolverSucceeded(QEventLoop&); void setupDownloadJob(QEventLoop&); void copyBlockedMods(QList const& blocked_mods); void validateOtherResources(QEventLoop& loop); QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion); private: QWidget* m_parent = nullptr; shared_qobject_ptr m_modIdResolver; Flame::Manifest m_pack; // Handle to allow aborting Task::Ptr m_processUpdateFileInfoJob = nullptr; NetJob::Ptr m_filesJob = nullptr; QString m_managedId, m_managedVersionId; QList> m_otherResources; std::optional m_instance; QStringList m_selectedOptionalMods; }; PrismLauncher-11.0.3/launcher/modplatform/flame/FlamePackExportTask.h0000644000175100017510000000424015224505336025215 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 TheKodeToad * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "MMCZip.h" #include "minecraft/MinecraftInstance.h" #include "modplatform/flame/FlameAPI.h" #include "tasks/Task.h" struct FlamePackExportOptions { QString name; QString version; QString author; bool optionalFiles; MinecraftInstance* instance; QString output; MMCZip::FilterFileFunction filter; int recommendedRAM; }; class FlamePackExportTask : public Task { Q_OBJECT public: FlamePackExportTask(FlamePackExportOptions&& options); protected: void executeTask() override; bool abort() override; private: static const QString TEMPLATE; static const QStringList FILE_EXTENSIONS; // inputs struct ResolvedFile { int addonId; int version; bool enabled; bool isMod; QString name; QString slug; QString authors; }; struct HashInfo { QString name; QString path; bool enabled; bool isMod; }; FlamePackExportOptions m_options; QDir m_gameRoot; FlameAPI api; QFileInfoList m_files; QMap pendingHashes{}; QMap resolvedFiles{}; Task::Ptr task; void collectFiles(); void collectHashes(); void makeApiRequest(); void getProjectsInfo(); void buildZip(); QByteArray generateIndex(); QByteArray generateHTML(); }; PrismLauncher-11.0.3/launcher/modplatform/flame/FileResolvingTask.h0000644000175100017510000000260715224505336024745 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "PackManifest.h" #include "tasks/Task.h" namespace Flame { class FileResolvingTask : public Task { Q_OBJECT public: explicit FileResolvingTask(Flame::Manifest& toProcess); virtual ~FileResolvingTask() = default; bool canAbort() const override { return true; } bool abort() override; const Flame::Manifest& getResults() const { return m_manifest; } protected: virtual void executeTask() override; protected slots: void netJobFinished(QByteArray* response); private: void getFlameProjects(); private: /* data */ Flame::Manifest m_manifest; Task::Ptr m_task; }; } // namespace Flame PrismLauncher-11.0.3/launcher/modplatform/flame/FlameInstanceCreationTask.cpp0000644000175100017510000006663015224505336026734 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FlameInstanceCreationTask.h" #include "InstanceTask.h" #include "QObjectPtr.h" #include "minecraft/mod/tasks/LocalResourceUpdateTask.h" #include "modplatform/flame/FileResolvingTask.h" #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" #include "modplatform/flame/PackManifest.h" #include "Application.h" #include "FileSystem.h" #include "InstanceList.h" #include "Json.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "modplatform/helpers/OverrideUtils.h" #include "settings/INISettingsObject.h" #include "SysInfo.h" #include "tasks/ConcurrentTask.h" #include "ui/dialogs/BlockedModsDialog.h" #include "ui/dialogs/CustomMessageBox.h" #include #include #include "HardwareInfo.h" #include "meta/Index.h" #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" #include "net/ApiDownload.h" #include "ui/pages/modplatform/OptionalModDialog.h" static const FlameAPI api; bool FlameCreationTask::abort() { if (!canAbort()) return false; if (m_processUpdateFileInfoJob) m_processUpdateFileInfoJob->abort(); if (m_filesJob) m_filesJob->abort(); if (m_modIdResolver) m_modIdResolver->abort(); return InstanceCreationTask::abort(); } bool FlameCreationTask::updateInstance() { auto instance_list = APPLICATION->instances(); // FIXME: How to handle situations when there's more than one install already for a given modpack? BaseInstance* inst; if (auto original_id = originalInstanceID(); !original_id.isEmpty()) { inst = instance_list->getInstanceById(original_id); Q_ASSERT(inst); } else { inst = instance_list->getInstanceByManagedName(originalName()); if (!inst) { inst = instance_list->getInstanceById(originalName()); if (!inst) return false; } } QString index_path(FS::PathCombine(m_stagingPath, "manifest.json")); try { Flame::loadManifest(m_pack, index_path); } catch (const JSONValidationError& e) { setError(tr("Could not understand pack manifest:\n") + e.cause()); return false; } auto version_id = inst->getManagedPackVersionName(); auto version_str = !version_id.isEmpty() ? tr(" (version %1)").arg(version_id) : ""; if (shouldConfirmUpdate()) { auto should_update = askIfShouldUpdate(m_parent, version_str); if (should_update == ShouldUpdate::SkipUpdating) return false; if (should_update == ShouldUpdate::Cancel) { m_abort = true; return false; } } QDir old_inst_dir(inst->instanceRoot()); QString old_index_folder(FS::PathCombine(old_inst_dir.absolutePath(), "flame")); QString old_index_path(FS::PathCombine(old_index_folder, "manifest.json")); QFileInfo old_index_file(old_index_path); if (old_index_file.exists()) { Flame::Manifest old_pack; Flame::loadManifest(old_pack, old_index_path); auto& old_files = old_pack.files; auto& files = m_pack.files; // Remove repeated files, we don't need to download them! auto files_iterator = files.begin(); while (files_iterator != files.end()) { auto const& file = files_iterator; auto old_file = old_files.find(file.key()); if (old_file != old_files.end()) { // We found a match, but is it a different version? if (old_file->fileId == file->fileId) { qDebug() << "Removed file at" << file->targetFolder << "with id" << file->fileId << "from list of downloads"; old_files.remove(file.key()); files_iterator = files.erase(files_iterator); if (files_iterator != files.begin()) files_iterator--; } } files_iterator++; } QDir old_minecraft_dir(inst->gameRoot()); // We will remove all the previous overrides, to prevent duplicate files! // TODO: Currently 'overrides' will always override the stuff on update. How do we preserve unchanged overrides? // FIXME: We may want to do something about disabled mods. auto old_overrides = Override::readOverrides("overrides", old_index_folder); for (const auto& entry : old_overrides) { scheduleToDelete(m_parent, old_minecraft_dir, entry); } // Remove remaining old files (we need to do an API request to know which ids are which files...) QStringList fileIds; for (auto& file : old_files) { fileIds.append(QString::number(file.fileId)); } auto [job, raw_response] = api.getFiles(fileIds); QEventLoop loop; connect(job.get(), &Task::succeeded, this, [this, raw_response, fileIds, old_inst_dir, &old_files, old_minecraft_dir] { // Parse the API response QJsonParseError parse_error{}; auto doc = QJsonDocument::fromJson(*raw_response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Flame files task at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *raw_response; return; } try { QJsonArray entries; if (fileIds.size() == 1) entries = { Json::requireObject(Json::requireObject(doc), "data") }; else entries = Json::requireArray(Json::requireObject(doc), "data"); for (auto entry : entries) { auto entry_obj = Json::requireObject(entry); Flame::File file; // We don't care about blocked mods, we just need local data to delete the file file.version = FlameMod::loadIndexedPackVersion(entry_obj); auto id = Json::requireInteger(entry_obj, "id"); old_files.insert(id, file); } } catch (Json::JsonException& e) { qCritical() << e.cause() << e.what(); } // Delete the files for (auto& file : old_files) { if (file.version.fileName.isEmpty() || file.targetFolder.isEmpty()) continue; QString relative_path(FS::PathCombine(file.targetFolder, file.version.fileName)); scheduleToDelete(m_parent, old_minecraft_dir, relative_path, true); } }); connect(job.get(), &Task::failed, this, [](QString reason) { qCritical() << "Failed to get files:" << reason; }); connect(job.get(), &Task::finished, &loop, &QEventLoop::quit); m_processUpdateFileInfoJob = job; job->start(); loop.exec(); m_processUpdateFileInfoJob = nullptr; } else { // We don't have an old index file, so we may duplicate stuff! auto dialog = CustomMessageBox::selectable(m_parent, tr("No index file."), tr("We couldn't find a suitable index file for the older version. This may cause some " "of the files to be duplicated. Do you want to continue?"), QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel); if (dialog->exec() == QDialog::DialogCode::Rejected) { m_abort = true; return false; } } setOverride(true, inst->id()); qDebug() << "Will override instance!"; m_instance = inst; // We let it go through the createInstance() stage, just with a couple modifications for updating return false; } QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, QString loaderVersion, QString mcVersion) { if (loaderVersion == "recommended") { auto vlist = APPLICATION->metadataIndex()->get(uid); if (!vlist) { setError(tr("Failed to get local metadata index for %1").arg(uid)); return {}; } if (!vlist->isLoaded()) { QEventLoop loadVersionLoop; auto task = vlist->getLoadTask(); connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit); if (!task->isRunning()) task->start(); loadVersionLoop.exec(); } for (auto version : vlist->versions()) { // first recommended build we find, we use. if (!version->isRecommended()) continue; auto reqs = version->requiredSet(); // filter by minecraft version, if the loader depends on a certain version. // not all mod loaders depend on a given Minecraft version, so we won't do this // filtering for those loaders. if (loaderType == "forge" || loaderType == "neoforge") { auto iter = std::find_if(reqs.begin(), reqs.end(), [mcVersion](const Meta::Require& req) { return req.uid == "net.minecraft" && req.equalsVersion == mcVersion; }); if (iter == reqs.end()) continue; } return version->descriptor(); } setError(tr("Failed to find version for %1 loader").arg(loaderType)); return {}; } if (loaderVersion.isEmpty()) { emitFailed(tr("No loader version set for modpack!")); return {}; } return loaderVersion; } std::unique_ptr FlameCreationTask::createInstance() { QEventLoop loop; QString parent_folder(FS::PathCombine(m_stagingPath, "flame")); try { QString index_path(FS::PathCombine(m_stagingPath, "manifest.json")); if (!m_pack.is_loaded) Flame::loadManifest(m_pack, index_path); // Keep index file in case we need it some other time (like when changing versions) QString new_index_place(FS::PathCombine(parent_folder, "manifest.json")); FS::ensureFilePathExists(new_index_place); FS::move(index_path, new_index_place); } catch (const JSONValidationError& e) { setError(tr("Could not understand pack manifest:\n") + e.cause()); return nullptr; } if (!m_pack.overrides.isEmpty()) { QString overridePath = FS::PathCombine(m_stagingPath, m_pack.overrides); if (QFile::exists(overridePath)) { // Create a list of overrides in "overrides.txt" inside flame/ Override::createOverrides("overrides", parent_folder, overridePath); QString mcPath = FS::PathCombine(m_stagingPath, "minecraft"); if (!FS::move(overridePath, mcPath)) { setError(tr("Could not rename the overrides folder:\n") + m_pack.overrides); return nullptr; } } else { logWarning( tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(m_pack.overrides)); } } QString loaderType; QString loaderUid; QString loaderVersion; for (auto& loader : m_pack.minecraft.modLoaders) { auto id = loader.id; if (id.startsWith("neoforge-")) { id.remove("neoforge-"); if (id.startsWith("1.20.1-")) id.remove("1.20.1-"); // this is a mess for curseforge loaderType = "neoforge"; loaderUid = "net.neoforged"; } else if (id.startsWith("forge-")) { id.remove("forge-"); loaderType = "forge"; loaderUid = "net.minecraftforge"; } else if (id.startsWith("fabric-")) { id.remove("fabric-"); loaderType = "fabric"; loaderUid = "net.fabricmc.fabric-loader"; } else if (id.startsWith("quilt-")) { id.remove("quilt-"); loaderType = "quilt"; loaderUid = "org.quiltmc.quilt-loader"; } else { logWarning(tr("Unknown mod loader in manifest: %1").arg(id)); continue; } loaderVersion = id; } QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_unique(configPath); auto instance = std::make_unique(m_globalSettings, std::move(instanceSettings), m_stagingPath); auto mcVersion = m_pack.minecraft.version; // Hack to correct some 'special sauce'... if (mcVersion.endsWith('.')) { static const QRegularExpression s_regex("[.]+$"); mcVersion.remove(s_regex); logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack.")); } auto components = instance->getPackProfile(); components->buildingFromScratch(); components->setComponentVersion("net.minecraft", mcVersion, true); if (!loaderType.isEmpty()) { auto version = getVersionForLoader(loaderUid, loaderType, loaderVersion, mcVersion); if (version.isEmpty()) return nullptr; components->setComponentVersion(loaderUid, version); } if (m_instIcon != "default") { instance->setIconKey(m_instIcon); } else { if (m_pack.name.contains("Direwolf20")) { instance->setIconKey("steve"); } else if (m_pack.name.contains("FTB") || m_pack.name.contains("Feed The Beast")) { instance->setIconKey("ftb_logo"); } else { instance->setIconKey("flame"); } } int recommendedRAM = m_pack.minecraft.recommendedRAM; // only set memory if this is a fresh instance if (!m_instance && recommendedRAM > 0) { const uint64_t sysMiB = HardwareInfo::totalRamMiB(); const uint64_t max = sysMiB * 0.9; if (static_cast(recommendedRAM) > max) { logWarning(tr("The recommended memory of the modpack exceeds 90% of your system RAM—reducing it from %1 MiB to %2 MiB!") .arg(recommendedRAM) .arg(max)); recommendedRAM = max; } instance->settings()->set("OverrideMemory", true); instance->settings()->set("MaxMemAlloc", recommendedRAM); } QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods"); QFileInfo jarmodsInfo(jarmodsPath); if (jarmodsInfo.isDir()) { // install all the jar mods qDebug() << "Found jarmods:"; QDir jarmodsDir(jarmodsPath); QStringList jarMods; for (const auto& info : jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) { qDebug() << info.fileName(); jarMods.push_back(info.absoluteFilePath()); } auto profile = instance->getPackProfile(); profile->installJarMods(jarMods); // nuke the original files FS::deletePath(jarmodsPath); } // Don't add managed info to packs without an ID (most likely imported from ZIP) if (!m_managedId.isEmpty()) instance->setManagedPack("flame", m_managedId, m_pack.name, m_managedVersionId, m_pack.version); else instance->setManagedPack("flame", "", name(), "", ""); instance->setName(name()); m_modIdResolver.reset(new Flame::FileResolvingTask(m_pack)); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, this, [this, &loop] { idResolverSucceeded(loop); }); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [this, &loop](QString reason) { m_modIdResolver.reset(); setError(tr("Unable to resolve mod IDs:\n") + reason); loop.quit(); }); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::aborted, &loop, &QEventLoop::quit); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propagateStepProgress); connect(m_modIdResolver.get(), &Flame::FileResolvingTask::details, this, &FlameCreationTask::setDetails); m_modIdResolver->start(); loop.exec(); bool did_succeed = getError().isEmpty(); // Update information of the already installed instance, if any. if (m_instance && did_succeed) { setAbortable(false); auto inst = m_instance.value(); inst->copyManagedPack(*instance); } if (did_succeed) { return instance; } return nullptr; } void FlameCreationTask::idResolverSucceeded(QEventLoop& loop) { auto results = m_modIdResolver->getResults().files; QStringList optionalFiles; for (auto& result : results) { if (!result.required) { optionalFiles << FS::PathCombine(result.targetFolder, result.version.fileName); } } if (!optionalFiles.empty()) { OptionalModDialog optionalModDialog(m_parent, optionalFiles); if (optionalModDialog.exec() == QDialog::Rejected) { emitAborted(); loop.quit(); return; } m_selectedOptionalMods = optionalModDialog.getResult(); } // first check for blocked mods QList blocked_mods; auto anyBlocked = false; for (const auto& result : results.values()) { if (result.resourceType != ModPlatform::ResourceType::Mod) { m_otherResources.append(std::make_pair(result.version.fileName, result.targetFolder)); } // skip optional mods that were not selected if (result.version.downloadUrl.isEmpty()) { BlockedMod blocked_mod; blocked_mod.name = result.version.fileName; blocked_mod.websiteUrl = QString("%1/download/%2").arg(result.pack.websiteUrl, QString::number(result.fileId)); blocked_mod.hash = result.version.hash; blocked_mod.matched = false; blocked_mod.localPath = ""; blocked_mod.targetFolder = result.targetFolder; auto fileName = result.version.fileName; fileName = FS::RemoveInvalidPathChars(fileName); auto relpath = FS::PathCombine(result.targetFolder, fileName); blocked_mod.disabled = !result.required && !m_selectedOptionalMods.contains(relpath); blocked_mods.append(blocked_mod); anyBlocked = true; } } if (anyBlocked) { qWarning() << "Blocked mods found, displaying mod list"; BlockedModsDialog message_dialog(m_parent, tr("Blocked mods found"), tr("The following files are not available for download in third party launchers.
" "You will need to manually download them and add them to the instance."), blocked_mods); message_dialog.setModal(true); if (message_dialog.exec()) { qDebug() << "Post dialog blocked mods list:" << blocked_mods; copyBlockedMods(blocked_mods); setupDownloadJob(loop); } else { m_modIdResolver.reset(); setError("Canceled"); loop.quit(); } } else { setupDownloadJob(loop); } } void FlameCreationTask::setupDownloadJob(QEventLoop& loop) { m_filesJob.reset(new NetJob(tr("Mod Download Flame"), APPLICATION->network())); auto results = m_modIdResolver->getResults().files; for (const auto& result : results) { auto fileName = result.version.fileName; fileName = FS::RemoveInvalidPathChars(fileName); auto relpath = FS::PathCombine(result.targetFolder, fileName); if (!result.required && !m_selectedOptionalMods.contains(relpath)) { relpath += ".disabled"; } relpath = FS::PathCombine("minecraft", relpath); auto path = FS::PathCombine(m_stagingPath, relpath); if (!result.version.downloadUrl.isEmpty()) { qDebug() << "Will download" << result.version.downloadUrl << "to" << path; auto dl = Net::ApiDownload::makeFile(result.version.downloadUrl, path); m_filesJob->addNetAction(dl); } } connect(m_filesJob.get(), &NetJob::finished, this, [this, &loop]() { m_filesJob.reset(); validateOtherResources(loop); }); connect(m_filesJob.get(), &NetJob::failed, [this](QString reason) { m_filesJob.reset(); setError(reason); }); connect(m_filesJob.get(), &NetJob::progress, this, [this](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); setProgress(current, total); }); connect(m_filesJob.get(), &NetJob::stepProgress, this, &FlameCreationTask::propagateStepProgress); setStatus(tr("Downloading mods...")); m_filesJob->start(); } /// @brief copy the matched blocked mods to the instance staging area /// @param blocked_mods list of the blocked mods and their matched paths void FlameCreationTask::copyBlockedMods(QList const& blocked_mods) { setStatus(tr("Copying Blocked Mods...")); setAbortable(false); int i = 0; int total = blocked_mods.length(); setProgress(i, total); for (auto const& mod : blocked_mods) { if (!mod.matched) { qDebug() << mod.name << "was not matched to a local file, skipping copy"; continue; } auto destPath = FS::PathCombine(m_stagingPath, "minecraft", mod.targetFolder, mod.name); if (mod.disabled) destPath += ".disabled"; setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total))); qDebug() << "Will try to copy" << mod.localPath << "to" << destPath; if (mod.move) { if (!FS::move(mod.localPath, destPath)) { qDebug() << "Move of" << mod.localPath << "to" << destPath << "Failed"; } } else { if (!FS::copy(mod.localPath, destPath)()) { qDebug() << "Copy of" << mod.localPath << "to" << destPath << "Failed"; } } i++; setProgress(i, total); } setAbortable(true); } void FlameCreationTask::validateOtherResources(QEventLoop& loop) { qDebug() << "Validating whether other resources are in the right place"; QStringList zipMods; for (auto [fileName, targetFolder] : m_otherResources) { qDebug() << "Checking" << fileName << "..."; auto localPath = FS::PathCombine(m_stagingPath, "minecraft", targetFolder, fileName); /// @brief check the target and move the the file /// @return path where file can now be found auto validatePath = [&localPath, this](QString fileName, QString targetFolder, QString realTarget) { if (targetFolder != realTarget) { qDebug() << "Target folder of" << fileName << "is incorrect, it belongs in" << realTarget; auto destPath = FS::PathCombine(m_stagingPath, "minecraft", realTarget, fileName); qDebug() << "Moving" << localPath << "to" << destPath; if (FS::move(localPath, destPath)) { return destPath; } } else { qDebug() << "Target folder of" << fileName << "is correct at" << targetFolder; } return localPath; }; auto installWorld = [this](QString worldPath) { qDebug() << "Installing World from" << worldPath; QFileInfo worldFileInfo(worldPath); World w(worldFileInfo); if (!w.isValid()) { qDebug() << "World at" << worldPath << "is not valid, skipping install."; } else { w.install(FS::PathCombine(m_stagingPath, "minecraft", "saves")); } }; QFileInfo localFileInfo(localPath); auto type = ResourceUtils::identify(localFileInfo); QString worldPath; switch (type) { case ModPlatform::ResourceType::Mod: validatePath(fileName, targetFolder, "mods"); zipMods.push_back(fileName); break; case ModPlatform::ResourceType::ResourcePack: validatePath(fileName, targetFolder, "resourcepacks"); break; case ModPlatform::ResourceType::TexturePack: validatePath(fileName, targetFolder, "texturepacks"); break; case ModPlatform::ResourceType::DataPack: validatePath(fileName, targetFolder, "datapacks"); break; case ModPlatform::ResourceType::ShaderPack: // in theory flame API can't do this but who knows, that *may* change ? // better to handle it if it *does* occur in the future validatePath(fileName, targetFolder, "shaderpacks"); break; case ModPlatform::ResourceType::World: worldPath = validatePath(fileName, targetFolder, "saves"); installWorld(worldPath); break; case ModPlatform::ResourceType::Unknown: /* fallthrough */ default: qDebug() << "Can't Identify" << fileName << "at" << localPath << ", leaving it where it is."; break; } } // TODO make this work with other sorts of resource auto task = makeShared("CreateModMetadata", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); auto results = m_modIdResolver->getResults().files; auto folder = FS::PathCombine(m_stagingPath, "minecraft", "mods", ".index"); for (auto file : results) { if (file.targetFolder != "mods" || (file.version.fileName.endsWith(".zip") && !zipMods.contains(file.version.fileName))) { continue; } task->addTask(makeShared(folder, file.pack, file.version)); } connect(task.get(), &Task::finished, &loop, &QEventLoop::quit); m_processUpdateFileInfoJob = task; task->start(); } PrismLauncher-11.0.3/launcher/modplatform/flame/FlameAPI.cpp0000644000175100017510000002426015224505336023262 0ustar runnerrunner// SPDX-FileCopyrightText: 2023 flowln // // SPDX-License-Identifier: GPL-3.0-only #include "FlameAPI.h" #include #include #include "BuildConfig.h" #include "FlameModIndex.h" #include "Application.h" #include "Json.h" #include "modplatform/ModIndex.h" #include "net/ApiDownload.h" #include "net/ApiUpload.h" #include "net/NetJob.h" std::pair FlameAPI::matchFingerprints(const QList& fingerprints) { auto netJob = makeShared(QString("Flame::MatchFingerprints"), APPLICATION->network()); QJsonObject body_obj; QJsonArray fingerprints_arr; for (auto& fp : fingerprints) { fingerprints_arr.append(QString("%1").arg(fp)); } body_obj["fingerprints"] = fingerprints_arr; QJsonDocument body(body_obj); auto body_raw = body.toJson(); auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.FLAME_BASE_URL + "/fingerprints"), body_raw); netJob->addNetAction(action); return { netJob, response }; } QString FlameAPI::getModFileChangelog(int modId, int fileId) { QEventLoop lock; QString changelog; auto netJob = makeShared(QString("Flame::FileChangelog"), APPLICATION->network()); auto [action, response] = Net::ApiDownload::makeByteArray( QString(BuildConfig.FLAME_BASE_URL + "/mods/%1/files/%2/changelog") .arg(QString::fromStdString(std::to_string(modId)), QString::fromStdString(std::to_string(fileId)))); netJob->addNetAction(action); QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &changelog] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Flame::FileChangelog at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; netJob->failed(parse_error.errorString()); return; } changelog = doc.object()["data"].toString(); }); QObject::connect(netJob.get(), &NetJob::finished, [&lock] { lock.quit(); }); netJob->start(); lock.exec(); return changelog; } QString FlameAPI::getModDescription(int modId) { QEventLoop lock; QString description; auto netJob = makeShared(QString("Flame::ModDescription"), APPLICATION->network()); auto [action, response] = Net::ApiDownload::makeByteArray(QString(BuildConfig.FLAME_BASE_URL + "/mods/%1/description").arg(QString::number(modId))); netJob->addNetAction(action); QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Flame::ModDescription at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; netJob->failed(parse_error.errorString()); return; } description = doc.object()["data"].toString(); }); QObject::connect(netJob.get(), &NetJob::finished, [&lock] { lock.quit(); }); netJob->start(); lock.exec(); return description; } std::pair FlameAPI::getProjects(QStringList addonIds) const { auto netJob = makeShared(QString("Flame::GetProjects"), APPLICATION->network()); QJsonObject body_obj; QJsonArray addons_arr; for (auto& addonId : addonIds) { addons_arr.append(addonId); } body_obj["modIds"] = addons_arr; QJsonDocument body(body_obj); auto body_raw = body.toJson(); auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.FLAME_BASE_URL + "/mods"), body_raw); netJob->addNetAction(action); QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); return { netJob, response }; } std::pair FlameAPI::getFiles(const QStringList& fileIds) const { auto netJob = makeShared(QString("Flame::GetFiles"), APPLICATION->network()); QJsonObject body_obj; QJsonArray files_arr; for (auto& fileId : fileIds) { files_arr.append(fileId); } body_obj["fileIds"] = files_arr; QJsonDocument body(body_obj); auto body_raw = body.toJson(); auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.FLAME_BASE_URL + "/mods/files"), body_raw); netJob->addNetAction(action); QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; }); return { netJob, response }; } std::pair FlameAPI::getFile(const QString& addonId, const QString& fileId) const { auto netJob = makeShared(QString("Flame::GetFile"), APPLICATION->network()); auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(QString(BuildConfig.FLAME_BASE_URL + "/mods/%1/files/%2").arg(addonId, fileId))); netJob->addNetAction(action); QObject::connect(netJob.get(), &NetJob::failed, [addonId, fileId] { qDebug() << "Flame API file failure" << addonId << fileId; }); return { netJob, response }; } QList FlameAPI::getSortingMethods() const { // https://docs.curseforge.com/?python#tocS_ModsSearchSortField return { { 1, "Featured", QObject::tr("Sort by Featured") }, { 2, "Popularity", QObject::tr("Sort by Popularity") }, { 3, "LastUpdated", QObject::tr("Sort by Last Updated") }, { 4, "Name", QObject::tr("Sort by Name") }, { 5, "Author", QObject::tr("Sort by Author") }, { 6, "TotalDownloads", QObject::tr("Sort by Downloads") }, { 7, "Category", QObject::tr("Sort by Category") }, { 8, "GameVersion", QObject::tr("Sort by Game Version") } }; } std::pair FlameAPI::getCategories(ModPlatform::ResourceType type) { auto netJob = makeShared(QString("Flame::GetCategories"), APPLICATION->network()); auto [action, response] = Net::ApiDownload::makeByteArray( QUrl(QString(BuildConfig.FLAME_BASE_URL + "/categories?gameId=432&classId=%1").arg(getClassId(type)))); netJob->addNetAction(action); QObject::connect(netJob.get(), &Task::failed, [](QString msg) { qDebug() << "Flame failed to get categories:" << msg; }); return { netJob, response }; } std::pair FlameAPI::getModCategories() { return getCategories(ModPlatform::ResourceType::Mod); } QList FlameAPI::loadModCategories(const QByteArray& response) { QList categories; QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from categories at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; return categories; } try { auto obj = Json::requireObject(doc); auto arr = Json::requireArray(obj, "data"); for (auto val : arr) { auto cat = Json::requireObject(val); auto id = Json::requireInteger(cat, "id"); auto name = Json::requireString(cat, "name"); categories.push_back({ name, QString::number(id) }); } } catch (Json::JsonException& e) { qCritical() << "Failed to parse response from a version request."; qCritical() << e.what(); qDebug() << doc; } return categories; }; std::optional FlameAPI::getLatestVersion(QList versions, QList instanceLoaders, ModPlatform::ModLoaderTypes modLoaders, bool checkLoaders) { static const auto noLoader = ModPlatform::ModLoaderType(0); if (!checkLoaders) { std::optional ver; for (auto file_tmp : versions) { if (!ver.has_value() || file_tmp.date > ver->date) { ver = file_tmp; } } return ver; } QHash bestMatch; auto checkVersion = [&bestMatch](const ModPlatform::IndexedVersion& version, const ModPlatform::ModLoaderType& loader) { if (bestMatch.contains(loader)) { auto best = bestMatch.value(loader); if (version.date > best.date) { bestMatch[loader] = version; } } else { bestMatch[loader] = version; } }; for (auto file_tmp : versions) { auto loaders = ModPlatform::modLoaderTypesToList(file_tmp.loaders); if (loaders.isEmpty()) { checkVersion(file_tmp, noLoader); } else { for (auto loader : loaders) { checkVersion(file_tmp, loader); } } } // edge case: mod has installed for forge but the instance is fabric => fabric version will be prioritizated on update auto currentLoaders = instanceLoaders + ModPlatform::modLoaderTypesToList(modLoaders); currentLoaders.append(noLoader); // add a fallback in case the versions do not define a loader for (auto loader : currentLoaders) { if (bestMatch.contains(loader)) { auto bestForLoader = bestMatch.value(loader); // awkward case where the mod has only two loaders and one of them is not specified if (loader != noLoader && bestMatch.contains(noLoader) && bestMatch.size() == 2) { auto bestForNoLoader = bestMatch.value(noLoader); if (bestForNoLoader.date > bestForLoader.date) { return bestForNoLoader; } } return bestForLoader; } } return {}; } PrismLauncher-11.0.3/launcher/modplatform/flame/FlameAPI.h0000644000175100017510000001717615224505336022737 0ustar runnerrunner// SPDX-FileCopyrightText: 2023 flowln // // SPDX-License-Identifier: GPL-3.0-only #pragma once #include #include #include "BuildConfig.h" #include "Json.h" #include "Version.h" #include "modplatform/ModIndex.h" #include "modplatform/ResourceAPI.h" #include "modplatform/flame/FlameModIndex.h" class FlameAPI : public ResourceAPI { public: QString getModFileChangelog(int modId, int fileId); QString getModDescription(int modId); std::optional getLatestVersion(QList versions, QList instanceLoaders, ModPlatform::ModLoaderTypes fallback, bool checkLoaders); std::pair getProjects(QStringList addonIds) const override; std::pair matchFingerprints(const QList& fingerprints); std::pair getFiles(const QStringList& fileIds) const; std::pair getFile(const QString& addonId, const QString& fileId) const; static std::pair getCategories(ModPlatform::ResourceType type); static std::pair getModCategories(); static QList loadModCategories(const QByteArray& response); QList getSortingMethods() const override; static inline bool validateModLoaders(ModPlatform::ModLoaderTypes loaders) { return loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt); } private: static int getClassId(ModPlatform::ResourceType type) { switch (type) { default: case ModPlatform::ResourceType::Mod: return 6; case ModPlatform::ResourceType::ResourcePack: return 12; case ModPlatform::ResourceType::ShaderPack: return 6552; case ModPlatform::ResourceType::Modpack: return 4471; case ModPlatform::ResourceType::DataPack: return 6945; } } static int getMappedModLoader(ModPlatform::ModLoaderType loaders) { // https://docs.curseforge.com/?http#tocS_ModLoaderType switch (loaders) { case ModPlatform::Forge: return 1; case ModPlatform::Cauldron: return 2; case ModPlatform::LiteLoader: return 3; case ModPlatform::Fabric: return 4; case ModPlatform::Quilt: return 5; case ModPlatform::NeoForge: return 6; case ModPlatform::DataPack: case ModPlatform::Babric: case ModPlatform::BTA: case ModPlatform::LegacyFabric: case ModPlatform::Ornithe: case ModPlatform::Rift: break; // not supported } return 0; } static const QStringList getModLoaderStrings(const ModPlatform::ModLoaderTypes types) { QStringList l; for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt }) { if (types & loader) { l << QString::number(getMappedModLoader(loader)); } } return l; } static const QString getModLoaderFilters(ModPlatform::ModLoaderTypes types) { return "[" + getModLoaderStrings(types).join(',') + "]"; } public: std::optional getSearchURL(const SearchArgs& args) const override { QStringList get_arguments; get_arguments.append(QString("classId=%1").arg(getClassId(args.type))); get_arguments.append(QString("index=%1").arg(args.offset)); get_arguments.append("pageSize=25"); if (args.search.has_value()) get_arguments.append(QString("searchFilter=%1").arg(args.search.value())); if (args.sorting.has_value()) get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index)); get_arguments.append("sortOrder=desc"); if (args.loaders.has_value()) { ModPlatform::ModLoaderTypes loaders = args.loaders.value(); loaders &= ~static_cast(ModPlatform::ModLoaderType::DataPack); if (loaders != 0) get_arguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(loaders))); } if (args.categoryIds.has_value() && !args.categoryIds->empty()) get_arguments.append(QString("categoryIds=[%1]").arg(args.categoryIds->join(","))); if (args.versions.has_value() && !args.versions.value().empty()) get_arguments.append(QString("gameVersion=%1").arg(args.versions.value().front().toString())); return BuildConfig.FLAME_BASE_URL + "/mods/search?gameId=432&" + get_arguments.join('&'); } std::optional getVersionsURL(const VersionSearchArgs& args) const override { auto addonId = args.pack->addonId.toString(); QString url = QString(BuildConfig.FLAME_BASE_URL + "/mods/%1/files?pageSize=10000").arg(addonId); if (args.mcVersions.has_value()) url += QString("&gameVersion=%1").arg(args.mcVersions.value().front().toString()); if (args.loaders.has_value() && args.loaders.value() != ModPlatform::ModLoaderType::DataPack && ModPlatform::hasSingleModLoaderSelected(args.loaders.value())) { int mappedModLoader = getMappedModLoader(static_cast(static_cast(args.loaders.value()))); url += QString("&modLoaderType=%1").arg(mappedModLoader); } return url; } QJsonArray documentToArray(QJsonDocument& obj) const override { return obj.object()["data"].toArray(); } void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) const override { FlameMod::loadIndexedPack(m, obj); } ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, ModPlatform::ResourceType resourceType) const override { auto arr = FlameMod::loadIndexedPackVersion(obj); if (resourceType != ModPlatform::ResourceType::TexturePack) { return arr; } // FIXME: Client-side version filtering. This won't take into account any user-selected filtering. const auto& mc_versions = arr.mcVersion; if (std::any_of(mc_versions.constBegin(), mc_versions.constEnd(), [](const auto& mc_version) { return Version(mc_version) <= Version("1.6"); })) { return arr; } return {}; }; void loadExtraPackInfo(ModPlatform::IndexedPack& m, [[maybe_unused]] QJsonObject&) const override { FlameMod::loadBody(m); } private: std::optional getInfoURL(const QString& id) const override { return QString(BuildConfig.FLAME_BASE_URL + "/mods/%1").arg(id); } std::optional getDependencyURL(const DependencySearchArgs& args) const override { auto addonId = args.dependency.addonId.toString(); auto url = QString(BuildConfig.FLAME_BASE_URL + "/mods/%1/files?pageSize=10000&gameVersion=%2").arg(addonId, args.mcVersion.toString()); if (args.loader && ModPlatform::hasSingleModLoaderSelected(args.loader)) { int mappedModLoader = getMappedModLoader(static_cast(static_cast(args.loader))); url += QString("&modLoaderType=%1").arg(mappedModLoader); } return url; } }; PrismLauncher-11.0.3/launcher/modplatform/flame/FlameCheckUpdate.cpp0000644000175100017510000001761215224505336025034 0ustar runnerrunner#include "FlameCheckUpdate.h" #include "Application.h" #include "FlameAPI.h" #include "FlameModIndex.h" #include #include #include "Json.h" #include "QObjectPtr.h" #include "ResourceDownloadTask.h" #include "minecraft/mod/tasks/GetModDependenciesTask.h" #include "modplatform/ModIndex.h" #include "net/ApiDownload.h" #include "net/NetJob.h" #include "tasks/Task.h" bool FlameCheckUpdate::abort() { bool result = false; if (m_task && m_task->canAbort()) { result = m_task->abort(); } Task::abort(); return result; } /* Check for update: * - Get latest version available * - Compare hash of the latest version with the current hash * - If equal, no updates, else, there's updates, so add to the list * */ void FlameCheckUpdate::executeTask() { setStatus(tr("Preparing resources for CurseForge...")); auto* netJob = new NetJob("Get latest versions", APPLICATION->network()); connect(netJob, &Task::finished, this, &FlameCheckUpdate::collectBlockedMods); connect(netJob, &Task::progress, this, &FlameCheckUpdate::setProgress); connect(netJob, &Task::stepProgress, this, &FlameCheckUpdate::propagateStepProgress); connect(netJob, &Task::details, this, &FlameCheckUpdate::setDetails); for (auto* resource : m_resources) { auto project = std::make_shared(); project->addonId = resource->metadata()->project_id.toString(); auto versionsUrlOptional = FlameAPI().getVersionsURL({ .pack = project, .mcVersions = m_gameVersions }); if (!versionsUrlOptional.has_value()) { continue; } auto [task, response] = Net::ApiDownload::makeByteArray(versionsUrlOptional.value()); connect(task.get(), &Task::succeeded, this, [this, resource, response] { getLatestVersionCallback(resource, response); }); netJob->addNetAction(task); } m_task.reset(netJob); m_task->start(); } void FlameCheckUpdate::getLatestVersionCallback(Resource* resource, QByteArray* response) { QJsonParseError parseError{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError); if (parseError.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from latest mod version at" << parseError.offset << "reason:" << parseError.errorString(); qWarning() << *response; return; } // Fake pack with the necessary info to pass to the download task :) auto pack = std::make_shared(); pack->name = resource->name(); pack->slug = resource->metadata()->slug; pack->addonId = resource->metadata()->project_id; pack->provider = ModPlatform::ResourceProvider::FLAME; try { auto obj = Json::requireObject(doc); auto arr = Json::requireArray(obj, "data"); FlameMod::loadIndexedPackVersions(*pack.get(), arr); } catch (Json::JsonException& e) { qCritical() << "Failed to parse response from a version request."; qCritical() << e.what(); qDebug() << doc; } auto latestVer = FlameAPI().getLatestVersion(pack->versions, m_loadersList, resource->metadata()->loaders, !m_loadersList.isEmpty()); setStatus(tr("Parsing the API response from CurseForge for '%1'...").arg(resource->name())); if (!latestVer.has_value() || !latestVer->addonId.isValid()) { QString reason; if (dynamic_cast(resource) != nullptr) { reason = tr("No valid version found for this resource. It's probably unavailable for the current game " "version / mod loader."); } else { reason = tr("No valid version found for this resource. It's probably unavailable for the current game version."); } emit checkFailed(resource, reason); return; } if (latestVer->downloadUrl.isEmpty() && latestVer->fileId != resource->metadata()->file_id) { m_blocked[resource] = latestVer->fileId.toString(); return; } if (!latestVer->hash.isEmpty() && (resource->metadata()->hash != latestVer->hash || resource->status() == ResourceStatus::NOT_INSTALLED)) { auto oldVersion = resource->metadata()->version_number; if (oldVersion.isEmpty()) { if (resource->status() == ResourceStatus::NOT_INSTALLED) { oldVersion = tr("Not installed"); } else { oldVersion = tr("Unknown"); } } auto downloadTask = makeShared(pack, latestVer.value(), m_resourceModel, true, "update"); m_updates.emplace_back(pack->name, resource->metadata()->hash, oldVersion, latestVer->version, latestVer->version_type, FlameAPI().getModFileChangelog(latestVer->addonId.toInt(), latestVer->fileId.toInt()), ModPlatform::ResourceProvider::FLAME, downloadTask, resource->enabled()); } m_deps.append(std::make_shared(pack, latestVer.value())); } void FlameCheckUpdate::collectBlockedMods() { QStringList addonIds; QHash quickSearch; for (const auto& resource : m_blocked.keys()) { auto addonId = resource->metadata()->project_id.toString(); addonIds.append(addonId); quickSearch[addonId] = resource; } Task::Ptr projTask; QByteArray* response = nullptr; if (addonIds.isEmpty()) { emitSucceeded(); return; } if (addonIds.size() == 1) { std::tie(projTask, response) = FlameAPI().getProject(*addonIds.begin()); } else { std::tie(projTask, response) = FlameAPI().getProjects(addonIds); } connect(projTask.get(), &Task::succeeded, this, [this, response, addonIds, quickSearch] { QJsonParseError parseError{}; auto doc = QJsonDocument::fromJson(*response, &parseError); if (parseError.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from Flame projects task at" << parseError.offset << "reason:" << parseError.errorString(); qWarning() << *response; return; } try { QJsonArray entries; if (addonIds.size() == 1) { entries = { Json::requireObject(Json::requireObject(doc), "data") }; } else { entries = Json::requireArray(Json::requireObject(doc), "data"); } for (auto entry : entries) { auto entryObj = Json::requireObject(entry); auto id = QString::number(Json::requireInteger(entryObj, "id")); auto* resource = quickSearch.find(id).value(); ModPlatform::IndexedPack pack; try { setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(resource->name())); FlameMod::loadIndexedPack(pack, entryObj); auto recoverUrl = QString("%1/download/%2").arg(pack.websiteUrl, m_blocked[resource]); emit checkFailed(resource, tr("Resource has a new update available, but is not downloadable using CurseForge."), recoverUrl); } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << entries; } } } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << doc; } }); connect(projTask.get(), &Task::finished, this, &FlameCheckUpdate::emitSucceeded); // do not care much about error connect(projTask.get(), &Task::progress, this, &FlameCheckUpdate::setProgress); connect(projTask.get(), &Task::stepProgress, this, &FlameCheckUpdate::propagateStepProgress); connect(projTask.get(), &Task::details, this, &FlameCheckUpdate::setDetails); m_task.reset(projTask); m_task->start(); } PrismLauncher-11.0.3/launcher/modplatform/flame/PackManifest.cpp0000644000175100017510000000456615224505336024260 0ustar runnerrunner#include "PackManifest.h" #include "Json.h" static void loadFileV1(Flame::File& f, QJsonObject& file) { f.projectId = Json::requireInteger(file, "projectID"); f.fileId = Json::requireInteger(file, "fileID"); f.required = file["required"].toBool(true); } static void loadModloaderV1(Flame::Modloader& m, QJsonObject& modLoader) { m.id = Json::requireString(modLoader, "id"); m.primary = modLoader["primary"].toBool(); } static void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft) { m.version = Json::requireString(minecraft, "version"); // extra libraries... apparently only used for a custom Minecraft launcher in the 1.2.5 FTB retro pack // intended use is likely hardcoded in the 'Flame' client, the manifest says nothing m.libraries = minecraft["libraries"].toString(); auto arr = minecraft["modLoaders"].toArray(); for (QJsonValueRef item : arr) { auto obj = Json::requireObject(item); Flame::Modloader loader; loadModloaderV1(loader, obj); m.modLoaders.append(loader); } m.recommendedRAM = minecraft["recommendedRam"].toInt(); } static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest) { auto mc = Json::requireObject(manifest, "minecraft"); loadMinecraftV1(pack.minecraft, mc); pack.name = manifest["name"].toString("Unnamed"); pack.version = manifest["version"].toString(); pack.author = manifest["author"].toString("Anonymous"); auto arr = manifest["files"].toArray(); for (auto item : arr) { auto obj = Json::requireObject(item); Flame::File file; loadFileV1(file, obj); Q_ASSERT(file.projectId != 0); pack.files.insert(file.fileId, file); } pack.overrides = manifest["overrides"].toString("overrides"); pack.is_loaded = true; } void Flame::loadManifest(Flame::Manifest& m, const QString& filepath) { auto doc = Json::requireDocument(filepath); auto obj = Json::requireObject(doc); m.manifestType = Json::requireString(obj, "manifestType"); if (m.manifestType != "minecraftModpack") { throw JSONValidationError("Not a modpack manifest!"); } m.manifestVersion = Json::requireInteger(obj, "manifestVersion"); if (m.manifestVersion != 1) { throw JSONValidationError(QString("Unknown manifest version (%1)").arg(m.manifestVersion)); } loadManifestV1(m, obj); } PrismLauncher-11.0.3/launcher/modplatform/flame/FlameCheckUpdate.h0000644000175100017510000000140315224505336024470 0ustar runnerrunner#pragma once #include "modplatform/CheckUpdateTask.h" class FlameCheckUpdate : public CheckUpdateTask { Q_OBJECT public: FlameCheckUpdate(QList& resources, std::vector& mcVersions, QList loadersList, ResourceFolderModel* resourceModel) : CheckUpdateTask(resources, mcVersions, std::move(loadersList), resourceModel) {} public slots: bool abort() override; protected slots: void executeTask() override; private slots: void getLatestVersionCallback(Resource* resource, QByteArray* response); void collectBlockedMods(); private: Task::Ptr m_task = nullptr; QHash m_blocked; }; PrismLauncher-11.0.3/launcher/modplatform/flame/FlameModIndex.cpp0000644000175100017510000001656115224505336024365 0ustar runnerrunner#include "FlameModIndex.h" #include "FileSystem.h" #include "Json.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "modplatform/ModIndex.h" #include "modplatform/flame/FlameAPI.h" static FlameAPI api; void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { pack.addonId = Json::requireInteger(obj, "id"); pack.provider = ModPlatform::ResourceProvider::FLAME; pack.name = Json::requireString(obj, "name"); pack.slug = Json::requireString(obj, "slug"); pack.websiteUrl = obj["links"].toObject()["websiteUrl"].toString(""); pack.description = obj["summary"].toString(""); QJsonObject logo = obj["logo"].toObject(); pack.logoName = logo["title"].toString(); pack.logoUrl = logo["thumbnailUrl"].toString(); if (pack.logoUrl.isEmpty()) { pack.logoUrl = logo["url"].toString(); } auto authors = obj["authors"].toArray(); if (!authors.isEmpty()) { pack.authors.clear(); for (auto authorIter : authors) { auto author = Json::requireObject(authorIter); ModPlatform::ModpackAuthor packAuthor; packAuthor.name = Json::requireString(author, "name"); packAuthor.url = Json::requireString(author, "url"); pack.authors.append(packAuthor); } } pack.extraDataLoaded = false; loadURLs(pack, obj); } void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj) { auto links_obj = obj["links"].toObject(); pack.extraData.issuesUrl = links_obj["issuesUrl"].toString(); if (pack.extraData.issuesUrl.endsWith('/')) pack.extraData.issuesUrl.chop(1); pack.extraData.sourceUrl = links_obj["sourceUrl"].toString(); if (pack.extraData.sourceUrl.endsWith('/')) pack.extraData.sourceUrl.chop(1); pack.extraData.wikiUrl = links_obj["wikiUrl"].toString(); if (pack.extraData.wikiUrl.endsWith('/')) pack.extraData.wikiUrl.chop(1); if (!pack.extraData.body.isEmpty()) pack.extraDataLoaded = true; } void FlameMod::loadBody(ModPlatform::IndexedPack& pack) { pack.extraData.body = api.getModDescription(pack.addonId.toInt()); if (!pack.extraData.issuesUrl.isEmpty() || !pack.extraData.sourceUrl.isEmpty() || !pack.extraData.wikiUrl.isEmpty()) pack.extraDataLoaded = true; } static QString enumToString(int hash_algorithm) { switch (hash_algorithm) { default: case 1: return "sha1"; case 2: return "md5"; } } void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr) { QList unsortedVersions; for (auto versionIter : arr) { auto obj = versionIter.toObject(); auto file = loadIndexedPackVersion(obj); if (!file.addonId.isValid()) file.addonId = pack.addonId; if (file.fileId.isValid()) // Heuristic to check if the returned value is valid unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { // dates are in RFC 3339 format return a.date > b.date; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); pack.versions = unsortedVersions; pack.versionsLoaded = true; } auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> ModPlatform::IndexedVersion { auto versionArray = Json::requireArray(obj, "gameVersions"); ModPlatform::IndexedVersion file; for (auto mcVer : versionArray) { auto str = mcVer.toString(); if (str.contains('.')) file.mcVersion.append(str); file.side = ModPlatform::Side::NoSide; if (auto loader = str.toLower(); loader == "neoforge") file.loaders |= ModPlatform::NeoForge; else if (loader == "forge") file.loaders |= ModPlatform::Forge; else if (loader == "cauldron") file.loaders |= ModPlatform::Cauldron; else if (loader == "liteloader") file.loaders |= ModPlatform::LiteLoader; else if (loader == "fabric") file.loaders |= ModPlatform::Fabric; else if (loader == "quilt") file.loaders |= ModPlatform::Quilt; else if (loader == "server" || loader == "client") { if (file.side == ModPlatform::Side::NoSide) file.side = ModPlatform::SideUtils::fromString(loader); else if (file.side != ModPlatform::SideUtils::fromString(loader)) file.side = ModPlatform::Side::UniversalSide; } } file.addonId = Json::requireInteger(obj, "modId"); file.fileId = Json::requireInteger(obj, "id"); file.date = Json::requireString(obj, "fileDate"); file.version = Json::requireString(obj, "displayName"); file.downloadUrl = obj["downloadUrl"].toString(); file.fileName = Json::requireString(obj, "fileName"); file.fileName = FS::RemoveInvalidPathChars(file.fileName); ModPlatform::IndexedVersionType ver_type; switch (Json::requireInteger(obj, "releaseType")) { case 1: ver_type = ModPlatform::IndexedVersionType::Release; break; case 2: ver_type = ModPlatform::IndexedVersionType::Beta; break; case 3: ver_type = ModPlatform::IndexedVersionType::Alpha; break; default: ver_type = ModPlatform::IndexedVersionType::Unknown; break; } file.version_type = ver_type; auto hash_list = obj["hashes"].toArray(); for (auto h : hash_list) { auto hash_entry = h.toObject(); auto hash_types = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::FLAME); auto hash_algo = enumToString(hash_entry["algo"].toInt(1)); if (hash_types.contains(hash_algo)) { file.hash = Json::requireString(hash_entry, "value"); file.hash_type = hash_algo; break; } } auto dependencies = obj["dependencies"].toArray(); for (auto d : dependencies) { auto dep = d.toObject(); ModPlatform::Dependency dependency; dependency.addonId = Json::requireInteger(dep, "modId"); switch (Json::requireInteger(dep, "relationType")) { case 1: // EmbeddedLibrary dependency.type = ModPlatform::DependencyType::EMBEDDED; break; case 2: // OptionalDependency dependency.type = ModPlatform::DependencyType::OPTIONAL; break; case 3: // RequiredDependency dependency.type = ModPlatform::DependencyType::REQUIRED; break; case 4: // Tool dependency.type = ModPlatform::DependencyType::TOOL; break; case 5: // Incompatible dependency.type = ModPlatform::DependencyType::INCOMPATIBLE; break; case 6: // Include dependency.type = ModPlatform::DependencyType::INCLUDE; break; default: dependency.type = ModPlatform::DependencyType::UNKNOWN; break; } file.dependencies.append(dependency); } if (load_changelog) file.changelog = api.getModFileChangelog(file.addonId.toInt(), file.fileId.toInt()); return file; } PrismLauncher-11.0.3/launcher/modplatform/flame/FlamePackExportTask.cpp0000644000175100017510000004050215224505336025551 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 TheKodeToad * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "FlamePackExportTask.h" #include #include #include #include #include #include #include #include #include #include "Application.h" #include "Json.h" #include "minecraft/PackProfile.h" #include "minecraft/mod/ModFolderModel.h" #include "modplatform/ModIndex.h" #include "modplatform/flame/FlameModIndex.h" #include "modplatform/helpers/HashUtils.h" #include "tasks/Task.h" #include "archive/ExportToZipTask.h" const QString FlamePackExportTask::TEMPLATE = "
  • {name}{authors}
  • \n"; const QStringList FlamePackExportTask::FILE_EXTENSIONS({ "jar", "zip" }); FlamePackExportTask::FlamePackExportTask(FlamePackExportOptions&& options) : m_options(std::move(options)), m_gameRoot(m_options.instance->gameRoot()) {} void FlamePackExportTask::executeTask() { setStatus(tr("Searching for files...")); setProgress(0, 5); collectFiles(); } bool FlamePackExportTask::abort() { if (task) { task->abort(); return true; } return false; } void FlamePackExportTask::collectFiles() { setAbortable(false); QCoreApplication::processEvents(); m_files.clear(); if (!MMCZip::collectFileListRecursively(m_options.instance->gameRoot(), nullptr, &m_files, m_options.filter)) { emitFailed(tr("Could not search for files")); return; } pendingHashes.clear(); resolvedFiles.clear(); m_options.instance->loaderModList()->update(); connect(m_options.instance->loaderModList(), &ModFolderModel::updateFinished, this, &FlamePackExportTask::collectHashes); } void FlamePackExportTask::collectHashes() { setAbortable(true); setStatus(tr("Finding file hashes...")); setProgress(1, 5); auto allMods = m_options.instance->loaderModList()->allMods(); ConcurrentTask::Ptr hashingTask(new ConcurrentTask("MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())); task.reset(hashingTask); for (const QFileInfo& file : m_files) { const QString relative = m_gameRoot.relativeFilePath(file.absoluteFilePath()); // require sensible file types if (!std::any_of(FILE_EXTENSIONS.begin(), FILE_EXTENSIONS.end(), [&relative](const QString& extension) { return relative.endsWith('.' + extension) || relative.endsWith('.' + extension + ".disabled"); })) continue; if (relative.startsWith("resourcepacks/") && (relative.endsWith(".zip") || relative.endsWith(".zip.disabled"))) { // is resourcepack auto hashTask = Hashing::createHasher(file.absoluteFilePath(), ModPlatform::ResourceProvider::FLAME); connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, relative, file](QString hash) { if (m_state == Task::State::Running) { pendingHashes.insert(hash, { relative, file.absoluteFilePath(), relative.endsWith(".zip") }); } }); connect(hashTask.get(), &Task::failed, this, &FlamePackExportTask::emitFailed); hashingTask->addTask(hashTask); continue; } if (auto modIter = std::find_if(allMods.begin(), allMods.end(), [&file](Mod* mod) { return mod->fileinfo() == file; }); modIter != allMods.end()) { const Mod* mod = *modIter; if (!mod || mod->type() == ResourceType::FOLDER) { continue; } if (mod->metadata() && mod->metadata()->provider == ModPlatform::ResourceProvider::FLAME) { resolvedFiles.insert(mod->fileinfo().absoluteFilePath(), { mod->metadata()->project_id.toInt(), mod->metadata()->file_id.toInt(), mod->enabled(), true, mod->metadata()->name, mod->metadata()->slug, mod->authors().join(", ") }); continue; } auto hashTask = Hashing::createHasher(mod->fileinfo().absoluteFilePath(), ModPlatform::ResourceProvider::FLAME); connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) { if (m_state == Task::State::Running) { pendingHashes.insert(hash, { mod->name(), mod->fileinfo().absoluteFilePath(), mod->enabled(), true }); } }); connect(hashTask.get(), &Task::failed, this, &FlamePackExportTask::emitFailed); hashingTask->addTask(hashTask); } } auto progressStep = std::make_shared(); connect(hashingTask.get(), &Task::finished, this, [this, progressStep] { progressStep->state = TaskStepState::Succeeded; stepProgress(*progressStep); }); connect(hashingTask.get(), &Task::succeeded, this, &FlamePackExportTask::makeApiRequest); connect(hashingTask.get(), &Task::failed, this, [this, progressStep](QString reason) { progressStep->state = TaskStepState::Failed; stepProgress(*progressStep); emitFailed(reason); }); connect(hashingTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propagateStepProgress); connect(hashingTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { progressStep->update(current, total); stepProgress(*progressStep); }); connect(hashingTask.get(), &Task::status, this, [this, progressStep](QString status) { progressStep->status = status; stepProgress(*progressStep); }); connect(hashingTask.get(), &Task::aborted, this, &FlamePackExportTask::emitAborted); hashingTask->start(); } void FlamePackExportTask::makeApiRequest() { if (pendingHashes.isEmpty()) { buildZip(); return; } setStatus(tr("Finding versions for hashes...")); setProgress(2, 5); QList fingerprints; for (auto& murmur : pendingHashes.keys()) { fingerprints.push_back(murmur.toUInt()); } auto [matchTask, response] = api.matchFingerprints(fingerprints); task = matchTask; connect(task.get(), &Task::succeeded, this, [this, response] { QJsonParseError parseError{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError); if (parseError.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from CurseForge::CurrentVersions at" << parseError.offset << "reason:" << parseError.errorString(); qWarning() << *response; emitFailed(parseError.errorString()); return; } try { auto docObj = Json::requireObject(doc); auto dataObj = Json::requireObject(docObj, "data"); auto dataArr = Json::requireArray(dataObj, "exactMatches"); if (dataArr.isEmpty()) { qWarning() << "No matches found for fingerprint search!"; getProjectsInfo(); return; } for (auto match : dataArr) { auto matchObj = match.toObject(); auto fileObj = matchObj["file"].toObject(); if (matchObj.isEmpty() || fileObj.isEmpty()) { qWarning() << "Fingerprint match is empty!"; return; } auto fingerprint = QString::number(fileObj["fileFingerprint"].toInteger()); auto mod = pendingHashes.find(fingerprint); if (mod == pendingHashes.end()) { qWarning() << "Invalid fingerprint from the API response."; continue; } setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod->name)); if (fileObj["isAvailable"].toBool()) resolvedFiles.insert(mod->path, { Json::requireInteger(fileObj, "modId"), Json::requireInteger(fileObj, "id"), mod->enabled, mod->isMod }); } } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << doc; } pendingHashes.clear(); getProjectsInfo(); }); connect(task.get(), &Task::failed, this, &FlamePackExportTask::getProjectsInfo); connect(task.get(), &Task::aborted, this, &FlamePackExportTask::emitAborted); task->start(); } void FlamePackExportTask::getProjectsInfo() { setStatus(tr("Finding project info from CurseForge...")); setProgress(3, 5); QStringList addonIds; for (const auto& resolved : resolvedFiles) { if (resolved.slug.isEmpty()) { addonIds << QString::number(resolved.addonId); } } Task::Ptr projTask; QByteArray* response; if (addonIds.isEmpty()) { buildZip(); return; } else if (addonIds.size() == 1) { std::tie(projTask, response) = api.getProject(*addonIds.begin()); } else { std::tie(projTask, response) = api.getProjects(addonIds); } connect(projTask.get(), &Task::succeeded, this, [this, response, addonIds] { QJsonParseError parseError{}; auto doc = QJsonDocument::fromJson(*response, &parseError); if (parseError.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from CurseForge projects task at" << parseError.offset << "reason:" << parseError.errorString(); qWarning() << *response; emitFailed(parseError.errorString()); return; } try { QJsonArray entries; if (addonIds.size() == 1) entries = { Json::requireObject(Json::requireObject(doc), "data") }; else entries = Json::requireArray(Json::requireObject(doc), "data"); for (auto entry : entries) { auto entryObj = Json::requireObject(entry); try { setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(Json::requireString(entryObj, "name"))); ModPlatform::IndexedPack pack; FlameMod::loadIndexedPack(pack, entryObj); for (auto key : resolvedFiles.keys()) { auto val = resolvedFiles.value(key); if (val.addonId == pack.addonId) { val.name = pack.name; val.slug = pack.slug; QStringList authors; for (auto author : pack.authors) authors << author.name; val.authors = authors.join(", "); resolvedFiles[key] = val; } } } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << entries; } } } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << doc; } buildZip(); }); connect(projTask.get(), &Task::failed, this, &FlamePackExportTask::emitFailed); connect(projTask.get(), &Task::aborted, this, &FlamePackExportTask::emitAborted); task.reset(projTask); task->start(); } void FlamePackExportTask::buildZip() { setStatus(tr("Adding files...")); setProgress(4, 5); auto zipTask = makeShared(m_options.output, m_gameRoot, m_files, "overrides/", true); zipTask->addExtraFile("manifest.json", generateIndex()); zipTask->addExtraFile("modlist.html", generateHTML()); QStringList exclude; std::transform(resolvedFiles.keyBegin(), resolvedFiles.keyEnd(), std::back_insert_iterator(exclude), [this](QString file) { return m_gameRoot.relativeFilePath(file); }); zipTask->setExcludeFiles(exclude); auto progressStep = std::make_shared(); connect(zipTask.get(), &Task::finished, this, [this, progressStep] { progressStep->state = TaskStepState::Succeeded; stepProgress(*progressStep); }); connect(zipTask.get(), &Task::succeeded, this, &FlamePackExportTask::emitSucceeded); connect(zipTask.get(), &Task::aborted, this, &FlamePackExportTask::emitAborted); connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { progressStep->state = TaskStepState::Failed; stepProgress(*progressStep); emitFailed(reason); }); connect(zipTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propagateStepProgress); connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { progressStep->update(current, total); stepProgress(*progressStep); }); connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { progressStep->status = status; stepProgress(*progressStep); }); task.reset(zipTask); zipTask->start(); } QByteArray FlamePackExportTask::generateIndex() { QJsonObject obj; obj["manifestType"] = "minecraftModpack"; obj["manifestVersion"] = 1; obj["name"] = m_options.name; obj["version"] = m_options.version; obj["author"] = m_options.author; obj["overrides"] = "overrides"; QJsonObject version; auto profile = m_options.instance->getPackProfile(); // collect all supported components const ComponentPtr minecraft = profile->getComponent("net.minecraft"); const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader"); const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader"); const ComponentPtr forge = profile->getComponent("net.minecraftforge"); const ComponentPtr neoforge = profile->getComponent("net.neoforged"); // convert all available components to mrpack dependencies if (minecraft != nullptr) version["version"] = minecraft->m_version; QString id; if (quilt != nullptr) id = "quilt-" + quilt->m_version; else if (fabric != nullptr) id = "fabric-" + fabric->m_version; else if (forge != nullptr) id = "forge-" + forge->m_version; else if (neoforge != nullptr) { id = "neoforge-"; if (minecraft->m_version == "1.20.1") id += "1.20.1-"; id += neoforge->m_version; } version["modLoaders"] = QJsonArray(); if (!id.isEmpty()) { QJsonObject loader; loader["id"] = id; loader["primary"] = true; version["modLoaders"] = QJsonArray({ loader }); } if (m_options.recommendedRAM > 0) version["recommendedRam"] = m_options.recommendedRAM; obj["minecraft"] = version; QJsonArray files; for (auto mod : resolvedFiles) { QJsonObject file; file["projectID"] = mod.addonId; file["fileID"] = mod.version; file["required"] = mod.enabled || !m_options.optionalFiles; files << file; } obj["files"] = files; return QJsonDocument(obj).toJson(QJsonDocument::Compact); } QByteArray FlamePackExportTask::generateHTML() { QString content = ""; for (auto mod : resolvedFiles) { if (mod.isMod) { content += QString(TEMPLATE) .replace("{name}", mod.name.toHtmlEscaped()) .replace("{url}", ModPlatform::getMetaURL(ModPlatform::ResourceProvider::FLAME, mod.addonId).toHtmlEscaped()) .replace("{authors}", !mod.authors.isEmpty() ? QString(" (by %1)").arg(mod.authors).toHtmlEscaped() : ""); } } content = "
      " + content + "
    "; return content.toUtf8(); } PrismLauncher-11.0.3/launcher/modplatform/legacy_ftb/0000755000175100017510000000000015224505336022207 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/modplatform/legacy_ftb/PrivatePackManager.h0000644000175100017510000000127615224505336026072 0ustar runnerrunner#pragma once #include #include #include namespace LegacyFTB { class PrivatePackManager { public: ~PrivatePackManager() { save(); } void load(); void save() const; bool empty() const { return currentPacks.empty(); } const QSet& getCurrentPackCodes() const { return currentPacks; } void add(const QString& code) { currentPacks.insert(code); dirty = true; } void remove(const QString& code) { currentPacks.remove(code); dirty = true; } private: QSet currentPacks; QString m_filename = "private_packs.txt"; mutable bool dirty = false; }; } // namespace LegacyFTB PrismLauncher-11.0.3/launcher/modplatform/legacy_ftb/PackHelpers.h0000644000175100017510000000134315224505336024562 0ustar runnerrunner#pragma once #include #include #include #include namespace LegacyFTB { // Header for structs etc... enum class PackType { Public, ThirdParty, Private }; struct Modpack { QString name; QString description; QString author; QStringList oldVersions; QString currentVersion; QString mcVersion; QString mods; QString logo; // Technical data QString dir; QString file; //<- Url in the xml, but doesn't make much sense bool bugged = false; bool broken = false; PackType type; QString packCode; }; using ModpackList = QList; } // namespace LegacyFTB // We need it for the proxy model Q_DECLARE_METATYPE(LegacyFTB::Modpack) PrismLauncher-11.0.3/launcher/modplatform/legacy_ftb/PrivatePackManager.cpp0000644000175100017510000000441615224505336026424 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "PrivatePackManager.h" #include #include "FileSystem.h" namespace LegacyFTB { void PrivatePackManager::load() { try { auto foo = QString::fromUtf8(FS::read(m_filename)).split('\n', Qt::SkipEmptyParts); currentPacks = QSet(foo.begin(), foo.end()); dirty = false; } catch (...) { currentPacks = {}; qWarning() << "Failed to read third party FTB pack codes from" << m_filename; } } void PrivatePackManager::save() const { if (!dirty) { return; } try { QStringList list = currentPacks.values(); FS::write(m_filename, list.join('\n').toUtf8()); dirty = false; } catch (...) { qWarning() << "Failed to write third party FTB pack codes to" << m_filename; } } } // namespace LegacyFTB PrismLauncher-11.0.3/launcher/modplatform/legacy_ftb/PackInstallTask.cpp0000644000175100017510000001660515224505336025753 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "PackInstallTask.h" #include #include "BaseInstance.h" #include "FileSystem.h" #include "MMCZip.h" #include "minecraft/GradleSpecifier.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "settings/INISettingsObject.h" #include "Application.h" #include "BuildConfig.h" #include "net/ApiDownload.h" namespace LegacyFTB { PackInstallTask::PackInstallTask(QNetworkAccessManager* network, const Modpack& pack, QString version) { m_pack = pack; m_version = version; m_network = network; } void PackInstallTask::executeTask() { downloadPack(); } void PackInstallTask::downloadPack() { setStatus(tr("Downloading zip for %1").arg(m_pack.name)); setProgress(1, 4); setAbortable(false); auto path = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file); auto entry = APPLICATION->metacache()->resolveEntry("FTBPacks", path); entry->setStale(true); archivePath = entry->getFullPath(); netJobContainer.reset(new NetJob("Download FTB Pack", m_network)); QString url; if (m_pack.type == PackType::Private) { url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(path); } else { url = QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(path); } netJobContainer->addNetAction(Net::ApiDownload::makeCached(url, entry)); connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip); connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed); connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted); netJobContainer->start(); setAbortable(true); progress(1, 4); } void PackInstallTask::unzip() { setStatus(tr("Extracting modpack")); setAbortable(false); progress(2, 4); QDir extractDir(m_stagingPath); m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload::of(MMCZip::extractDir), archivePath, extractDir.absolutePath() + "/unzip"); connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, &PackInstallTask::onUnzipFinished); connect(&m_extractFutureWatcher, &QFutureWatcher::canceled, this, &PackInstallTask::onUnzipCanceled); m_extractFutureWatcher.setFuture(m_extractFuture); } void PackInstallTask::onUnzipFinished() { install(); } void PackInstallTask::onUnzipCanceled() { emitAborted(); } void PackInstallTask::install() { setStatus(tr("Installing modpack")); progress(3, 4); QDir unzipMcDir(m_stagingPath + "/unzip/minecraft"); if (unzipMcDir.exists()) { // ok, found minecraft dir, move contents to instance dir if (!FS::move(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/minecraft")) { emitFailed(tr("Failed to move unpacked Minecraft!")); return; } } QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); MinecraftInstance instance(m_globalSettings, std::make_unique(instanceConfigPath), m_stagingPath); { SettingsObject::Lock lock(instance.settings()); auto components = instance.getPackProfile(); components->buildingFromScratch(); components->setComponentVersion("net.minecraft", m_pack.mcVersion, true); bool fallback = true; // handle different versions QFile packJson(m_stagingPath + "/minecraft/pack.json"); QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods"); if (packJson.exists()) { if (packJson.open(QIODevice::ReadOnly | QIODevice::Text)) { QJsonDocument doc = QJsonDocument::fromJson(packJson.readAll()); packJson.close(); // we only care about the libs QJsonArray libs = doc.object().value("libraries").toArray(); for (const auto& value : libs) { QString nameValue = value.toObject().value("name").toString(); if (!nameValue.startsWith("net.minecraftforge")) { continue; } GradleSpecifier forgeVersion(nameValue); components->setComponentVersion("net.minecraftforge", forgeVersion.version().replace(m_pack.mcVersion, "").replace("-", "")); packJson.remove(); fallback = false; break; } } else { qWarning() << "Failed to open file" << packJson.fileName() << "for reading:" << packJson.errorString(); } } if (jarmodDir.exists()) { qDebug() << "Found jarmods, installing..."; QStringList jarmods; for (auto info : jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) { qDebug() << "Jarmod:" << info.fileName(); jarmods.push_back(info.absoluteFilePath()); } components->installJarMods(jarmods); fallback = false; } // just nuke unzip directory, it s not needed anymore FS::deletePath(m_stagingPath + "/unzip"); if (fallback) { // TODO: Some fallback mechanism... or just keep failing! emitFailed(tr("No installation method found!")); return; } components->saveNow(); progress(4, 4); instance.setName(name()); if (m_instIcon == "default") { m_instIcon = "ftb_logo"; } instance.setIconKey(m_instIcon); } emitSucceeded(); } bool PackInstallTask::abort() { if (!canAbort()) { return false; } netJobContainer->abort(); return InstanceTask::abort(); } } // namespace LegacyFTB PrismLauncher-11.0.3/launcher/modplatform/legacy_ftb/PackInstallTask.h0000644000175100017510000000207715224505336025416 0ustar runnerrunner#pragma once #include "InstanceTask.h" #include "PackHelpers.h" #include "meta/Index.h" #include "meta/Version.h" #include "meta/VersionList.h" #include "net/NetJob.h" #include namespace LegacyFTB { class PackInstallTask : public InstanceTask { Q_OBJECT public: explicit PackInstallTask(QNetworkAccessManager* network, const Modpack& pack, QString version); virtual ~PackInstallTask() {} bool canAbort() const override { return true; } bool abort() override; protected: //! Entry point for tasks. virtual void executeTask() override; private: void downloadPack(); void unzip(); void install(); private slots: void onUnzipFinished(); void onUnzipCanceled(); private: /* data */ QNetworkAccessManager* m_network; bool abortable = false; QFuture> m_extractFuture; QFutureWatcher> m_extractFutureWatcher; NetJob::Ptr netJobContainer; QString archivePath; Modpack m_pack; QString m_version; }; } // namespace LegacyFTB PrismLauncher-11.0.3/launcher/modplatform/legacy_ftb/PackFetchTask.h0000644000175100017510000000215415224505336025035 0ustar runnerrunner#pragma once #include #include #include #include "PackHelpers.h" #include "net/NetJob.h" namespace LegacyFTB { class PackFetchTask : public QObject { Q_OBJECT public: PackFetchTask(QNetworkAccessManager* network) : QObject(nullptr), m_network(network) {}; virtual ~PackFetchTask() = default; void fetch(); void fetchPrivate(const QStringList& toFetch); private: QNetworkAccessManager* m_network; NetJob::Ptr jobPtr; bool parseAndAddPacks(QByteArray& data, PackType packType, ModpackList& list); ModpackList publicPacks; ModpackList thirdPartyPacks; protected slots: void fileDownloadFinished(QByteArray* publicResponse, QByteArray* thirdPartyResponse); void fileDownloadFailed(QString reason); void fileDownloadAborted(); signals: void finished(ModpackList publicPacks, ModpackList thirdPartyPacks); void failed(QString reason); void aborted(); void privateFileDownloadFinished(const Modpack& modpack); void privateFileDownloadFailed(QString reason, QString packCode); }; } // namespace LegacyFTB PrismLauncher-11.0.3/launcher/modplatform/legacy_ftb/PackFetchTask.cpp0000644000175100017510000001602515224505336025372 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "PackFetchTask.h" #include "PrivatePackManager.h" #include #include "Application.h" #include "BuildConfig.h" #include "net/ApiDownload.h" namespace LegacyFTB { void PackFetchTask::fetch() { publicPacks.clear(); thirdPartyPacks.clear(); jobPtr.reset(new NetJob("LegacyFTB::ModpackFetch", m_network)); QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml"); qDebug() << "Downloading public version info from" << publicPacksUrl.toString(); auto [publicAction, publicResponse] = Net::ApiDownload::makeByteArray(publicPacksUrl); jobPtr->addNetAction(publicAction); QUrl thirdPartyUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/thirdparty.xml"); qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString(); auto [thirdPartyAction, thirdPartyResponse] = Net::Download::makeByteArray(thirdPartyUrl); jobPtr->addNetAction(thirdPartyAction); connect(jobPtr.get(), &NetJob::succeeded, this, [this, publicResponse, thirdPartyResponse] { fileDownloadFinished(publicResponse, thirdPartyResponse); }); connect(jobPtr.get(), &NetJob::failed, this, &PackFetchTask::fileDownloadFailed); connect(jobPtr.get(), &NetJob::aborted, this, &PackFetchTask::fileDownloadAborted); jobPtr->start(); } void PackFetchTask::fetchPrivate(const QStringList& toFetch) { QString privatePackBaseUrl = BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1.xml"; for (auto& packCode : toFetch) { NetJob* job = new NetJob("Fetching private pack", m_network); auto [action, data] = Net::ApiDownload::makeByteArray(privatePackBaseUrl.arg(packCode)); job->addNetAction(action); job->setAskRetry(false); connect(job, &NetJob::succeeded, this, [this, job, data, packCode] { ModpackList packs; parseAndAddPacks(*data, PackType::Private, packs); for (auto& currentPack : packs) { currentPack.packCode = packCode; emit privateFileDownloadFinished(currentPack); } job->deleteLater(); }); connect(job, &NetJob::failed, this, [this, job, packCode](QString reason) { emit privateFileDownloadFailed(reason, packCode); job->deleteLater(); }); connect(job, &NetJob::aborted, this, [this, job] { job->deleteLater(); emit aborted(); }); job->start(); } } void PackFetchTask::fileDownloadFinished(QByteArray* publicPtr, QByteArray* thirdPartyPtr) { QStringList failedLists; if (!parseAndAddPacks(*publicPtr, PackType::Public, publicPacks)) { failedLists.append(tr("Public Packs")); } if (!parseAndAddPacks(*thirdPartyPtr, PackType::ThirdParty, thirdPartyPacks)) { failedLists.append(tr("Third Party Packs")); } // NOTE(TheKodeToad): we don't want to reset the jobPtr earlier as it may invalidate the responses! jobPtr.reset(); if (failedLists.size() > 0) { emit failed(tr("Failed to download some pack lists: %1").arg(failedLists.join("\n- "))); } else { emit finished(publicPacks, thirdPartyPacks); } } bool PackFetchTask::parseAndAddPacks(QByteArray& data, PackType packType, ModpackList& list) { QDomDocument doc; QString errorMsg = "Unknown error."; int errorLine = -1; int errorCol = -1; if (!doc.setContent(data, false, &errorMsg, &errorLine, &errorCol)) { auto fullErrMsg = QString("Failed to fetch modpack data: %1 %2:%3!").arg(errorMsg).arg(errorLine).arg(errorCol); qWarning() << fullErrMsg; return false; } QDomNodeList nodes = doc.elementsByTagName("modpack"); for (int i = 0; i < nodes.length(); i++) { QDomElement element = nodes.at(i).toElement(); Modpack modpack; modpack.name = element.attribute("name"); modpack.currentVersion = element.attribute("version"); modpack.mcVersion = element.attribute("mcVersion"); modpack.description = element.attribute("description"); modpack.mods = element.attribute("mods"); modpack.logo = element.attribute("logo"); modpack.oldVersions = element.attribute("oldVersions").split(";"); modpack.broken = false; modpack.bugged = false; // remove empty if the xml is bugged for (QString curr : modpack.oldVersions) { if (curr.isNull() || curr.isEmpty()) { modpack.oldVersions.removeAll(curr); modpack.bugged = true; qWarning() << "Removed some empty versions from" << modpack.name; } } if (modpack.oldVersions.size() < 1) { if (!modpack.currentVersion.isNull() && !modpack.currentVersion.isEmpty()) { modpack.oldVersions.append(modpack.currentVersion); qWarning() << "Added current version to oldVersions because oldVersions was empty! (" + modpack.name + ")"; } else { modpack.broken = true; qWarning() << "Broken pack:" << modpack.name << "=> No valid version!"; } } modpack.author = element.attribute("author"); modpack.dir = element.attribute("dir"); modpack.file = element.attribute("url"); modpack.type = packType; list.append(modpack); } return true; } void PackFetchTask::fileDownloadFailed(QString reason) { qWarning() << "Fetching FTBPacks failed:" << reason; emit failed(reason); } void PackFetchTask::fileDownloadAborted() { emit aborted(); } } // namespace LegacyFTB PrismLauncher-11.0.3/launcher/modplatform/ModIndex.cpp0000644000175100017510000001442415224505336022330 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "modplatform/ModIndex.h" #include #include #include namespace ModPlatform { ModLoaderType operator|(ModLoaderType lhs, ModLoaderType rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); } static const QMap s_indexed_version_type_names = { { "release", IndexedVersionType::Release }, { "beta", IndexedVersionType::Beta }, { "alpha", IndexedVersionType::Alpha } }; static const QList loaderList = { NeoForge, Forge, Cauldron, LiteLoader, Quilt, Fabric, Babric, BTA, LegacyFabric, Ornithe, Rift }; QList modLoaderTypesToList(ModLoaderTypes flags) { QList flagList; for (auto flag : loaderList) { if (flags.testFlag(flag)) { flagList.append(flag); } } return flagList; } QString IndexedVersionType::toString() const { return s_indexed_version_type_names.key(m_type, "unknown"); } IndexedVersionType IndexedVersionType::fromString(const QString& type) { return s_indexed_version_type_names.value(type, IndexedVersionType::Unknown); } const char* ProviderCapabilities::name(ResourceProvider p) { switch (p) { case ResourceProvider::MODRINTH: return "modrinth"; case ResourceProvider::FLAME: return "curseforge"; } return {}; } QString ProviderCapabilities::readableName(ResourceProvider p) { switch (p) { case ResourceProvider::MODRINTH: return "Modrinth"; case ResourceProvider::FLAME: return "CurseForge"; } return {}; } QStringList ProviderCapabilities::hashType(ResourceProvider p) { switch (p) { case ResourceProvider::MODRINTH: return { "sha512", "sha1" }; case ResourceProvider::FLAME: // Try newer formats first, fall back to old format return { "sha1", "md5", "murmur2" }; } return {}; } QString getMetaURL(ResourceProvider provider, QVariant projectID) { return ((provider == ModPlatform::ResourceProvider::FLAME) ? "https://www.curseforge.com/projects/" : "https://modrinth.com/mod/") + projectID.toString(); } auto getModLoaderAsString(ModLoaderType type) -> const QString { switch (type) { case NeoForge: return "neoforge"; case Forge: return "forge"; case Cauldron: return "cauldron"; case LiteLoader: return "liteloader"; case Fabric: return "fabric"; case Quilt: return "quilt"; case DataPack: return "datapack"; case Babric: return "babric"; case BTA: return "bta-babric"; case LegacyFabric: return "legacy-fabric"; case Ornithe: return "ornithe"; case Rift: return "rift"; default: break; } return ""; } auto getModLoaderFromString(QString type) -> ModLoaderType { if (type == "neoforge") return NeoForge; if (type == "forge") return Forge; if (type == "cauldron") return Cauldron; if (type == "liteloader") return LiteLoader; if (type == "fabric") return Fabric; if (type == "quilt") return Quilt; if (type == "babric") return Babric; if (type == "bta-babric") return BTA; if (type == "legacy-fabric") return LegacyFabric; if (type == "ornithe") return Ornithe; if (type == "rift") return Rift; return {}; } QString SideUtils::toString(Side side) { switch (side) { case Side::ClientSide: return "client"; case Side::ServerSide: return "server"; case Side::UniversalSide: return "both"; case Side::NoSide: break; } return {}; } Side SideUtils::fromString(QString side) { if (side == "client") return Side::ClientSide; if (side == "server") return Side::ServerSide; if (side == "both") return Side::UniversalSide; return Side::UniversalSide; } QString DependencyTypeUtils::toString(DependencyType type) { switch (type) { case DependencyType::REQUIRED: return "REQUIRED"; case DependencyType::OPTIONAL: return "OPTIONAL"; case DependencyType::INCOMPATIBLE: return "INCOMPATIBLE"; case DependencyType::EMBEDDED: return "EMBEDDED"; case DependencyType::TOOL: return "TOOL"; case DependencyType::INCLUDE: return "INCLUDE"; case DependencyType::UNKNOWN: return "UNKNOWN"; } return "UNKNOWN"; } DependencyType DependencyTypeUtils::fromString(const QString& str) { static const QHash map = { { "REQUIRED", DependencyType::REQUIRED }, { "OPTIONAL", DependencyType::OPTIONAL }, { "INCOMPATIBLE", DependencyType::INCOMPATIBLE }, { "EMBEDDED", DependencyType::EMBEDDED }, { "TOOL", DependencyType::TOOL }, { "INCLUDE", DependencyType::INCLUDE }, { "UNKNOWN", DependencyType::UNKNOWN }, }; return map.value(str.toUpper(), DependencyType::UNKNOWN); } } // namespace ModPlatform PrismLauncher-11.0.3/launcher/modplatform/ResourceAPI.cpp0000644000175100017510000002772415224505336022751 0ustar runnerrunner#include "modplatform/ResourceAPI.h" #include "Application.h" #include "Json.h" #include "net/NetJob.h" #include "modplatform/ModIndex.h" #include "net/ApiDownload.h" Task::Ptr ResourceAPI::searchProjects(SearchArgs&& args, Callback>&& callbacks) const { auto search_url_optional = getSearchURL(args); if (!search_url_optional.has_value()) { callbacks.on_fail("Failed to create search URL", -1); return nullptr; } auto search_url = search_url_optional.value(); auto netJob = makeShared(QString("%1::Search").arg(debugName()), APPLICATION->network()); auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(search_url)); netJob->addNetAction(action); QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from" << debugName() << "at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; callbacks.on_fail(parse_error.errorString(), -1); return; } QList newList; auto packs = documentToArray(doc); for (auto packRaw : packs) { auto packObj = packRaw.toObject(); ModPlatform::IndexedPack::Ptr pack = std::make_shared(); try { loadIndexedPack(*pack, packObj); newList << pack; } catch (const JSONValidationError& e) { qWarning().nospace() << "Error while loading resource from " << debugName() << ": " << e.cause(); continue; } } callbacks.on_succeed(newList); }); // Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues. // This prevents the lambda from extending the lifetime of the shared resource, // as it only temporarily locks the resource when needed. auto weak = netJob.toWeakRef(); QObject::connect(netJob.get(), &NetJob::failed, [weak, callbacks](const QString& reason) { int network_error_code = -1; if (auto netJob = weak.lock()) { if (auto* failed_action = netJob->getFailedActions().at(0); failed_action) network_error_code = failed_action->replyStatusCode(); } callbacks.on_fail(reason, network_error_code); }); QObject::connect(netJob.get(), &NetJob::aborted, [callbacks] { if (callbacks.on_abort != nullptr) callbacks.on_abort(); }); return netJob; } Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback>&& callbacks) const { auto versions_url_optional = getVersionsURL(args); if (!versions_url_optional.has_value()) return nullptr; auto versions_url = versions_url_optional.value(); auto netJob = makeShared(QString("%1::Versions").arg(args.pack->name), APPLICATION->network()); auto [action, response] = Net::ApiDownload::makeByteArray(versions_url); netJob->addNetAction(action); QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks, args] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response for getting versions at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; return; } QVector unsortedVersions; try { auto arr = doc.isObject() ? doc.object()["data"].toArray() : doc.array(); for (auto versionIter : arr) { auto obj = versionIter.toObject(); auto file = loadIndexedPackVersion(obj, args.resourceType); if (!file.addonId.isValid()) { file.addonId = args.pack->addonId; } if (file.fileId.isValid() && !file.downloadUrl.isEmpty()) { // Heuristic to check if the returned value is valid unsortedVersions.append(file); } } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { // dates are in RFC 3339 format return a.date > b.date; }; std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate); } catch (const JSONValidationError& e) { qDebug() << doc; qWarning() << "Error while reading" << debugName() << "resource version:" << e.cause(); } callbacks.on_succeed(unsortedVersions); }); // Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues. // This prevents the lambda from extending the lifetime of the shared resource, // as it only temporarily locks the resource when needed. auto weak = netJob.toWeakRef(); QObject::connect(netJob.get(), &NetJob::failed, [weak, callbacks](const QString& reason) { int network_error_code = -1; if (auto netJob = weak.lock()) { if (auto* failed_action = netJob->getFailedActions().at(0); failed_action) network_error_code = failed_action->replyStatusCode(); } callbacks.on_fail(reason, network_error_code); }); QObject::connect(netJob.get(), &NetJob::aborted, [callbacks] { if (callbacks.on_abort != nullptr) callbacks.on_abort(); }); return netJob; } Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback&& callbacks, bool askRetry) const { auto [job, response] = getProject(args.pack->addonId.toString(), askRetry); QObject::connect(job.get(), &NetJob::succeeded, [this, response, callbacks, args] { auto pack = args.pack; QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response for mod info at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; return; } try { auto obj = Json::requireObject(doc); if (obj.contains("data")) obj = Json::requireObject(obj, "data"); loadIndexedPack(*pack, obj); loadExtraPackInfo(*pack, obj); } catch (const JSONValidationError& e) { qDebug() << doc; qWarning() << "Error while reading" << debugName() << "resource info:" << e.cause(); } callbacks.on_succeed(pack); }); // Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues. // This prevents the lambda from extending the lifetime of the shared resource, // as it only temporarily locks the resource when needed. auto weak = job.toWeakRef(); QObject::connect(job.get(), &NetJob::failed, [weak, callbacks](const QString& reason) { int network_error_code = -1; if (auto job = weak.lock()) { if (auto netJob = qSharedPointerDynamicCast(job)) { if (auto* failed_action = netJob->getFailedActions().at(0); failed_action) { network_error_code = failed_action->replyStatusCode(); } } } callbacks.on_fail(reason, network_error_code); }); QObject::connect(job.get(), &NetJob::aborted, [callbacks] { if (callbacks.on_abort != nullptr) callbacks.on_abort(); }); return job; } Task::Ptr ResourceAPI::getDependencyVersion(DependencySearchArgs&& args, Callback&& callbacks) const { auto versions_url_optional = getDependencyURL(args); if (!versions_url_optional.has_value()) return nullptr; auto versions_url = versions_url_optional.value(); auto netJob = makeShared(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network()); auto [action, response] = Net::ApiDownload::makeByteArray(versions_url); netJob->addNetAction(action); QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks, args] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response for getting dependency version at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; return; } QJsonArray arr; if (args.dependency.version.length() != 0 && doc.isObject()) { arr.append(doc.object()); } else { arr = doc.isObject() ? doc.object()["data"].toArray() : doc.array(); } QVector versions; for (auto versionIter : arr) { auto obj = versionIter.toObject(); auto file = loadIndexedPackVersion(obj, ModPlatform::ResourceType::Mod); if (!file.addonId.isValid()) file.addonId = args.dependency.addonId; if (file.fileId.isValid() && (!file.loaders || args.loader & file.loaders)) // Heuristic to check if the returned value is valid versions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { // dates are in RFC 3339 format return a.date > b.date; }; std::sort(versions.begin(), versions.end(), orderSortPredicate); auto bestMatch = versions.size() != 0 ? versions.front() : ModPlatform::IndexedVersion(); callbacks.on_succeed(bestMatch); }); // Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues. // This prevents the lambda from extending the lifetime of the shared resource, // as it only temporarily locks the resource when needed. auto weak = netJob.toWeakRef(); QObject::connect(netJob.get(), &NetJob::failed, [weak, callbacks](const QString& reason) { int network_error_code = -1; if (auto netJob = weak.lock()) { if (auto* failed_action = netJob->getFailedActions().at(0); failed_action) network_error_code = failed_action->replyStatusCode(); } callbacks.on_fail(reason, network_error_code); }); return netJob; } QString ResourceAPI::getGameVersionsString(std::vector mcVersions) const { QString s; for (auto& ver : mcVersions) { s += QString("\"%1\",").arg(mapMCVersionToModrinth(ver)); } s.remove(s.length() - 1, 1); // remove last comma return s; } QString ResourceAPI::mapMCVersionToModrinth(Version v) const { static const QString preString = " Pre-Release "; auto verStr = v.toString(); if (verStr.contains(preString)) { verStr.replace(preString, "-pre"); } verStr.replace(" ", "-"); return verStr; } std::pair ResourceAPI::getProject(QString addonId, bool askRetry) const { auto project_url_optional = getInfoURL(addonId); if (!project_url_optional.has_value()) return { nullptr, nullptr }; auto project_url = project_url_optional.value(); auto netJob = makeShared(QString("%1::GetProject").arg(addonId), APPLICATION->network()); netJob->setAskRetry(askRetry); auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(project_url)); netJob->addNetAction(action); return { netJob, response }; } PrismLauncher-11.0.3/launcher/modplatform/helpers/0000755000175100017510000000000015224505336021552 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/modplatform/helpers/OverrideUtils.cpp0000644000175100017510000000336015224505336025060 0ustar runnerrunner#include "OverrideUtils.h" #include #include "FileSystem.h" namespace Override { void createOverrides(const QString& name, const QString& parent_folder, const QString& override_path) { QString file_path(FS::PathCombine(parent_folder, name + ".txt")); if (QFile::exists(file_path)) FS::deletePath(file_path); FS::ensureFilePathExists(file_path); QFile file(file_path); if (!file.open(QFile::WriteOnly)) { qWarning() << "Failed to open file" << file.fileName() << "for writing:" << file.errorString(); return; } QDirIterator override_iterator(override_path, QDirIterator::Subdirectories); while (override_iterator.hasNext()) { auto override_file_path = override_iterator.next(); QFileInfo info(override_file_path); if (info.isFile()) { // Absolute path with temp directory -> relative path override_file_path = override_file_path.split(name).last().remove(0, 1); file.write(override_file_path.toUtf8()); file.write("\n"); } } file.close(); } QStringList readOverrides(const QString& name, const QString& parent_folder) { QString file_path(FS::PathCombine(parent_folder, name + ".txt")); QFile file(file_path); if (!file.exists()) return {}; QStringList previous_overrides; if (!file.open(QFile::ReadOnly)) { qWarning() << "Failed to open file" << file.fileName() << "for reading:" << file.errorString(); return previous_overrides; } QString entry; do { entry = file.readLine(); previous_overrides.append(entry.trimmed()); } while (!entry.isEmpty()); file.close(); return previous_overrides; } } // namespace Override PrismLauncher-11.0.3/launcher/modplatform/helpers/HashUtils.cpp0000644000175100017510000001134615224505336024167 0ustar runnerrunner#include "HashUtils.h" #include #include #include #include #include namespace Hashing { Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider) { switch (provider) { case ModPlatform::ResourceProvider::MODRINTH: return makeShared(file_path, ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first()); case ModPlatform::ResourceProvider::FLAME: return makeShared(file_path, Algorithm::Murmur2); default: qCritical() << "[Hashing]" << "Unrecognized mod platform!"; return nullptr; } } Hasher::Ptr createHasher(QString file_path, QString type) { return makeShared(file_path, type); } class QIODeviceReader : public Murmur2::Reader { public: QIODeviceReader(QIODevice* device) : m_device(device) {} virtual ~QIODeviceReader() = default; virtual int read(char* s, int n) { return m_device->read(s, n); } virtual bool eof() { return m_device->atEnd(); } virtual void goToBeginning() { m_device->seek(0); } virtual void close() { m_device->close(); } private: QIODevice* m_device; }; QString algorithmToString(Algorithm type) { switch (type) { case Algorithm::Md4: return "md4"; case Algorithm::Md5: return "md5"; case Algorithm::Sha1: return "sha1"; case Algorithm::Sha256: return "sha256"; case Algorithm::Sha512: return "sha512"; case Algorithm::Murmur2: return "murmur2"; // case Algorithm::Unknown: default: break; } return "unknown"; } Algorithm algorithmFromString(QString type) { if (type == "md4") return Algorithm::Md4; if (type == "md5") return Algorithm::Md5; if (type == "sha1") return Algorithm::Sha1; if (type == "sha256") return Algorithm::Sha256; if (type == "sha512") return Algorithm::Sha512; if (type == "murmur2") return Algorithm::Murmur2; return Algorithm::Unknown; } QString hash(QIODevice* device, Algorithm type) { if (!device->isOpen() && !device->open(QFile::ReadOnly)) return ""; QCryptographicHash::Algorithm alg = QCryptographicHash::Sha1; switch (type) { case Algorithm::Md4: alg = QCryptographicHash::Algorithm::Md4; break; case Algorithm::Md5: alg = QCryptographicHash::Algorithm::Md5; break; case Algorithm::Sha1: alg = QCryptographicHash::Algorithm::Sha1; break; case Algorithm::Sha256: alg = QCryptographicHash::Algorithm::Sha256; break; case Algorithm::Sha512: alg = QCryptographicHash::Algorithm::Sha512; break; case Algorithm::Murmur2: { // CF-specific auto should_filter_out = [](char c) { return (c == 9 || c == 10 || c == 13 || c == 32); }; auto reader = std::make_unique(device); auto result = QString::number(Murmur2::hash(reader.get(), 4 * MiB, should_filter_out)); device->close(); return result; } case Algorithm::Unknown: device->close(); return ""; } QCryptographicHash hash(alg); if (!hash.addData(device)) qCritical() << "Failed to read JAR to create hash!"; Q_ASSERT(hash.result().length() == hash.hashLength(alg)); auto result = hash.result().toHex(); device->close(); return result; } QString hash(QString fileName, Algorithm type) { QFile file(fileName); return hash(&file, type); } QString hash(QByteArray data, Algorithm type) { QBuffer buff(&data); return hash(&buff, type); } void Hasher::executeTask() { m_future = QtConcurrent::run( QThreadPool::globalInstance(), [](QString fileName, Algorithm type) { return hash(fileName, type); }, m_path, m_alg); connect(&m_watcher, &QFutureWatcher::finished, this, [this] { if (m_future.isCanceled()) { emitAborted(); } else if (m_result = m_future.result(); m_result.isEmpty()) { emitFailed("Empty hash!"); } else { emit resultsReady(m_result); emitSucceeded(); } }); m_watcher.setFuture(m_future); } bool Hasher::abort() { if (m_future.isRunning()) { m_future.cancel(); // NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not // occur immediately. return true; } return false; } } // namespace Hashing PrismLauncher-11.0.3/launcher/modplatform/helpers/ExportToModList.cpp0000644000175100017510000001654415224505336025350 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ExportToModList.h" #include #include #include namespace ExportToModList { QString toHTML(QList mods, OptionalData extraData) { QStringList lines; for (auto mod : mods) { auto meta = mod->metadata(); auto modName = mod->name().toHtmlEscaped(); if (extraData & Url) { auto url = mod->homepage().toHtmlEscaped(); if (!url.isEmpty()) modName = QString("%2").arg(url, modName); } auto line = modName; if (extraData & Version) { auto ver = mod->version(); if (ver.isEmpty() && meta != nullptr) ver = meta->version().toString(); if (!ver.isEmpty()) line += QString(" [%1]").arg(ver.toHtmlEscaped()); } if (extraData & Authors && !mod->authors().isEmpty()) line += " by " + mod->authors().join(", ").toHtmlEscaped(); if (extraData & FileName) line += QString(" (%1)").arg(mod->fileinfo().fileName().toHtmlEscaped()); lines.append(QString("
  • %1
  • ").arg(line)); } return QString("
      \n\t%1\n
    ").arg(lines.join("\n\t")); } QString toMarkdownEscaped(QString src) { for (auto ch : "\\`*_{}[]<>()#+-.!|") src.replace(ch, QString("\\%1").arg(ch)); return src; } QString toMarkdown(QList mods, OptionalData extraData) { QStringList lines; for (auto mod : mods) { auto meta = mod->metadata(); auto modName = toMarkdownEscaped(mod->name()); if (extraData & Url) { auto url = mod->homepage(); if (!url.isEmpty()) modName = QString("[%1](%2)").arg(modName, url); } auto line = modName; if (extraData & Version) { auto ver = toMarkdownEscaped(mod->version()); if (ver.isEmpty() && meta != nullptr) ver = toMarkdownEscaped(meta->version().toString()); if (!ver.isEmpty()) line += QString(" [%1]").arg(ver); } if (extraData & Authors && !mod->authors().isEmpty()) line += " by " + toMarkdownEscaped(mod->authors().join(", ")); if (extraData & FileName) line += QString(" (%1)").arg(toMarkdownEscaped(mod->fileinfo().fileName())); lines << "- " + line; } return lines.join("\n"); } QString toPlainTXT(QList mods, OptionalData extraData) { QStringList lines; for (auto mod : mods) { auto meta = mod->metadata(); auto modName = mod->name(); auto line = modName; if (extraData & Url) { auto url = mod->homepage(); if (!url.isEmpty()) line += QString(" (%1)").arg(url); } if (extraData & Version) { auto ver = mod->version(); if (ver.isEmpty() && meta != nullptr) ver = meta->version().toString(); if (!ver.isEmpty()) line += QString(" [%1]").arg(ver); } if (extraData & Authors && !mod->authors().isEmpty()) line += " by " + mod->authors().join(", "); if (extraData & FileName) line += QString(" (%1)").arg(mod->fileinfo().fileName()); lines << line; } return lines.join("\n"); } QString toJSON(QList mods, OptionalData extraData) { QJsonArray lines; for (auto mod : mods) { auto meta = mod->metadata(); auto modName = mod->name(); QJsonObject line; line["name"] = modName; if (extraData & Url) { auto url = mod->homepage(); if (!url.isEmpty()) line["url"] = url; } if (extraData & Version) { auto ver = mod->version(); if (ver.isEmpty() && meta != nullptr) ver = meta->version().toString(); if (!ver.isEmpty()) line["version"] = ver; } if (extraData & Authors && !mod->authors().isEmpty()) line["authors"] = QJsonArray::fromStringList(mod->authors()); if (extraData & FileName) line["filename"] = mod->fileinfo().fileName(); lines << line; } QJsonDocument doc; doc.setArray(lines); return doc.toJson(); } QString toCSV(QList mods, OptionalData extraData) { QStringList lines; for (auto mod : mods) { QStringList data; auto meta = mod->metadata(); auto modName = mod->name(); data << modName; if (extraData & Url) data << mod->homepage(); if (extraData & Version) { auto ver = mod->version(); if (ver.isEmpty() && meta != nullptr) ver = meta->version().toString(); data << ver; } if (extraData & Authors) { QString authors; if (mod->authors().length() == 1) authors = mod->authors().back(); else if (mod->authors().length() > 1) authors = QString("\"%1\"").arg(mod->authors().join(",")); data << authors; } if (extraData & FileName) data << mod->fileinfo().fileName(); lines << data.join(","); } return lines.join("\n"); } QString exportToModList(QList mods, Formats format, OptionalData extraData) { switch (format) { case HTML: return toHTML(mods, extraData); case MARKDOWN: return toMarkdown(mods, extraData); case PLAINTXT: return toPlainTXT(mods, extraData); case JSON: return toJSON(mods, extraData); case CSV: return toCSV(mods, extraData); default: { return QString("unknown format:%1").arg(format); } } } QString exportToModList(QList mods, QString lineTemplate) { QStringList lines; for (auto mod : mods) { auto meta = mod->metadata(); auto modName = mod->name(); auto modID = mod->mod_id(); auto url = mod->homepage(); auto ver = mod->version(); if (ver.isEmpty() && meta != nullptr) ver = meta->version().toString(); auto authors = mod->authors().join(", "); auto filename = mod->fileinfo().fileName(); lines << QString(lineTemplate) .replace("{name}", modName) .replace("{mod_id}", modID) .replace("{url}", url) .replace("{version}", ver) .replace("{authors}", authors) .replace("{filename}", filename); } return lines.join("\n"); } } // namespace ExportToModList PrismLauncher-11.0.3/launcher/modplatform/helpers/OverrideUtils.h0000644000175100017510000000115215224505336024522 0ustar runnerrunner#pragma once #include namespace Override { /** This creates a file in `parent_folder` that holds information about which * overrides are in `override_path`. * * If there's already an existing such file, it will be ovewritten. */ void createOverrides(const QString& name, const QString& parent_folder, const QString& override_path); /** This reads an existing overrides archive, returning a list of overrides. * * If there's no such file in `parent_folder`, it will return an empty list. */ QStringList readOverrides(const QString& name, const QString& parent_folder); } // namespace Override PrismLauncher-11.0.3/launcher/modplatform/helpers/ExportToModList.h0000644000175100017510000000224415224505336025005 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include "minecraft/mod/Mod.h" namespace ExportToModList { enum Formats { HTML, MARKDOWN, PLAINTXT, JSON, CSV, CUSTOM }; enum OptionalData { Authors = 1 << 0, Url = 1 << 1, Version = 1 << 2, FileName = 1 << 3 }; QString exportToModList(QList mods, Formats format, OptionalData extraData); QString exportToModList(QList mods, QString lineTemplate); } // namespace ExportToModList PrismLauncher-11.0.3/launcher/modplatform/helpers/HashUtils.h0000644000175100017510000000242515224505336023632 0ustar runnerrunner#pragma once #include #include #include #include #include "modplatform/ModIndex.h" #include "tasks/Task.h" namespace Hashing { enum class Algorithm { Md4, Md5, Sha1, Sha256, Sha512, Murmur2, Unknown }; QString algorithmToString(Algorithm type); Algorithm algorithmFromString(QString type); QString hash(QIODevice* device, Algorithm type); QString hash(QString fileName, Algorithm type); QString hash(QByteArray data, Algorithm type); class Hasher : public Task { Q_OBJECT public: using Ptr = shared_qobject_ptr; Hasher(QString file_path, Algorithm alg) : m_path(file_path), m_alg(alg) {} Hasher(QString file_path, QString alg) : Hasher(file_path, algorithmFromString(alg)) {} bool abort() override; void executeTask() override; QString getResult() const { return m_result; }; QString getPath() const { return m_path; }; signals: void resultsReady(QString hash); private: QString m_result; QString m_path; Algorithm m_alg; QFuture m_future; QFutureWatcher m_watcher; }; Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider); Hasher::Ptr createHasher(QString file_path, QString type); } // namespace Hashing PrismLauncher-11.0.3/launcher/modplatform/import_ftb/0000755000175100017510000000000015224505336022255 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/modplatform/import_ftb/PackHelpers.cpp0000644000175100017510000001401115224505336025157 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "modplatform/import_ftb/PackHelpers.h" #include #include #include #include #include "FileSystem.h" #include "Json.h" namespace FTBImportAPP { QIcon loadFTBIcon(const QString& imagePath) { // Map of type byte to image type string static const QHash imageTypeMap = { { 0x00, "png" }, { 0x01, "jpg" }, { 0x02, "gif" }, { 0x03, "webp" } }; QFile file(imagePath); if (!file.exists() || !file.open(QIODevice::ReadOnly)) { return QIcon(); } char type; if (!file.getChar(&type)) { qDebug() << "Missing FTB image type header at" << imagePath; return QIcon(); } if (!imageTypeMap.contains(type)) { qDebug().nospace().noquote() << "Don't recognize FTB image type 0x" << QString::number(type, 16); return QIcon(); } auto imageType = imageTypeMap[type]; // Extract actual image data beyond the first byte QImageReader reader(&file, imageType); auto pixmap = QPixmap::fromImageReader(&reader); if (pixmap.isNull()) { qDebug() << "The FTB image at" << imagePath << "is not valid"; return QIcon(); } return QIcon(pixmap); } Modpack parseDirectory(QString path) { Modpack modpack{ path }; auto instanceFile = QFileInfo(FS::PathCombine(path, "instance.json")); if (!instanceFile.exists() || !instanceFile.isFile()) return {}; try { auto doc = Json::requireDocument(instanceFile.absoluteFilePath(), "FTB_APP instance JSON file"); const auto root = doc.object(); modpack.uuid = Json::requireString(root, "uuid", "uuid"); modpack.id = Json::requireInteger(root, "id", "id"); modpack.versionId = Json::requireInteger(root, "versionId", "versionId"); modpack.name = Json::requireString(root, "name", "name"); modpack.version = Json::requireString(root, "version", "version"); modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion"); modpack.jvmArgs = root["jvmArgs"].toVariant(); modpack.totalPlayTime = Json::requireInteger(root, "totalPlayTime", "totalPlayTime"); auto modLoader = Json::requireString(root, "modLoader", "modLoader"); if (!modLoader.isEmpty()) { const auto parts = modLoader.split('-', Qt::KeepEmptyParts); if (parts.size() >= 2) { const auto loader = parts.first().toLower(); modpack.loaderVersion = parts.at(1).trimmed(); if (loader == "neoforge") { modpack.loaderType = ModPlatform::NeoForge; } else if (loader == "forge") { modpack.loaderType = ModPlatform::Forge; } else if (loader == "fabric") { modpack.loaderType = ModPlatform::Fabric; } else if (loader == "quilt") { modpack.loaderType = ModPlatform::Quilt; } } } } catch (const Exception& e) { qDebug() << "Couldn't load ftb instance json:" << e.cause(); return {}; } if (!modpack.loaderType.has_value()) { legacyInstanceParsing(path, &modpack.loaderType, &modpack.loaderVersion); } auto iconFile = QFileInfo(FS::PathCombine(path, "folder.jpg")); if (iconFile.exists() && iconFile.isFile()) { modpack.icon = QIcon(iconFile.absoluteFilePath()); } else { // the logo is a file that the first bit denotes the image tipe followed by the actual image data modpack.icon = loadFTBIcon(FS::PathCombine(path, ".ftbapp", "logo")); } return modpack; } void legacyInstanceParsing(QString path, std::optional* loaderType, QString* loaderVersion) { auto versionsFile = QFileInfo(FS::PathCombine(path, ".ftbapp", "version.json")); if (!versionsFile.exists() || !versionsFile.isFile()) { versionsFile = QFileInfo(FS::PathCombine(path, "version.json")); } if (!versionsFile.exists() || !versionsFile.isFile()) { qDebug() << "Couldn't find ftb version json"; return; } try { auto doc = Json::requireDocument(versionsFile.absoluteFilePath(), "FTB_APP version JSON file"); const auto root = doc.object(); auto targets = Json::requireArray(root, "targets", "targets"); for (auto target : targets) { auto obj = Json::requireObject(target, "target"); auto name = Json::requireString(obj, "name", "name"); auto version = Json::requireString(obj, "version", "version"); if (name == "neoforge") { *loaderType = ModPlatform::NeoForge; *loaderVersion = version; break; } else if (name == "forge") { *loaderType = ModPlatform::Forge; *loaderVersion = version; break; } else if (name == "fabric") { *loaderType = ModPlatform::Fabric; *loaderVersion = version; break; } else if (name == "quilt") { *loaderType = ModPlatform::Quilt; *loaderVersion = version; break; } } } catch (const Exception& e) { qDebug() << "Couldn't load ftb version json:" << e.cause(); return; } } } // namespace FTBImportAPP PrismLauncher-11.0.3/launcher/modplatform/import_ftb/PackHelpers.h0000644000175100017510000000304715224505336024633 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include #include #include "modplatform/ResourceAPI.h" namespace FTBImportAPP { struct Modpack { QString path; // json data QString uuid; int id; int versionId; QString name; QString version; QString mcVersion; int totalPlayTime; // not needed for instance creation QVariant jvmArgs; std::optional loaderType; QString loaderVersion; QIcon icon; }; using ModpackList = QList; Modpack parseDirectory(QString path); void legacyInstanceParsing(QString path, std::optional* loaderType, QString* loaderVersion); } // namespace FTBImportAPP // We need it for the proxy model Q_DECLARE_METATYPE(FTBImportAPP::Modpack) PrismLauncher-11.0.3/launcher/modplatform/import_ftb/PackInstallTask.cpp0000644000175100017510000001031415224505336026010 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "PackInstallTask.h" #include #include "BaseInstance.h" #include "FileSystem.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "modplatform/ResourceAPI.h" #include "modplatform/import_ftb/PackHelpers.h" #include "settings/INISettingsObject.h" namespace FTBImportAPP { void PackInstallTask::executeTask() { setStatus(tr("Copying files...")); setAbortable(false); progress(1, 2); m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { FS::copy folderCopy(m_pack.path, FS::PathCombine(m_stagingPath, "minecraft")); return folderCopy(); }); connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &PackInstallTask::copySettings); connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &PackInstallTask::emitAborted); m_copyFutureWatcher.setFuture(m_copyFuture); } void PackInstallTask::copySettings() { setStatus(tr("Copying settings...")); progress(2, 2); QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); MinecraftInstance instance(m_globalSettings, std::make_unique(instanceConfigPath), m_stagingPath); { SettingsObject::Lock lock(instance.settings()); instance.settings()->set("InstanceType", "OneSix"); instance.settings()->set("totalTimePlayed", m_pack.totalPlayTime / 1000); if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) { instance.settings()->set("OverrideJavaArgs", true); instance.settings()->set("JvmArgs", m_pack.jvmArgs.toString()); } auto components = instance.getPackProfile(); components->buildingFromScratch(); components->setComponentVersion("net.minecraft", m_pack.mcVersion, true); auto modloader = m_pack.loaderType; if (modloader.has_value()) switch (modloader.value()) { case ModPlatform::NeoForge: { components->setComponentVersion("net.neoforged", m_pack.loaderVersion, true); break; } case ModPlatform::Forge: { components->setComponentVersion("net.minecraftforge", m_pack.loaderVersion, true); break; } case ModPlatform::Fabric: { components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.loaderVersion, true); break; } case ModPlatform::Quilt: { components->setComponentVersion("org.quiltmc.quilt-loader", m_pack.loaderVersion, true); break; } case ModPlatform::Cauldron: break; case ModPlatform::LiteLoader: break; case ModPlatform::DataPack: break; case ModPlatform::Babric: break; case ModPlatform::BTA: break; case ModPlatform::LegacyFabric: break; case ModPlatform::Ornithe: break; case ModPlatform::Rift: break; } components->saveNow(); instance.setName(name()); if (m_instIcon == "default") m_instIcon = "ftb_logo"; instance.setIconKey(m_instIcon); } emitSucceeded(); } } // namespace FTBImportAPP PrismLauncher-11.0.3/launcher/modplatform/import_ftb/PackInstallTask.h0000644000175100017510000000244415224505336025462 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include "InstanceTask.h" #include "PackHelpers.h" namespace FTBImportAPP { class PackInstallTask : public InstanceTask { Q_OBJECT public: explicit PackInstallTask(const Modpack& pack) : m_pack(pack) {} virtual ~PackInstallTask() = default; protected: virtual void executeTask() override; private slots: void copySettings(); private: QFuture m_copyFuture; QFutureWatcher m_copyFutureWatcher; const Modpack m_pack; }; } // namespace FTBImportAPP PrismLauncher-11.0.3/launcher/modplatform/atlauncher/0000755000175100017510000000000015224505336022236 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/modplatform/atlauncher/ATLPackIndex.h0000644000175100017510000000226415224505336024622 0ustar runnerrunner/* * Copyright 2020-2021 Jamie Mansfield * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "ATLPackManifest.h" #include #include #include namespace ATLauncher { struct IndexedVersion { QString version; QString minecraft; }; struct IndexedPack { int id; int position; QString name; PackType type; QList versions; bool system; QString description; QString safeName; }; void loadIndexedPack(IndexedPack& m, QJsonObject& obj); } // namespace ATLauncher Q_DECLARE_METATYPE(ATLauncher::IndexedPack) Q_DECLARE_METATYPE(QList) PrismLauncher-11.0.3/launcher/modplatform/atlauncher/ATLShareCode.cpp0000644000175100017510000000355415224505336025147 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ATLShareCode.h" #include "Json.h" namespace ATLauncher { static void loadShareCodeMod(ShareCodeMod& m, QJsonObject& obj) { m.selected = Json::requireBoolean(obj, "selected"); m.name = Json::requireString(obj, "name"); } static void loadShareCode(ShareCode& c, QJsonObject& obj) { c.pack = Json::requireString(obj, "pack"); c.version = Json::requireString(obj, "version"); auto mods = Json::requireObject(obj, "mods"); auto optional = Json::requireArray(mods, "optional"); for (const auto modRaw : optional) { auto modObj = Json::requireObject(modRaw); ShareCodeMod mod; loadShareCodeMod(mod, modObj); c.mods.append(mod); } } void loadShareCodeResponse(ShareCodeResponse& r, QJsonObject& obj) { r.error = Json::requireBoolean(obj, "error"); r.code = Json::requireInteger(obj, "code"); if (obj.contains("message") && !obj.value("message").isNull()) r.message = Json::requireString(obj, "message"); if (!r.error) { auto dataRaw = Json::requireObject(obj, "data"); loadShareCode(r.data, dataRaw); } } } // namespace ATLauncher PrismLauncher-11.0.3/launcher/modplatform/atlauncher/ATLPackInstallTask.h0000644000175100017510000001132115224505336025776 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2020-2021 Jamie Mansfield * Copyright 2021 Petr Mrazek * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include "ATLPackManifest.h" #include "InstanceTask.h" #include "meta/Version.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "net/NetJob.h" #include #include namespace ATLauncher { enum class InstallMode : std::uint8_t { Install, Reinstall, Update, }; class UserInteractionSupport { public: /** * Requests a user interaction to select which optional mods should be installed. */ virtual std::optional> chooseOptionalMods(const PackVersion& version, QList mods) = 0; /** * Requests a user interaction to select a component version from a given version list * and constrained to a given Minecraft version. */ virtual QString chooseVersion(Meta::VersionList::Ptr vlist, QString minecraftVersion) = 0; /** * Requests a user interaction to display a message. */ virtual void displayMessage(QString message) = 0; virtual ~UserInteractionSupport() = default; }; class PackInstallTask : public InstanceTask { Q_OBJECT public: explicit PackInstallTask(UserInteractionSupport* support, QString packName, QString version, InstallMode installMode = InstallMode::Install); ~PackInstallTask() override { delete m_support; } bool canAbort() const override { return true; } bool abort() override; protected: void executeTask() override; private slots: void onDownloadSucceeded(QByteArray* responsePtr); void onDownloadFailed(QString reason); void onDownloadAborted(); void onModsDownloaded(); void onModsExtracted(); private: QString getDirForModType(ModType type, const QString& raw); QString getVersionForLoader(const QString& uid); static QString detectLibrary(const VersionLibrary& library); bool createLibrariesComponent(const QString& instanceRoot, PackProfile* profile); bool createPackComponent(const QString& instanceRoot, PackProfile* profile); void deleteExistingFiles(); void installConfigs(); void extractConfigs(); void downloadMods(); bool extractMods(const QMap& toExtract, const QMap& toDecomp, const QMap& toCopy); void install(); private: UserInteractionSupport* m_support; bool abortable = false; NetJob::Ptr jobPtr; InstallMode m_install_mode; QString m_pack_name; QString m_pack_safe_name; QString m_version_name; PackVersion m_version; QMap modsToExtract; QMap modsToDecomp; QMap modsToCopy; QString archivePath; QStringList jarmods; Meta::Version::Ptr minecraftVersion; QMap componentsToInstall; QFuture> m_extractFuture; QFutureWatcher> m_extractFutureWatcher; QFuture m_modExtractFuture; QFutureWatcher m_modExtractFutureWatcher; }; } // namespace ATLauncher PrismLauncher-11.0.3/launcher/modplatform/atlauncher/ATLShareCode.h0000644000175100017510000000226015224505336024605 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include namespace ATLauncher { struct ShareCodeMod { bool selected; QString name; }; struct ShareCode { QString pack; QString version; QList mods; }; struct ShareCodeResponse { bool error; int code; QString message; ShareCode data; }; void loadShareCodeResponse(ShareCodeResponse& r, QJsonObject& obj); } // namespace ATLauncher PrismLauncher-11.0.3/launcher/modplatform/atlauncher/ATLPackManifest.cpp0000644000175100017510000002735015224505336025657 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2020-2021 Jamie Mansfield * Copyright 2021 Petr Mrazek * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ATLPackManifest.h" #include "Json.h" static ATLauncher::DownloadType parseDownloadType(QString rawType) { if (rawType == QString("server")) { return ATLauncher::DownloadType::Server; } else if (rawType == QString("browser")) { return ATLauncher::DownloadType::Browser; } else if (rawType == QString("direct")) { return ATLauncher::DownloadType::Direct; } return ATLauncher::DownloadType::Unknown; } static ATLauncher::ModType parseModType(QString rawType) { // See https://wiki.atlauncher.com/mod_types if (rawType == QString("root")) { return ATLauncher::ModType::Root; } else if (rawType == QString("forge")) { return ATLauncher::ModType::Forge; } else if (rawType == QString("jar")) { return ATLauncher::ModType::Jar; } else if (rawType == QString("mods")) { return ATLauncher::ModType::Mods; } else if (rawType == QString("flan")) { return ATLauncher::ModType::Flan; } else if (rawType == QString("dependency") || rawType == QString("depandency")) { return ATLauncher::ModType::Dependency; } else if (rawType == QString("ic2lib")) { return ATLauncher::ModType::Ic2Lib; } else if (rawType == QString("denlib")) { return ATLauncher::ModType::DenLib; } else if (rawType == QString("coremods")) { return ATLauncher::ModType::Coremods; } else if (rawType == QString("mcpc")) { return ATLauncher::ModType::MCPC; } else if (rawType == QString("plugins")) { return ATLauncher::ModType::Plugins; } else if (rawType == QString("extract")) { return ATLauncher::ModType::Extract; } else if (rawType == QString("decomp")) { return ATLauncher::ModType::Decomp; } else if (rawType == QString("texturepack")) { return ATLauncher::ModType::TexturePack; } else if (rawType == QString("resourcepack")) { return ATLauncher::ModType::ResourcePack; } else if (rawType == QString("shaderpack")) { return ATLauncher::ModType::ShaderPack; } else if (rawType == QString("texturepackextract")) { return ATLauncher::ModType::TexturePackExtract; } else if (rawType == QString("resourcepackextract")) { return ATLauncher::ModType::ResourcePackExtract; } else if (rawType == QString("millenaire")) { return ATLauncher::ModType::Millenaire; } return ATLauncher::ModType::Unknown; } static void loadVersionLoader(ATLauncher::VersionLoader& p, QJsonObject& obj) { p.type = Json::requireString(obj, "type"); p.choose = obj["choose"].toBool(); auto metadata = Json::requireObject(obj, "metadata"); p.latest = metadata["latest"].toBool(); p.recommended = metadata["recommended"].toBool(); // Minecraft Forge if (p.type == "forge" || p.type == "neoforge") { p.version = metadata["version"].toString(""); } // Fabric Loader if (p.type == "fabric") { p.version = metadata["loader"].toString(""); } } static void loadVersionLibrary(ATLauncher::VersionLibrary& p, QJsonObject& obj) { p.url = Json::requireString(obj, "url"); p.file = Json::requireString(obj, "file"); p.md5 = Json::requireString(obj, "md5"); p.download_raw = Json::requireString(obj, "download"); p.download = parseDownloadType(p.download_raw); p.server = obj["server"].toString(""); } static void loadVersionConfigs(ATLauncher::VersionConfigs& p, QJsonObject& obj) { p.filesize = Json::requireInteger(obj, "filesize"); p.sha1 = Json::requireString(obj, "sha1"); } static void loadVersionMod(ATLauncher::VersionMod& p, QJsonObject& obj) { p.name = Json::requireString(obj, "name"); p.version = Json::requireString(obj, "version"); p.url = Json::requireString(obj, "url"); p.file = Json::requireString(obj, "file"); p.md5 = obj["md5"].toString(""); p.download_raw = Json::requireString(obj, "download"); p.download = parseDownloadType(p.download_raw); p.type_raw = Json::requireString(obj, "type"); p.type = parseModType(p.type_raw); // This contributes to the Minecraft Forge detection, where we rely on mod.type being "Forge" // when the mod represents Forge. As there is little difference between "Jar" and "Forge, some // packs regretfully use "Jar". This will correct the type to "Forge" in these cases (as best // it can). if (p.name == QString("Minecraft Forge") && p.type == ATLauncher::ModType::Jar) { p.type_raw = "forge"; p.type = ATLauncher::ModType::Forge; } if (obj.contains("extractTo")) { p.extractTo_raw = Json::requireString(obj, "extractTo"); p.extractTo = parseModType(p.extractTo_raw); p.extractFolder = obj["extractFolder"].toString("").replace("%s%", "/"); } if (obj.contains("decompType")) { p.decompType_raw = Json::requireString(obj, "decompType"); p.decompType = parseModType(p.decompType_raw); p.decompFile = Json::requireString(obj, "decompFile"); } p.description = obj["description"].toString(""); p.optional = obj["optional"].toBool(); p.recommended = obj["recommended"].toBool(); p.selected = obj["selected"].toBool(); p.hidden = obj["hidden"].toBool(); p.library = obj["library"].toBool(); p.group = obj["group"].toString(""); if (obj.contains("depends")) { auto dependsArr = Json::requireArray(obj, "depends"); for (const auto depends : dependsArr) { p.depends.append(Json::requireString(depends)); } } p.colour = obj["colour"].toString(""); p.warning = obj["warning"].toString(""); p.client = obj["client"].toBool(); // computed p.effectively_hidden = p.hidden || p.library; } static void loadVersionMessages(ATLauncher::VersionMessages& m, QJsonObject& obj) { m.install = obj["install"].toString(""); m.update = obj["update"].toString(""); } static void loadVersionMainClass(ATLauncher::PackVersionMainClass& m, QJsonObject& obj) { m.mainClass = obj["mainClass"].toString(""); m.depends = obj["depends"].toString(""); } static void loadVersionExtraArguments(ATLauncher::PackVersionExtraArguments& a, QJsonObject& obj) { a.arguments = obj["arguments"].toString(""); a.depends = obj["depends"].toString(""); } static void loadVersionKeep(ATLauncher::VersionKeep& k, QJsonObject& obj) { k.base = Json::requireString(obj, "base"); k.target = Json::requireString(obj, "target"); } static void loadVersionKeeps(ATLauncher::VersionKeeps& k, QJsonObject& obj) { if (obj.contains("files")) { auto files = Json::requireArray(obj, "files"); for (const auto keepRaw : files) { auto keepObj = Json::requireObject(keepRaw); ATLauncher::VersionKeep keep; loadVersionKeep(keep, keepObj); k.files.append(keep); } } if (obj.contains("folders")) { auto folders = Json::requireArray(obj, "folders"); for (const auto keepRaw : folders) { auto keepObj = Json::requireObject(keepRaw); ATLauncher::VersionKeep keep; loadVersionKeep(keep, keepObj); k.folders.append(keep); } } } static void loadVersionDelete(ATLauncher::VersionDelete& d, QJsonObject& obj) { d.base = Json::requireString(obj, "base"); d.target = Json::requireString(obj, "target"); } static void loadVersionDeletes(ATLauncher::VersionDeletes& d, QJsonObject& obj) { if (obj.contains("files")) { auto files = Json::requireArray(obj, "files"); for (const auto deleteRaw : files) { auto deleteObj = Json::requireObject(deleteRaw); ATLauncher::VersionDelete versionDelete; loadVersionDelete(versionDelete, deleteObj); d.files.append(versionDelete); } } if (obj.contains("folders")) { auto folders = Json::requireArray(obj, "folders"); for (const auto deleteRaw : folders) { auto deleteObj = Json::requireObject(deleteRaw); ATLauncher::VersionDelete versionDelete; loadVersionDelete(versionDelete, deleteObj); d.folders.append(versionDelete); } } } void ATLauncher::loadVersion(PackVersion& v, QJsonObject& obj) { v.version = Json::requireString(obj, "version"); v.minecraft = Json::requireString(obj, "minecraft"); v.noConfigs = obj["noConfigs"].toBool(); if (obj.contains("mainClass")) { auto main = Json::requireObject(obj, "mainClass"); loadVersionMainClass(v.mainClass, main); } if (obj.contains("extraArguments")) { auto arguments = Json::requireObject(obj, "extraArguments"); loadVersionExtraArguments(v.extraArguments, arguments); } if (obj.contains("loader")) { auto loader = Json::requireObject(obj, "loader"); loadVersionLoader(v.loader, loader); } if (obj.contains("libraries")) { auto libraries = Json::requireArray(obj, "libraries"); for (const auto libraryRaw : libraries) { auto libraryObj = Json::requireObject(libraryRaw); ATLauncher::VersionLibrary target; loadVersionLibrary(target, libraryObj); v.libraries.append(target); } } if (obj.contains("mods")) { auto mods = Json::requireArray(obj, "mods"); for (const auto modRaw : mods) { auto modObj = Json::requireObject(modRaw); ATLauncher::VersionMod mod; loadVersionMod(mod, modObj); v.mods.append(mod); } } if (obj.contains("configs")) { auto configsObj = Json::requireObject(obj, "configs"); loadVersionConfigs(v.configs, configsObj); } auto colourObj = obj["colours"].toObject(); for (const auto& key : colourObj.keys()) { v.colours[key] = Json::requireString(colourObj.value(key), "colour"); } auto warningsObj = obj["warnings"].toObject(); for (const auto& key : warningsObj.keys()) { v.warnings[key] = Json::requireString(warningsObj.value(key), "warning"); } auto messages = obj["messages"].toObject(); loadVersionMessages(v.messages, messages); auto keeps = obj["keeps"].toObject(); loadVersionKeeps(v.keeps, keeps); auto deletes = obj["deletes"].toObject(); loadVersionDeletes(v.deletes, deletes); } PrismLauncher-11.0.3/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp0000644000175100017510000011741715224505336026346 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2020-2021 Jamie Mansfield * Copyright 2021 Petr Mrazek * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ATLPackInstallTask.h" #include #include #include #include "FileSystem.h" #include "Json.h" #include "MMCZip.h" #include "Version.h" #include "meta/Index.h" #include "meta/Version.h" #include "meta/VersionList.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/OneSixVersionFormat.h" #include "minecraft/PackProfile.h" #include "modplatform/atlauncher/ATLPackManifest.h" #include "net/ChecksumValidator.h" #include "settings/INISettingsObject.h" #include "net/ApiDownload.h" #include "Application.h" #include "BuildConfig.h" #include "ui/dialogs/BlockedModsDialog.h" namespace { bool isPathTraversal(const QString& basePath, const QString& entryName) { auto safeName = FS::RemoveInvalidPathChars(entryName); auto fullPath = FS::PathCombine(basePath, safeName); auto baseUrl = QUrl::fromLocalFile(basePath); return !baseUrl.isParentOf(QUrl::fromLocalFile(fullPath)); } Meta::Version::Ptr getComponentVersion(const QString& uid, const QString& version) { return APPLICATION->metadataIndex()->getLoadedVersion(uid, version); } } // namespace namespace ATLauncher { PackInstallTask::PackInstallTask(UserInteractionSupport* support, QString packName, QString version, InstallMode installMode) : m_support(support), m_install_mode(installMode), m_pack_name(packName), m_version_name(std::move(version)) { static const QRegularExpression s_regex("[^A-Za-z0-9]"); m_pack_safe_name = packName.replace(s_regex, ""); } bool PackInstallTask::abort() { if (abortable) { return jobPtr->abort(); } return false; } void PackInstallTask::executeTask() { qDebug() << "PackInstallTask::executeTask:" << QThread::currentThreadId(); NetJob::Ptr netJob{ new NetJob("ATLauncher::VersionFetch", APPLICATION->network()) }; auto searchUrl = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json").arg(m_pack_safe_name).arg(m_version_name); auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(searchUrl)); netJob->addNetAction(action); connect(netJob.get(), &NetJob::succeeded, this, [this, response] { onDownloadSucceeded(response); }); connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed); connect(netJob.get(), &NetJob::aborted, this, &PackInstallTask::onDownloadAborted); jobPtr = netJob; jobPtr->start(); } void PackInstallTask::onDownloadSucceeded(QByteArray* responsePtr) { qDebug() << "PackInstallTask::onDownloadSucceeded:" << QThread::currentThreadId(); // NOTE(TheKodeToad): moving the response out to avoid it from being destroyed by jobPtr.reset() QByteArray response = std::move(*responsePtr); jobPtr.reset(); QJsonParseError parseError{}; QJsonDocument doc = QJsonDocument::fromJson(response, &parseError); if (parseError.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from ATLauncher at" << parseError.offset << "reason:" << parseError.errorString(); qWarning() << response; return; } auto obj = doc.object(); ATLauncher::PackVersion version; try { ATLauncher::loadVersion(version, obj); } catch (const JSONValidationError& e) { emitFailed(tr("Could not understand pack manifest:\n") + e.cause()); return; } m_version = version; // Derived from the installation mode QString message; bool resetDirectory = false; switch (m_install_mode) { case InstallMode::Reinstall: case InstallMode::Update: message = m_version.messages.update; resetDirectory = true; break; case InstallMode::Install: message = m_version.messages.install; resetDirectory = false; break; default: emitFailed(tr("Unsupported installation mode")); return; } // Display message if one exists if (!message.isEmpty()) { m_support->displayMessage(message); } auto ver = getComponentVersion("net.minecraft", m_version.minecraft); if (!ver) { emitFailed(tr("Failed to get local metadata index for '%1' v%2").arg("net.minecraft", m_version.minecraft)); return; } minecraftVersion = ver; if (resetDirectory) { deleteExistingFiles(); } if (m_version.noConfigs) { downloadMods(); } else { installConfigs(); } } void PackInstallTask::onDownloadFailed(QString reason) { qDebug() << "PackInstallTask::onDownloadFailed:" << QThread::currentThreadId(); jobPtr.reset(); emitFailed(std::move(reason)); } void PackInstallTask::onDownloadAborted() { jobPtr.reset(); emitAborted(); } void PackInstallTask::deleteExistingFiles() { setStatus(tr("Deleting existing files...")); // Setup defaults, as per https://wiki.atlauncher.com/pack-admin/xml/delete VersionDeletes deletes; deletes.folders.append(VersionDelete{ "root", "mods%s%" }); deletes.folders.append(VersionDelete{ "root", "configs%s%" }); deletes.folders.append(VersionDelete{ "root", "bin%s%" }); // Setup defaults, as per https://wiki.atlauncher.com/pack-admin/xml/keep VersionKeeps keeps; keeps.files.append(VersionKeep{ "root", "mods%s%PortalGunSounds.pak" }); keeps.folders.append(VersionKeep{ "root", "mods%s%rei_minimap%s%" }); keeps.folders.append(VersionKeep{ "root", "mods%s%VoxelMods%s%" }); keeps.files.append(VersionKeep{ "root", "config%s%NEI.cfg" }); keeps.files.append(VersionKeep{ "root", "options.txt" }); keeps.files.append(VersionKeep{ "root", "servers.dat" }); // Merge with version deletes and keeps for (const auto& item : m_version.deletes.files) { deletes.files.append(item); } for (const auto& item : m_version.deletes.folders) { deletes.folders.append(item); } for (const auto& item : m_version.keeps.files) { keeps.files.append(item); } for (const auto& item : m_version.keeps.folders) { keeps.folders.append(item); } auto getPathForBase = [this](const QString& base) { auto minecraftPath = FS::PathCombine(m_stagingPath, "minecraft"); if (base == "root") { return minecraftPath; } if (base == "config") { return FS::PathCombine(minecraftPath, "config"); } qWarning() << "Unrecognised base path" << base; return minecraftPath; }; auto convertToSystemPath = [](const QString& path) { auto t = path; t.replace("%s%", QDir::separator()); return t; }; auto shouldKeep = [keeps, getPathForBase, convertToSystemPath](const QString& fullPath) { if (std::ranges::any_of(keeps.files, [&fullPath, &getPathForBase, &convertToSystemPath](const auto& item) { auto basePath = getPathForBase(item.base); auto targetPath = convertToSystemPath(item.target); auto path = FS::PathCombine(basePath, targetPath); return fullPath == path; })) { return true; } if (std::ranges::any_of(keeps.folders, [&fullPath, &getPathForBase, &convertToSystemPath](const auto& item) { auto basePath = getPathForBase(item.base); auto targetPath = convertToSystemPath(item.target); auto path = FS::PathCombine(basePath, targetPath); return fullPath.startsWith(path); })) { return true; } return false; }; // Keep track of files to delete QSet filesToDelete; for (const auto& item : deletes.files) { auto basePath = getPathForBase(item.base); auto targetPath = convertToSystemPath(item.target); auto fullPath = FS::PathCombine(basePath, targetPath); if (shouldKeep(fullPath)) { continue; } filesToDelete.insert(fullPath); } for (const auto& item : deletes.folders) { auto basePath = getPathForBase(item.base); auto targetPath = convertToSystemPath(item.target); auto fullPath = FS::PathCombine(basePath, targetPath); QDirIterator it(fullPath, QDirIterator::Subdirectories); while (it.hasNext()) { auto path = it.next(); if (shouldKeep(path)) { continue; } filesToDelete.insert(path); } } // Delete the files for (const auto& item : filesToDelete) { FS::deletePath(item); } } QString PackInstallTask::getDirForModType(ModType type, const QString& raw) { switch (type) { // Mod types that can either be ignored at this stage, or ignored // completely. case ModType::Root: case ModType::Extract: case ModType::Decomp: case ModType::TexturePackExtract: case ModType::ResourcePackExtract: case ModType::MCPC: return Q_NULLPTR; case ModType::Forge: // Forge detection happens later on, if it cannot be detected it will // install a jarmod component. case ModType::Jar: return "jarmods"; case ModType::Mods: return "mods"; case ModType::Flan: return "Flan"; case ModType::Dependency: return FS::PathCombine("mods", m_version.minecraft); case ModType::Ic2Lib: return FS::PathCombine("mods", "ic2"); case ModType::DenLib: return FS::PathCombine("mods", "denlib"); case ModType::Coremods: return "coremods"; case ModType::Plugins: return "plugins"; case ModType::TexturePack: return "texturepacks"; case ModType::ResourcePack: return "resourcepacks"; case ModType::ShaderPack: return "shaderpacks"; case ModType::Millenaire: qWarning() << "Unsupported mod type: " + raw; return Q_NULLPTR; case ModType::Unknown: emitFailed(tr("Unknown mod type: %1").arg(raw)); return Q_NULLPTR; } return Q_NULLPTR; } QString PackInstallTask::getVersionForLoader(const QString& uid) { if (m_version.loader.recommended || m_version.loader.latest || m_version.loader.choose) { auto vlist = APPLICATION->metadataIndex()->get(uid); if (!vlist) { emitFailed(tr("Failed to get local metadata index for %1").arg(uid)); return Q_NULLPTR; } vlist->waitToLoad(); if (m_version.loader.recommended || m_version.loader.latest) { for (int i = 0; i < vlist->versions().size(); i++) { auto version = vlist->versions().at(i); auto reqs = version->requiredSet(); // filter by minecraft version, if the loader depends on a certain version. // not all mod loaders depend on a given Minecraft version, so we won't do this // filtering for those loaders. if (m_version.loader.type != "fabric") { auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Meta::Require& req) { return req.uid == "net.minecraft"; }); if (iter == reqs.end()) { continue; } if (iter->equalsVersion != m_version.minecraft) { continue; } } if (m_version.loader.recommended) { // first recommended build we find, we use. if (!version->isRecommended()) { continue; } } return version->descriptor(); } emitFailed(tr("Failed to find version for %1 loader").arg(m_version.loader.type)); return Q_NULLPTR; } if (m_version.loader.choose) { // Fabric Loader doesn't depend on a given Minecraft version. if (m_version.loader.type == "fabric") { return m_support->chooseVersion(vlist, Q_NULLPTR); } return m_support->chooseVersion(vlist, m_version.minecraft); } } if (m_version.loader.version == Q_NULLPTR || m_version.loader.version.isEmpty()) { emitFailed(tr("No loader version set for modpack!")); return Q_NULLPTR; } return m_version.loader.version; } QString PackInstallTask::detectLibrary(const VersionLibrary& library) { // Try to detect what the library is if (!library.server.isEmpty() && library.server.split("/").length() >= 3) { auto lastSlash = library.server.lastIndexOf("/"); auto locationAndVersion = library.server.mid(0, lastSlash); auto fileName = library.server.mid(lastSlash + 1); lastSlash = locationAndVersion.lastIndexOf("/"); auto location = locationAndVersion.mid(0, lastSlash); auto version = locationAndVersion.mid(lastSlash + 1); lastSlash = location.lastIndexOf("/"); auto group = location.mid(0, lastSlash).replace("/", "."); auto artefact = location.mid(lastSlash + 1); return group + ":" + artefact + ":" + version; } if (library.file.contains("-")) { auto lastSlash = library.file.lastIndexOf("-"); auto name = library.file.mid(0, lastSlash); auto version = library.file.mid(lastSlash + 1).remove(".jar"); if (name == QString("guava")) { return "com.google.guava:guava:" + version; } if (name == QString("commons-lang3")) { return "org.apache.commons:commons-lang3:" + version; } } return "org.multimc.atlauncher:" + library.md5 + ":1"; } bool PackInstallTask::createLibrariesComponent(const QString& instanceRoot, PackProfile* profile) { if (m_version.libraries.isEmpty()) { return true; } QList exempt; for (const auto& componentUid : componentsToInstall.keys()) { auto componentVersion = componentsToInstall.value(componentUid); if (componentVersion->data()) { for (const auto& library : componentVersion->data()->libraries) { GradleSpecifier lib(library->rawName()); exempt.append(lib); } } } if (minecraftVersion->data()) { for (const auto& library : minecraftVersion->data()->libraries) { GradleSpecifier lib(library->rawName()); exempt.append(lib); } } auto id = QUuid::createUuid().toString(QUuid::WithoutBraces); auto targetId = "org.multimc.atlauncher." + id; auto patchDir = FS::PathCombine(instanceRoot, "patches"); if (!FS::ensureFolderPathExists(patchDir)) { return false; } auto patchFileName = FS::PathCombine(patchDir, targetId + ".json"); auto f = std::make_shared(); f->name = m_pack_name + " " + m_version_name + " (libraries)"; const static QMap s_liteLoaderMap = { { "61179803bcd5fb7790789b790908663d", "1.12-SNAPSHOT" }, { "1420785ecbfed5aff4a586c5c9dd97eb", "1.12.2-SNAPSHOT" }, { "073f68e2fcb518b91fd0d99462441714", "1.6.2_03" }, { "10a15b52fc59b1bfb9c05b56de1097d6", "1.6.2_02" }, { "b52f90f08303edd3d4c374e268a5acf1", "1.6.2_04" }, { "ea747e24e03e24b7cad5bc8a246e0319", "1.6.2_01" }, { "55785ccc82c07ff0ba038fe24be63ea2", "1.7.10_01" }, { "63ada46e033d0cb6782bada09ad5ca4e", "1.7.10_04" }, { "7983e4b28217c9ae8569074388409c86", "1.7.10_03" }, { "c09882458d74fe0697c7681b8993097e", "1.7.10_02" }, { "db7235aefd407ac1fde09a7baba50839", "1.7.10_00" }, { "6e9028816027f53957bd8fcdfabae064", "1.8" }, { "5e732dc446f9fe2abe5f9decaec40cde", "1.10-SNAPSHOT" }, { "3a98b5ed95810bf164e71c1a53be568d", "1.11.2-SNAPSHOT" }, { "ba8e6285966d7d988a96496f48cbddaa", "1.8.9-SNAPSHOT" }, { "8524af3ac3325a82444cc75ae6e9112f", "1.11-SNAPSHOT" }, { "53639d52340479ccf206a04f5e16606f", "1.5.2_01" }, { "1fcdcf66ce0a0806b7ad8686afdce3f7", "1.6.4_00" }, { "531c116f71ae2b11033f9a11a0f8e668", "1.6.4_01" }, { "4009eeb99c9068f608d3483a6439af88", "1.7.2_03" }, { "66f343354b8417abce1a10d557d2c6e9", "1.7.2_04" }, { "ab554c21f28fbc4ae9b098bcb5f4cceb", "1.7.2_05" }, { "e1d76a05a3723920e2f80a5e66c45f16", "1.7.2_02" }, { "00318cb0c787934d523f63cdfe8ddde4", "1.9-SNAPSHOT" }, { "986fd1ee9525cb0dcab7609401cef754", "1.9.4-SNAPSHOT" }, { "571ad5e6edd5ff40259570c9be588bb5", "1.9.4" }, { "1cdd72f7232e45551f16cc8ffd27ccf3", "1.10.2-SNAPSHOT" }, { "8a7c21f32d77ee08b393dd3921ced8eb", "1.10.2" }, { "b9bef8abc8dc309069aeba6fbbe58980", "1.12.1-SNAPSHOT" } }; for (const auto& lib : m_version.libraries) { // If the library is LiteLoader, we need to ignore it and handle it separately. if (s_liteLoaderMap.contains(lib.md5)) { auto ver = getComponentVersion("com.mumfrey.liteloader", s_liteLoaderMap.value(lib.md5)); if (ver) { componentsToInstall.insert("com.mumfrey.liteloader", ver); continue; } } auto libName = detectLibrary(lib); GradleSpecifier libSpecifier(libName); bool libExempt = false; for (const auto& existingLib : exempt) { if (libSpecifier.matchName(existingLib)) { // If the pack specifies a newer version of the lib, use that! libExempt = Version(libSpecifier.version()) >= Version(existingLib.version()); } } if (libExempt) { continue; } auto library = std::make_shared(); library->setRawName(libName); switch (lib.download) { case DownloadType::Server: library->setAbsoluteUrl(BuildConfig.ATL_DOWNLOAD_SERVER_URL + lib.url); break; case DownloadType::Direct: library->setAbsoluteUrl(lib.url); break; case DownloadType::Browser: case DownloadType::Unknown: emitFailed(tr("Unknown or unsupported download type: %1").arg(lib.download_raw)); return false; } f->libraries.append(library); } if (f->libraries.isEmpty()) { return true; } QFile file(patchFileName); if (!file.open(QFile::WriteOnly)) { qCritical() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); return false; } file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); file.close(); profile->appendComponent(ComponentPtr{ new Component(profile, targetId, f) }); return true; } bool PackInstallTask::createPackComponent(const QString& instanceRoot, PackProfile* profile) { if (m_version.mainClass.mainClass.isEmpty() && m_version.extraArguments.arguments.isEmpty()) { return true; } auto mainClass = m_version.mainClass.mainClass; auto extraArguments = m_version.extraArguments.arguments; auto hasMainClassDepends = !m_version.mainClass.depends.isEmpty(); auto hasExtraArgumentsDepends = !m_version.extraArguments.depends.isEmpty(); if (hasMainClassDepends || hasExtraArgumentsDepends) { QSet mods; for (const auto& item : m_version.mods) { mods.insert(item.name); } if (hasMainClassDepends && !mods.contains(m_version.mainClass.depends)) { mainClass = ""; } if (hasExtraArgumentsDepends && !mods.contains(m_version.extraArguments.depends)) { extraArguments = ""; } } if (mainClass.isEmpty() && extraArguments.isEmpty()) { return true; } auto id = QUuid::createUuid().toString(QUuid::WithoutBraces); auto targetId = "org.multimc.atlauncher." + id; auto patchDir = FS::PathCombine(instanceRoot, "patches"); if (!FS::ensureFolderPathExists(patchDir)) { return false; } auto patchFileName = FS::PathCombine(patchDir, targetId + ".json"); QStringList mainClasses; QStringList tweakers; for (const auto& componentUid : componentsToInstall.keys()) { auto componentVersion = componentsToInstall.value(componentUid); if (componentVersion->data()) { if (componentVersion->data()->mainClass != QString("")) { mainClasses.append(componentVersion->data()->mainClass); } tweakers.append(componentVersion->data()->addTweakers); } } auto f = std::make_shared(); f->name = m_pack_name + " " + m_version_name; if (!mainClass.isEmpty() && !mainClasses.contains(mainClass)) { f->mainClass = mainClass; } // Parse out tweakers auto args = extraArguments.split(" "); QString previous; for (auto arg : args) { if (arg.startsWith("--tweakClass=") || previous == "--tweakClass") { auto tweakClass = arg.remove("--tweakClass="); if (tweakers.contains(tweakClass)) { continue; } f->addTweakers.append(tweakClass); } previous = arg; } if (f->mainClass == QString() && f->addTweakers.isEmpty()) { return true; } QFile file(patchFileName); if (!file.open(QFile::WriteOnly)) { qCritical() << "Error opening" << file.fileName() << "for writing:" << file.errorString(); return false; } file.write(OneSixVersionFormat::versionFileToJson(f).toJson()); file.close(); profile->appendComponent(ComponentPtr{ new Component(profile, targetId, f) }); return true; } void PackInstallTask::installConfigs() { qDebug() << "PackInstallTask::installConfigs:" << QThread::currentThreadId(); setStatus(tr("Downloading configs...")); jobPtr.reset(new NetJob(tr("Config download"), APPLICATION->network())); auto path = QString("Configs/%1/%2.zip").arg(m_pack_safe_name).arg(m_version_name); auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.zip").arg(m_pack_safe_name).arg(m_version_name); auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", path); entry->setStale(true); auto dl = Net::ApiDownload::makeCached(url, entry); if (!m_version.configs.sha1.isEmpty()) { dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, m_version.configs.sha1)); } jobPtr->addNetAction(dl); archivePath = entry->getFullPath(); connect(jobPtr.get(), &NetJob::succeeded, this, [this]() { abortable = false; jobPtr.reset(); extractConfigs(); }); connect(jobPtr.get(), &NetJob::failed, [this](QString reason) { abortable = false; jobPtr.reset(); emitFailed(std::move(reason)); }); connect(jobPtr.get(), &NetJob::progress, [this](qint64 current, qint64 total) { abortable = true; setProgress(current, total); }); connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); connect(jobPtr.get(), &NetJob::aborted, [this] { abortable = false; jobPtr.reset(); emitAborted(); }); jobPtr->start(); } void PackInstallTask::extractConfigs() { qDebug() << "PackInstallTask::extractConfigs:" << QThread::currentThreadId(); setStatus(tr("Extracting configs...")); QDir extractDir(m_stagingPath); m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload::of(MMCZip::extractDir), archivePath, extractDir.absolutePath() + "/minecraft"); connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, [this]() { downloadMods(); }); connect(&m_extractFutureWatcher, &QFutureWatcher::canceled, this, [this]() { emitAborted(); }); m_extractFutureWatcher.setFuture(m_extractFuture); } void PackInstallTask::downloadMods() { qDebug() << "PackInstallTask::installMods:" << QThread::currentThreadId(); QList optionalMods; for (const auto& mod : m_version.mods) { if (mod.optional) { optionalMods.push_back(mod); } } // Select optional mods, if pack contains any QList selectedMods; if (!optionalMods.isEmpty()) { setStatus(tr("Selecting optional mods...")); auto mods = m_support->chooseOptionalMods(m_version, optionalMods); if (!mods.has_value()) { emitAborted(); return; } selectedMods = mods.value(); } setStatus(tr("Downloading mods...")); jarmods.clear(); jobPtr.reset(new NetJob(tr("Mod download"), APPLICATION->network())); QList blockedMods; for (const auto& mod : m_version.mods) { // skip non-client mods if (!mod.client) { continue; } // skip optional mods that were not selected if (mod.optional && !selectedMods.contains(mod.name)) { continue; } QString url; switch (mod.download) { case DownloadType::Server: url = BuildConfig.ATL_DOWNLOAD_SERVER_URL + mod.url; break; case DownloadType::Browser: { blockedMods.append(mod); continue; } case DownloadType::Direct: url = mod.url; break; case DownloadType::Unknown: emitFailed(tr("Unknown download type: %1").arg(mod.download_raw)); return; } QFileInfo fileName(mod.file); auto cacheName = fileName.completeBaseName() + "-" + mod.md5 + "." + fileName.suffix(); if (mod.type == ModType::Extract || mod.type == ModType::TexturePackExtract || mod.type == ModType::ResourcePackExtract) { auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName); entry->setStale(true); modsToExtract.insert(entry->getFullPath(), mod); auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5)); } jobPtr->addNetAction(dl); } else if (mod.type == ModType::Decomp) { auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName); entry->setStale(true); modsToDecomp.insert(entry->getFullPath(), mod); auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5)); } jobPtr->addNetAction(dl); } else { auto relpath = getDirForModType(mod.type, mod.type_raw); if (relpath == Q_NULLPTR) { continue; } auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName); entry->setStale(true); auto dl = Net::ApiDownload::makeCached(url, entry); if (!mod.md5.isEmpty()) { dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, mod.md5)); } jobPtr->addNetAction(dl); auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file); if (mod.type == ModType::Forge) { auto ver = getComponentVersion("net.minecraftforge", mod.version); if (ver) { componentsToInstall.insert("net.minecraftforge", ver); continue; } qDebug() << "Jarmod: " + path; jarmods.push_back(path); } if (mod.type == ModType::Jar) { qDebug() << "Jarmod: " + path; jarmods.push_back(path); } // Download after Forge handling, to avoid downloading Forge twice. qDebug() << "Will download" << url << "to" << path; modsToCopy[entry->getFullPath()] = path; } } if (!blockedMods.isEmpty()) { QList mods; for (const auto& mod : blockedMods) { BlockedMod blockedMod; blockedMod.name = mod.file; blockedMod.websiteUrl = mod.url; blockedMod.hash = mod.md5; blockedMod.matched = false; blockedMod.localPath = ""; mods.append(blockedMod); } qWarning() << "Blocked mods found, displaying mod list"; BlockedModsDialog messageDialog(nullptr, tr("Blocked mods found"), tr("The following files are not available for download in third party launchers.
    " "You will need to manually download them and add them to the instance."), mods, "md5"); messageDialog.setModal(true); if (messageDialog.exec() != 0) { qDebug() << "Post dialog blocked mods list:" << mods; for (const auto& blocked : mods) { if (!blocked.matched) { qDebug() << blocked.name << "was not matched to a local file, skipping copy"; continue; } auto modIter = std::ranges::find_if(blockedMods, [blocked](const VersionMod& mod) { return mod.url == blocked.websiteUrl; }); if (modIter == blockedMods.end()) { continue; } const auto& mod = *modIter; if (mod.type == ModType::Extract || mod.type == ModType::TexturePackExtract || mod.type == ModType::ResourcePackExtract) { modsToExtract.insert(blocked.localPath, mod); } else if (mod.type == ModType::Decomp) { modsToDecomp.insert(blocked.localPath, mod); } else { auto relpath = getDirForModType(mod.type, mod.type_raw); if (relpath == Q_NULLPTR) { continue; } auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file); if (mod.type == ModType::Forge) { auto ver = getComponentVersion("net.minecraftforge", mod.version); if (ver) { componentsToInstall.insert("net.minecraftforge", ver); continue; } qDebug() << "Jarmod: " + path; jarmods.push_back(path); } if (mod.type == ModType::Jar) { qDebug() << "Jarmod: " + path; jarmods.push_back(path); } modsToCopy[blocked.localPath] = path; } } } else { emitFailed(tr("Unknown download type: %1").arg("browser")); return; } } connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModsDownloaded); connect(jobPtr.get(), &NetJob::progress, [this](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); abortable = true; setProgress(current, total); }); connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress); connect(jobPtr.get(), &NetJob::aborted, &PackInstallTask::emitAborted); connect(jobPtr.get(), &NetJob::failed, &PackInstallTask::emitFailed); jobPtr->start(); } void PackInstallTask::onModsDownloaded() { abortable = false; qDebug() << "PackInstallTask::onModsDownloaded:" << QThread::currentThreadId(); jobPtr.reset(); if (!modsToExtract.empty() || !modsToDecomp.empty() || !modsToCopy.empty()) { m_modExtractFuture = QtConcurrent::run(QThreadPool::globalInstance(), &PackInstallTask::extractMods, this, modsToExtract, modsToDecomp, modsToCopy); connect(&m_modExtractFutureWatcher, &QFutureWatcher::finished, this, &PackInstallTask::onModsExtracted); connect(&m_modExtractFutureWatcher, &QFutureWatcher::canceled, this, &PackInstallTask::emitAborted); m_modExtractFutureWatcher.setFuture(m_modExtractFuture); } else { install(); } } void PackInstallTask::onModsExtracted() { qDebug() << "PackInstallTask::onModsExtracted:" << QThread::currentThreadId(); if (m_modExtractFuture.result()) { install(); } else { emitFailed(tr("Failed to extract mods...")); } } bool PackInstallTask::extractMods(const QMap& toExtract, const QMap& toDecomp, const QMap& toCopy) { qDebug() << "PackInstallTask::extractMods:" << QThread::currentThreadId(); setStatus(tr("Extracting mods...")); for (auto iter = toExtract.begin(); iter != toExtract.end(); iter++) { const auto& modPath = iter.key(); const auto& mod = iter.value(); QString extractToDir; if (mod.type == ModType::Extract) { extractToDir = getDirForModType(mod.extractTo, mod.extractTo_raw); } else if (mod.type == ModType::TexturePackExtract) { extractToDir = FS::PathCombine("texturepacks", "extracted"); } else if (mod.type == ModType::ResourcePackExtract) { extractToDir = FS::PathCombine("resourcepacks", "extracted"); } QDir extractDir(m_stagingPath); auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir); QString folderToExtract = ""; if (mod.type == ModType::Extract) { folderToExtract = mod.extractFolder; static const QRegularExpression s_regex("^/"); folderToExtract.remove(s_regex); if (isPathTraversal(extractToPath, folderToExtract)) { qWarning() << "Blocked path traversal in" << mod.extractFolder; return false; } } qDebug() << "Extracting " + mod.file + " to " + extractToDir; if (!MMCZip::extractDir(modPath, folderToExtract, extractToPath)) { // assume error return false; } } for (auto iter = toDecomp.begin(); iter != toDecomp.end(); iter++) { const auto& modPath = iter.key(); const auto& mod = iter.value(); auto extractToDir = getDirForModType(mod.decompType, mod.decompType_raw); QDir extractDir(m_stagingPath); auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir, mod.decompFile); if (isPathTraversal(extractToPath, mod.decompFile)) { qWarning() << "Blocked path traversal in decompFile" << mod.decompFile; return false; } qDebug() << "Extracting " + mod.decompFile + " to " + extractToDir; if (!MMCZip::extractFile(modPath, mod.decompFile, extractToPath)) { qWarning() << "Failed to extract" << mod.decompFile; return false; } } for (auto iter = toCopy.begin(); iter != toCopy.end(); iter++) { const auto& from = iter.key(); const auto& to = iter.value(); // If the file already exists, assume the mod is the correct copy - and remove // the copy from the Configs.zip QFileInfo fileInfo(to); if (fileInfo.exists()) { if (!FS::deletePath(to)) { qWarning() << "Failed to delete" << to; return false; } } FS::copy fileCopyOperation(from, to); if (!fileCopyOperation()) { qWarning() << "Failed to copy" << from << "to" << to; return false; } } return true; } void PackInstallTask::install() { qDebug() << "PackInstallTask::install:" << QThread::currentThreadId(); setStatus(tr("Installing modpack")); auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); MinecraftInstance instance(m_globalSettings, std::make_unique(instanceConfigPath), m_stagingPath); { SettingsObject::Lock lock(instance.settings()); auto* components = instance.getPackProfile(); components->buildingFromScratch(); // Use a component to add libraries BEFORE Minecraft if (!createLibrariesComponent(instance.instanceRoot(), components)) { emitFailed(tr("Failed to create libraries component")); return; } // Minecraft components->setComponentVersion("net.minecraft", m_version.minecraft, true); // Loader if (m_version.loader.type == QString("forge")) { auto version = getVersionForLoader("net.minecraftforge"); if (version == Q_NULLPTR) { return; } components->setComponentVersion("net.minecraftforge", version); } else if (m_version.loader.type == QString("neoforge")) { auto version = getVersionForLoader("net.neoforged"); if (version == Q_NULLPTR) { return; } components->setComponentVersion("net.neoforged", version); } else if (m_version.loader.type == QString("fabric")) { auto version = getVersionForLoader("net.fabricmc.fabric-loader"); if (version == Q_NULLPTR) { return; } components->setComponentVersion("net.fabricmc.fabric-loader", version); } else if (m_version.loader.type != QString()) { emitFailed(tr("Unknown loader type: ") + m_version.loader.type); return; } for (const auto& componentUid : componentsToInstall.keys()) { auto version = componentsToInstall.value(componentUid); components->setComponentVersion(componentUid, version->version()); } components->installJarMods(jarmods); // Use a component to fill in the rest of the data // todo: use more detection if (!createPackComponent(instance.instanceRoot(), components)) { emitFailed(tr("Failed to create pack component")); return; } components->saveNow(); instance.setName(name()); instance.setIconKey(m_instIcon); instance.setManagedPack("atlauncher", m_pack_safe_name, m_pack_name, m_version_name, m_version_name); jarmods.clear(); } emitSucceeded(); } } // namespace ATLauncher PrismLauncher-11.0.3/launcher/modplatform/atlauncher/ATLPackIndex.cpp0000644000175100017510000000347515224505336025162 0ustar runnerrunner/* * Copyright 2020-2021 Jamie Mansfield * Copyright 2021 Petr Mrazek * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ATLPackIndex.h" #include #include "Json.h" static void loadIndexedVersion(ATLauncher::IndexedVersion& v, QJsonObject& obj) { v.version = Json::requireString(obj, "version"); v.minecraft = Json::requireString(obj, "minecraft"); } void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack& m, QJsonObject& obj) { m.id = Json::requireInteger(obj, "id"); m.position = Json::requireInteger(obj, "position"); m.name = Json::requireString(obj, "name"); m.type = Json::requireString(obj, "type") == "private" ? ATLauncher::PackType::Private : ATLauncher::PackType::Public; auto versionsArr = Json::requireArray(obj, "versions"); for (const auto versionRaw : versionsArr) { auto versionObj = Json::requireObject(versionRaw); ATLauncher::IndexedVersion version; loadIndexedVersion(version, versionObj); m.versions.append(version); } m.system = obj["system"].toBool(); m.description = obj["description"].toString(""); static const QRegularExpression s_regex("[^A-Za-z0-9]"); m.safeName = Json::requireString(obj, "name").replace(s_regex, "").toLower() + ".png"; } PrismLauncher-11.0.3/launcher/modplatform/atlauncher/ATLPackManifest.h0000644000175100017510000000776115224505336025330 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2020 Jamie Mansfield * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include namespace ATLauncher { enum class PackType { Public, Private }; enum class ModType { Root, Forge, Jar, Mods, Flan, Dependency, Ic2Lib, DenLib, Coremods, MCPC, Plugins, Extract, Decomp, TexturePack, ResourcePack, ShaderPack, TexturePackExtract, ResourcePackExtract, Millenaire, Unknown }; enum class DownloadType { Server, Browser, Direct, Unknown }; struct VersionLoader { QString type; bool latest; bool recommended; bool choose; QString version; }; struct VersionLibrary { QString url; QString file; QString server; QString md5; DownloadType download; QString download_raw; }; struct VersionMod { QString name; QString version; QString url; QString file; QString md5; DownloadType download; QString download_raw; ModType type; QString type_raw; ModType extractTo; QString extractTo_raw; QString extractFolder; ModType decompType; QString decompType_raw; QString decompFile; QString description; bool optional; bool recommended; bool selected; bool hidden; bool library; QString group; QStringList depends; QString colour; QString warning; bool client; // computed bool effectively_hidden; }; struct VersionConfigs { int filesize; QString sha1; }; struct VersionMessages { QString install; QString update; }; struct VersionKeep { QString base; QString target; }; struct VersionKeeps { QList files; QList folders; }; struct VersionDelete { QString base; QString target; }; struct VersionDeletes { QList files; QList folders; }; struct PackVersionMainClass { QString mainClass; QString depends; }; struct PackVersionExtraArguments { QString arguments; QString depends; }; struct PackVersion { QString version; QString minecraft; bool noConfigs; PackVersionMainClass mainClass; PackVersionExtraArguments extraArguments; VersionLoader loader; QList libraries; QList mods; VersionConfigs configs; QMap colours; QMap warnings; VersionMessages messages; VersionKeeps keeps; VersionDeletes deletes; }; void loadVersion(PackVersion& v, QJsonObject& obj); } // namespace ATLauncher PrismLauncher-11.0.3/launcher/modplatform/ftb/0000755000175100017510000000000015224505336020663 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/modplatform/ftb/FTBPackManifest.h0000644000175100017510000000663115224505336023743 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2020-2021 Jamie Mansfield * Copyright 2020 Petr Mrazek * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include namespace FTB { struct Specs { int id; int minimum; int recommended; }; struct Tag { int id; QString name; }; struct Art { int id; QString url; QString type; int width; int height; bool compressed; QString sha1; int size; int64_t updated; }; struct Author { int id; QString name; QString type; QString website; int64_t updated; }; struct VersionInfo { int id; QString name; QString type; int64_t updated; Specs specs; }; struct Modpack { int id; QString name; QString synopsis; QString description; QString type; bool featured; int installs; int plays; int64_t updated; int64_t refreshed; QVector art; QVector authors; QVector versions; QVector tags; QString safeName; }; struct VersionTarget { int id; QString type; QString name; QString version; int64_t updated; }; struct VersionFileCurseForge { int project_id; int file_id; }; struct VersionFile { int id; QString type; QString path; QString name; QString version; QString url; QString sha1; int size; bool clientOnly; bool serverOnly; bool optional; int64_t updated; VersionFileCurseForge curseforge; }; struct Version { int id; int parent; QString name; QString type; int installs; int plays; int64_t updated; int64_t refreshed; Specs specs; QVector targets; QVector files; }; struct VersionChangelog { QString content; int64_t updated; }; void loadModpack(Modpack& m, QJsonObject& obj); void loadVersion(Version& m, QJsonObject& obj); } // namespace FTB Q_DECLARE_METATYPE(FTB::Modpack) PrismLauncher-11.0.3/launcher/modplatform/ftb/FTBPackInstallTask.h0000644000175100017510000000573215224505336024427 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2020-2021 Jamie Mansfield * Copyright 2020-2021 Petr Mrazek * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "FTBPackManifest.h" #include "InstanceTask.h" #include "QObjectPtr.h" #include "modplatform/flame/FileResolvingTask.h" #include "net/NetJob.h" #include "ui/dialogs/BlockedModsDialog.h" #include namespace FTB { class PackInstallTask final : public InstanceTask { Q_OBJECT public: explicit PackInstallTask(Modpack pack, QString version, QWidget* parent = nullptr); ~PackInstallTask() override = default; bool abort() override; protected: void executeTask() override; private slots: void onManifestDownloadSucceeded(QByteArray* responsePtr); void onResolveModsSucceeded(); void onCreateInstanceSucceeded(); void onModDownloadSucceeded(); void onManifestDownloadFailed(QString reason); void onResolveModsFailed(QString reason); void onCreateInstanceFailed(QString reason); void onModDownloadFailed(QString reason); private: void resolveMods(); void createInstance(); void downloadPack(); void copyBlockedMods(); private: NetJob::Ptr m_net_job = nullptr; shared_qobject_ptr m_modIdResolverTask = nullptr; QList m_fileIds; Modpack m_pack; QString m_versionName; Version m_version; QMap m_filesToCopy; QList m_blockedMods; // FIXME: nuke QWidget* m_parent; }; } // namespace FTB PrismLauncher-11.0.3/launcher/modplatform/ftb/FTBPackManifest.cpp0000644000175100017510000001575215224505336024302 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2020 Jamie Mansfield * Copyright 2020-2021 Petr Mrazek * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FTBPackManifest.h" #include "Json.h" static void loadSpecs(FTB::Specs& s, QJsonObject& obj) { s.id = Json::requireInteger(obj, "id"); s.minimum = Json::requireInteger(obj, "minimum"); s.recommended = Json::requireInteger(obj, "recommended"); } static void loadTag(FTB::Tag& t, QJsonObject& obj) { t.id = Json::requireInteger(obj, "id"); t.name = Json::requireString(obj, "name"); } static void loadArt(FTB::Art& a, QJsonObject& obj) { a.id = Json::requireInteger(obj, "id"); a.url = Json::requireString(obj, "url"); a.type = Json::requireString(obj, "type"); a.width = Json::requireInteger(obj, "width"); a.height = Json::requireInteger(obj, "height"); a.compressed = Json::requireBoolean(obj, "compressed"); a.sha1 = Json::requireString(obj, "sha1"); a.size = obj["size"].toInt(); a.updated = Json::requireInteger(obj, "updated"); } static void loadAuthor(FTB::Author& a, QJsonObject& obj) { a.id = Json::requireInteger(obj, "id"); a.name = Json::requireString(obj, "name"); a.type = Json::requireString(obj, "type"); a.website = Json::requireString(obj, "website"); a.updated = Json::requireInteger(obj, "updated"); } static void loadVersionInfo(FTB::VersionInfo& v, QJsonObject& obj) { v.id = Json::requireInteger(obj, "id"); v.name = Json::requireString(obj, "name"); v.type = Json::requireString(obj, "type"); v.updated = Json::requireInteger(obj, "updated"); auto specs = Json::requireObject(obj, "specs"); loadSpecs(v.specs, specs); } void FTB::loadModpack(FTB::Modpack& m, QJsonObject& obj) { m.id = Json::requireInteger(obj, "id"); m.name = Json::requireString(obj, "name"); m.safeName = Json::requireString(obj, "name").replace(QRegularExpression("[^A-Za-z0-9]"), "").toLower() + ".png"; m.synopsis = Json::requireString(obj, "synopsis"); m.description = Json::requireString(obj, "description"); m.type = Json::requireString(obj, "type"); m.featured = Json::requireBoolean(obj, "featured"); m.installs = Json::requireInteger(obj, "installs"); m.plays = Json::requireInteger(obj, "plays"); m.updated = Json::requireInteger(obj, "updated"); m.refreshed = obj["refreshed"].toInt(); auto artArr = Json::requireArray(obj, "art"); for (QJsonValueRef artRaw : artArr) { auto artObj = Json::requireObject(artRaw); FTB::Art art; loadArt(art, artObj); m.art.append(art); } auto authorArr = Json::requireArray(obj, "authors"); for (QJsonValueRef authorRaw : authorArr) { auto authorObj = Json::requireObject(authorRaw); FTB::Author author; loadAuthor(author, authorObj); m.authors.append(author); } auto versionArr = Json::requireArray(obj, "versions"); for (QJsonValueRef versionRaw : versionArr) { auto versionObj = Json::requireObject(versionRaw); FTB::VersionInfo version; loadVersionInfo(version, versionObj); m.versions.append(version); } auto tagArr = Json::requireArray(obj, "tags"); for (QJsonValueRef tagRaw : tagArr) { auto tagObj = Json::requireObject(tagRaw); FTB::Tag tag; loadTag(tag, tagObj); m.tags.append(tag); } m.updated = Json::requireInteger(obj, "updated"); } static void loadVersionTarget(FTB::VersionTarget& a, QJsonObject& obj) { a.id = Json::requireInteger(obj, "id"); a.name = Json::requireString(obj, "name"); a.type = Json::requireString(obj, "type"); a.version = Json::requireString(obj, "version"); a.updated = Json::requireInteger(obj, "updated"); } static void loadVersionFile(FTB::VersionFile& a, QJsonObject& obj) { a.id = Json::requireInteger(obj, "id"); a.type = Json::requireString(obj, "type"); a.path = Json::requireString(obj, "path"); a.name = Json::requireString(obj, "name"); a.version = Json::requireString(obj, "version"); a.url = obj["url"].toString(); // optional a.sha1 = Json::requireString(obj, "sha1"); a.size = obj["size"].toInt(); a.clientOnly = Json::requireBoolean(obj, "clientonly"); a.serverOnly = Json::requireBoolean(obj, "serveronly"); a.optional = Json::requireBoolean(obj, "optional"); a.updated = Json::requireInteger(obj, "updated"); auto curseforgeObj = obj["curseforge"].toObject(); // optional a.curseforge.project_id = curseforgeObj["project"].toInt(); a.curseforge.file_id = curseforgeObj["file"].toInt(); } void FTB::loadVersion(FTB::Version& m, QJsonObject& obj) { m.id = Json::requireInteger(obj, "id"); m.parent = Json::requireInteger(obj, "parent"); m.name = Json::requireString(obj, "name"); m.type = Json::requireString(obj, "type"); m.installs = Json::requireInteger(obj, "installs"); m.plays = Json::requireInteger(obj, "plays"); m.updated = Json::requireInteger(obj, "updated"); m.refreshed = obj["refreshed"].toInt(); auto specs = Json::requireObject(obj, "specs"); loadSpecs(m.specs, specs); auto targetArr = Json::requireArray(obj, "targets"); for (QJsonValueRef targetRaw : targetArr) { auto versionObj = Json::requireObject(targetRaw); FTB::VersionTarget target; loadVersionTarget(target, versionObj); m.targets.append(target); } auto fileArr = Json::requireArray(obj, "files"); for (QJsonValueRef fileRaw : fileArr) { auto fileObj = Json::requireObject(fileRaw); FTB::VersionFile file; loadVersionFile(file, fileObj); m.files.append(file); } } PrismLauncher-11.0.3/launcher/modplatform/ftb/FTBPackInstallTask.cpp0000644000175100017510000003151215224505336024755 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 flowln * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2020-2021 Jamie Mansfield * Copyright 2020-2021 Petr Mrazek * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FTBPackInstallTask.h" #include "FileSystem.h" #include "Json.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "modplatform/flame/FileResolvingTask.h" #include "modplatform/flame/PackManifest.h" #include "net/ChecksumValidator.h" #include "settings/INISettingsObject.h" #include "Application.h" #include "BuildConfig.h" #include "ui/dialogs/BlockedModsDialog.h" namespace FTB { PackInstallTask::PackInstallTask(Modpack pack, QString version, QWidget* parent) : m_pack(std::move(pack)), m_versionName(std::move(version)), m_parent(parent) {} bool PackInstallTask::abort() { if (!canAbort()) return false; bool aborted = true; if (m_net_job) aborted &= m_net_job->abort(); if (m_modIdResolverTask) aborted &= m_modIdResolverTask->abort(); return aborted ? InstanceTask::abort() : false; } void PackInstallTask::executeTask() { setStatus(tr("Getting the manifest...")); setAbortable(false); // Find pack version auto version_it = std::find_if(m_pack.versions.constBegin(), m_pack.versions.constEnd(), [this](const FTB::VersionInfo& a) { return a.name == m_versionName; }); if (version_it == m_pack.versions.constEnd()) { emitFailed(tr("Failed to find pack version %1").arg(m_versionName)); return; } auto version = *version_it; auto netJob = makeShared("FTB::VersionFetch", APPLICATION->network()); auto searchUrl = QString(BuildConfig.FTB_API_BASE_URL + "/modpack/%1/%2").arg(m_pack.id).arg(version.id); auto [action, response] = Net::Download::makeByteArray(QUrl(searchUrl)); netJob->addNetAction(action); QObject::connect(netJob.get(), &NetJob::succeeded, this, [this, response] { onManifestDownloadSucceeded(response); }); QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onManifestDownloadFailed); QObject::connect(netJob.get(), &NetJob::aborted, this, &PackInstallTask::abort); QObject::connect(netJob.get(), &NetJob::progress, this, &PackInstallTask::setProgress); m_net_job = netJob; setAbortable(true); netJob->start(); } void PackInstallTask::onManifestDownloadSucceeded(QByteArray* responsePtr) { // NOTE(TheKodeToad): moving the response out to avoid it from being destroyed by m_net_job.reset() QByteArray response = std::move(*responsePtr); m_net_job.reset(); QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from FTB at " << parse_error.offset << " reason: " << parse_error.errorString(); qWarning() << response; return; } FTB::Version version; try { auto obj = Json::requireObject(doc); FTB::loadVersion(version, obj); } catch (const JSONValidationError& e) { emitFailed(tr("Could not understand pack manifest:\n") + e.cause()); return; } m_version = version; resolveMods(); } void PackInstallTask::resolveMods() { setStatus(tr("Resolving mods...")); setAbortable(false); setProgress(0, 100); m_fileIds.clear(); Flame::Manifest manifest; for (const auto& file : m_version.files) { if (!file.serverOnly && file.url.isEmpty()) { if (file.curseforge.file_id <= 0) { emitFailed(tr("Invalid manifest: There's no information available to download the file '%1'!").arg(file.name)); return; } Flame::File flameFile; flameFile.projectId = file.curseforge.project_id; flameFile.fileId = file.curseforge.file_id; manifest.files.insert(flameFile.fileId, flameFile); m_fileIds.append(flameFile.fileId); } else { m_fileIds.append(-1); } } m_modIdResolverTask.reset(new Flame::FileResolvingTask(manifest)); connect(m_modIdResolverTask.get(), &Flame::FileResolvingTask::succeeded, this, &PackInstallTask::onResolveModsSucceeded); connect(m_modIdResolverTask.get(), &Flame::FileResolvingTask::failed, this, &PackInstallTask::onResolveModsFailed); connect(m_modIdResolverTask.get(), &Flame::FileResolvingTask::aborted, this, &PackInstallTask::abort); connect(m_modIdResolverTask.get(), &Flame::FileResolvingTask::progress, this, &PackInstallTask::setProgress); setAbortable(true); m_modIdResolverTask->start(); } void PackInstallTask::onResolveModsSucceeded() { auto anyBlocked = false; Flame::Manifest results = m_modIdResolverTask->getResults(); for (int index = 0; index < m_fileIds.size(); index++) { const auto file_id = m_fileIds.at(index); if (file_id < 0) continue; Flame::File resultsFile = results.files[file_id]; VersionFile& localFile = m_version.files[index]; // First check for blocked mods if (resultsFile.version.downloadUrl.isEmpty()) { BlockedMod blocked_mod; blocked_mod.name = resultsFile.version.fileName; blocked_mod.websiteUrl = QString("%1/download/%2").arg(resultsFile.pack.websiteUrl, QString::number(resultsFile.fileId)); blocked_mod.hash = resultsFile.version.hash; blocked_mod.matched = false; blocked_mod.localPath = ""; blocked_mod.targetFolder = resultsFile.targetFolder; m_blockedMods.append(blocked_mod); anyBlocked = true; } else { localFile.url = resultsFile.version.downloadUrl; } } m_modIdResolverTask.reset(); if (anyBlocked) { qDebug() << "Blocked files found, displaying file list"; BlockedModsDialog message_dialog(m_parent, tr("Blocked files found"), tr("The following files are not available for download in third party launchers.
    " "You will need to manually download them and add them to the instance."), m_blockedMods); message_dialog.setModal(true); if (message_dialog.exec() == QDialog::Accepted) { qDebug() << "Post dialog blocked mods list: " << m_blockedMods; createInstance(); } else { abort(); } } else { createInstance(); } } void PackInstallTask::createInstance() { setAbortable(false); setStatus(tr("Creating the instance...")); QCoreApplication::processEvents(); auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_unique(instanceConfigPath); MinecraftInstance instance(m_globalSettings, std::move(instanceSettings), m_stagingPath); auto components = instance.getPackProfile(); components->buildingFromScratch(); for (auto target : m_version.targets) { if (target.type == "game" && target.name == "minecraft") { components->setComponentVersion("net.minecraft", target.version, true); break; } } for (auto target : m_version.targets) { if (target.type != "modloader") continue; if (target.name == "forge") { components->setComponentVersion("net.minecraftforge", target.version); } else if (target.name == "fabric") { components->setComponentVersion("net.fabricmc.fabric-loader", target.version); } else if (target.name == "neoforge") { components->setComponentVersion("net.neoforged", target.version); } else if (target.name == "quilt") { components->setComponentVersion("org.quiltmc.quilt-loader", target.version); } } // install any jar mods QDir jarModsDir(FS::PathCombine(m_stagingPath, "minecraft", "jarmods")); if (jarModsDir.exists()) { QStringList jarMods; for (const auto& info : jarModsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) { jarMods.push_back(info.absoluteFilePath()); } components->installJarMods(jarMods); } components->saveNow(); instance.setName(name()); instance.setIconKey(m_instIcon); instance.setManagedPack("ftb", QString::number(m_pack.id), m_pack.name, QString::number(m_version.id), m_version.name); instance.saveNow(); onCreateInstanceSucceeded(); } void PackInstallTask::onCreateInstanceSucceeded() { downloadPack(); } void PackInstallTask::downloadPack() { setStatus(tr("Downloading mods...")); setAbortable(false); auto jobPtr = makeShared(tr("Mod download"), APPLICATION->network()); for (const auto& file : m_version.files) { if (file.serverOnly || file.url.isEmpty()) continue; auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path, file.name); qDebug() << "Will try to download" << file.url << "to" << path; QFileInfo file_info(file.name); auto dl = Net::Download::makeFile(file.url, path); if (!file.sha1.isEmpty()) { dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.sha1)); } jobPtr->addNetAction(dl); } jobPtr->setMaxConcurrent(1); // FTB blocks multiple requests at a time connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModDownloadSucceeded); connect(jobPtr.get(), &NetJob::failed, this, &PackInstallTask::onModDownloadFailed); connect(jobPtr.get(), &NetJob::aborted, this, &PackInstallTask::abort); connect(jobPtr.get(), &NetJob::progress, this, &PackInstallTask::setProgress); m_net_job = jobPtr; setAbortable(true); jobPtr->start(); } void PackInstallTask::onModDownloadSucceeded() { m_net_job.reset(); if (!m_blockedMods.isEmpty()) { copyBlockedMods(); } emitSucceeded(); } void PackInstallTask::onManifestDownloadFailed(QString reason) { m_net_job.reset(); emitFailed(reason); } void PackInstallTask::onResolveModsFailed(QString reason) { m_net_job.reset(); emitFailed(reason); } void PackInstallTask::onCreateInstanceFailed(QString reason) { emitFailed(reason); } void PackInstallTask::onModDownloadFailed(QString reason) { m_net_job.reset(); emitFailed(reason); } /// @brief copy the matched blocked mods to the instance staging area void PackInstallTask::copyBlockedMods() { setStatus(tr("Copying Blocked Mods...")); setAbortable(false); int i = 0; int total = m_blockedMods.length(); setProgress(i, total); for (const auto& mod : m_blockedMods) { if (!mod.matched) { qDebug() << mod.name << "was not matched to a local file, skipping copy"; continue; } auto dest_path = FS::PathCombine(m_stagingPath, ".minecraft", mod.targetFolder, mod.name); setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total))); qDebug() << "Will try to copy" << mod.localPath << "to" << dest_path; if (!FS::copy(mod.localPath, dest_path)()) { qDebug() << "Copy of" << mod.localPath << "to" << dest_path << "Failed"; } i++; setProgress(i, total); } setAbortable(true); } } // namespace FTB PrismLauncher-11.0.3/launcher/modplatform/modrinth/0000755000175100017510000000000015224505336021734 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/modplatform/modrinth/ModrinthCheckUpdate.h0000644000175100017510000000160715224505336025776 0ustar runnerrunner#pragma once #include "modplatform/CheckUpdateTask.h" class ModrinthCheckUpdate : public CheckUpdateTask { Q_OBJECT public: ModrinthCheckUpdate(QList& resources, std::vector& mcVersions, QList loadersList, ResourceFolderModel* resourceModel); public slots: bool abort() override; protected slots: void executeTask() override; void getUpdateModsForLoader(std::optional loader = {}, bool forceModLoaderCheck = false); void checkVersionsResponse(QByteArray* response, std::optional loader); void checkNextLoader(); private: Task::Ptr m_job = nullptr; QHash m_mappings; QString m_hashType; int m_loaderIdx = 0; qsizetype m_initialSize = 0; }; PrismLauncher-11.0.3/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h0000644000175100017510000000321415224505336027666 0ustar runnerrunner#pragma once #include #include #include #include #include #include #include #include "BaseInstance.h" #include "InstanceCreationTask.h" class ModrinthCreationTask final : public InstanceCreationTask { Q_OBJECT struct File { QString path; QCryptographicHash::Algorithm hashAlgorithm; QByteArray hash; QQueue downloads; bool required = true; }; public: ModrinthCreationTask(const QString& stagingPath, SettingsObject* globalSettings, QWidget* parent, QString id, QString versionId = {}, QString originalInstanceId = {}) : m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(versionId)) { setStagingPath(stagingPath); setParentSettings(globalSettings); m_original_instance_id = std::move(originalInstanceId); } bool abort() override; bool updateInstance() override; std::unique_ptr createInstance() override; private: bool parseManifest(const QString&, std::vector&, bool setInternalData = true, bool showOptionalDialog = true); private: QWidget* m_parent = nullptr; QString m_minecraft_version, m_fabric_version, m_quilt_version, m_forge_version, m_neoForge_version; QString m_managed_id, m_managed_version_id, m_managed_name; std::vector m_files; Task::Ptr m_task; std::optional m_instance; QString m_root_path = "minecraft"; }; PrismLauncher-11.0.3/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp0000644000175100017510000004735315224505336030235 0ustar runnerrunner#include "ModrinthInstanceCreationTask.h" #include "Application.h" #include "FileSystem.h" #include "InstanceList.h" #include "Json.h" #include "QObjectPtr.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "minecraft/mod/Mod.h" #include "modplatform/EnsureMetadataTask.h" #include "modplatform/helpers/OverrideUtils.h" #include "net/ChecksumValidator.h" #include "net/ApiDownload.h" #include "net/ApiHeaderProxy.h" #include "net/NetJob.h" #include "modplatform/ModIndex.h" #include "settings/INISettingsObject.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/pages/modplatform/OptionalModDialog.h" #include #include #include #include bool ModrinthCreationTask::abort() { if (!canAbort()) { return false; } if (m_task) { m_task->abort(); } return InstanceCreationTask::abort(); } bool ModrinthCreationTask::updateInstance() { auto* instanceList = APPLICATION->instances(); // FIXME: How to handle situations when there's more than one install already for a given modpack? BaseInstance* inst = nullptr; if (auto originalId = originalInstanceID(); !originalId.isEmpty()) { inst = instanceList->getInstanceById(originalId); Q_ASSERT(inst); } else { inst = instanceList->getInstanceByManagedName(originalName()); if (!inst) { inst = instanceList->getInstanceById(originalName()); if (!inst) { return false; } } } QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json"); if (!parseManifest(indexPath, m_files, true, false)) { return false; } auto versionName = inst->getManagedPackVersionName(); m_root_path = QFileInfo(inst->gameRoot()).fileName(); auto versionStr = !versionName.isEmpty() ? tr(" (version %1)").arg(versionName) : ""; if (shouldConfirmUpdate()) { auto shouldUpdate = askIfShouldUpdate(m_parent, versionStr); if (shouldUpdate == ShouldUpdate::SkipUpdating) { return false; } if (shouldUpdate == ShouldUpdate::Cancel) { m_abort = true; return false; } } // Remove repeated files, we don't need to download them! QDir oldInstDir(inst->instanceRoot()); QString oldIndexFolder(FS::PathCombine(oldInstDir.absolutePath(), "mrpack")); QString oldIndexPath(FS::PathCombine(oldIndexFolder, "modrinth.index.json")); QFileInfo oldIndexFile(oldIndexPath); if (oldIndexFile.exists()) { std::vector oldFiles; parseManifest(oldIndexPath, oldFiles, false, false); // Let's remove all duplicated, identical resources! auto filesIterator = m_files.begin(); begin: while (filesIterator != m_files.end()) { const auto& file = *filesIterator; auto oldFilesIterator = oldFiles.begin(); while (oldFilesIterator != oldFiles.end()) { const auto& oldFile = *oldFilesIterator; if (oldFile.hash == file.hash) { qDebug() << "Removed file at" << file.path << "from list of downloads"; filesIterator = m_files.erase(filesIterator); oldFilesIterator = oldFiles.erase(oldFilesIterator); goto begin; // Sorry :c } oldFilesIterator++; } filesIterator++; } QDir oldMinecraftDir(inst->gameRoot()); // Some files were removed from the old version, and some will be downloaded in an updated version, // so we're fine removing them! if (!oldFiles.empty()) { for (const auto& file : oldFiles) { scheduleToDelete(m_parent, oldMinecraftDir, file.path, true); } } // We will remove all the previous overrides, to prevent duplicate files! // TODO: Currently 'overrides' will always override the stuff on update. How do we preserve unchanged overrides? // FIXME: We may want to do something about disabled mods. auto oldOverrides = Override::readOverrides("overrides", oldIndexFolder); for (const auto& entry : oldOverrides) { scheduleToDelete(m_parent, oldMinecraftDir, entry); } auto oldClientOverrides = Override::readOverrides("client-overrides", oldIndexFolder); for (const auto& entry : oldClientOverrides) { scheduleToDelete(m_parent, oldMinecraftDir, entry); } } else { // We don't have an old index file, so we may duplicate stuff! auto* dialog = CustomMessageBox::selectable(m_parent, tr("No index file."), tr("We couldn't find a suitable index file for the older version. This may cause some " "of the files to be duplicated. Do you want to continue?"), QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel); if (dialog->exec() == QDialog::DialogCode::Rejected) { m_abort = true; return false; } } setOverride(true, inst->id()); qDebug() << "Will override instance!"; m_instance = inst; // We let it go through the createInstance() stage, just with a couple modifications for updating return false; } // https://docs.modrinth.com/docs/modpacks/format_definition/ std::unique_ptr ModrinthCreationTask::createInstance() { QEventLoop loop; QString parentFolder(FS::PathCombine(m_stagingPath, "mrpack")); QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json"); if (m_files.empty() && !parseManifest(indexPath, m_files, true, true)) { return nullptr; } // Keep index file in case we need it some other time (like when changing versions) QString newIndexPlace(FS::PathCombine(parentFolder, "modrinth.index.json")); FS::ensureFilePathExists(newIndexPlace); FS::move(indexPath, newIndexPlace); auto mcPath = FS::PathCombine(m_stagingPath, m_root_path); auto overridePath = FS::PathCombine(m_stagingPath, "overrides"); if (QFile::exists(overridePath)) { // Create a list of overrides in "overrides.txt" inside mrpack/ Override::createOverrides("overrides", parentFolder, overridePath); // Apply the overrides if (!FS::move(overridePath, mcPath)) { setError(tr("Could not rename the overrides folder:\n") + "overrides"); return nullptr; } } // Do client overrides auto clientOverridePath = FS::PathCombine(m_stagingPath, "client-overrides"); if (QFile::exists(clientOverridePath)) { // Create a list of overrides in "client-overrides.txt" inside mrpack/ Override::createOverrides("client-overrides", parentFolder, clientOverridePath); // Apply the overrides if (!FS::overrideFolder(mcPath, clientOverridePath)) { setError(tr("Could not rename the client overrides folder:\n") + "client overrides"); return nullptr; } } QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_unique(configPath); auto instance = std::make_unique(m_globalSettings, std::move(instanceSettings), m_stagingPath); auto* components = instance->getPackProfile(); components->buildingFromScratch(); components->setComponentVersion("net.minecraft", m_minecraft_version, true); QString loader; if (!m_fabric_version.isEmpty()) { components->setComponentVersion("net.fabricmc.fabric-loader", m_fabric_version); loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::Fabric); } if (!m_quilt_version.isEmpty()) { components->setComponentVersion("org.quiltmc.quilt-loader", m_quilt_version); loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::Quilt); } if (!m_forge_version.isEmpty()) { components->setComponentVersion("net.minecraftforge", m_forge_version); loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::Forge); } if (!m_neoForge_version.isEmpty()) { components->setComponentVersion("net.neoforged", m_neoForge_version); loader = ModPlatform::getModLoaderAsString(ModPlatform::ModLoaderType::NeoForge); } if (m_instIcon != "default") { instance->setIconKey(m_instIcon); } else if (!m_managed_id.isEmpty()) { instance->setIconKey("modrinth"); } // Don't add managed info to packs without an ID (most likely imported from ZIP) if (!m_managed_id.isEmpty()) { instance->setManagedPack("modrinth", m_managed_id, m_managed_name, m_managed_version_id, version()); } else { instance->setManagedPack("modrinth", "", name(), "", ""); } instance->setName(name()); instance->saveNow(); auto downloadMods = makeShared(tr("Mod Download Modrinth"), APPLICATION->network()); auto rootModpackPath = FS::PathCombine(m_stagingPath, m_root_path); auto rootModpackUrl = QUrl::fromLocalFile(rootModpackPath); // TODO make this work with other sorts of resource QHash resources; for (auto& file : m_files) { auto fileName = file.path; fileName = FS::RemoveInvalidPathChars(fileName); auto filePath = FS::PathCombine(rootModpackPath, fileName); if (!rootModpackUrl.isParentOf(QUrl::fromLocalFile(filePath))) { // This means we somehow got out of the root folder, so abort here to prevent exploits setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.") .arg(fileName)); return nullptr; } if (fileName.startsWith("mods/")) { auto* mod = new Mod(filePath); ModDetails d; d.mod_id = filePath; mod->setDetails(d); resources[file.hash.toHex()] = mod; } if (file.downloads.empty()) { setError(tr("The file '%1' is missing a download link. This is invalid in the pack format.").arg(fileName)); return nullptr; } qDebug() << "Will try to download" << file.downloads.front() << "to" << filePath; Net::ModrinthDownloadMeta meta{ .reason = m_instance.has_value() ? "update" : "modpack", .gameVersion = m_minecraft_version, .loader = loader, }; QUrl downloadUrl = file.downloads.dequeue(); auto dl = Net::ApiDownload::makeFile(downloadUrl, filePath, Net::Download::Option::NoOptions, meta); dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); downloadMods->addNetAction(dl); if (!file.downloads.empty()) { // FIXME: This really needs to be put into a ConcurrentTask of // MultipleOptionsTask's , once those exist :) auto param = dl.toWeakRef(); connect(dl.get(), &Task::failed, [&file, filePath, param, downloadMods, meta] { QUrl fallbackUrl = file.downloads.dequeue(); auto ndl = Net::ApiDownload::makeFile(fallbackUrl, filePath, Net::Download::Option::NoOptions, meta); ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash)); downloadMods->addNetAction(ndl); if (auto shared = param.lock()) { shared->succeeded(); } }); } } bool endedWell = false; connect(downloadMods.get(), &NetJob::succeeded, this, [&endedWell]() { endedWell = true; }); connect(downloadMods.get(), &NetJob::failed, [this, &endedWell](const QString& reason) { endedWell = false; setError(reason); }); connect(downloadMods.get(), &NetJob::finished, &loop, &QEventLoop::quit); connect(downloadMods.get(), &NetJob::progress, [this](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); setProgress(current, total); }); connect(downloadMods.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propagateStepProgress); setStatus(tr("Downloading mods...")); downloadMods->start(); m_task = downloadMods; loop.exec(); if (!endedWell) { for (auto* resource : resources) { delete resource; } return nullptr; } QEventLoop ensureMetaLoop; QDir folder = FS::PathCombine(instance->modsRoot(), ".index"); auto ensureMetadataTask = makeShared(resources, folder, ModPlatform::ResourceProvider::MODRINTH); connect(ensureMetadataTask.get(), &Task::succeeded, this, [&endedWell]() { endedWell = true; }); connect(ensureMetadataTask.get(), &Task::finished, &ensureMetaLoop, &QEventLoop::quit); connect(ensureMetadataTask.get(), &Task::progress, [this](qint64 current, qint64 total) { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); setProgress(current, total); }); connect(ensureMetadataTask.get(), &Task::stepProgress, this, &ModrinthCreationTask::propagateStepProgress); ensureMetadataTask->start(); m_task = ensureMetadataTask; ensureMetaLoop.exec(); for (auto* resource : resources) { delete resource; } resources.clear(); // Update information of the already installed instance, if any. if (m_instance && endedWell) { setAbortable(false); auto* inst = m_instance.value(); // Only change the name if it didn't use a custom name, so that the previous custom name // is preserved, but if we're using the original one, we update the version string. // NOTE: This needs to come before the copyManagedPack call! if (inst->name().contains(inst->getManagedPackVersionName()) && inst->name() != instance->name()) { if (askForChangingInstanceName(m_parent, inst->name(), instance->name()) == InstanceNameChange::ShouldChange) { inst->setName(instance->name()); } } inst->copyManagedPack(*instance); } if (endedWell) { return instance; } return nullptr; } bool ModrinthCreationTask::parseManifest(const QString& indexPath, std::vector& files, bool setInternalData, bool showOptionalDialog) { try { auto doc = Json::requireDocument(indexPath); auto obj = Json::requireObject(doc, "modrinth.index.json"); int formatVersion = Json::requireInteger(obj, "formatVersion", "modrinth.index.json"); if (formatVersion == 1) { auto game = Json::requireString(obj, "game", "modrinth.index.json"); if (game != "minecraft") { throw JSONValidationError("Unknown game: " + game); } if (setInternalData) { if (m_managed_version_id.isEmpty()) { m_managed_version_id = obj["versionId"].toString(); } m_managed_name = obj["name"].toString(); } auto jsonFiles = Json::requireIsArrayOf(obj, "files", "modrinth.index.json"); std::vector optionalFiles; for (const auto& modInfo : jsonFiles) { File file; file.path = Json::requireString(modInfo, "path").replace("\\", "/"); auto env = modInfo["env"].toObject(); // 'env' field is optional if (!env.isEmpty()) { QString support = env["client"].toString("unsupported"); if (support == "unsupported") { continue; } if (support == "optional") { file.required = false; } } QJsonObject hashes = Json::requireObject(modInfo, "hashes"); file.hash = QByteArray::fromHex(Json::requireString(hashes, "sha512").toLatin1()); file.hashAlgorithm = QCryptographicHash::Sha512; // Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode // (as Modrinth seems to incorrectly handle spaces) auto downloadArr = modInfo["downloads"].toArray(); for (auto download : downloadArr) { qWarning() << download.toString(); bool isLast = download.toString() == downloadArr.last().toString(); auto downloadUrl = QUrl(download.toString()); if (!downloadUrl.isValid()) { qDebug() << QString("Download URL (%1) for %2 is not a correctly formatted URL").arg(downloadUrl.toString(), file.path); if (isLast && file.downloads.isEmpty()) { throw JSONValidationError(tr("Download URL for %1 is not a correctly formatted URL").arg(file.path)); } } else { file.downloads.push_back(downloadUrl); } } (file.required ? files : optionalFiles).push_back(file); } if (!optionalFiles.empty()) { if (showOptionalDialog) { QStringList oFiles; for (const auto& file : optionalFiles) { oFiles.push_back(file.path); } OptionalModDialog optionalModDialog(m_parent, oFiles); if (optionalModDialog.exec() == QDialog::Rejected) { emitAborted(); return false; } auto selectedMods = optionalModDialog.getResult(); for (auto file : optionalFiles) { if (selectedMods.contains(file.path)) { file.required = true; } else { file.path += ".disabled"; } files.push_back(file); } } else { for (auto file : optionalFiles) { file.path += ".disabled"; files.push_back(file); } } } if (setInternalData) { auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json"); for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) { QString name = it.key(); if (name == "minecraft") { m_minecraft_version = Json::requireString(*it, "Minecraft version"); } else if (name == "fabric-loader") { m_fabric_version = Json::requireString(*it, "Fabric Loader version"); } else if (name == "quilt-loader") { m_quilt_version = Json::requireString(*it, "Quilt Loader version"); } else if (name == "forge") { m_forge_version = Json::requireString(*it, "Forge version"); } else if (name == "neoforge") { m_neoForge_version = Json::requireString(*it, "NeoForge version"); } else { throw JSONValidationError("Unknown dependency type: " + name); } } } } else { throw JSONValidationError(QStringLiteral("Unknown format version: %s").arg(formatVersion)); } } catch (const JSONValidationError& e) { setError(tr("Could not understand pack index:\n") + e.cause()); return false; } return true; } PrismLauncher-11.0.3/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp0000644000175100017510000002217115224505336026330 0ustar runnerrunner#include "ModrinthCheckUpdate.h" #include "Application.h" #include "ModrinthAPI.h" #include "ModrinthPackIndex.h" #include "Json.h" #include "QObjectPtr.h" #include "ResourceDownloadTask.h" #include "modplatform/ModIndex.h" #include "modplatform/helpers/HashUtils.h" #include "tasks/ConcurrentTask.h" static const ModrinthAPI g_api; ModrinthCheckUpdate::ModrinthCheckUpdate(QList& resources, std::vector& mcVersions, QList loadersList, ResourceFolderModel* resourceModel) : CheckUpdateTask(resources, mcVersions, std::move(loadersList), resourceModel) , m_hashType(ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first()) { if (!m_loadersList.isEmpty()) { // this is for mods so append all the other posible loaders to the initial list m_initialSize = m_loadersList.length(); ModPlatform::ModLoaderTypes modLoaders; for (auto* m : resources) { modLoaders |= m->metadata()->loaders; } for (auto l : m_loadersList) { modLoaders &= ~static_cast(l); } m_loadersList.append(ModPlatform::modLoaderTypesToList(modLoaders)); } } bool ModrinthCheckUpdate::abort() { if (m_job) { return m_job->abort(); } return true; } /* Check for update: * - Get latest version available * - Compare hash of the latest version with the current hash * - If equal, no updates, else, there's updates, so add to the list * */ void ModrinthCheckUpdate::executeTask() { setStatus(tr("Preparing resources for Modrinth...")); setProgress(0, ((m_loadersList.isEmpty() ? 1 : m_loadersList.length()) * 2) + 1); auto hashingTask = makeShared("MakeModrinthHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); bool startHasing = false; for (auto* resource : m_resources) { auto hash = resource->metadata()->hash; // Sadly the API can only handle one hash type per call, se we // need to generate a new hash if the current one is innadequate // (though it will rarely happen, if at all) if (resource->metadata()->hash_format != m_hashType) { auto hashTask = Hashing::createHasher(resource->fileinfo().absoluteFilePath(), ModPlatform::ResourceProvider::MODRINTH); connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, resource](const QString& hash) { m_mappings.insert(hash, resource); }); connect(hashTask.get(), &Task::failed, [this] { failed("Failed to generate hash"); }); hashingTask->addTask(hashTask); startHasing = true; } else { m_mappings.insert(hash, resource); } } if (startHasing) { connect(hashingTask.get(), &Task::finished, this, &ModrinthCheckUpdate::checkNextLoader); m_job = hashingTask; hashingTask->start(); } else { checkNextLoader(); } } void ModrinthCheckUpdate::getUpdateModsForLoader(std::optional loader, bool forceModLoaderCheck) { m_loaderIdx++; setStatus(tr("Waiting for the API response from Modrinth...")); setProgress(m_progress + 1, m_progressTotal); QStringList hashes; if (forceModLoaderCheck && loader.has_value()) { for (const auto& hash : m_mappings.keys()) { if ((m_mappings.value(hash)->metadata()->loaders & loader.value()) != 0) { hashes.append(hash); } } } else { hashes = m_mappings.keys(); } if (hashes.isEmpty()) { checkNextLoader(); return; } auto [job, response] = g_api.latestVersions(hashes, m_hashType, m_gameVersions, loader); connect(job.get(), &Task::succeeded, this, [this, response, loader] { checkVersionsResponse(response, loader); }); connect(job.get(), &Task::failed, this, &ModrinthCheckUpdate::checkNextLoader); m_job = job; job->start(); } void ModrinthCheckUpdate::checkVersionsResponse(QByteArray* response, std::optional loader) { setStatus(tr("Parsing the API response from Modrinth...")); setProgress(m_progress + 1, m_progressTotal); QJsonParseError parseError{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError); if (parseError.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from ModrinthCheckUpdate at" << parseError.offset << "reason:" << parseError.errorString(); qWarning() << *response; emitFailed(parseError.errorString()); return; } try { auto iter = m_mappings.begin(); while (iter != m_mappings.end()) { const QString hash = iter.key(); Resource* resource = iter.value(); auto projectObj = doc[hash].toObject(); // If the returned project is empty, but we have Modrinth metadata, // it means this specific version is not available if (projectObj.isEmpty()) { qDebug() << "Mod" << m_mappings.find(hash).value()->name() << "got an empty response. Hash:" << hash; ++iter; continue; } // Sometimes a version may have multiple files, one with "forge" and one with "fabric", // so we may want to filter it QString loaderFilter; if (loader.has_value() && loader != 0) { auto modLoaders = ModPlatform::modLoaderTypesToList(*loader); if (!modLoaders.isEmpty()) { loaderFilter = ModPlatform::getModLoaderAsString(modLoaders.first()); } } // Currently, we rely on a couple heuristics to determine whether an update is actually available or not: // - The file needs to be preferred: It is either the primary file, or the one found via (explicit) usage of the // loader_filter // - The version reported by the JAR is different from the version reported by the indexed version (it's usually the case) // Such is the pain of having arbitrary files for a given version .-. auto projectVer = Modrinth::loadIndexedPackVersion(projectObj, m_hashType, loaderFilter); if (projectVer.downloadUrl.isEmpty()) { qCritical() << "Modrinth mod without download url!" << projectVer.fileName; ++iter; continue; } // Fake pack with the necessary info to pass to the download task :) auto pack = std::make_shared(); pack->name = resource->name(); pack->slug = resource->metadata()->slug; pack->addonId = resource->metadata()->project_id; pack->provider = ModPlatform::ResourceProvider::MODRINTH; if ((projectVer.hash != hash && projectVer.is_preferred) || (resource->status() == ResourceStatus::NOT_INSTALLED)) { auto downloadTask = makeShared(pack, projectVer, m_resourceModel, true, "update"); QString oldVersion = resource->metadata()->version_number; if (oldVersion.isEmpty()) { if (resource->status() == ResourceStatus::NOT_INSTALLED) { oldVersion = tr("Not installed"); } else { oldVersion = tr("Unknown"); } } m_updates.emplace_back(pack->name, hash, oldVersion, projectVer.version_number, projectVer.version_type, projectVer.changelog, ModPlatform::ResourceProvider::MODRINTH, downloadTask, resource->enabled()); } m_deps.append(std::make_shared(pack, projectVer)); iter = m_mappings.erase(iter); } } catch (Json::JsonException& e) { emitFailed(e.cause() + ": " + e.what()); return; } checkNextLoader(); } void ModrinthCheckUpdate::checkNextLoader() { if (m_mappings.isEmpty()) { emitSucceeded(); return; } if (m_loaderIdx < m_loadersList.size()) { // this are mods so check with loades getUpdateModsForLoader(m_loadersList.at(m_loaderIdx), m_loaderIdx > m_initialSize); return; } if (m_loadersList.isEmpty() && m_loaderIdx == 0) { // this are other resources no need to check more than once with empty loader getUpdateModsForLoader(); return; } for (auto* resource : m_mappings) { QString reason; if (dynamic_cast(resource) != nullptr) { reason = tr("No valid version found for this resource. It's probably unavailable for the current game " "version / mod loader."); } else { reason = tr("No valid version found for this resource. It's probably unavailable for the current game version."); } emit checkFailed(resource, reason); } emitSucceeded(); } PrismLauncher-11.0.3/launcher/modplatform/modrinth/ModrinthPackExportTask.h0000644000175100017510000000441015224505336026514 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include "BaseInstance.h" #include "MMCZip.h" #include "minecraft/MinecraftInstance.h" #include "modplatform/ModIndex.h" #include "modplatform/modrinth/ModrinthAPI.h" #include "tasks/Task.h" class ModrinthPackExportTask : public Task { Q_OBJECT public: ModrinthPackExportTask(const QString& name, const QString& version, const QString& summary, bool optionalFiles, BaseInstance* instance, const QString& output, MMCZip::FilterFileFunction filter); protected: void executeTask() override; bool abort() override; private: struct ResolvedFile { QString sha1, sha512, url; qint64 size; ModPlatform::Side side; }; static const QStringList PREFIXES; static const QStringList FILE_EXTENSIONS; // inputs const QString name, version, summary; const bool optionalFiles; const BaseInstance* instance; MinecraftInstance* mcInstance; const QDir gameRoot; const QString output; const MMCZip::FilterFileFunction filter; ModrinthAPI api; QFileInfoList files; QMap pendingHashes; QMap resolvedFiles; Task::Ptr task; void collectFiles(); void collectHashes(); void makeApiRequest(); void parseApiResponse(QByteArray* response); void buildZip(); QByteArray generateIndex(); }; PrismLauncher-11.0.3/launcher/modplatform/modrinth/ModrinthPackIndex.h0000644000175100017510000000214415224505336025461 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "modplatform/ModIndex.h" #include "BaseInstance.h" namespace Modrinth { void loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj); void loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& obj); auto loadIndexedPackVersion(QJsonObject& obj, const QString& preferred_hash_type = "sha512", const QString& preferred_file_name = "") -> ModPlatform::IndexedVersion; } // namespace Modrinth PrismLauncher-11.0.3/launcher/modplatform/modrinth/ModrinthPackIndex.cpp0000644000175100017510000002116715224505336026022 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ModrinthPackIndex.h" #include "FileSystem.h" #include "ModrinthAPI.h" #include "Json.h" #include "minecraft/MinecraftInstance.h" #include "minecraft/PackProfile.h" #include "modplatform/ModIndex.h" bool shouldDownloadOnSide(const QString& side) { return side == "required" || side == "optional"; } // https://docs.modrinth.com/api/operations/getproject/ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { pack.addonId = obj["project_id"].toString(); if (pack.addonId.toString().isEmpty()) { pack.addonId = Json::requireString(obj, "id"); } pack.provider = ModPlatform::ResourceProvider::MODRINTH; pack.name = Json::requireString(obj, "title"); pack.slug = obj["slug"].toString(""); if (!pack.slug.isEmpty()) { pack.websiteUrl = "https://modrinth.com/mod/" + pack.slug; } else { pack.websiteUrl = ""; } pack.description = obj["description"].toString(""); pack.logoUrl = obj["icon_url"].toString(""); pack.logoName = QString("%1.%2").arg(obj["slug"].toString(), QFileInfo(QUrl(pack.logoUrl).fileName()).suffix()); if (obj.contains("author")) { ModPlatform::ModpackAuthor modAuthor; modAuthor.name = obj["author"].toString(); modAuthor.url = ModrinthAPI::getAuthorURL(modAuthor.name); pack.authors = { modAuthor }; } auto client = shouldDownloadOnSide(obj["client_side"].toString()); auto server = shouldDownloadOnSide(obj["server_side"].toString()); if (server && client) { pack.side = ModPlatform::Side::UniversalSide; } else if (server) { pack.side = ModPlatform::Side::ServerSide; } else if (client) { pack.side = ModPlatform::Side::ClientSide; } // Modrinth can have more data than what's provided by the basic search :) pack.extraDataLoaded = false; } void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& obj) { pack.extraData.issuesUrl = obj["issues_url"].toString(); if (pack.extraData.issuesUrl.endsWith('/')) pack.extraData.issuesUrl.chop(1); pack.extraData.sourceUrl = obj["source_url"].toString(); if (pack.extraData.sourceUrl.endsWith('/')) pack.extraData.sourceUrl.chop(1); pack.extraData.wikiUrl = obj["wiki_url"].toString(); if (pack.extraData.wikiUrl.endsWith('/')) pack.extraData.wikiUrl.chop(1); pack.extraData.discordUrl = obj["discord_url"].toString(); if (pack.extraData.discordUrl.endsWith('/')) { pack.extraData.discordUrl.chop(1); } auto donate_arr = obj["donation_urls"].toArray(); for (auto d : donate_arr) { auto d_obj = Json::requireObject(d); ModPlatform::DonationData donate; donate.id = d_obj["id"].toString(); donate.platform = d_obj["platform"].toString(); donate.url = d_obj["url"].toString(); pack.extraData.donate.append(donate); } pack.extraData.status = obj["status"].toString(); pack.extraData.body = obj["body"].toString().remove("
    "); pack.extraDataLoaded = true; } ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj, const QString& preferred_hash_type, const QString& preferred_file_name) { ModPlatform::IndexedVersion file; file.addonId = Json::requireString(obj, "project_id"); file.fileId = Json::requireString(obj, "id"); file.date = Json::requireString(obj, "date_published"); auto versionArray = Json::requireArray(obj, "game_versions"); if (versionArray.empty()) { return {}; } for (auto mcVer : versionArray) { file.mcVersion.append({ ModrinthAPI::mapMCVersionFromModrinth(mcVer.toString()), mcVer.toString() }); // double this so we can check both strings when filtering } auto loaders = Json::requireArray(obj, "loaders"); for (auto loader : loaders) { if (loader == "neoforge") { file.loaders |= ModPlatform::NeoForge; } else if (loader == "forge") { file.loaders |= ModPlatform::Forge; } else if (loader == "cauldron") { file.loaders |= ModPlatform::Cauldron; } else if (loader == "liteloader") { file.loaders |= ModPlatform::LiteLoader; } else if (loader == "fabric") { file.loaders |= ModPlatform::Fabric; } else if (loader == "quilt") { file.loaders |= ModPlatform::Quilt; } } file.version = Json::requireString(obj, "name"); file.version_number = Json::requireString(obj, "version_number"); file.version_type = ModPlatform::IndexedVersionType::fromString(Json::requireString(obj, "version_type")); if (obj.contains("changelog")) { file.changelog = Json::requireString(obj, "changelog"); } auto dependencies = obj["dependencies"].toArray(); for (auto d : dependencies) { auto dep = d.toObject(); ModPlatform::Dependency dependency; dependency.addonId = dep["project_id"].toString(); dependency.version = dep["version_id"].toString(); auto depType = Json::requireString(dep, "dependency_type"); if (depType == "required") { dependency.type = ModPlatform::DependencyType::REQUIRED; } else if (depType == "optional") { dependency.type = ModPlatform::DependencyType::OPTIONAL; } else if (depType == "incompatible") { dependency.type = ModPlatform::DependencyType::INCOMPATIBLE; } else if (depType == "embedded") { dependency.type = ModPlatform::DependencyType::EMBEDDED; } else { dependency.type = ModPlatform::DependencyType::UNKNOWN; } file.dependencies.append(dependency); } auto files = Json::requireArray(obj, "files"); int i = 0; if (files.empty()) { // This should not happen normally, but check just in case qWarning() << "Modrinth returned an unexpected empty list of files:" << obj; return {}; } // Find correct file (needed in cases where one version may have multiple files) // Will default to the last one if there's no primary (though I think Modrinth requires that // at least one file is primary, idk) // NOTE: files.count() is 1-indexed, so we need to subtract 1 to become 0-indexed while (i < files.count() - 1) { auto parent = files[i].toObject(); auto fileName = Json::requireString(parent, "filename"); if (!preferred_file_name.isEmpty() && fileName.contains(preferred_file_name)) { file.is_preferred = true; break; } // Grab the primary file, if available if (Json::requireBoolean(parent, "primary")) { break; } i++; } auto parent = files[i].toObject(); if (parent.contains("url")) { file.downloadUrl = Json::requireString(parent, "url"); file.fileName = Json::requireString(parent, "filename"); file.fileName = FS::RemoveInvalidPathChars(file.fileName); file.is_preferred = Json::requireBoolean(parent, "primary") || (files.count() == 1); auto hash_list = Json::requireObject(parent, "hashes"); if (hash_list.contains(preferred_hash_type)) { file.hash = Json::requireString(hash_list, preferred_hash_type); file.hash_type = preferred_hash_type; } else { auto hash_types = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH); for (auto& hash_type : hash_types) { if (hash_list.contains(hash_type)) { file.hash = Json::requireString(hash_list, hash_type); file.hash_type = hash_type; break; } } } return file; } return {}; } PrismLauncher-11.0.3/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp0000644000175100017510000002666515224505336027067 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ModrinthPackExportTask.h" #include #include #include #include #include #include "Json.h" #include "MMCZip.h" #include "archive/ExportToZipTask.h" #include "minecraft/PackProfile.h" #include "minecraft/mod/MetadataHandler.h" #include "minecraft/mod/ModFolderModel.h" #include "modplatform/ModIndex.h" #include "modplatform/helpers/HashUtils.h" #include "tasks/Task.h" const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" }); const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" }); ModrinthPackExportTask::ModrinthPackExportTask(const QString& name, const QString& version, const QString& summary, bool optionalFiles, BaseInstance* instance, const QString& output, MMCZip::FilterFileFunction filter) : name(name) , version(version) , summary(summary) , optionalFiles(optionalFiles) , instance(instance) , mcInstance(dynamic_cast(instance)) , gameRoot(instance->gameRoot()) , output(output) , filter(filter) {} void ModrinthPackExportTask::executeTask() { setStatus(tr("Searching for files...")); setProgress(0, 0); collectFiles(); } bool ModrinthPackExportTask::abort() { if (task) { task->abort(); return true; } return false; } void ModrinthPackExportTask::collectFiles() { setAbortable(false); QCoreApplication::processEvents(); files.clear(); if (!MMCZip::collectFileListRecursively(instance->gameRoot(), nullptr, &files, filter)) { emitFailed(tr("Could not search for files")); return; } pendingHashes.clear(); resolvedFiles.clear(); if (mcInstance) { mcInstance->loaderModList()->update(); connect(mcInstance->loaderModList(), &ModFolderModel::updateFinished, this, &ModrinthPackExportTask::collectHashes); } else collectHashes(); } void ModrinthPackExportTask::collectHashes() { setStatus(tr("Finding file hashes...")); for (const QFileInfo& file : files) { QCoreApplication::processEvents(); const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); // require sensible file types if (!std::any_of(PREFIXES.begin(), PREFIXES.end(), [&relative](const QString& prefix) { return relative.startsWith(prefix); })) continue; if (!std::any_of(FILE_EXTENSIONS.begin(), FILE_EXTENSIONS.end(), [&relative](const QString& extension) { return relative.endsWith('.' + extension) || relative.endsWith('.' + extension + ".disabled"); })) continue; QFile openFile(file.absoluteFilePath()); if (!openFile.open(QFile::ReadOnly)) { qWarning() << "Could not open" << file << "for hashing:" << openFile.errorString(); continue; } const QByteArray data = openFile.readAll(); if (openFile.error() != QFileDevice::NoError) { qWarning() << "Could not read" << file << "error:" << openFile.errorString(); continue; } auto sha512 = Hashing::hash(data, Hashing::Algorithm::Sha512); auto allMods = mcInstance->loaderModList()->allMods(); if (auto modIter = std::find_if(allMods.begin(), allMods.end(), [&file](Mod* mod) { return mod->fileinfo() == file; }); modIter != allMods.end()) { const Mod* mod = *modIter; if (mod->metadata() != nullptr) { const QUrl& url = mod->metadata()->url; // ensure the url is permitted on modrinth.com if (!url.isEmpty() && BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) { qDebug() << "Resolving" << relative << "from index"; auto sha1 = Hashing::hash(data, Hashing::Algorithm::Sha1); ResolvedFile resolvedFile{ sha1, sha512, url.toEncoded(), openFile.size(), mod->metadata()->side }; resolvedFiles[relative] = resolvedFile; // nice! we've managed to resolve based on local metadata! // no need to enqueue it continue; } } } qDebug() << "Enqueueing" << relative << "for Modrinth query"; pendingHashes[relative] = sha512; } setAbortable(true); makeApiRequest(); } void ModrinthPackExportTask::makeApiRequest() { if (pendingHashes.isEmpty()) buildZip(); else { setStatus(tr("Finding versions for hashes...")); auto [versionsTask, response] = api.currentVersions(pendingHashes.values(), "sha512"); task = versionsTask; connect(task.get(), &Task::succeeded, [this, response]() { parseApiResponse(response); }); connect(task.get(), &Task::failed, this, &ModrinthPackExportTask::emitFailed); connect(task.get(), &Task::aborted, this, &ModrinthPackExportTask::emitAborted); task->start(); } } void ModrinthPackExportTask::parseApiResponse(QByteArray* response) { task = nullptr; try { const QJsonDocument doc = Json::requireDocument(*response); QMapIterator iterator(pendingHashes); while (iterator.hasNext()) { iterator.next(); const QJsonObject obj = doc[iterator.value()].toObject(); if (obj.isEmpty()) continue; const QJsonArray files_array = obj["files"].toArray(); if (auto fileIter = std::find_if(files_array.begin(), files_array.end(), [&iterator](const QJsonValue& file) { return file["hashes"]["sha512"] == iterator.value(); }); fileIter != files_array.end()) { // map the file to the url resolvedFiles[iterator.key()] = ResolvedFile{ fileIter->toObject()["hashes"].toObject()["sha1"].toString(), iterator.value(), fileIter->toObject()["url"].toString(), fileIter->toObject()["size"].toInt() }; } } } catch (const Json::JsonException& e) { emitFailed(tr("Failed to parse versions response: %1").arg(e.what())); return; } pendingHashes.clear(); buildZip(); } void ModrinthPackExportTask::buildZip() { setStatus(tr("Adding files...")); auto zipTask = makeShared(output, gameRoot, files, "overrides/", true); zipTask->addExtraFile("modrinth.index.json", generateIndex()); zipTask->setExcludeFiles(resolvedFiles.keys()); auto progressStep = std::make_shared(); connect(zipTask.get(), &Task::finished, this, [this, progressStep] { progressStep->state = TaskStepState::Succeeded; stepProgress(*progressStep); }); connect(zipTask.get(), &Task::succeeded, this, &ModrinthPackExportTask::emitSucceeded); connect(zipTask.get(), &Task::aborted, this, &ModrinthPackExportTask::emitAborted); connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { progressStep->state = TaskStepState::Failed; stepProgress(*progressStep); emitFailed(reason); }); connect(zipTask.get(), &Task::stepProgress, this, &ModrinthPackExportTask::propagateStepProgress); connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { progressStep->update(current, total); stepProgress(*progressStep); }); connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { progressStep->status = status; stepProgress(*progressStep); }); task.reset(zipTask); zipTask->start(); } QByteArray ModrinthPackExportTask::generateIndex() { QJsonObject out; out["formatVersion"] = 1; out["game"] = "minecraft"; out["name"] = name; out["versionId"] = version; if (!summary.isEmpty()) out["summary"] = summary; if (mcInstance) { auto profile = mcInstance->getPackProfile(); // collect all supported components const ComponentPtr minecraft = profile->getComponent("net.minecraft"); const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader"); const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader"); const ComponentPtr forge = profile->getComponent("net.minecraftforge"); const ComponentPtr neoForge = profile->getComponent("net.neoforged"); // convert all available components to mrpack dependencies QJsonObject dependencies; if (minecraft != nullptr) dependencies["minecraft"] = minecraft->m_version; if (quilt != nullptr) dependencies["quilt-loader"] = quilt->m_version; if (fabric != nullptr) dependencies["fabric-loader"] = fabric->m_version; if (forge != nullptr) dependencies["forge"] = forge->m_version; if (neoForge != nullptr) dependencies["neoforge"] = neoForge->m_version; out["dependencies"] = dependencies; } QJsonArray filesOut; for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) { QJsonObject fileOut; QString path = iterator.key(); const ResolvedFile& value = iterator.value(); QJsonObject env; // detect disabled mod const QFileInfo pathInfo(path); if (optionalFiles && pathInfo.suffix() == "disabled") { // rename it path = pathInfo.dir().filePath(pathInfo.completeBaseName()); env["client"] = "optional"; env["server"] = "optional"; } else { env["client"] = "required"; env["server"] = "required"; } // a server side mod does not imply that the mod does not work on the client // however, if a mrpack mod is marked as server-only it will not install on the client if (iterator->side == ModPlatform::Side::ClientSide) env["server"] = "unsupported"; fileOut["env"] = env; fileOut["path"] = path; fileOut["downloads"] = QJsonArray{ iterator->url }; QJsonObject hashes; hashes["sha1"] = value.sha1; hashes["sha512"] = value.sha512; fileOut["hashes"] = hashes; fileOut["fileSize"] = value.size; filesOut << fileOut; } out["files"] = filesOut; return QJsonDocument(out).toJson(QJsonDocument::Compact); } PrismLauncher-11.0.3/launcher/modplatform/modrinth/ModrinthAPI.cpp0000644000175100017510000001513715224505336024565 0ustar runnerrunner// SPDX-FileCopyrightText: 2023 flowln // // SPDX-License-Identifier: GPL-3.0-only #include "ModrinthAPI.h" #include "Application.h" #include "Json.h" #include "net/ApiDownload.h" #include "net/ApiUpload.h" #include "net/NetJob.h" std::pair ModrinthAPI::currentVersion(const QString& hash, const QString& hash_format) const { auto netJob = makeShared(QString("Modrinth::GetCurrentVersion"), APPLICATION->network()); auto [action, response] = Net::ApiDownload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format)); netJob->addNetAction(action); return { netJob, response }; } std::pair ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format) const { auto netJob = makeShared(QString("Modrinth::GetCurrentVersions"), APPLICATION->network()); QJsonObject body_obj; Json::writeStringList(body_obj, "hashes", hashes); Json::writeString(body_obj, "algorithm", hash_format); QJsonDocument body(body_obj); auto body_raw = body.toJson(); auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), body_raw); netJob->addNetAction(action); netJob->setAskRetry(false); return { netJob, response }; } std::pair ModrinthAPI::latestVersion(const QString& hash, const QString& hash_format, std::optional> mcVersions, std::optional loaders) const { auto netJob = makeShared(QString("Modrinth::GetLatestVersion"), APPLICATION->network()); QJsonObject body_obj; if (loaders.has_value()) { Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders.value())); } if (mcVersions.has_value()) { QStringList game_versions; for (auto& ver : mcVersions.value()) { game_versions.append(mapMCVersionToModrinth(ver)); } Json::writeStringList(body_obj, "game_versions", game_versions); } QJsonDocument body(body_obj); auto body_raw = body.toJson(); auto [action, response] = Net::ApiUpload::makeByteArray( QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), body_raw); netJob->addNetAction(action); return { netJob, response }; } std::pair ModrinthAPI::latestVersions(const QStringList& hashes, const QString& hash_format, std::optional> mcVersions, std::optional loaders) const { auto netJob = makeShared(QString("Modrinth::GetLatestVersions"), APPLICATION->network()); QJsonObject body_obj; Json::writeStringList(body_obj, "hashes", hashes); Json::writeString(body_obj, "algorithm", hash_format); if (loaders.has_value()) { Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders.value())); } if (mcVersions.has_value()) { QStringList game_versions; for (auto& ver : mcVersions.value()) { game_versions.append(mapMCVersionToModrinth(ver)); } Json::writeStringList(body_obj, "game_versions", game_versions); } QJsonDocument body(body_obj); auto body_raw = body.toJson(); auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), body_raw); netJob->addNetAction(action); return { netJob, response }; } std::pair ModrinthAPI::getProjects(QStringList addonIds) const { auto netJob = makeShared(QString("Modrinth::GetProjects"), APPLICATION->network()); auto searchUrl = getMultipleModInfoURL(addonIds); auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(searchUrl)); netJob->addNetAction(action); return { netJob, response }; } QList ModrinthAPI::getSortingMethods() const { // https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects return { { .index = 1, .name = "relevance", .readable_name = QObject::tr("Sort by Relevance") }, { .index = 2, .name = "downloads", .readable_name = QObject::tr("Sort by Downloads") }, { .index = 3, .name = "follows", .readable_name = QObject::tr("Sort by Follows") }, { .index = 4, .name = "newest", .readable_name = QObject::tr("Sort by Newest") }, { .index = 5, .name = "updated", .readable_name = QObject::tr("Sort by Last Updated") } }; } std::pair ModrinthAPI::getModCategories() { auto netJob = makeShared(QString("Modrinth::GetCategories"), APPLICATION->network()); auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(BuildConfig.MODRINTH_PROD_URL + "/tag/category")); netJob->addNetAction(action); QObject::connect(netJob.get(), &Task::failed, [](const QString& msg) { qDebug() << "Modrinth failed to get categories:" << msg; }); return { netJob, response }; } QList ModrinthAPI::loadCategories(const QByteArray& response, const QString& projectType) { QList categories; QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response from categories at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *response; return categories; } try { auto arr = Json::requireArray(doc); for (auto val : arr) { auto cat = Json::requireObject(val); auto name = Json::requireString(cat, "name"); if (cat["project_type"].toString() == projectType) { categories.push_back({ .name = name, .id = name }); } } } catch (Json::JsonException& e) { qCritical() << "Failed to parse response from a version request."; qCritical() << e.what(); qDebug() << doc; } return categories; } QList ModrinthAPI::loadModCategories(const QByteArray& response) { return loadCategories(response, "mod"); }; PrismLauncher-11.0.3/launcher/modplatform/modrinth/ModrinthAPI.h0000644000175100017510000002365015224505336024231 0ustar runnerrunner// SPDX-FileCopyrightText: 2022-2023 flowln // // SPDX-License-Identifier: GPL-3.0-only #pragma once #include "BuildConfig.h" #include "modplatform/ModIndex.h" #include "modplatform/ResourceAPI.h" #include "modplatform/modrinth/ModrinthPackIndex.h" #include #include class ModrinthAPI : public ResourceAPI { public: std::pair currentVersion(const QString& hash, const QString& hash_format) const; std::pair currentVersions(const QStringList& hashes, QString hash_format) const; std::pair latestVersion(const QString& hash, const QString& hash_format, std::optional> mcVersions, std::optional loaders) const; std::pair latestVersions(const QStringList& hashes, const QString& hash_format, std::optional> mcVersions, std::optional loaders) const; std::pair getProjects(QStringList addonIds) const override; static std::pair getModCategories(); static QList loadCategories(const QByteArray& response, const QString& projectType); static QList loadModCategories(const QByteArray& response); public: auto getSortingMethods() const -> QList override; static auto getAuthorURL(const QString& name) -> QString { return "https://modrinth.com/user/" + name; }; static auto getModLoaderStrings(const ModPlatform::ModLoaderTypes types) -> QStringList { QStringList l; for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt, ModPlatform::LiteLoader, ModPlatform::DataPack, ModPlatform::Babric, ModPlatform::BTA, ModPlatform::LegacyFabric, ModPlatform::Ornithe, ModPlatform::Rift }) { if ((types & loader) != 0U) { l << getModLoaderAsString(loader); } } return l; } static auto getModLoaderFilters(ModPlatform::ModLoaderTypes types) -> QString { QStringList l; for (const auto& loader : getModLoaderStrings(types)) { l << QString("\"categories:%1\"").arg(loader); } return l.join(','); } static auto getCategoriesFilters(const QStringList& categories) -> QString { QStringList l; for (const auto& cat : categories) { l << QString("\"categories:%1\"").arg(cat); } return l.join(','); } static QString getSideFilters(ModPlatform::Side side) { switch (side) { case ModPlatform::Side::ClientSide: return { R"("client_side:required","client_side:optional"],["server_side:optional","server_side:unsupported")" }; case ModPlatform::Side::ServerSide: return { R"("server_side:required","server_side:optional"],["client_side:optional","client_side:unsupported")" }; case ModPlatform::Side::UniversalSide: return { R"("client_side:required"],["server_side:required")" }; case ModPlatform::Side::NoSide: // fallthrough default: return {}; } } static QString mapMCVersionFromModrinth(QString v) { static const QString s_preString = " Pre-Release "; bool pre = false; if (v.contains("-pre")) { pre = true; v.replace("-pre", s_preString); } v.replace("-", " "); if (pre) { v.replace(" Pre Release ", s_preString); } return v; } private: static QString resourceTypeParameter(ModPlatform::ResourceType type) { switch (type) { case ModPlatform::ResourceType::Mod: return "mod"; case ModPlatform::ResourceType::ResourcePack: return "resourcepack"; case ModPlatform::ResourceType::ShaderPack: return "shader"; case ModPlatform::ResourceType::DataPack: return "datapack"; case ModPlatform::ResourceType::Modpack: return "modpack"; default: qWarning() << "Invalid resource type for Modrinth API!"; break; } return ""; } QString createFacets(const SearchArgs& args) const { QStringList facets_list; if (args.loaders.has_value() && args.loaders.value() != 0) { facets_list.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value()))); } if (args.versions.has_value() && !args.versions.value().empty()) { facets_list.append(QString("[%1]").arg(getGameVersionsArray(args.versions.value()))); } if (args.side.has_value()) { auto side = getSideFilters(args.side.value()); if (!side.isEmpty()) { facets_list.append(QString("[%1]").arg(side)); } } if (args.categoryIds.has_value() && !args.categoryIds->empty()) { facets_list.append(QString("[%1]").arg(getCategoriesFilters(args.categoryIds.value()))); } if (args.openSource) { facets_list.append("[\"open_source:true\"]"); } facets_list.append(QString("[\"project_type:%1\"]").arg(resourceTypeParameter(args.type))); return QString("[%1]").arg(facets_list.join(',')); } public: auto getSearchURL(const SearchArgs& args) const -> std::optional override { if (args.loaders.has_value() && args.loaders.value() != 0) { if (!validateModLoaders(args.loaders.value())) { qWarning() << "Modrinth - or our interface - does not support any the provided mod loaders!"; return {}; } } QStringList get_arguments; get_arguments.append(QString("offset=%1").arg(args.offset)); get_arguments.append(QString("limit=25")); if (args.search.has_value()) { get_arguments.append(QString("query=%1").arg(args.search.value())); } if (args.sorting.has_value()) { get_arguments.append(QString("index=%1").arg(args.sorting.value().name)); } get_arguments.append(QString("facets=%1").arg(createFacets(args))); return BuildConfig.MODRINTH_PROD_URL + "/search?" + get_arguments.join('&'); }; auto getInfoURL(const QString& id) const -> std::optional override { return BuildConfig.MODRINTH_PROD_URL + "/project/" + id; }; auto getMultipleModInfoURL(const QStringList& ids) const -> QString { return BuildConfig.MODRINTH_PROD_URL + QString("/projects?ids=[\"%1\"]").arg(ids.join("\",\"")); }; auto getVersionsURL(const VersionSearchArgs& args) const -> std::optional override { QStringList get_arguments; if (args.mcVersions.has_value()) { get_arguments.append(QString("game_versions=[%1]").arg(getGameVersionsString(args.mcVersions.value()))); } if (args.loaders.has_value()) { get_arguments.append(QString("loaders=[\"%1\"]").arg(getModLoaderStrings(args.loaders.value()).join("\",\""))); } get_arguments.append(QString("include_changelog=%1").arg(args.includeChangelog ? "true" : "false")); return QString("%1/project/%2/version%3%4") .arg(BuildConfig.MODRINTH_PROD_URL, args.pack->addonId.toString(), get_arguments.isEmpty() ? "" : "?", get_arguments.join('&')); }; QString getGameVersionsArray(const std::vector& mcVersions) const { QString s; for (const auto& ver : mcVersions) { s += QString(R"("versions:%1",)").arg(mapMCVersionToModrinth(ver)); } s.remove(s.length() - 1, 1); // remove last comma return s.isEmpty() ? QString() : s; } static auto validateModLoaders(ModPlatform::ModLoaderTypes loaders) -> bool { return (loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt | ModPlatform::LiteLoader | ModPlatform::DataPack | ModPlatform::Babric | ModPlatform::BTA | ModPlatform::LegacyFabric | ModPlatform::Ornithe | ModPlatform::Rift)) != 0; } std::optional getDependencyURL(const DependencySearchArgs& args) const override { return args.dependency.version.length() != 0 ? QString("%1/version/%2").arg(BuildConfig.MODRINTH_PROD_URL, args.dependency.version) : QString(R"(%1/project/%2/version?game_versions=["%3"]&loaders=["%4"]&include_changelog=%5)") .arg(BuildConfig.MODRINTH_PROD_URL) .arg(args.dependency.addonId.toString()) .arg(mapMCVersionToModrinth(args.mcVersion)) .arg(getModLoaderStrings(args.loader).join("\",\"")) .arg(args.includeChangelog ? "true" : "false"); }; QJsonArray documentToArray(QJsonDocument& obj) const override { return obj.object().value("hits").toArray(); } void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) const override { Modrinth::loadIndexedPack(m, obj); } ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, ModPlatform::ResourceType /*unused*/) const override { return Modrinth::loadIndexedPackVersion(obj); }; void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) const override { Modrinth::loadExtraPackData(m, obj); } }; PrismLauncher-11.0.3/launcher/modplatform/ModIndex.h0000644000175100017510000001547115224505336022000 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include #include #include #include class QIODevice; namespace ModPlatform { enum class ModLoaderType : std::uint16_t { NeoForge = 1U << 0U, Forge = 1U << 1U, Cauldron = 1U << 2U, LiteLoader = 1U << 3U, Fabric = 1U << 4U, Quilt = 1U << 5U, DataPack = 1U << 6U, Babric = 1U << 7U, BTA = 1U << 8U, LegacyFabric = 1U << 9U, Ornithe = 1U << 10U, Rift = 1U << 11U }; ModLoaderType operator|(ModLoaderType lhs, ModLoaderType rhs); using enum ModLoaderType; Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType) QList modLoaderTypesToList(ModLoaderTypes flags); enum class ResourceProvider : std::uint8_t { MODRINTH, FLAME }; enum class DependencyType : std::uint8_t { REQUIRED, OPTIONAL, INCOMPATIBLE, EMBEDDED, TOOL, INCLUDE, UNKNOWN }; enum class Side : std::uint8_t { NoSide = 0, ClientSide = 1U << 0U, ServerSide = 1U << 1U, UniversalSide = ClientSide | ServerSide }; namespace SideUtils { QString toString(Side side); Side fromString(QString side); } // namespace SideUtils namespace DependencyTypeUtils { QString toString(DependencyType type); DependencyType fromString(const QString& str); } // namespace DependencyTypeUtils namespace ProviderCapabilities { const char* name(ResourceProvider); QString readableName(ResourceProvider); QStringList hashType(ResourceProvider); } // namespace ProviderCapabilities struct ModpackAuthor { QString name; QString url; }; struct DonationData { QString id; QString platform; QString url; }; struct IndexedVersionType { enum class Enum : std::uint8_t { Unknown = 0, Release = 1, Beta = 2, Alpha = 3 }; using enum Enum; constexpr IndexedVersionType(Enum e = Unknown) : m_type(e) {} // NOLINT(hicpp-explicit-conversions) static IndexedVersionType fromString(const QString& type); bool isValid() const { return m_type != Unknown; } std::strong_ordering operator<=>(const IndexedVersionType& other) const = default; std::strong_ordering operator<=>(const IndexedVersionType::Enum& other) const { return m_type <=> other; } QString toString() const; explicit operator int() const { return static_cast(m_type); } explicit operator IndexedVersionType::Enum() { return m_type; } private: Enum m_type; }; struct Dependency { QVariant addonId; DependencyType type; QString version; }; struct IndexedVersion { QVariant addonId; QVariant fileId; QString version; QString version_number; IndexedVersionType version_type; QStringList mcVersion; QString downloadUrl; QString date; QString fileName; ModLoaderTypes loaders; QString hash_type; QString hash; bool is_preferred = true; QString changelog; QList dependencies; Side side = Side::NoSide; // this is for flame API // For internal use, not provided by APIs bool is_currently_selected = false; QString getVersionDisplayString() const { auto release_type = version_type.isValid() ? QString(" [%1]").arg(version_type.toString()) : ""; auto versionStr = !version.contains(version_number) ? version_number : ""; QString gameVersion = ""; for (const auto& v : mcVersion) { if (version.contains(v)) { gameVersion = ""; break; } if (gameVersion.isEmpty()) { gameVersion = QObject::tr(" for %1").arg(v); } } return QString("%1%2 — %3%4").arg(version, gameVersion, versionStr, release_type); } }; struct ExtraPackData { QList donate; QString issuesUrl; QString sourceUrl; QString wikiUrl; QString discordUrl; QString status; QString body; }; struct IndexedPack { using Ptr = std::shared_ptr; QVariant addonId; ResourceProvider provider; QString name; QString slug; QString description; QList authors; QString logoName; QString logoUrl; QString websiteUrl; Side side = Side::NoSide; bool versionsLoaded = false; QList versions; // Don't load by default, since some modplatform don't have that info bool extraDataLoaded = true; ExtraPackData extraData; // For internal use, not provided by APIs bool isVersionSelected(int index) const { if (!versionsLoaded) { return false; } return versions.at(index).is_currently_selected; } bool isAnyVersionSelected() const { if (!versionsLoaded) { return false; } return std::any_of(versions.constBegin(), versions.constEnd(), [](const auto& v) { return v.is_currently_selected; }); } }; struct OverrideDep { QString quilt; QString fabric; QString slug; ModPlatform::ResourceProvider provider; }; inline auto getOverrideDeps() -> QList { return { { .quilt = "634179", .fabric = "306612", .slug = "API", .provider = ModPlatform::ResourceProvider::FLAME }, { .quilt = "720410", .fabric = "308769", .slug = "KotlinLibraries", .provider = ModPlatform::ResourceProvider::FLAME }, { .quilt = "qvIfYCYJ", .fabric = "P7dR8mSH", .slug = "API", .provider = ModPlatform::ResourceProvider::MODRINTH }, { .quilt = "lwVhp9o5", .fabric = "Ha28R6CL", .slug = "KotlinLibraries", .provider = ModPlatform::ResourceProvider::MODRINTH } }; } QString getMetaURL(ResourceProvider provider, QVariant projectID); auto getModLoaderAsString(ModLoaderType type) -> const QString; auto getModLoaderFromString(QString type) -> ModLoaderType; constexpr bool hasSingleModLoaderSelected(ModLoaderTypes l) noexcept { auto x = static_cast(l); return (x != 0U) && ((x & (x - 1U)) == 0U); } struct Category { QString name; QString id; }; } // namespace ModPlatform Q_DECLARE_METATYPE(ModPlatform::IndexedPack) Q_DECLARE_METATYPE(ModPlatform::IndexedPack::Ptr) Q_DECLARE_METATYPE(ModPlatform::ResourceProvider) PrismLauncher-11.0.3/launcher/modplatform/packwiz/0000755000175100017510000000000015224505336021560 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/modplatform/packwiz/Packwiz.h0000644000175100017510000000644315224505336023350 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "modplatform/ModIndex.h" #include #include #include class QDir; namespace Packwiz { class V1 { public: // can also represent other resources beside loader mods - but this is what packwiz calls it struct Mod { QString slug{}; QString name{}; QString filename{}; ModPlatform::Side side{ ModPlatform::Side::UniversalSide }; ModPlatform::ModLoaderTypes loaders; QStringList mcVersions; ModPlatform::IndexedVersionType releaseType; // [download] QString mode{}; QUrl url{}; QString hash_format{}; QString hash{}; // [update] ModPlatform::ResourceProvider provider{}; QVariant file_id{}; QVariant project_id{}; QString version_number{}; QList dependencies; public: // This is a totally heuristic, but should work for now. auto isValid() const -> bool { return !slug.isEmpty() && !project_id.isNull(); } // Different providers can use different names for the same thing // Modrinth-specific auto mod_id() -> QVariant& { return project_id; } auto version() -> QVariant& { return file_id; } }; /* Generates the object representing the information in a mod.pw.toml file via * its common representation in the launcher, when downloading mods. * */ static auto createModFormat(const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod; /* Updates the mod index for the provided mod. * This creates a new index if one does not exist already * TODO: Ask the user if they want to override, and delete the old mod's files, or keep the old one. * */ static void updateModIndex(const QDir& index_dir, Mod& mod); /* Deletes the metadata for the mod with the given slug. If the metadata doesn't exist, it does nothing. */ static void deleteModIndex(const QDir& index_dir, QString& mod_slug); /* Gets the metadata for a mod with a particular file name. * If the mod doesn't have a metadata, it simply returns an empty Mod object. * */ static auto getIndexForMod(const QDir& index_dir, QString slug) -> Mod; /* Gets the metadata for a mod with a particular id. * If the mod doesn't have a metadata, it simply returns an empty Mod object. * */ static auto getIndexForMod(const QDir& index_dir, QVariant& mod_id) -> Mod; }; } // namespace Packwiz PrismLauncher-11.0.3/launcher/modplatform/packwiz/Packwiz.cpp0000644000175100017510000003361715224505336023706 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "Packwiz.h" #include #include #include #include #include #include #include #include #include "FileSystem.h" #include "StringUtils.h" #include "Version.h" #include "modplatform/ModIndex.h" #include namespace Packwiz { namespace { auto getRealIndexName(const QDir& indexDir, const QString& normalizedFname, bool shouldFindMatch = false) -> QString { const QFile indexFile(indexDir.absoluteFilePath(normalizedFname)); QString realFname = normalizedFname; if (!indexFile.exists()) { // Tries to get similar entries for (auto& fileName : indexDir.entryList(QDir::Filter::Files)) { if (QString::compare(normalizedFname, fileName, Qt::CaseInsensitive) == 0) { realFname = fileName; break; } } if (shouldFindMatch && (QString::compare(normalizedFname, realFname, Qt::CaseSensitive) == 0)) { qCritical() << "Could not find a match for a valid metadata file!"; qCritical() << "File:" << normalizedFname; return {}; } } return realFname; } // Helpers auto indexFileName(const QString& modSlug) -> QString { if (modSlug.endsWith(".pw.toml")) { return modSlug; } return QString("%1.pw.toml").arg(modSlug); } // Helper functions for extracting data from the TOML file auto stringEntry(toml::table table, const QString& entryName) -> QString { auto* node = table.get(StringUtils::toStdString(entryName)); if (!node) { qDebug() << "Failed to read str property '" + entryName + "' in mod metadata."; return {}; } return node->value_or(""); } auto intEntry(toml::table table, const QString& entryName) -> int { auto* node = table.get(StringUtils::toStdString(entryName)); if (!node) { qDebug() << "Failed to read int property '" + entryName + "' in mod metadata."; return {}; } return node->value_or(0); } bool sortMCVersions(const QString& a, const QString& b) { auto cmp = Version(a) <=> Version(b); if (cmp == std::strong_ordering::equal) { return a < b; } return cmp == std::strong_ordering::less; } } // namespace auto V1::createModFormat([[maybe_unused]] const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod { Mod mod; mod.slug = mod_pack.slug; mod.name = mod_pack.name; mod.filename = mod_version.fileName; if (mod_pack.provider == ModPlatform::ResourceProvider::FLAME) { mod.mode = "metadata:curseforge"; } else { mod.mode = "url"; mod.url = mod_version.downloadUrl; } mod.hash_format = mod_version.hash_type; mod.hash = mod_version.hash; mod.provider = mod_pack.provider; mod.file_id = mod_version.fileId; mod.project_id = mod_pack.addonId; mod.side = mod_version.side == ModPlatform::Side::NoSide ? mod_pack.side : mod_version.side; mod.loaders = mod_version.loaders; mod.mcVersions = mod_version.mcVersion; mod.mcVersions.removeDuplicates(); std::ranges::sort(mod.mcVersions, sortMCVersions); mod.releaseType = mod_version.version_type; mod.version_number = mod_version.version_number; if (mod.version_number.isNull()) // on CurseForge, there is only a version name - not a version number mod.version_number = mod_version.version; mod.dependencies = mod_version.dependencies; return mod; } void V1::updateModIndex(const QDir& index_dir, Mod& mod) { if (!mod.isValid()) { qCritical() << QString("Tried to update metadata of an invalid mod!"); return; } // Ensure the corresponding mod's info exists, and create it if not auto normalized_fname = indexFileName(mod.slug); auto real_fname = getRealIndexName(index_dir, normalized_fname); QFile index_file(index_dir.absoluteFilePath(real_fname)); if (real_fname != normalized_fname) index_file.rename(normalized_fname); // There's already data on there! // TODO: We should do more stuff here, as the user is likely trying to // override a file. In this case, check versions and ask the user what // they want to do! if (index_file.exists()) { index_file.remove(); } else { FS::ensureFilePathExists(index_file.fileName()); } toml::table update; switch (mod.provider) { case (ModPlatform::ResourceProvider::FLAME): if (mod.file_id.toInt() == 0 || mod.project_id.toInt() == 0) { qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname); return; } update = toml::table{ { "file-id", mod.file_id.toInt() }, { "project-id", mod.project_id.toInt() }, }; break; case (ModPlatform::ResourceProvider::MODRINTH): if (mod.mod_id().toString().isEmpty() || mod.version().toString().isEmpty()) { qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname); return; } update = toml::table{ { "mod-id", mod.mod_id().toString().toStdString() }, { "version", mod.version().toString().toStdString() }, }; break; } toml::array loaders; for (auto loader : ModPlatform::modLoaderTypesToList(mod.loaders)) { loaders.push_back(getModLoaderAsString(loader).toStdString()); } toml::array mcVersions; for (auto version : mod.mcVersions) { mcVersions.push_back(version.toStdString()); } if (!index_file.open(QIODevice::ReadWrite)) { qCritical() << "Could not open file" << normalized_fname << "error:" << index_file.errorString(); return; } toml::array deps; for (auto dep : mod.dependencies) { auto tbl = toml::table{ { "addonId", dep.addonId.toString().toStdString() }, { "type", ModPlatform::DependencyTypeUtils::toString(dep.type).toStdString() } }; if (!dep.version.isEmpty()) { tbl.emplace("version", dep.version.toStdString()); } deps.push_back(tbl); } // Put TOML data into the file QTextStream in_stream(&index_file); { auto tbl = toml::table{ { "name", mod.name.toStdString() }, { "filename", mod.filename.toStdString() }, { "side", ModPlatform::SideUtils::toString(mod.side).toStdString() }, { "x-prismlauncher-loaders", loaders }, { "x-prismlauncher-mc-versions", mcVersions }, { "x-prismlauncher-release-type", mod.releaseType.toString().toStdString() }, { "x-prismlauncher-version-number", mod.version_number.toStdString() }, { "x-prismlauncher-dependencies", deps }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, { "url", mod.url.toString().toStdString() }, { "hash-format", mod.hash_format.toStdString() }, { "hash", mod.hash.toStdString() }, } }, { "update", toml::table{ { ModPlatform::ProviderCapabilities::name(mod.provider), update } } } }; std::stringstream ss; ss << tbl; in_stream << QString::fromStdString(ss.str()); } index_file.flush(); index_file.close(); } void V1::deleteModIndex(const QDir& index_dir, QString& mod_slug) { auto normalized_fname = indexFileName(mod_slug); auto real_fname = getRealIndexName(index_dir, normalized_fname); if (real_fname.isEmpty()) return; QFile index_file(index_dir.absoluteFilePath(real_fname)); if (!index_file.exists()) { qWarning() << QString("Tried to delete non-existent mod metadata for %1!").arg(mod_slug); return; } if (!index_file.remove()) { qWarning() << QString("Failed to remove metadata for mod %1!").arg(mod_slug); } } auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod { Mod mod; auto normalized_fname = indexFileName(slug); auto real_fname = getRealIndexName(index_dir, normalized_fname, true); if (real_fname.isEmpty()) return {}; toml::table table; #if TOML_EXCEPTIONS try { table = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname))); } catch (const toml::parse_error& err) { qWarning() << QString("Could not open file %1!").arg(normalized_fname); qWarning() << "Reason:" << QString(err.what()); return {}; } #else toml::parse_result result = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname))); if (!result) { qWarning() << QString("Could not open file %1!").arg(normalized_fname); qWarning() << "Reason:" << result.error().description(); return {}; } table = result.table(); #endif // index_file.close(); mod.slug = slug; { // Basic info mod.name = stringEntry(table, "name"); mod.filename = stringEntry(table, "filename"); mod.side = ModPlatform::SideUtils::fromString(stringEntry(table, "side")); mod.releaseType = ModPlatform::IndexedVersionType::fromString(table["x-prismlauncher-release-type"].value_or("")); if (auto loaders = table["x-prismlauncher-loaders"]; loaders && loaders.is_array()) { for (auto&& loader : *loaders.as_array()) { if (loader.is_string()) { mod.loaders |= ModPlatform::getModLoaderFromString(QString::fromStdString(loader.as_string()->value_or(""))); } } } if (auto versions = table["x-prismlauncher-mc-versions"]; versions && versions.is_array()) { for (auto&& version : *versions.as_array()) { if (version.is_string()) { auto ver = QString::fromStdString(version.as_string()->value_or("")); if (!ver.isEmpty()) { mod.mcVersions << ver; } } } mod.mcVersions.removeDuplicates(); std::ranges::sort(mod.mcVersions, sortMCVersions); } } mod.version_number = table["x-prismlauncher-version-number"].value_or(""); { // [download] info auto download_table = table["download"].as_table(); if (!download_table) { qCritical() << QString("No [download] section found on mod metadata!"); return {}; } mod.mode = stringEntry(*download_table, "mode"); mod.url = stringEntry(*download_table, "url"); mod.hash_format = stringEntry(*download_table, "hash-format"); mod.hash = stringEntry(*download_table, "hash"); } { // [update] info using Provider = ModPlatform::ResourceProvider; auto update_table = table["update"]; if (!update_table || !update_table.is_table()) { qCritical() << QString("No [update] section found on mod metadata!"); return {}; } toml::table* mod_provider_table = nullptr; if ((mod_provider_table = update_table[ModPlatform::ProviderCapabilities::name(Provider::FLAME)].as_table())) { mod.provider = Provider::FLAME; mod.file_id = intEntry(*mod_provider_table, "file-id"); mod.project_id = intEntry(*mod_provider_table, "project-id"); } else if ((mod_provider_table = update_table[ModPlatform::ProviderCapabilities::name(Provider::MODRINTH)].as_table())) { mod.provider = Provider::MODRINTH; mod.mod_id() = stringEntry(*mod_provider_table, "mod-id"); mod.version() = stringEntry(*mod_provider_table, "version"); } else { qCritical() << QString("No mod provider on mod metadata!"); return {}; } } { // dependencies auto deps = table["x-prismlauncher-dependencies"].as_array(); if (deps) { for (auto&& depNode : *deps) { auto dep = depNode.as_table(); if (dep) { ModPlatform::Dependency d; d.addonId = stringEntry(*dep, "addonId"); if (dep->contains("version")) { d.version = stringEntry(*dep, "version"); } d.type = ModPlatform::DependencyTypeUtils::fromString(stringEntry(*dep, "type")); mod.dependencies << d; } } } } return mod; } auto V1::getIndexForMod(const QDir& index_dir, QVariant& mod_id) -> Mod { for (auto& file_name : index_dir.entryList(QDir::Filter::Files)) { auto mod = getIndexForMod(index_dir, file_name); if (mod.mod_id() == mod_id) return mod; } return {}; } } // namespace Packwiz PrismLauncher-11.0.3/launcher/modplatform/EnsureMetadataTask.h0000644000175100017510000000366115224505336024014 0ustar runnerrunner#pragma once #include "ModIndex.h" #include "net/NetJob.h" #include "modplatform/helpers/HashUtils.h" #include "minecraft/mod/Resource.h" #include "tasks/ConcurrentTask.h" class Mod; class QDir; class EnsureMetadataTask : public Task { Q_OBJECT public: EnsureMetadataTask(Resource*, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); EnsureMetadataTask(QList&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); EnsureMetadataTask(QHash&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); ~EnsureMetadataTask() = default; Task::Ptr getHashingTask() { return m_hashingTask; } public slots: bool abort() override; protected slots: void executeTask() override; private: // FIXME: Move to their own namespace Task::Ptr modrinthVersionsTask(); Task::Ptr modrinthProjectsTask(); Task::Ptr flameVersionsTask(); Task::Ptr flameProjectsTask(); // Helpers enum class RemoveFromList { Yes, No }; void emitReady(Resource*, QString key = {}, RemoveFromList = RemoveFromList::Yes); void emitFail(Resource*, QString key = {}, RemoveFromList = RemoveFromList::Yes); // Hashes and stuff Hashing::Hasher::Ptr createNewHash(Resource*); QString getExistingHash(Resource*); private slots: void updateMetadata(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Resource*); void updateMetadataCallback(ModPlatform::IndexedPack& pack, Resource* resource); signals: void metadataReady(Resource*); void metadataFailed(Resource*); private: QHash m_resources; QDir m_indexDir; ModPlatform::ResourceProvider m_provider; QHash m_tempVersions; Task::Ptr m_hashingTask; Task::Ptr m_currentTask; QHash m_updateMetadataTasks; }; PrismLauncher-11.0.3/launcher/modplatform/CheckUpdateTask.h0000644000175100017510000000446715224505336023277 0ustar runnerrunner#pragma once #include "minecraft/mod/tasks/GetModDependenciesTask.h" #include "modplatform/ModIndex.h" #include "tasks/Task.h" class ResourceDownloadTask; class ModFolderModel; class CheckUpdateTask : public Task { Q_OBJECT public: CheckUpdateTask(QList& resources, std::vector& mcVersions, QList loadersList, ResourceFolderModel* resourceModel) : m_resources(resources), m_gameVersions(mcVersions), m_loadersList(std::move(loadersList)), m_resourceModel(resourceModel) {} struct Update { QString name; QString oldHash; QString oldVersion; QString newVersion; std::optional newVersionType; QString changelog; ModPlatform::ResourceProvider provider; shared_qobject_ptr download; bool enabled = true; public: Update(QString name, QString oldH, QString oldV, QString newV, std::optional newVType, QString changelog, ModPlatform::ResourceProvider p, shared_qobject_ptr t, bool enabled = true) : name(std::move(name)) , oldHash(std::move(oldH)) , oldVersion(std::move(oldV)) , newVersion(std::move(newV)) , newVersionType(newVType) , changelog(std::move(changelog)) , provider(p) , download(std::move(t)) , enabled(enabled) {} }; auto getUpdates() -> std::vector&& { return std::move(m_updates); } auto getDependencies() -> QList>&& { return std::move(m_deps); } protected slots: void executeTask() override = 0; signals: void checkFailed(Resource* failed, QString reason, QUrl recoverUrl = {}); protected: QList& m_resources; std::vector& m_gameVersions; QList m_loadersList; ResourceFolderModel* m_resourceModel; std::vector m_updates; QList> m_deps; }; PrismLauncher-11.0.3/launcher/QObjectPtr.h0000644000175100017510000000315115224505336017752 0ustar runnerrunner#pragma once #include #include #include #include /** * A unique pointer class with unique pointer semantics intended for derivates of QObject * Calls deleteLater() instead of destroying the contained object immediately */ template using unique_qobject_ptr = QScopedPointer; /** * A shared pointer class with shared pointer semantics intended for derivates of QObject * Calls deleteLater() instead of destroying the contained object immediately */ template class shared_qobject_ptr : public QSharedPointer { public: constexpr explicit shared_qobject_ptr() : QSharedPointer() {} constexpr explicit shared_qobject_ptr(T* ptr) : QSharedPointer(ptr, &QObject::deleteLater) {} constexpr shared_qobject_ptr(std::nullptr_t null_ptr) : QSharedPointer(null_ptr, &QObject::deleteLater) {} template constexpr shared_qobject_ptr(const shared_qobject_ptr& other) : QSharedPointer(other) {} template constexpr shared_qobject_ptr(const QSharedPointer& other) : QSharedPointer(other) {} void reset() { QSharedPointer::reset(); } void reset(T* other) { shared_qobject_ptr t(other); this->swap(t); } void reset(const shared_qobject_ptr& other) { shared_qobject_ptr t(other); this->swap(t); } }; template shared_qobject_ptr makeShared(Args... args) { auto obj = new T(args...); return shared_qobject_ptr(obj); } PrismLauncher-11.0.3/launcher/net/0000755000175100017510000000000015224505336016352 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/net/Download.cpp0000644000175100017510000000617615224505336020637 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Download.h" #include #include #include #include #include "ByteArraySink.h" #include "ChecksumValidator.h" #include "MetaCacheSink.h" namespace Net { #if defined(LAUNCHER_APPLICATION) auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr { auto dl = makeShared(); dl->m_url = url; dl->setObjectName(QString("CACHE:") + url.toString()); dl->m_options = options; auto md5Node = new ChecksumValidator(QCryptographicHash::Md5); auto cachedNode = new MetaCacheSink(entry, md5Node, options.testFlag(Option::MakeEternal)); dl->m_sink.reset(cachedNode); return dl; } #endif auto Download::makeByteArray(QUrl url, Options options) -> std::pair { auto dl = makeShared(); dl->m_url = url; dl->setObjectName(QString("BYTES:") + url.toString()); dl->m_options = options; auto sink = std::make_unique(); QByteArray* response = sink->output(); dl->m_sink = std::move(sink); return { dl, response }; } auto Download::makeFile(QUrl url, QString path, Options options) -> Download::Ptr { auto dl = makeShared(); dl->m_url = url; dl->setObjectName(QString("FILE:") + url.toString()); dl->m_options = options; dl->m_sink.reset(new FileSink(path)); return dl; } QNetworkReply* Download::getReply(QNetworkRequest& request) { return m_network->get(request); } } // namespace Net PrismLauncher-11.0.3/launcher/net/NetRequest.cpp0000644000175100017510000003513715224505336021166 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "NetRequest.h" #include #include #include #include #include #include #include #if defined(LAUNCHER_APPLICATION) #include "Application.h" #include "settings/SettingsObject.h" #endif #include "BuildConfig.h" #include "MMCTime.h" #include "StringUtils.h" namespace Net { NetRequest::NetRequest() : Task() { connect(&m_retryTimer, &QTimer::timeout, this, &NetRequest::executeTask); } void NetRequest::addValidator(Validator* v) { m_sink->addValidator(v); } void NetRequest::executeTask() { setStatus(tr("Requesting %1").arg(StringUtils::truncateUrlHumanFriendly(m_url, 80))); if (getState() == Task::State::AbortedByUser) { qCWarning(logCat) << getUid().toString() << "Attempt to start an aborted Request:" << m_url.toString(); emit aborted(); emit finished(); return; } QNetworkRequest request(m_url); m_state = m_sink->init(request); switch (m_state) { case State::Succeeded: qCDebug(logCat) << getUid().toString() << "Request cache hit" << m_url.toString(); emit succeeded(); emit finished(); return; case State::Running: qCDebug(logCat) << getUid().toString() << "Running" << m_url.toString(); break; case State::Inactive: case State::Failed: m_failReason = m_sink->failReason(); emit failed(m_sink->failReason()); emit finished(); return; case State::AbortedByUser: emit aborted(); emit finished(); return; } #if defined(LAUNCHER_APPLICATION) auto user_agent = APPLICATION->getUserAgent(); #else auto user_agent = BuildConfig.USER_AGENT; #endif request.setHeader(QNetworkRequest::UserAgentHeader, user_agent.toUtf8()); for (auto& header_proxy : m_headerProxies) { header_proxy->writeHeaders(request); } #if defined(LAUNCHER_APPLICATION) request.setTransferTimeout(APPLICATION->settings()->get("RequestTimeout").toInt() * 1000); #else request.setTransferTimeout(); #endif m_last_progress_time = m_clock.now(); m_last_progress_bytes = 0; auto rep = getReply(request); if (rep == nullptr) // it failed return; m_reply.reset(rep); connect(rep, &QNetworkReply::uploadProgress, this, &NetRequest::onProgress); connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::onProgress); connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished); connect(rep, &QNetworkReply::errorOccurred, this, &NetRequest::downloadError); connect(rep, &QNetworkReply::sslErrors, this, &NetRequest::sslErrors); connect(rep, &QNetworkReply::readyRead, this, &NetRequest::downloadReadyRead); } void NetRequest::onProgress(qint64 bytesReceived, qint64 bytesTotal) { auto now = m_clock.now(); auto elapsed = now - m_last_progress_time; // use milliseconds for speed precision auto elapsed_ms = std::chrono::duration_cast(elapsed); auto bytes_received_since = bytesReceived - m_last_progress_bytes; auto dl_speed_bps = (double)bytes_received_since / elapsed_ms.count() * 1000; auto remaining_time_s = (bytesTotal - bytesReceived) / dl_speed_bps; //: Current amount of bytes downloaded, out of the total amount of bytes in the download QString dl_progress = tr("%1 / %2").arg(StringUtils::humanReadableFileSize(bytesReceived)).arg(StringUtils::humanReadableFileSize(bytesTotal)); QString dl_speed_str; if (elapsed_ms.count() > 0) { auto str_eta = bytesTotal > 0 ? Time::humanReadableDuration(remaining_time_s) : tr("unknown"); //: Download speed, in bytes per second (remaining download time in parenthesis) dl_speed_str = tr("%1 /s (%2)").arg(StringUtils::humanReadableFileSize(dl_speed_bps)).arg(str_eta); } else { //: Download speed at 0 bytes per second dl_speed_str = tr("0 B/s"); } setDetails(dl_progress + "\n" + dl_speed_str); setProgress(bytesReceived, bytesTotal); } void NetRequest::downloadError(QNetworkReply::NetworkError error) { if (error == QNetworkReply::OperationCanceledError) { qCCritical(logCat) << getUid().toString() << "Aborted" << m_url.toString(); m_state = State::Failed; } else if (replyStatusCode() == 429 /* HTTP Too Many Requests*/ && m_options & Option::AutoRetry) { qCDebug(logCat) << getUid().toString() << "Rate Limited!"; int64_t delay = 10 * std::pow(2, m_retryCount); if (m_reply->hasRawHeader("Retry-After")) { auto retryAfter = m_reply->rawHeader("Retry-After"); if (retryAfter.trimmed().endsWith("GMT")) /* HTTP Date format */ { auto afterTimestamp = QDateTime::fromString(QString::fromUtf8(retryAfter.trimmed()), "ddd, dd MMM yyyy HH:mm:ss 'GMT'"); auto now = QDateTime::currentDateTime(); delay = now.secsTo(afterTimestamp); } else { delay = retryAfter.toLong(); } } handleAutoRetry(delay); } else { if (m_options & Option::AcceptLocalFiles) { if (m_sink->hasLocalData()) { m_state = State::Succeeded; return; } } // error happened during download. qCCritical(logCat) << getUid().toString() << "Failed" << m_url.toString() << "with error" << error; if (m_reply) qCCritical(logCat) << getUid().toString() << "HTTP status:" << replyStatusCode() << errorString(); if (m_errorResponse.size() > 0) qCCritical(logCat) << getUid().toString() << "Response from server:" << m_errorResponse; m_state = State::Failed; } } void NetRequest::sslErrors(const QList& errors) { int i = 1; for (auto error : errors) { qCCritical(logCat).nospace() << getUid().toString() << " Request " << m_url.toString() << " SSL Error #" << i << ": " << error.errorString(); auto cert = error.certificate(); qCCritical(logCat) << getUid().toString() << "Certificate in question:\n" << cert.toText(); i++; } } auto NetRequest::handleRedirect() -> bool { QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl(); if (!redirect.isValid()) { if (!m_reply->hasRawHeader("Location")) { // no redirect -> it's fine to continue return false; } // there is a Location header, but it's not correct. we need to apply some workarounds... QByteArray redirectBA = m_reply->rawHeader("Location"); if (redirectBA.size() == 0) { // empty, yet present redirect header? WTF? return false; } QString redirectStr = QString::fromUtf8(redirectBA); if (redirectStr.startsWith("//")) { /* * IF the URL begins with //, we need to insert the URL scheme. * See: https://bugreports.qt.io/browse/QTBUG-41061 * See: http://tools.ietf.org/html/rfc3986#section-4.2 */ redirectStr = m_reply->url().scheme() + ":" + redirectStr; } else if (redirectStr.startsWith("/")) { /* * IF the URL begins with /, we need to process it as a relative URL */ auto url = m_reply->url(); url.setPath(redirectStr, QUrl::TolerantMode); redirectStr = url.toString(); } /* * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues. * FIXME: report Qt bug for this */ redirect = QUrl(redirectStr, QUrl::TolerantMode); if (!redirect.isValid()) { qCWarning(logCat) << getUid().toString() << "Failed to parse redirect URL:" << redirectStr; downloadError(QNetworkReply::ProtocolFailure); return false; } qCDebug(logCat) << getUid().toString() << "Fixed location header:" << redirect; } else { qCDebug(logCat) << getUid().toString() << "Location header:" << redirect; } m_url = QUrl(redirect.toString()); qCDebug(logCat) << getUid().toString() << "Following redirect to" << m_url.toString(); executeTask(); return true; } void NetRequest::handleAutoRetry(int64_t delay) { m_retryCount++; if (delay > 60 || m_retryCount > 4) { /* 1 minute is too long to wait for retry, fail for now */ m_state = State::Failed; auto retryAfter = QDateTime::currentDateTime().addSecs(delay); emitFailed(tr("Request Rate Limited for %n second(s): Retry After %1", "seconds", delay) .arg(retryAfter.toLocalTime().toString(QLocale::system().dateTimeFormat(QLocale::ShortFormat)))); return; } else { qCDebug(logCat) << getUid().toString() << "Retyring Request in" << delay << "seconds"; setStatus(tr("Rate Limited: Waiting %n second(s)", "seconds", delay)); m_retryTimer.setTimerType(Qt::VeryCoarseTimer); m_retryTimer.setSingleShot(true); m_retryTimer.setInterval(delay * 1000); m_retryTimer.start(); } } void NetRequest::downloadFinished() { // currently waiting for retry if (m_retryTimer.isActive()) { return; } // handle HTTP redirection first if (handleRedirect()) { qCDebug(logCat) << getUid().toString() << "Request redirected:" << m_url.toString(); return; } // if the download failed before this point ... if (m_state == State::Succeeded) // pretend to succeed so we continue processing :) { qCDebug(logCat) << getUid().toString() << "Request failed but we are allowed to proceed:" << m_url.toString(); m_sink->abort(); emit succeeded(); emit finished(); return; } else if (m_state == State::Failed) { qCDebug(logCat) << getUid().toString() << "Request failed in previous step:" << m_url.toString(); m_sink->abort(); m_failReason = m_reply->errorString(); emit failed(m_reply->errorString()); emit finished(); return; } else if (m_state == State::AbortedByUser) { qCDebug(logCat) << getUid().toString() << "Request aborted in previous step:" << m_url.toString(); m_sink->abort(); emit aborted(); emit finished(); return; } // make sure we got all the remaining data, if any auto data = m_reply->readAll(); if (data.size()) { qCDebug(logCat) << getUid().toString() << "Writing extra" << data.size() << "bytes"; m_state = m_sink->write(data); if (m_state != State::Succeeded) { qCDebug(logCat) << getUid().toString() << "Request failed to write:" << m_url.toString(); m_sink->abort(); m_failReason = m_sink->failReason(); emit failed(m_sink->failReason()); emit finished(); return; } } // otherwise, finalize the whole graph m_state = m_sink->finalize(*m_reply.get()); if (m_state != State::Succeeded) { qCDebug(logCat) << getUid().toString() << "Request failed to finalize:" << m_url.toString(); m_sink->abort(); m_failReason = m_sink->failReason(); emit failed(m_sink->failReason()); emit finished(); return; } qCDebug(logCat) << getUid().toString() << "Request succeeded:" << m_url.toString(); emit succeeded(); emit finished(); } void NetRequest::downloadReadyRead() { if (m_state == State::Running) { auto data = m_reply->readAll(); m_state = m_sink->write(data); if (replyStatusCode() >= 400) { m_errorResponse.append(data); } if (m_state == State::Failed) { qCCritical(logCat) << getUid().toString() << "Failed to process response chunk:" << m_sink->failReason(); } // qDebug() << "Request" << m_url.toString() << "gained" << data.size() << "bytes"; } else { qCCritical(logCat) << getUid().toString() << "Cannot write download data! illegal status" << m_status; } } auto NetRequest::abort() -> bool { m_state = State::AbortedByUser; if (m_reply) { disconnect(m_reply.get(), &QNetworkReply::errorOccurred, nullptr, nullptr); m_reply->abort(); } return true; } int NetRequest::replyStatusCode() const { return m_reply ? m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() : -1; } QNetworkReply::NetworkError NetRequest::error() const { return m_reply ? m_reply->error() : QNetworkReply::NoError; } QUrl NetRequest::url() const { return m_url; } QString NetRequest::errorString() const { return m_reply ? m_reply->errorString() : ""; } void NetRequest::enableAutoRetry(bool enable) { if (enable) { m_options |= Option::AutoRetry; } else { m_options &= ~static_cast(Option::AutoRetry); } } } // namespace Net PrismLauncher-11.0.3/launcher/net/Validator.h0000644000175100017510000000342315224505336020452 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include namespace Net { class Validator { public: /* con/des */ Validator() {} virtual ~Validator() {} public: /* methods */ virtual bool init(QNetworkRequest& request) = 0; virtual bool write(QByteArray& data) = 0; virtual bool abort() = 0; virtual bool validate(QNetworkReply& reply) = 0; }; } // namespace Net PrismLauncher-11.0.3/launcher/net/MetaCacheSink.h0000644000175100017510000000406515224505336021167 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "ChecksumValidator.h" #include "FileSink.h" #include "net/HttpMetaCache.h" namespace Net { class MetaCacheSink : public FileSink { public: MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum, bool is_eternal = false); virtual ~MetaCacheSink() = default; auto hasLocalData() -> bool override; protected: auto initCache(QNetworkRequest& request) -> Task::State override; auto finalizeCache(QNetworkReply& reply) -> Task::State override; private: MetaEntryPtr m_entry; ChecksumValidator* m_md5Node; bool m_is_eternal; }; } // namespace Net PrismLauncher-11.0.3/launcher/net/ApiDownload.cpp0000644000175100017510000000323515224505336021262 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "net/ApiDownload.h" #include #include "net/ApiHeaderProxy.h" namespace Net { Download::Ptr ApiDownload::makeCached(QUrl url, MetaEntryPtr entry, Download::Options options) { auto dl = Download::makeCached(std::move(url), std::move(entry), options); dl->addHeaderProxy(std::make_unique()); return dl; } std::pair ApiDownload::makeByteArray(QUrl url, Download::Options options) { auto [dl, response] = Download::makeByteArray(std::move(url), options); dl->addHeaderProxy(std::make_unique()); return { dl, response }; } Download::Ptr ApiDownload::makeFile(QUrl url, QString path, Download::Options options, ModrinthDownloadMeta meta) { auto dl = Download::makeFile(std::move(url), std::move(path), options); dl->addHeaderProxy(std::make_unique(std::move(meta))); return dl; } } // namespace Net PrismLauncher-11.0.3/launcher/net/HttpMetaCache.cpp0000644000175100017510000002537115224505336021540 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "HttpMetaCache.h" #include "FileSystem.h" #include "Json.h" #include #include #include #include #include #include "net/Logging.h" auto MetaEntry::getFullPath() -> QString { // FIXME: make local? return FS::PathCombine(m_basePath, m_relativePath); } HttpMetaCache::HttpMetaCache(QString path) : QObject(), m_index_file(path) { saveBatchingTimer.setSingleShot(true); saveBatchingTimer.setTimerType(Qt::VeryCoarseTimer); connect(&saveBatchingTimer, &QTimer::timeout, this, &HttpMetaCache::SaveNow); } HttpMetaCache::~HttpMetaCache() { saveBatchingTimer.stop(); SaveNow(); } auto HttpMetaCache::getEntry(QString base, QString resource_path) -> MetaEntryPtr { // no base. no base path. can't store if (!m_entries.contains(base)) { // TODO: log problem return {}; } EntryMap& map = m_entries[base]; if (map.entry_list.contains(resource_path)) { return map.entry_list[resource_path]; } return {}; } auto HttpMetaCache::resolveEntry(QString base, QString resource_path, QString expected_etag) -> MetaEntryPtr { resource_path = FS::RemoveInvalidPathChars(resource_path); auto entry = getEntry(base, resource_path); // it's not present? generate a default stale entry if (!entry) { return staleEntry(base, resource_path); } auto& selected_base = m_entries[base]; QString real_path = FS::PathCombine(selected_base.base_path, resource_path); QFileInfo finfo(real_path); // is the file really there? if not -> stale if (!finfo.isFile() || !finfo.isReadable()) { // if the file doesn't exist, we disown the entry selected_base.entry_list.remove(resource_path); return staleEntry(base, resource_path); } if (!expected_etag.isEmpty() && expected_etag != entry->m_etag) { // if the etag doesn't match expected, we disown the entry selected_base.entry_list.remove(resource_path); return staleEntry(base, resource_path); } // if the file changed, check md5sum qint64 file_last_changed = finfo.lastModified().toUTC().toMSecsSinceEpoch(); if (file_last_changed != entry->m_local_changed_timestamp) { QFile input(real_path); if (!input.open(QIODevice::ReadOnly)) { qWarning() << "Failed to open file" << input.fileName() << "for reading:" << input.errorString(); return staleEntry(base, resource_path); } QString md5sum = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Md5).toHex().constData(); if (entry->m_md5sum != md5sum) { selected_base.entry_list.remove(resource_path); return staleEntry(base, resource_path); } // md5sums matched... keep entry and save the new state to file entry->m_local_changed_timestamp = file_last_changed; SaveEventually(); } // Get rid of old entries, to prevent cache problems auto current_time = QDateTime::currentSecsSinceEpoch(); if (entry->isExpired(current_time - (file_last_changed / 1000))) { qCWarning(taskNetLogC) << "[HttpMetaCache]" << "Removing cache entry because of old age!"; selected_base.entry_list.remove(resource_path); return staleEntry(base, resource_path); } // entry passed all the checks we cared about. entry->m_basePath = getBasePath(base); return entry; } auto HttpMetaCache::updateEntry(MetaEntryPtr stale_entry) -> bool { if (!m_entries.contains(stale_entry->m_baseId)) { qCCritical(taskHttpMetaCacheLogC) << "Cannot add entry with unknown base:" << stale_entry->m_baseId.toLocal8Bit(); return false; } if (stale_entry->m_stale) { qCCritical(taskHttpMetaCacheLogC) << "Cannot add stale entry:" << stale_entry->getFullPath().toLocal8Bit(); return false; } m_entries[stale_entry->m_baseId].entry_list[stale_entry->m_relativePath] = stale_entry; SaveEventually(); return true; } auto HttpMetaCache::evictEntry(MetaEntryPtr entry) -> bool { if (!entry) return false; entry->m_stale = true; SaveEventually(); return true; } // returns true on success, false otherwise auto HttpMetaCache::evictAll() -> bool { bool ret = true; for (QString& base : m_entries.keys()) { EntryMap& map = m_entries[base]; qCDebug(taskHttpMetaCacheLogC) << "Evicting base" << base; for (MetaEntryPtr entry : map.entry_list) { if (!evictEntry(entry)) qCWarning(taskHttpMetaCacheLogC) << "Unexpected missing cache entry" << entry->m_basePath; } map.entry_list.clear(); // AND all return codes together so the result is true iff all runs of deletePath() are true ret &= FS::deleteContents(map.base_path); } return ret; } auto HttpMetaCache::staleEntry(QString base, QString resource_path) -> MetaEntryPtr { auto foo = new MetaEntry(); foo->m_baseId = base; foo->m_basePath = getBasePath(base); foo->m_relativePath = resource_path; foo->m_stale = true; return MetaEntryPtr(foo); } void HttpMetaCache::addBase(QString base, QString base_root) { // TODO: report error if (m_entries.contains(base)) return; // TODO: check if the base path is valid EntryMap foo; foo.base_path = base_root; m_entries[base] = foo; } auto HttpMetaCache::getBasePath(QString base) -> QString { if (m_entries.contains(base)) { return m_entries[base].base_path; } return {}; } void HttpMetaCache::Load() { if (m_index_file.isNull()) return; QFile index(m_index_file); if (!index.open(QIODevice::ReadOnly)) return; QJsonParseError parseError; QJsonDocument json = QJsonDocument::fromJson(index.readAll(), &parseError); // Fail if the JSON is invalid. if (parseError.error != QJsonParseError::NoError) { qCritical() << QString("Failed to parse HttpMetaCache file: %1 at offset %2") .arg(parseError.errorString(), QString::number(parseError.offset)) .toUtf8(); return; } // Make sure the root is an object. if (!json.isObject()) { qCritical() << "HttpMetaCache root should be an object."; return; } auto root = json.object(); // check file version first auto version_val = root["version"].toString(); if (version_val != "1") return; // read the entry array auto array = root["entries"].toArray(); for (auto element : array) { auto element_obj = element.toObject(); auto base = element_obj["base"].toString(); if (!m_entries.contains(base)) continue; auto& entrymap = m_entries[base]; auto foo = new MetaEntry(); foo->m_baseId = base; foo->m_relativePath = element_obj["path"].toString(); foo->m_md5sum = element_obj["md5sum"].toString(); foo->m_etag = element_obj["etag"].toString(); foo->m_local_changed_timestamp = element_obj["last_changed_timestamp"].toDouble(); foo->m_remote_changed_timestamp = element_obj["remote_changed_timestamp"].toString(); foo->makeEternal(element_obj[QStringLiteral("eternal")].toBool()); if (!foo->isEternal()) { foo->m_current_age = element_obj["current_age"].toDouble(); foo->m_max_age = element_obj["max_age"].toDouble(); } // presumed innocent until closer examination foo->m_stale = false; entrymap.entry_list[foo->m_relativePath] = MetaEntryPtr(foo); } } void HttpMetaCache::SaveEventually() { // reset the save timer saveBatchingTimer.stop(); saveBatchingTimer.start(30000); } void HttpMetaCache::SaveNow() { if (m_index_file.isNull()) return; qCDebug(taskHttpMetaCacheLogC) << "Saving metacache with" << m_entries.size() << "entries"; QJsonObject toplevel; Json::writeString(toplevel, "version", "1"); QJsonArray entriesArr; for (auto group : m_entries) { for (auto entry : group.entry_list) { // do not save stale entries. they are dead. if (entry->m_stale) { continue; } QJsonObject entryObj; Json::writeString(entryObj, "base", entry->m_baseId); Json::writeString(entryObj, "path", entry->m_relativePath); Json::writeString(entryObj, "md5sum", entry->m_md5sum); Json::writeString(entryObj, "etag", entry->m_etag); entryObj.insert("last_changed_timestamp", QJsonValue(double(entry->m_local_changed_timestamp))); if (!entry->m_remote_changed_timestamp.isEmpty()) entryObj.insert("remote_changed_timestamp", QJsonValue(entry->m_remote_changed_timestamp)); if (entry->isEternal()) { entryObj.insert("eternal", true); } else { entryObj.insert("current_age", QJsonValue(double(entry->m_current_age))); entryObj.insert("max_age", QJsonValue(double(entry->m_max_age))); } entriesArr.append(entryObj); } } toplevel.insert("entries", entriesArr); try { Json::write(toplevel, m_index_file); } catch (const Exception& e) { qCWarning(taskHttpMetaCacheLogC) << "Error writing cache:" << e.what(); } } PrismLauncher-11.0.3/launcher/net/MetaCacheSink.cpp0000644000175100017510000001150115224505336021513 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MetaCacheSink.h" #include #include #include #include "Application.h" #include "net/Logging.h" namespace Net { /** Maximum time to hold a cache entry * = 1 week in seconds */ #define MAX_TIME_TO_EXPIRE 1 * 7 * 24 * 60 * 60 MetaCacheSink::MetaCacheSink(MetaEntryPtr entry, ChecksumValidator* md5sum, bool is_eternal) : Net::FileSink(entry->getFullPath()), m_entry(entry), m_md5Node(md5sum), m_is_eternal(is_eternal) { addValidator(md5sum); } Task::State MetaCacheSink::initCache(QNetworkRequest& request) { if (!m_entry->isStale()) { return Task::State::Succeeded; } // check if file exists, if it does, use its information for the request QFile current(m_filename); if (current.exists() && current.size() != 0) { if (m_entry->getRemoteChangedTimestamp().size()) { request.setRawHeader(QString("If-Modified-Since").toLatin1(), m_entry->getRemoteChangedTimestamp().toLatin1()); } if (m_entry->getETag().size()) { request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1()); } } return Task::State::Running; } Task::State MetaCacheSink::finalizeCache(QNetworkReply& reply) { QFileInfo output_file_info(m_filename); if (m_wroteAnyData) { m_entry->setMD5Sum(m_md5Node->hash().toHex().constData()); } m_entry->setETag(reply.rawHeader("ETag").constData()); if (reply.hasRawHeader("Last-Modified")) { m_entry->setRemoteChangedTimestamp(reply.rawHeader("Last-Modified").constData()); } m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch()); { // Cache lifetime if (m_is_eternal) { qCDebug(taskMetaCacheLogC) << "Adding eternal cache entry:" << m_entry->getFullPath(); m_entry->makeEternal(true); } else if (reply.hasRawHeader("Cache-Control")) { auto cache_control_header = reply.rawHeader("Cache-Control"); qCDebug(taskMetaCacheLogC) << "Parsing 'Cache-Control' header with" << cache_control_header; static const QRegularExpression s_maxAgeExpr("max-age=([0-9]+)"); qint64 max_age = s_maxAgeExpr.match(cache_control_header).captured(1).toLongLong(); m_entry->setMaximumAge(max_age); } else if (reply.hasRawHeader("Expires")) { auto expires_header = reply.rawHeader("Expires"); qCDebug(taskMetaCacheLogC) << "Parsing 'Expires' header with" << expires_header; qint64 max_age = QDateTime::fromString(expires_header).toSecsSinceEpoch() - QDateTime::currentSecsSinceEpoch(); m_entry->setMaximumAge(max_age); } else { m_entry->setMaximumAge(MAX_TIME_TO_EXPIRE); } if (reply.hasRawHeader("Age")) { auto age_header = reply.rawHeader("Age"); qCDebug(taskMetaCacheLogC) << "Parsing 'Age' header with" << age_header; qint64 current_age = age_header.toLongLong(); m_entry->setCurrentAge(current_age); } else { m_entry->setCurrentAge(0); } } m_entry->setStale(false); APPLICATION->metacache()->updateEntry(m_entry); return Task::State::Succeeded; } bool MetaCacheSink::hasLocalData() { QFileInfo info(m_filename); return info.exists() && info.size() != 0; } } // namespace Net PrismLauncher-11.0.3/launcher/net/Upload.h0000644000175100017510000000430515224505336017751 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include "net/NetRequest.h" namespace Net { class Upload : public NetRequest { Q_OBJECT public: using Ptr = shared_qobject_ptr; explicit Upload() : NetRequest() { logCat = taskUploadLogC; }; /** * Creates a request downloading to the returned QByteArray,. * The QByteArray will live as long as the Upload object. */ static std::pair makeByteArray(QUrl url, QByteArray m_post_data); protected: virtual QNetworkReply* getReply(QNetworkRequest&) override; QByteArray m_post_data; }; } // namespace Net PrismLauncher-11.0.3/launcher/net/Sink.h0000644000175100017510000000607015224505336017432 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "Validator.h" #include "tasks/Task.h" namespace Net { class Sink { public: Sink() = default; virtual ~Sink() = default; public: virtual auto init(QNetworkRequest& request) -> Task::State = 0; virtual auto write(QByteArray& data) -> Task::State = 0; virtual auto abort() -> Task::State = 0; virtual auto finalize(QNetworkReply& reply) -> Task::State = 0; virtual auto hasLocalData() -> bool = 0; QString failReason() const { return m_fail_reason; } void addValidator(Validator* validator) { if (validator) { validators.push_back(std::shared_ptr(validator)); } } protected: bool initAllValidators(QNetworkRequest& request) { for (auto& validator : validators) { if (!validator->init(request)) return false; } return true; } bool finalizeAllValidators(QNetworkReply& reply) { for (auto& validator : validators) { if (!validator->validate(reply)) return false; } return true; } bool failAllValidators() { bool success = true; for (auto& validator : validators) { success &= validator->abort(); } return success; } bool writeAllValidators(QByteArray& data) { for (auto& validator : validators) { if (!validator->write(data)) return false; } return true; } protected: std::vector> validators; QString m_fail_reason; }; } // namespace Net PrismLauncher-11.0.3/launcher/net/NetUtils.h0000644000175100017510000000626015224505336020276 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include namespace Net { inline bool isApplicationError(QNetworkReply::NetworkError x) { // Mainly taken from https://github.com/qt/qtbase/blob/dev/src/network/access/qhttpthreaddelegate.cpp static QSet errors = { QNetworkReply::ProtocolInvalidOperationError, QNetworkReply::AuthenticationRequiredError, QNetworkReply::ContentAccessDenied, QNetworkReply::ContentNotFoundError, QNetworkReply::ContentOperationNotPermittedError, QNetworkReply::ProxyAuthenticationRequiredError, QNetworkReply::ContentConflictError, QNetworkReply::ContentGoneError, QNetworkReply::InternalServerError, QNetworkReply::OperationNotImplementedError, QNetworkReply::ServiceUnavailableError, QNetworkReply::UnknownServerError, QNetworkReply::UnknownContentError }; return errors.contains(x); } // 500 class errors, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/500 // microsoft may send these error codes when services (auth) are down. // We treat this as a reason to launch in offline mode. inline bool isServerError(QNetworkReply::NetworkError x) { static QSet errors = { QNetworkReply::InternalServerError, QNetworkReply::OperationNotImplementedError, QNetworkReply::ServiceUnavailableError, // 503 | seen in logs in 2026 //QNetworkReply::GatewayTimeoutError, // 504 | seen in logs in 2024 // Qt doesn't have it mapped. Unknown covers it QNetworkReply::UnknownServerError }; return errors.contains(x); } } // namespace Net PrismLauncher-11.0.3/launcher/net/Logging.h0000644000175100017510000000206215224505336020111 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include Q_DECLARE_LOGGING_CATEGORY(taskNetLogC) Q_DECLARE_LOGGING_CATEGORY(taskDownloadLogC) Q_DECLARE_LOGGING_CATEGORY(taskUploadLogC) Q_DECLARE_LOGGING_CATEGORY(taskMCSkinsLogC) Q_DECLARE_LOGGING_CATEGORY(taskMetaCacheLogC) Q_DECLARE_LOGGING_CATEGORY(taskHttpMetaCacheLogC) PrismLauncher-11.0.3/launcher/net/DummySink.h0000644000175100017510000000246115224505336020446 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2025 Octol1ttle * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once namespace Net { class DummySink : public Sink { public: explicit DummySink() {} ~DummySink() override {} auto init(QNetworkRequest& request) -> Task::State override { return Task::State::Running; } auto write(QByteArray& data) -> Task::State override { return Task::State::Succeeded; } auto abort() -> Task::State override { return Task::State::AbortedByUser; } auto finalize(QNetworkReply& reply) -> Task::State override { return Task::State::Succeeded; } auto hasLocalData() -> bool override { return false; } }; } // namespace Net PrismLauncher-11.0.3/launcher/net/ApiUpload.cpp0000644000175100017510000000213415224505336020734 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "net/ApiUpload.h" #include "net/ApiHeaderProxy.h" namespace Net { std::pair ApiUpload::makeByteArray(QUrl url, QByteArray m_post_data) { auto [up, response] = Upload::makeByteArray(url, m_post_data); up->addHeaderProxy(std::make_unique()); return { up, response }; } } // namespace Net PrismLauncher-11.0.3/launcher/net/NetJob.cpp0000644000175100017510000001414215224505336020241 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "NetJob.h" #include #include "net/NetRequest.h" #include "tasks/ConcurrentTask.h" #if defined(LAUNCHER_APPLICATION) #include "Application.h" #include "settings/SettingsObject.h" #include "ui/dialogs/NetworkJobFailedDialog.h" #endif NetJob::NetJob(QString job_name, QNetworkAccessManager* network, int max_concurrent) : ConcurrentTask(job_name), m_network(network) { #if defined(LAUNCHER_APPLICATION) if (APPLICATION_DYN && max_concurrent < 0) max_concurrent = APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt(); #endif if (max_concurrent > 0) setMaxConcurrent(max_concurrent); } auto NetJob::addNetAction(Net::NetRequest::Ptr action) -> bool { action->setNetwork(m_network); addTask(action); return true; } void NetJob::executeNextSubTask() { // We're finished, check for failures and retry if we can (up to 3 times) if (isRunning() && m_queue.isEmpty() && m_doing.isEmpty() && !m_failed.isEmpty() && m_try < 3) { m_try += 1; m_failed.removeIf([this](QHash::iterator task) { // there is no point in retying on 404 Not Found if (static_cast(task->get())->replyStatusCode() == 404) { return false; } m_done.remove(task->get()); m_queue.enqueue(*task); return true; }); } ConcurrentTask::executeNextSubTask(); } auto NetJob::size() const -> int { return m_queue.size() + m_doing.size() + m_done.size(); } auto NetJob::canAbort() const -> bool { bool canFullyAbort = true; // can abort the downloads on the queue? for (auto part : m_queue) canFullyAbort &= part->canAbort(); // can abort the active downloads? for (auto part : m_doing) canFullyAbort &= part->canAbort(); return canFullyAbort; } auto NetJob::abort() -> bool { // fail all downloads on the queue for (auto task : m_queue) m_failed.insert(task.get(), task); m_queue.clear(); if (m_doing.isEmpty()) { // no downloads to abort, NetJob is not running return true; } bool fullyAborted = true; // abort active downloads auto toKill = m_doing.values(); for (auto part : toKill) { fullyAborted &= part->abort(); } if (fullyAborted) emitAborted(); else emitFailed(tr("Failed to abort all tasks in the NetJob!")); return fullyAborted; } auto NetJob::getFailedActions() -> QList { QList failed; for (auto index : m_failed) { failed.push_back(dynamic_cast(index.get())); } return failed; } auto NetJob::getFailedFiles() -> QList { QList failed; for (auto index : m_failed) { failed.append(static_cast(index.get())->url().toString()); } return failed; } void NetJob::updateState() { emit progress(m_done.count(), totalSize()); setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)") .arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize()))); } bool NetJob::isOnline() { // check some errors that are ussually associated with the lack of internet for (auto job : getFailedActions()) { auto err = job->error(); if (err != QNetworkReply::HostNotFoundError && err != QNetworkReply::NetworkSessionFailedError) { return true; } } return false; }; void NetJob::emitFailed(QString reason) { #if defined(LAUNCHER_APPLICATION) if (APPLICATION_DYN && m_ask_retry && m_manual_try < APPLICATION->settings()->get("NumberOfManualRetries").toInt() && isOnline()) { m_manual_try++; auto failed = getFailedActions(); auto dialog = new NetworkJobFailedDialog(objectName(), m_try, m_done.size(), failed.size(), nullptr); dialog->setAttribute(Qt::WA_DeleteOnClose); for (const auto& request : failed) { dialog->addFailedRequest(request->url(), request->errorString()); } dialog->open(); connect(dialog, &QDialog::finished, this, [this, reason = std::move(reason)](int result) { if (result == QDialog::Accepted) { m_try = 0; executeNextSubTask(); } else { ConcurrentTask::emitFailed(reason); } }); return; } #endif ConcurrentTask::emitFailed(reason); } void NetJob::setAskRetry(bool askRetry) { m_ask_retry = askRetry; } PrismLauncher-11.0.3/launcher/net/HttpMetaCache.h0000644000175100017510000001107715224505336021203 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include class HttpMetaCache; class MetaEntry { friend class HttpMetaCache; protected: MetaEntry() = default; public: auto isStale() -> bool { return m_stale; } void setStale(bool stale) { m_stale = stale; } auto getFullPath() -> QString; auto getRemoteChangedTimestamp() -> QString { return m_remote_changed_timestamp; } void setRemoteChangedTimestamp(QString remote_changed_timestamp) { m_remote_changed_timestamp = remote_changed_timestamp; } void setLocalChangedTimestamp(qint64 timestamp) { m_local_changed_timestamp = timestamp; } auto getETag() -> QString { return m_etag; } void setETag(QString etag) { m_etag = etag; } auto getMD5Sum() -> QString { return m_md5sum; } void setMD5Sum(QString md5sum) { m_md5sum = md5sum; } /* Whether the entry expires after some time (false) or not (true). */ void makeEternal(bool eternal) { m_is_eternal = eternal; } bool isEternal() const { return m_is_eternal; } auto getCurrentAge() -> qint64 { return m_current_age; } void setCurrentAge(qint64 age) { m_current_age = age; } auto getMaximumAge() -> qint64 { return m_max_age; } void setMaximumAge(qint64 age) { m_max_age = age; } bool isExpired(qint64 offset) { return !m_is_eternal && (m_current_age >= m_max_age - offset); } protected: QString m_baseId; QString m_basePath; QString m_relativePath; QString m_md5sum; QString m_etag; qint64 m_local_changed_timestamp = 0; QString m_remote_changed_timestamp; // QString for now, RFC 2822 encoded time qint64 m_current_age = 0; qint64 m_max_age = 0; bool m_is_eternal = false; bool m_stale = true; }; using MetaEntryPtr = std::shared_ptr; class HttpMetaCache : public QObject { Q_OBJECT public: // supply path to the cache index file HttpMetaCache(QString path = QString()); ~HttpMetaCache() override; // get the entry solely from the cache // you probably don't want this, unless you have some specific caching needs. auto getEntry(QString base, QString resource_path) -> MetaEntryPtr; // get the entry from cache and verify that it isn't stale (within reason) auto resolveEntry(QString base, QString resource_path, QString expected_etag = QString()) -> MetaEntryPtr; // add a previously resolved stale entry auto updateEntry(MetaEntryPtr stale_entry) -> bool; // evict selected entry from cache auto evictEntry(MetaEntryPtr entry) -> bool; bool evictAll(); void addBase(QString base, QString base_root); // (re)start a timer that calls SaveNow later. void SaveEventually(); void Load(); auto getBasePath(QString base) -> QString; public slots: void SaveNow(); private: // create a new stale entry, given the parameters auto staleEntry(QString base, QString resource_path) -> MetaEntryPtr; struct EntryMap { QString base_path; QMap entry_list; }; QMap m_entries; QString m_index_file; QTimer saveBatchingTimer; }; PrismLauncher-11.0.3/launcher/net/Logging.cpp0000644000175100017510000000224415224505336020446 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "net/Logging.h" Q_LOGGING_CATEGORY(taskNetLogC, "launcher.task.net") Q_LOGGING_CATEGORY(taskDownloadLogC, "launcher.task.net.download") Q_LOGGING_CATEGORY(taskUploadLogC, "launcher.task.net.upload") Q_LOGGING_CATEGORY(taskMCSkinsLogC, "launcher.task.minecraft.skins") Q_LOGGING_CATEGORY(taskMetaCacheLogC, "launcher.task.net.metacache") Q_LOGGING_CATEGORY(taskHttpMetaCacheLogC, "launcher.task.net.metacache.http") PrismLauncher-11.0.3/launcher/net/PasteUpload.h0000644000175100017510000000556115224505336020753 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Lenny McLennington * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "net/ByteArraySink.h" #include "net/NetRequest.h" #include "tasks/Task.h" #include #include #include #include #include #include class PasteUpload : public Net::NetRequest { public: enum PasteType : int { // 0x0.st NullPointer, // hastebin.com Hastebin, // paste.gg PasteGG, // mclo.gs Mclogs, // Helpful to get the range of valid values on the enum for input sanitisation: First = NullPointer, Last = Mclogs }; struct PasteTypeInfo { const QString name; const QString defaultBase; const QString endpointPath; }; static const std::array PasteTypes; class Sink : public Net::ByteArraySink { public: Sink(PasteUpload* p) : m_d(p) {}; virtual ~Sink() = default; public: auto finalize(QNetworkReply& reply) -> Task::State override; private: PasteUpload* m_d; }; friend Sink; PasteUpload(const QString& log, QString url, PasteType pasteType); virtual ~PasteUpload() = default; QString pasteLink() { return m_pasteLink; } private: virtual QNetworkReply* getReply(QNetworkRequest&) override; QString m_log; QString m_pasteLink; QString m_baseUrl; const PasteType m_paste_type; }; PrismLauncher-11.0.3/launcher/net/ByteArraySink.h0000644000175100017510000000553215224505336021257 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "Sink.h" namespace Net { /* * Sink object for downloads that uses an owned QByteArray as a target. */ class ByteArraySink : public Sink { public: virtual ~ByteArraySink() = default; public: auto init(QNetworkRequest& request) -> Task::State override { m_output.clear(); if (initAllValidators(request)) return Task::State::Running; m_fail_reason = "Failed to initialize validators"; return Task::State::Failed; }; auto write(QByteArray& data) -> Task::State override { m_output.append(data); if (writeAllValidators(data)) return Task::State::Running; m_fail_reason = "Failed to write validators"; return Task::State::Failed; } auto abort() -> Task::State override { failAllValidators(); m_fail_reason = "Aborted"; return Task::State::Failed; } auto finalize(QNetworkReply& reply) -> Task::State override { if (finalizeAllValidators(reply)) return Task::State::Succeeded; m_fail_reason = "Failed to finalize validators"; return Task::State::Failed; } auto hasLocalData() -> bool override { return false; } QByteArray* output() { return &m_output; } protected: QByteArray m_output; }; } // namespace Net PrismLauncher-11.0.3/launcher/net/NetJob.h0000644000175100017510000000530515224505336017707 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "net/NetRequest.h" #include "tasks/ConcurrentTask.h" // Those are included so that they are also included by anyone using NetJob #include "net/Download.h" #include "net/HttpMetaCache.h" class NetJob : public ConcurrentTask { Q_OBJECT public: // TODO: delete using Ptr = shared_qobject_ptr; explicit NetJob(QString job_name, QNetworkAccessManager* network, int max_concurrent = -1); ~NetJob() override = default; auto size() const -> int; auto canAbort() const -> bool override; auto addNetAction(Net::NetRequest::Ptr action) -> bool; auto getFailedActions() -> QList; auto getFailedFiles() -> QList; void setAskRetry(bool askRetry); public slots: // Qt can't handle auto at the start for some reason? bool abort() override; void emitFailed(QString reason) override; protected slots: void executeNextSubTask() override; protected: void updateState() override; bool isOnline(); private: QNetworkAccessManager* m_network; int m_try = 1; bool m_ask_retry = true; int m_manual_try = 0; }; PrismLauncher-11.0.3/launcher/net/ChecksumValidator.h0000644000175100017510000000551715224505336022143 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "Validator.h" #include namespace Net { class ChecksumValidator : public Validator { public: ChecksumValidator(QCryptographicHash::Algorithm algorithm, QString expectedHex) : Net::ChecksumValidator(algorithm, QByteArray::fromHex(expectedHex.toLatin1())) {} ChecksumValidator(QCryptographicHash::Algorithm algorithm, QByteArray expected = QByteArray()) : m_checksum(algorithm), m_expected(expected) {}; virtual ~ChecksumValidator() = default; public: auto init(QNetworkRequest&) -> bool override { m_checksum.reset(); return true; } auto write(QByteArray& data) -> bool override { m_checksum.addData(data); return true; } auto abort() -> bool override { m_checksum.reset(); return true; } auto validate(QNetworkReply& reply) -> bool override { if (!m_expected.isEmpty() && m_expected != hash()) { qWarning() << "Checksum mismatch for URL:" << reply.url().toString() << "expected:" << m_expected << "got:" << hash(); return false; } return true; } auto hash() -> QByteArray { return m_checksum.result(); } void setExpected(QByteArray expected) { m_expected = expected; } private: QCryptographicHash m_checksum; QByteArray m_expected; }; } // namespace Net PrismLauncher-11.0.3/launcher/net/FileSink.cpp0000644000175100017510000001174215224505336020567 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FileSink.h" #include "FileSystem.h" #include "net/Logging.h" namespace Net { Task::State FileSink::init(QNetworkRequest& request) { auto result = initCache(request); if (result != Task::State::Running) { return result; } // create a new save file and open it for writing if (!FS::ensureFilePathExists(m_filename)) { qCCritical(taskNetLogC) << "Could not create folder for " + m_filename; m_fail_reason = "Could not create folder"; return Task::State::Failed; } m_wroteAnyData = false; m_output_file.reset(new PSaveFile(m_filename)); if (!m_output_file->open(QIODevice::WriteOnly)) { const auto error = QString("Could not open %1 for writing: %2").arg(m_filename).arg(m_output_file->errorString()); qCCritical(taskNetLogC) << error; m_fail_reason = error; return Task::State::Failed; } if (initAllValidators(request)) return Task::State::Running; m_fail_reason = "Failed to initialize validators"; return Task::State::Failed; } Task::State FileSink::write(QByteArray& data) { if (!writeAllValidators(data) || m_output_file->write(data) != data.size()) { QString error = QString("Failed writing into %1: %2").arg(m_filename); if (m_output_file->error() == QFileDevice::NoError) { error = error.arg("Validators failed"); } else { error = error.arg(m_output_file->errorString()); } qCCritical(taskNetLogC) << error; m_fail_reason = error; m_output_file->cancelWriting(); m_output_file.reset(); m_wroteAnyData = false; return Task::State::Failed; } m_wroteAnyData = true; return Task::State::Running; } Task::State FileSink::abort() { if (m_output_file) { m_output_file->cancelWriting(); } failAllValidators(); return Task::State::Failed; } Task::State FileSink::finalize(QNetworkReply& reply) { bool gotFile = false; QVariant statusCodeV = reply.attribute(QNetworkRequest::HttpStatusCodeAttribute); bool validStatus = false; int statusCode = statusCodeV.toInt(&validStatus); if (validStatus) { // this leaves out 304 Not Modified gotFile = statusCode == 200 || statusCode == 203; } // if we wrote any data to the save file, we try to commit the data to the real file. // if it actually got a proper file, we write it even if it was empty if (gotFile || m_wroteAnyData) { // ask validators for data consistency // we only do this for actual downloads, not 'your data is still the same' cache hits if (!finalizeAllValidators(reply)) { m_fail_reason = "Failed to finalize validators"; return Task::State::Failed; } // nothing went wrong... if (!m_output_file->commit()) { const auto error = QString("Failed to commit changes to %1: %2").arg(m_filename).arg(m_output_file->errorString()); qCCritical(taskNetLogC) << error; m_fail_reason = error; m_output_file->cancelWriting(); return Task::State::Failed; } } // then get rid of the save file m_output_file.reset(); return finalizeCache(reply); } Task::State FileSink::initCache(QNetworkRequest&) { return Task::State::Running; } Task::State FileSink::finalizeCache(QNetworkReply&) { return Task::State::Succeeded; } bool FileSink::hasLocalData() { QFileInfo info(m_filename); return info.exists() && info.size() != 0; } } // namespace Net PrismLauncher-11.0.3/launcher/net/HeaderProxy.h0000644000175100017510000000245015224505336020756 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include #include namespace Net { struct HeaderPair { QByteArray headerName; QByteArray headerValue; }; class HeaderProxy { public: HeaderProxy() {} virtual ~HeaderProxy() {} public: virtual QList headers(const QNetworkRequest& request) const = 0; public: void writeHeaders(QNetworkRequest& request) { for (auto header : headers(request)) { request.setRawHeader(header.headerName, header.headerValue); } } }; } // namespace Net PrismLauncher-11.0.3/launcher/net/NetRequest.h0000644000175100017510000000765715224505336020641 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include "HeaderProxy.h" #include "Sink.h" #include "Validator.h" #include "QObjectPtr.h" #include "net/Logging.h" #include "tasks/Task.h" namespace Net { class NetRequest : public Task { Q_OBJECT protected: explicit NetRequest(); public: using Ptr = shared_qobject_ptr; enum class Option { NoOptions = 0, AcceptLocalFiles = 1, MakeEternal = 2, AutoRetry = 4 }; Q_DECLARE_FLAGS(Options, Option) public: ~NetRequest() override = default; void addValidator(Validator* v); auto abort() -> bool override; auto canAbort() const -> bool override { return true; } void setNetwork(QNetworkAccessManager* network) { m_network = network; } void addHeaderProxy(std::unique_ptr proxy) { m_headerProxies.push_back(std::move(proxy)); } // automatically handle HTTP 429 Too Many Requests errors and retry void enableAutoRetry(bool enable); QUrl url() const; void setUrl(QUrl url) { m_url = url; } int replyStatusCode() const; QNetworkReply::NetworkError error() const; QString errorString() const; private: auto handleRedirect() -> bool; void handleAutoRetry(int64_t delay); virtual QNetworkReply* getReply(QNetworkRequest&) = 0; protected slots: void onProgress(qint64 bytesReceived, qint64 bytesTotal); void downloadError(QNetworkReply::NetworkError error); void sslErrors(const QList& errors); void downloadFinished(); void downloadReadyRead(); void executeTask() override; protected: std::unique_ptr m_sink; Options m_options; using logCatFunc = const QLoggingCategory& (*)(); logCatFunc logCat = taskUploadLogC; std::chrono::steady_clock m_clock; std::chrono::time_point m_last_progress_time; qint64 m_last_progress_bytes; QNetworkAccessManager* m_network; /// the network reply std::unique_ptr m_reply; QByteArray m_errorResponse; /// source URL QUrl m_url; std::vector> m_headerProxies; int m_retryCount = 0; QTimer m_retryTimer; }; } // namespace Net Q_DECLARE_OPERATORS_FOR_FLAGS(Net::NetRequest::Options) PrismLauncher-11.0.3/launcher/net/ApiDownload.h0000644000175100017510000000254615224505336020733 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include "Download.h" #include "net/ApiHeaderProxy.h" namespace Net { namespace ApiDownload { Download::Ptr makeCached(QUrl url, MetaEntryPtr entry, Download::Options options = Download::Option::NoOptions); std::pair makeByteArray(QUrl url, Download::Options options = Download::Option::NoOptions); Download::Ptr makeFile(QUrl url, QString path, Download::Options options = Download::Option::NoOptions, ModrinthDownloadMeta meta = ModrinthDownloadMeta()); }; // namespace ApiDownload } // namespace Net PrismLauncher-11.0.3/launcher/net/Mode.h0000644000175100017510000000010515224505336017403 0ustar runnerrunner#pragma once namespace Net { enum class Mode { Offline, Online }; } PrismLauncher-11.0.3/launcher/net/PasteUpload.cpp0000644000175100017510000002350615224505336021305 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Lenny McLennington * Copyright (C) 2022 Swirl * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "PasteUpload.h" #include #include #include #include #include #include #include "logs/AnonymizeLog.h" const std::array PasteUpload::PasteTypes = { { { "0x0.st", "https://0x0.st", "" }, { "hastebin", "https://hst.sh", "/documents" }, { "paste.gg", "https://paste.gg", "/api/v1/pastes" }, { "mclo.gs", "https://api.mclo.gs", "/1/log" } } }; QNetworkReply* PasteUpload::getReply(QNetworkRequest& request) { switch (m_paste_type) { case PasteUpload::NullPointer: { QHttpMultiPart* multiPart = new QHttpMultiPart{ QHttpMultiPart::FormDataType, this }; QHttpPart filePart; filePart.setBody(m_log.toUtf8()); filePart.setHeader(QNetworkRequest::ContentTypeHeader, "text/plain"); filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"file\"; filename=\"log.txt\""); multiPart->append(filePart); return m_network->post(request, multiPart); } case PasteUpload::Hastebin: { return m_network->post(request, m_log.toUtf8()); } case PasteUpload::Mclogs: { QUrlQuery postData; postData.addQueryItem("content", m_log); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); return m_network->post(request, postData.toString().toUtf8()); } case PasteUpload::PasteGG: { QJsonObject obj; QJsonDocument doc; request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); obj.insert("expires", QDateTime::currentDateTimeUtc().addDays(100).toString(Qt::DateFormat::ISODate)); QJsonArray files; QJsonObject logFileInfo; QJsonObject logFileContentInfo; logFileContentInfo.insert("format", "text"); logFileContentInfo.insert("value", m_log); logFileInfo.insert("name", "log.txt"); logFileInfo.insert("content", logFileContentInfo); files.append(logFileInfo); obj.insert("files", files); doc.setObject(obj); return m_network->post(request, doc.toJson()); } } return nullptr; }; auto PasteUpload::Sink::finalize(QNetworkReply& reply) -> Task::State { if (!finalizeAllValidators(reply)) { m_fail_reason = "Failed to finalize validators"; return Task::State::Failed; } int statusCode = reply.attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (reply.error() != QNetworkReply::NetworkError::NoError) { m_fail_reason = QObject::tr("Network error: %1").arg(reply.errorString()); return Task::State::Failed; } else if (statusCode != 200 && statusCode != 201) { QString reasonPhrase = reply.attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); m_fail_reason = QObject::tr("Error: %1 returned unexpected status code %2 %3").arg(m_d->url().toString()).arg(statusCode).arg(reasonPhrase); return Task::State::Failed; } switch (m_d->m_paste_type) { case PasteUpload::NullPointer: m_d->m_pasteLink = QString::fromUtf8(*output()).trimmed(); break; case PasteUpload::Hastebin: { QJsonParseError jsonError; auto doc = QJsonDocument::fromJson(*output(), &jsonError); if (jsonError.error != QJsonParseError::NoError) { qDebug() << "hastebin server did not reply with JSON" << jsonError.errorString(); m_fail_reason = QObject::tr("Failed to parse response from hastebin server: expected JSON but got an invalid response. Error: %1") .arg(jsonError.errorString()); return Task::State::Failed; } auto obj = doc.object(); if (obj.contains("key") && obj["key"].isString()) { QString key = doc.object()["key"].toString(); m_d->m_pasteLink = m_d->m_baseUrl + "/" + key; } else { qDebug() << "Log upload failed:" << doc.toJson(); m_fail_reason = QObject::tr("Error: %1 returned a malformed response body").arg(m_d->url().toString()); return Task::State::Failed; } break; } case PasteUpload::Mclogs: { QJsonParseError jsonError; auto doc = QJsonDocument::fromJson(*output(), &jsonError); if (jsonError.error != QJsonParseError::NoError) { qDebug() << "mclogs server did not reply with JSON" << jsonError.errorString(); m_fail_reason = QObject::tr("Failed to parse response from mclogs server: expected JSON but got an invalid response. Error: %1") .arg(jsonError.errorString()); return Task::State::Failed; } auto obj = doc.object(); if (obj.contains("success") && obj["success"].isBool()) { bool success = obj["success"].toBool(); if (success) { m_d->m_pasteLink = obj["url"].toString(); } else { QString error = obj["error"].toString(); m_fail_reason = QObject::tr("Error: %1 returned an error: %2").arg(m_d->url().toString(), error); return Task::State::Failed; } } else { qDebug() << "Log upload failed:" << doc.toJson(); m_fail_reason = QObject::tr("Error: %1 returned a malformed response body").arg(m_d->url().toString()); return Task::State::Failed; } break; } case PasteUpload::PasteGG: QJsonParseError jsonError; auto doc = QJsonDocument::fromJson(*output(), &jsonError); if (jsonError.error != QJsonParseError::NoError) { qDebug() << "pastegg server did not reply with JSON" << jsonError.errorString(); m_fail_reason = QObject::tr("Failed to parse response from pasteGG server: expected JSON but got an invalid response. Error: %1") .arg(jsonError.errorString()); return Task::State::Failed; } auto obj = doc.object(); if (obj.contains("status") && obj["status"].isString()) { QString status = obj["status"].toString(); if (status == "success") { m_d->m_pasteLink = m_d->m_baseUrl + "/p/anonymous/" + obj["result"].toObject()["id"].toString(); } else { QString error = obj["error"].toString(); QString message = (obj.contains("message") && obj["message"].isString()) ? obj["message"].toString() : "none"; m_fail_reason = QObject::tr("Error: %1 returned an error code: %2\nError message: %3").arg(m_d->url().toString(), error, message); return Task::State::Failed; } } else { qDebug() << "Log upload failed:" << doc.toJson(); m_fail_reason = QObject::tr("Error: %1 returned a malformed response body").arg(m_d->url().toString()); return Task::State::Failed; } break; } return Task::State::Succeeded; } PasteUpload::PasteUpload(const QString& log, QString url, PasteType pasteType) : m_log(log), m_baseUrl(url), m_paste_type(pasteType) { anonymizeLog(m_log); auto base = PasteUpload::PasteTypes.at(pasteType); if (m_baseUrl.isEmpty()) m_baseUrl = base.defaultBase; // HACK: Paste's docs say the standard API path is at /api/ but the official instance paste.gg doesn't follow that?? if (pasteType == PasteUpload::PasteGG && m_baseUrl == base.defaultBase) m_url = "https://api.paste.gg/v1/pastes"; else m_url = m_baseUrl + base.endpointPath; m_sink.reset(new Sink(this)); } PrismLauncher-11.0.3/launcher/net/Download.h0000644000175100017510000000503215224505336020272 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include "HttpMetaCache.h" #include "QObjectPtr.h" #include "net/NetRequest.h" namespace Net { class ByteArraySink; class Download : public NetRequest { Q_OBJECT public: using Ptr = shared_qobject_ptr; explicit Download() : NetRequest() { logCat = taskDownloadLogC; } #if defined(LAUNCHER_APPLICATION) static auto makeCached(QUrl url, MetaEntryPtr entry, Options options = Option::NoOptions) -> Download::Ptr; #endif /** * Creates a request downloading to the returned QByteArray,. * The QByteArray will live as long as the Download object. */ static auto makeByteArray(QUrl url, Options options = Option::NoOptions) -> std::pair; static auto makeFile(QUrl url, QString path, Options options = Option::NoOptions) -> Download::Ptr; protected: virtual QNetworkReply* getReply(QNetworkRequest&) override; }; } // namespace Net PrismLauncher-11.0.3/launcher/net/RawHeaderProxy.h0000644000175100017510000000332015224505336021425 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * Copyright (c) 2023 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include "net/HeaderProxy.h" namespace Net { class RawHeaderProxy : public HeaderProxy { public: RawHeaderProxy(QList headers = {}) : HeaderProxy(), m_headers(std::move(headers)) {}; virtual ~RawHeaderProxy() = default; public: virtual QList headers(const QNetworkRequest&) const override { return m_headers; }; void addHeader(const HeaderPair& header) { m_headers.append(header); } void addHeader(const QByteArray& headerName, const QByteArray& headerValue) { m_headers.append({ headerName, headerValue }); } void addHeaders(const QList& headers) { m_headers.append(headers); } void setHeaders(QList headers) { m_headers = headers; }; private: QList m_headers; }; } // namespace Net PrismLauncher-11.0.3/launcher/net/ApiHeaderProxy.h0000644000175100017510000000533515224505336021415 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include "Application.h" #include "BuildConfig.h" #include "net/HeaderProxy.h" #include #include namespace Net { struct ModrinthDownloadMeta { QString reason; QString gameVersion; QString loader; bool isEmpty() const { return reason.isEmpty(); } QByteArray toJson() const { QJsonObject obj; if (!reason.isEmpty()) { obj["reason"] = reason; } if (!gameVersion.isEmpty()) { obj["game_version"] = gameVersion; } if (!loader.isEmpty()) { obj["loader"] = loader; } return QJsonDocument(obj).toJson(QJsonDocument::Compact); } }; class ApiHeaderProxy : public HeaderProxy { public: ApiHeaderProxy() = default; explicit ApiHeaderProxy(ModrinthDownloadMeta meta) : m_meta(std::move(meta)) {} ~ApiHeaderProxy() override = default; public: QList headers(const QNetworkRequest& request) const override { QList hdrs; const auto host = request.url().host(); if (APPLICATION->capabilities() & Application::SupportsFlame && (host == QUrl(BuildConfig.FLAME_BASE_URL).host() || host == BuildConfig.FLAME_DOWNLOAD_HOST)) { hdrs.append({ .headerName = "x-api-key", .headerValue = APPLICATION->getFlameAPIKey().toUtf8() }); } else if (host == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || host == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { QString token = APPLICATION->getModrinthAPIToken(); if (!token.isNull()) { hdrs.append({ .headerName = "Authorization", .headerValue = token.toUtf8() }); } } if (host == BuildConfig.MODRINTH_DOWNLOAD_HOST && !m_meta.isEmpty()) { hdrs.append({ .headerName = "modrinth-download-meta", .headerValue = m_meta.toJson() }); } return hdrs; }; private: ModrinthDownloadMeta m_meta; }; } // namespace Net PrismLauncher-11.0.3/launcher/net/Upload.cpp0000644000175100017510000000456115224505336020310 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Upload.h" #include #include #include "ByteArraySink.h" namespace Net { QNetworkReply* Upload::getReply(QNetworkRequest& request) { if (!request.hasRawHeader("Content-Type")) request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); return m_network->post(request, m_post_data); } std::pair Upload::makeByteArray(QUrl url, QByteArray m_post_data) { auto up = makeShared(); up->m_url = std::move(url); auto sink = std::make_unique(); QByteArray* response = sink->output(); up->m_sink = std::move(sink); up->m_post_data = std::move(m_post_data); return { up, response }; } } // namespace Net PrismLauncher-11.0.3/launcher/net/ApiUpload.h0000644000175100017510000000166215224505336020406 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include "Upload.h" namespace Net { namespace ApiUpload { std::pair makeByteArray(QUrl url, QByteArray m_post_data); }; } // namespace Net PrismLauncher-11.0.3/launcher/net/FileSink.h0000644000175100017510000000433115224505336020230 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "PSaveFile.h" #include "Sink.h" namespace Net { class FileSink : public Sink { public: FileSink(QString filename) : m_filename(filename) {}; virtual ~FileSink() = default; public: auto init(QNetworkRequest& request) -> Task::State override; auto write(QByteArray& data) -> Task::State override; auto abort() -> Task::State override; auto finalize(QNetworkReply& reply) -> Task::State override; auto hasLocalData() -> bool override; protected: virtual auto initCache(QNetworkRequest&) -> Task::State; virtual auto finalizeCache(QNetworkReply& reply) -> Task::State; protected: QString m_filename; bool m_wroteAnyData = false; std::unique_ptr m_output_file; }; } // namespace Net PrismLauncher-11.0.3/launcher/MMCTime.h0000644000175100017510000000221515224505336017170 0ustar runnerrunner/* * Copyright 2021 Jamie Mansfield * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include namespace Time { QString prettifyDuration(int64_t duration, bool noDays = false); /** * @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`. * miliseconds are only included if `precision` is greater than 0. * * @param duration a number of seconds as floating point * @param precision number of decmial points to display on fractons of a second, defualts to 0. * @return QString */ QString humanReadableDuration(double duration, int precision = 0); } // namespace Time PrismLauncher-11.0.3/launcher/Markdown.cpp0000644000175100017510000000206015224505336020050 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Joshua Goins * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "Markdown.h" QString markdownToHTML(const QString& markdown) { const QByteArray markdownData = markdown.toUtf8(); char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE); QString htmlStr(buffer); free(buffer); return htmlStr; } PrismLauncher-11.0.3/launcher/archive/0000755000175100017510000000000015224505336017205 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/archive/ArchiveReader.cpp0000644000175100017510000002237315224505336022424 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only AND LicenseRef-PublicDomain /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2025 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additional note: Portions of this file are released into the public domain * under LicenseRef-PublicDomain. */ #include "ArchiveReader.h" #include #include #include #include #include #include #include #include namespace MMCZip { QStringList ArchiveReader::getFiles() { return m_fileNames; } bool ArchiveReader::collectFiles(bool onlyFiles) { return parse([this, onlyFiles](File* f) { if (!onlyFiles || f->isFile()) { m_fileNames << f->filename(); } return f->skip(); }); } using getPathFunc = std::function; static QString decodeLibArchivePath(archive_entry* entry, const getPathFunc& getUtf8Path, const getPathFunc& getPath) { auto fileName = QString::fromUtf8(getUtf8Path(entry)); if (fileName.isEmpty()) { fileName = QString::fromLocal8Bit(getPath(entry)); } return fileName; } QString ArchiveReader::File::filename() { return decodeLibArchivePath(m_entry, archive_entry_pathname_utf8, archive_entry_pathname); } QByteArray ArchiveReader::File::readAll(int* outStatus) { QByteArray data; const void* buff = nullptr; size_t size = 0; la_int64_t offset = 0; int status = 0; while ((status = archive_read_data_block(m_archive.get(), &buff, &size, &offset)) == ARCHIVE_OK) { data.append(static_cast(buff), static_cast(size)); } if (status != ARCHIVE_EOF && status != ARCHIVE_OK) { qWarning() << "libarchive read error:" << archive_error_string(m_archive.get()); } if (outStatus) { *outStatus = status; } return data; } QDateTime ArchiveReader::File::dateTime() { auto mtime = archive_entry_mtime(m_entry); auto mtime_nsec = archive_entry_mtime_nsec(m_entry); auto dt = QDateTime::fromSecsSinceEpoch(mtime); return dt.addMSecs(mtime_nsec / 1e6); } int ArchiveReader::File::readNextHeader() { return archive_read_next_header(m_archive.get(), &m_entry); } auto ArchiveReader::goToFile(const QString& filename) -> std::unique_ptr { auto f = std::make_unique(); auto* a = f->m_archive.get(); archive_read_support_format_all(a); archive_read_support_filter_all(a); auto fileName = m_archivePath.toStdWString(); if (archive_read_open_filename_w(a, fileName.data(), m_blockSize) != ARCHIVE_OK) { qCritical() << "Failed to open archive file:" << m_archivePath << "-" << archive_error_string(a); return nullptr; } while (f->readNextHeader() == ARCHIVE_OK) { if (f->filename() == filename) { return f; } f->skip(); } archive_read_close(a); return nullptr; } static int copy_data(struct archive* ar, struct archive* aw, bool notBlock = false) { int r = 0; const void* buff = nullptr; size_t size = 0; la_int64_t offset = 0; for (;;) { r = archive_read_data_block(ar, &buff, &size, &offset); if (r == ARCHIVE_EOF) { return ARCHIVE_OK; } if (r < ARCHIVE_OK) { qCritical() << "Failed reading data block:" << archive_error_string(ar); return (r); } if (notBlock) { r = archive_write_data(aw, buff, size); } else { r = archive_write_data_block(aw, buff, size, offset); } if (r < ARCHIVE_OK) { qCritical() << "Failed writing data block:" << archive_error_string(aw); return (r); } } } static bool willEscapeRoot(const QDir& root, archive_entry* entry) { auto entryPath = decodeLibArchivePath(entry, archive_entry_pathname_utf8, archive_entry_pathname); auto linkTarget = decodeLibArchivePath(entry, archive_entry_symlink_utf8, archive_entry_symlink); auto hardLink = decodeLibArchivePath(entry, archive_entry_hardlink_utf8, archive_entry_hardlink); if (entryPath.isEmpty() || (linkTarget.isEmpty() && hardLink.isEmpty())) { return false; } bool isHardLink = false; if (isHardLink = linkTarget.isEmpty(); isHardLink) { linkTarget = hardLink; } QString linkFullPath = root.filePath(entryPath); auto rootDir = QUrl::fromLocalFile(root.absolutePath()); if (!rootDir.isParentOf(QUrl::fromLocalFile(linkFullPath))) { return true; } QDir linkDir = QFileInfo(linkFullPath).dir(); if (!QDir::isAbsolutePath(linkTarget)) { linkTarget = (!isHardLink ? linkDir : root).filePath(linkTarget); } return !rootDir.isParentOf(QUrl::fromLocalFile(QDir::cleanPath(linkTarget))); } bool ArchiveReader::File::writeFile(archive* out, const QString& targetFileName, bool notBlock) { return writeFile(out, targetFileName, {}, notBlock); }; bool ArchiveReader::File::writeFile(archive* out, const QString& targetFileName, std::optional root, bool notBlock) { auto* entry = m_entry; std::unique_ptr entryClone(nullptr, &archive_entry_free); if (!targetFileName.isEmpty()) { entryClone.reset(archive_entry_clone(m_entry)); entry = entryClone.get(); auto nameUtf8 = targetFileName.toUtf8(); archive_entry_set_pathname_utf8(entry, nameUtf8.constData()); } if (root.has_value() && willEscapeRoot(root.value(), entry)) { qCritical() << "Failed to write header to entry:" << filename() << "-" << "file outside root"; return false; } if (archive_write_header(out, entry) < ARCHIVE_OK) { qCritical() << "Failed to write header to entry:" << filename() << "-" << archive_error_string(out) << targetFileName; return false; } if (archive_entry_size(m_entry) > 0) { auto r = copy_data(m_archive.get(), out, notBlock); if (r < ARCHIVE_OK) { qCritical() << "Failed reading data block:" << archive_error_string(out); } if (r < ARCHIVE_WARN) { return false; } } auto r = archive_write_finish_entry(out); if (r < ARCHIVE_OK) { qCritical() << "Failed to finish writing entry:" << archive_error_string(out); } return (r >= ARCHIVE_WARN); } bool ArchiveReader::parse(const std::function& doStuff) { auto f = std::make_unique(); auto* a = f->m_archive.get(); archive_read_support_format_all(a); archive_read_support_filter_all(a); auto fileName = m_archivePath.toStdWString(); if (archive_read_open_filename_w(a, fileName.data(), m_blockSize) != ARCHIVE_OK) { qCritical() << "Failed to open archive file:" << m_archivePath << "-" << f->error(); return false; } bool breakControl = false; while (f->readNextHeader() == ARCHIVE_OK) { if (f && !doStuff(f.get(), breakControl)) { qCritical() << "Failed to parse file:" << f->filename() << "-" << f->error(); return false; } if (breakControl) { break; } } archive_read_close(a); return true; } bool ArchiveReader::parse(const std::function& doStuff) { return parse([doStuff](File* f, bool&) { return doStuff(f); }); } bool ArchiveReader::File::isFile() { return (archive_entry_filetype(m_entry) & AE_IFMT) == AE_IFREG; } bool ArchiveReader::File::skip() { return archive_read_data_skip(m_archive.get()) == ARCHIVE_OK; } const char* ArchiveReader::File::error() { return archive_error_string(m_archive.get()); } QString ArchiveReader::getZipName() { return m_archivePath; } bool ArchiveReader::exists(const QString& filePath) const { if (filePath == QLatin1String("/") || filePath.isEmpty()) { return true; } // Normalize input path (remove trailing slash, if any) QString normalizedPath = QDir::cleanPath(filePath); if (normalizedPath.startsWith('/')) { normalizedPath.remove(0, 1); } if (normalizedPath == QLatin1String(".")) { return true; } if (normalizedPath == QLatin1String("..")) { return false; // root only } // Check for exact file match if (m_fileNames.contains(normalizedPath, Qt::CaseInsensitive)) { return true; } // Check for directory existence by seeing if any file starts with that path QString dirPath = normalizedPath + QLatin1Char('/'); for (const QString& f : m_fileNames) { if (f.startsWith(dirPath, Qt::CaseInsensitive)) { return true; } } return false; } ArchiveReader::File::File() : m_archive(ArchivePtr(archive_read_new(), archive_read_free)) {} } // namespace MMCZip PrismLauncher-11.0.3/launcher/archive/ExportToZipTask.cpp0000644000175100017510000000624715224505336023014 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2025 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ExportToZipTask.h" #include #include "FileSystem.h" namespace MMCZip { void ExportToZipTask::executeTask() { setStatus("Adding files..."); setProgress(0, m_files.length()); m_buildZipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return exportZip(); }); connect(&m_buildZipWatcher, &QFutureWatcher::finished, this, &ExportToZipTask::finish); m_buildZipWatcher.setFuture(m_buildZipFuture); } auto ExportToZipTask::exportZip() -> ZipResult { if (!m_dir.exists()) { return ZipResult(tr("Folder doesn't exist")); } if (!m_output.open()) { return ZipResult(tr("Could not create file")); } for (auto fileName : m_extraFiles.keys()) { if (m_buildZipFuture.isCanceled()) return ZipResult(); if (!m_output.addFile(fileName, m_extraFiles[fileName])) { return ZipResult(tr("Could not add:") + fileName); } } for (const QFileInfo& file : m_files) { if (m_buildZipFuture.isCanceled()) return ZipResult(); auto absolute = file.absoluteFilePath(); auto relative = m_dir.relativeFilePath(absolute); setStatus("Compressing: " + relative); setProgress(m_progress + 1, m_progressTotal); if (m_followSymlinks) { if (file.isSymLink()) absolute = file.symLinkTarget(); else absolute = file.canonicalFilePath(); } if (!m_excludeFiles.contains(relative) && !m_output.addFile(absolute, m_destinationPrefix + relative)) { return ZipResult(tr("Could not read and compress %1").arg(relative)); } } if (!m_output.close()) { return ZipResult(tr("A zip error occurred")); } return ZipResult(); } void ExportToZipTask::finish() { if (m_buildZipFuture.isCanceled()) { FS::deletePath(m_outputPath); emitAborted(); } else if (auto result = m_buildZipFuture.result(); result.has_value()) { FS::deletePath(m_outputPath); emitFailed(result.value()); } else { emitSucceeded(); } } bool ExportToZipTask::abort() { if (m_buildZipFuture.isRunning()) { m_buildZipFuture.cancel(); // NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur // immediately. return true; } return false; } } // namespace MMCZip PrismLauncher-11.0.3/launcher/archive/ExtractZipTask.cpp0000644000175100017510000001201115224505336022624 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2025 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ExtractZipTask.h" #include #include "FileSystem.h" #include "archive/ArchiveReader.h" #include "archive/ArchiveWriter.h" namespace MMCZip { void ExtractZipTask::executeTask() { m_zipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return extractZip(); }); connect(&m_zipWatcher, &QFutureWatcher::finished, this, &ExtractZipTask::finish); m_zipWatcher.setFuture(m_zipFuture); } auto ExtractZipTask::extractZip() -> ZipResult { auto target = m_outputDir.absolutePath(); auto target_top_dir = QUrl::fromLocalFile(target); QStringList extracted; qDebug() << "Extracting subdir" << m_subdirectory << "from" << m_input.getZipName() << "to" << target; if (!m_input.collectFiles()) { return ZipResult(tr("Failed to enumerate files in archive")); } if (m_input.getFiles().isEmpty()) { logWarning(tr("Extracting empty archives seems odd...")); return ZipResult(); } auto extPtr = ArchiveWriter::createDiskWriter(); auto ext = extPtr.get(); setStatus("Extracting files..."); setProgress(0, m_input.getFiles().count()); ZipResult result; auto fileName = m_input.getZipName(); if (!m_input.parse([this, &result, &target, &target_top_dir, ext, &extracted](ArchiveReader::File* f) { if (m_zipFuture.isCanceled()) return false; setProgress(m_progress + 1, m_progressTotal); QString file_name = f->filename(); if (!file_name.startsWith(m_subdirectory)) { f->skip(); return true; } auto relative_file_name = QDir::fromNativeSeparators(file_name.mid(m_subdirectory.size())); auto original_name = relative_file_name; setStatus("Unpacking: " + relative_file_name); // Fix subdirs/files ending with a / getting transformed into absolute paths if (relative_file_name.startsWith('/')) relative_file_name = relative_file_name.mid(1); // Fix weird "folders with a single file get squashed" thing QString sub_path; if (relative_file_name.contains('/') && !relative_file_name.endsWith('/')) { sub_path = relative_file_name.section('/', 0, -2) + '/'; FS::ensureFolderPathExists(FS::PathCombine(target, sub_path)); relative_file_name = relative_file_name.split('/').last(); } QString target_file_path; if (relative_file_name.isEmpty()) { target_file_path = target + '/'; } else { target_file_path = FS::PathCombine(target_top_dir.toLocalFile(), sub_path, relative_file_name); if (relative_file_name.endsWith('/') && !target_file_path.endsWith('/')) target_file_path += '/'; } if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) { result = ZipResult(tr("Extracting %1 was cancelled, because it was effectively outside of the target path %2") .arg(relative_file_name, target)); return false; } if (!f->writeFile(ext, target_file_path, target)) { result = ZipResult(tr("Failed to extract file %1 to %2").arg(original_name, target_file_path)); return false; } extracted.append(target_file_path); qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path; return true; })) { FS::removeFiles(extracted); return result.has_value() ? result : ZipResult(tr("Failed to parse file %1").arg(fileName)); } return ZipResult(); } void ExtractZipTask::finish() { if (m_zipFuture.isCanceled()) { emitAborted(); } else if (auto result = m_zipFuture.result(); result.has_value()) { emitFailed(result.value()); } else { emitSucceeded(); } } bool ExtractZipTask::abort() { if (m_zipFuture.isRunning()) { m_zipFuture.cancel(); // NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur // immediately. return true; } return false; } } // namespace MMCZip PrismLauncher-11.0.3/launcher/archive/ArchiveWriter.h0000644000175100017510000000260315224505336022135 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2025 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include "archive/ArchiveReader.h" struct archive; namespace MMCZip { class ArchiveWriter { public: ArchiveWriter(const QString& archiveName); virtual ~ArchiveWriter(); bool open(); bool close(); bool addFile(const QString& fileName, const QString& fileDest); bool addFile(const QString& fileDest, const QByteArray& data); bool addFile(ArchiveReader::File* f); static std::unique_ptr createDiskWriter(); private: struct archive* m_archive = nullptr; QString m_filename; QString m_format = "zip"; }; } // namespace MMCZip PrismLauncher-11.0.3/launcher/archive/ArchiveWriter.cpp0000644000175100017510000002027615224505336022476 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2025 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ArchiveWriter.h" #include #include #include #include #include #include #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif // clang-format off #include #include // clang-format on #endif namespace MMCZip { ArchiveWriter::ArchiveWriter(const QString& archiveName) : m_filename(archiveName) {} ArchiveWriter::~ArchiveWriter() { close(); } bool ArchiveWriter::open() { if (m_filename.isEmpty()) { qCritical() << "Archive m_filename not set."; return false; } m_archive = archive_write_new(); if (!m_archive) { qCritical() << "Archive not initialized."; return false; } auto format = m_format.toUtf8(); archive_write_set_format_by_name(m_archive, format.constData()); if (archive_write_set_options(m_archive, "hdrcharset=UTF-8") != ARCHIVE_OK) { qCritical() << "Failed to open archive file:" << m_filename << "-" << archive_error_string(m_archive); return false; } auto archiveNameW = m_filename.toStdWString(); if (archive_write_open_filename_w(m_archive, archiveNameW.data()) != ARCHIVE_OK) { qCritical() << "Failed to open archive file:" << m_filename << "-" << archive_error_string(m_archive); return false; } return true; } bool ArchiveWriter::close() { bool success = true; if (m_archive) { if (archive_write_close(m_archive) != ARCHIVE_OK) { qCritical() << "Failed to close archive" << m_filename << "-" << archive_error_string(m_archive); success = false; } if (archive_write_free(m_archive) != ARCHIVE_OK) { qCritical() << "Failed to free archive" << m_filename << "-" << archive_error_string(m_archive); success = false; } m_archive = nullptr; } return success; } bool ArchiveWriter::addFile(const QString& fileName, const QString& fileDest) { QFileInfo fileInfo(fileName); if (!fileInfo.exists()) { qCritical() << "File does not exist:" << fileInfo.filePath(); return false; } std::unique_ptr entry_ptr(archive_entry_new(), archive_entry_free); auto entry = entry_ptr.get(); if (!entry) { qCritical() << "Failed to create archive entry"; return false; } auto fileDestUtf8 = fileDest.toUtf8(); archive_entry_set_pathname_utf8(entry, fileDestUtf8.constData()); #if defined Q_OS_WIN32 { // Windows needs to use this method, thanks I hate it. auto widePath = fileInfo.absoluteFilePath().toStdWString(); HANDLE file_handle = CreateFileW(widePath.data(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (file_handle == INVALID_HANDLE_VALUE) { qCritical() << "Failed to stat file:" << fileInfo.filePath(); return false; } BY_HANDLE_FILE_INFORMATION file_info; if (!GetFileInformationByHandle(file_handle, &file_info)) { qCritical() << "Failed to stat file:" << fileInfo.filePath(); CloseHandle(file_handle); return false; } archive_entry_copy_bhfi(entry, &file_info); CloseHandle(file_handle); } #else { // this only works for multibyte encoded filenames if the local is properly set, // a wide character version doesn't seem to exist: here's hoping... QByteArray utf8 = fileInfo.absoluteFilePath().toUtf8(); const char* cpath = utf8.constData(); struct stat st; if (stat(cpath, &st) != 0) { qCritical() << "Failed to stat file:" << fileInfo.filePath(); return false; } // This should handle the copying of most attributes archive_entry_copy_stat(entry, &st); } #endif // However: // "The [filetype] constants used by stat(2) may have different numeric values from the corresponding [libarchive constants]." // - `archive_entry_stat(3)` if (fileInfo.isSymLink()) { archive_entry_set_filetype(entry, AE_IFLNK); // We also need to manually copy some attributes from the link itself, as `stat` above operates on its target auto target = fileInfo.symLinkTarget().toUtf8(); archive_entry_set_symlink_utf8(entry, target.constData()); archive_entry_set_size(entry, 0); archive_entry_set_perm(entry, fileInfo.permissions()); } else if (fileInfo.isFile()) { archive_entry_set_filetype(entry, AE_IFREG); } else { qCritical() << "Unsupported file type:" << fileInfo.filePath(); return false; } if (archive_write_header(m_archive, entry) != ARCHIVE_OK) { qCritical() << "Failed to write header for:" << fileDest << "-" << archive_error_string(m_archive); return false; } if (fileInfo.isFile() && !fileInfo.isSymLink()) { QFile file(fileInfo.absoluteFilePath()); if (!file.open(QIODevice::ReadOnly)) { qCritical() << "Failed to open file:" << fileInfo.filePath() << "error:" << file.errorString(); return false; } constexpr qint64 chunkSize = 8192; QByteArray buffer; buffer.resize(chunkSize); while (!file.atEnd()) { auto bytesRead = file.read(buffer.data(), chunkSize); if (bytesRead < 0) { qCritical() << "Read error in file:" << fileInfo.filePath(); return false; } if (archive_write_data(m_archive, buffer.constData(), bytesRead) < 0) { qCritical() << "Write error in archive for:" << fileDest; return false; } } } return true; } bool ArchiveWriter::addFile(const QString& fileDest, const QByteArray& data) { std::unique_ptr entry_ptr(archive_entry_new(), archive_entry_free); auto entry = entry_ptr.get(); if (!entry) { qCritical() << "Failed to create archive entry"; return false; } auto fileDestUtf8 = fileDest.toUtf8(); archive_entry_set_pathname_utf8(entry, fileDestUtf8.constData()); archive_entry_set_perm(entry, 0644); archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_size(entry, data.size()); if (archive_write_header(m_archive, entry) != ARCHIVE_OK) { qCritical() << "Failed to write header for:" << fileDest << "-" << archive_error_string(m_archive); return false; } if (archive_write_data(m_archive, data.constData(), data.size()) < 0) { qCritical() << "Write error in archive for:" << fileDest << "-" << archive_error_string(m_archive); return false; } return true; } bool ArchiveWriter::addFile(ArchiveReader::File* f) { return f->writeFile(m_archive, "", true); } std::unique_ptr ArchiveWriter::createDiskWriter() { int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_SECURE_NODOTDOT | ARCHIVE_EXTRACT_SECURE_SYMLINKS; std::unique_ptr extPtr(archive_write_disk_new(), [](archive* a) { if (a) { archive_write_close(a); archive_write_free(a); } }); archive* ext = extPtr.get(); archive_write_disk_set_options(ext, flags); archive_write_disk_set_standard_lookup(ext); return extPtr; } } // namespace MMCZip PrismLauncher-11.0.3/launcher/archive/ExtractZipTask.h0000644000175100017510000000300415224505336022273 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2025 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include "archive/ArchiveReader.h" #include "tasks/Task.h" namespace MMCZip { class ExtractZipTask : public Task { Q_OBJECT public: ExtractZipTask(QString input, QDir outputDir, QString subdirectory = "") : m_input(input), m_outputDir(outputDir), m_subdirectory(subdirectory) {} virtual ~ExtractZipTask() = default; using ZipResult = std::optional; protected: virtual void executeTask() override; bool abort() override; ZipResult extractZip(); void finish(); private: ArchiveReader m_input; QDir m_outputDir; QString m_subdirectory; QFuture m_zipFuture; QFutureWatcher m_zipWatcher; }; } // namespace MMCZip PrismLauncher-11.0.3/launcher/archive/ArchiveReader.h0000644000175100017510000000436515224505336022072 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2025 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include #include #include #include struct archive; struct archive_entry; namespace MMCZip { class ArchiveReader { public: using ArchivePtr = std::unique_ptr; explicit ArchiveReader(QString fileName) : m_archivePath(std::move(fileName)) {} virtual ~ArchiveReader() = default; QStringList getFiles(); QString getZipName(); bool collectFiles(bool onlyFiles = true); bool exists(const QString& filePath) const; class File { public: File(); virtual ~File() = default; QString filename(); bool isFile(); QDateTime dateTime(); const char* error(); QByteArray readAll(int* outStatus = nullptr); bool skip(); bool writeFile(archive* out, const QString& targetFileName = "", bool notBlock = false); bool writeFile(archive* out, const QString& targetFileName, std::optional root, bool notBlock = false); private: int readNextHeader(); private: friend ArchiveReader; ArchivePtr m_archive; archive_entry* m_entry; }; std::unique_ptr goToFile(const QString& filename); bool parse(const std::function&); bool parse(const std::function&); private: QString m_archivePath; size_t m_blockSize = 10240; QStringList m_fileNames; }; } // namespace MMCZip PrismLauncher-11.0.3/launcher/archive/ExportToZipTask.h0000644000175100017510000000447315224505336022460 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2025 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include #include "archive/ArchiveWriter.h" #include "tasks/Task.h" namespace MMCZip { class ExportToZipTask : public Task { Q_OBJECT public: ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) : m_outputPath(outputPath) , m_output(outputPath) , m_dir(dir) , m_files(files) , m_destinationPrefix(destinationPrefix) , m_followSymlinks(followSymlinks) { setAbortable(true); }; ExportToZipTask(QString outputPath, QString dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false) : ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks) {}; virtual ~ExportToZipTask() = default; void setExcludeFiles(QStringList excludeFiles) { m_excludeFiles = excludeFiles; } void addExtraFile(QString fileName, QByteArray data) { m_extraFiles.insert(fileName, data); } using ZipResult = std::optional; protected: virtual void executeTask() override; bool abort() override; ZipResult exportZip(); void finish(); private: QString m_outputPath; ArchiveWriter m_output; QDir m_dir; QFileInfoList m_files; QString m_destinationPrefix; bool m_followSymlinks; QStringList m_excludeFiles; QHash m_extraFiles; QFuture m_buildZipFuture; QFutureWatcher m_buildZipWatcher; }; } // namespace MMCZip PrismLauncher-11.0.3/launcher/Version.h0000644000175100017510000000375415224505336017373 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 flowln * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2026 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include // this implements the FlexVer // https://git.sleeping.town/exa/FlexVer class Version { public: Version(QString str) : m_string(std::move(str)) { parse(); } // NOLINT(hicpp-explicit-conversions) Version() = default; private: struct Section { enum class Type : std::uint8_t { Null, Textual, Numeric, PreRelease }; explicit Section(Type t = Type::Null, QString value = "") : t(t), value(std::move(value)) {} Type t; QString value; bool operator==(const Section& other) const = default; std::strong_ordering operator<=>(const Section& other) const; }; private: void parse(); public: QString toString() const { return m_string; } bool isEmpty() const { return m_string.isEmpty(); } friend QDebug operator<<(QDebug debug, const Version& v); bool operator==(const Version& other) const { return (*this <=> other) == std::strong_ordering::equal; } std::strong_ordering operator<=>(const Version& other) const; private: QString m_string; QList
    m_sections; };PrismLauncher-11.0.3/launcher/InstanceCopyPrefs.cpp0000644000175100017510000000740715224505336021677 0ustar runnerrunner// // Created by marcelohdez on 10/22/22. // #include "InstanceCopyPrefs.h" bool InstanceCopyPrefs::allTrue() const { return copySaves && keepPlaytime && copyGameOptions && copyResourcePacks && copyShaderPacks && copyServers && copyMods && copyScreenshots; } // Returns a single RegEx string of the selected folders/files to filter out (ex: ".minecraft/saves|.minecraft/server.dat") QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const { return getSelectedFiltersAsRegex({}); } QString InstanceCopyPrefs::getSelectedFiltersAsRegex(const QStringList& additionalFilters) const { QStringList filters; if (!copySaves) filters << "saves"; if (!copyGameOptions) filters << "options.txt"; if (!copyResourcePacks) filters << "resourcepacks" << "texturepacks"; if (!copyShaderPacks) filters << "shaderpacks"; if (!copyServers) filters << "servers.dat" << "servers.dat_old" << "server-resource-packs"; if (!copyMods) filters << "coremods" << "mods" << "config"; if (!copyScreenshots) filters << "screenshots"; for (auto filter : additionalFilters) { filters << filter; } // If we have any filters to add, join them as a single regex string to return: if (!filters.isEmpty()) { const QString MC_ROOT = "[.]?minecraft/"; // Ensure first filter starts with root, then join other filters with OR regex before root (ex: ".minecraft/saves|.minecraft/mods"): return MC_ROOT + filters.join("|" + MC_ROOT); } return {}; } // ======= Getters ======= bool InstanceCopyPrefs::isCopySavesEnabled() const { return copySaves; } bool InstanceCopyPrefs::isKeepPlaytimeEnabled() const { return keepPlaytime; } bool InstanceCopyPrefs::isCopyGameOptionsEnabled() const { return copyGameOptions; } bool InstanceCopyPrefs::isCopyResourcePacksEnabled() const { return copyResourcePacks; } bool InstanceCopyPrefs::isCopyShaderPacksEnabled() const { return copyShaderPacks; } bool InstanceCopyPrefs::isCopyServersEnabled() const { return copyServers; } bool InstanceCopyPrefs::isCopyModsEnabled() const { return copyMods; } bool InstanceCopyPrefs::isCopyScreenshotsEnabled() const { return copyScreenshots; } bool InstanceCopyPrefs::isUseSymLinksEnabled() const { return useSymLinks; } bool InstanceCopyPrefs::isUseHardLinksEnabled() const { return useHardLinks; } bool InstanceCopyPrefs::isLinkRecursivelyEnabled() const { return linkRecursively; } bool InstanceCopyPrefs::isDontLinkSavesEnabled() const { return dontLinkSaves; } bool InstanceCopyPrefs::isUseCloneEnabled() const { return useClone; } // ======= Setters ======= void InstanceCopyPrefs::enableCopySaves(bool b) { copySaves = b; } void InstanceCopyPrefs::enableKeepPlaytime(bool b) { keepPlaytime = b; } void InstanceCopyPrefs::enableCopyGameOptions(bool b) { copyGameOptions = b; } void InstanceCopyPrefs::enableCopyResourcePacks(bool b) { copyResourcePacks = b; } void InstanceCopyPrefs::enableCopyShaderPacks(bool b) { copyShaderPacks = b; } void InstanceCopyPrefs::enableCopyServers(bool b) { copyServers = b; } void InstanceCopyPrefs::enableCopyMods(bool b) { copyMods = b; } void InstanceCopyPrefs::enableCopyScreenshots(bool b) { copyScreenshots = b; } void InstanceCopyPrefs::enableUseSymLinks(bool b) { useSymLinks = b; } void InstanceCopyPrefs::enableLinkRecursively(bool b) { linkRecursively = b; } void InstanceCopyPrefs::enableUseHardLinks(bool b) { useHardLinks = b; } void InstanceCopyPrefs::enableDontLinkSaves(bool b) { dontLinkSaves = b; } void InstanceCopyPrefs::enableUseClone(bool b) { useClone = b; } PrismLauncher-11.0.3/launcher/AssertHelpers.h0000644000175100017510000000160315224505336020521 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2025 Octol1ttle * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #if defined(ASSERT_NEVER) #error ASSERT_NEVER already defined #else #define ASSERT_NEVER(cond) (Q_ASSERT((cond) == false), (cond)) #endif PrismLauncher-11.0.3/launcher/HardwareInfo.h0000644000175100017510000000227515224505336020314 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2026 Octol1ttle * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include namespace HardwareInfo { QString cpuInfo(); uint64_t totalRamMiB(); uint64_t availableRamMiB(); QStringList gpuInfo(); } // namespace HardwareInfo #ifdef Q_OS_MACOS namespace MacOSHardwareInfo { enum class MemoryPressureLevel : uint8_t { Normal = 1, Warning = 2, Critical = 4, }; MemoryPressureLevel memoryPressureLevel(); QString memoryPressureLevelName(); } // namespace MacOSHardwareInfo #endifPrismLauncher-11.0.3/launcher/InstanceCreationTask.h0000644000175100017510000000304715224505336022015 0ustar runnerrunner#pragma once #include "BaseVersion.h" #include "InstanceTask.h" #include "minecraft/MinecraftInstance.h" class InstanceCreationTask : public InstanceTask { Q_OBJECT public: InstanceCreationTask() = default; virtual ~InstanceCreationTask() = default; bool abort() override; protected: void executeTask() final override; /** * Tries to update an already existing instance. * * This can be implemented by subclasses to provide a way of updating an already existing * instance, according to that implementation's concept of 'identity' (i.e. instances that * are updates / downgrades of one another). * * If this returns true, createInstance() will not run, so you should do all update steps in here. * Otherwise, createInstance() is run as normal. */ virtual bool updateInstance() { return false; }; /** * Creates a new instance. * * Returns the instance if it was created or nullptr otherwise. */ virtual std::unique_ptr createInstance() { return nullptr; } QString getError() const { return m_error_message; } protected: void setError(const QString& message) { m_error_message = message; }; void scheduleToDelete(QWidget* parent, QDir dir, QString path, bool checkDisabled = false); protected: bool m_abort = false; QStringList m_filesToRemove; ShouldDeleteSaves m_shouldDeleteSaves; private: QString m_error_message; std::unique_ptr m_instance; Task::Ptr m_gameFilesTask; }; PrismLauncher-11.0.3/launcher/macsandbox/0000755000175100017510000000000015224505336017703 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/macsandbox/SecurityBookmarkFileAccess.h0000644000175100017510000001062115224505336025273 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2024 Kenneth Chew <79120643+kthchew@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILEACCESS_H #define FILEACCESS_H #include #include Q_FORWARD_DECLARE_OBJC_CLASS(NSData); Q_FORWARD_DECLARE_OBJC_CLASS(NSURL); Q_FORWARD_DECLARE_OBJC_CLASS(NSString); Q_FORWARD_DECLARE_OBJC_CLASS(NSAutoreleasePool); Q_FORWARD_DECLARE_OBJC_CLASS(NSMutableDictionary); Q_FORWARD_DECLARE_OBJC_CLASS(NSMutableSet); class QString; class QByteArray; class QUrl; class SecurityBookmarkFileAccess { /// The keys are bookmarks and the values are URLs. NSMutableDictionary* m_bookmarks; /// The keys are paths and the values are bookmarks. NSMutableDictionary* m_paths; /// Contains URLs that are currently being accessed. NSMutableSet* m_activeURLs; bool m_readOnly; NSURL* securityScopedBookmarkToNSURL(QByteArray& bookmark, bool& isStale); public: /// \param readOnly A boolean indicating whether the bookmark should be read-only. SecurityBookmarkFileAccess(bool readOnly = false); ~SecurityBookmarkFileAccess(); /// Get a security scoped bookmark from a URL. /// /// The URL must be accessible before calling this function. That is, call `startAccessingSecurityScopedResource()` before calling /// this function. Note that this is called implicitly if the user selects the directory from a file picker. /// \param url The URL to get the security scoped bookmark from. /// \return A QByteArray containing the security scoped bookmark. QByteArray urlToSecurityScopedBookmark(const QUrl& url); /// Get a security scoped bookmark from a path. /// /// The path must be accessible before calling this function. That is, call `startAccessingSecurityScopedResource()` before calling /// this function. Note that this is called implicitly if the user selects the directory from a file picker. /// \param path The path to get the security scoped bookmark from. /// \return A QByteArray containing the security scoped bookmark. QByteArray pathToSecurityScopedBookmark(const QString& path); /// Get a QUrl from a security scoped bookmark. If the bookmark is stale, isStale will be set to true and the bookmark will be updated. /// /// You must check whether the URL is valid before using it. /// \param bookmark The security scoped bookmark to get the URL from. /// \param isStale A boolean that will be set to true if the bookmark is stale. /// \return The URL from the security scoped bookmark. QUrl securityScopedBookmarkToURL(QByteArray& bookmark, bool& isStale); /// Makes the file or directory at the path pointed to by the bookmark accessible. Unlike `startAccessingSecurityScopedResource()`, this /// class ensures that only one "access" is active at a time. Calling this function again after the security-scoped resource has /// already been used will do nothing, and a single call to `stopUsingSecurityScopedBookmark()` will release the resource provided that /// this is the only `SecurityBookmarkFileAccess` accessing the resource. /// /// If the bookmark is stale, `isStale` will be set to true and the bookmark will be updated. Stored copies of the bookmark need to be /// updated. /// \param bookmark The security scoped bookmark to start accessing. /// \param isStale A boolean that will be set to true if the bookmark is stale. /// \return A boolean indicating whether the bookmark was successfully accessed. bool startUsingSecurityScopedBookmark(QByteArray& bookmark, bool& isStale); void stopUsingSecurityScopedBookmark(QByteArray& bookmark); /// Returns true if access to the `path` is currently being maintained by this object. bool isAccessingPath(const QString& path); }; #endif // FILEACCESS_H PrismLauncher-11.0.3/launcher/macsandbox/SecurityBookmarkFileAccess.mm0000644000175100017510000001433515224505336025463 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2024 Kenneth Chew <79120643+kthchew@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "SecurityBookmarkFileAccess.h" #include #include #include QByteArray SecurityBookmarkFileAccess::urlToSecurityScopedBookmark(const QUrl& url) { if (!url.isLocalFile()) return {}; NSError* error = nil; NSURL* nsurl = [url.toNSURL() absoluteURL]; NSData* bookmark; if ([m_paths objectForKey:[nsurl path]]) { bookmark = m_paths[[nsurl path]]; } else { bookmark = [nsurl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope | (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0) includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; } if (error) { return {}; } // remove/reapply access to ensure that write access is immediately cut off for read-only bookmarks // sometimes you need to call this twice to actually stop access (extra calls aren't harmful) [nsurl stopAccessingSecurityScopedResource]; [nsurl stopAccessingSecurityScopedResource]; nsurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope | (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0) relativeToURL:nil bookmarkDataIsStale:nil error:&error]; m_paths[[nsurl path]] = bookmark; m_bookmarks[bookmark] = nsurl; QByteArray qBookmark = QByteArray::fromNSData(bookmark); bool isStale = false; startUsingSecurityScopedBookmark(qBookmark, isStale); return qBookmark; } SecurityBookmarkFileAccess::SecurityBookmarkFileAccess(bool readOnly) : m_readOnly(readOnly) { m_bookmarks = [NSMutableDictionary new]; m_paths = [NSMutableDictionary new]; m_activeURLs = [NSMutableSet new]; } SecurityBookmarkFileAccess::~SecurityBookmarkFileAccess() { for (NSURL* url : m_activeURLs) { [url stopAccessingSecurityScopedResource]; } } QByteArray SecurityBookmarkFileAccess::pathToSecurityScopedBookmark(const QString& path) { return urlToSecurityScopedBookmark(QUrl::fromLocalFile(path)); } NSURL* SecurityBookmarkFileAccess::securityScopedBookmarkToNSURL(QByteArray& bookmark, bool& isStale) { NSError* error = nil; BOOL localStale = NO; NSURL* nsurl = [NSURL URLByResolvingBookmarkData:bookmark.toNSData() options:NSURLBookmarkResolutionWithSecurityScope | (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0) relativeToURL:nil bookmarkDataIsStale:&localStale error:&error]; if (error) { return nil; } isStale = localStale; if (isStale) { NSData* nsBookmark = [nsurl bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope | (m_readOnly ? NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess : 0) includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; if (error) { return nil; } bookmark = QByteArray::fromNSData(nsBookmark); } NSData* nsBookmark = bookmark.toNSData(); m_paths[[nsurl path]] = nsBookmark; m_bookmarks[nsBookmark] = nsurl; return nsurl; } QUrl SecurityBookmarkFileAccess::securityScopedBookmarkToURL(QByteArray& bookmark, bool& isStale) { if (bookmark.isEmpty()) return {}; NSURL* url = securityScopedBookmarkToNSURL(bookmark, isStale); if (!url) return {}; return QUrl::fromNSURL(url); } bool SecurityBookmarkFileAccess::startUsingSecurityScopedBookmark(QByteArray& bookmark, bool& isStale) { NSURL* url = [m_bookmarks objectForKey:bookmark.toNSData()] ? m_bookmarks[bookmark.toNSData()] : securityScopedBookmarkToNSURL(bookmark, isStale); if ([m_activeURLs containsObject:url]) return false; [url stopAccessingSecurityScopedResource]; if ([url startAccessingSecurityScopedResource]) { [m_activeURLs addObject:url]; return true; } return false; } void SecurityBookmarkFileAccess::stopUsingSecurityScopedBookmark(QByteArray& bookmark) { if (![m_bookmarks objectForKey:bookmark.toNSData()]) return; NSURL* url = m_bookmarks[bookmark.toNSData()]; if ([m_activeURLs containsObject:url]) { [url stopAccessingSecurityScopedResource]; [url stopAccessingSecurityScopedResource]; [m_activeURLs removeObject:url]; [m_paths removeObjectForKey:[url path]]; [m_bookmarks removeObjectForKey:bookmark.toNSData()]; } } bool SecurityBookmarkFileAccess::isAccessingPath(const QString& path) { NSData* bookmark = [m_paths objectForKey:path.toNSString()]; if (!bookmark && path.endsWith('/')) { bookmark = [m_paths objectForKey:path.left(path.length() - 1).toNSString()]; } if (!bookmark) { return false; } NSURL* url = [m_bookmarks objectForKey:bookmark]; return [m_activeURLs containsObject:url]; } PrismLauncher-11.0.3/launcher/LoggedProcess.cpp0000644000175100017510000001354615224505336021041 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022,2023 Sefa Eyeoglu * Copyright (c) 2023 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "LoggedProcess.h" #include #include #include "MessageLevel.h" LoggedProcess::LoggedProcess(const QStringConverter::Encoding output_codec, QObject* parent) : QProcess(parent), m_err_decoder(output_codec), m_out_decoder(output_codec) { // QProcess has a strange interface... let's map a lot of those into a few. connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut); connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr); connect(this, &QProcess::finished, this, &LoggedProcess::on_exit); connect(this, &QProcess::errorOccurred, this, &LoggedProcess::on_error); connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange); } LoggedProcess::~LoggedProcess() { if (m_is_detachable) { setProcessState(QProcess::NotRunning); } } QStringList LoggedProcess::reprocess(const QByteArray& data, QStringDecoder& decoder) { QString str = decoder(data); if (!m_leftover_line.isEmpty()) { str.prepend(m_leftover_line); m_leftover_line = ""; } auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed); m_leftover_line = lines.takeLast(); return lines; } void LoggedProcess::on_stdErr() { auto lines = reprocess(readAllStandardError(), m_err_decoder); emit log(lines, MessageLevel::StdErr); } void LoggedProcess::on_stdOut() { auto lines = reprocess(readAllStandardOutput(), m_out_decoder); emit log(lines, MessageLevel::StdOut); } void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status) { // save the exit code m_exit_code = exit_code; // based on state, send signals if (!m_is_aborting) { if (status == QProcess::NormalExit) { //: Message displayed on instance exit emit log({ tr("Process exited with code %1.").arg(exit_code) }, MessageLevel::Launcher); changeState(LoggedProcess::Finished); } else { //: Message displayed on instance crashed if (exit_code == -1) emit log({ tr("Process crashed.") }, MessageLevel::Launcher); else emit log({ tr("Process crashed with exitcode %1.").arg(exit_code) }, MessageLevel::Launcher); changeState(LoggedProcess::Crashed); } } else { //: Message displayed after the instance exits due to kill request emit log({ tr("Process was killed by user.") }, MessageLevel::Error); changeState(LoggedProcess::Aborted); } } void LoggedProcess::on_error(QProcess::ProcessError error) { switch (error) { case QProcess::FailedToStart: { emit log({ tr("The process failed to start: %1").arg(errorString()) }, MessageLevel::Fatal); changeState(LoggedProcess::FailedToStart); break; } // we'll just ignore those... never needed them case QProcess::Crashed: case QProcess::ReadError: case QProcess::Timedout: case QProcess::UnknownError: case QProcess::WriteError: break; } } void LoggedProcess::kill() { m_is_aborting = true; QProcess::kill(); } int LoggedProcess::exitCode() const { return m_exit_code; } void LoggedProcess::changeState(LoggedProcess::State state) { if (state == m_state) return; m_state = state; emit stateChanged(m_state); } LoggedProcess::State LoggedProcess::state() const { return m_state; } void LoggedProcess::on_stateChange(QProcess::ProcessState state) { switch (state) { case QProcess::NotRunning: break; // let's not - there are too many that handle this already. case QProcess::Starting: { if (m_state != LoggedProcess::NotRunning) { qWarning() << "Wrong state change for process from state" << m_state << "to" << (int)LoggedProcess::Starting; } changeState(LoggedProcess::Starting); return; } case QProcess::Running: { if (m_state != LoggedProcess::Starting) { qWarning() << "Wrong state change for process from state" << m_state << "to" << (int)LoggedProcess::Running; } changeState(LoggedProcess::Running); return; } } } void LoggedProcess::setDetachable(bool detachable) { m_is_detachable = detachable; } PrismLauncher-11.0.3/launcher/KonamiCode.cpp0000644000175100017510000000147315224505336020306 0ustar runnerrunner#include "KonamiCode.h" #include #include namespace { const std::array konamiCode = { { Qt::Key_Up, Qt::Key_Up, Qt::Key_Down, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_Left, Qt::Key_Right, Qt::Key_B, Qt::Key_A } }; } KonamiCode::KonamiCode(QObject* parent) : QObject(parent) {} void KonamiCode::input(QEvent* event) { if (event->type() == QEvent::KeyPress) { QKeyEvent* keyEvent = static_cast(event); auto key = Qt::Key(keyEvent->key()); if (key == konamiCode[m_progress]) { m_progress++; } else { m_progress = 0; } if (m_progress == static_cast(konamiCode.size())) { m_progress = 0; emit triggered(); } } } PrismLauncher-11.0.3/launcher/LoggedProcess.h0000644000175100017510000000561715224505336020506 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022,2023 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "MessageLevel.h" /* * This is a basic process. * It has line-based logging support and hides some of the nasty bits. */ class LoggedProcess : public QProcess { Q_OBJECT public: enum State { NotRunning, Starting, FailedToStart, Running, Finished, Crashed, Aborted }; public: explicit LoggedProcess(QStringConverter::Encoding outputEncoding = QStringConverter::System, QObject* parent = nullptr); virtual ~LoggedProcess(); State state() const; int exitCode() const; void setDetachable(bool detachable); signals: void log(QStringList lines, MessageLevel level); void stateChanged(LoggedProcess::State state); public slots: /** * @brief kill the process - equivalent to kill -9 */ void kill(); private slots: void on_stdErr(); void on_stdOut(); void on_exit(int exit_code, QProcess::ExitStatus status); void on_error(QProcess::ProcessError error); void on_stateChange(QProcess::ProcessState); private: void changeState(LoggedProcess::State state); QStringList reprocess(const QByteArray& data, QStringDecoder& decoder); private: QStringDecoder m_err_decoder; QStringDecoder m_out_decoder; QString m_leftover_line; bool m_killed = false; State m_state = NotRunning; int m_exit_code = 0; bool m_is_aborting = false; bool m_is_detachable = false; }; PrismLauncher-11.0.3/launcher/settings/0000755000175100017510000000000015224505336017424 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/settings/SettingsObject.h0000644000175100017510000002125615224505336022532 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #ifdef Q_OS_MACOS #include "macsandbox/SecurityBookmarkFileAccess.h" #endif class Setting; class SettingsObject; /*! * \brief The SettingsObject handles communicating settings between the application and a *settings file. * The class keeps a list of Setting objects. Each Setting object represents one * of the application's settings. These Setting objects are registered with * a SettingsObject and can be managed similarly to the way a list works. * * \author Andrew Okin * \date 2/22/2013 * * \sa Setting */ class SettingsObject : public QObject { Q_OBJECT public: class Lock { public: Lock(SettingsObject* locked) : m_locked(locked) { m_locked->suspendSave(); } ~Lock() { m_locked->resumeSave(); } private: SettingsObject* m_locked; }; public: explicit SettingsObject(QObject* parent = 0); virtual ~SettingsObject(); /*! * Registers an override setting for the given original setting in this settings object * gate decides if the passthrough (true) or the original (false) is used for value * * This will fail if there is already a setting with the same ID as * the one that is being registered. * \return A valid Setting shared pointer if successful. */ std::shared_ptr registerOverride(std::shared_ptr original, std::shared_ptr gate); /*! * Registers a passthorugh setting for the given original setting in this settings object * gate decides if the passthrough (true) or the original (false) is used for value * * This will fail if there is already a setting with the same ID as * the one that is being registered. * \return A valid Setting shared pointer if successful. */ std::shared_ptr registerPassthrough(std::shared_ptr original, std::shared_ptr gate); /*! * Registers the given setting with this SettingsObject and connects the necessary signals. * * This will fail if there is already a setting with the same ID as * the one that is being registered. * \return A valid Setting shared pointer if successful. */ std::shared_ptr registerSetting(QStringList synonyms, QVariant defVal = QVariant()); /*! * Registers the given setting with this SettingsObject and connects the necessary signals. * * This will fail if there is already a setting with the same ID as * the one that is being registered. * \return A valid Setting shared pointer if successful. */ std::shared_ptr registerSetting(QString id, QVariant defVal = QVariant()) { return registerSetting(QStringList(id), defVal); } /*! * \brief Gets the setting with the given ID. * \param id The ID of the setting to get. * \return A pointer to the setting with the given ID. * Returns null if there is no setting with the given ID. * \sa operator []() */ std::shared_ptr getSetting(const QString& id) const; /*! * \brief Gets the setting with the given ID. * \brief if is not registered yet it does that * \param id The ID of the setting to get. * \return A pointer to the setting with the given ID. * Returns null if there is no setting with the given ID. * \sa operator []() */ std::shared_ptr getOrRegisterSetting(const QString& id, QVariant defVal = QVariant()); /*! * \brief Gets the value of the setting with the given ID. * \param id The ID of the setting to get. * \return The setting's value as a QVariant. * If no setting with the given ID exists, returns an invalid QVariant. */ QVariant get(const QString& id); #ifdef Q_OS_MACOS /*! * \brief Get the path to the file or directory represented by the bookmark stored in the associated setting. * \param id The setting ID of the relevant directory - this should not include "Bookmark" at the end. * \return A path to the file or directory represented by the bookmark. * If a bookmark is not valid or stored, use default logic (directly return the stored path). * This can attempt to create a bookmark if the path is accessible and the bookmark is not valid. */ QString getPathFromBookmark(const QString& id); /*! * \brief Set a security-scoped bookmark to the provided path for the associated setting. * \param id The setting ID of the relevant directory - this should not include "Bookmark" at the end. * \param path The new desired path. * \return A boolean indicating whether a bookmark was successfully set. * The path needs to be accessible to the launcher before calling this function. For example, * it could come from a user selection in an open panel. */ bool setPathWithBookmark(const QString& id, const QString& path); #endif /*! * \brief Sets the value of the setting with the given ID. * If no setting with the given ID exists, returns false * \param id The ID of the setting to change. * \param value The new value of the setting. * \return True if successful, false if it failed. */ bool set(const QString& id, QVariant value); /*! * \brief Reverts the setting with the given ID to default. * \param id The ID of the setting to reset. */ void reset(const QString& id) const; /*! * \brief Checks if this SettingsObject contains a setting with the given ID. * \param id The ID to check for. * \return True if the SettingsObject has a setting with the given ID. */ bool contains(const QString& id); /*! * \brief Reloads the settings and emit signals for changed settings * \return True if reloading was successful */ virtual bool reload(); virtual void suspendSave() = 0; virtual void resumeSave() = 0; signals: /*! * \brief Signal emitted when one of this SettingsObject object's settings changes. * This is usually just connected directly to each Setting object's * SettingChanged() signals. * \param setting A reference to the Setting object that changed. * \param value The Setting object's new value. */ void SettingChanged(const Setting& setting, QVariant value); /*! * \brief Signal emitted when one of this SettingsObject object's settings resets. * This is usually just connected directly to each Setting object's * settingReset() signals. * \param setting A reference to the Setting object that changed. */ void settingReset(const Setting& setting); protected slots: /*! * \brief Changes a setting. * This slot is usually connected to each Setting object's * SettingChanged() signal. The signal is emitted, causing this slot * to update the setting's value in the config file. * \param setting A reference to the Setting object that changed. * \param value The setting's new value. */ virtual void changeSetting(const Setting& setting, QVariant value) = 0; /*! * \brief Resets a setting. * This slot is usually connected to each Setting object's * settingReset() signal. The signal is emitted, causing this slot * to update the setting's value in the config file. * \param setting A reference to the Setting object that changed. */ virtual void resetSetting(const Setting& setting) = 0; protected: /*! * \brief Connects the necessary signals to the given Setting. * \param setting The setting to connect. */ void connectSignals(const Setting& setting); /*! * \brief Function used by Setting objects to get their values from the SettingsObject. * \param setting The * \return */ virtual QVariant retrieveValue(const Setting& setting) = 0; friend class Setting; private: QMap> m_settings; #ifdef Q_OS_MACOS SecurityBookmarkFileAccess m_sandboxedFileAccess; #endif protected: bool m_suspendSave = false; bool m_doSave = false; }; PrismLauncher-11.0.3/launcher/settings/Setting.h0000644000175100017510000000676515224505336021230 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include class SettingsObject; /*! * */ class Setting : public QObject { Q_OBJECT public: /** * Construct a Setting * * Synonyms are all the possible names used in the settings object, in order of preference. * First synonym is the ID, which identifies the setting in Prism Launcher. * * defVal is the default value that will be returned when the settings object * doesn't have any value for this setting. */ explicit Setting(QStringList synonyms, QVariant defVal = QVariant()); /*! * \brief Gets this setting's ID. * This is used to refer to the setting within the application. * \warning Changing the ID while the setting is registered with a SettingsObject results in * undefined behavior. * \return The ID of the setting. */ virtual QString id() const { return m_synonyms.first(); } /*! * \brief Gets this setting's config file key. * This is used to store the setting's value in the config file. It is usually * the same as the setting's ID, but it can be different. * \return The setting's config file key. */ virtual QStringList configKeys() const { return m_synonyms; } /*! * \brief Gets this setting's value as a QVariant. * This is done by calling the SettingsObject's retrieveValue() function. * If this Setting doesn't have a SettingsObject, this returns an invalid QVariant. * \return QVariant containing this setting's value. * \sa value() */ virtual QVariant get() const; /*! * \brief Gets this setting's default value. * \return The default value of this setting. */ virtual QVariant defValue() const; signals: /*! * \brief Signal emitted when this Setting object's value changes. * \param setting A reference to the Setting that changed. * \param value This Setting object's new value. */ void SettingChanged(const Setting& setting, QVariant value); /*! * \brief Signal emitted when this Setting object's value resets to default. * \param setting A reference to the Setting that changed. */ void settingReset(const Setting& setting); public slots: /*! * \brief Changes the setting's value. * This is done by emitting the SettingChanged() signal which will then be * handled by the SettingsObject object and cause the setting to change. * \param value The new value. */ virtual void set(QVariant value); /*! * \brief Reset the setting to default * This is done by emitting the settingReset() signal which will then be * handled by the SettingsObject object and cause the setting to change. */ virtual void reset(); protected: friend class SettingsObject; SettingsObject* m_storage; QStringList m_synonyms; QVariant m_defVal; }; PrismLauncher-11.0.3/launcher/settings/OverrideSetting.cpp0000644000175100017510000000240115224505336023242 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "OverrideSetting.h" OverrideSetting::OverrideSetting(std::shared_ptr other, std::shared_ptr gate) : Setting(other->configKeys(), QVariant()) { Q_ASSERT(other); Q_ASSERT(gate); m_other = other; m_gate = gate; } bool OverrideSetting::isOverriding() const { return m_gate->get().toBool(); } QVariant OverrideSetting::defValue() const { return m_other->get(); } QVariant OverrideSetting::get() const { if (isOverriding()) { return Setting::get(); } return m_other->get(); } void OverrideSetting::reset() { Setting::reset(); } void OverrideSetting::set(QVariant value) { Setting::set(value); } PrismLauncher-11.0.3/launcher/settings/PassthroughSetting.cpp0000644000175100017510000000276315224505336024005 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "PassthroughSetting.h" PassthroughSetting::PassthroughSetting(std::shared_ptr other, std::shared_ptr gate) : Setting(other->configKeys(), QVariant()) { Q_ASSERT(other); m_other = other; m_gate = gate; } bool PassthroughSetting::isOverriding() const { if (!m_gate) { return false; } return m_gate->get().toBool(); } QVariant PassthroughSetting::defValue() const { if (isOverriding()) { return m_other->get(); } return m_other->defValue(); } QVariant PassthroughSetting::get() const { if (isOverriding()) { return Setting::get(); } return m_other->get(); } void PassthroughSetting::reset() { if (isOverriding()) { Setting::reset(); } m_other->reset(); } void PassthroughSetting::set(QVariant value) { if (isOverriding()) { Setting::set(value); } m_other->set(value); } PrismLauncher-11.0.3/launcher/settings/OverrideSetting.h0000644000175100017510000000251615224505336022716 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "Setting.h" /*! * \brief A setting that 'overrides another.' * This means that the setting's default value will be the value of another setting. * The other setting can be (and usually is) a part of a different SettingsObject * than this one. */ class OverrideSetting : public Setting { Q_OBJECT public: explicit OverrideSetting(std::shared_ptr overridden, std::shared_ptr gate); virtual QVariant defValue() const; virtual QVariant get() const; virtual void set(QVariant value); virtual void reset(); private: bool isOverriding() const; protected: std::shared_ptr m_other; std::shared_ptr m_gate; }; PrismLauncher-11.0.3/launcher/settings/Setting.cpp0000644000175100017510000000233515224505336021550 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Setting.h" #include "settings/SettingsObject.h" Setting::Setting(QStringList synonyms, QVariant defVal) : QObject(), m_synonyms(synonyms), m_defVal(defVal) {} QVariant Setting::get() const { SettingsObject* sbase = m_storage; if (!sbase) { return defValue(); } else { QVariant test = sbase->retrieveValue(*this); if (!test.isValid()) return defValue(); return test; } } QVariant Setting::defValue() const { return m_defVal; } void Setting::set(QVariant value) { emit SettingChanged(*this, value); } void Setting::reset() { emit settingReset(*this); } PrismLauncher-11.0.3/launcher/settings/INIFile.h0000644000175100017510000000373715224505336021026 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include // Sectionless INI parser (for instance config files) class INIFile : public QMap { public: explicit INIFile(); bool loadFile(QString fileName); bool loadFile(QByteArray data); bool saveFile(QString fileName); QVariant get(QString key, QVariant def) const; void set(QString key, QVariant val); }; PrismLauncher-11.0.3/launcher/settings/INISettingsObject.cpp0000644000175100017510000000611315224505336023420 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "INISettingsObject.h" #include "Setting.h" #include #include INISettingsObject::INISettingsObject(QStringList paths, QObject* parent) : SettingsObject(parent) { auto first_path = paths.constFirst(); for (auto path : paths) { if (!QFile::exists(path)) continue; if (path != first_path && QFile::exists(path)) { // Copy the fallback to the preferred path. QFile::copy(path, first_path); qDebug() << "Copied settings from" << path << "to" << first_path; break; } } m_filePath = first_path; m_ini.loadFile(first_path); } INISettingsObject::INISettingsObject(QString path, QObject* parent) : SettingsObject(parent) { m_filePath = path; m_ini.loadFile(path); } void INISettingsObject::setFilePath(const QString& filePath) { m_filePath = filePath; } bool INISettingsObject::reload() { return m_ini.loadFile(m_filePath) && SettingsObject::reload(); } void INISettingsObject::suspendSave() { m_suspendSave = true; } void INISettingsObject::resumeSave() { m_suspendSave = false; if (m_doSave) { m_ini.saveFile(m_filePath); } } void INISettingsObject::changeSetting(const Setting& setting, QVariant value) { if (contains(setting.id())) { // valid value -> set the main config, remove all the sysnonyms if (value.isValid()) { auto list = setting.configKeys(); m_ini.set(list.takeFirst(), value); for (auto iter : list) m_ini.remove(iter); } // invalid -> remove all (just like resetSetting) else { for (auto iter : setting.configKeys()) m_ini.remove(iter); } doSave(); } } void INISettingsObject::doSave() { if (m_suspendSave) { m_doSave = true; } else { m_ini.saveFile(m_filePath); } } void INISettingsObject::resetSetting(const Setting& setting) { // if we have the setting, remove all the synonyms. ALL OF THEM if (contains(setting.id())) { for (auto iter : setting.configKeys()) m_ini.remove(iter); doSave(); } } QVariant INISettingsObject::retrieveValue(const Setting& setting) { // if we have the setting, return value of the first matching synonym if (contains(setting.id())) { for (auto iter : setting.configKeys()) { if (m_ini.contains(iter)) return m_ini[iter]; } } return QVariant(); } PrismLauncher-11.0.3/launcher/settings/INISettingsObject.h0000644000175100017510000000350615224505336023070 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include "settings/INIFile.h" #include "settings/SettingsObject.h" /*! * \brief A settings object that stores its settings in an INIFile. */ class INISettingsObject : public SettingsObject { Q_OBJECT public: /** 'paths' is a list of INI files to try, in order, for fallback support. */ explicit INISettingsObject(QStringList paths, QObject* parent = nullptr); explicit INISettingsObject(QString path, QObject* parent = nullptr); /*! * \brief Gets the path to the INI file. * \return The path to the INI file. */ virtual QString filePath() const { return m_filePath; } /*! * \brief Sets the path to the INI file and reloads it. * \param filePath The INI file's new path. */ virtual void setFilePath(const QString& filePath); bool reload() override; void suspendSave() override; void resumeSave() override; protected slots: virtual void changeSetting(const Setting& setting, QVariant value) override; virtual void resetSetting(const Setting& setting) override; protected: virtual QVariant retrieveValue(const Setting& setting) override; void doSave(); protected: INIFile m_ini; QString m_filePath; }; PrismLauncher-11.0.3/launcher/settings/INIFile.cpp0000644000175100017510000002025015224505336021346 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "settings/INIFile.h" #include #include #include #include #include #include #include #include #include "Json.h" INIFile::INIFile() {} bool INIFile::saveFile(QString fileName) { if (!contains("ConfigVersion")) insert("ConfigVersion", "1.3"); QSettings _settings_obj{ fileName, QSettings::Format::IniFormat }; _settings_obj.setFallbacksEnabled(false); _settings_obj.clear(); for (Iterator iter = begin(); iter != end(); iter++) _settings_obj.setValue(iter.key(), iter.value()); _settings_obj.sync(); if (auto status = _settings_obj.status(); status != QSettings::Status::NoError) { if (status == QSettings::Status::AccessError) qCritical() << "An access error occurred while saving INI file" << fileName << "(is the file read-only?)"; if (ASSERT_NEVER(status == QSettings::Status::FormatError)) qCritical() << "A format error occurred while saving INI file" << fileName << "(this shouldn't be possible!)"; return false; } return true; } QString unescape(QString orig) { QString out; QChar prev = QChar::Null; for (auto c : orig) { if (prev == '\\') { if (c == 'n') out += '\n'; else if (c == 't') out += '\t'; else if (c == '#') out += '#'; else out += c; prev = QChar::Null; } else { if (c == '\\') { prev = c; continue; } out += c; prev = QChar::Null; } } return out; } QString unquote(QString str) { if ((str.contains(QChar(';')) || str.contains(QChar('=')) || str.contains(QChar(','))) && str.endsWith("\"") && str.startsWith("\"")) { #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) str = str.remove(0, 1); str = str.remove(str.size() - 1, 1); #else str = str.removeFirst().removeLast(); #endif } return str; } bool parseOldFileFormat(QIODevice& device, QSettings::SettingsMap& map) { QTextStream in(device.readAll()); #if QT_VERSION <= QT_VERSION_CHECK(6, 0, 0) in.setCodec("UTF-8"); #endif QStringList lines = in.readAll().split('\n'); for (int i = 0; i < lines.count(); i++) { QString& lineRaw = lines[i]; // Ignore comments. int commentIndex = 0; QString line = lineRaw; // Search for comments until no more escaped # are available while ((commentIndex = line.indexOf('#', commentIndex + 1)) != -1) { if (commentIndex > 0 && line.at(commentIndex - 1) == '\\') { continue; } line = line.left(lineRaw.indexOf('#')).trimmed(); } int eqPos = line.indexOf('='); if (eqPos == -1) continue; QString key = line.left(eqPos).trimmed(); QString valueStr = line.right(line.length() - eqPos - 1).trimmed(); valueStr = unquote(unescape(valueStr)); QVariant value(valueStr); map.insert(key, value); } return true; } QVariant migrateQByteArrayToBase64(QString key, QVariant value) { static const QStringList otherByteArrays = { "MainWindowState", "MainWindowGeometry", "ConsoleWindowState", "ConsoleWindowGeometry", "PagedGeometry", "NewInstanceGeometry", "ModDownloadGeometry", "RPDownloadGeometry", "TPDownloadGeometry", "ShaderDownloadGeometry" }; if (key.startsWith("WideBarVisibility_") || (key.startsWith("UI/") && key.endsWith("_Page/Columns"))) { return QString::fromUtf8(value.toByteArray().toBase64()); } if (otherByteArrays.contains(key)) { return QString::fromUtf8(value.toByteArray()); } if (key == "linkedInstances") { return Json::fromStringList(value.toStringList()); } if (key == "Env") { return Json::fromMap(value.toMap()); } return value; } bool INIFile::loadFile(QString fileName) { QSettings _settings_obj{ fileName, QSettings::Format::IniFormat }; _settings_obj.setFallbacksEnabled(false); if (auto status = _settings_obj.status(); status != QSettings::Status::NoError) { if (status == QSettings::Status::AccessError) qCritical() << "An access error occurred while loading INI file" << fileName; if (status == QSettings::Status::FormatError) qCritical() << "A format error occurred while loading INI file" << fileName << "(is the file malformed or corrupted?)"; return false; } if (!_settings_obj.value("ConfigVersion").isValid()) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) return false; QSettings::SettingsMap map; parseOldFileFormat(file, map); file.close(); for (auto&& key : map.keys()) { auto value = migrateQByteArrayToBase64(key, map.value(key)); insert(key, value); } insert("ConfigVersion", "1.3"); } else if (_settings_obj.value("ConfigVersion").toString() == "1.1") { for (auto&& key : _settings_obj.allKeys()) { auto value = migrateQByteArrayToBase64(key, _settings_obj.value(key)); if (auto valueStr = value.toString(); (valueStr.contains(QChar(';')) || valueStr.contains(QChar('=')) || valueStr.contains(QChar(','))) && valueStr.endsWith("\"") && valueStr.startsWith("\"")) { insert(key, unquote(valueStr)); } else { insert(key, value); } } insert("ConfigVersion", "1.3"); } else if (_settings_obj.value("ConfigVersion").toString() == "1.2") { for (auto&& key : _settings_obj.allKeys()) { auto value = migrateQByteArrayToBase64(key, _settings_obj.value(key)); insert(key, value); } insert("ConfigVersion", "1.3"); } else { for (auto&& key : _settings_obj.allKeys()) { insert(key, _settings_obj.value(key)); } } return true; } bool INIFile::loadFile(QByteArray data) { QTemporaryFile file; if (!file.open()) return false; file.write(data); file.flush(); file.close(); auto loaded = loadFile(file.fileName()); file.remove(); return loaded; } QVariant INIFile::get(QString key, QVariant def) const { if (!this->contains(key)) return def; else return this->operator[](key); } void INIFile::set(QString key, QVariant val) { this->operator[](key) = val; } PrismLauncher-11.0.3/launcher/settings/PassthroughSetting.h0000644000175100017510000000247315224505336023450 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "Setting.h" /*! * \brief A setting that 'overrides another.' based on the value of a 'gate' setting * If 'gate' evaluates to true, the override stores and returns data * If 'gate' evaluates to false, the original does, */ class PassthroughSetting : public Setting { Q_OBJECT public: explicit PassthroughSetting(std::shared_ptr overridden, std::shared_ptr gate); virtual QVariant defValue() const; virtual QVariant get() const; virtual void set(QVariant value); virtual void reset(); private: bool isOverriding() const; protected: std::shared_ptr m_other; std::shared_ptr m_gate; }; PrismLauncher-11.0.3/launcher/settings/SettingsObject.cpp0000644000175100017510000001762115224505336023066 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "settings/SettingsObject.h" #include #include "PassthroughSetting.h" #include "settings/OverrideSetting.h" #include "settings/Setting.h" #include #include #include #ifdef Q_OS_MACOS #include "macsandbox/SecurityBookmarkFileAccess.h" #endif SettingsObject::SettingsObject(QObject* parent) : QObject(parent) {} SettingsObject::~SettingsObject() { m_settings.clear(); } std::shared_ptr SettingsObject::registerOverride(std::shared_ptr original, std::shared_ptr gate) { if (contains(original->id())) { qCritical() << QString("Failed to register setting %1. ID already exists.").arg(original->id()); return nullptr; // Fail } auto override = std::make_shared(original, gate); override->m_storage = this; connectSignals(*override); m_settings.insert(override->id(), override); return override; } std::shared_ptr SettingsObject::registerPassthrough(std::shared_ptr original, std::shared_ptr gate) { if (contains(original->id())) { qCritical() << QString("Failed to register setting %1. ID already exists.").arg(original->id()); return nullptr; // Fail } auto passthrough = std::make_shared(original, gate); passthrough->m_storage = this; connectSignals(*passthrough); m_settings.insert(passthrough->id(), passthrough); return passthrough; } std::shared_ptr SettingsObject::registerSetting(QStringList synonyms, QVariant defVal) { if (synonyms.empty()) return nullptr; if (contains(synonyms.first())) { qCritical() << QString("Failed to register setting %1. ID already exists.").arg(synonyms.first()); return nullptr; // Fail } auto setting = std::make_shared(synonyms, defVal); setting->m_storage = this; connectSignals(*setting); m_settings.insert(setting->id(), setting); return setting; } std::shared_ptr SettingsObject::getSetting(const QString& id) const { // Make sure there is a setting with the given ID. if (!m_settings.contains(id)) return NULL; return m_settings[id]; } QVariant SettingsObject::get(const QString& id) { auto setting = getSetting(id); #ifdef Q_OS_MACOS // for macOS, use a security scoped bookmark for the paths if (id.endsWith("Dir")) { return { getPathFromBookmark(id) }; } #endif return (setting ? setting->get() : QVariant()); } bool SettingsObject::set(const QString& id, QVariant value) { auto setting = getSetting(id); if (!setting) { qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id); return false; } #ifdef Q_OS_MACOS // for macOS, keep a security scoped bookmark for the paths if (value.userType() == QMetaType::QString && id.endsWith("Dir")) { setPathWithBookmark(id, value.toString()); } #endif setting->set(std::move(value)); return true; } #ifdef Q_OS_MACOS QString SettingsObject::getPathFromBookmark(const QString& id) { auto setting = getSetting(id); if (!setting) { qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id); return ""; } // there is no need to use bookmarks if the default value is used or the directory is within the data directory (already can access) if (setting->get() == setting->defValue() || QDir(setting->get().toString()).absolutePath().startsWith(QDir::current().absolutePath())) { return setting->get().toString(); } auto bookmarkId = id + "Bookmark"; auto bookmarkSetting = getSetting(bookmarkId); if (!bookmarkSetting) { qCritical() << QString("Error changing setting %1. Bookmark setting doesn't exist.").arg(id); return ""; } QByteArray bookmark = bookmarkSetting->get().toByteArray(); if (bookmark.isEmpty()) { qDebug() << "Creating bookmark for" << id << "at" << setting->get().toString(); setPathWithBookmark(id, setting->get().toString()); return setting->get().toString(); } bool stale; QUrl url = m_sandboxedFileAccess.securityScopedBookmarkToURL(bookmark, stale); if (url.isValid()) { if (stale) { setting->set(url.path()); bookmarkSetting->set(bookmark); } m_sandboxedFileAccess.startUsingSecurityScopedBookmark(bookmark, stale); // already did a stale check, no need to do it again // convert to relative path to current directory if `url` is a descendant of the current directory QDir currentDir = QDir::current().absolutePath(); return url.path().startsWith(currentDir.absolutePath()) ? currentDir.relativeFilePath(url.path()) : url.path(); } return setting->get().toString(); } bool SettingsObject::setPathWithBookmark(const QString& id, const QString& path) { auto setting = getSetting(id); if (!setting) { qCritical() << QString("Error changing setting %1. Setting doesn't exist.").arg(id); return false; } QDir dir(path); if (!dir.exists()) { qCritical() << QString("Error changing setting %1. Path doesn't exist.").arg(id); return false; } QString absolutePath = dir.absolutePath(); QString bookmarkId = id + "Bookmark"; std::shared_ptr bookmarkSetting = getSetting(bookmarkId); // there is no need to use bookmarks if the default value is used or the directory is within the data directory (already can access) if (path == setting->defValue().toString() || absolutePath.startsWith(QDir::current().absolutePath())) { bookmarkSetting->reset(); return true; } QByteArray bytes = m_sandboxedFileAccess.pathToSecurityScopedBookmark(absolutePath); if (bytes.isEmpty()) { qCritical() << QString("Failed to create bookmark for %1 - no access?").arg(id); // TODO: show an alert to the user asking them to reselect the directory return false; } auto oldBookmark = bookmarkSetting->get().toByteArray(); m_sandboxedFileAccess.stopUsingSecurityScopedBookmark(oldBookmark); if (!bytes.isEmpty() && bookmarkSetting) { bookmarkSetting->set(bytes); bool stale; m_sandboxedFileAccess.startUsingSecurityScopedBookmark(bytes, stale); // just created the bookmark, it shouldn't be stale } setting->set(path); return true; } #endif void SettingsObject::reset(const QString& id) const { auto setting = getSetting(id); if (setting) setting->reset(); } bool SettingsObject::contains(const QString& id) { return m_settings.contains(id); } bool SettingsObject::reload() { for (auto setting : m_settings.values()) { setting->set(setting->get()); } return true; } void SettingsObject::connectSignals(const Setting& setting) { connect(&setting, &Setting::SettingChanged, this, &SettingsObject::changeSetting); connect(&setting, &Setting::SettingChanged, this, &SettingsObject::SettingChanged); connect(&setting, &Setting::settingReset, this, &SettingsObject::resetSetting); connect(&setting, &Setting::settingReset, this, &SettingsObject::settingReset); } std::shared_ptr SettingsObject::getOrRegisterSetting(const QString& id, QVariant defVal) { return contains(id) ? getSetting(id) : registerSetting(id, defVal); } PrismLauncher-11.0.3/launcher/translations/0000755000175100017510000000000015224505336020305 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/translations/TranslationsModel.h0000644000175100017510000000433515224505336024125 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include struct Language; class TranslationsModel : public QAbstractListModel { Q_OBJECT public: explicit TranslationsModel(const QString& path, QObject* parent = nullptr); ~TranslationsModel() override; // hide copy constructor TranslationsModel(const TranslationsModel&) = delete; // hide assign op TranslationsModel& operator=(const TranslationsModel&) = delete; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; // NOLINT(*-default-arguments) QVariant headerData(int section, Qt::Orientation orientation, int role) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; // NOLINT(*-default-arguments) bool selectLanguage(QString key) const; void updateLanguage(const QString& key); QModelIndex selectedIndex() const; QString selectedLanguage() const; void downloadIndex(); void setUseSystemLocale(bool useSystemLocale) const; private: int columnCount(const QModelIndex& parent) const override; QList::Iterator findLanguage(const QString& key) const; std::optional findLanguageAsOptional(const QString& key) const; void reloadLocalFiles(); void downloadTranslation(const QString& key); void downloadNext(); private slots: void indexReceived(); void indexFailed(const QString& reason) const; void dlFailed(const QString& reason); void dlGood(); void translationDirChanged(const QString& path); private: /* data */ struct Private; std::unique_ptr d; }; PrismLauncher-11.0.3/launcher/translations/TranslationsModel.cpp0000644000175100017510000005215215224505336024460 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "TranslationsModel.h" #include #include #include #include "BuildConfig.h" #include "FileSystem.h" #include "Json.h" #include "net/ChecksumValidator.h" #include "net/NetJob.h" #include "POTranslator.h" #include "Application.h" #include "settings/SettingsObject.h" static constexpr QLatin1String g_defaultLangCode("en_US"); namespace { enum class FileType : std::uint8_t { None, Qm, Po }; QString getSystemLocaleName() { return QLocale::system().name(); } QString getSystemLanguage() { return getSystemLocaleName().split('_').front(); } } // namespace struct Language { Language() : updated(true) {} explicit Language(QString _key) : key(std::move(_key)), updated(key == g_defaultLangCode) { locale = QLocale(key); } QString languageName() const { QString result; if (key == "ja_KANJI") { result = locale.nativeLanguageName() + u8" (漢字)"; } else if (key == "es_UY") { result = u8"Español de Latinoamérica"; } else if (key == "en_NZ") { result = u8"New Zealand English"; // No idea why qt translates this to just english and not to New Zealand English } else if (key == "en@pirate") { result = u8"Tongue of the High Seas"; } else if (key == "en@uwu") { result = u8"Cute Engwish"; } else if (key == "tok") { result = u8"toki pona"; } else if (key == "nan") { result = u8"閩�語"; // Using traditional Chinese script. Not sure if we should use simplified instead? } else { result = locale.nativeLanguageName(); } if (result.isEmpty()) { result = key; } return result; } float percentTranslated() const { if (total == 0) { return 100.0F; } return 100.0F * static_cast(translated) / static_cast(total); } void setTranslationStats(const unsigned _translated, const unsigned _untranslated, const unsigned _fuzzy) { translated = _translated; untranslated = _untranslated; fuzzy = _fuzzy; total = translated + untranslated + fuzzy; } bool isOfSameNameAs(const Language& other) const { return key == other.key; } bool isIdenticalTo(const Language& other) const { return (key == other.key && fileName == other.fileName && fileSize == other.fileSize && fileSha1 == other.fileSha1 && translated == other.translated && fuzzy == other.fuzzy && total == other.fuzzy && localFileType == other.localFileType); } Language& apply(const Language& other) { if (!isOfSameNameAs(other)) { return *this; } fileName = other.fileName; fileSize = other.fileSize; fileSha1 = other.fileSha1; translated = other.translated; fuzzy = other.fuzzy; total = other.total; localFileType = other.localFileType; return *this; } QString key; QLocale locale; bool updated; QString fileName = QString(); std::size_t fileSize = 0; QString fileSha1 = QString(); unsigned translated = 0; unsigned untranslated = 0; unsigned fuzzy = 0; unsigned total = 0; FileType localFileType = FileType::None; }; struct TranslationsModel::Private { QDir m_dir; // initial state is just english QList m_languages = { Language(g_defaultLangCode) }; QString m_selectedLanguage = g_defaultLangCode; std::unique_ptr m_qtTranslator; std::unique_ptr m_appTranslator; Net::Download* m_indexTask = nullptr; QString m_downloadingTranslation; NetJob::Ptr m_downloadJob; NetJob::Ptr m_indexJob; QString m_nextDownload; QFileSystemWatcher* watcher = nullptr; bool m_noLanguageSet = false; }; TranslationsModel::TranslationsModel(const QString& path, QObject* parent) : QAbstractListModel(parent) { d = std::make_unique(); d->m_dir.setPath(path); d->m_selectedLanguage = APPLICATION->settings()->get("Language").toString(); FS::ensureFolderPathExists(path); reloadLocalFiles(); d->watcher = new QFileSystemWatcher(this); connect(d->watcher, &QFileSystemWatcher::directoryChanged, this, &TranslationsModel::translationDirChanged); d->watcher->addPath(d->m_dir.canonicalPath()); } TranslationsModel::~TranslationsModel() = default; void TranslationsModel::translationDirChanged(const QString& path) { qDebug() << "Dir changed:" << path; if (!d->m_noLanguageSet) { reloadLocalFiles(); } selectLanguage(selectedLanguage()); } void TranslationsModel::indexReceived() { qDebug() << "Got translations index!"; d->m_indexJob.reset(); reloadLocalFiles(); if (d->m_noLanguageSet) { auto language = getSystemLocaleName(); if (!findLanguageAsOptional(language).has_value()) { language = getSystemLanguage(); } selectLanguage(language); APPLICATION->settings()->set("Language", selectedLanguage()); d->m_noLanguageSet = false; } if (selectedLanguage() != g_defaultLangCode) { updateLanguage(selectedLanguage()); } } namespace { void readIndex(const QString& path, QMap& languages) { QByteArray data; try { data = FS::read(path); } catch ([[maybe_unused]] const Exception& e) { qCritical() << "Translations Download Failed: index file not readable"; return; } try { auto toplevelDoc = Json::requireDocument(data); auto doc = Json::requireObject(toplevelDoc); auto fileType = Json::requireString(doc, "file_type"); if (fileType != "MMC-TRANSLATION-INDEX") { qCritical() << "Translations Download Failed: index file is of unknown file type" << fileType; return; } auto version = Json::requireInteger(doc, "version"); if (version > 2) { qCritical() << "Translations Download Failed: index file is of unknown format version" << fileType; return; } auto langObjs = Json::requireObject(doc, "languages"); for (auto iter = langObjs.begin(); iter != langObjs.end(); ++iter) { Language lang(iter.key()); auto langObj = Json::requireObject(iter.value()); lang.setTranslationStats(langObj["translated"].toInt(), langObj["untranslated"].toInt(), langObj["fuzzy"].toInt()); lang.fileName = Json::requireString(langObj, "file"); lang.fileSha1 = Json::requireString(langObj, "sha1"); lang.fileSize = Json::requireInteger(langObj, "size"); languages.insert(lang.key, lang); } } catch ([[maybe_unused]] Json::JsonException& e) { qCritical() << "Translations Download Failed: index file could not be parsed as json"; } } } // namespace void TranslationsModel::reloadLocalFiles() { QMap languages = { { g_defaultLangCode, Language(g_defaultLangCode) } }; const auto indexPath = d->m_dir.absoluteFilePath("index_v2.json"); if (!QFileInfo::exists(indexPath)) { downloadIndex(); return; } readIndex(indexPath, languages); auto entries = d->m_dir.entryInfoList({ "mmc_*.qm", "*.po" }, QDir::Files | QDir::NoDotAndDotDot); for (auto& entry : entries) { auto completeSuffix = entry.completeSuffix(); QString langCode; FileType fileType = FileType::None; if (completeSuffix == "qm") { langCode = entry.baseName().remove(0, 4); fileType = FileType::Qm; } else if (completeSuffix == "po") { langCode = entry.baseName(); fileType = FileType::Po; } else { continue; } auto langIter = languages.find(langCode); if (langIter != languages.end()) { auto& language = *langIter; // TODO: use std::to_underlying in C++23 if (static_cast(fileType) > static_cast(language.localFileType)) { language.localFileType = fileType; } } else { if (fileType == FileType::Po) { Language localFound(langCode); localFound.localFileType = FileType::Po; languages.insert(langCode, localFound); } } } // changed and removed languages for (auto iter = d->m_languages.begin(); iter != d->m_languages.end();) { auto& language = *iter; auto row = iter - d->m_languages.begin(); auto updatedLanguageIter = languages.find(language.key); if (updatedLanguageIter != languages.end()) { if (language.isIdenticalTo(*updatedLanguageIter)) { languages.remove(language.key); } else { language.apply(*updatedLanguageIter); emit dataChanged(index(row), index(row)); languages.remove(language.key); } ++iter; } else { beginRemoveRows(QModelIndex(), row, row); iter = d->m_languages.erase(iter); endRemoveRows(); } } // added languages if (languages.isEmpty()) { return; } beginInsertRows(QModelIndex(), 0, d->m_languages.size() + languages.size() - 1); for (auto& language : languages) { d->m_languages.append(language); } const auto comp = [systemLocale = getSystemLocaleName(), systemLanguage = getSystemLanguage()](const Language& a, const Language& b) { if (a.key != b.key) { if (a.key == systemLocale || a.key == systemLanguage) { return true; } if (b.key == systemLocale || b.key == systemLanguage) { return false; } } return a.languageName().toLower() < b.languageName().toLower(); }; std::ranges::sort(d->m_languages, comp); endInsertRows(); } namespace { enum class Column : std::uint8_t { Language, Completeness }; } QVariant TranslationsModel::data(const QModelIndex& index, const int role) const { if (!index.isValid()) { return {}; } const int row = index.row(); const auto column = static_cast(index.column()); if (row < 0 || row >= d->m_languages.size()) { return {}; } auto& lang = d->m_languages[row]; switch (role) { case Qt::DisplayRole: { switch (column) { case Column::Language: { return lang.languageName(); } case Column::Completeness: { return QString("%1%").arg(lang.percentTranslated(), 3, 'f', 1); } } qWarning("TranslationModel::data not implemented when role is DisplayRole"); } case Qt::ToolTipRole: { return tr("%1:\n%2 translated\n%3 fuzzy\n%4 total") .arg(lang.key, QString::number(lang.translated), QString::number(lang.fuzzy), QString::number(lang.total)); } case Qt::UserRole: return lang.key; default: return {}; } } QVariant TranslationsModel::headerData(int section, const Qt::Orientation orientation, const int role) const { auto column = static_cast(section); if (role == Qt::DisplayRole) { switch (column) { case Column::Language: { return tr("Language"); } case Column::Completeness: { return tr("Completeness"); } } } else if (role == Qt::ToolTipRole) { switch (column) { case Column::Language: { return tr("The native language name."); } case Column::Completeness: { return tr("Completeness is the percentage of fully translated strings, not counting automatically guessed ones."); } } } return QAbstractListModel::headerData(section, orientation, role); } int TranslationsModel::rowCount([[maybe_unused]] const QModelIndex& parent) const { return d->m_languages.size(); } int TranslationsModel::columnCount([[maybe_unused]] const QModelIndex& parent) const { return 2; } QList::Iterator TranslationsModel::findLanguage(const QString& key) const { return std::ranges::find_if(d->m_languages, [key](const Language& lang) { return lang.key == key; }); } std::optional TranslationsModel::findLanguageAsOptional(const QString& key) const { auto found = findLanguage(key); if (found != d->m_languages.end()) { return *found; } return {}; } void TranslationsModel::setUseSystemLocale(const bool useSystemLocale) const { APPLICATION->settings()->set("UseSystemLocale", useSystemLocale); QLocale::setDefault(useSystemLocale ? QLocale::system() : QLocale(selectedLanguage())); } bool TranslationsModel::selectLanguage(QString key) const { QString& langCode = key; auto langPtr = findLanguageAsOptional(key); if (langCode.isEmpty()) { d->m_noLanguageSet = true; } if (!langPtr.has_value()) { qWarning() << "Selected invalid language" << key << ", defaulting to" << g_defaultLangCode; langCode = g_defaultLangCode; } else { langCode = langPtr->key; } // uninstall existing translators if there are any if (d->m_appTranslator) { QCoreApplication::removeTranslator(d->m_appTranslator.get()); d->m_appTranslator.reset(); } if (d->m_qtTranslator) { QCoreApplication::removeTranslator(d->m_qtTranslator.get()); d->m_qtTranslator.reset(); } /* * FIXME: potential source of crashes: * In a multithreaded application, the default locale should be set at application startup, before any non-GUI threads are created. * This function is not reentrant. */ const bool useSystemLocale = APPLICATION->settings()->get("UseSystemLocale").toBool(); QLocale::setDefault(useSystemLocale ? QLocale::system() : QLocale(langCode)); // if it's the default UI language, finish if (langCode == g_defaultLangCode) { d->m_selectedLanguage = langCode; return true; } // otherwise install new translations bool successful = false; // FIXME: this is likely never present. FIX IT. d->m_qtTranslator = std::make_unique(); if (d->m_qtTranslator->load("qt_" + langCode, QLibraryInfo::path(QLibraryInfo::TranslationsPath))) { qDebug() << "Loading Qt Language File for" << langCode.toLocal8Bit().constData() << "..."; if (!QCoreApplication::installTranslator(d->m_qtTranslator.get())) { qCritical() << "Loading Qt Language File failed."; d->m_qtTranslator.reset(); } else { successful = true; } } else { d->m_qtTranslator.reset(); } if (langPtr->localFileType == FileType::Po) { qDebug() << "Loading Application Language File for" << langCode.toLocal8Bit().constData() << "..."; d->m_appTranslator = std::make_unique(FS::PathCombine(d->m_dir.path(), langCode + ".po")); if (!d->m_appTranslator->isEmpty()) { if (!QCoreApplication::installTranslator(d->m_appTranslator.get())) { qCritical() << "Installing Application Language File failed."; d->m_appTranslator.reset(); } else { successful = true; } } else { qCritical() << "Loading Application Language File failed."; d->m_appTranslator.reset(); } } else if (langPtr->localFileType == FileType::Qm) { d->m_appTranslator = std::make_unique(); if (d->m_appTranslator->load("mmc_" + langCode, d->m_dir.path())) { qDebug() << "Loading Application Language File for" << langCode.toLocal8Bit().constData() << "..."; if (!QCoreApplication::installTranslator(d->m_appTranslator.get())) { qCritical() << "Installing Application Language File failed."; d->m_appTranslator.reset(); } else { successful = true; } } else { d->m_appTranslator.reset(); } } else { d->m_appTranslator.reset(); } d->m_selectedLanguage = langCode; return successful; } QModelIndex TranslationsModel::selectedIndex() const { auto found = findLanguage(d->m_selectedLanguage); if (found != d->m_languages.end()) { return index(std::distance(d->m_languages.begin(), found), 0, QModelIndex()); } return {}; } QString TranslationsModel::selectedLanguage() const { return d->m_selectedLanguage; } void TranslationsModel::downloadIndex() { if (d->m_indexJob || d->m_downloadJob) { return; } qDebug() << "Downloading Translations Index..."; d->m_indexJob.reset(new NetJob("Translations Index", APPLICATION->network())); const MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "index_v2.json"); entry->setStale(true); auto task = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATION_FILES_URL + "index_v2.json"), entry); d->m_indexTask = task.get(); d->m_indexJob->addNetAction(task); d->m_indexJob->setAskRetry(false); connect(d->m_indexJob.get(), &NetJob::failed, this, &TranslationsModel::indexFailed); connect(d->m_indexJob.get(), &NetJob::succeeded, this, &TranslationsModel::indexReceived); d->m_indexJob->start(); } void TranslationsModel::updateLanguage(const QString& key) { if (key == g_defaultLangCode) { qWarning() << "Cannot update builtin language" << key; return; } auto found = findLanguageAsOptional(key); if (!found.has_value()) { qWarning() << "Cannot update invalid language" << key; return; } if (!found->updated) { downloadTranslation(key); } } void TranslationsModel::downloadTranslation(const QString& key) { if (d->m_downloadJob) { d->m_nextDownload = key; return; } auto lang = findLanguageAsOptional(key); if (!lang.has_value()) { qWarning() << "Will not download an unknown translation" << key; return; } d->m_downloadingTranslation = key; const MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("translations", "mmc_" + key + ".qm"); entry->setStale(true); auto dl = Net::Download::makeCached(QUrl(BuildConfig.TRANSLATION_FILES_URL + lang->fileName), entry); dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, lang->fileSha1)); dl->setProgress(dl->getProgress(), lang->fileSize); d->m_downloadJob.reset(new NetJob("Translation for " + key, APPLICATION->network())); d->m_downloadJob->addNetAction(dl); d->m_downloadJob->setAskRetry(false); connect(d->m_downloadJob.get(), &NetJob::succeeded, this, &TranslationsModel::dlGood); connect(d->m_downloadJob.get(), &NetJob::failed, this, &TranslationsModel::dlFailed); d->m_downloadJob->start(); } void TranslationsModel::downloadNext() { if (!d->m_nextDownload.isEmpty()) { downloadTranslation(d->m_nextDownload); d->m_nextDownload.clear(); } } void TranslationsModel::dlFailed(const QString& reason) { qCritical() << "Translations Download Failed:" << reason; d->m_downloadJob.reset(); downloadNext(); } void TranslationsModel::dlGood() { qDebug() << "Got translation:" << d->m_downloadingTranslation; if (d->m_downloadingTranslation == d->m_selectedLanguage) { selectLanguage(d->m_selectedLanguage); } d->m_downloadJob.reset(); downloadNext(); } void TranslationsModel::indexFailed(const QString& reason) const { qCritical() << "Translations Index Download Failed:" << reason; d->m_indexJob.reset(); } PrismLauncher-11.0.3/launcher/translations/POTranslator.cpp0000644000175100017510000002370515224505336023410 0ustar runnerrunner#include "POTranslator.h" #include #include "FileSystem.h" struct POEntry { QString text; bool fuzzy; }; struct POTranslatorPrivate { QString filename; QHash mapping; QHash mapping_disambiguatrion; bool loaded = false; void reload(); }; class ParserArray : public QByteArray { public: ParserArray(const QByteArray& in) : QByteArray(in) {} bool chomp(const char* data, int length) { if (startsWith(data)) { remove(0, length); return true; } return false; } bool chompString(QByteArray& appendHere) { QByteArray msg; bool escape = false; if (size() < 2) { qDebug() << "String fragment is too short"; return false; } if (!startsWith('"')) { qDebug() << "String fragment does not start with \""; return false; } if (!endsWith('"')) { qDebug() << "String fragment does not end with \", instead, there is" << at(size() - 1); return false; } for (int i = 1; i < size() - 1; i++) { char c = operator[](i); if (escape) { switch (c) { case 'r': msg += '\r'; break; case 'n': msg += '\n'; break; case 't': msg += '\t'; break; case 'v': msg += '\v'; break; case 'a': msg += '\a'; break; case 'b': msg += '\b'; break; case 'f': msg += '\f'; break; case '"': msg += '"'; break; case '\\': msg.append('\\'); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { int octal_start = i; while ((c = operator[](i)) >= '0' && c <= '7') { i++; if (i == length() - 1) { qDebug() << "Something went bad while parsing an octal escape string..."; return false; } } msg += mid(octal_start, i - octal_start).toUInt(0, 8); break; } case 'x': { // chomp the 'x' i++; int hex_start = i; while (isxdigit(operator[](i))) { i++; if (i == length() - 1) { qDebug() << "Something went bad while parsing a hex escape string..."; return false; } } msg += mid(hex_start, i - hex_start).toUInt(0, 16); break; } default: { qDebug() << "Invalid escape sequence character:" << c; return false; } } escape = false; } else if (c == '\\') { escape = true; } else { msg += c; } } if (escape) { qDebug() << "Unterminated escape sequence..."; return false; } appendHere += msg; return true; } }; void POTranslatorPrivate::reload() { QFile file(filename); if (!file.open(QFile::OpenMode::enum_type::ReadOnly | QFile::OpenMode::enum_type::Text)) { qDebug() << "Failed to open PO file:" << filename << "error:" << file.errorString(); return; } QByteArray context; QByteArray disambiguation; QByteArray id; QByteArray str; bool fuzzy = false; bool nextFuzzy = false; enum class Mode { First, MessageContext, MessageId, MessageString } mode = Mode::First; int lineNumber = 0; QHash newMapping; QHash newMapping_disambiguation; auto endEntry = [&]() { auto strStr = QString::fromUtf8(str); // NOTE: PO header has empty id. We skip it. if (!id.isEmpty()) { auto normalKey = context + "|" + id; newMapping.insert(normalKey, { strStr, fuzzy }); if (!disambiguation.isEmpty()) { auto disambiguationKey = context + "|" + id + "@" + disambiguation; newMapping_disambiguation.insert(disambiguationKey, { strStr, fuzzy }); } } context.clear(); disambiguation.clear(); id.clear(); str.clear(); fuzzy = nextFuzzy; nextFuzzy = false; }; while (!file.atEnd()) { ParserArray line = file.readLine(); if (line.endsWith('\n')) { line.resize(line.size() - 1); } if (line.endsWith('\r')) { line.resize(line.size() - 1); } if (!line.size()) { // NIL } else if (line[0] == '#') { if (line.contains(", fuzzy")) { nextFuzzy = true; } } else if (line.startsWith('"')) { QByteArray temp; QByteArray* out = &temp; switch (mode) { case Mode::First: qDebug() << "Unexpected escaped string during initial state... line:" << lineNumber; return; case Mode::MessageString: out = &str; break; case Mode::MessageContext: out = &context; break; case Mode::MessageId: out = &id; break; } if (!line.chompString(*out)) { qDebug() << "Badly formatted string on line:" << lineNumber; return; } } else if (line.chomp("msgctxt ", 8)) { switch (mode) { case Mode::First: break; case Mode::MessageString: endEntry(); break; case Mode::MessageContext: case Mode::MessageId: qDebug() << "Unexpected msgctxt line:" << lineNumber; return; } if (line.chompString(context)) { auto parts = context.split('|'); context = parts[0]; if (parts.size() > 1 && !parts[1].isEmpty()) { disambiguation = parts[1]; } mode = Mode::MessageContext; } } else if (line.chomp("msgid ", 6)) { switch (mode) { case Mode::MessageContext: case Mode::First: break; case Mode::MessageString: endEntry(); break; case Mode::MessageId: qDebug() << "Unexpected msgid line:" << lineNumber; return; } if (line.chompString(id)) { mode = Mode::MessageId; } } else if (line.chomp("msgstr ", 7)) { switch (mode) { case Mode::First: case Mode::MessageString: case Mode::MessageContext: qDebug() << "Unexpected msgstr line:" << lineNumber; return; case Mode::MessageId: break; } if (line.chompString(str)) { mode = Mode::MessageString; } } else { qDebug() << "I did not understand line:" << lineNumber << ":" << QString::fromUtf8(line); } lineNumber++; } endEntry(); mapping = std::move(newMapping); mapping_disambiguatrion = std::move(newMapping_disambiguation); loaded = true; } POTranslator::POTranslator(const QString& filename, QObject* parent) : QTranslator(parent) { d = new POTranslatorPrivate; d->filename = filename; d->reload(); } POTranslator::~POTranslator() { delete d; } QString POTranslator::translate(const char* context, const char* sourceText, const char* disambiguation, [[maybe_unused]] int n) const { if (disambiguation) { auto disambiguationKey = QByteArray(context) + "|" + QByteArray(sourceText) + "@" + QByteArray(disambiguation); auto iter = d->mapping_disambiguatrion.find(disambiguationKey); if (iter != d->mapping_disambiguatrion.end()) { auto& entry = *iter; if (entry.text.isEmpty()) { qDebug() << "Translation entry has no content:" << disambiguationKey; } if (entry.fuzzy) { qDebug() << "Translation entry is fuzzy:" << disambiguationKey << "->" << entry.text; } return entry.text; } } auto key = QByteArray(context) + "|" + QByteArray(sourceText); auto iter = d->mapping.find(key); if (iter != d->mapping.end()) { auto& entry = *iter; if (entry.text.isEmpty()) { qDebug() << "Translation entry has no content:" << key; } if (entry.fuzzy) { qDebug() << "Translation entry is fuzzy:" << key << "->" << entry.text; } return entry.text; } return QString(); } bool POTranslator::isEmpty() const { return !d->loaded; } PrismLauncher-11.0.3/launcher/translations/POTranslator.h0000644000175100017510000000066615224505336023056 0ustar runnerrunner#pragma once #include struct POTranslatorPrivate; class POTranslator : public QTranslator { Q_OBJECT public: explicit POTranslator(const QString& filename, QObject* parent = nullptr); virtual ~POTranslator(); QString translate(const char* context, const char* sourceText, const char* disambiguation, int n) const override; bool isEmpty() const override; private: POTranslatorPrivate* d; }; PrismLauncher-11.0.3/launcher/updater/0000755000175100017510000000000015224505336017230 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/updater/prismupdater/0000755000175100017510000000000015224505336021747 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/updater/prismupdater/GitHubRelease.h0000644000175100017510000000314315224505336024604 0ustar runnerrunner// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include #include #include #include #include "Version.h" struct GitHubReleaseAsset { int id = -1; QString name; QString label; QString content_type; int size; QDateTime created_at; QDateTime updated_at; QString browser_download_url; bool isValid() { return id > 0; } }; struct GitHubRelease { int id = -1; QString name; QString tag_name; QDateTime created_at; QDateTime published_at; bool prerelease; bool draft; QString body; QList assets; Version version; bool isValid() const { return id > 0; } }; QDebug operator<<(QDebug debug, const GitHubReleaseAsset& rls); QDebug operator<<(QDebug debug, const GitHubRelease& rls); PrismLauncher-11.0.3/launcher/updater/prismupdater/PrismUpdater.h0000644000175100017510000000765615224505336024555 0ustar runnerrunner// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include "QObjectPtr.h" #include "net/Download.h" #define PRISM_EXTERNAL_EXE #include "FileSystem.h" #include "GitHubRelease.h" class PrismUpdaterApp : public QApplication { Q_OBJECT public: enum Status { Starting, Failed, Succeeded, Initialized, Aborted }; PrismUpdaterApp(int& argc, char** argv); virtual ~PrismUpdaterApp(); void loadReleaseList(); void run(); Status status() const { return m_status; } private: void fail(const QString& reason); void abort(const QString& reason); void showFatalErrorMessage(const QString& title, const QString& content); bool loadPrismVersionFromExe(const QString& exe_path); void downloadReleasePage(const QString& api_url, int page); int parseReleasePage(const QByteArray* response); bool needUpdate(const GitHubRelease& release); GitHubRelease getLatestRelease(); GitHubRelease selectRelease(); QList newerReleases(); QList nonDraftReleases(); void printReleases(); QList validReleaseArtifacts(const GitHubRelease& release); GitHubReleaseAsset selectAsset(const QList& assets); void performUpdate(const GitHubRelease& release); void performInstall(QFileInfo file); void unpackAndInstall(QFileInfo file); void backupAppDir(); std::optional unpackArchive(QFileInfo file); QFileInfo downloadAsset(const GitHubReleaseAsset& asset); bool callAppImageUpdate(); void moveAndFinishUpdate(QDir target); public slots: void downloadError(QString reason); private: const QString& root() { return m_rootPath; } bool isPortable() { return m_isPortable; } void clearUpdateLog(); void logUpdate(const QString& msg); QString m_rootPath; QString m_dataPath; bool m_isPortable = false; bool m_isAppimage = false; bool m_isFlatpak = false; QString m_appimagePath; QString m_prismExecutable; QUrl m_prismRepoUrl; Version m_userSelectedVersion; bool m_checkOnly; bool m_forceUpdate; bool m_printOnly; bool m_selectUI; bool m_allowDowngrade; bool m_allowPreRelease; QString m_updateLogPath; QString m_prismBinaryName; QString m_prismVersion; int m_prismVersionMajor = -1; int m_prismVersionMinor = -1; int m_prismVersionPatch = -1; QString m_prsimVersionChannel; QString m_prismGitCommit; GitHubRelease m_install_release; Status m_status = Status::Starting; std::unique_ptr m_network; QString m_current_url; Task::Ptr m_current_task; QList m_releases; public: std::unique_ptr logFile; bool logToConsole = false; #if defined Q_OS_WIN32 // used on Windows to attach the standard IO streams bool consoleAttached = false; #endif }; PrismLauncher-11.0.3/launcher/updater/prismupdater/PrismUpdater.cpp0000644000175100017510000013560215224505336025101 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "PrismUpdater.h" #include "BuildConfig.h" #include "ui/dialogs/ProgressDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fs = std::filesystem; #include "DesktopServices.h" #include "updater/prismupdater/UpdaterDialogs.h" #include "FileSystem.h" #include "Json.h" #include "StringUtils.h" #include "net/Download.h" #include "net/RawHeaderProxy.h" #include "MMCZip.h" /** output to the log file */ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { static std::mutex loggerMutex; const std::lock_guard lock(loggerMutex); // synchronized, QFile logFile is not thread-safe QString out = qFormatLogMessage(type, context, msg); out += QChar::LineFeed; PrismUpdaterApp* app = static_cast(QCoreApplication::instance()); app->logFile->write(out.toUtf8()); app->logFile->flush(); if (app->logToConsole) { QTextStream(stderr) << out.toLocal8Bit(); fflush(stderr); } } PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, argv) { setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); setApplicationName(BuildConfig.LAUNCHER_NAME + "Updater"); setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT); // Command line parsing QCommandLineParser parser; parser.setApplicationDescription(QObject::tr("An auto-updater for Prism Launcher")); parser.addOptions( { { { "d", "dir" }, tr("Use a custom path as application root (use '.' for current directory)."), tr("directory") }, { { "V", "prism-version" }, tr("Use this version as the installed launcher version. (provided because stdout can not be reliably captured on windows)"), tr("installed launcher version") }, { { "I", "install-version" }, "Install a specific version.", tr("version name") }, { { "U", "update-url" }, tr("Update from the specified repo."), tr("github repo url") }, { { "c", "check-only" }, tr("Only check if an update is needed. Exit status 100 if true, 0 if false (or non 0 if there was an error).") }, { { "p", "pre-release" }, tr("Allow updating to pre-release releases") }, { { "F", "force" }, tr("Force an update, even if one is not needed.") }, { { "l", "list" }, tr("List available releases.") }, { "debug", tr("Log debug to console.") }, { { "S", "select-ui" }, tr("Select the version to install with a GUI.") }, { { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous versions.") } }); parser.addHelpOption(); parser.addVersionOption(); parser.process(arguments()); logToConsole = parser.isSet("debug"); QString origCwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); { // find data director // Root path is used for updates and portable data #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr m_rootPath = foo.absolutePath(); #elif defined(Q_OS_WIN32) m_rootPath = binPath; #elif defined(Q_OS_MAC) QDir foo(FS::PathCombine(binPath, "../..")); m_rootPath = foo.absolutePath(); // on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues) FS::updateTimestamp(m_rootPath); #endif } QString adjustedBy; // change folder QString dirParam = parser.value("dir"); if (!dirParam.isEmpty()) { // the dir param. it makes prism launcher data path point to whatever the user specified // on command line adjustedBy = "Command line"; m_dataPath = dirParam; #ifndef Q_OS_MACOS if (QDir(FS::PathCombine(m_rootPath, "UserData")).exists()) { m_isPortable = true; } if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { m_isPortable = true; } #endif } else if (auto dataDirEnv = QProcessEnvironment::systemEnvironment().value(QString("%1_DATA_DIR").arg(BuildConfig.LAUNCHER_NAME.toUpper())); !dataDirEnv.isEmpty()) { adjustedBy = "System environment"; m_dataPath = dataDirEnv; #ifndef Q_OS_MACOS if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { m_isPortable = true; } #endif } else { QDir foo(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); m_dataPath = foo.absolutePath(); adjustedBy = "Persistent data path"; #ifndef Q_OS_MACOS if (auto portableUserData = FS::PathCombine(m_rootPath, "UserData"); QDir(portableUserData).exists()) { m_dataPath = portableUserData; adjustedBy = "Portable user data path"; m_isPortable = true; } else if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { m_dataPath = m_rootPath; adjustedBy = "Portable data path"; m_isPortable = true; } #endif } m_updateLogPath = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); { // setup logging FS::ensureFolderPathExists(FS::PathCombine(m_dataPath, "logs")); static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "Updater" + (m_checkOnly ? "-CheckOnly" : "") + "-%0.log"; static const QString logBase = FS::PathCombine(m_dataPath, "logs", baseLogFile); if (FS::ensureFolderPathExists("logs")) { // enough history to track both launches of the updater during a portable install FS::move(logBase.arg(1), logBase.arg(2)); FS::move(logBase.arg(0), logBase.arg(1)); } logFile = std::unique_ptr(new QFile(logBase.arg(0))); if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { showFatalErrorMessage(tr("The launcher data folder is not writable!"), tr("The updater couldn't create a log file - %1.\n" "\n" "Make sure you have write permissions to the data folder.\n" "(%2)\n" "\n" "The updater cannot continue until you fix this problem.") .arg(logFile->errorString()) .arg(m_dataPath)); return; } qInstallMessageHandler(appDebugOutput); qSetMessagePattern( "%{time process}" " " "%{if-debug}D%{endif}" "%{if-info}I%{endif}" "%{if-warning}W%{endif}" "%{if-critical}C%{endif}" "%{if-fatal}F%{endif}" " " "|" " " "%{if-category}[%{category}]: %{endif}" "%{message}"); bool foundLoggingRules = false; auto logRulesFile = QStringLiteral("qtlogging.ini"); auto logRulesPath = FS::PathCombine(m_dataPath, logRulesFile); qDebug() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); // search the dataPath() // seach app data standard path if (!foundLoggingRules && !isPortable() && dirParam.isEmpty()) { logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile)); if (!logRulesPath.isEmpty()) { qDebug() << "Found" << logRulesPath << "..."; foundLoggingRules = true; } } // seach root path if (!foundLoggingRules) { logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); qDebug() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); } if (foundLoggingRules) { // load and set logging rules qDebug() << "Loading logging rules from:" << logRulesPath; QSettings loggingRules(logRulesPath, QSettings::IniFormat); loggingRules.beginGroup("Rules"); QStringList rule_names = loggingRules.childKeys(); QStringList rules; qDebug() << "Setting log rules:"; for (auto rule_name : rule_names) { auto rule = QString("%1=%2").arg(rule_name).arg(loggingRules.value(rule_name).toString()); rules.append(rule); qDebug() << " " << rule; } auto rules_str = rules.join("\n"); QLoggingCategory::setFilterRules(rules_str); } qDebug() << "<> Log initialized."; } { // log debug program info qDebug() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + " Updater, " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); qDebug() << "Version :" << BuildConfig.printableVersionString(); qDebug() << "Git commit :" << BuildConfig.GIT_COMMIT; qDebug() << "Git refspec :" << BuildConfig.GIT_REFSPEC; qDebug() << "Compiled for :" << BuildConfig.systemID(); qDebug() << "Compiled by :" << BuildConfig.compilerID(); qDebug() << "Build Artifact :" << BuildConfig.BUILD_ARTIFACT; if (adjustedBy.size()) { qDebug() << "Data dir before adjustment :" << origCwdPath; qDebug() << "Data dir after adjustment :" << m_dataPath; qDebug() << "Adjusted by :" << adjustedBy; } else { qDebug() << "Data dir :" << QDir::currentPath(); } qDebug() << "Work dir :" << QDir::currentPath(); qDebug() << "Binary path :" << binPath; qDebug() << "Application root path :" << m_rootPath; qDebug() << "Portable install :" << m_isPortable; qDebug() << "<> Paths set."; } { // network m_network = std::make_unique(); qDebug() << "Detecting proxy settings..."; QNetworkProxy proxy = QNetworkProxy::applicationProxy(); m_network->setProxy(proxy); } #ifdef Q_OS_MACOS showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support installations on MacOS")); #endif if (binPath.startsWith("/tmp/.mount_")) { m_isAppimage = true; m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); if (m_appimagePath.isEmpty()) { showFatalErrorMessage(tr("Unsupported Installation"), tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); } } m_isFlatpak = DesktopServices::isFlatpak(); QString prism_executable = FS::PathCombine(binPath, BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 prism_executable.append(".exe"); #endif if (!QFileInfo(prism_executable).isFile()) { showFatalErrorMessage(tr("Unsupported Installation"), tr("The updater can not find the main executable.")); } m_prismExecutable = prism_executable; auto prism_update_url = parser.value("update-url"); if (prism_update_url.isEmpty()) prism_update_url = BuildConfig.UPDATER_GITHUB_REPO; m_prismRepoUrl = QUrl::fromUserInput(prism_update_url); m_checkOnly = parser.isSet("check-only"); m_forceUpdate = parser.isSet("force"); m_printOnly = parser.isSet("list"); auto user_version = parser.value("install-version"); if (!user_version.isEmpty()) { m_userSelectedVersion = Version(user_version); } m_selectUI = parser.isSet("select-ui"); m_allowDowngrade = parser.isSet("allow-downgrade"); auto version = parser.value("prism-version"); if (!version.isEmpty()) { if (version.contains('-')) { auto index = version.indexOf('-'); m_prsimVersionChannel = version.mid(index + 1); version = version.left(index); } else { m_prsimVersionChannel = "stable"; } auto version_parts = version.split('.'); m_prismVersionMajor = version_parts.takeFirst().toInt(); m_prismVersionMinor = version_parts.takeFirst().toInt(); if (!version_parts.isEmpty()) m_prismVersionPatch = version_parts.takeFirst().toInt(); else m_prismVersionPatch = 0; } m_allowPreRelease = parser.isSet("pre-release"); auto marker_file_path = QDir(m_rootPath).absoluteFilePath(".prism_launcher_updater_unpack.marker"); auto marker_file = QFileInfo(marker_file_path); if (marker_file.exists()) { auto target_dir = QString(FS::read(marker_file_path)).trimmed(); if (target_dir.isEmpty()) { qWarning() << "Empty updater marker file contains no install target. making best guess of parent dir"; target_dir = QDir(m_rootPath).absoluteFilePath(".."); } QMetaObject::invokeMethod(this, [this, target_dir]() { moveAndFinishUpdate(target_dir); }, Qt::QueuedConnection); } else { QMetaObject::invokeMethod(this, &PrismUpdaterApp::loadReleaseList, Qt::QueuedConnection); } } PrismUpdaterApp::~PrismUpdaterApp() { qDebug() << "updater shutting down"; // Shut down logger by setting the logger function to nothing qInstallMessageHandler(nullptr); } void PrismUpdaterApp::fail(const QString& reason) { qCritical() << qPrintable(reason); m_status = Failed; exit(1); } void PrismUpdaterApp::abort(const QString& reason) { qCritical() << qPrintable(reason); m_status = Aborted; exit(2); } void PrismUpdaterApp::showFatalErrorMessage(const QString& title, const QString& content) { m_status = Failed; auto msgBox = new QMessageBox(); msgBox->setWindowTitle(title); msgBox->setText(content); msgBox->setStandardButtons(QMessageBox::Ok); msgBox->setDefaultButton(QMessageBox::Ok); msgBox->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction); msgBox->setIcon(QMessageBox::Critical); msgBox->setMinimumWidth(460); msgBox->adjustSize(); msgBox->exec(); exit(1); } void PrismUpdaterApp::run() { qDebug() << "found" << m_releases.length() << "releases on github"; qDebug() << "loading exe at" << m_prismExecutable; if (m_printOnly) { printReleases(); m_status = Succeeded; return exit(0); } if (!loadPrismVersionFromExe(m_prismExecutable)) { m_prismVersion = BuildConfig.printableVersionString(); m_prismVersionMajor = BuildConfig.VERSION_MAJOR; m_prismVersionMinor = BuildConfig.VERSION_MINOR; m_prismVersionPatch = BuildConfig.VERSION_PATCH; m_prsimVersionChannel = BuildConfig.VERSION_CHANNEL; m_prismGitCommit = BuildConfig.GIT_COMMIT; } m_status = Succeeded; qDebug() << "Executable reports as:" << m_prismBinaryName << "version:" << m_prismVersion; qDebug() << "Version major:" << m_prismVersionMajor; qDebug() << "Version minor:" << m_prismVersionMinor; qDebug() << "Version minor:" << m_prismVersionPatch; qDebug() << "Version channel:" << m_prsimVersionChannel; qDebug() << "Git Commit:" << m_prismGitCommit; auto latest = getLatestRelease(); qDebug() << "Latest release" << latest.version; auto need_update = needUpdate(latest); if (m_checkOnly) { if (need_update) { QTextStream stdOutStream(stdout); stdOutStream << "Name: " << latest.name << "\n"; stdOutStream << "Version: " << latest.tag_name << "\n"; stdOutStream << "TimeStamp: " << latest.created_at.toString(Qt::ISODate) << "\n"; stdOutStream << latest.body << "\n"; stdOutStream.flush(); return exit(100); } else { return exit(0); } } if (m_isFlatpak) { showFatalErrorMessage(tr("Updating flatpack not supported"), tr("Actions outside of checking if an update is available are not " "supported when running the flatpak version of Prism Launcher.")); return; } if (m_isAppimage) { bool result = true; if (need_update) result = callAppImageUpdate(); return exit(result ? 0 : 1); } if (need_update || m_forceUpdate || !m_userSelectedVersion.isEmpty()) { GitHubRelease update_release = latest; if (!m_userSelectedVersion.isEmpty()) { bool found = false; for (auto rls : m_releases) { if (rls.version == m_userSelectedVersion) { found = true; update_release = rls; break; } } if (!found) { showFatalErrorMessage( "No release for version!", QString("Can not find a github release for specified version %1").arg(m_userSelectedVersion.toString())); return; } } else if (m_selectUI) { update_release = selectRelease(); if (!update_release.isValid()) { showFatalErrorMessage("No version selected.", "No version was selected."); return; } } performUpdate(update_release); } exit(0); } void PrismUpdaterApp::moveAndFinishUpdate(QDir target) { logUpdate("Finishing update process"); logUpdate("Waiting 2 seconds for resources to free"); this->thread()->sleep(2); auto manifest_path = FS::PathCombine(m_rootPath, "manifest.txt"); QFileInfo manifest(manifest_path); auto app_dir = QDir(m_rootPath); QStringList file_list; if (manifest.isFile()) { // load manifest from file logUpdate(tr("Reading manifest from %1").arg(manifest.absoluteFilePath())); try { auto contents = QString::fromUtf8(FS::read(manifest.absoluteFilePath())); auto files = contents.split('\n'); for (auto file : files) { file_list.append(file.trimmed()); } } catch (FS::FileSystemException&) { } } if (file_list.isEmpty()) { logUpdate(tr("Manifest empty, making best guess of the directory contents of %1").arg(m_rootPath)); auto entries = target.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); for (auto entry : entries) { file_list.append(entry.fileName()); } } logUpdate(tr("Installing the following to %1 :\n %2").arg(target.absolutePath()).arg(file_list.join(",\n "))); bool error = false; QProgressDialog progress(tr("Installing from %1").arg(m_rootPath), "", 0, file_list.length()); progress.setCancelButton(nullptr); progress.setMinimumWidth(400); progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); logUpdate(tr("Installing from %1").arg(m_rootPath)); auto copy = [this, app_dir, target](QString to_install_file) { auto rel_path = app_dir.relativeFilePath(to_install_file); auto install_path = FS::PathCombine(target.absolutePath(), rel_path); logUpdate(tr("Installing %1 from %2").arg(install_path).arg(to_install_file)); FS::ensureFilePathExists(install_path); auto result = FS::copy(to_install_file, install_path).overwrite(true)(); if (!result) { logUpdate(tr("Failed copy %1 to %2").arg(to_install_file).arg(install_path)); return true; } return false; }; int i = 0; for (auto glob : file_list) { QDirIterator iter(m_rootPath, QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); progress.setValue(i); QCoreApplication::processEvents(); if (!iter.hasNext() && !glob.isEmpty()) { if (auto file_info = QFileInfo(FS::PathCombine(m_rootPath, glob)); file_info.exists()) { error |= copy(file_info.absoluteFilePath()); } else { logUpdate(tr("File doesn't exist, ignoring: %1").arg(FS::PathCombine(m_rootPath, glob))); } } else { while (iter.hasNext()) { error |= copy(iter.next()); } } i++; } progress.setValue(i); QCoreApplication::processEvents(); if (error) { logUpdate(tr("There were errors installing the update.")); auto fail_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.fail"); FS::copy(m_updateLogPath, fail_marker).overwrite(true)(); } else { logUpdate(tr("Update succeed.")); auto success_marker = FS::PathCombine(m_dataPath, ".prism_launcher_update.success"); FS::copy(m_updateLogPath, success_marker).overwrite(true)(); } auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); FS::deletePath(update_lock_path); QProcess proc; auto app_exe_name = BuildConfig.LAUNCHER_APP_BINARY_NAME; #if defined Q_OS_WIN32 app_exe_name.append(".exe"); auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); #else app_exe_name.prepend("bin/"); #endif auto app_exe_path = target.absoluteFilePath(app_exe_name); proc.startDetached(app_exe_path); exit(error ? 1 : 0); } void PrismUpdaterApp::printReleases() { for (auto release : m_releases) { std::cout << release.name.toStdString() << " Version: " << release.tag_name.toStdString() << std::endl; } } QList PrismUpdaterApp::nonDraftReleases() { QList nonDraft; for (auto rls : m_releases) { if (rls.isValid() && !rls.draft) nonDraft.append(rls); } return nonDraft; } QList PrismUpdaterApp::newerReleases() { QList newer; for (auto rls : nonDraftReleases()) { if (rls.version > m_prismVersion) newer.append(rls); } return newer; } GitHubRelease PrismUpdaterApp::selectRelease() { QList releases; if (m_allowDowngrade) { releases = nonDraftReleases(); } else { releases = newerReleases(); } if (releases.isEmpty()) return {}; SelectReleaseDialog dlg(Version(m_prismVersion), releases); auto result = dlg.exec(); if (result == QDialog::Rejected) { return {}; } GitHubRelease release = dlg.selectedRelease(); return release; } QList PrismUpdaterApp::validReleaseArtifacts(const GitHubRelease& release) { QList valid; qDebug() << "Selecting best asset from" << release.tag_name << "for platform" << BuildConfig.BUILD_ARTIFACT << "portable:" << m_isPortable; if (BuildConfig.BUILD_ARTIFACT.isEmpty()) qWarning() << "Build platform is not set!"; for (auto asset : release.assets) { if (asset.name.endsWith(".zsync")) { qDebug() << "Rejecting zsync file" << asset.name; continue; } if (!m_isAppimage && asset.name.toLower().endsWith("appimage")) { qDebug() << "Rejecting" << asset.name << "because it is an AppImage"; continue; } else if (m_isAppimage && !asset.name.toLower().endsWith("appimage")) { qDebug() << "Rejecting" << asset.name << "because it is not an AppImage"; continue; } auto asset_name = asset.name.toLower(); auto [platform, platform_qt_ver] = StringUtils::splitFirst(BuildConfig.BUILD_ARTIFACT.toLower(), "-qt"); auto system_is_arm = QSysInfo::buildCpuArchitecture().contains("arm64"); auto asset_is_arm = asset_name.contains("arm64"); auto asset_is_archive = asset_name.endsWith(".zip") || asset_name.endsWith(".tar.gz"); bool for_platform = !platform.isEmpty() && asset_name.contains(platform); if (!for_platform) { qDebug() << "Rejecting" << asset.name << "because platforms do not match"; } bool for_portable = asset_name.contains("portable"); if (for_platform && asset_name.contains("legacy") && !platform.contains("legacy")) { qDebug() << "Rejecting" << asset.name << "because platforms do not match"; for_platform = false; } if (for_platform && ((asset_is_arm && !system_is_arm) || (!asset_is_arm && system_is_arm))) { qDebug() << "Rejecting" << asset.name << "because architecture does not match"; for_platform = false; } if (for_platform && platform.contains("windows") && !m_isPortable && asset_is_archive) { qDebug() << "Rejecting" << asset.name << "because it is not an installer"; for_platform = false; } static const QRegularExpression s_qtPattern("-qt(\\d+)"); auto qt_match = s_qtPattern.match(asset_name); if (for_platform && qt_match.hasMatch()) { if (platform_qt_ver.isEmpty() || platform_qt_ver.toInt() != qt_match.captured(1).toInt()) { qDebug() << "Rejecting" << asset.name << "because it is not for the correct qt version" << platform_qt_ver.toInt() << "vs" << qt_match.captured(1).toInt(); for_platform = false; } } if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) { qDebug() << "Accepting" << asset.name; valid.append(asset); } } return valid; } GitHubReleaseAsset PrismUpdaterApp::selectAsset(const QList& assets) { SelectReleaseAssetDialog dlg(assets); auto result = dlg.exec(); if (result == QDialog::Rejected) { return {}; } GitHubReleaseAsset asset = dlg.selectedAsset(); return asset; } void PrismUpdaterApp::performUpdate(const GitHubRelease& release) { m_install_release = release; qDebug() << "Updating to" << release.tag_name; auto valid_assets = validReleaseArtifacts(release); qDebug() << "valid release assets:" << valid_assets; GitHubReleaseAsset selected_asset; if (valid_assets.isEmpty()) { return showFatalErrorMessage( tr("No Valid Release Assets"), tr("Github release %1 has no valid assets for this platform: %2") .arg(release.tag_name) .arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_ARTIFACT).arg(m_isPortable ? tr("yes") : tr("no")))); } else if (valid_assets.length() > 1) { selected_asset = selectAsset(valid_assets); } else { selected_asset = valid_assets.takeFirst(); } if (!selected_asset.isValid()) { return showFatalErrorMessage(tr("No version selected."), tr("No version was selected.")); } qDebug() << "will install" << selected_asset; auto file = downloadAsset(selected_asset); if (!file.exists()) { return showFatalErrorMessage(tr("Failed to Download"), tr("Failed to download the selected asset.")); } performInstall(file); } QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset) { auto temp_dir = QDir::tempPath(); auto file_url = QUrl(asset.browser_download_url); auto out_file_path = FS::PathCombine(temp_dir, file_url.fileName()); qDebug() << "downloading" << file_url << "to" << out_file_path; auto download = Net::Download::makeFile(file_url, out_file_path); download->setNetwork(m_network.get()); auto progress_dialog = ProgressDialog(); progress_dialog.adjustSize(); progress_dialog.execWithTask(download.get()); qDebug() << "download complete"; QFileInfo out_file(out_file_path); return out_file; } bool PrismUpdaterApp::callAppImageUpdate() { auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); QProcess proc = QProcess(); qDebug() << "Calling: AppImageUpdate" << appimage_path; proc.setProgram(FS::PathCombine(m_rootPath, "bin", "AppImageUpdate.AppImage")); proc.setArguments({ appimage_path }); auto result = proc.startDetached(); if (!result) qDebug() << "Failed to start AppImageUpdate reason:" << proc.errorString(); return result; } void PrismUpdaterApp::clearUpdateLog() { FS::deletePath(m_updateLogPath); } void PrismUpdaterApp::logUpdate(const QString& msg) { qDebug() << qUtf8Printable(msg); FS::append(m_updateLogPath, QStringLiteral("%1\n").arg(msg).toUtf8()); } std::tuple read_lock_File(const QString& path) { auto contents = QString(FS::read(path)); auto lines = contents.split('\n'); QDateTime timestamp; QString from, to, target, data_path; for (auto line : lines) { auto index = line.indexOf("="); if (index < 0) continue; auto left = line.left(index); auto right = line.mid(index + 1); if (left.toLower() == "timestamp") { timestamp = QDateTime::fromString(right, Qt::ISODate); } else if (left.toLower() == "from") { from = right; } else if (left.toLower() == "to") { to = right; } else if (left.toLower() == "target") { target = right; } else if (left.toLower() == "data_path") { data_path = right; } } return std::make_tuple(timestamp, from, to, target, data_path); } bool write_lock_file(const QString& path, QDateTime timestamp, QString from, QString to, QString target, QString data_path) { try { FS::write(path, QStringLiteral("TIMESTAMP=%1\nFROM=%2\nTO=%3\nTARGET=%4\nDATA_PATH=%5\n") .arg(timestamp.toString(Qt::ISODate)) .arg(from) .arg(to) .arg(target) .arg(data_path) .toUtf8()); } catch (FS::FileSystemException& err) { qWarning() << "Error writing lockfile:" << err.what() << "\n" << err.cause(); return false; } return true; } void PrismUpdaterApp::performInstall(QFileInfo file) { qDebug() << "starting install"; auto update_lock_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.lock"); QFileInfo update_lock(update_lock_path); if (update_lock.exists()) { auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock_path); auto msg = tr("Update already in progress\n"); auto infoMsg = tr("This installation has a update lock file present at: %1\n" "\n" "Timestamp: %2\n" "Updating from version %3 to %4\n" "Target install path: %5\n" "Data Path: %6" "\n" "This likely means that a previous update attempt failed. Please ensure your installation is in working order before " "proceeding.\n" "Check the Prism Launcher updater log at: \n" "%7\n" "for details on the last update attempt.\n" "\n" "To overwrite this lock and proceed with this update anyway, select \"Ignore\" below.") .arg(update_lock_path) .arg(timestamp.toString(Qt::ISODate), from, to, target, data_path) .arg(m_updateLogPath); QMessageBox msgBox; msgBox.setText(msg); msgBox.setInformativeText(infoMsg); msgBox.setStandardButtons(QMessageBox::Ignore | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Cancel); msgBox.setMinimumWidth(460); msgBox.adjustSize(); switch (msgBox.exec()) { case QMessageBox::AcceptRole: break; case QMessageBox::RejectRole: [[fallthrough]]; default: return showFatalErrorMessage(tr("Update Aborted"), tr("The update attempt was aborted")); } } clearUpdateLog(); auto changelog_path = FS::PathCombine(m_dataPath, ".prism_launcher_update.changelog"); FS::write(changelog_path, m_install_release.body.toUtf8()); logUpdate(tr("Updating from %1 to %2").arg(m_prismVersion).arg(m_install_release.tag_name)); if (m_isPortable || file.fileName().endsWith(".zip") || file.fileName().endsWith(".tar.gz")) { write_lock_file(update_lock_path, QDateTime::currentDateTime(), m_prismVersion, m_install_release.tag_name, m_rootPath, m_dataPath); logUpdate(tr("Updating portable install at %1").arg(m_rootPath)); unpackAndInstall(file); } else { logUpdate(tr("Running installer file at %1").arg(file.absoluteFilePath())); QProcess proc = QProcess(); #if defined Q_OS_WIN auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); #endif proc.setProgram(file.absoluteFilePath()); bool result = proc.startDetached(); logUpdate(tr("Process start result: %1").arg(result ? tr("yes") : tr("no"))); exit(result ? 0 : 1); } } void PrismUpdaterApp::unpackAndInstall(QFileInfo archive) { logUpdate(tr("Backing up install")); backupAppDir(); if (auto loc = unpackArchive(archive)) { auto marker_file_path = loc.value().absoluteFilePath(".prism_launcher_updater_unpack.marker"); FS::write(marker_file_path, m_rootPath.toUtf8()); QProcess proc = QProcess(); auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); #else exe_name.prepend("bin/"); #endif auto new_updater_path = loc.value().absoluteFilePath(exe_name); logUpdate(tr("Starting new updater at '%1'").arg(new_updater_path)); if (!proc.startDetached(new_updater_path, { "-d", m_dataPath }, loc.value().absolutePath())) { logUpdate(tr("Failed to launch '%1' %2").arg(new_updater_path).arg(proc.errorString())); return exit(10); } return exit(); // up to the new updater now } return exit(1); // unpack failure } void PrismUpdaterApp::backupAppDir() { auto manifest_path = FS::PathCombine(m_rootPath, "manifest.txt"); QFileInfo manifest(manifest_path); QStringList file_list; if (manifest.isFile()) { // load manifest from file logUpdate(tr("Reading manifest from %1").arg(manifest.absoluteFilePath())); try { auto contents = QString::fromUtf8(FS::read(manifest.absoluteFilePath())); auto files = contents.split('\n'); for (auto file : files) { file_list.append(file.trimmed()); } } catch (FS::FileSystemException&) { } } if (file_list.isEmpty()) { // best guess if (BuildConfig.BUILD_ARTIFACT.toLower().contains("linux")) { file_list.append({ "PrismLauncher", "bin", "share", "lib" }); } else { // windows by process of elimination file_list.append({ "jars", "prismlauncher.exe", "prismlauncher_filelink.exe", "prismlauncher_updater.exe", "qtlogging.ini", "imageformats", "iconengines", "platforms", "styles", "tls", "qt.conf", "Qt*.dll", }); } logUpdate("manifest.txt empty or missing. making best guess at files to back up."); } logUpdate(tr("Backing up:\n %1").arg(file_list.join(",\n "))); static const QRegularExpression s_replaceRegex("[" + QRegularExpression::escape("\\/:*?\"<>|") + "]"); auto app_dir = QDir(m_rootPath); auto backup_dir = FS::PathCombine(app_dir.absolutePath(), QStringLiteral("backup_") + QString(m_prismVersion).replace(s_replaceRegex, QString("_")) + "-" + m_prismGitCommit); FS::ensureFolderPathExists(backup_dir); auto backup_marker_path = FS::PathCombine(m_dataPath, ".prism_launcher_update_backup_path.txt"); FS::write(backup_marker_path, backup_dir.toUtf8()); QProgressDialog progress(tr("Backing up install at %1").arg(m_rootPath), "", 0, file_list.length()); progress.setCancelButton(nullptr); progress.setMinimumWidth(400); progress.adjustSize(); progress.show(); QCoreApplication::processEvents(); logUpdate(tr("Backing up install at %1").arg(m_rootPath)); auto copy = [this, app_dir, backup_dir](QString to_bak_file) { auto rel_path = app_dir.relativeFilePath(to_bak_file); auto bak_path = FS::PathCombine(backup_dir, rel_path); logUpdate(tr("Backing up and then removing %1").arg(to_bak_file)); FS::ensureFilePathExists(bak_path); auto result = FS::copy(to_bak_file, bak_path).overwrite(true)(); if (!result) { logUpdate(tr("Failed to backup %1 to %2").arg(to_bak_file).arg(bak_path)); } else { if (!FS::deletePath(to_bak_file)) logUpdate(tr("Failed to remove %1").arg(to_bak_file)); } }; int i = 0; for (auto glob : file_list) { QDirIterator iter(app_dir.absolutePath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); progress.setValue(i); QCoreApplication::processEvents(); if (!iter.hasNext() && !glob.isEmpty()) { if (auto file_info = QFileInfo(FS::PathCombine(app_dir.absolutePath(), glob)); file_info.exists()) { copy(file_info.absoluteFilePath()); } else { logUpdate(tr("File doesn't exist, ignoring: %1").arg(FS::PathCombine(app_dir.absolutePath(), glob))); } } else { while (iter.hasNext()) { copy(iter.next()); } } i++; } progress.setValue(i); QCoreApplication::processEvents(); } std::optional PrismUpdaterApp::unpackArchive(QFileInfo archive) { auto temp_extract_path = FS::PathCombine(m_dataPath, "prism_launcher_update_release"); FS::ensureFolderPathExists(temp_extract_path); auto tmp_extract_dir = QDir(temp_extract_path); auto result = MMCZip::extractDir(archive.absoluteFilePath(), tmp_extract_dir.absolutePath()); if (result) { logUpdate(tr("Extracted the following to \"%1\":\n %2").arg(tmp_extract_dir.absolutePath()).arg(result->join("\n "))); } else { logUpdate(tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath())); showFatalErrorMessage("Failed to extract archive", tr("Failed to extract %1 to %2").arg(archive.absoluteFilePath()).arg(tmp_extract_dir.absolutePath())); return std::nullopt; } return tmp_extract_dir; } bool PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path) { QProcess proc = QProcess(); proc.setProcessChannelMode(QProcess::MergedChannels); proc.setReadChannel(QProcess::StandardOutput); proc.start(exe_path, { "--version" }); if (!proc.waitForStarted(5000)) { showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launch child process to read version.")); return false; } // wait 5 seconds to start if (!proc.waitForFinished(5000)) { showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed.")); return false; } auto out = proc.readAllStandardOutput(); auto lines = out.split('\n'); lines.removeAll(""); if (lines.length() < 2) return false; else if (lines.length() > 2) { auto line1 = lines.takeLast(); auto line2 = lines.takeLast(); lines = { line2, line1 }; } auto first = lines.takeFirst(); auto first_parts = first.split(' '); if (first_parts.length() < 2) return false; m_prismBinaryName = first_parts.takeFirst(); auto version = first_parts.takeFirst().trimmed(); m_prismVersion = version; if (version.contains('-')) { auto index = version.indexOf('-'); m_prsimVersionChannel = version.mid(index + 1); version = version.left(index); } else { m_prsimVersionChannel = "stable"; } auto version_parts = version.split('.'); if (version_parts.length() < 2) return false; m_prismVersionMajor = version_parts.takeFirst().toInt(); m_prismVersionMinor = version_parts.takeFirst().toInt(); if (!version_parts.isEmpty()) m_prismVersionPatch = version_parts.takeFirst().toInt(); else m_prismVersionPatch = 0; m_prismGitCommit = lines.takeFirst().simplified(); return true; } void PrismUpdaterApp::loadReleaseList() { auto github_repo = m_prismRepoUrl; if (github_repo.host() != "github.com") return fail("updating from a non github url is not supported"); auto path_parts = github_repo.path().split('/'); path_parts.removeFirst(); // empty segment from leading / auto repo_owner = path_parts.takeFirst(); auto repo_name = path_parts.takeFirst(); auto api_url = QString("https://api.github.com/repos/%1/%2/releases").arg(repo_owner, repo_name); qDebug() << "Fetching release list from" << api_url; downloadReleasePage(api_url, 1); } void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) { int per_page = 30; auto page_url = QString("%1?per_page=%2&page=%3").arg(api_url).arg(QString::number(per_page)).arg(QString::number(page)); auto [download, response] = Net::Download::makeByteArray(page_url); download->setNetwork(m_network.get()); m_current_url = page_url; auto github_api_headers = std::make_unique(); github_api_headers->addHeaders({ { "Accept", "application/vnd.github+json" }, { "X-GitHub-Api-Version", "2022-11-28" }, }); download->addHeaderProxy(std::move(github_api_headers)); connect(download.get(), &Net::Download::succeeded, this, [this, response, per_page, api_url, page]() { int num_found = parseReleasePage(response); if (!(num_found < per_page)) { // there may be more, fetch next page downloadReleasePage(api_url, page + 1); } else { run(); } }); connect(download.get(), &Net::Download::failed, this, &PrismUpdaterApp::downloadError); m_current_task.reset(download); connect(download.get(), &Net::Download::finished, this, [this]() { qDebug() << "Download" << m_current_task->getUid().toString() << "finished"; }); QCoreApplication::processEvents(); QMetaObject::invokeMethod(download.get(), &Task::start, Qt::QueuedConnection); } int PrismUpdaterApp::parseReleasePage(const QByteArray* response) { if (response->isEmpty()) // empty page return 0; int num_releases = 0; try { auto doc = Json::requireDocument(*response); auto release_list = Json::requireArray(doc); for (auto release_json : release_list) { auto release_obj = Json::requireObject(release_json); GitHubRelease release = {}; release.id = Json::requireInteger(release_obj, "id"); release.name = release_obj["name"].toString(); release.tag_name = Json::requireString(release_obj, "tag_name"); release.created_at = QDateTime::fromString(Json::requireString(release_obj, "created_at"), Qt::ISODate); release.published_at = QDateTime::fromString(release_obj["published_at"].toString(), Qt::ISODate); release.draft = Json::requireBoolean(release_obj, "draft"); release.prerelease = Json::requireBoolean(release_obj, "prerelease"); release.body = release_obj["body"].toString(); release.version = Version(release.tag_name); auto release_assets_obj = Json::requireArray(release_obj, "assets"); for (auto asset_json : release_assets_obj) { auto asset_obj = Json::requireObject(asset_json); GitHubReleaseAsset asset = {}; asset.id = Json::requireInteger(asset_obj, "id"); asset.name = Json::requireString(asset_obj, "name"); asset.label = asset_obj["label"].toString(); asset.content_type = Json::requireString(asset_obj, "content_type"); asset.size = Json::requireInteger(asset_obj, "size"); asset.created_at = QDateTime::fromString(Json::requireString(asset_obj, "created_at"), Qt::ISODate); asset.updated_at = QDateTime::fromString(Json::requireString(asset_obj, "updated_at"), Qt::ISODate); asset.browser_download_url = Json::requireString(asset_obj, "browser_download_url"); release.assets.append(asset); } m_releases.append(release); num_releases++; } } catch (Json::JsonException& e) { auto err_msg = QString("Failed to parse releases from github: %1\n%2").arg(e.what()).arg(QString::fromStdString(response->toStdString())); fail(err_msg); } return num_releases; } GitHubRelease PrismUpdaterApp::getLatestRelease() { GitHubRelease latest; for (auto release : m_releases) { if (release.draft) continue; if (release.prerelease && !m_allowPreRelease) continue; if (!latest.isValid() || (release.version > latest.version)) { latest = release; } } return latest; } bool PrismUpdaterApp::needUpdate(const GitHubRelease& release) { auto current_ver = Version(QString("%1.%2.%3").arg(m_prismVersionMajor).arg(m_prismVersionMinor).arg(m_prismVersionPatch)); return current_ver < release.version; } void PrismUpdaterApp::downloadError(QString reason) { fail(QString("Network request Failed: %1 with reason %2").arg(m_current_url).arg(reason)); } PrismLauncher-11.0.3/launcher/updater/prismupdater/UpdaterDialogs.h0000644000175100017510000000442015224505336025027 0ustar runnerrunner// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include #include #include "GitHubRelease.h" #include "Version.h" namespace Ui { class SelectReleaseDialog; } class SelectReleaseDialog : public QDialog { Q_OBJECT public: explicit SelectReleaseDialog(const Version& cur_version, const QList& releases, QWidget* parent = 0); ~SelectReleaseDialog(); void loadReleases(); void appendRelease(GitHubRelease const& release); GitHubRelease selectedRelease() { return m_selectedRelease; } private slots: GitHubRelease getRelease(QTreeWidgetItem* item); void selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous); protected: QList m_releases; GitHubRelease m_selectedRelease; Version m_currentVersion; Ui::SelectReleaseDialog* ui; }; class SelectReleaseAssetDialog : public QDialog { Q_OBJECT public: explicit SelectReleaseAssetDialog(const QList& assets, QWidget* parent = 0); ~SelectReleaseAssetDialog(); void loadAssets(); void appendAsset(GitHubReleaseAsset const& asset); GitHubReleaseAsset selectedAsset() { return m_selectedAsset; } private slots: GitHubReleaseAsset getAsset(QTreeWidgetItem* item); void selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous); protected: QList m_assets; GitHubReleaseAsset m_selectedAsset; Ui::SelectReleaseDialog* ui; }; PrismLauncher-11.0.3/launcher/updater/prismupdater/updater_main.cpp0000644000175100017510000000273015224505336025125 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "PrismUpdater.h" #if defined Q_OS_WIN32 #include "console/WindowsConsole.h" #endif int main(int argc, char* argv[]) { #if defined Q_OS_WIN32 // attach the parent console if stdout not already captured console::WindowsConsoleGuard _consoleGuard; #endif PrismUpdaterApp wUpApp(argc, argv); switch (wUpApp.status()) { case PrismUpdaterApp::Starting: case PrismUpdaterApp::Initialized: { return wUpApp.exec(); } case PrismUpdaterApp::Failed: return 1; case PrismUpdaterApp::Succeeded: return 0; default: return -1; } } PrismLauncher-11.0.3/launcher/updater/prismupdater/updater.exe.manifest0000644000175100017510000000173615224505336025732 0ustar runnerrunner PrismLauncher-11.0.3/launcher/updater/prismupdater/UpdaterDialogs.cpp0000644000175100017510000001362115224505336025365 0ustar runnerrunner// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "UpdaterDialogs.h" #include "ui_SelectReleaseDialog.h" #include #include #include "Markdown.h" #include "StringUtils.h" SelectReleaseDialog::SelectReleaseDialog(const Version& current_version, const QList& releases, QWidget* parent) : QDialog(parent), m_releases(releases), m_currentVersion(current_version), ui(new Ui::SelectReleaseDialog) { ui->setupUi(this); ui->changelogTextBrowser->setOpenExternalLinks(true); ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); ui->versionsTree->setColumnCount(2); ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); ui->versionsTree->setHeaderLabels({ tr("Version"), tr("Published Date") }); ui->versionsTree->header()->setStretchLastSection(false); ui->eplainLabel->setText(tr("Select a version to install.\n" "\n" "Currently installed version: %1") .arg(m_currentVersion.toString())); loadReleases(); connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseDialog::selectionChanged); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseDialog::accept); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &SelectReleaseDialog::reject); ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK")); } SelectReleaseDialog::~SelectReleaseDialog() { delete ui; } void SelectReleaseDialog::loadReleases() { for (auto rls : m_releases) { appendRelease(rls); } } void SelectReleaseDialog::appendRelease(GitHubRelease const& release) { auto rls_item = new QTreeWidgetItem(ui->versionsTree); rls_item->setText(0, release.tag_name); rls_item->setExpanded(true); rls_item->setText(1, release.published_at.toString()); rls_item->setData(0, Qt::UserRole, QVariant(release.id)); ui->versionsTree->addTopLevelItem(rls_item); } GitHubRelease SelectReleaseDialog::getRelease(QTreeWidgetItem* item) { int id = item->data(0, Qt::UserRole).toInt(); GitHubRelease release; for (auto rls : m_releases) { if (rls.id == id) release = rls; } return release; } void SelectReleaseDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* /*previous*/) { GitHubRelease release = getRelease(current); QString body = markdownToHTML(release.body.toUtf8()); m_selectedRelease = release; ui->changelogTextBrowser->setHtml(StringUtils::htmlListPatch(body)); } SelectReleaseAssetDialog::SelectReleaseAssetDialog(const QList& assets, QWidget* parent) : QDialog(parent), m_assets(assets), ui(new Ui::SelectReleaseDialog) { ui->setupUi(this); ui->changelogTextBrowser->setOpenExternalLinks(true); ui->changelogTextBrowser->setLineWrapMode(QTextBrowser::LineWrapMode::WidgetWidth); ui->changelogTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); ui->versionsTree->setColumnCount(2); ui->versionsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); ui->versionsTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); ui->versionsTree->setHeaderLabels({ tr("Version"), tr("Published Date") }); ui->versionsTree->header()->setStretchLastSection(false); ui->eplainLabel->setText(tr("Select a version to install.")); ui->changelogTextBrowser->setHidden(true); loadAssets(); connect(ui->versionsTree, &QTreeWidget::currentItemChanged, this, &SelectReleaseAssetDialog::selectionChanged); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SelectReleaseAssetDialog::accept); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &SelectReleaseAssetDialog::reject); } SelectReleaseAssetDialog::~SelectReleaseAssetDialog() { delete ui; } void SelectReleaseAssetDialog::loadAssets() { for (auto rls : m_assets) { appendAsset(rls); } } void SelectReleaseAssetDialog::appendAsset(GitHubReleaseAsset const& asset) { auto rls_item = new QTreeWidgetItem(ui->versionsTree); rls_item->setText(0, asset.name); rls_item->setExpanded(true); rls_item->setText(1, asset.updated_at.toString()); rls_item->setData(0, Qt::UserRole, QVariant(asset.id)); ui->versionsTree->addTopLevelItem(rls_item); } GitHubReleaseAsset SelectReleaseAssetDialog::getAsset(QTreeWidgetItem* item) { int id = item->data(0, Qt::UserRole).toInt(); GitHubReleaseAsset selected_asset; for (auto asset : m_assets) { if (asset.id == id) selected_asset = asset; } return selected_asset; } void SelectReleaseAssetDialog::selectionChanged(QTreeWidgetItem* current, QTreeWidgetItem* /*previous*/) { GitHubReleaseAsset asset = getAsset(current); m_selectedAsset = asset; } PrismLauncher-11.0.3/launcher/updater/prismupdater/GitHubRelease.cpp0000644000175100017510000000575515224505336025152 0ustar runnerrunner// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "GitHubRelease.h" QDebug operator<<(QDebug debug, const GitHubReleaseAsset& asset) { QDebugStateSaver saver(debug); debug.nospace() << "GitHubReleaseAsset( " "id: " << asset.id << ", " "name " << asset.name << ", " "label: " << asset.label << ", " "content_type: " << asset.content_type << ", " "size: " << asset.size << ", " "created_at: " << asset.created_at << ", " "updated_at: " << asset.updated_at << ", " "browser_download_url: " << asset.browser_download_url << " " ")"; return debug; } QDebug operator<<(QDebug debug, const GitHubRelease& rls) { QDebugStateSaver saver(debug); debug.nospace() << "GitHubRelease( " "id: " << rls.id << ", " "name " << rls.name << ", " "tag_name: " << rls.tag_name << ", " "created_at: " << rls.created_at << ", " "published_at: " << rls.published_at << ", " "prerelease: " << rls.prerelease << ", " "draft: " << rls.draft << ", " "version" << rls.version << ", " "body: " << rls.body << ", " "assets: " << rls.assets << " " ")"; return debug; } PrismLauncher-11.0.3/launcher/updater/prismupdater/SelectReleaseDialog.ui0000644000175100017510000000412415224505336026147 0ustar runnerrunner SelectReleaseDialog 0 0 468 385 Select Release to Install true Please select the release you wish to update to. true 1 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() SelectReleaseDialog accept() 248 254 157 274 buttonBox rejected() SelectReleaseDialog reject() 316 260 286 274 PrismLauncher-11.0.3/launcher/updater/PrismExternalUpdater.h0000644000175100017510000000604015224505336023523 0ustar runnerrunner// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include "ExternalUpdater.h" /*! * An implementation for the updater on Windows and linux that uses out external updater. */ class PrismExternalUpdater : public ExternalUpdater { Q_OBJECT public: PrismExternalUpdater(QWidget* parent, const QString& appDir, const QString& dataDir); ~PrismExternalUpdater() override; /*! * Check for updates manually, showing the user a progress bar and an alert if no updates are found. */ void checkForUpdates() override; void checkForUpdates(bool triggeredByUser) const; /*! * Indicates whether or not to check for updates automatically. */ bool getAutomaticallyChecksForUpdates() override; /*! * Indicates the current automatic update check interval in seconds. */ double getUpdateCheckInterval() override; /*! * Indicates whether or not beta updates should be checked for in addition to regular releases. */ bool getBetaAllowed() override; /*! * Set whether or not to check for updates automatically. * * The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow * reverting this property without kicking off a schedule change immediately. */ void setAutomaticallyChecksForUpdates(bool check) override; /*! * Set the current automatic update check interval in seconds. * * The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow * reverting this property without kicking off a schedule change immediately. */ void setUpdateCheckInterval(double seconds) override; /*! * Set whether or not beta updates should be checked for in addition to regular releases. */ void setBetaAllowed(bool allowed) override; void resetAutoCheckTimer() const; void disconnectTimer(); void connectTimer(); void offerUpdate(const QString& versionName, const QString& versionTag, const QString& releaseNotes, bool triggeredByUser) const; void performUpdate(const QString& versionTag) const; public slots: void autoCheckTimerFired() const; private: class Private; Private* priv; }; PrismLauncher-11.0.3/launcher/updater/ExternalUpdater.h0000644000175100017510000000602515224505336022513 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Kenneth Chew * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LAUNCHER_EXTERNALUPDATER_H #define LAUNCHER_EXTERNALUPDATER_H #include /*! * A base class for an updater that uses an external library. * This class contains basic functions to control the updater. * * To implement the updater on a new platform, create a new class that inherits from this class and * implement the pure virtual functions. * * The initializer of the new class should have the side effect of starting the automatic updater. That is, * once the class is initialized, the program should automatically check for updates if necessary. */ class ExternalUpdater : public QObject { Q_OBJECT public: /*! * Check for updates manually, showing the user a progress bar and an alert if no updates are found. */ virtual void checkForUpdates() = 0; /*! * Indicates whether or not to check for updates automatically. */ virtual bool getAutomaticallyChecksForUpdates() = 0; /*! * Indicates the current automatic update check interval in seconds. */ virtual double getUpdateCheckInterval() = 0; /*! * Indicates whether or not beta updates should be checked for in addition to regular releases. */ virtual bool getBetaAllowed() = 0; /*! * Set whether or not to check for updates automatically. */ virtual void setAutomaticallyChecksForUpdates(bool check) = 0; /*! * Set the current automatic update check interval in seconds. */ virtual void setUpdateCheckInterval(double seconds) = 0; /*! * Set whether or not beta updates should be checked for in addition to regular releases. */ virtual void setBetaAllowed(bool allowed) = 0; signals: /*! * Emits whenever the user's ability to check for updates changes. * * As per Sparkle documentation, "An update check can be made by the user when an update session isn’t in progress, * or when an update or its progress is being shown to the user. A user cannot check for updates when data (such * as the feed or an update) is still being downloaded automatically in the background. * * This property is suitable to use for menu item validation for seeing if checkForUpdates can be invoked." */ void canCheckForUpdatesChanged(bool canCheck); }; #endif // LAUNCHER_EXTERNALUPDATER_H PrismLauncher-11.0.3/launcher/updater/PrismExternalUpdater.cpp0000644000175100017510000003140215224505336024056 0ustar runnerrunner// SPDX-FileCopyrightText: 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "PrismExternalUpdater.h" #include #include #include #include #include #include #include #include #include #include "StringUtils.h" #include "BuildConfig.h" #include "ui/dialogs/UpdateAvailableDialog.h" class PrismExternalUpdater::Private { public: QDir appDir; QDir dataDir; QTimer updateTimer; bool allowBeta{}; bool autoCheck{}; double updateInterval{}; QDateTime lastCheck; std::unique_ptr settings; QWidget* parent{}; }; PrismExternalUpdater::PrismExternalUpdater(QWidget* parent, const QString& appDir, const QString& dataDir) : priv(new PrismExternalUpdater::Private()) { priv->appDir = QDir(appDir); priv->dataDir = QDir(dataDir); auto settingsFile = priv->dataDir.absoluteFilePath("prismlauncher_update.cfg"); priv->settings = std::make_unique(settingsFile, QSettings::Format::IniFormat); priv->allowBeta = priv->settings->value("allow_beta", false).toBool(); priv->autoCheck = priv->settings->value("auto_check", true).toBool(); bool intervalOk = false; // default once per day priv->updateInterval = priv->settings->value("update_interval", 86400).toInt(&intervalOk); if (!intervalOk) { priv->updateInterval = 86400; } if (const auto lastCheck = priv->settings->value("last_check"); !lastCheck.isNull() && lastCheck.isValid()) { priv->lastCheck = QDateTime::fromString(lastCheck.toString(), Qt::ISODate); } priv->parent = parent; connectTimer(); resetAutoCheckTimer(); if (priv->updateInterval == 0) { // "On Launch" checkForUpdates(false); } } PrismExternalUpdater::~PrismExternalUpdater() { if (priv->updateTimer.isActive()) { priv->updateTimer.stop(); } disconnectTimer(); priv->settings->sync(); delete priv; } void PrismExternalUpdater::checkForUpdates() { checkForUpdates(true); } void PrismExternalUpdater::checkForUpdates(bool triggeredByUser) const { QProgressDialog progress(tr("Checking for updates..."), "", 0, 0, priv->parent); progress.setMinimumDuration(0); // Appear immediately without waiting progress.setCancelButton(nullptr); progress.adjustSize(); if (triggeredByUser) { progress.show(); } QCoreApplication::processEvents(); QProcess proc; auto exeName = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #ifdef Q_OS_WIN32 exeName.append(".exe"); auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); #else exeName = QString("bin/%1").arg(exeName); #endif QStringList args = { "--check-only", "--dir", priv->dataDir.absolutePath(), "--debug" }; if (priv->allowBeta) { args.append("--pre-release"); } proc.start(priv->appDir.absoluteFilePath(exeName), args); if (auto resultStart = proc.waitForStarted(5000); !resultStart) { auto err = proc.error(); qDebug() << "Failed to start updater after 5 seconds." << "reason:" << err << proc.errorString(); auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent); msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); priv->lastCheck = QDateTime::currentDateTime(); priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate)); priv->settings->sync(); resetAutoCheckTimer(); return; } QCoreApplication::processEvents(); if (auto resultFinished = proc.waitForFinished(60000); !resultFinished) { proc.kill(); auto err = proc.error(); auto output = proc.readAll(); qDebug() << "Updater failed to close after 60 seconds." << "reason:" << err << proc.errorString(); auto msgBox = QMessageBox(QMessageBox::Information, tr("Update Check Failed"), tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent); msgBox.setDetailedText(output); msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); priv->lastCheck = QDateTime::currentDateTime(); priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate)); priv->settings->sync(); resetAutoCheckTimer(); return; } auto exitCode = proc.exitCode(); auto stdOutput = proc.readAllStandardOutput(); auto stdError = proc.readAllStandardError(); progress.cancel(); QCoreApplication::processEvents(); switch (exitCode) { case 0: // no update available if (triggeredByUser) { qDebug() << "No update available"; auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("You are running the latest version."), QMessageBox::Ok, priv->parent); msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); } break; case 1: // there was an error { qDebug() << "Updater subprocess error" << qPrintable(stdError); auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Check Error"), tr("There was an error running the update check."), QMessageBox::Ok, priv->parent); msgBox.setDetailedText(QString(stdError)); msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); } break; case 100: // update available { auto [firstLine, remainder1] = StringUtils::splitFirst(stdOutput, '\n'); auto [secondLine, remainder2] = StringUtils::splitFirst(remainder1, '\n'); auto [thirdLine, releaseNotes] = StringUtils::splitFirst(remainder2, '\n'); auto versionName = StringUtils::splitFirst(firstLine, ": ").second.trimmed(); auto versionTag = StringUtils::splitFirst(secondLine, ": ").second.trimmed(); auto releaseTimestamp = QDateTime::fromString(StringUtils::splitFirst(thirdLine, ": ").second.trimmed(), Qt::ISODate); qDebug() << "Update available:" << versionName << versionTag << releaseTimestamp; qDebug() << "Update release notes:" << releaseNotes; offerUpdate(versionName, versionTag, releaseNotes, triggeredByUser); } break; default: // unknown error code { qDebug() << "Updater exited with unknown code" << exitCode; auto msgBox = QMessageBox(QMessageBox::Information, tr("Unknown Update Error"), tr("The updater exited with an unknown condition.\nExit Code: %1").arg(QString::number(exitCode)), QMessageBox::Ok, priv->parent); auto detailTxt = tr("StdOut: %1\nStdErr: %2").arg(QString(stdOutput)).arg(QString(stdError)); msgBox.setDetailedText(detailTxt); msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); } } priv->lastCheck = QDateTime::currentDateTime(); priv->settings->setValue("last_check", priv->lastCheck.toString(Qt::ISODate)); priv->settings->sync(); resetAutoCheckTimer(); } bool PrismExternalUpdater::getAutomaticallyChecksForUpdates() { return priv->autoCheck; } double PrismExternalUpdater::getUpdateCheckInterval() { return priv->updateInterval; } bool PrismExternalUpdater::getBetaAllowed() { return priv->allowBeta; } void PrismExternalUpdater::setAutomaticallyChecksForUpdates(bool check) { priv->autoCheck = check; priv->settings->setValue("auto_check", check); priv->settings->sync(); resetAutoCheckTimer(); } void PrismExternalUpdater::setUpdateCheckInterval(double seconds) { priv->updateInterval = seconds; priv->settings->setValue("update_interval", seconds); priv->settings->sync(); resetAutoCheckTimer(); } void PrismExternalUpdater::setBetaAllowed(bool allowed) { priv->allowBeta = allowed; priv->settings->setValue("auto_beta", allowed); priv->settings->sync(); } void PrismExternalUpdater::resetAutoCheckTimer() const { if (priv->autoCheck && priv->updateInterval > 0) { auto now = QDateTime::currentDateTime(); qint64 timeoutMs = 0; if (priv->lastCheck.isValid()) { const qint64 diff = priv->lastCheck.secsTo(now); const qint64 secsLeft = std::max(priv->updateInterval - diff, 0); timeoutMs = secsLeft * 1000; } timeoutMs = std::min(timeoutMs, static_cast(INT_MAX)); qDebug() << "Auto update timer starting," << timeoutMs / 1000 << "seconds left"; priv->updateTimer.start(static_cast(timeoutMs)); } else { if (priv->updateTimer.isActive()) { priv->updateTimer.stop(); } } } void PrismExternalUpdater::connectTimer() { connect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); } void PrismExternalUpdater::disconnectTimer() { disconnect(&priv->updateTimer, &QTimer::timeout, this, &PrismExternalUpdater::autoCheckTimerFired); } void PrismExternalUpdater::autoCheckTimerFired() const { qDebug() << "Auto update Timer fired"; checkForUpdates(false); } void PrismExternalUpdater::offerUpdate(const QString& versionName, const QString& versionTag, const QString& releaseNotes, const bool triggeredByUser) const { priv->settings->beginGroup("skip"); auto shouldSkip = !triggeredByUser && priv->settings->value(versionTag, false).toBool(); priv->settings->endGroup(); if (shouldSkip) { if (triggeredByUser) { auto msgBox = QMessageBox(QMessageBox::Information, tr("No Update Available"), tr("There are no new updates available."), QMessageBox::Ok, priv->parent); msgBox.setMinimumWidth(460); msgBox.adjustSize(); msgBox.exec(); } return; } UpdateAvailableDialog dlg(BuildConfig.printableVersionString(), versionName, releaseNotes); auto result = dlg.exec(); qDebug() << "offer dlg result" << result; priv->settings->beginGroup("skip"); if (result == UpdateAvailableDialog::Skip) { priv->settings->setValue(versionTag, true); } else { if (result == UpdateAvailableDialog::Install) { performUpdate(versionTag); } priv->settings->remove(versionTag); } priv->settings->endGroup(); priv->settings->sync(); } void PrismExternalUpdater::performUpdate(const QString& versionTag) const { QProcess proc; auto exeName = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #ifdef Q_OS_WIN32 exeName.append(".exe"); auto env = QProcessEnvironment::systemEnvironment(); env.insert("__COMPAT_LAYER", "RUNASINVOKER"); proc.setProcessEnvironment(env); #else exeName = QString("bin/%1").arg(exeName); #endif QStringList args = { "--dir", priv->dataDir.absolutePath(), "--install-version", versionTag }; if (priv->allowBeta) { args.append("--pre-release"); } proc.setProgram(priv->appDir.absoluteFilePath(exeName)); proc.setArguments(args); auto result = proc.startDetached(); if (!result) { qDebug() << "Failed to start updater:" << proc.error() << proc.errorString(); } QCoreApplication::exit(); } PrismLauncher-11.0.3/launcher/updater/MacSparkleUpdater.h0000644000175100017510000001061015224505336022746 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Kenneth Chew * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LAUNCHER_MACSPARKLEUPDATER_H #define LAUNCHER_MACSPARKLEUPDATER_H #include #include #include "ExternalUpdater.h" /*! * An implementation for the updater on macOS that uses the Sparkle framework. */ class MacSparkleUpdater : public ExternalUpdater { Q_OBJECT public: /*! * Start the Sparkle updater, which automatically checks for updates if necessary. */ MacSparkleUpdater(); ~MacSparkleUpdater() override; /*! * Check for updates manually, showing the user a progress bar and an alert if no updates are found. */ void checkForUpdates() override; /*! * Indicates whether or not to check for updates automatically. */ bool getAutomaticallyChecksForUpdates() override; /*! * Indicates the current automatic update check interval in seconds. */ double getUpdateCheckInterval() override; /*! * Indicates the set of Sparkle channels the updater is allowed to find new updates from. */ QSet getAllowedChannels(); /*! * Indicates whether or not beta updates should be checked for in addition to regular releases. */ bool getBetaAllowed() override; /*! * Set whether or not to check for updates automatically. * * As per Sparkle documentation, "By default, Sparkle asks users on second launch for permission if they want * automatic update checks enabled and sets this property based on their response. If SUEnableAutomaticChecks is * set in the Info.plist, this permission request is not performed however. * * Setting this property will persist in the host bundle’s user defaults. Only set this property if you need * dynamic behavior (e.g. user preferences). * * The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow * reverting this property without kicking off a schedule change immediately." */ void setAutomaticallyChecksForUpdates(bool check) override; /*! * Set the current automatic update check interval in seconds. * * As per Sparkle documentation, "Setting this property will persist in the host bundle’s user defaults. For this * reason, only set this property if you need dynamic behavior (eg user preferences). Otherwise prefer to set * SUScheduledCheckInterval directly in your Info.plist. * * The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow * reverting this property without kicking off a schedule change immediately." */ void setUpdateCheckInterval(double seconds) override; /*! * Clears all allowed Sparkle channels, returning to the default updater channel behavior. */ void clearAllowedChannels(); /*! * Set a single Sparkle channel the updater is allowed to find new updates from. * * Items in the default channel can always be found, regardless of this setting. If an empty string is passed, * return to the default behavior. */ void setAllowedChannel(const QString& channel); /*! * Set a set of Sparkle channels the updater is allowed to find new updates from. * * Items in the default channel can always be found, regardless of this setting. If an empty set is passed, * return to the default behavior. */ void setAllowedChannels(const QSet& channels); /*! * Set whether or not beta updates should be checked for in addition to regular releases. */ void setBetaAllowed(bool allowed) override; private: class Private; Private* priv; }; #endif // LAUNCHER_MACSPARKLEUPDATER_H PrismLauncher-11.0.3/launcher/updater/MacSparkleUpdater.mm0000644000175100017510000001340415224505336023134 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Kenneth Chew * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "MacSparkleUpdater.h" #include "Application.h" #include #include @interface UpdaterObserver : NSObject @property(nonatomic, readonly) SPUUpdater* updater; /// A callback to run when the state of `canCheckForUpdates` for the `updater` changes. @property(nonatomic, copy) void (^callback)(bool); - (id)initWithUpdater:(SPUUpdater*)updater; @end @implementation UpdaterObserver - (id)initWithUpdater:(SPUUpdater*)updater { self = [super init]; _updater = updater; [self addObserver:self forKeyPath:@"updater.canCheckForUpdates" options:NSKeyValueObservingOptionNew context:nil]; return self; } - (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context { if ([keyPath isEqualToString:@"updater.canCheckForUpdates"]) { bool canCheck = [change[NSKeyValueChangeNewKey] boolValue]; self.callback(canCheck); } } @end @interface UpdaterDelegate : NSObject @property(nonatomic, copy) NSSet* allowedChannels; @end @implementation UpdaterDelegate - (NSSet*)allowedChannelsForUpdater:(SPUUpdater*)updater { return _allowedChannels; } @end class MacSparkleUpdater::Private { public: SPUStandardUpdaterController* updaterController; UpdaterObserver* updaterObserver; UpdaterDelegate* updaterDelegate; NSAutoreleasePool* autoReleasePool; }; MacSparkleUpdater::MacSparkleUpdater() { priv = new MacSparkleUpdater::Private(); // Enable Cocoa's memory management. NSApplicationLoad(); priv->autoReleasePool = [[NSAutoreleasePool alloc] init]; // Delegate is used for setting/getting allowed update channels. priv->updaterDelegate = [[UpdaterDelegate alloc] init]; // Controller is the interface for actually doing the updates. priv->updaterController = [[SPUStandardUpdaterController alloc] initWithStartingUpdater:true updaterDelegate:priv->updaterDelegate userDriverDelegate:nil]; priv->updaterObserver = [[UpdaterObserver alloc] initWithUpdater:priv->updaterController.updater]; // Use KVO to run a callback that emits a Qt signal when `canCheckForUpdates` changes, so the UI can respond accordingly. priv->updaterObserver.callback = ^(bool canCheck) { emit canCheckForUpdatesChanged(canCheck); }; } MacSparkleUpdater::~MacSparkleUpdater() { [priv->updaterObserver removeObserver:priv->updaterObserver forKeyPath:@"updater.canCheckForUpdates"]; [priv->updaterController release]; [priv->updaterObserver release]; [priv->updaterDelegate release]; [priv->autoReleasePool release]; delete priv; } void MacSparkleUpdater::checkForUpdates() { [priv->updaterController checkForUpdates:nil]; } bool MacSparkleUpdater::getAutomaticallyChecksForUpdates() { return priv->updaterController.updater.automaticallyChecksForUpdates; } double MacSparkleUpdater::getUpdateCheckInterval() { return priv->updaterController.updater.updateCheckInterval; } QSet MacSparkleUpdater::getAllowedChannels() { // Convert NSSet -> QSet __block QSet channels; [priv->updaterDelegate.allowedChannels enumerateObjectsUsingBlock:^(NSString* channel, BOOL* stop) { channels.insert(QString::fromNSString(channel)); }]; return channels; } bool MacSparkleUpdater::getBetaAllowed() { return getAllowedChannels().contains("beta"); } void MacSparkleUpdater::setAutomaticallyChecksForUpdates(bool check) { priv->updaterController.updater.automaticallyChecksForUpdates = check ? YES : NO; // make clang-tidy happy } void MacSparkleUpdater::setUpdateCheckInterval(double seconds) { priv->updaterController.updater.updateCheckInterval = seconds; } void MacSparkleUpdater::clearAllowedChannels() { priv->updaterDelegate.allowedChannels = [NSSet set]; } void MacSparkleUpdater::setAllowedChannel(const QString& channel) { if (channel.isEmpty()) { clearAllowedChannels(); return; } NSSet* nsChannels = [NSSet setWithObject:channel.toNSString()]; priv->updaterDelegate.allowedChannels = nsChannels; } void MacSparkleUpdater::setAllowedChannels(const QSet& channels) { if (channels.isEmpty()) { clearAllowedChannels(); return; } QString channelsConfig = ""; // Convert QSet -> NSSet NSMutableSet* nsChannels = [NSMutableSet setWithCapacity:channels.count()]; for (const QString& channel : channels) { [nsChannels addObject:channel.toNSString()]; channelsConfig += channel + " "; } priv->updaterDelegate.allowedChannels = nsChannels; } void MacSparkleUpdater::setBetaAllowed(bool allowed) { if (allowed) { setAllowedChannel("beta"); } else { clearAllowedChannels(); } } PrismLauncher-11.0.3/launcher/Commandline.cpp0000644000175100017510000000522315224505336020520 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Authors: Orochimarufan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Commandline.h" /** * @file libutil/src/cmdutils.cpp */ namespace Commandline { // commandline splitter QStringList splitArgs(QString args) { QStringList argv; QString current; bool escape = false; QChar inquotes; for (int i = 0; i < args.length(); i++) { QChar cchar = args.at(i); // \ escaped if (escape) { current += cchar; escape = false; // in "quotes" } else if (!inquotes.isNull()) { if (cchar == '\\') escape = true; else if (cchar == inquotes) inquotes = QChar::Null; else current += cchar; // otherwise } else { if (cchar == ' ') { if (!current.isEmpty()) { argv << current; current.clear(); } } else if (cchar == '"' || cchar == '\'') inquotes = cchar; else current += cchar; } } if (!current.isEmpty()) argv << current; return argv; } } // namespace Commandline PrismLauncher-11.0.3/launcher/InstanceCopyTask.h0000644000175100017510000000170115224505336021156 0ustar runnerrunner#pragma once #include #include #include #include "BaseInstance.h" #include "BaseVersion.h" #include "Filter.h" #include "InstanceCopyPrefs.h" #include "InstanceTask.h" #include "net/NetJob.h" #include "settings/SettingsObject.h" #include "tasks/Task.h" class InstanceCopyTask : public InstanceTask { Q_OBJECT public: explicit InstanceCopyTask(BaseInstance* origInstance, const InstanceCopyPrefs& prefs); protected: //! Entry point for tasks. virtual void executeTask() override; bool abort() override; void copyFinished(); void copyAborted(); private: /* data */ BaseInstance* m_origInstance; QFuture m_copyFuture; QFutureWatcher m_copyFutureWatcher; Filter m_matcher; bool m_keepPlaytime; bool m_useLinks = false; bool m_useHardLinks = false; bool m_copySaves = false; bool m_linkRecursively = false; bool m_useClone = false; }; PrismLauncher-11.0.3/launcher/Application.h0000644000175100017510000002227515224505336020210 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Tayou * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include "QObjectPtr.h" #include "minecraft/auth/MinecraftAccount.h" class LaunchController; class LocalPeer; class InstanceWindow; class MainWindow; class ViewLogWindow; class SetupWizard; class GenericPageProvider; class QFile; class HttpMetaCache; class SettingsObject; class InstanceList; class AccountList; class IconList; class QNetworkAccessManager; class JavaInstallList; class ExternalUpdater; class BaseProfilerFactory; class BaseDetachedToolFactory; class TranslationsModel; class ITheme; class MCEditTool; class ThemeManager; class IconTheme; class BaseInstance; class LogModel; struct MinecraftTarget; class MinecraftAccount; namespace Meta { class Index; } #if defined(APPLICATION) #undef APPLICATION #endif #define APPLICATION (static_cast(QCoreApplication::instance())) // Used for checking if is a test #if defined(APPLICATION_DYN) #undef APPLICATION_DYN #endif #define APPLICATION_DYN (dynamic_cast(QCoreApplication::instance())) class Application : public QApplication { Q_OBJECT public: enum Status { StartingUp, Failed, Succeeded, Initialized }; enum Capability { None = 0, SupportsMSA = 1 << 0, SupportsFlame = 1 << 1, SupportsGameMode = 1 << 2, SupportsMangoHud = 1 << 3, }; Q_DECLARE_FLAGS(Capabilities, Capability) public: Application(int& argc, char** argv); virtual ~Application(); bool event(QEvent* event) override; SettingsObject* settings() const { return m_settings.get(); } qint64 timeSinceStart() const { return m_startTime.msecsTo(QDateTime::currentDateTime()); } QIcon logo(); ThemeManager* themeManager() { return m_themeManager.get(); } ExternalUpdater* updater() { return m_updater.get(); } void triggerUpdateCheck(); TranslationsModel* translations(); JavaInstallList* javalist(); InstanceList* instances() const { return m_instances.get(); } IconList* icons() const { return m_icons.get(); } MCEditTool* mcedit() const { return m_mcedit.get(); } AccountList* accounts() const { return m_accounts.get(); } Status status() const { return m_status; } const QMap>& profilers() const { return m_profilers; } void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password); QNetworkAccessManager* network(); HttpMetaCache* metacache(); Meta::Index* metadataIndex(); void updateCapabilities(); void detectLibraries(); /*! * Finds and returns the full path to a jar file. * Returns a null-string if it could not be found. */ QString getJarPath(QString jarFile); QString getMSAClientID(); QString getFlameAPIKey(); QString getModrinthAPIToken(); QString getUserAgent(); /// this is the root of the 'installation'. Used for automatic updates const QString& root() { return m_rootPath; } /// the data path the application is using const QString& dataRoot() { return m_dataPath; } /// the java installed path the application is using const QString javaPath(); bool isPortable() { return m_portable; } const Capabilities capabilities() { return m_capabilities; } /*! * Opens a json file using either a system default editor, or, if not empty, the editor * specified in the settings */ bool openJsonEditor(const QString& filename); InstanceWindow* showInstanceWindow(BaseInstance* instance, QString page = QString()); MainWindow* showMainWindow(bool minimized = false); ViewLogWindow* showLogWindow(); void updateIsRunning(bool running); bool updatesAreAllowed(); void ShowGlobalSettings(class QWidget* parent, QString open_page = QString()); bool updaterEnabled(); QString updaterBinaryName(); QUrl normalizeImportUrl(const QString& url); signals: void updateAllowedChanged(bool status); void globalSettingsAboutToOpen(); void globalSettingsApplied(); int currentCatChanged(int index); void oauthReplyRecieved(QVariantMap); #ifdef Q_OS_MACOS void clickedOnDock(); #endif public slots: bool launch(BaseInstance* instance, LaunchMode mode = LaunchMode::Normal, std::shared_ptr targetToJoin = nullptr, shared_qobject_ptr accountToUse = nullptr, const QString& offlineName = QString()); bool kill(BaseInstance* instance); void closeCurrentWindow(); private slots: void on_windowClose(); void messageReceived(const QByteArray& message); void controllerFinished(); void setupWizardFinished(int status); private: bool handleDataMigration(const QString& currentData, const QString& oldData, const QString& name, const QString& configFile) const; bool createSetupWizard(); void performMainStartupAction(); // sets the fatal error message and m_status to Failed. void showFatalErrorMessage(const QString& title, const QString& content); private: void addRunningInstance(); void subRunningInstance(); bool shouldExitNow() const; private: QHash m_qsaveResources; mutable QMutex m_qsaveResourcesMutex; private: QDateTime m_startTime; std::unique_ptr m_network; std::unique_ptr m_updater; std::unique_ptr m_accounts; std::unique_ptr m_metacache; std::unique_ptr m_metadataIndex; std::unique_ptr m_settings; std::unique_ptr m_instances; std::unique_ptr m_icons; std::unique_ptr m_javalist; std::unique_ptr m_translations; std::unique_ptr m_globalSettingsProvider; std::unique_ptr m_mcedit; QSet m_features; std::unique_ptr m_themeManager; QMap> m_profilers; QString m_rootPath; QString m_dataPath; Status m_status = Application::StartingUp; Capabilities m_capabilities; bool m_portable = false; #ifdef Q_OS_MACOS Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive; #endif // FIXME: attach to instances instead. struct InstanceXtras { InstanceWindow* window = nullptr; std::unique_ptr controller; }; std::map m_instanceExtras; mutable QMutex m_instanceExtrasMutex; // main state variables size_t m_openWindows = 0; size_t m_runningInstances = 0; bool m_updateRunning = false; // main window, if any MainWindow* m_mainWindow = nullptr; // log window, if any ViewLogWindow* m_viewLogWindow = nullptr; // peer launcher instance connector - used to implement single instance launcher and signalling LocalPeer* m_peerInstance = nullptr; SetupWizard* m_setupWizard = nullptr; public: QString m_detectedGLFWPath; QString m_detectedOpenALPath; QString m_instanceIdToLaunch; QString m_serverToJoin; QString m_worldToJoin; QString m_profileToUse; bool m_launchOffline = false; QString m_offlineName; bool m_liveCheck = false; QList m_urlsToImport; QString m_instanceIdToShowWindowOf; bool m_showMainWindow = false; std::unique_ptr logFile; std::unique_ptr logModel; public: void addQSavePath(QString); void removeQSavePath(QString); bool checkQSavePath(QString); }; PrismLauncher-11.0.3/launcher/PSaveFile.h0000644000175100017510000000477215224505336017565 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include "Application.h" #if defined(LAUNCHER_APPLICATION) /* PSaveFile * A class that mimics QSaveFile for Windows. * * When reading resources, we need to avoid accessing temporary files * generated by QSaveFile. If we start reading such a file, we may * inadvertently keep it open while QSaveFile is trying to remove it, * or we might detect the file just before it is removed, leading to * race conditions and errors. * * Unfortunately, QSaveFile doesn't provide a way to retrieve the * temporary file name or to set a specific template for the temporary * file name it uses. By default, QSaveFile appends a `.XXXXXX` suffix * to the original file name, where the `XXXXXX` part is dynamically * generated to ensure uniqueness. * * This class acts like a lock by adding and removing the target file * name into/from a global string set, helping to manage access to * files during critical operations. * * Note: Please do not use the `setFileName` function directly, as it * is not virtual and cannot be overridden. */ class PSaveFile : public QSaveFile { public: PSaveFile(const QString& name) : QSaveFile(name) { addPath(name); } PSaveFile(const QString& name, QObject* parent) : QSaveFile(name, parent) { addPath(name); } virtual ~PSaveFile() { if (auto app = APPLICATION_DYN) { app->removeQSavePath(m_absoluteFilePath); } } private: void addPath(const QString& path) { m_absoluteFilePath = QFileInfo(path).absoluteFilePath() + "."; // add dot for tmp files only if (auto app = APPLICATION_DYN) { app->addQSavePath(m_absoluteFilePath); } } QString m_absoluteFilePath; }; #else using PSaveFile = QSaveFile; #endif PrismLauncher-11.0.3/launcher/BaseInstance.h0000644000175100017510000002404015224505336020274 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include "QObjectPtr.h" #include "settings/SettingsObject.h" #include "BaseVersionList.h" #include "MessageLevel.h" #include "minecraft/auth/MinecraftAccount.h" #include "settings/INIFile.h" #include "net/Mode.h" #include "RuntimeContext.h" #include "minecraft/launch/MinecraftTarget.h" class QDir; class Task; class LaunchTask; class BaseInstance; /// Shortcut saving target representations enum class ShortcutTarget { Desktop, Applications, Other }; /// Shortcut data representation struct ShortcutData { QString name; QString filePath; ShortcutTarget target = ShortcutTarget::Other; }; /// Console settings int getConsoleMaxLines(SettingsObject* settings); bool shouldStopOnConsoleOverflow(SettingsObject* settings); /*! * \brief Base class for instances. * This class implements many functions that are common between instances and * provides a standard interface for all instances. * * To create a new instance type, create a new class inheriting from this class * and implement the pure virtual functions. */ class BaseInstance : public QObject { Q_OBJECT protected: /// no-touchy! BaseInstance(SettingsObject* globalSettings, std::unique_ptr settings, const QString& rootDir); public: /* types */ enum class Status { Present, Gone // either nuked or invalidated }; public: /// virtual destructor to make sure the destruction is COMPLETE virtual ~BaseInstance(); virtual void saveNow() = 0; /*** * the instance has been invalidated - it is no longer tracked by the launcher for some reason, * but it has not necessarily been deleted. * * Happens when the instance folder changes to some other location, or the instance is removed by external means. */ void invalidate(); /// The instance's ID. The ID SHALL be determined by LAUNCHER internally. The ID IS guaranteed to /// be unique. virtual QString id() const; void setMinecraftRunning(bool running); void setRunning(bool running); bool isRunning() const; int64_t totalTimePlayed() const; int64_t lastTimePlayed() const; void resetTimePlayed(); /// get the type of this instance QString instanceType() const; /// Path to the instance's root directory. QString instanceRoot() const; /// Path to the instance's game root directory. virtual QString gameRoot() const { return instanceRoot(); } /// Path to the instance's mods directory. virtual QString modsRoot() const = 0; QString name() const; void setName(QString val); /// Sync name and rename instance dir accordingly; returns true if successful bool syncInstanceDirName(const QString& newRoot) const; /// Register a created shortcut void registerShortcut(const ShortcutData& data); QList shortcuts() const; void setShortcuts(const QList& shortcuts); /// Value used for instance window titles QString windowTitle() const; QString iconKey() const; void setIconKey(QString val); QString notes() const; void setNotes(QString val); QString getPreLaunchCommand(); QString getPostExitCommand(); QString getWrapperCommand(); bool isManagedPack() const; QString getManagedPackType() const; QString getManagedPackID() const; QString getManagedPackName() const; QString getManagedPackVersionID() const; QString getManagedPackVersionName() const; void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version); void copyManagedPack(BaseInstance& other); virtual QStringList extraArguments(); /// Traits. Normally inside the version, depends on instance implementation. virtual QSet traits() const = 0; /** * Gets the time that the instance was last launched. * Stored in milliseconds since epoch. */ qint64 lastLaunch() const; /// Sets the last launched time to 'val' milliseconds since epoch void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch()); /*! * \brief Gets this instance's settings object. * This settings object stores instance-specific settings. * * Note that this method is not const. * It may call loadSpecificSettings() to ensure those are loaded. * * \return A pointer to this instance's settings object. */ virtual SettingsObject* settings(); /*! * \brief Loads settings specific to an instance type if they're not already loaded. */ virtual void loadSpecificSettings() = 0; /// returns a valid update task virtual QList createUpdateTask() = 0; /// returns a valid launcher (task container) virtual LaunchTask* createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) = 0; /// returns the current launch task (if any) LaunchTask* getLaunchTask(); /*! * Create envrironment variables for running the instance */ virtual QProcessEnvironment createEnvironment() = 0; virtual QProcessEnvironment createLaunchEnvironment() = 0; /*! * Returns the root folder to use for looking up log files */ virtual QStringList getLogFileSearchPaths() = 0; virtual QString getStatusbarDescription() = 0; /// FIXME: this really should be elsewhere... virtual QString instanceConfigFolder() const = 0; /// get variables this instance exports virtual QMap getVariables() = 0; virtual QString typeName() const = 0; virtual void updateRuntimeContext(); RuntimeContext runtimeContext() const { return m_runtimeContext; } bool hasVersionBroken() const { return m_hasBrokenVersion; } void setVersionBroken(bool value) { if (m_hasBrokenVersion != value) { m_hasBrokenVersion = value; emit propertiesChanged(this); } } bool hasUpdateAvailable() const { return m_hasUpdate; } void setUpdateAvailable(bool value) { if (m_hasUpdate != value) { m_hasUpdate = value; emit propertiesChanged(this); } } bool hasCrashed() const { return m_crashed; } void setCrashed(bool value) { if (m_crashed != value) { m_crashed = value; emit propertiesChanged(this); } } virtual bool canLaunch() const; virtual bool canEdit() const = 0; virtual bool canExport() const = 0; virtual void populateLaunchMenu(QMenu* menu) = 0; bool reloadSettings(); /** * 'print' a verbose description of the instance into a QStringList */ virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) = 0; Status currentStatus() const; QStringList getLinkedInstances() const; void setLinkedInstances(const QStringList& list); void addLinkedInstanceId(const QString& id); bool removeLinkedInstanceId(const QString& id); bool isLinkedToInstanceId(const QString& id) const; bool isLegacy(); protected: void changeStatus(Status newStatus); SettingsObject* globalSettings() const { return m_global_settings; } bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; } void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; } signals: /*! * \brief Signal emitted when properties relevant to the instance view change */ void propertiesChanged(BaseInstance* inst); void launchTaskChanged(LaunchTask*); void runningStatusChanged(bool running); void profilerChanged(); void statusChanged(Status from, Status to); protected slots: void iconUpdated(QString key); protected: /* data */ QString m_rootDir; std::unique_ptr m_settings; // InstanceFlags m_flags; bool m_isRunning = false; std::unique_ptr m_launchProcess; QDateTime m_timeStarted; RuntimeContext m_runtimeContext; private: /* data */ Status m_status = Status::Present; bool m_crashed = false; bool m_hasUpdate = false; bool m_hasBrokenVersion = false; SettingsObject* m_global_settings; bool m_specific_settings_loaded = false; }; Q_DECLARE_METATYPE(shared_qobject_ptr) // Q_DECLARE_METATYPE(BaseInstance::InstanceFlag) // Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags) PrismLauncher-11.0.3/launcher/SeparatorPrefixTree.h0000644000175100017510000001573715224505336021710 0ustar runnerrunner#pragma once #include #include #include template class SeparatorPrefixTree { public: SeparatorPrefixTree(QStringList paths) { insert(paths); } SeparatorPrefixTree(bool contained = false) { m_contained = contained; } void insert(QStringList paths) { for (auto& path : paths) { insert(path); } } /// insert an exact path into the tree SeparatorPrefixTree& insert(QString path) { auto sepIndex = path.indexOf(Tseparator); if (sepIndex == -1) { children[path] = SeparatorPrefixTree(true); return children[path]; } else { auto prefix = path.left(sepIndex); if (!children.contains(prefix)) { children[prefix] = SeparatorPrefixTree(false); } return children[prefix].insert(path.mid(sepIndex + 1)); } } /// is the path fully contained in the tree? bool contains(QString path) const { auto node = find(path); return node != nullptr; } /// does the tree cover a path? That means the prefix of the path is contained in the tree bool covers(QString path) const { // if we found some valid node, it's good enough. the tree covers the path if (m_contained) { return true; } auto sepIndex = path.indexOf(Tseparator); if (sepIndex == -1) { auto found = children.find(path); if (found == children.end()) { return false; } return (*found).covers(QString()); } else { auto prefix = path.left(sepIndex); auto found = children.find(prefix); if (found == children.end()) { return false; } return (*found).covers(path.mid(sepIndex + 1)); } } /// return the contained path that covers the path specified QString cover(QString path) const { // if we found some valid node, it's good enough. the tree covers the path if (m_contained) { return QString(""); } auto sepIndex = path.indexOf(Tseparator); if (sepIndex == -1) { auto found = children.find(path); if (found == children.end()) { return QString(); } auto nested = (*found).cover(QString()); if (nested.isNull()) { return nested; } if (nested.isEmpty()) return path; return path + Tseparator + nested; } else { auto prefix = path.left(sepIndex); auto found = children.find(prefix); if (found == children.end()) { return QString(); } auto nested = (*found).cover(path.mid(sepIndex + 1)); if (nested.isNull()) { return nested; } if (nested.isEmpty()) return prefix; return prefix + Tseparator + nested; } } /// Does the path-specified node exist in the tree? It does not have to be contained. bool exists(QString path) const { auto sepIndex = path.indexOf(Tseparator); if (sepIndex == -1) { auto found = children.find(path); if (found == children.end()) { return false; } return true; } else { auto prefix = path.left(sepIndex); auto found = children.find(prefix); if (found == children.end()) { return false; } return (*found).exists(path.mid(sepIndex + 1)); } } /// find a node in the tree by name const SeparatorPrefixTree* find(QString path) const { auto sepIndex = path.indexOf(Tseparator); if (sepIndex == -1) { auto found = children.find(path); if (found == children.end()) { return nullptr; } return &(*found); } else { auto prefix = path.left(sepIndex); auto found = children.find(prefix); if (found == children.end()) { return nullptr; } return (*found).find(path.mid(sepIndex + 1)); } } /// is this a leaf node? bool leaf() const { return children.isEmpty(); } /// is this node actually contained in the tree, or is it purely structural? bool contained() const { return m_contained; } /// Remove a path from the tree bool remove(QString path) { return removeInternal(path) != Failed; } /// Clear all children of this node tree node void clear() { children.clear(); } QStringList toStringList() const { QStringList collected; // collecting these is more expensive. auto iter = children.begin(); while (iter != children.end()) { QStringList list = iter.value().toStringList(); for (int i = 0; i < list.size(); i++) { list[i] = iter.key() + Tseparator + list[i]; } collected.append(list); if ((*iter).m_contained) { collected.append(iter.key()); } iter++; } return collected; } private: enum Removal { Failed, Succeeded, HasChildren }; Removal removeInternal(QString path = QString()) { if (path.isEmpty()) { if (!m_contained) { // remove all children - we are removing a prefix clear(); return Succeeded; } m_contained = false; if (children.size()) { return HasChildren; } return Succeeded; } Removal remStatus = Failed; QString childToRemove; auto sepIndex = path.indexOf(Tseparator); if (sepIndex == -1) { childToRemove = path; auto found = children.find(childToRemove); if (found == children.end()) { return Failed; } remStatus = (*found).removeInternal(); } else { childToRemove = path.left(sepIndex); auto found = children.find(childToRemove); if (found == children.end()) { return Failed; } remStatus = (*found).removeInternal(path.mid(sepIndex + 1)); } switch (remStatus) { case Failed: case HasChildren: { return remStatus; } case Succeeded: { children.remove(childToRemove); if (m_contained) { return HasChildren; } if (children.size()) { return HasChildren; } return Succeeded; } } return Failed; } private: QMap> children; bool m_contained = false; }; PrismLauncher-11.0.3/launcher/BaseVersionList.cpp0000644000175100017510000000735715224505336021360 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "BaseVersionList.h" #include "BaseVersion.h" BaseVersionList::BaseVersionList(QObject* parent) : QAbstractListModel(parent) {} BaseVersion::Ptr BaseVersionList::findVersion(const QString& descriptor) { for (int i = 0; i < count(); i++) { if (at(i)->descriptor() == descriptor) return at(i); } return nullptr; } BaseVersion::Ptr BaseVersionList::getRecommended() const { if (count() <= 0) return nullptr; else return at(0); } QVariant BaseVersionList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() > count()) return QVariant(); BaseVersion::Ptr version = at(index.row()); switch (role) { case VersionPointerRole: return QVariant::fromValue(version); case VersionRole: return version->name(); case VersionIdRole: return version->descriptor(); case TypeRole: return version->typeString(); case JavaMajorRole: { auto major = version->name(); if (major.startsWith("java")) { major = "Java " + major.mid(4); } return major; } default: return QVariant(); } } BaseVersionList::RoleList BaseVersionList::providesRoles() const { return { VersionPointerRole, VersionRole, VersionIdRole, TypeRole }; } int BaseVersionList::rowCount(const QModelIndex& parent) const { // Return count return parent.isValid() ? 0 : count(); } int BaseVersionList::columnCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : 1; } QHash BaseVersionList::roleNames() const { QHash roles = QAbstractListModel::roleNames(); roles.insert(VersionRole, "version"); roles.insert(VersionIdRole, "versionId"); roles.insert(ParentVersionRole, "parentGameVersion"); roles.insert(RecommendedRole, "recommended"); roles.insert(LatestRole, "latest"); roles.insert(TypeRole, "type"); roles.insert(BranchRole, "branch"); roles.insert(PathRole, "path"); roles.insert(JavaNameRole, "javaName"); roles.insert(CPUArchitectureRole, "architecture"); roles.insert(JavaMajorRole, "javaMajor"); return roles; } PrismLauncher-11.0.3/launcher/LaunchController.cpp0000644000175100017510000004460415224505336021556 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "LaunchController.h" #include "Application.h" #include "launch/steps/PrintServers.h" #include "minecraft/auth/AccountData.h" #include "minecraft/auth/AccountList.h" #include "net/NetUtils.h" #include "ui/InstanceWindow.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/MSALoginDialog.h" #include "ui/dialogs/ProfileSelectDialog.h" #include "ui/dialogs/ProfileSetupDialog.h" #include "ui/dialogs/ProgressDialog.h" #include #include #include #include #include "BuildConfig.h" #include "JavaCommon.h" #include "launch/steps/TextPrint.h" #include "tasks/Task.h" #include "ui/dialogs/ChooseOfflineNameDialog.h" LaunchController::LaunchController() = default; void LaunchController::executeTask() { if (!m_instance) { emitFailed(tr("No instance specified!")); return; } if (!JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget)) { emitFailed(tr("Invalid Java arguments specified. Please fix this first.")); return; } login(); } void LaunchController::decideAccount() { if (m_accountToUse) { return; } // Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used auto* accounts = APPLICATION->accounts(); const auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString(); const auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId); if (instanceAccountIndex == -1 || instanceAccountId.isEmpty()) { m_accountToUse = accounts->defaultAccount(); } else { m_accountToUse = accounts->at(instanceAccountIndex); } if (!accounts->anyAccountIsValid()) { // Tell the user they need to log in at least one account in order to play. auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"), tr("In order to play Minecraft, you must have at least one Microsoft " "account which owns Minecraft logged in. " "Would you like to open the account manager to add an account now?"), QMessageBox::Information, QMessageBox::Yes | QMessageBox::No) ->exec(); if (reply == QMessageBox::Yes) { // Open the account manager. APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts"); } else if (reply == QMessageBox::No) { // Do not open "profile select" dialog. return; } } if (!m_accountToUse && accounts->anyAccountIsValid()) { // If no default account is set, ask the user which one to use. ProfileSelectDialog selectDialog(tr("Which account would you like to use?"), ProfileSelectDialog::GlobalDefaultCheckbox, m_parentWidget); selectDialog.exec(); // Launch the instance with the selected account. m_accountToUse = selectDialog.selectedAccount(); // If the user said to use the account as default, do that. if (selectDialog.useAsGlobalDefault() && m_accountToUse) { accounts->setDefaultAccount(m_accountToUse); } } } LaunchDecision LaunchController::decideLaunchMode() { if (!m_accountToUse || m_wantedLaunchMode == LaunchMode::Demo) { m_actualLaunchMode = LaunchMode::Demo; return LaunchDecision::Continue; } const auto* accounts = APPLICATION->accounts(); MinecraftAccountPtr accountToCheck = nullptr; if (m_accountToUse->accountType() != AccountType::Offline) { accountToCheck = m_accountToUse->ownsMinecraft() ? m_accountToUse : nullptr; } else if (const auto defaultAccount = accounts->defaultAccount(); defaultAccount && defaultAccount->ownsMinecraft()) { accountToCheck = defaultAccount; } else { for (int i = 0; i < accounts->count(); i++) { if (const auto account = accounts->at(i); account->ownsMinecraft()) { accountToCheck = account; break; } } } if (!accountToCheck) { m_actualLaunchMode = LaunchMode::Demo; return LaunchDecision::Continue; } auto state = accountToCheck->accountState(); const bool needsRefresh = m_wantedLaunchMode == LaunchMode::Normal && (state == AccountState::Offline || accountToCheck->shouldRefresh()); if (state == AccountState::Unchecked || state == AccountState::Errored || needsRefresh) { accountToCheck->refresh(); state = AccountState::Working; } if (state == AccountState::Working) { // refresh is in progress, we need to wait for it to finish to proceed. ProgressDialog progDialog(m_parentWidget); progDialog.setSkipButton(true, tr("Abort")); // TODO: this relies on tasks' synchronous signal dispatching nature // TODO: meaning currentTask can't complete and become null while this code is running // TODO: this code will produce a race condition when tasks become fully async auto task = accountToCheck->currentTask(); progDialog.execWithTask(task.get()); if (task->getState() == State::AbortedByUser) { return LaunchDecision::Abort; } state = accountToCheck->accountState(); } QString reauthReason; switch (state) { case AccountState::Errored: reauthReason = tr("An error occurred while refreshing '%1'").arg(accountToCheck->profileName()); break; case AccountState::Expired: reauthReason = tr("'%1' has expired and needs to be reauthenticated").arg(accountToCheck->profileName()); break; case AccountState::Disabled: reauthReason = tr("The launcher's client identification has changed"); break; case AccountState::Gone: reauthReason = tr("'%1' no longer exists on the servers").arg(accountToCheck->profileName()); break; default: m_actualLaunchMode = state == AccountState::Online && m_wantedLaunchMode == LaunchMode::Normal ? LaunchMode::Normal : LaunchMode::Offline; return LaunchDecision::Continue; // All good to go } if (reauthenticateAccount(accountToCheck, reauthReason)) { return LaunchDecision::Undecided; } return LaunchDecision::Abort; } bool LaunchController::askPlayDemo() const { QMessageBox box(m_parentWidget); box.setWindowTitle(tr("Play demo?")); QString text = m_accountToUse ? tr("This account does not own Minecraft.\nYou need to purchase the game first to play the full version.") : tr("No account was selected for launch."); text += tr("\n\nDo you want to play the demo?"); box.setText(text); box.setIcon(QMessageBox::Warning); const auto* demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole); auto* cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole); box.setDefaultButton(cancelButton); box.exec(); return box.clickedButton() == demoButton; } QString LaunchController::askOfflineName(const QString& playerName, bool* ok) { if (ok != nullptr) { *ok = false; } QString title, message; title = tr("Player name"); switch (m_actualLaunchMode) { case LaunchMode::Normal: Q_ASSERT(false); return ""; case LaunchMode::Demo: message = tr("Choose your demo mode player name"); break; case LaunchMode::Offline: if (m_wantedLaunchMode == LaunchMode::Normal) { auto netErr = m_accountToUse->accountData()->networkError; if (Net::isServerError(netErr)) { title = tr("Auth servers offline"); message = tr("The Minecraft authentication servers are currently unavailable, launching in offline mode.\n\n"); } else { title = tr("No internet connection"); message = tr("You are not connected to the Internet, launching in offline mode.\n\n"); } } message += tr("Choose your offline mode player name"); break; } const QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); QString usedname = lastOfflinePlayerName.isEmpty() ? playerName : lastOfflinePlayerName; ChooseOfflineNameDialog dialog(message, m_parentWidget); dialog.setWindowTitle(title); dialog.setUsername(usedname); if (dialog.exec() != QDialog::Accepted) { return {}; } usedname = dialog.getUsername(); APPLICATION->settings()->set("LastOfflinePlayerName", usedname); if (ok != nullptr) { *ok = true; } return usedname; } void LaunchController::login() { decideAccount(); LaunchDecision decision = decideLaunchMode(); while (decision == LaunchDecision::Undecided) { decision = decideLaunchMode(); } if (decision == LaunchDecision::Abort) { emitAborted(); return; } if (m_actualLaunchMode == LaunchMode::Demo) { if (m_wantedLaunchMode == LaunchMode::Demo || askPlayDemo()) { bool ok = false; auto name = askOfflineName("Player", &ok); if (ok) { m_session = std::make_shared(); m_session->MakeDemo(name, MinecraftAccount::uuidFromUsername(name).toString(QUuid::Id128)); launchInstance(); return; } } emitFailed(tr("No account selected for launch")); return; } m_session = std::make_shared(); m_session->launchMode = m_actualLaunchMode; m_accountToUse->fillSession(m_session); if (m_accountToUse->accountType() != AccountType::Offline) { if (m_actualLaunchMode == LaunchMode::Normal && !m_accountToUse->hasProfile()) { // Now handle setting up a profile name here... if (ProfileSetupDialog dialog(m_accountToUse, m_parentWidget); dialog.exec() != QDialog::Accepted) { emitAborted(); return; } } if (m_actualLaunchMode == LaunchMode::Offline && m_accountToUse->accountType() != AccountType::Offline) { bool ok = false; QString name = m_offlineName; if (name.isEmpty()) { name = askOfflineName(m_session->player_name, &ok); if (!ok) { emitAborted(); return; } } m_session->MakeOffline(name); } } launchInstance(); } bool LaunchController::reauthenticateAccount(const MinecraftAccountPtr& account, const QString& reason) { auto button = QMessageBox::warning( m_parentWidget, tr("Account refresh failed"), tr("%1. Do you want to reauthenticate this account?").arg(reason), QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, QMessageBox::StandardButton::Yes); if (button == QMessageBox::StandardButton::Yes) { auto* accounts = APPLICATION->accounts(); const bool isDefault = accounts->defaultAccount() == account; if (account->accountType() == AccountType::MSA) { auto newAccount = MSALoginDialog::newAccount(m_parentWidget); if (newAccount != nullptr) { accounts->removeAccount(accounts->index(accounts->findAccountByProfileId(account->profileId()))); accounts->addAccount(newAccount); if (isDefault) { accounts->setDefaultAccount(newAccount); } if (m_accountToUse == account) { m_accountToUse = nullptr; decideAccount(); } return true; } } } return false; } void LaunchController::launchInstance() { Q_ASSERT(m_instance != nullptr); Q_ASSERT(m_session.get() != nullptr); if (!m_instance->reloadSettings()) { QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't load the instance profile.")); emitFailed(tr("Couldn't load the instance profile.")); return; } m_launcher = m_instance->createLaunchTask(m_session, m_targetToJoin); if (!m_launcher) { emitFailed(tr("Couldn't instantiate a launcher.")); return; } const auto* console = qobject_cast(m_parentWidget); const auto showConsole = m_instance->settings()->get("ShowConsole").toBool(); if (!console && showConsole) { APPLICATION->showInstanceWindow(m_instance); } connect(m_launcher, &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch); connect(m_launcher, &LaunchTask::succeeded, this, &LaunchController::onSucceeded); connect(m_launcher, &LaunchTask::failed, this, &LaunchController::onFailed); connect(m_launcher, &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested); // Prepend Online and Auth Status QString online_mode; if (m_actualLaunchMode == LaunchMode::Normal) { online_mode = "online"; // Prepend Server Status const QStringList servers = { "login.microsoftonline.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" }; m_launcher->prependStep(makeShared(m_launcher, servers)); } else { online_mode = m_actualLaunchMode == LaunchMode::Demo ? "demo" : "offline"; } m_launcher->prependStep(makeShared(m_launcher, "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); // Prepend Version { auto versionString = QString("%1 version: %2 (%3)") .arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM); m_launcher->prependStep(makeShared(m_launcher, versionString + "\n", MessageLevel::Launcher)); } m_launcher->start(); } void LaunchController::readyForLaunch() { if (!m_profiler) { m_launcher->proceed(); return; } QString error; if (!m_profiler->check(&error)) { m_launcher->abort(); emitFailed("Profiler startup failed!"); QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Profiler check for %1 failed: %2").arg(m_profiler->name(), error)); return; } BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this); connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) { QMessageBox msg(m_parentWidget); msg.setText(tr("The game launch is delayed until you press the " "button. This is the right time to setup the profiler, as the " "profiler server is running now.\n\n%1") .arg(message)); msg.setWindowTitle(tr("Waiting.")); msg.setIcon(QMessageBox::Information); msg.addButton(tr("&Launch"), QMessageBox::AcceptRole); msg.exec(); m_launcher->proceed(); }); connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString& message) { QMessageBox msg; msg.setText(tr("Couldn't start the profiler: %1").arg(message)); msg.setWindowTitle(tr("Error")); msg.setIcon(QMessageBox::Critical); msg.addButton(QMessageBox::Ok); msg.setModal(true); msg.exec(); m_launcher->abort(); emitFailed("Profiler startup failed!"); }); profilerInstance->beginProfiling(m_launcher); } void LaunchController::onSucceeded() { emitSucceeded(); } void LaunchController::onFailed(QString reason) { if (m_instance->settings()->get("ShowConsoleOnError").toBool()) { APPLICATION->showInstanceWindow(m_instance, "console"); } emitFailed(std::move(reason)); } void LaunchController::onProgressRequested(Task* task) const { ProgressDialog progDialog(m_parentWidget); progDialog.setSkipButton(true, tr("Abort")); m_launcher->proceed(); progDialog.execWithTask(task); } bool LaunchController::abort() { if (!m_launcher) { return true; } if (!m_launcher->canAbort()) { return false; } auto response = CustomMessageBox::selectable(m_parentWidget, tr("Kill Minecraft?"), tr("This can cause the instance to get corrupted and should only be used if Minecraft " "is frozen for some reason"), QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) ->exec(); if (response == QMessageBox::Yes) { return m_launcher->abort(); } return false; } PrismLauncher-11.0.3/launcher/InstancePageProvider.h0000644000175100017510000000472115224505336022015 0ustar runnerrunner#pragma once #include #include #include "minecraft/MinecraftInstance.h" #include "ui/pages/BasePage.h" #include "ui/pages/BasePageProvider.h" #include "ui/pages/instance/InstanceSettingsPage.h" #include "ui/pages/instance/LogPage.h" #include "ui/pages/instance/ManagedPackPage.h" #include "ui/pages/instance/ModFolderPage.h" #include "ui/pages/instance/NotesPage.h" #include "ui/pages/instance/OtherLogsPage.h" #include "ui/pages/instance/ResourcePackPage.h" #include "ui/pages/instance/ScreenshotsPage.h" #include "ui/pages/instance/ServersPage.h" #include "ui/pages/instance/ShaderPackPage.h" #include "ui/pages/instance/TexturePackPage.h" #include "ui/pages/instance/VersionPage.h" #include "ui/pages/instance/WorldListPage.h" class InstancePageProvider : protected QObject, public BasePageProvider { Q_OBJECT public: explicit InstancePageProvider(BaseInstance* parent) { inst = parent; } virtual ~InstancePageProvider() = default; virtual QList getPages() override { QList values; values.append(new LogPage(inst)); MinecraftInstance* onesix = dynamic_cast(inst); values.append(new VersionPage(onesix)); values.append(ManagedPackPage::createPage(onesix)); auto modsPage = new ModFolderPage(onesix, onesix->loaderModList()); modsPage->setFilter("%1 (*.zip *.jar *.litemod *.nilmod)"); values.append(modsPage); values.append(new CoreModFolderPage(onesix, onesix->coreModList())); values.append(new NilModFolderPage(onesix, onesix->nilModList())); values.append(new ResourcePackPage(onesix, onesix->resourcePackList())); values.append(new GlobalDataPackPage(onesix)); values.append(new TexturePackPage(onesix, onesix->texturePackList())); values.append(new ShaderPackPage(onesix, onesix->shaderPackList())); values.append(new NotesPage(onesix)); values.append(new WorldListPage(onesix, onesix->worldList())); values.append(new ServersPage(onesix)); values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots"))); values.append(new InstanceSettingsPage(onesix)); values.append(new OtherLogsPage("logs", tr("Other Logs"), "Other-Logs", inst)); return values; } virtual QString dialogTitle() override { return tr("Edit Instance (%1)").arg(inst->name()); } protected: BaseInstance* inst; }; PrismLauncher-11.0.3/launcher/include/0000755000175100017510000000000015224505336017207 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/include/qtgui.pch.hpp0000644000175100017510000000140715224505336021624 0ustar runnerrunner#pragma once #ifndef PRISM_PRECOMPILED_QTGUI_HEADERS_H #define PRISM_PRECOMPILED_QTGUI_HEADERS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif // PRISM_PRECOMPILED_GUI_HEADERS_H PrismLauncher-11.0.3/launcher/include/base.pch.hpp0000644000175100017510000000053515224505336021406 0ustar runnerrunner#pragma once #ifndef PRISM_PRECOMPILED_BASE_HEADERS_H #define PRISM_PRECOMPILED_BASE_HEADERS_H #include #include #include #include #include #include #include #include #include #include #endif // PRISM_PRECOMPILED_BASE_HEADERS_H PrismLauncher-11.0.3/launcher/include/qtcore.pch.hpp0000644000175100017510000000210015224505336021757 0ustar runnerrunner#pragma once #ifndef PRISM_PRECOMPILED_QTCORE_HEADERS_H #define PRISM_PRECOMPILED_QTCORE_HEADERS_H #include #include #include #include #include #include #include #include #include #include // collections #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif // PRISM_PRECOMPILED_QTCORE_HEADERS_H PrismLauncher-11.0.3/launcher/Json.cpp0000644000175100017510000002271115224505336017204 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Json.h" #include #include #include "FileSystem.h" namespace Json { void write(const QJsonDocument& doc, const QString& filename) { FS::write(filename, doc.toJson()); } void write(const QJsonObject& object, const QString& filename) { write(QJsonDocument(object), filename); } void write(const QJsonArray& array, const QString& filename) { write(QJsonDocument(array), filename); } QByteArray toText(const QJsonObject& obj) { return QJsonDocument(obj).toJson(QJsonDocument::Compact); } QByteArray toText(const QJsonArray& array) { return QJsonDocument(array).toJson(QJsonDocument::Compact); } static bool isBinaryJson(const QByteArray& data) { decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag; return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0; } QJsonDocument requireDocument(const QByteArray& data, const QString& what) { if (isBinaryJson(data)) { // FIXME: Is this needed? throw JsonException(what + ": Invalid JSON. Binary JSON unsupported"); } else { QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(data, &error); if (error.error != QJsonParseError::NoError) { throw JsonException(what + ": Error parsing JSON: " + error.errorString()); } return doc; } } QJsonDocument requireDocument(const QString& filename, const QString& what) { return requireDocument(FS::read(filename), what); } QJsonObject requireObject(const QJsonDocument& doc, const QString& what) { if (!doc.isObject()) { throw JsonException(what + " is not an object"); } return doc.object(); } QJsonArray requireArray(const QJsonDocument& doc, const QString& what) { if (!doc.isArray()) { throw JsonException(what + " is not an array"); } return doc.array(); } QJsonDocument parseUntilGarbage(const QByteArray& json, QJsonParseError* error, QString* garbage) { auto doc = QJsonDocument::fromJson(json, error); if (error->error == QJsonParseError::GarbageAtEnd) { qsizetype offset = error->offset; QByteArray validJson = json.left(offset); doc = QJsonDocument::fromJson(validJson, error); if (garbage) *garbage = json.right(json.size() - offset); } return doc; } void writeString(QJsonObject& to, const QString& key, const QString& value) { if (!value.isEmpty()) { to.insert(key, value); } } void writeStringList(QJsonObject& to, const QString& key, const QStringList& values) { if (!values.isEmpty()) { QJsonArray array; for (auto value : values) { array.append(value); } to.insert(key, array); } } template <> QJsonValue toJson(const QUrl& url) { return QJsonValue(url.toString(QUrl::FullyEncoded)); } template <> QJsonValue toJson(const QByteArray& data) { return QJsonValue(QString::fromLatin1(data.toHex())); } template <> QJsonValue toJson(const QDateTime& datetime) { return QJsonValue(datetime.toString(Qt::ISODate)); } template <> QJsonValue toJson(const QDir& dir) { return QDir::current().relativeFilePath(dir.absolutePath()); } template <> QJsonValue toJson(const QUuid& uuid) { return uuid.toString(); } template <> QJsonValue toJson(const QVariant& variant) { return QJsonValue::fromVariant(variant); } template <> QByteArray requireIsType(const QJsonValue& value, const QString& what) { const QString string = value.toString(what); // ensure that the string can be safely cast to Latin1 if (string != QString::fromLatin1(string.toLatin1())) { throw JsonException(what + " is not encodable as Latin1"); } return QByteArray::fromHex(string.toLatin1()); } template <> QJsonArray requireIsType(const QJsonValue& value, const QString& what) { if (!value.isArray()) { throw JsonException(what + " is not an array"); } return value.toArray(); } template <> QString requireIsType(const QJsonValue& value, const QString& what) { if (!value.isString()) { throw JsonException(what + " is not a string"); } return value.toString(); } template <> bool requireIsType(const QJsonValue& value, const QString& what) { if (!value.isBool()) { throw JsonException(what + " is not a bool"); } return value.toBool(); } template <> double requireIsType(const QJsonValue& value, const QString& what) { if (!value.isDouble()) { throw JsonException(what + " is not a double"); } return value.toDouble(); } template <> int requireIsType(const QJsonValue& value, const QString& what) { const double doubl = requireIsType(value, what); if (fmod(doubl, 1) != 0) { throw JsonException(what + " is not an integer"); } return int(doubl); } template <> QDateTime requireIsType(const QJsonValue& value, const QString& what) { const QString string = requireIsType(value, what); const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate); if (!datetime.isValid()) { throw JsonException(what + " is not a ISO formatted date/time value"); } return datetime; } template <> QUrl requireIsType(const QJsonValue& value, const QString& what) { const QString string = value.toString(what); if (string.isEmpty()) { return QUrl(); } const QUrl url = QUrl(string, QUrl::StrictMode); if (!url.isValid()) { throw JsonException(what + " is not a correctly formatted URL"); } return url; } template <> QDir requireIsType(const QJsonValue& value, const QString& what) { const QString string = requireIsType(value, what); // FIXME: does not handle invalid characters! return QDir::current().absoluteFilePath(string); } template <> QUuid requireIsType(const QJsonValue& value, const QString& what) { const QString string = requireIsType(value, what); const QUuid uuid = QUuid(string); if (uuid.toString() != string) // converts back => valid { throw JsonException(what + " is not a valid UUID"); } return uuid; } template <> QJsonObject requireIsType(const QJsonValue& value, const QString& what) { if (!value.isObject()) { throw JsonException(what + " is not an object"); } return value.toObject(); } template <> QVariant requireIsType(const QJsonValue& value, const QString& what) { if (value.isNull() || value.isUndefined()) { throw JsonException(what + " is null or undefined"); } return value.toVariant(); } template <> QJsonValue requireIsType(const QJsonValue& value, const QString& what) { if (value.isNull() || value.isUndefined()) { throw JsonException(what + " is null or undefined"); } return value; } QStringList toStringList(const QString& jsonString) { QJsonParseError parseError; QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8(), &parseError); if (parseError.error != QJsonParseError::NoError || !doc.isArray()) return {}; try { return requireIsArrayOf(doc); } catch (Json::JsonException&) { return {}; } } QString fromStringList(const QStringList& list) { QJsonArray array; for (const QString& str : list) { array.append(str); } QJsonDocument doc(toJsonArray(list)); return QString::fromUtf8(doc.toJson(QJsonDocument::Compact)); } QVariantMap toMap(const QString& jsonString) { QJsonParseError parseError; QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8(), &parseError); if (parseError.error != QJsonParseError::NoError || !doc.isObject()) return {}; QJsonObject obj = doc.object(); return obj.toVariantMap(); } QString fromMap(const QVariantMap& map) { QJsonObject obj = QJsonObject::fromVariantMap(map); QJsonDocument doc(obj); return QString::fromUtf8(doc.toJson(QJsonDocument::Compact)); } } // namespace Json PrismLauncher-11.0.3/launcher/RecursiveFileSystemWatcher.h0000644000175100017510000000217115224505336023230 0ustar runnerrunner#pragma once #include #include #include "Filter.h" class RecursiveFileSystemWatcher : public QObject { Q_OBJECT public: RecursiveFileSystemWatcher(QObject* parent); void setRootDir(const QDir& root); QDir rootDir() const { return m_root; } // WARNING: setting this to true may be bad for performance void setWatchFiles(bool watchFiles); bool watchFiles() const { return m_watchFiles; } void setMatcher(Filter matcher) { m_matcher = std::move(matcher); } QStringList files() const { return m_files; } signals: void filesChanged(); void fileChanged(const QString& path); public slots: void enable(); void disable(); private: QDir m_root; bool m_watchFiles = false; bool m_isEnabled = false; Filter m_matcher; QFileSystemWatcher* m_watcher; QStringList m_files; void setFiles(const QStringList& files); void addFilesToWatcherRecursive(const QDir& dir); QStringList scanRecursive(const QDir& dir); private slots: void fileChange(const QString& path); void directoryChange(const QString& path); }; PrismLauncher-11.0.3/launcher/InstanceDirUpdate.h0000644000175100017510000000354515224505336021312 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "BaseInstance.h" /// Update instanceRoot to make it sync with name/id; return newRoot if a directory rename happened QString askToUpdateInstanceDirName(BaseInstance* instance, const QString& oldName, const QString& newName, QWidget* parent); /// Check if there are linked instances, and display a warning; return true if the operation should proceed bool checkLinkedInstances(const QString& id, QWidget* parent, const QString& verb); PrismLauncher-11.0.3/launcher/Usable.h0000644000175100017510000000175115224505336017154 0ustar runnerrunner#pragma once #include #include #include "QObjectPtr.h" class Usable; /** * Base class for things that can be used by multiple other things and we want to track the use count. * * @see UseLock */ class Usable { friend class UseLock; public: virtual ~Usable() {} std::size_t useCount() const { return m_useCount; } bool isInUse() const { return m_useCount > 0; } protected: virtual void decrementUses() { m_useCount--; } virtual void incrementUses() { m_useCount++; } private: std::size_t m_useCount = 0; }; /** * Lock class to use for keeping track of uses of other things derived from Usable * * @see Usable */ class UseLock { public: UseLock(Usable* usable) : m_usable(usable) { // this doesn't use shared pointer use count, because that wouldn't be correct. this count is separate. m_usable->incrementUses(); } ~UseLock() { m_usable->decrementUses(); } private: Usable* m_usable; }; PrismLauncher-11.0.3/launcher/MTPixmapCache.h0000644000175100017510000001616615224505336020372 0ustar runnerrunner#pragma once #include #include #include #include #include #include #define GET_TYPE() \ Qt::ConnectionType type; \ if (QThread::currentThread() != QCoreApplication::instance()->thread()) \ type = Qt::BlockingQueuedConnection; \ else \ type = Qt::DirectConnection; #define DEFINE_FUNC_NO_PARAM(NAME, RET_TYPE) \ static RET_TYPE NAME() \ { \ RET_TYPE ret; \ GET_TYPE() \ QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret)); \ return ret; \ } #define DEFINE_FUNC_ONE_PARAM(NAME, RET_TYPE, PARAM_1_TYPE) \ static RET_TYPE NAME(PARAM_1_TYPE p1) \ { \ RET_TYPE ret; \ GET_TYPE() \ QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret), Q_ARG(PARAM_1_TYPE, p1)); \ return ret; \ } #define DEFINE_FUNC_TWO_PARAM(NAME, RET_TYPE, PARAM_1_TYPE, PARAM_2_TYPE) \ static RET_TYPE NAME(PARAM_1_TYPE p1, PARAM_2_TYPE p2) \ { \ RET_TYPE ret; \ GET_TYPE() \ QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret), Q_ARG(PARAM_1_TYPE, p1), \ Q_ARG(PARAM_2_TYPE, p2)); \ return ret; \ } /** A wrapper around QPixmapCache with thread affinity with the main thread. */ class PixmapCache final : public QObject { Q_OBJECT public: PixmapCache(QObject* parent) : QObject(parent) {} ~PixmapCache() override = default; static PixmapCache& instance() { return *s_instance; } static void setInstance(PixmapCache* i) { s_instance = i; } public: DEFINE_FUNC_NO_PARAM(cacheLimit, int) DEFINE_FUNC_NO_PARAM(clear, bool) DEFINE_FUNC_TWO_PARAM(find, bool, const QString&, QPixmap*) DEFINE_FUNC_TWO_PARAM(find, bool, const QPixmapCache::Key&, QPixmap*) DEFINE_FUNC_TWO_PARAM(insert, bool, const QString&, const QPixmap&) DEFINE_FUNC_ONE_PARAM(insert, QPixmapCache::Key, const QPixmap&) DEFINE_FUNC_ONE_PARAM(remove, bool, const QString&) DEFINE_FUNC_ONE_PARAM(remove, bool, const QPixmapCache::Key&) DEFINE_FUNC_TWO_PARAM(replace, bool, const QPixmapCache::Key&, const QPixmap&) DEFINE_FUNC_ONE_PARAM(setCacheLimit, bool, int) DEFINE_FUNC_NO_PARAM(markCacheMissByEviciton, bool) DEFINE_FUNC_ONE_PARAM(setFastEvictionThreshold, bool, int) // NOTE: Every function returns something non-void to simplify the macros. private slots: int _cacheLimit() { return QPixmapCache::cacheLimit(); } bool _clear() { QPixmapCache::clear(); return true; } bool _find(const QString& key, QPixmap* pixmap) { return QPixmapCache::find(key, pixmap); } bool _find(const QPixmapCache::Key& key, QPixmap* pixmap) { return QPixmapCache::find(key, pixmap); } bool _insert(const QString& key, const QPixmap& pixmap) { return QPixmapCache::insert(key, pixmap); } QPixmapCache::Key _insert(const QPixmap& pixmap) { return QPixmapCache::insert(pixmap); } bool _remove(const QString& key) { QPixmapCache::remove(key); return true; } bool _remove(const QPixmapCache::Key& key) { QPixmapCache::remove(key); return true; } bool _replace(const QPixmapCache::Key& key, const QPixmap& pixmap) { return QPixmapCache::replace(key, pixmap); } bool _setCacheLimit(int n) { QPixmapCache::setCacheLimit(n); return true; } /** * Mark that a cache miss occurred because of a eviction if too many of these occur too fast the cache size is increased * @return if the cache size was increased */ bool _markCacheMissByEviciton() { static constexpr uint maxCache = static_cast(std::numeric_limits::max()) / 4; static constexpr uint step = 10240; static constexpr int oneSecond = 1000; auto now = QTime::currentTime(); if (!m_last_cache_miss_by_eviciton.isNull()) { auto diff = m_last_cache_miss_by_eviciton.msecsTo(now); if (diff < oneSecond) { // less than a second ago ++m_consecutive_fast_evicitons; } else { m_consecutive_fast_evicitons = 0; } } m_last_cache_miss_by_eviciton = now; if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) { // increase the cache size uint newSize = _cacheLimit() + step; if (newSize >= maxCache) { // increase it until you overflow :D newSize = maxCache; qDebug() << m_consecutive_fast_evicitons << tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit"); } else { qDebug() << m_consecutive_fast_evicitons << tr("pixmap cache misses by eviction happened too fast, increasing cache size to") << static_cast(newSize); } _setCacheLimit(static_cast(newSize)); m_consecutive_fast_evicitons = 0; return true; } return false; } bool _setFastEvictionThreshold(int threshold) { m_consecutive_fast_evicitons_threshold = threshold; return true; } private: static PixmapCache* s_instance; QTime m_last_cache_miss_by_eviciton; int m_consecutive_fast_evicitons = 0; int m_consecutive_fast_evicitons_threshold = 15; }; PrismLauncher-11.0.3/launcher/RecursiveFileSystemWatcher.cpp0000644000175100017510000000517215224505336023567 0ustar runnerrunner#include "RecursiveFileSystemWatcher.h" #include RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject* parent) : QObject(parent), m_watcher(new QFileSystemWatcher(this)) { connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &RecursiveFileSystemWatcher::fileChange); connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &RecursiveFileSystemWatcher::directoryChange); } void RecursiveFileSystemWatcher::setRootDir(const QDir& root) { bool wasEnabled = m_isEnabled; disable(); m_root = root; setFiles(scanRecursive(m_root)); if (wasEnabled) { enable(); } } void RecursiveFileSystemWatcher::setWatchFiles(const bool watchFiles) { bool wasEnabled = m_isEnabled; disable(); m_watchFiles = watchFiles; if (wasEnabled) { enable(); } } void RecursiveFileSystemWatcher::enable() { if (m_isEnabled) { return; } Q_ASSERT(m_root != QDir::root()); addFilesToWatcherRecursive(m_root); m_isEnabled = true; } void RecursiveFileSystemWatcher::disable() { if (!m_isEnabled) { return; } m_isEnabled = false; m_watcher->removePaths(m_watcher->files()); m_watcher->removePaths(m_watcher->directories()); } void RecursiveFileSystemWatcher::setFiles(const QStringList& files) { if (files != m_files) { m_files = files; emit filesChanged(); } } void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir& dir) { m_watcher->addPath(dir.absolutePath()); for (const QString& directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { addFilesToWatcherRecursive(dir.absoluteFilePath(directory)); } if (m_watchFiles) { for (const QFileInfo& info : dir.entryInfoList(QDir::Files)) { m_watcher->addPath(info.absoluteFilePath()); } } } QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir& directory) { QStringList ret; if (!m_matcher) { return {}; } for (const QString& dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden)) { ret.append(scanRecursive(directory.absoluteFilePath(dir))); } for (const QString& file : directory.entryList(QDir::Files | QDir::Hidden)) { auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file)); if (m_matcher(relPath)) { ret.append(relPath); } } return ret; } void RecursiveFileSystemWatcher::fileChange(const QString& path) { emit fileChanged(path); } void RecursiveFileSystemWatcher::directoryChange([[maybe_unused]] const QString& path) { setFiles(scanRecursive(m_root)); } PrismLauncher-11.0.3/launcher/BaseInstaller.h0000644000175100017510000000235015224505336020465 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include "BaseVersion.h" class MinecraftInstance; class QDir; class QString; class QObject; class Task; class BaseVersion; class BaseInstaller { public: BaseInstaller(); virtual ~BaseInstaller() {}; bool isApplied(MinecraftInstance* on); virtual bool add(MinecraftInstance* to); virtual bool remove(MinecraftInstance* from); virtual Task* createInstallTask(MinecraftInstance* instance, BaseVersion::Ptr version, QObject* parent) = 0; protected: virtual QString id() const = 0; QString filename(const QString& root) const; QDir patchesDir(const QString& root) const; }; PrismLauncher-11.0.3/launcher/Exception.h0000644000175100017510000000402615224505336017675 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2024 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include class Exception : public std::exception { public: Exception(const QString& message) : std::exception(), m_message(message.toUtf8()) { qCritical() << "Exception:" << message; } Exception(const Exception& other) : std::exception(), m_message(other.m_message) {} virtual ~Exception() noexcept {} const char* what() const noexcept { return m_message.constData(); } QString cause() const { return QString::fromUtf8(m_message); } private: QByteArray m_message; }; PrismLauncher-11.0.3/launcher/JavaCommon.h0000644000175100017510000000260615224505336017773 0ustar runnerrunner#pragma once #include class QWidget; /** * Common UI bits for the java pages to use. */ namespace JavaCommon { bool checkJVMArgs(QString args, QWidget* parent); // Show a dialog saying that the Java binary was usable void javaWasOk(QWidget* parent, const JavaChecker::Result& result); // Show a dialog saying that the Java binary was not usable because of bad options void javaArgsWereBad(QWidget* parent, const JavaChecker::Result& result); // Show a dialog saying that the Java binary was not usable void javaBinaryWasBad(QWidget* parent, const JavaChecker::Result& result); // Show a dialog if we couldn't find Java Checker void javaCheckNotFound(QWidget* parent); class TestCheck : public QObject { Q_OBJECT public: TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen) : m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen) {} virtual ~TestCheck() = default; void run(); signals: void finished(); private slots: void checkFinished(const JavaChecker::Result& result); void checkFinishedWithArgs(const JavaChecker::Result& result); private: JavaChecker::Ptr checker; QWidget* m_parent = nullptr; QString m_path; QString m_args; int m_minMem = 0; int m_maxMem = 0; int m_permGen = 64; }; } // namespace JavaCommon PrismLauncher-11.0.3/launcher/HardwareInfo.cpp0000644000175100017510000002304715224505336020647 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2026 Octol1ttle * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "HardwareInfo.h" #include #include #if defined(Q_OS_MACOS) || defined(Q_OS_LINUX) namespace { QString afterColon(QString str) { return str.remove(0, str.indexOf(':') + 2).trimmed(); } template bool readFromOutput(const char* command, F function) { FILE* file = popen(command, "r"); // NOLINT(*-command-processor) if (!file) { qWarning().nospace() << "Could not execute command '" << command << "': " << strerror(errno); return false; } constexpr size_t bufferSize = 512; std::array buffer{}; while (fgets(buffer.data(), bufferSize, file) != nullptr) { function(buffer.data()); } const int exitCode = pclose(file); if (exitCode != 0) { if (exitCode == -1) { qWarning().nospace() << "Could not close stream for command '" << command << "': " << strerror(errno); } else { qWarning().nospace() << "Command '" << command << "' exited with code " << exitCode; } return false; } return true; } } // namespace #endif #ifdef Q_OS_WINDOWS #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #include using Microsoft::WRL::ComPtr; QString HardwareInfo::cpuInfo() { const QSettings registry(R"(HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0)", QSettings::NativeFormat); return registry.value("ProcessorNameString").toString(); } uint64_t HardwareInfo::totalRamMiB() { MEMORYSTATUSEX status; status.dwLength = sizeof status; if (GlobalMemoryStatusEx(&status) == TRUE) { // transforming bytes -> mib return status.ullTotalPhys / 1024 / 1024; } qWarning() << "Could not get total RAM: GlobalMemoryStatusEx"; return 0; } uint64_t HardwareInfo::availableRamMiB() { MEMORYSTATUSEX status; status.dwLength = sizeof status; if (GlobalMemoryStatusEx(&status) == TRUE) { // transforming bytes -> mib return status.ullAvailPhys / 1024 / 1024; } qWarning() << "Could not get available RAM: GlobalMemoryStatusEx"; return 0; } QStringList HardwareInfo::gpuInfo() { ComPtr factory; HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory)); if (FAILED(hr)) { qWarning() << "Could not create DXGI factory:" << Qt::hex << hr; return { "GPU discovery failed: could not create DXGI factory" }; } UINT i = 0; ComPtr adapter; QStringList out; while (factory->EnumAdapterByGpuPreference(i, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)) != DXGI_ERROR_NOT_FOUND) { DXGI_ADAPTER_DESC desc; hr = adapter->GetDesc(&desc); if (SUCCEEDED(hr)) { out << "GPU: " + QString::fromWCharArray(desc.Description); // NOLINT(*-pro-bounds-array-to-pointer-decay, *-no-array-decay) } else { qWarning() << "Could not get DXGI adapter description:" << Qt::hex << hr; } ++i; } return out; } #elif defined(Q_OS_MACOS) #include "sys/sysctl.h" QString HardwareInfo::cpuInfo() { std::array buffer{}; size_t bufferSize = buffer.size(); if (sysctlbyname("machdep.cpu.brand_string", &buffer, &bufferSize, nullptr, 0) == 0) { return { buffer.data() }; } qWarning() << "Could not get CPU model: sysctlbyname"; return ""; } uint64_t HardwareInfo::totalRamMiB() { uint64_t memsize = 0; size_t memsizeSize = sizeof memsize; if (sysctlbyname("hw.memsize", &memsize, &memsizeSize, nullptr, 0) == 0) { // transforming bytes -> mib return memsize / 1024 / 1024; } qWarning() << "Could not get total RAM: sysctlbyname"; return 0; } uint64_t HardwareInfo::availableRamMiB() { return 0; } MacOSHardwareInfo::MemoryPressureLevel MacOSHardwareInfo::memoryPressureLevel() { uint32_t level = 0; size_t levelSize = sizeof level; if (sysctlbyname("kern.memorystatus_vm_pressure_level", &level, &levelSize, nullptr, 0) == 0) { return static_cast(level); } qWarning() << "Could not get memory pressure level: sysctlbyname"; return MemoryPressureLevel::Normal; } QString MacOSHardwareInfo::memoryPressureLevelName() { // The names are internal, users refer to levels by their graph colors in Activity Monitor switch (memoryPressureLevel()) { case MemoryPressureLevel::Normal: return "Green"; case MemoryPressureLevel::Warning: return "Yellow"; case MemoryPressureLevel::Critical: return "Red"; default: Q_ASSERT(false); return ""; } } QStringList HardwareInfo::gpuInfo() { QStringList out; const bool success = readFromOutput("system_profiler SPDisplaysDataType", [&](const QString& str) { // Chipset Model: Intel HD Graphics 620 if (str.contains("Chipset Model")) { out << "GPU: " + afterColon(str); } }); if (!success) { return { "GPU discovery failed: could not read from system_profiler" }; } return out; } #elif defined(Q_OS_LINUX) #include QString HardwareInfo::cpuInfo() { std::ifstream cpuin("/proc/cpuinfo"); for (std::string line; std::getline(cpuin, line);) { // model name : AMD Ryzen 7 5800X 8-Core Processor if (const QString str = QString::fromStdString(line); str.startsWith("model name")) { return afterColon(str); } } qWarning() << "Could not get CPU model: /proc/cpuinfo"; return "unknown"; } namespace { uint64_t readMemInfo(const QString& searchTarget) { std::ifstream memin("/proc/meminfo"); for (std::string line; std::getline(memin, line);) { // MemTotal: 16287480 kB if (const QString str = QString::fromStdString(line); str.startsWith(searchTarget)) { bool ok = false; const uint total = str.simplified().section(' ', 1, 1).toUInt(&ok); if (!ok) { qWarning() << "Could not read /proc/meminfo: failed to parse string:" << str; return 0; } // transforming kib -> mib return total / 1024; } } qWarning() << "Could not read /proc/meminfo: search target not found:" << searchTarget; return 0; } } // namespace uint64_t HardwareInfo::totalRamMiB() { return readMemInfo("MemTotal"); } uint64_t HardwareInfo::availableRamMiB() { return readMemInfo("MemAvailable"); } QStringList HardwareInfo::gpuInfo() { bool readingGpuInfo = false; QString gpu; QString driverInUse = "NONE"; QString driversAvailable = "NONE"; QStringList out; const bool success = readFromOutput("lspci -k", [&](const QString& str) { // clang-format off // 04:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (rev e7) // Subsystem: Sapphire Technology Limited Radeon RX 580 Pulse 4GB // Kernel driver in use: amdgpu // Kernel modules: amdgpu // clang-format on if (str.contains("VGA compatible controller") || str.contains("3D controller")) { readingGpuInfo = true; } else if (!str.startsWith('\t')) { if (readingGpuInfo) { out << QString("GPU: %1 (driver in use: %2; drivers available: %3)").arg(gpu, driverInUse, driversAvailable); driverInUse = "NONE"; driversAvailable = "NONE"; } readingGpuInfo = false; } if (!readingGpuInfo) { return; } const QString value = afterColon(str); if (str.contains("Subsystem")) { gpu = value; } if (str.contains("Kernel driver in use")) { driverInUse = value; } if (str.contains("Kernel modules")) { driversAvailable = value; } }); if (!success) { return { "GPU discovery failed: could not read from lspci" }; } return out; } #else QString HardwareInfo::cpuInfo() { return "unknown"; } #if defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #include uint64_t HardwareInfo::totalRamMiB() { uint64_t out = 0; const bool success = readFromOutput("sysctl hw.physmem", [&](const QString& str) { const uint64_t mem = str.mid(12).toULong(); // transforming kib -> mib out = mem / 1024; }); if (!success) { qWarning() << "Could not get total RAM: could not read from sysctl"; return 0; } return out; } #else uint64_t HardwareInfo::totalRamMiB() { return 0; } #endif uint64_t HardwareInfo::availableRamMiB() { return 0; } QStringList HardwareInfo::gpuInfo() { return { "GPU discovery failed: not implemented for this OS" }; } #endif PrismLauncher-11.0.3/launcher/screenshots/0000755000175100017510000000000015224505336020124 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/screenshots/ImgurAlbumCreation.cpp0000644000175100017510000000766615224505336024400 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ImgurAlbumCreation.h" #include #include #include #include #include #include #include #include #include "BuildConfig.h" #include "net/RawHeaderProxy.h" Net::NetRequest::Ptr ImgurAlbumCreation::make(std::shared_ptr output, QList screenshots) { auto up = makeShared(); up->m_url = BuildConfig.IMGUR_BASE_URL + "album"; up->m_sink.reset(new Sink(output)); up->m_screenshots = screenshots; up->addHeaderProxy(std::make_unique( QList{ { "Content-Type", "application/x-www-form-urlencoded" }, { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() }, { "Accept", "application/json" } })); return up; } QNetworkReply* ImgurAlbumCreation::getReply(QNetworkRequest& request) { QStringList hashes; for (auto shot : m_screenshots) { hashes.append(shot->m_imgurDeleteHash); } const QByteArray data = "deletehashes=" + hashes.join(',').toUtf8() + "&title=Minecraft%20Screenshots&privacy=hidden"; return m_network->post(request, data); } auto ImgurAlbumCreation::Sink::init(QNetworkRequest& request) -> Task::State { m_output.clear(); return Task::State::Running; } auto ImgurAlbumCreation::Sink::write(QByteArray& data) -> Task::State { m_output.append(data); return Task::State::Running; } auto ImgurAlbumCreation::Sink::abort() -> Task::State { m_output.clear(); m_fail_reason = "Aborted"; return Task::State::Failed; } auto ImgurAlbumCreation::Sink::finalize(QNetworkReply&) -> Task::State { QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(m_output, &jsonError); if (jsonError.error != QJsonParseError::NoError) { qDebug() << jsonError.errorString(); m_fail_reason = "Invalid json reply"; return Task::State::Failed; } auto object = doc.object(); if (!object.value("success").toBool()) { qDebug() << doc.toJson(); m_fail_reason = "Failed to create album"; return Task::State::Failed; } m_result->deleteHash = object.value("data").toObject().value("deletehash").toString(); m_result->id = object.value("data").toObject().value("id").toString(); return Task::State::Succeeded; } PrismLauncher-11.0.3/launcher/screenshots/ImgurUpload.cpp0000644000175100017510000001126215224505336023062 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ImgurUpload.h" #include "BuildConfig.h" #include "net/RawHeaderProxy.h" #include #include #include #include #include #include #include #include QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request) { auto file = new QFile(m_fileInfo.absoluteFilePath(), this); if (!file->open(QFile::ReadOnly)) { emitFailed(tr("Could not open file %1 for reading: %2").arg(m_fileInfo.absoluteFilePath()).arg(file->errorString())); return nullptr; } QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType, this); file->setParent(multipart); QHttpPart filePart; filePart.setBodyDevice(file); filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png"); filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\"; filename=\"" + file->fileName() + "\""); multipart->append(filePart); QHttpPart typePart; typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\""); typePart.setBody("file"); multipart->append(typePart); QHttpPart namePart; namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"title\""); namePart.setBody(m_fileInfo.baseName().toUtf8()); multipart->append(namePart); return m_network->post(request, multipart); } auto ImgurUpload::Sink::init(QNetworkRequest& request) -> Task::State { m_output.clear(); return Task::State::Running; } auto ImgurUpload::Sink::write(QByteArray& data) -> Task::State { m_output.append(data); return Task::State::Running; } auto ImgurUpload::Sink::abort() -> Task::State { m_output.clear(); m_fail_reason = "Aborted"; return Task::State::Failed; } auto ImgurUpload::Sink::finalize(QNetworkReply&) -> Task::State { QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(m_output, &jsonError); if (jsonError.error != QJsonParseError::NoError) { qDebug() << "imgur server did not reply with JSON" << jsonError.errorString(); m_fail_reason = "Invalid json reply"; return Task::State::Failed; } auto object = doc.object(); if (!object.value("success").toBool()) { qDebug() << "Screenshot upload not successful:" << doc.toJson(); m_fail_reason = "Screenshot was not uploaded successfully"; return Task::State::Failed; } m_shot->m_imgurId = object.value("data").toObject().value("id").toString(); m_shot->m_url = object.value("data").toObject().value("link").toString(); m_shot->m_imgurDeleteHash = object.value("data").toObject().value("deletehash").toString(); return Task::State::Succeeded; } Net::NetRequest::Ptr ImgurUpload::make(ScreenShot::Ptr m_shot) { auto up = makeShared(m_shot->m_file); up->m_url = BuildConfig.IMGUR_BASE_URL + "image"; up->m_sink.reset(new Sink(m_shot)); up->addHeaderProxy(std::make_unique(QList{ { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toUtf8() }, { "Accept", "application/json" } })); return up; } PrismLauncher-11.0.3/launcher/screenshots/ImgurAlbumCreation.h0000644000175100017510000000474315224505336024036 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "Screenshot.h" #include "net/NetRequest.h" class ImgurAlbumCreation : public Net::NetRequest { public: virtual ~ImgurAlbumCreation() = default; struct Result { QString deleteHash; QString id; }; class Sink : public Net::Sink { public: Sink(std::shared_ptr res) : m_result(res) {}; virtual ~Sink() = default; public: auto init(QNetworkRequest& request) -> Task::State override; auto write(QByteArray& data) -> Task::State override; auto abort() -> Task::State override; auto finalize(QNetworkReply& reply) -> Task::State override; auto hasLocalData() -> bool override { return false; } private: std::shared_ptr m_result; QByteArray m_output; }; static NetRequest::Ptr make(std::shared_ptr output, QList screenshots); QNetworkReply* getReply(QNetworkRequest& request) override; private: QList m_screenshots; }; PrismLauncher-11.0.3/launcher/screenshots/Screenshot.h0000644000175100017510000000046415224505336022416 0ustar runnerrunner#pragma once #include #include #include #include struct ScreenShot { using Ptr = std::shared_ptr; ScreenShot(QFileInfo file) { m_file = file; } QFileInfo m_file; QString m_url; QString m_imgurId; QString m_imgurDeleteHash; }; PrismLauncher-11.0.3/launcher/screenshots/ImgurUpload.h0000644000175100017510000000461315224505336022531 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include "Screenshot.h" #include "net/NetRequest.h" class ImgurUpload : public Net::NetRequest { public: class Sink : public Net::Sink { public: Sink(ScreenShot::Ptr shot) : m_shot(shot) {}; virtual ~Sink() = default; public: auto init(QNetworkRequest& request) -> Task::State override; auto write(QByteArray& data) -> Task::State override; auto abort() -> Task::State override; auto finalize(QNetworkReply& reply) -> Task::State override; auto hasLocalData() -> bool override { return false; } private: ScreenShot::Ptr m_shot; QByteArray m_output; }; ImgurUpload(QFileInfo info) : m_fileInfo(info) {} virtual ~ImgurUpload() = default; static NetRequest::Ptr make(ScreenShot::Ptr m_shot); private: virtual QNetworkReply* getReply(QNetworkRequest&) override; const QFileInfo m_fileInfo; }; PrismLauncher-11.0.3/launcher/BaseVersion.h0000644000175100017510000000306115224505336020155 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include /*! * An abstract base class for versions. */ class BaseVersion { public: // TODO: delete using Ptr = std::shared_ptr; virtual ~BaseVersion() {} /*! * A string used to identify this version in config files. * This should be unique within the version list or shenanigans will occur. */ virtual QString descriptor() const = 0; /*! * The name of this version as it is displayed to the user. * For example: "1.5.1" */ virtual QString name() const = 0; /*! * This should return a string that describes * the kind of version this is (Stable, Beta, Snapshot, whatever) */ virtual QString typeString() const = 0; virtual bool operator<(BaseVersion& a) const { return name() < a.name(); } virtual bool operator>(BaseVersion& a) const { return name() > a.name(); } }; Q_DECLARE_METATYPE(BaseVersion::Ptr) PrismLauncher-11.0.3/launcher/ApplicationMessage.cpp0000644000175100017510000000452215224505336022043 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ApplicationMessage.h" #include #include #include "Json.h" void ApplicationMessage::parse(const QByteArray& input) { auto doc = Json::requireDocument(input, "ApplicationMessage"); auto root = Json::requireObject(doc, "ApplicationMessage"); command = root.value("command").toString(); args.clear(); auto parsedArgs = root.value("args").toObject(); for (auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) { args.insert(iter.key(), iter.value().toString()); } } QByteArray ApplicationMessage::serialize() { QJsonObject root; root.insert("command", command); QJsonObject outArgs; for (auto iter = args.constBegin(); iter != args.constEnd(); iter++) { outArgs.insert(iter.key(), iter.value()); } root.insert("args", outArgs); return Json::toText(root); } PrismLauncher-11.0.3/launcher/filelink/0000755000175100017510000000000015224505336017361 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/filelink/filelink_main.cpp0000644000175100017510000000263015224505336022667 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "FileLink.h" #if defined Q_OS_WIN32 #include "console/WindowsConsole.h" #endif int main(int argc, char* argv[]) { #if defined Q_OS_WIN32 // attach the parent console console::WindowsConsoleGuard _consoleGuard; #endif FileLinkApp ldh(argc, argv); switch (ldh.status()) { case FileLinkApp::Starting: case FileLinkApp::Initialized: { return ldh.exec(); } case FileLinkApp::Failed: return 1; case FileLinkApp::Succeeded: return 0; default: return -1; } } PrismLauncher-11.0.3/launcher/filelink/FileLink.h0000644000175100017510000000331715224505336021233 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #pragma once #include #include #include #include #include #include #include #include #include #include #define PRISM_EXTERNAL_EXE #include "FileSystem.h" class FileLinkApp : public QCoreApplication { Q_OBJECT public: enum Status { Starting, Failed, Succeeded, Initialized }; FileLinkApp(int& argc, char** argv); virtual ~FileLinkApp(); Status status() const { return m_status; } private: void joinServer(QString server); void readPathPairs(); void runLink(); void sendResults(); Status m_status = Status::Starting; bool m_useHardLinks = false; QDateTime m_startTime; QLocalSocket socket; QDataStream in; quint32 blockSize; QList m_links_to_make; QList m_path_results; }; PrismLauncher-11.0.3/launcher/filelink/filelink.exe.manifest0000644000175100017510000000177515224505336023500 0ustar runnerrunner PrismLauncher-11.0.3/launcher/filelink/FileLink.cpp0000644000175100017510000001673615224505336021577 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> // // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "FileLink.h" #include "BuildConfig.h" #include "StringUtils.h" #include #include #include #include #include #include namespace fs = std::filesystem; FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this)) { setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); setApplicationName(BuildConfig.LAUNCHER_NAME + "FileLink"); setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT); // Commandline parsing QCommandLineParser parser; parser.setApplicationDescription(QObject::tr("a batch MKLINK program for windows to be used with prismlauncher")); parser.addOptions({ { { "s", "server" }, "Join the specified server on launch", "pipe name" }, { { "H", "hard" }, "use hard links instead of symbolic", "true/false" } }); parser.addHelpOption(); parser.addVersionOption(); parser.process(arguments()); QString serverToJoin = parser.value("server"); m_useHardLinks = QVariant(parser.value("hard")).toBool(); qDebug() << "link program launched"; if (!serverToJoin.isEmpty()) { qDebug() << "joining server" << serverToJoin; joinServer(serverToJoin); } else { qDebug() << "no server to join"; m_status = Failed; exit(); } } void FileLinkApp::joinServer(QString server) { blockSize = 0; in.setDevice(&socket); connect(&socket, &QLocalSocket::connected, this, []() { qDebug() << "connected to server"; }); connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs); connect(&socket, &QLocalSocket::errorOccurred, this, [this](QLocalSocket::LocalSocketError socketError) { m_status = Failed; switch (socketError) { case QLocalSocket::ServerNotFoundError: qDebug() << ("The host was not found. Please make sure " "that the server is running and that the " "server name is correct."); break; case QLocalSocket::ConnectionRefusedError: qDebug() << ("The connection was refused by the peer. " "Make sure the server is running, " "and check that the server name " "is correct."); break; case QLocalSocket::PeerClosedError: qDebug() << ("The connection was closed by the peer. "); break; default: qDebug() << "The following error occurred:" << socket.errorString(); } }); connect(&socket, &QLocalSocket::disconnected, this, [this]() { qDebug() << "disconnected from server, should exit"; m_status = Succeeded; exit(); }); socket.connectToServer(server); } void FileLinkApp::runLink() { std::error_code os_err; qDebug() << "creating links"; for (auto link : m_links_to_make) { QString src_path = link.src; QString dst_path = link.dst; FS::ensureFilePathExists(dst_path); if (m_useHardLinks) { qDebug() << "making hard link:" << src_path << "to" << dst_path; fs::create_hard_link(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), os_err); } else if (fs::is_directory(StringUtils::toStdString(src_path))) { qDebug() << "making directory_symlink:" << src_path << "to" << dst_path; fs::create_directory_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), os_err); } else { qDebug() << "making symlink:" << src_path << "to" << dst_path; fs::create_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), os_err); } if (os_err) { qWarning() << "Failed to link files:" << QString::fromStdString(os_err.message()); qDebug() << "Source file:" << src_path; qDebug() << "Destination file:" << dst_path; qDebug() << "Error category:" << os_err.category().name(); qDebug() << "Error code:" << os_err.value(); FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() }; m_path_results.append(result); } else { FS::LinkResult result = { src_path, dst_path, "", 0 }; m_path_results.append(result); } } sendResults(); qDebug() << "done, should exit soon"; } void FileLinkApp::sendResults() { // construct block of data to send QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); qint32 blocksize = quint32(sizeof(quint32)); for (auto result : m_path_results) { blocksize += quint32(result.src.size()); blocksize += quint32(result.dst.size()); blocksize += quint32(result.err_msg.size()); blocksize += quint32(sizeof(quint32)); } qDebug() << "About to write block of size:" << blocksize; out << blocksize; out << quint32(m_path_results.length()); for (auto result : m_path_results) { out << result.src; out << result.dst; out << result.err_msg; out << quint32(result.err_value); } qint64 byteswritten = socket.write(block); bool bytesflushed = socket.flush(); qDebug() << "block flushed" << byteswritten << bytesflushed; } void FileLinkApp::readPathPairs() { m_links_to_make.clear(); qDebug() << "Reading path pairs from server"; qDebug() << "bytes available" << socket.bytesAvailable(); if (blockSize == 0) { // Relies on the fact that QDataStream serializes a quint32 into // sizeof(quint32) bytes if (socket.bytesAvailable() < (int)sizeof(quint32)) return; qDebug() << "reading block size"; in >> blockSize; } qDebug() << "blocksize is" << blockSize; qDebug() << "bytes available" << socket.bytesAvailable(); if (socket.bytesAvailable() < blockSize || in.atEnd()) return; quint32 numLinks; in >> numLinks; qDebug() << "numLinks" << numLinks; for (quint32 i = 0; i < numLinks; i++) { FS::LinkPair pair; in >> pair.src; in >> pair.dst; qDebug() << "link" << pair.src << "to" << pair.dst; m_links_to_make.append(pair); } runLink(); } FileLinkApp::~FileLinkApp() { qDebug() << "link program shutting down"; // Shut down logger by setting the logger function to nothing qInstallMessageHandler(nullptr); } PrismLauncher-11.0.3/launcher/DataMigrationTask.cpp0000644000175100017510000000567615224505336021654 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu // // SPDX-License-Identifier: GPL-3.0-only #include "DataMigrationTask.h" #include "FileSystem.h" #include #include #include #include DataMigrationTask::DataMigrationTask(const QString& sourcePath, const QString& targetPath, Filter pathMatcher) : Task(), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath) { m_copy.matcher(m_pathMatcher).whitelist(true); } void DataMigrationTask::executeTask() { setStatus(tr("Scanning files...")); // 1. Scan // Check how many files we gotta copy m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { return m_copy(true); // dry run to collect amount of files }); connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::dryRunFinished); connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::dryRunAborted); m_copyFutureWatcher.setFuture(m_copyFuture); } void DataMigrationTask::dryRunFinished() { disconnect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::dryRunFinished); disconnect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::dryRunAborted); if (!m_copyFuture.isValid() || !m_copyFuture.result()) { emitFailed(tr("Failed to scan source path.")); return; } // 2. Copy // Actually copy all files now. m_toCopy = m_copy.totalCopied(); connect(&m_copy, &FS::copy::fileCopied, [&, this](const QString& relativeName) { QString shortenedName = relativeName; // shorten the filename to hopefully fit into one line if (shortenedName.length() > 50) shortenedName = relativeName.left(20) + "…" + relativeName.right(29); setProgress(m_copy.totalCopied(), m_toCopy); setStatus(tr("Copying %1…").arg(shortenedName)); }); m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { return m_copy(false); // actually copy now }); connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::copyFinished); connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::copyAborted); m_copyFutureWatcher.setFuture(m_copyFuture); } void DataMigrationTask::dryRunAborted() { emitAborted(); } void DataMigrationTask::copyFinished() { disconnect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &DataMigrationTask::copyFinished); disconnect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &DataMigrationTask::copyAborted); if (!m_copyFuture.isValid() || !m_copyFuture.result()) { emitFailed(tr("Some paths could not be copied!")); return; } emitSucceeded(); } void DataMigrationTask::copyAborted() { emitAborted(); } PrismLauncher-11.0.3/launcher/SysInfo.cpp0000644000175100017510000000713315224505336017666 0ustar runnerrunner // SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 r58Playz * Copyright (C) 2024 timoreo * Copyright (C) 2024 Trial97 * Copyright (C) 2025 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "HardwareInfo.h" #ifdef Q_OS_MACOS #include bool rosettaDetect() { int ret = 0; size_t size = sizeof(ret); if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) == -1) { return false; } return ret == 1; } #endif namespace SysInfo { QString currentSystem() { #if defined(Q_OS_LINUX) return "linux"; #elif defined(Q_OS_MACOS) return "osx"; #elif defined(Q_OS_WINDOWS) return "windows"; #elif defined(Q_OS_FREEBSD) return "freebsd"; #elif defined(Q_OS_OPENBSD) return "openbsd"; #else return "unknown"; #endif } QString useQTForArch() { #if defined(Q_OS_MACOS) && !defined(Q_PROCESSOR_ARM) if (rosettaDetect()) { return "arm64"; } else { return "x86_64"; } #endif return QSysInfo::currentCpuArchitecture(); } int defaultMaxJvmMem() { // If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB if (const uint64_t totalRAM = HardwareInfo::totalRamMiB(); totalRAM < (4096 * 1.5)) return totalRAM / 1.5; else return 4096; } QString getSupportedJavaArchitecture() { auto sys = currentSystem(); auto arch = useQTForArch(); if (sys == "windows") { if (arch == "x86_64") return "windows-x64"; if (arch == "i386") return "windows-x86"; // Unknown, maybe arm, appending arch return "windows-" + arch; } if (sys == "osx") { if (arch == "arm64") return "mac-os-arm64"; if (arch.contains("64")) return "mac-os-x64"; if (arch.contains("86")) return "mac-os-x86"; // Unknown, maybe something new, appending arch return "mac-os-" + arch; } else if (sys == "linux") { if (arch == "x86_64") return "linux-x64"; if (arch == "i386") return "linux-x86"; // will work for arm32 arm(64) return "linux-" + arch; } return {}; } } // namespace SysInfo PrismLauncher-11.0.3/launcher/SysInfo.h0000644000175100017510000000032315224505336017325 0ustar runnerrunner#pragma once #include #include namespace SysInfo { QString currentSystem(); QString useQTForArch(); QString getSupportedJavaArchitecture(); int defaultMaxJvmMem(); } // namespace SysInfo PrismLauncher-11.0.3/launcher/FileSystem.cpp0000644000175100017510000015650215224505336020365 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 TheKodeToad * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FileSystem.h" #include #include #include "BuildConfig.h" #include #include #include #include #include #include #include #include #include #include #include #include "DesktopServices.h" #include "PSaveFile.h" #include "StringUtils.h" #if defined Q_OS_WIN32 #define NOMINMAX #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include // for ShellExecute #include #include #include #else #include #endif #include namespace fs = std::filesystem; // clone #if defined(Q_OS_LINUX) #include #include /* Definition of FICLONE* constants */ #include #include #include #elif defined(Q_OS_MACOS) #include #include #elif defined(Q_OS_WIN) // winbtrfs clone vs rundll32 shellbtrfs.dll,ReflinkCopy #include #include #include #include // refs #include #if defined(__MINGW32__) #include #endif #endif #if defined(Q_OS_WIN) #if defined(__MINGW32__) // Avoid re-defining structs retroactively added to MinGW // https://github.com/mingw-w64/mingw-w64/issues/90#issuecomment-2829284729 #if __MINGW64_VERSION_MAJOR < 13 struct _DUPLICATE_EXTENTS_DATA { HANDLE FileHandle; LARGE_INTEGER SourceFileOffset; LARGE_INTEGER TargetFileOffset; LARGE_INTEGER ByteCount; }; using DUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA; using PDUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA*; #endif struct _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER { WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32 WORD Reserved; // Must be 0 DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx DWORD ChecksumChunkSizeInBytes; DWORD ClusterSizeInBytes; }; using FSCTL_GET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER; using PFSCTL_GET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER*; struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER { WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32 WORD Reserved; // Must be 0 DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx }; using FSCTL_SET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER; using PFSCTL_SET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER*; #endif #ifndef FSCTL_DUPLICATE_EXTENTS_TO_FILE #define FSCTL_DUPLICATE_EXTENTS_TO_FILE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 209, METHOD_BUFFERED, FILE_WRITE_DATA) #endif #ifndef FSCTL_GET_INTEGRITY_INFORMATION #define FSCTL_GET_INTEGRITY_INFORMATION \ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 159, METHOD_BUFFERED, FILE_ANY_ACCESS) // FSCTL_GET_INTEGRITY_INFORMATION_BUFFER #endif #ifndef FSCTL_SET_INTEGRITY_INFORMATION #define FSCTL_SET_INTEGRITY_INFORMATION \ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 160, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) // FSCTL_SET_INTEGRITY_INFORMATION_BUFFER #endif #ifndef ERROR_NOT_CAPABLE #define ERROR_NOT_CAPABLE 775L #endif #ifndef ERROR_BLOCK_TOO_MANY_REFERENCES #define ERROR_BLOCK_TOO_MANY_REFERENCES 347L #endif #endif namespace FS { void ensureExists(const QDir& dir) { if (!QDir().mkpath(dir.absolutePath())) { throw FileSystemException("Unable to create folder " + dir.dirName() + " (" + dir.absolutePath() + ")"); } } void write(const QString& filename, const QByteArray& data) { ensureExists(QFileInfo(filename).dir()); PSaveFile file(filename); if (!file.open(PSaveFile::WriteOnly)) { throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString()); } if (data.size() != file.write(data)) { throw FileSystemException("Error writing data to " + filename + ": " + file.errorString()); } if (!file.commit()) { throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString()); } } void appendSafe(const QString& filename, const QByteArray& data) { ensureExists(QFileInfo(filename).dir()); QByteArray buffer; try { buffer = read(filename); } catch (FileSystemException&) { buffer = QByteArray(); } buffer.append(data); PSaveFile file(filename); if (!file.open(PSaveFile::WriteOnly)) { throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString()); } if (buffer.size() != file.write(buffer)) { throw FileSystemException("Error writing data to " + filename + ": " + file.errorString()); } if (!file.commit()) { throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString()); } } void append(const QString& filename, const QByteArray& data) { ensureExists(QFileInfo(filename).dir()); QFile file(filename); if (!file.open(QFile::Append)) { throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString()); } if (data.size() != file.write(data)) { throw FileSystemException("Error writing data to " + filename + ": " + file.errorString()); } } QByteArray read(const QString& filename) { QFile file(filename); if (!file.open(QFile::ReadOnly)) { throw FileSystemException("Unable to open " + filename + " for reading: " + file.errorString()); } const qint64 size = file.size(); QByteArray data(int(size), 0); const qint64 ret = file.read(data.data(), size); if (ret == -1 || ret != size) { throw FileSystemException("Error reading data from " + filename + ": " + file.errorString()); } return data; } bool updateTimestamp(const QString& filename) { #ifdef Q_OS_WIN32 std::wstring filename_utf_16 = filename.toStdWString(); return (_wutime64(filename_utf_16.c_str(), nullptr) == 0); #else QByteArray filenameBA = QFile::encodeName(filename); return (utime(filenameBA.data(), nullptr) == 0); #endif } bool ensureFilePathExists(QString filenamepath) { QFileInfo a(filenamepath); QDir dir; QString ensuredPath = a.path(); bool success = dir.mkpath(ensuredPath); return success; } bool ensureFolderPathExists(const QFileInfo folderPath) { QDir dir; QString ensuredPath = folderPath.filePath(); if (folderPath.exists()) return true; bool success = dir.mkpath(ensuredPath); return success; } bool ensureFolderPathExists(const QString folderPathName) { return ensureFolderPathExists(QFileInfo(folderPathName)); } bool copyFileAttributes(QString src, QString dst) { #ifdef Q_OS_WIN32 auto attrs = GetFileAttributesW(src.toStdWString().c_str()); if (attrs == INVALID_FILE_ATTRIBUTES) return false; return SetFileAttributesW(dst.toStdWString().c_str(), attrs); #else Q_UNUSED(src); Q_UNUSED(dst); #endif return true; } // needs folders to exists void copyFolderAttributes(QString src, QString dst, QString relative) { auto path = PathCombine(src, relative); QDir dsrc(src); while ((path = QFileInfo(path).path()).length() >= src.length()) { auto dst_path = PathCombine(dst, dsrc.relativeFilePath(path)); copyFileAttributes(path, dst_path); } } /** * @brief Copies a directory and it's contents from src to dest * @param offset subdirectory form src to copy to dest * @return if there was an error during the filecopy */ bool copy::operator()(const QString& offset, bool dryRun) { using copy_opts = fs::copy_options; m_copied = 0; // reset counter m_failedPaths.clear(); // NOTE always deep copy on windows. the alternatives are too messy. #if defined Q_OS_WIN32 m_followSymlinks = true; #endif auto src = PathCombine(m_src.absolutePath(), offset); auto dst = PathCombine(m_dst.absolutePath(), offset); std::error_code err; fs::copy_options opt = copy_opts::none; // The default behavior is to follow symlinks if (!m_followSymlinks) opt |= copy_opts::copy_symlinks; if (m_overwrite) opt |= copy_opts::overwrite_existing; // Function that'll do the actual copying auto copy_file = [this, dryRun, src, dst, opt, &err](QString src_path, QString relative_dst_path) { if (m_matcher && (m_matcher(relative_dst_path) != m_whitelist)) return; auto dst_path = PathCombine(dst, relative_dst_path); if (!dryRun) { ensureFilePathExists(dst_path); #ifdef Q_OS_WIN32 copyFolderAttributes(src, dst, relative_dst_path); #endif fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err); } if (err) { qWarning() << "Failed to copy files:" << QString::fromStdString(err.message()); qDebug() << "Source file:" << src_path; qDebug() << "Destination file:" << dst_path; m_failedPaths.append(dst_path); emit copyFailed(relative_dst_path); return; } m_copied++; emit fileCopied(relative_dst_path); }; // We can't use copy_opts::recursive because we need to take into account the // blacklisted paths, so we iterate over the source directory, and if there's no blacklist // match, we copy the file. QDir src_dir(src); QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories); while (source_it.hasNext()) { auto src_path = source_it.next(); auto relative_path = src_dir.relativeFilePath(src_path); copy_file(src_path, relative_path); } // If the root src is not a directory, the previous iterator won't run. if (!fs::is_directory(StringUtils::toStdString(src))) copy_file(src, ""); return err.value() == 0; } /// qDebug print support for the LinkPair struct QDebug operator<<(QDebug debug, const LinkPair& lp) { QDebugStateSaver saver(debug); debug.nospace() << "LinkPair{ src: " << lp.src << " , dst: " << lp.dst << " }"; return debug; } bool create_link::operator()(const QString& offset, bool dryRun) { m_linked = 0; // reset counter m_path_results.clear(); m_links_to_make.clear(); m_path_results.clear(); make_link_list(offset); if (!dryRun) return make_links(); return true; } /** * @brief Make a list of all the links to make * @param offset subdirectory of src to link to dest */ void create_link::make_link_list(const QString& offset) { for (auto pair : m_path_pairs) { const QString& srcPath = pair.src; const QString& dstPath = pair.dst; auto src = PathCombine(QDir(srcPath).absolutePath(), offset); auto dst = PathCombine(QDir(dstPath).absolutePath(), offset); // you can't hard link a directory so make sure if we deal with a directory we do so recursively if (m_useHardLinks) m_recursive = true; // Function that'll do the actual linking auto link_file = [this, dst](QString src_path, QString relative_dst_path) { if (m_matcher && (m_matcher(relative_dst_path) != m_whitelist)) { qDebug() << "path" << relative_dst_path << "in black list or not in whitelist"; return; } auto dst_path = PathCombine(dst, relative_dst_path); LinkPair link = { src_path, dst_path }; m_links_to_make.append(link); }; if ((!m_recursive) || !fs::is_directory(StringUtils::toStdString(src))) { if (m_debug) qDebug() << "linking single file or dir:" << src << "to" << dst; link_file(src, ""); } else { if (m_debug) qDebug().nospace() << "linking recursively: " << src << " to " << dst << ", max_depth: " << m_max_depth; QDir src_dir(src); QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories); QStringList linkedPaths; while (source_it.hasNext()) { auto src_path = source_it.next(); auto relative_path = src_dir.relativeFilePath(src_path); if (m_max_depth >= 0 && pathDepth(relative_path) > m_max_depth) { relative_path = pathTruncate(relative_path, m_max_depth); src_path = src_dir.filePath(relative_path); if (linkedPaths.contains(src_path)) { continue; } } linkedPaths.append(src_path); link_file(src_path, relative_path); } } } } bool create_link::make_links() { for (auto link : m_links_to_make) { QString src_path = link.src; QString dst_path = link.dst; auto src_path_std = StringUtils::toStdString(link.src); auto dst_path_std = StringUtils::toStdString(link.dst); ensureFilePathExists(dst_path); if (m_useHardLinks) { if (m_debug) qDebug() << "making hard link:" << src_path << "to" << dst_path; fs::create_hard_link(src_path_std, dst_path_std, m_os_err); } else if (fs::is_directory(src_path_std)) { if (m_debug) qDebug() << "making directory_symlink:" << src_path << "to" << dst_path; fs::create_directory_symlink(src_path_std, dst_path_std, m_os_err); } else { if (m_debug) qDebug() << "making symlink:" << src_path << "to" << dst_path; fs::create_symlink(src_path_std, dst_path_std, m_os_err); } if (m_os_err) { qWarning() << "Failed to link files:" << QString::fromStdString(m_os_err.message()); qDebug() << "Source file:" << src_path; qDebug() << "Destination file:" << dst_path; qDebug() << "Error category:" << m_os_err.category().name(); qDebug() << "Error code:" << m_os_err.value(); emit linkFailed(src_path, dst_path, QString::fromStdString(m_os_err.message()), m_os_err.value()); } else { m_linked++; emit fileLinked(src_path, dst_path); } if (m_os_err) return false; } return true; } void create_link::runPrivileged(const QString& offset) { m_linked = 0; // reset counter m_path_results.clear(); m_links_to_make.clear(); bool gotResults = false; make_link_list(offset); QString serverName = BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink_server" + StringUtils::getRandomAlphaNumeric(); connect(&m_linkServer, &QLocalServer::newConnection, this, [this, &gotResults]() { qDebug() << "Client connected, sending out pairs"; // construct block of data to send QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); qint32 blocksize = quint32(sizeof(quint32)); for (auto link : m_links_to_make) { blocksize += quint32(link.src.size()); blocksize += quint32(link.dst.size()); } qDebug() << "About to write block of size:" << blocksize; out << blocksize; out << quint32(m_links_to_make.length()); for (auto link : m_links_to_make) { out << link.src; out << link.dst; } QLocalSocket* clientConnection = m_linkServer.nextPendingConnection(); connect(clientConnection, &QLocalSocket::disconnected, clientConnection, &QLocalSocket::deleteLater); connect(clientConnection, &QLocalSocket::readyRead, this, [&, clientConnection]() { QDataStream in; quint32 blockSize = 0; in.setDevice(clientConnection); qDebug() << "Reading path results from client"; qDebug() << "bytes available" << clientConnection->bytesAvailable(); // Relies on the fact that QDataStream serializes a quint32 into // sizeof(quint32) bytes if (clientConnection->bytesAvailable() < (int)sizeof(quint32)) return; qDebug() << "reading block size"; in >> blockSize; qDebug() << "blocksize is" << blockSize; qDebug() << "bytes available" << clientConnection->bytesAvailable(); if (clientConnection->bytesAvailable() < blockSize || in.atEnd()) return; quint32 numResults; in >> numResults; qDebug() << "numResults" << numResults; for (quint32 i = 0; i < numResults; i++) { FS::LinkResult result; in >> result.src; in >> result.dst; in >> result.err_msg; qint32 err_value; in >> err_value; result.err_value = err_value; if (result.err_value) { qDebug() << "privileged link fail" << result.src << "to" << result.dst << "code" << result.err_value << result.err_msg; emit linkFailed(result.src, result.dst, result.err_msg, result.err_value); } else { qDebug() << "privileged link success" << result.src << "to" << result.dst; m_linked++; emit fileLinked(result.src, result.dst); } m_path_results.append(result); } gotResults = true; qDebug() << "results received, closing connection"; clientConnection->close(); }); qint64 byteswritten = clientConnection->write(block); bool bytesflushed = clientConnection->flush(); qDebug() << "block flushed" << byteswritten << bytesflushed; }); qDebug() << "Listening on pipe" << serverName; if (!m_linkServer.listen(serverName)) { qDebug() << "Unable to start local pipe server on" << serverName << ":" << m_linkServer.errorString(); return; } ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this); connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [this, &gotResults]() { emit finishedPrivileged(gotResults); }); connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater); linkFileProcess->start(); } void ExternalLinkFileProcess::runLinkFile() { QString fileLinkExe = PathCombine(QCoreApplication::instance()->applicationDirPath(), BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink"); QString params = "-s " + m_server; params += " -H " + QVariant(m_useHardLinks).toString(); #if defined Q_OS_WIN32 SHELLEXECUTEINFO ShExecInfo; fileLinkExe = fileLinkExe + ".exe"; qDebug() << "Running: runas" << fileLinkExe << params; LPCWSTR programNameWin = (const wchar_t*)fileLinkExe.utf16(); LPCWSTR paramsWin = (const wchar_t*)params.utf16(); // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-shellexecuteinfoa ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; ShExecInfo.hwnd = NULL; // Optional. A handle to the owner window, used to display and position any UI that the system might produce // while executing this function. ShExecInfo.lpVerb = L"runas"; // elevate to admin, show UAC ShExecInfo.lpFile = programNameWin; ShExecInfo.lpParameters = paramsWin; ShExecInfo.lpDirectory = NULL; ShExecInfo.nShow = SW_HIDE; ShExecInfo.hInstApp = NULL; ShellExecuteEx(&ShExecInfo); WaitForSingleObject(ShExecInfo.hProcess, INFINITE); CloseHandle(ShExecInfo.hProcess); #endif qDebug() << "Process exited"; } bool moveByCopy(const QString& source, const QString& dest) { if (!copy(source, dest)()) { // copy qDebug() << "Copy of" << source << "to" << dest << "failed!"; return false; } if (!deletePath(source)) { // remove original qDebug() << "Deletion of" << source << "failed!"; return false; }; return true; } bool move(const QString& source, const QString& dest) { std::error_code err; ensureFilePathExists(dest); fs::rename(StringUtils::toStdString(source), StringUtils::toStdString(dest), err); if (err.value() != 0) { if (moveByCopy(source, dest)) return true; qDebug() << "Move of" << source << "to" << dest << "failed!"; qWarning() << "Failed to move file:" << QString::fromStdString(err.message()) << QString::number(err.value()); return false; } return true; } bool deletePath(QString path) { std::error_code err; fs::remove_all(StringUtils::toStdString(path), err); if (err) { qWarning() << "Failed to remove files:" << QString::fromStdString(err.message()); } return err.value() == 0; } bool deleteContents(const QString& path) { const QFileInfo info(path); if (!info.exists()) { return true; } if (!info.isDir()) { qWarning() << "Attempted to delete contents of non-directory path:" << path; return false; } bool ret = true; for (const auto& entry : fs::directory_iterator(StringUtils::toStdString(path))) { std::error_code err; fs::remove_all(entry.path(), err); if (err.value() != 0) { qWarning().nospace() << "Could not delete directory entry " << entry.path() << ": " << QString::fromStdString(err.message()); ret = false; } } return ret; } bool trash(QString path, QString* pathInTrash) { // FIXME: Figure out trash in Flatpak. Qt seemingly doesn't use the Trash portal if (DesktopServices::isFlatpak()) return false; #if defined Q_OS_WIN32 if (IsWindowsServer()) return false; #endif return QFile::moveToTrash(path, pathInTrash); } QString PathCombine(const QString& path1, const QString& path2) { if (!path1.size()) return path2; if (!path2.size()) return path1; return QDir::cleanPath(path1 + QDir::separator() + path2); } QString PathCombine(const QString& path1, const QString& path2, const QString& path3) { return PathCombine(PathCombine(path1, path2), path3); } QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4) { return PathCombine(PathCombine(path1, path2, path3), path4); } QString AbsolutePath(const QString& path) { return QFileInfo(path).absolutePath(); } int pathDepth(const QString& path) { if (path.isEmpty()) return 0; QFileInfo info(path); auto parts = QDir::toNativeSeparators(info.path()).split(QDir::separator(), Qt::SkipEmptyParts); int numParts = parts.length(); numParts -= parts.count("."); numParts -= parts.count("..") * 2; return numParts; } QString pathTruncate(const QString& path, int depth) { if (path.isEmpty() || (depth < 0)) return ""; QString trunc = QFileInfo(path).path(); if (pathDepth(trunc) > depth) { return pathTruncate(trunc, depth); } auto parts = QDir::toNativeSeparators(trunc).split(QDir::separator(), Qt::SkipEmptyParts); if (parts.startsWith(".") && !path.startsWith(".")) { parts.removeFirst(); } if (QDir::toNativeSeparators(path).startsWith(QDir::separator())) { parts.prepend(""); } trunc = parts.join(QDir::separator()); return trunc; } QString ResolveExecutable(QString path) { if (path.isEmpty()) { return QString(); } if (!path.contains('/')) { path = QStandardPaths::findExecutable(path); } QFileInfo pathInfo(path); if (!pathInfo.exists() || !pathInfo.isExecutable()) { return QString(); } return pathInfo.absoluteFilePath(); } /** * Normalize path * * Any paths inside the current folder will be normalized to relative paths (to current) * Other paths will be made absolute */ QString NormalizePath(QString path) { QDir a = QDir::currentPath(); QString currentAbsolute = a.absolutePath(); QDir b(path); QString newAbsolute = b.absolutePath(); if (newAbsolute.startsWith(currentAbsolute)) { return a.relativeFilePath(newAbsolute); } else { return newAbsolute; } } namespace { const QString g_badChars = "<>:\"|?*\r\n!"; QString removeChars(QString source, QChar replace, const QString& extraChars = "") { auto badChars = g_badChars; if (!extraChars.isEmpty()) { badChars += extraChars; } for (auto& c : source) { if (c.unicode() < 0x20 || !c.isPrint() || badChars.contains(c)) { c = replace; } } return source; } } // namespace QString RemoveInvalidFilenameChars(QString string, QChar replaceWith) { return removeChars(std::move(string), replaceWith, "\\/"); } QString RemoveInvalidPathChars(QString string, QChar replaceWith) { return removeChars(std::move(string), replaceWith); } QString DirNameFromString(QString string, QString inDir) { int num = 0; QString baseName = RemoveInvalidFilenameChars(string, '-'); QString dirName; do { if (num == 0) { dirName = baseName; } else { dirName = baseName + "(" + QString::number(num) + ")"; } // If it's over 9000 if (num > 9000) return ""; num++; } while (QFileInfo(PathCombine(inDir, dirName)).exists()); return dirName; } // Does the folder path contain any '!'? If yes, return true, otherwise false. // (This is a problem for Java) bool checkProblemticPathJava(QDir folder) { QString pathfoldername = folder.absolutePath(); return pathfoldername.contains("!", Qt::CaseInsensitive); } QString getDesktopDir() { return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); } QString getApplicationsDir() { return QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); } QString quoteArgs(const QStringList& args, const QString& wrap, const QString& escapeChar, bool wrapOnlyIfNeeded = false) { QString result; auto size = args.size(); for (int i = 0; i < size; ++i) { QString arg = args[i]; arg.replace(wrap, escapeChar); bool needsWrapping = !wrapOnlyIfNeeded || arg.contains(' ') || arg.contains('\t') || arg.contains(wrap); if (needsWrapping) result += wrap + arg + wrap; else result += arg; if (i < size - 1) result += ' '; } return result; } // Cross-platform Shortcut creation QString createShortcut(QString destination, QString target, QStringList args, QString name, QString icon) { if (destination.isEmpty()) { destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name)); } if (!ensureFilePathExists(destination)) { qWarning() << "Destination path can't be created!"; return QString(); } #if defined(Q_OS_MACOS) QDir application = destination + ".app/"; if (application.exists()) { qWarning() << "Application already exists!"; return QString(); } if (!application.mkpath(".")) { qWarning() << "Couldn't create application"; return QString(); } QDir content = application.path() + "/Contents/"; QDir resources = content.path() + "/Resources/"; QDir binaryDir = content.path() + "/MacOS/"; QFile info(content.path() + "/Info.plist"); if (!(content.mkpath(".") && resources.mkpath(".") && binaryDir.mkpath("."))) { qWarning() << "Couldn't create directories within application"; return QString(); } if (!info.open(QIODevice::WriteOnly | QIODevice::Text)) { qWarning() << "Failed to open file" << info.fileName() << "for writing:" << info.errorString(); return QString(); } QFile(icon).rename(resources.path() + "/Icon.icns"); // Create the Command file QString exec = binaryDir.path() + "/Run.command"; QFile f(exec); if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { qWarning() << "Failed to open file" << f.fileName() << "for writing:" << f.errorString(); return QString(); } QTextStream stream(&f); auto argstring = quoteArgs(args, "\"", "\\\""); stream << "#!/bin/bash" << "\n"; stream << "\"" << target << "\" " << argstring << "\n"; stream.flush(); f.close(); f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther); // Generate the Info.plist QTextStream infoStream(&info); infoStream << " \n" "" "\n" "\n" " CFBundleExecutable\n" " Run.command\n" // The path to the executable " CFBundleIconFile\n" " Icon.icns\n" " CFBundleName\n" " " << name << "\n" // Name of the application " CFBundlePackageType\n" " APPL\n" " CFBundleShortVersionString\n" " 1.0\n" " CFBundleVersion\n" " 1.0\n" "\n" ""; return application.path(); #elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) if (!destination.endsWith(".desktop")) // in case of isFlatpak destination is already populated destination += ".desktop"; QFile f(destination); if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { qWarning() << "Failed to open file" << f.fileName() << "for writing:" << f.errorString(); return QString(); } QTextStream stream(&f); auto argstring = quoteArgs(args, "'", "'\\''"); stream << "[Desktop Entry]" << "\n"; stream << "Type=Application" << "\n"; stream << "Categories=Game;ActionGame;AdventureGame;Simulation" << "\n"; stream << "Exec=\"" << target.toLocal8Bit() << "\" " << argstring.toLocal8Bit() << "\n"; stream << "Name=" << name.toLocal8Bit() << "\n"; if (!icon.isEmpty()) { stream << "Icon=" << icon.toLocal8Bit() << "\n"; } stream.flush(); f.close(); f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther); return destination; #elif defined(Q_OS_WIN) QFileInfo targetInfo(target); if (!targetInfo.exists()) { qWarning() << "Target file does not exist!"; return QString(); } target = targetInfo.absoluteFilePath(); if (target.length() >= MAX_PATH) { qWarning() << "Target file path is too long!"; return QString(); } if (!icon.isEmpty() && icon.length() >= MAX_PATH) { qWarning() << "Icon path is too long!"; return QString(); } destination += ".lnk"; if (destination.length() >= MAX_PATH) { qWarning() << "Destination path is too long!"; return QString(); } auto argStr = quoteArgs(args, "\"", "\\\"", true); if (argStr.length() >= MAX_PATH) { qWarning() << "Arguments string is too long!"; return QString(); } HRESULT hres; // ...yes, you need to initialize the entire COM stack just to make a shortcut hres = CoInitialize(nullptr); if (FAILED(hres)) { qWarning() << "Failed to initialize COM!"; return QString(); } WCHAR wsz[MAX_PATH]; IShellLink* psl; // create an IShellLink instance - this stores the shortcut's attributes hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); if (SUCCEEDED(hres)) { wmemset(wsz, 0, MAX_PATH); target.toWCharArray(wsz); psl->SetPath(wsz); wmemset(wsz, 0, MAX_PATH); argStr.toWCharArray(wsz); psl->SetArguments(wsz); wmemset(wsz, 0, MAX_PATH); targetInfo.absolutePath().toWCharArray(wsz); psl->SetWorkingDirectory(wsz); // "Starts in" attribute if (!icon.isEmpty()) { wmemset(wsz, 0, MAX_PATH); icon.toWCharArray(wsz); psl->SetIconLocation(wsz, 0); } // query an IPersistFile interface from our IShellLink instance // this is the interface that will actually let us save the shortcut to disk! IPersistFile* ppf; hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); if (SUCCEEDED(hres)) { wmemset(wsz, 0, MAX_PATH); destination.toWCharArray(wsz); hres = ppf->Save(wsz, TRUE); if (FAILED(hres)) { qWarning() << "IPresistFile->Save() failed"; qWarning() << "hres =" << hres; } ppf->Release(); } else { qWarning() << "Failed to query IPersistFile interface from IShellLink instance"; qWarning() << "hres =" << hres; } psl->Release(); } else { qWarning() << "Failed to create IShellLink instance"; qWarning() << "hres =" << hres; } // go away COM, nobody likes you CoUninitialize(); if (SUCCEEDED(hres)) return destination; return QString(); #else qWarning("Desktop Shortcuts not supported on your platform!"); return QString(); #endif } bool overrideFolder(QString overwritten_path, QString override_path) { using copy_opts = fs::copy_options; if (!FS::ensureFolderPathExists(overwritten_path)) return false; std::error_code err; fs::copy_options opt = copy_opts::recursive | copy_opts::overwrite_existing; // FIXME: hello traveller! Apparently std::copy does NOT overwrite existing files on GNU libstdc++ on Windows? fs::copy(StringUtils::toStdString(override_path), StringUtils::toStdString(overwritten_path), opt, err); if (err) { qCritical() << QString("Failed to apply override from %1 to %2").arg(override_path, overwritten_path); qCritical() << "Reason:" << QString::fromStdString(err.message()); } return err.value() == 0; } QString getFilesystemTypeName(FilesystemType type) { auto iter = s_filesystem_type_names.constFind(type); if (iter != s_filesystem_type_names.constEnd()) { return iter.value().constFirst(); } return getFilesystemTypeName(FilesystemType::UNKNOWN); } FilesystemType getFilesystemTypeFuzzy(const QString& name) { for (auto iter = s_filesystem_type_names.constBegin(); iter != s_filesystem_type_names.constEnd(); ++iter) { auto fs_names = iter.value(); for (auto fs_name : fs_names) { if (name.toUpper().contains(fs_name.toUpper())) return iter.key(); } } return FilesystemType::UNKNOWN; } FilesystemType getFilesystemType(const QString& name) { for (auto iter = s_filesystem_type_names.constBegin(); iter != s_filesystem_type_names.constEnd(); ++iter) { auto fs_names = iter.value(); if (fs_names.contains(name.toUpper())) return iter.key(); } return FilesystemType::UNKNOWN; } /** * @brief path to the near ancestor that exists * */ QString nearestExistentAncestor(const QString& path) { if (QFileInfo::exists(path)) return path; QDir dir(path); if (!dir.makeAbsolute()) return {}; do { dir.setPath(QDir::cleanPath(dir.filePath(QStringLiteral("..")))); } while (!dir.exists() && !dir.isRoot()); return dir.exists() ? dir.path() : QString(); } /** * @brief colect information about the filesystem under a file * */ FilesystemInfo statFS(const QString& path) { FilesystemInfo info; QStorageInfo storage_info(nearestExistentAncestor(path)); info.fsTypeName = storage_info.fileSystemType(); info.fsType = getFilesystemTypeFuzzy(info.fsTypeName); info.blockSize = storage_info.blockSize(); info.bytesAvailable = storage_info.bytesAvailable(); info.bytesFree = storage_info.bytesFree(); info.bytesTotal = storage_info.bytesTotal(); info.name = storage_info.name(); info.rootPath = storage_info.rootPath(); return info; } /** * @brief if the Filesystem is reflink/clone capable * */ bool canCloneOnFS(const QString& path) { FilesystemInfo info = statFS(path); return canCloneOnFS(info); } bool canCloneOnFS(const FilesystemInfo& info) { return canCloneOnFS(info.fsType); } bool canCloneOnFS(FilesystemType type) { return s_clone_filesystems.contains(type); } /** * @brief if the Filesystem is reflink/clone capable and both paths are on the same device * */ bool canClone(const QString& src, const QString& dst) { auto srcVInfo = statFS(src); auto dstVInfo = statFS(dst); bool sameDevice = srcVInfo.rootPath == dstVInfo.rootPath; return sameDevice && canCloneOnFS(srcVInfo) && canCloneOnFS(dstVInfo); } /** * @brief reflink/clones a directory and it's contents from src to dest * @param offset subdirectory form src to copy to dest * @return if there was an error during the filecopy */ bool clone::operator()(const QString& offset, bool dryRun) { if (!canClone(m_src.absolutePath(), m_dst.absolutePath())) { qWarning() << "Can not clone: not same device or not clone/reflink filesystem"; qDebug() << "Source path:" << m_src.absolutePath(); qDebug() << "Destination path:" << m_dst.absolutePath(); emit cloneFailed(m_src.absolutePath(), m_dst.absolutePath()); return false; } m_cloned = 0; // reset counter m_failedClones.clear(); auto src = PathCombine(m_src.absolutePath(), offset); auto dst = PathCombine(m_dst.absolutePath(), offset); std::error_code err; // Function that'll do the actual cloneing auto cloneFile = [this, dryRun, dst, &err](QString src_path, QString relative_dst_path) { if (m_matcher && (m_matcher(relative_dst_path) != m_whitelist)) return; auto dst_path = PathCombine(dst, relative_dst_path); if (!dryRun) { ensureFilePathExists(dst_path); clone_file(src_path, dst_path, err); } if (err) { qDebug() << "Failed to clone files: error" << err.value() << "message" << QString::fromStdString(err.message()); qDebug() << "Source file:" << src_path; qDebug() << "Destination file:" << dst_path; m_failedClones.append(qMakePair(src_path, dst_path)); emit cloneFailed(src_path, dst_path); return; } m_cloned++; emit fileCloned(src_path, dst_path); }; // We can't use copy_opts::recursive because we need to take into account the // blacklisted paths, so we iterate over the source directory, and if there's no blacklist // match, we copy the file. QDir src_dir(src); QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories); while (source_it.hasNext()) { auto src_path = source_it.next(); auto relative_path = src_dir.relativeFilePath(src_path); cloneFile(src_path, relative_path); } // If the root src is not a directory, the previous iterator won't run. if (!fs::is_directory(StringUtils::toStdString(src))) cloneFile(src, ""); return err.value() == 0; } /** * @brief clone/reflink file from src to dst * */ bool clone_file(const QString& src, const QString& dst, std::error_code& ec) { auto src_path = StringUtils::toStdString(QDir::toNativeSeparators(QFileInfo(src).absoluteFilePath())); auto dst_path = StringUtils::toStdString(QDir::toNativeSeparators(QFileInfo(dst).absoluteFilePath())); FilesystemInfo srcinfo = statFS(src); FilesystemInfo dstinfo = statFS(dst); if ((srcinfo.rootPath != dstinfo.rootPath) || (srcinfo.fsType != dstinfo.fsType)) { ec = std::make_error_code(std::errc::not_supported); qWarning() << "reflink/clone must be to the same device and filesystem! src and dst root filesystems do not match."; return false; } #if defined(Q_OS_WIN) if (!win_ioctl_clone(src_path, dst_path, ec)) { qDebug() << "failed win_ioctl_clone"; qWarning() << "clone/reflink not supported on windows outside of btrfs or ReFS!"; qWarning() << "check out https://github.com/maharmstone/btrfs for btrfs support!"; return false; } #elif defined(Q_OS_LINUX) if (!linux_ficlone(src_path, dst_path, ec)) { qDebug() << "failed linux_ficlone:"; return false; } #elif defined(Q_OS_MACOS) if (!macos_bsd_clonefile(src_path, dst_path, ec)) { qDebug() << "failed macos_bsd_clonefile:"; return false; } #else qWarning() << "clone/reflink not supported! unknown OS"; ec = std::make_error_code(std::errc::not_supported); return false; #endif return true; } #if defined(Q_OS_WIN) static long RoundUpToPowerOf2(long originalValue, long roundingMultiplePowerOf2) { long mask = roundingMultiplePowerOf2 - 1; return (originalValue + mask) & ~mask; } bool win_ioctl_clone(const std::wstring& src_path, const std::wstring& dst_path, std::error_code& ec) { /** * This algorithm inspired from https://github.com/0xbadfca11/reflink * LICENSE MIT * * Additional references * https://learn.microsoft.com/en-us/windows/win32/api/winioctl/ni-winioctl-fsctl_duplicate_extents_to_file * https://github.com/microsoft/CopyOnWrite/blob/main/lib/Windows/WindowsCopyOnWriteFilesystem.cs#L94 */ HANDLE hSourceFile = CreateFileW(src_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if (hSourceFile == INVALID_HANDLE_VALUE) { ec = std::error_code(GetLastError(), std::system_category()); qDebug() << "Failed to open source file" << src_path.c_str(); return false; } ULONG fs_flags; if (!GetVolumeInformationByHandleW(hSourceFile, nullptr, 0, nullptr, nullptr, &fs_flags, nullptr, 0)) { ec = std::error_code(GetLastError(), std::system_category()); qDebug() << "Failed to get Filesystem information for" << src_path.c_str(); CloseHandle(hSourceFile); return false; } if (!(fs_flags & FILE_SUPPORTS_BLOCK_REFCOUNTING)) { SetLastError(ERROR_NOT_CAPABLE); ec = std::error_code(GetLastError(), std::system_category()); qWarning() << "Filesystem at" << src_path.c_str() << "does not support reflink"; CloseHandle(hSourceFile); return false; } FILE_END_OF_FILE_INFO sourceFileLength; if (!GetFileSizeEx(hSourceFile, &sourceFileLength.EndOfFile)) { ec = std::error_code(GetLastError(), std::system_category()); qDebug() << "Failed to size of source file" << src_path.c_str(); CloseHandle(hSourceFile); return false; } FILE_BASIC_INFO sourceFileBasicInfo; if (!GetFileInformationByHandleEx(hSourceFile, FileBasicInfo, &sourceFileBasicInfo, sizeof(sourceFileBasicInfo))) { ec = std::error_code(GetLastError(), std::system_category()); qDebug() << "Failed to source file info" << src_path.c_str(); CloseHandle(hSourceFile); return false; } ULONG junk; FSCTL_GET_INTEGRITY_INFORMATION_BUFFER sourceFileIntegrity; if (!DeviceIoControl(hSourceFile, FSCTL_GET_INTEGRITY_INFORMATION, nullptr, 0, &sourceFileIntegrity, sizeof(sourceFileIntegrity), &junk, nullptr)) { ec = std::error_code(GetLastError(), std::system_category()); qDebug() << "Failed to source file integrity info" << src_path.c_str(); CloseHandle(hSourceFile); return false; } HANDLE hDestFile = CreateFileW(dst_path.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, 0, nullptr, CREATE_NEW, 0, hSourceFile); if (hDestFile == INVALID_HANDLE_VALUE) { ec = std::error_code(GetLastError(), std::system_category()); qDebug() << "Failed to open dest file" << dst_path.c_str(); CloseHandle(hSourceFile); return false; } FILE_DISPOSITION_INFO destFileDispose = { TRUE }; if (!SetFileInformationByHandle(hDestFile, FileDispositionInfo, &destFileDispose, sizeof(destFileDispose))) { ec = std::error_code(GetLastError(), std::system_category()); qDebug() << "Failed to set dest file info" << dst_path.c_str(); CloseHandle(hSourceFile); CloseHandle(hDestFile); return false; } if (!DeviceIoControl(hDestFile, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &junk, nullptr)) { ec = std::error_code(GetLastError(), std::system_category()); qDebug() << "Failed to set dest sparseness" << dst_path.c_str(); CloseHandle(hSourceFile); CloseHandle(hDestFile); return false; } FSCTL_SET_INTEGRITY_INFORMATION_BUFFER setDestFileintegrity = { sourceFileIntegrity.ChecksumAlgorithm, sourceFileIntegrity.Reserved, sourceFileIntegrity.Flags }; if (!DeviceIoControl(hDestFile, FSCTL_SET_INTEGRITY_INFORMATION, &setDestFileintegrity, sizeof(setDestFileintegrity), nullptr, 0, nullptr, nullptr)) { ec = std::error_code(GetLastError(), std::system_category()); qDebug() << "Failed to set dest file integrity info" << dst_path.c_str(); CloseHandle(hSourceFile); CloseHandle(hDestFile); return false; } if (!SetFileInformationByHandle(hDestFile, FileEndOfFileInfo, &sourceFileLength, sizeof(sourceFileLength))) { ec = std::error_code(GetLastError(), std::system_category()); qDebug() << "Failed to set dest file size" << dst_path.c_str(); CloseHandle(hSourceFile); CloseHandle(hDestFile); return false; } const LONG64 splitThreshold = (1LL << 32) - sourceFileIntegrity.ClusterSizeInBytes; DUPLICATE_EXTENTS_DATA dupExtent; dupExtent.FileHandle = hSourceFile; for (LONG64 offset = 0, remain = RoundUpToPowerOf2(sourceFileLength.EndOfFile.QuadPart, sourceFileIntegrity.ClusterSizeInBytes); remain > 0; offset += splitThreshold, remain -= splitThreshold) { dupExtent.SourceFileOffset.QuadPart = dupExtent.TargetFileOffset.QuadPart = offset; dupExtent.ByteCount.QuadPart = std::min(splitThreshold, remain); if (!DeviceIoControl(hDestFile, FSCTL_DUPLICATE_EXTENTS_TO_FILE, &dupExtent, sizeof(dupExtent), nullptr, 0, &junk, nullptr)) { DWORD err = GetLastError(); QString additionalMessage; if (err == ERROR_BLOCK_TOO_MANY_REFERENCES) { static const int MaxClonesPerFile = 8175; additionalMessage = QString( " This is ERROR_BLOCK_TOO_MANY_REFERENCES and may mean you have surpassed the maximum " "allowed %1 references for a single file. " "See " "https://docs.microsoft.com/en-us/windows-server/storage/refs/block-cloning#functionality-restrictions-and-remarks") .arg(MaxClonesPerFile); } ec = std::error_code(err, std::system_category()); qDebug() << "Failed copy-on-write cloning of" << src_path.c_str() << "to" << dst_path.c_str() << "with error" << err << additionalMessage; CloseHandle(hSourceFile); CloseHandle(hDestFile); return false; } } if (!(sourceFileBasicInfo.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE)) { FILE_SET_SPARSE_BUFFER setDestSparse = { FALSE }; if (!DeviceIoControl(hDestFile, FSCTL_SET_SPARSE, &setDestSparse, sizeof(setDestSparse), nullptr, 0, &junk, nullptr)) { qDebug() << "Failed to set dest file sparseness" << dst_path.c_str(); CloseHandle(hSourceFile); CloseHandle(hDestFile); return false; } } sourceFileBasicInfo.CreationTime.QuadPart = 0; if (!SetFileInformationByHandle(hDestFile, FileBasicInfo, &sourceFileBasicInfo, sizeof(sourceFileBasicInfo))) { qDebug() << "Failed to set dest file creation time" << dst_path.c_str(); CloseHandle(hSourceFile); CloseHandle(hDestFile); return false; } if (!FlushFileBuffers(hDestFile)) { qDebug() << "Failed to flush dest file buffer" << dst_path.c_str(); CloseHandle(hSourceFile); CloseHandle(hDestFile); return false; } destFileDispose = { FALSE }; bool result = !!SetFileInformationByHandle(hDestFile, FileDispositionInfo, &destFileDispose, sizeof(destFileDispose)); CloseHandle(hSourceFile); CloseHandle(hDestFile); return result; } #elif defined(Q_OS_LINUX) bool linux_ficlone(const std::string& src_path, const std::string& dst_path, std::error_code& ec) { // https://man7.org/linux/man-pages/man2/ioctl_ficlone.2.html int src_fd = open(src_path.c_str(), O_RDONLY); if (src_fd == -1) { qDebug() << "Failed to open file:" << src_path.c_str(); qDebug() << "Error:" << strerror(errno); ec = std::make_error_code(static_cast(errno)); return false; } int dst_fd = open(dst_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); if (dst_fd == -1) { qDebug() << "Failed to open file:" << dst_path.c_str(); qDebug() << "Error:" << strerror(errno); ec = std::make_error_code(static_cast(errno)); close(src_fd); return false; } // attempt to clone if (ioctl(dst_fd, FICLONE, src_fd) == -1) { qDebug() << "Failed to clone file:" << src_path.c_str() << "to" << dst_path.c_str(); qDebug() << "Error:" << strerror(errno); ec = std::make_error_code(static_cast(errno)); close(src_fd); close(dst_fd); return false; } if (close(src_fd)) { qDebug() << "Failed to close file:" << src_path.c_str(); qDebug() << "Error:" << strerror(errno); } if (close(dst_fd)) { qDebug() << "Failed to close file:" << dst_path.c_str(); qDebug() << "Error:" << strerror(errno); } return true; } #elif defined(Q_OS_MACOS) bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec) { // clonefile(const char * src, const char * dst, int flags); // https://www.manpagez.com/man/2/clonefile/ qDebug() << "attempting file clone via clonefile" << src_path.c_str() << "to" << dst_path.c_str(); if (clonefile(src_path.c_str(), dst_path.c_str(), 0) == -1) { qDebug() << "Failed to clone file:" << src_path.c_str() << "to" << dst_path.c_str(); qDebug() << "Error:" << strerror(errno); ec = std::make_error_code(static_cast(errno)); return false; } return true; } #endif /** * @brief if the Filesystem is symlink capable * */ bool canLinkOnFS(const QString& path) { FilesystemInfo info = statFS(path); return canLinkOnFS(info); } bool canLinkOnFS(const FilesystemInfo& info) { return canLinkOnFS(info.fsType); } bool canLinkOnFS(FilesystemType type) { return !s_non_link_filesystems.contains(type); } /** * @brief if the Filesystem is symlink capable on both ends * */ bool canLink(const QString& src, const QString& dst) { return canLinkOnFS(src) && canLinkOnFS(dst); } uintmax_t hardLinkCount(const QString& path) { std::error_code err; int count = fs::hard_link_count(StringUtils::toStdString(path), err); if (err) { qWarning() << "Failed to count hard links for" << path << ":" << QString::fromStdString(err.message()); count = 0; } return count; } #ifdef Q_OS_WIN // returns 8.3 file format from long path QString shortPathName(const QString& file) { auto input = file.toStdWString(); std::wstring output; long length = GetShortPathNameW(input.c_str(), NULL, 0); if (length == 0) return {}; // NOTE: this resizing might seem weird... // when GetShortPathNameW fails, it returns length including null character // when it succeeds, it returns length excluding null character // See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx output.resize(length); if (GetShortPathNameW(input.c_str(), (LPWSTR)output.c_str(), length) == 0) return {}; output.resize(length - 1); QString ret = QString::fromStdWString(output); return ret; } // if the string survives roundtrip through local 8bit encoding... bool fitsInLocal8bit(const QString& string) { return string == QString::fromLocal8Bit(string.toLocal8Bit()); } QString getPathNameInLocal8bit(const QString& file) { if (!fitsInLocal8bit(file)) { auto path = shortPathName(file); if (!path.isEmpty()) { return path; } // in case shortPathName fails just return the path as is } return file; } #endif QString getUniqueResourceName(const QString& filePath) { auto newFileName = filePath; if (!newFileName.endsWith(".disabled")) { return newFileName; // prioritize enabled mods } newFileName.chop(9); if (!QFile::exists(newFileName)) { return filePath; } QFileInfo fileInfo(filePath); auto baseName = fileInfo.completeBaseName(); auto path = fileInfo.absolutePath(); int counter = 1; do { if (counter == 1) { newFileName = FS::PathCombine(path, baseName + ".duplicate"); } else { newFileName = FS::PathCombine(path, baseName + ".duplicate" + QString::number(counter)); } counter++; } while (QFile::exists(newFileName)); return newFileName; } bool removeFiles(QStringList listFile) { bool ret = true; // For each file for (int i = 0; i < listFile.count(); i++) { // Remove ret = ret && QFile::remove(listFile.at(i)); } return ret; } } // namespace FS PrismLauncher-11.0.3/launcher/QVariantUtils.h0000644000175100017510000000415515224505336020510 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include namespace QVariantUtils { template inline QList toList(QVariant src) { QVariantList variantList = src.toList(); QList list_t; list_t.reserve(variantList.size()); for (const QVariant& v : variantList) { list_t.append(v.value()); } return list_t; } template inline QVariant fromList(QList val) { QVariantList variantList; variantList.reserve(val.size()); for (const T& v : val) { variantList.append(v); } return variantList; } } // namespace QVariantUtils PrismLauncher-11.0.3/launcher/MessageLevel.cpp0000644000175100017510000000446715224505336020657 0ustar runnerrunner#include "MessageLevel.h" MessageLevel MessageLevel::fromName(const QString& levelName) { QString name = levelName.toUpper(); if (name == "LAUNCHER") return MessageLevel::Launcher; else if (name == "TRACE") return MessageLevel::Trace; else if (name == "DEBUG") return MessageLevel::Debug; else if (name == "INFO") return MessageLevel::Info; else if (name == "MESSAGE") return MessageLevel::Message; else if (name == "WARNING" || name == "WARN") return MessageLevel::Warning; else if (name == "ERROR" || name == "CRITICAL") return MessageLevel::Error; else if (name == "FATAL") return MessageLevel::Fatal; // Skip PrePost, it's not exposed to !![]! // Also skip StdErr and StdOut else return MessageLevel::Unknown; } MessageLevel MessageLevel::fromQtMsgType(const QtMsgType& type) { switch (type) { case QtDebugMsg: return MessageLevel::Debug; case QtInfoMsg: return MessageLevel::Info; case QtWarningMsg: return MessageLevel::Warning; case QtCriticalMsg: return MessageLevel::Error; case QtFatalMsg: return MessageLevel::Fatal; default: return MessageLevel::Unknown; } } /* Get message level from a line. Line is modified if it was successful. */ MessageLevel MessageLevel::takeFromLine(QString& line) { // Level prefix int endmark = line.indexOf("]!"); if (line.startsWith("!![") && endmark != -1) { auto level = MessageLevel::fromName(line.left(endmark).mid(3)); line = line.mid(endmark + 2); return level; } return MessageLevel::Unknown; } /* Get message level from a line from the launcher log. Line is modified if it was successful. */ MessageLevel MessageLevel::takeFromLauncherLine(QString& line) { // Level prefix int startMark = 0; while (startMark < line.size() && (line[startMark].isDigit() || line[startMark].isSpace() || line[startMark] == '.')) ++startMark; int endmark = line.indexOf(":"); if (startMark < line.size() && endmark != -1) { auto level = MessageLevel::fromName(line.left(endmark).mid(startMark)); line = line.mid(endmark + 2); return level; } return MessageLevel::Unknown; } PrismLauncher-11.0.3/launcher/Launcher.in0000755000175100017510000000221015224505336017653 0ustar runnerrunner#!/usr/bin/env bash # Basic start script for running the launcher with the libs packaged with it. function printerror { printf "$1" if which zenity >/dev/null; then zenity --error --text="$1" &>/dev/null; elif which kdialog >/dev/null; then kdialog --error "$1" &>/dev/null; fi } if [[ $EUID -eq 0 ]]; then printerror "This program should not be run using sudo or as the root user!\n" exit 1 fi LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@ LAUNCHER_ENVNAME=@Launcher_ENVName@ LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")" echo "Launcher Dir: ${LAUNCHER_DIR}" # Makes the launcher use portals for file picking export QT_QPA_PLATFORMTHEME=xdgdesktopportal # disable OpenGL and Vulkan launcher features on sharun until https://github.com/VHSgunzo/sharun/issues/35 if [[ -f "${LAUNCHER_DIR}/sharun" ]]; then export ${LAUNCHER_ENVNAME}_DISABLE_GLVULKAN=1 fi # Just to be sure... chmod +x "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}" ARGS=("${LAUNCHER_DIR}/${LAUNCHER_NAME}" "${LAUNCHER_DIR}/bin/${LAUNCHER_NAME}") if [ -f portable.txt ]; then ARGS+=("-d" "${LAUNCHER_DIR}") fi ARGS+=("$@") # Run the launcher exec -a "${ARGS[@]}" PrismLauncher-11.0.3/launcher/Commandline.h0000644000175100017510000000205415224505336020164 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Authors: Orochimarufan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include /** * @file libutil/include/cmdutils.h * @brief commandline parsing and processing utilities */ namespace Commandline { /** * @brief split a string into argv items like a shell would do * @param args the argument string * @return a QStringList containing all arguments */ QStringList splitArgs(QString args); } // namespace Commandline PrismLauncher-11.0.3/launcher/InstanceTask.cpp0000644000175100017510000000705415224505336020665 0ustar runnerrunner#include "InstanceTask.h" #include #include "Application.h" #include "settings/SettingsObject.h" #include "ui/dialogs/CustomMessageBox.h" #include InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name) { auto dialog = CustomMessageBox::selectable(parent, QObject::tr("Change instance name"), QObject::tr("The instance's name seems to include the old version. Would you like to update it?\n\n" "Old name: %1\n" "New name: %2") .arg(old_name, new_name), QMessageBox::Question, QMessageBox::No | QMessageBox::Yes); auto result = dialog->exec(); if (result == QMessageBox::Yes) return InstanceNameChange::ShouldChange; return InstanceNameChange::ShouldKeep; } ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name) { if (APPLICATION->settings()->get("SkipModpackUpdatePrompt").toBool()) return ShouldUpdate::SkipUpdating; auto info = CustomMessageBox::selectable( parent, QObject::tr("Similar modpack was found!"), QObject::tr( "One or more of your instances are from this same modpack%1. Do you want to create a " "separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before " "updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).") .arg(original_version_name), QMessageBox::Information, QMessageBox::Cancel); QAbstractButton* update = info->addButton(QObject::tr("Update existing instance"), QMessageBox::AcceptRole); QAbstractButton* skip = info->addButton(QObject::tr("Create new instance"), QMessageBox::ResetRole); info->exec(); if (info->clickedButton() == update) return ShouldUpdate::Update; if (info->clickedButton() == skip) return ShouldUpdate::SkipUpdating; return ShouldUpdate::Cancel; } QString InstanceName::name() const { if (!m_modified_name.isEmpty()) return modifiedName(); if (!m_original_version.isEmpty()) return QString("%1 %2").arg(m_original_name, m_original_version); return m_original_name; } QString InstanceName::originalName() const { return m_original_name; } QString InstanceName::modifiedName() const { if (!m_modified_name.isEmpty()) return m_modified_name; return m_original_name; } QString InstanceName::version() const { return m_original_version; } void InstanceName::setName(InstanceName& other) { m_original_name = other.m_original_name; m_original_version = other.m_original_version; m_modified_name = other.m_modified_name; } InstanceTask::InstanceTask() : Task(), InstanceName() {} ShouldDeleteSaves askIfShouldDeleteSaves(QWidget* parent) { auto dialog = CustomMessageBox::selectable(parent, QObject::tr("Delete Existing Save Files"), QObject::tr("An earlier version of this mod pack installed save files.\n" "Would you like to remove those existing saves as part of this update?"), QMessageBox::Question, QMessageBox::No | QMessageBox::Yes); auto result = dialog->exec(); return result == QMessageBox::Yes ? ShouldDeleteSaves::Yes : ShouldDeleteSaves::No; } PrismLauncher-11.0.3/launcher/KonamiCode.h0000644000175100017510000000035715224505336017753 0ustar runnerrunner#pragma once #include class KonamiCode : public QObject { Q_OBJECT public: KonamiCode(QObject* parent = 0); void input(QEvent* event); signals: void triggered(); private: int m_progress = 0; }; PrismLauncher-11.0.3/launcher/InstanceDirUpdate.cpp0000644000175100017510000001317115224505336021641 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "InstanceDirUpdate.h" #include #include "Application.h" #include "FileSystem.h" #include "InstanceList.h" #include "ui/dialogs/CustomMessageBox.h" QString askToUpdateInstanceDirName(BaseInstance* instance, const QString& oldName, const QString& newName, QWidget* parent) { if (oldName == newName) return QString(); QString renamingMode = APPLICATION->settings()->get("InstRenamingMode").toString(); if (renamingMode == "MetadataOnly") return QString(); auto oldRoot = instance->instanceRoot(); auto newDirName = FS::DirNameFromString(newName, QFileInfo(oldRoot).dir().absolutePath()); auto newRoot = FS::PathCombine(QFileInfo(oldRoot).dir().absolutePath(), newDirName); if (oldRoot == newRoot) return QString(); if (oldRoot == FS::PathCombine(QFileInfo(oldRoot).dir().absolutePath(), newName)) return QString(); // Check for conflict if (QDir(newRoot).exists()) { QMessageBox::warning(parent, QObject::tr("Cannot rename instance"), QObject::tr("New instance root (%1) already exists.
    Only the metadata will be renamed.").arg(newRoot)); return QString(); } // Ask if we should rename if (renamingMode == "AskEverytime") { auto checkBox = new QCheckBox(QObject::tr("&Remember my choice"), parent); auto dialog = CustomMessageBox::selectable(parent, QObject::tr("Rename instance folder"), QObject::tr("Would you also like to rename the instance folder?\n\n" "Old name: %1\n" "New name: %2") .arg(oldName, newName), QMessageBox::Question, QMessageBox::No | QMessageBox::Yes, QMessageBox::NoButton, checkBox); auto res = dialog->exec(); if (checkBox->isChecked()) { if (res == QMessageBox::Yes) APPLICATION->settings()->set("InstRenamingMode", "PhysicalDir"); else APPLICATION->settings()->set("InstRenamingMode", "MetadataOnly"); } if (res == QMessageBox::No) return QString(); } // Check for linked instances if (!checkLinkedInstances(instance->id(), parent, QObject::tr("Renaming"))) return QString(); // Now we can confirm that a renaming is happening if (!instance->syncInstanceDirName(newRoot)) { QMessageBox::warning(parent, QObject::tr("Cannot rename instance"), QObject::tr("An error occurred when performing the following renaming operation:
    " " - Old instance root: %1
    " " - New instance root: %2
    " "Only the metadata is renamed.") .arg(oldRoot, newRoot)); return QString(); } return newRoot; } bool checkLinkedInstances(const QString& id, QWidget* parent, const QString& verb) { auto linkedInstances = APPLICATION->instances()->getLinkedInstancesById(id); if (!linkedInstances.empty()) { auto response = CustomMessageBox::selectable(parent, QObject::tr("There are linked instances"), QObject::tr("The following instance(s) might reference files in this instance:\n\n" "%1\n\n" "%2 it could break the other instance(s), \n\n" "Do you wish to proceed?", nullptr, linkedInstances.count()) .arg(linkedInstances.join("\n")) .arg(verb), QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ->exec(); if (response != QMessageBox::Yes) return false; } return true; } PrismLauncher-11.0.3/launcher/LaunchMode.h0000644000175100017510000000146715224505336017764 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2026 Octol1ttle * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once enum class LaunchMode { Normal, Offline, Demo, }; PrismLauncher-11.0.3/launcher/Application.cpp0000644000175100017510000023135615224505336020545 0ustar runnerrunner// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu // // SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2022 Lenny McLennington * Copyright (C) 2022 Tayou * Copyright (C) 2023 TheKodeToad * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Application.h" #include "BuildConfig.h" #include "DataMigrationTask.h" #include "java/JavaInstallList.h" #include "net/PasteUpload.h" #include "tasks/Task.h" #include "tools/GenericProfiler.h" #include "ui/InstanceWindow.h" #include "ui/MainWindow.h" #include "ui/ToolTipFilter.h" #include "ui/ViewLogWindow.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/instanceview/AccessibleInstanceView.h" #include "ui/pages/BasePageProvider.h" #include "ui/pages/global/APIPage.h" #include "ui/pages/global/AccountListPage.h" #include "ui/pages/global/AppearancePage.h" #include "ui/pages/global/ExternalToolsPage.h" #include "ui/pages/global/JavaPage.h" #include "ui/pages/global/LanguagePage.h" #include "ui/pages/global/LauncherPage.h" #include "ui/pages/global/MinecraftPage.h" #include "ui/pages/global/ProxyPage.h" #include "ui/setupwizard/AutoJavaWizardPage.h" #include "ui/setupwizard/JavaWizardPage.h" #include "ui/setupwizard/LanguageWizardPage.h" #include "ui/setupwizard/LoginWizardPage.h" #include "ui/setupwizard/PasteWizardPage.h" #include "ui/setupwizard/SetupWizard.h" #include "ui/setupwizard/ThemeWizardPage.h" #include "ui/dialogs/CustomMessageBox.h" #include "ui/pagedialog/PageDialog.h" #include "ui/themes/ThemeManager.h" #include "ApplicationMessage.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "InstanceList.h" #include "MTPixmapCache.h" #include #include "icons/IconList.h" #include "net/HttpMetaCache.h" #include "updater/ExternalUpdater.h" #include "tools/JProfiler.h" #include "tools/JVisualVM.h" #include "tools/MCEditTool.h" #include "settings/INISettingsObject.h" #include "settings/Setting.h" #include "meta/Index.h" #include "translations/TranslationsModel.h" #include #include #include #include #include "SysInfo.h" #ifdef Q_OS_LINUX #include #include "LibraryUtils.h" #include "gamemode_client.h" #endif #if defined(Q_OS_LINUX) #include #endif #if defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #include #include #endif #if defined(Q_OS_MAC) #if defined(SPARKLE_ENABLED) #include "updater/MacSparkleUpdater.h" #endif #else #include "updater/PrismExternalUpdater.h" #endif #if defined Q_OS_WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #endif #include "console/Console.h" #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) static const QLatin1String liveCheckFile("live.check"); PixmapCache* PixmapCache::s_instance = nullptr; static bool isANSIColorConsole; static QString defaultLogFormat = QStringLiteral( "%{time process}" " " "%{if-debug}Debug:%{endif}" "%{if-info}Info:%{endif}" "%{if-warning}Warning:%{endif}" "%{if-critical}Critical:%{endif}" "%{if-fatal}Fatal:%{endif}" " " "%{if-category}[%{category}] %{endif}" "%{message}" " " "(%{function}:%{line})"); #define ansi_reset "\x1b[0m" #define ansi_bold "\x1b[1m" #define ansi_reset_bold "\x1b[22m" #define ansi_faint "\x1b[2m" #define ansi_italic "\x1b[3m" #define ansi_red_fg "\x1b[31m" #define ansi_green_fg "\x1b[32m" #define ansi_yellow_fg "\x1b[33m" #define ansi_blue_fg "\x1b[34m" #define ansi_purple_fg "\x1b[35m" #define ansi_inverse "\x1b[7m" // clang-format off static QString ansiLogFormat = QStringLiteral( ansi_faint "%{time process}" ansi_reset " " "%{if-debug}" ansi_bold ansi_green_fg "D:" ansi_reset "%{endif}" "%{if-info}" ansi_bold ansi_blue_fg "I:" ansi_reset "%{endif}" "%{if-warning}" ansi_bold ansi_yellow_fg "W:" ansi_reset_bold "%{endif}" "%{if-critical}" ansi_bold ansi_red_fg "C:" ansi_reset_bold "%{endif}" "%{if-fatal}" ansi_bold ansi_inverse ansi_red_fg "F:" ansi_reset_bold "%{endif}" " " "%{if-category}" ansi_bold "[%{category}]" ansi_reset_bold " %{endif}" "%{message}" " " ansi_reset ansi_faint "(%{function}:%{line})" ansi_reset ); // clang-format on #undef ansi_inverse #undef ansi_purple_fg #undef ansi_blue_fg #undef ansi_yellow_fg #undef ansi_green_fg #undef ansi_red_fg #undef ansi_italic #undef ansi_faint #undef ansi_bold #undef ansi_reset_bold #undef ansi_reset namespace { /** This is used so that we can output to the log file in addition to the CLI. */ void appDebugOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { static std::mutex loggerMutex; const std::lock_guard lock(loggerMutex); // synchronized, QFile logFile is not thread-safe if (isANSIColorConsole) { // ensure default is set for log file qSetMessagePattern(defaultLogFormat); } QString out = qFormatLogMessage(type, context, msg); if (APPLICATION->logModel) { APPLICATION->logModel->append(MessageLevel::fromQtMsgType(type), out); } out += QChar::LineFeed; APPLICATION->logFile->write(out.toUtf8()); APPLICATION->logFile->flush(); if (isANSIColorConsole) { // format ansi for console; qSetMessagePattern(ansiLogFormat); out = qFormatLogMessage(type, context, msg); out += QChar::LineFeed; } QTextStream(stderr) << out.toLocal8Bit(); fflush(stderr); } } // namespace std::tuple read_lock_File(const QString& path) { auto contents = QString(FS::read(path)); auto lines = contents.split('\n'); QDateTime timestamp; QString from, to, target, data_path; for (auto line : lines) { auto index = line.indexOf("="); if (index < 0) continue; auto left = line.left(index); auto right = line.mid(index + 1); if (left.toLower() == "timestamp") { timestamp = QDateTime::fromString(right, Qt::ISODate); } else if (left.toLower() == "from") { from = right; } else if (left.toLower() == "to") { to = right; } else if (left.toLower() == "target") { target = right; } else if (left.toLower() == "data_path") { data_path = right; } } return std::make_tuple(timestamp, from, to, target, data_path); } Application::Application(int& argc, char** argv) : QApplication(argc, argv) { if (console::isConsole()) { isANSIColorConsole = true; } setOrganizationName(BuildConfig.LAUNCHER_NAME); setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN); setApplicationName(BuildConfig.LAUNCHER_NAME); setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString())); setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT); setDesktopFileName(BuildConfig.LAUNCHER_APPID); m_startTime = QDateTime::currentDateTime(); // Don't quit on hiding the last window this->setQuitOnLastWindowClosed(false); this->setQuitLockEnabled(false); // Commandline parsing QCommandLineParser parser; parser.setApplicationDescription(BuildConfig.LAUNCHER_DISPLAYNAME); parser.addOptions( { { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory)", "directory" }, { { "l", "launch" }, "Launch the specified instance (by instance ID)", "instance" }, { { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" }, { { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" }, { { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" }, { { "o", "offline" }, "Launch offline, with given player name (only valid in combination with --launch)", "offline" }, { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" }, { "show-window", "Show the main launcher window (useful in combination with --launch)" }, { { "I", "import" }, "Import instance or resource from specified local path or URL", "url" }, { "show", "Opens the window for the specified instance (by instance ID)", "show" } }); // Has to be positional for some OS to handle that properly parser.addPositionalArgument("URL", "Import the resource(s) at the given URL(s) (same as -I / --import)", "[URL...]"); parser.addHelpOption(); parser.addVersionOption(); parser.process(arguments()); m_instanceIdToLaunch = parser.value("launch"); m_serverToJoin = parser.value("server"); m_worldToJoin = parser.value("world"); m_profileToUse = parser.value("profile"); if (parser.isSet("offline")) { m_launchOffline = true; m_offlineName = parser.value("offline"); } m_liveCheck = parser.isSet("alive"); m_instanceIdToShowWindowOf = parser.value("show"); m_showMainWindow = parser.isSet("show-window"); for (auto url : parser.values("import")) { m_urlsToImport.append(normalizeImportUrl(url)); } // treat unspecified positional arguments as import urls for (auto url : parser.positionalArguments()) { m_urlsToImport.append(normalizeImportUrl(url)); } // error if --launch is missing with --server or --profile if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_launchOffline) && m_instanceIdToLaunch.isEmpty()) { std::cerr << "--server, --profile and --offline can only be used in combination with --launch!" << std::endl; m_status = Application::Failed; return; } QString origcwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); { // Root path is used for updates and portable data #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr m_rootPath = foo.absolutePath(); #elif defined(Q_OS_WIN32) m_rootPath = binPath; #elif defined(Q_OS_MAC) QDir foo(FS::PathCombine(binPath, "../..")); m_rootPath = foo.absolutePath(); // on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues) FS::updateTimestamp(m_rootPath); #endif } QString adjustedBy; QString dataPath; // change folder QString dataDirEnv; QString dirParam = parser.value("dir"); if (!dirParam.isEmpty()) { // the dir param. it makes multimc data path point to whatever the user specified // on command line adjustedBy = "Command line"; dataPath = dirParam; } else if (dataDirEnv = QProcessEnvironment::systemEnvironment().value(QString("%1_DATA_DIR").arg(BuildConfig.LAUNCHER_NAME.toUpper())); !dataDirEnv.isEmpty()) { adjustedBy = "System environment"; dataPath = dataDirEnv; } else { QDir foo; if (DesktopServices::isSnap()) { foo = QDir(qEnvironmentVariable("SNAP_USER_COMMON")); } else { foo = QDir(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "..")); } dataPath = foo.absolutePath(); adjustedBy = "Persistent data path"; #ifndef Q_OS_MACOS if (auto portableUserData = FS::PathCombine(m_rootPath, "UserData"); QDir(portableUserData).exists()) { dataPath = portableUserData; adjustedBy = "Portable user data path"; m_portable = true; } else if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) { dataPath = m_rootPath; adjustedBy = "Portable data path"; m_portable = true; } #endif } if (!FS::ensureFolderPathExists(dataPath)) { showFatalErrorMessage( "The launcher data folder could not be created.", QString("The launcher data folder could not be created.\n" "\n" "Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n" "(%1)\n" "\n" "The launcher cannot continue until you fix this problem.") .arg(dataPath)); return; } if (!QDir::setCurrent(dataPath)) { showFatalErrorMessage("The launcher data folder could not be opened.", QString("The launcher data folder could not be opened.\n" "\n" "Make sure you have the right permissions to the launcher data folder.\n" "(%1)\n" "\n" "The launcher cannot continue until you fix this problem.") .arg(dataPath)); return; } m_dataPath = dataPath; /* * Establish the mechanism for communication with an already running PrismLauncher that uses the same data path. * If there is one, tell it what the user actually wanted to do and exit. * We want to initialize this before logging to avoid messing with the log of a potential already running copy. */ auto appID = ApplicationId::fromPathAndVersion(QDir::currentPath(), BuildConfig.printableVersionString()); { // FIXME: you can run the same binaries with multiple data dirs and they won't clash. This could cause issues for updates. m_peerInstance = new LocalPeer(this, appID); connect(m_peerInstance, &LocalPeer::messageReceived, this, &Application::messageReceived); if (m_peerInstance->isClient()) { bool sentMessage = false; int timeout = 2000; if (m_instanceIdToLaunch.isEmpty()) { ApplicationMessage activate; activate.command = "activate"; sentMessage = m_peerInstance->sendMessage(activate.serialize(), timeout); if (!m_urlsToImport.isEmpty()) { for (auto url : m_urlsToImport) { ApplicationMessage import; import.command = "import"; import.args.insert("url", url.toString()); sentMessage = m_peerInstance->sendMessage(import.serialize(), timeout); } } } else { ApplicationMessage launch; launch.command = "launch"; launch.args["id"] = m_instanceIdToLaunch; if (!m_serverToJoin.isEmpty()) { launch.args["server"] = m_serverToJoin; } else if (!m_worldToJoin.isEmpty()) { launch.args["world"] = m_worldToJoin; } if (!m_profileToUse.isEmpty()) { launch.args["profile"] = m_profileToUse; } if (m_launchOffline) { launch.args["offline_enabled"] = "true"; launch.args["offline_name"] = m_offlineName; } sentMessage = m_peerInstance->sendMessage(launch.serialize(), timeout); } if (sentMessage) { m_status = Application::Succeeded; return; } else { std::cerr << "Unable to redirect command to already running instance\n"; // C function not Qt function - event loop not started yet ::exit(1); } } } // init the logger { static const QString baseLogFile = BuildConfig.LAUNCHER_NAME + "-%0.log"; static const QString logBase = FS::PathCombine("logs", baseLogFile); if (FS::ensureFolderPathExists("logs")) { // if this did not fail for (auto i = 0; i <= 4; i++) if (auto oldName = baseLogFile.arg(i); QFile::exists(oldName)) // do not pointlessly delete new files if the old ones are not there FS::move(oldName, logBase.arg(i)); } for (auto i = 4; i > 0; i--) FS::move(logBase.arg(i - 1), logBase.arg(i)); logFile = std::unique_ptr(new QFile(logBase.arg(0))); if (!logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { showFatalErrorMessage("The launcher data folder is not writable!", QString("The launcher couldn't create a log file - %1.\n" "\n" "Make sure you have write permissions to the data folder.\n" "(%2)\n" "\n" "The launcher cannot continue until you fix this problem.") .arg(logFile->errorString()) .arg(dataPath)); return; } qInstallMessageHandler(appDebugOutput); qSetMessagePattern(defaultLogFormat); logModel.reset(new LogModel(this)); bool foundLoggingRules = false; auto logRulesFile = QStringLiteral("qtlogging.ini"); auto logRulesPath = FS::PathCombine(dataPath, logRulesFile); qInfo() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); // search the dataPath() // seach app data standard path if (!foundLoggingRules && !isPortable() && dirParam.isEmpty() && dataDirEnv.isEmpty()) { logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile)); if (!logRulesPath.isEmpty()) { qInfo() << "Found" << logRulesPath << "..."; foundLoggingRules = true; } } // seach root path if (!foundLoggingRules) { #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) logRulesPath = FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME, logRulesFile); #else logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); #endif qInfo() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); } if (foundLoggingRules) { // load and set logging rules qInfo() << "Loading logging rules from:" << logRulesPath; QSettings loggingRules(logRulesPath, QSettings::IniFormat); loggingRules.beginGroup("Rules"); QStringList rule_names = loggingRules.childKeys(); QStringList rules; qInfo() << "Setting log rules:"; for (auto rule_name : rule_names) { auto rule = QString("%1=%2").arg(rule_name).arg(loggingRules.value(rule_name).toString()); rules.append(rule); qInfo() << " " << rule; } auto rules_str = rules.join("\n"); QLoggingCategory::setFilterRules(rules_str); } qInfo() << "<> Log initialized."; } { bool migrated = false; if (!migrated) migrated = handleDataMigration( dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../PolyMC"), "PolyMC", "polymc.cfg"); if (!migrated) migrated = handleDataMigration( dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../multimc"), "MultiMC", "multimc.cfg"); } { qInfo() << qPrintable(BuildConfig.LAUNCHER_DISPLAYNAME + ", " + QString(BuildConfig.LAUNCHER_COPYRIGHT).replace("\n", ", ")); qInfo() << "Version :" << BuildConfig.printableVersionString(); qInfo() << "Platform :" << BuildConfig.BUILD_PLATFORM; qInfo() << "Git commit :" << BuildConfig.GIT_COMMIT; qInfo() << "Git refspec :" << BuildConfig.GIT_REFSPEC; qInfo() << "Compiled for :" << BuildConfig.systemID(); qInfo() << "Compiled by :" << BuildConfig.compilerID(); qInfo() << "Build Artifact :" << BuildConfig.BUILD_ARTIFACT; qInfo() << "Updates Enabled :" << (updaterEnabled() ? "Yes" : "No"); if (adjustedBy.size()) { qInfo() << "Work dir before adjustment :" << origcwdPath; qInfo() << "Work dir after adjustment :" << QDir::currentPath(); qInfo() << "Adjusted by :" << adjustedBy; } else { qInfo() << "Work dir :" << QDir::currentPath(); } qInfo() << "Binary path :" << binPath; qInfo() << "Application root path :" << m_rootPath; if (!m_instanceIdToLaunch.isEmpty()) { qInfo() << "ID of instance to launch :" << m_instanceIdToLaunch; } if (!m_serverToJoin.isEmpty()) { qInfo() << "Address of server to join :" << m_serverToJoin; } else if (!m_worldToJoin.isEmpty()) { qInfo() << "Name of the world to join :" << m_worldToJoin; } qInfo() << "<> Paths set."; } if (m_liveCheck) { QFile check(liveCheckFile); if (check.open(QIODevice::WriteOnly | QIODevice::Truncate)) { auto payload = appID.toString().toUtf8(); if (check.write(payload) == payload.size()) { check.close(); } else { qWarning() << "Could not write into" << liveCheckFile << "error:" << check.errorString(); check.remove(); // also closes file! } } else { qWarning() << "Could not open" << liveCheckFile << "for writing:" << check.errorString(); } } // Initialize application settings { // Provide a fallback for migration from PolyMC m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this)); // Theming m_settings->registerSetting("IconTheme", QString()); m_settings->registerSetting("ApplicationTheme", QString()); m_settings->registerSetting("BackgroundCat", QString("kitteh")); // Remembered state m_settings->registerSetting("LastUsedGroupForNewInstance", QString()); m_settings->registerSetting("MenuBarInsteadOfToolBar", false); m_settings->registerSetting("NumberOfConcurrentTasks", 10); m_settings->registerSetting("NumberOfConcurrentDownloads", 6); m_settings->registerSetting("NumberOfManualRetries", 1); m_settings->registerSetting("RequestTimeout", 60); QString defaultMonospace; int defaultSize = 11; #ifdef Q_OS_WIN32 defaultMonospace = "Courier"; defaultSize = 10; #elif defined(Q_OS_MAC) defaultMonospace = "Menlo"; #else defaultMonospace = "Monospace"; #endif // resolve the font so the default actually matches QFont consoleFont; consoleFont.setFamily(defaultMonospace); consoleFont.setStyleHint(QFont::Monospace); consoleFont.setFixedPitch(true); QFontInfo consoleFontInfo(consoleFont); QString resolvedDefaultMonospace = consoleFontInfo.family(); QFont resolvedFont(resolvedDefaultMonospace); qDebug().nospace() << "Detected default console font: " << resolvedDefaultMonospace << ", substitutions: " << resolvedFont.substitutions().join(','); m_settings->registerSetting("ConsoleFont", resolvedDefaultMonospace); m_settings->registerSetting("ConsoleFontSize", defaultSize); m_settings->registerSetting("ConsoleMaxLines", 100000); m_settings->registerSetting("ConsoleOverflowStop", true); logModel->setMaxLines(getConsoleMaxLines(settings())); logModel->setStopOnOverflow(shouldStopOnConsoleOverflow(settings())); logModel->setOverflowMessage(tr("Cannot display this log since the log length surpassed %1 lines.").arg(logModel->getMaxLines())); // Folders m_settings->registerSetting("InstanceDir", "instances"); m_settings->registerSetting({ "CentralModsDir", "ModsDir" }, "mods"); m_settings->registerSetting("IconsDir", "icons"); m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); m_settings->registerSetting("DownloadsDirWatchRecursive", false); m_settings->registerSetting("MoveModsFromDownloadsDir", false); m_settings->registerSetting("SkinsDir", "skins"); m_settings->registerSetting("JavaDir", "java"); #ifdef Q_OS_MACOS // Folder security-scoped bookmarks m_settings->registerSetting("InstanceDirBookmark", ""); m_settings->registerSetting("CentralModsDirBookmark", ""); m_settings->registerSetting("IconsDirBookmark", ""); m_settings->registerSetting("DownloadsDirBookmark", ""); m_settings->registerSetting("SkinsDirBookmark", ""); m_settings->registerSetting("JavaDirBookmark", ""); #endif // Editors m_settings->registerSetting("JsonEditor", QString()); // Language m_settings->registerSetting("Language", QString()); m_settings->registerSetting("UseSystemLocale", false); // Console m_settings->registerSetting("ShowConsole", false); m_settings->registerSetting("AutoCloseConsole", false); m_settings->registerSetting("ShowConsoleOnError", true); m_settings->registerSetting("LogPrePostOutput", true); // Window Size m_settings->registerSetting({ "LaunchMaximized", "MCWindowMaximize" }, false); m_settings->registerSetting({ "MinecraftWinWidth", "MCWindowWidth" }, 854); m_settings->registerSetting({ "MinecraftWinHeight", "MCWindowHeight" }, 480); // Proxy Settings m_settings->registerSetting("ProxyType", "None"); m_settings->registerSetting({ "ProxyAddr", "ProxyHostName" }, "127.0.0.1"); m_settings->registerSetting("ProxyPort", 8080); m_settings->registerSetting({ "ProxyUser", "ProxyUsername" }, ""); m_settings->registerSetting({ "ProxyPass", "ProxyPassword" }, ""); // Memory m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512); m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, SysInfo::defaultMaxJvmMem()); m_settings->registerSetting("PermGen", 128); m_settings->registerSetting("LowMemWarning", true); // Java Settings m_settings->registerSetting("JavaPath", ""); m_settings->registerSetting("JavaSignature", ""); m_settings->registerSetting("JavaArchitecture", ""); m_settings->registerSetting("JavaRealArchitecture", ""); m_settings->registerSetting("JavaVersion", ""); m_settings->registerSetting("JavaVendor", ""); m_settings->registerSetting("LastHostname", ""); m_settings->registerSetting("JvmArgs", ""); m_settings->registerSetting("IgnoreJavaCompatibility", false); m_settings->registerSetting("IgnoreJavaWizard", false); auto defaultEnableAutoJava = m_settings->get("JavaPath").toString().isEmpty(); m_settings->registerSetting("AutomaticJavaSwitch", defaultEnableAutoJava); m_settings->registerSetting("AutomaticJavaDownload", defaultEnableAutoJava); m_settings->registerSetting("UserAskedAboutAutomaticJavaDownload", false); // Legacy settings m_settings->registerSetting("OnlineFixes", false); // Native library workarounds m_settings->registerSetting("UseNativeOpenAL", false); m_settings->registerSetting("CustomOpenALPath", ""); m_settings->registerSetting("UseNativeGLFW", false); m_settings->registerSetting("CustomGLFWPath", ""); // Performance related options m_settings->registerSetting("EnableFeralGamemode", false); m_settings->registerSetting("EnableMangoHud", false); m_settings->registerSetting("UseDiscreteGpu", false); m_settings->registerSetting("UseZink", false); // Game time m_settings->registerSetting("ShowGameTime", true); m_settings->registerSetting("ShowGlobalGameTime", true); m_settings->registerSetting("RecordGameTime", true); m_settings->registerSetting("ShowGameTimeWithoutDays", false); // Minecraft mods m_settings->registerSetting("ModMetadataDisabled", false); m_settings->registerSetting("ModDependenciesDisabled", false); m_settings->registerSetting("SkipModpackUpdatePrompt", false); m_settings->registerSetting("ShowModIncompat", false); // Minecraft offline player name m_settings->registerSetting("LastOfflinePlayerName", ""); // Wrapper command for launch m_settings->registerSetting("WrapperCommand", ""); // Custom Commands m_settings->registerSetting({ "PreLaunchCommand", "PreLaunchCmd" }, ""); m_settings->registerSetting({ "PostExitCommand", "PostExitCmd" }, ""); // The cat m_settings->registerSetting("TheCat", false); m_settings->registerSetting("CatOpacity", 100); m_settings->registerSetting("CatFit", "fit"); m_settings->registerSetting("StatusBarVisible", true); m_settings->registerSetting("ToolbarsLocked", false); // Instance m_settings->registerSetting("InstSortMode", "Name"); m_settings->registerSetting("InstRenamingMode", "AskEverytime"); m_settings->registerSetting("SelectedInstance", QString()); // Window state and geometry m_settings->registerSetting("MainWindowState", ""); m_settings->registerSetting("MainWindowGeometry", ""); m_settings->registerSetting("ConsoleWindowState", ""); m_settings->registerSetting("ConsoleWindowGeometry", ""); m_settings->registerSetting("SettingsGeometry", ""); m_settings->registerSetting("PagedGeometry", ""); m_settings->registerSetting("NewInstanceGeometry", ""); m_settings->registerSetting("UpdateDialogGeometry", ""); m_settings->registerSetting("NewsGeometry", ""); m_settings->registerSetting("ModDownloadGeometry", ""); m_settings->registerSetting("RPDownloadGeometry", ""); m_settings->registerSetting("TPDownloadGeometry", ""); m_settings->registerSetting("ShaderDownloadGeometry", ""); m_settings->registerSetting("DataPackDownloadGeometry", ""); // data pack window // in future, more pages may be added - so this name is chosen to avoid needing migration m_settings->registerSetting("WorldManagementGeometry", ""); // HACK: This code feels so stupid is there a less stupid way of doing this? { m_settings->registerSetting("PastebinURL", ""); m_settings->registerSetting("PastebinType", PasteUpload::PasteType::Mclogs); m_settings->registerSetting("PastebinCustomAPIBase", ""); QString pastebinURL = m_settings->get("PastebinURL").toString(); bool userHadDefaultPastebin = pastebinURL == "https://0x0.st"; if (!pastebinURL.isEmpty() && !userHadDefaultPastebin) { m_settings->set("PastebinType", PasteUpload::PasteType::NullPointer); m_settings->set("PastebinCustomAPIBase", pastebinURL); m_settings->reset("PastebinURL"); } bool ok; int pasteType = m_settings->get("PastebinType").toInt(&ok); // If PastebinType is invalid then reset the related settings. if (!ok || !(PasteUpload::PasteType::First <= pasteType && pasteType <= PasteUpload::PasteType::Last)) { m_settings->reset("PastebinType"); m_settings->reset("PastebinCustomAPIBase"); } } { auto resetIfInvalid = [this](const Setting* setting) { if (const QUrl url(setting->get().toString()); !url.isValid() || (url.scheme() != "http" && url.scheme() != "https")) { m_settings->reset(setting->id()); } }; // Meta URL resetIfInvalid(m_settings->registerSetting("MetaURLOverride", "").get()); // Resource URL resetIfInvalid(m_settings->registerSetting({ "ResourceURLOverride", "ResourceURL" }, "").get()); // Legacy FML libs URL resetIfInvalid(m_settings->registerSetting("LegacyFMLLibsURLOverride", "").get()); } m_settings->registerSetting("CloseAfterLaunch", false); m_settings->registerSetting("QuitAfterGameStop", false); m_settings->registerSetting("Env", "{}"); // Custom Microsoft Authentication Client ID m_settings->registerSetting("MSAClientIDOverride", ""); // Custom Flame API Key { m_settings->registerSetting("CFKeyOverride", ""); m_settings->registerSetting("FlameKeyOverride", ""); QString flameKey = m_settings->get("CFKeyOverride").toString(); if (!flameKey.isEmpty()) m_settings->set("FlameKeyOverride", flameKey); m_settings->reset("CFKeyOverride"); } m_settings->registerSetting("FallbackMRBlockedMods", true); m_settings->registerSetting("ModrinthToken", ""); m_settings->registerSetting("UserAgentOverride", ""); // FTBApp instances m_settings->registerSetting("FTBAppInstancesPath", ""); // Custom Technic Client ID m_settings->registerSetting("TechnicClientID", ""); // Init page provider { m_globalSettingsProvider = std::make_unique(tr("Settings")); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); m_globalSettingsProvider->addPage(); } PixmapCache::setInstance(new PixmapCache(this)); qInfo() << "<> Settings loaded."; } #ifndef QT_NO_ACCESSIBILITY QAccessible::installFactory(groupViewAccessibleFactory); #endif /* !QT_NO_ACCESSIBILITY */ // initialize network access and proxy setup { m_network.reset(new QNetworkAccessManager()); QString proxyTypeStr = settings()->get("ProxyType").toString(); QString addr = settings()->get("ProxyAddr").toString(); int port = settings()->get("ProxyPort").value(); QString user = settings()->get("ProxyUser").toString(); QString pass = settings()->get("ProxyPass").toString(); updateProxySettings(proxyTypeStr, addr, port, user, pass); qInfo() << "<> Network done."; } // Instance icons { auto setting = APPLICATION->settings()->getSetting("IconsDir"); QStringList instFolders = { ":/icons/multimc/32x32/instances/", ":/icons/multimc/50x50/instances/", ":/icons/multimc/128x128/instances/", ":/icons/multimc/scalable/instances/" }; m_icons.reset(new IconList(instFolders, setting->get().toString())); connect(setting.get(), &Setting::SettingChanged, [this](const Setting&, QVariant value) { m_icons->directoryChanged(value.toString()); }); qInfo() << "<> Instance icons initialized."; } // Themes m_themeManager = std::make_unique(); #ifdef Q_OS_MACOS // for macOS: getting directory settings will generate URL security-scoped bookmarks if needed and not present // this facilitates a smooth transition from a non-sandboxed version of the launcher, that likely can access the directory, // and a sandboxed version that can't access the directory without a bookmark // this section can likely be removed once the sandboxed version has been released for a while and migrations aren't done anymore { m_settings->get("InstanceDir"); m_settings->get("CentralModsDir"); m_settings->get("IconsDir"); m_settings->get("DownloadsDir"); m_settings->get("SkinsDir"); m_settings->get("JavaDir"); } #endif // initialize and load all instances { auto InstDirSetting = m_settings->getSetting("InstanceDir"); // instance path: check for problems with '!' in instance path and warn the user in the log // and remember that we have to show him a dialog when the gui starts (if it does so) QString instDir = m_settings->get("InstanceDir").toString(); qInfo() << "Instance path :" << instDir; if (FS::checkProblemticPathJava(QDir(instDir))) { qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!"; } m_instances.reset(new InstanceList(m_settings.get(), instDir, this)); connect(InstDirSetting.get(), &Setting::SettingChanged, m_instances.get(), &InstanceList::on_InstFolderChanged); qInfo() << "Loading Instances..."; m_instances->loadList(); qInfo() << "<> Instances loaded."; } // and accounts { m_accounts.reset(new AccountList(this)); qInfo() << "Loading accounts..."; m_accounts->setListFilePath("accounts.json", true); m_accounts->loadList(); m_accounts->fillQueue(); qInfo() << "<> Accounts loaded."; } // init the http meta cache { m_metacache.reset(new HttpMetaCache("metacache")); m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath()); m_metacache->addBase("libraries", QDir("libraries").absolutePath()); m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath()); m_metacache->addBase("general", QDir("cache").absolutePath()); m_metacache->addBase("ATLauncherPacks", QDir("cache/ATLauncherPacks").absolutePath()); m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath()); m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").absolutePath()); m_metacache->addBase("FlamePacks", QDir("cache/FlamePacks").absolutePath()); m_metacache->addBase("FlameMods", QDir("cache/FlameMods").absolutePath()); m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath()); m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath()); m_metacache->addBase("translations", QDir("translations").absolutePath()); m_metacache->addBase("meta", QDir("meta").absolutePath()); m_metacache->addBase("java", QDir("cache/java").absolutePath()); m_metacache->addBase("feed", QDir("cache/feed").absolutePath()); m_metacache->Load(); qInfo() << "<> Cache initialized."; } // load translations { m_translations.reset(new TranslationsModel("translations")); m_translations->downloadIndex(); qInfo() << "Your language is" << m_translations->selectedLanguage(); qInfo() << "<> Translations loaded."; } // FIXME: what to do with these? m_profilers.insert("jprofiler", std::shared_ptr(new JProfilerFactory())); m_profilers.insert("jvisualvm", std::shared_ptr(new JVisualVMFactory())); m_profilers.insert("generic", std::shared_ptr(new GenericProfilerFactory())); for (auto profiler : m_profilers.values()) { profiler->registerSettings(m_settings.get()); } // Create the MCEdit thing... why is this here? { m_mcedit.reset(new MCEditTool(m_settings.get())); } #ifdef Q_OS_MACOS connect(this, &Application::clickedOnDock, [this]() { this->showMainWindow(); }); #endif connect(this, &Application::aboutToQuit, [this]() { if (m_instances) { // save any remaining instance state m_instances->saveNow(); } if (logFile) { logFile->flush(); logFile->close(); } }); updateCapabilities(); detectLibraries(); // check update locks { auto update_log_path = FS::PathCombine(m_dataPath, "logs", "prism_launcher_update.log"); auto update_lock = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.lock")); if (update_lock.exists()) { auto [timestamp, from, to, target, data_path] = read_lock_File(update_lock.absoluteFilePath()); auto infoMsg = tr("This installation has a update lock file present at: %1\n" "\n" "Timestamp: %2\n" "Updating from version %3 to %4\n" "Target install path: %5\n" "Data Path: %6" "\n" "This likely means that a update attempt failed. Please ensure your installation is in working order before " "proceeding.\n" "Check the Prism Launcher updater log at: \n" "%7\n" "for details on the last update attempt.\n" "\n" "To delete this lock and proceed select \"Ignore\" below.") .arg(update_lock.absoluteFilePath()) .arg(timestamp.toString(Qt::ISODate), from, to, target, data_path) .arg(update_log_path); auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update In Progress"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); msgBox.setDetailedText(FS::read(update_log_path)); msgBox.setMinimumWidth(460); msgBox.adjustSize(); auto res = msgBox.exec(); switch (res) { case QMessageBox::Ignore: { FS::deletePath(update_lock.absoluteFilePath()); break; } case QMessageBox::Abort: [[fallthrough]]; default: { qDebug() << "Exiting because update lockfile is present"; QMetaObject::invokeMethod(this, []() { exit(1); }, Qt::QueuedConnection); return; } } } auto update_fail_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.fail")); if (update_fail_marker.exists()) { auto infoMsg = tr("An update attempt failed\n" "\n" "Please ensure your installation is in working order before " "proceeding.\n" "Check the Prism Launcher updater log at: \n" "%1\n" "for details on the last update attempt.") .arg(update_log_path); auto msgBox = QMessageBox(QMessageBox::Warning, tr("Update Failed"), infoMsg, QMessageBox::Ignore | QMessageBox::Abort); msgBox.setDefaultButton(QMessageBox::Abort); msgBox.setModal(true); msgBox.setDetailedText(FS::read(update_log_path)); msgBox.setMinimumWidth(460); msgBox.adjustSize(); auto res = msgBox.exec(); switch (res) { case QMessageBox::Ignore: { FS::deletePath(update_fail_marker.absoluteFilePath()); break; } case QMessageBox::Abort: [[fallthrough]]; default: { qDebug() << "Exiting because update lockfile is present"; QMetaObject::invokeMethod(this, []() { exit(1); }, Qt::QueuedConnection); return; } } } auto update_success_marker = QFileInfo(FS::PathCombine(m_dataPath, ".prism_launcher_update.success")); if (update_success_marker.exists()) { auto infoMsg = tr("Update succeeded\n" "\n" "You are now running %1 .\n" "Check the Prism Launcher updater log at: \n" "%2\n" "for details.") .arg(BuildConfig.printableVersionString()) .arg(update_log_path); auto msgBox = new QMessageBox(QMessageBox::Information, tr("Update Succeeded"), infoMsg, QMessageBox::Ok); msgBox->setDefaultButton(QMessageBox::Ok); msgBox->setDetailedText(FS::read(update_log_path)); msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setMinimumWidth(460); msgBox->adjustSize(); msgBox->open(); FS::deletePath(update_success_marker.absoluteFilePath()); } } // notify user if /tmp is mounted with `noexec` (#1693) QString jvmArgs = m_settings->get("JvmArgs").toString(); if (jvmArgs.indexOf("java.io.tmpdir") == -1) { /* java.io.tmpdir is a valid workaround, so don't annoy */ bool is_tmp_noexec = false; #if defined(Q_OS_LINUX) struct statvfs tmp_stat; statvfs("/tmp", &tmp_stat); is_tmp_noexec = tmp_stat.f_flag & ST_NOEXEC; #elif defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) struct statfs tmp_stat; statfs("/tmp", &tmp_stat); is_tmp_noexec = tmp_stat.f_flags & MNT_NOEXEC; #endif if (is_tmp_noexec) { auto infoMsg = tr("Your /tmp directory is currently mounted with the 'noexec' flag enabled.\n" "Some versions of Minecraft may not launch.\n" "\n" "You may solve this issue by remounting /tmp as 'exec' or setting " "the java.io.tmpdir JVM argument to a writeable directory in a " "filesystem where the 'exec' flag is set (e.g., /home/user/.local/tmp)\n"); auto msgBox = new QMessageBox(QMessageBox::Information, tr("Incompatible system configuration"), infoMsg, QMessageBox::Ok); msgBox->setDefaultButton(QMessageBox::Ok); msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setMinimumWidth(460); msgBox->adjustSize(); msgBox->open(); } } if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") { installEventFilter(new ToolTipFilter); } if (createSetupWizard()) { return; } m_themeManager->applyCurrentlySelectedTheme(true); performMainStartupAction(); } bool Application::createSetupWizard() { bool javaRequired = [this]() { if (BuildConfig.JAVA_DOWNLOADER_ENABLED && settings()->get("AutomaticJavaDownload").toBool()) { return false; } bool ignoreJavaWizard = settings()->get("IgnoreJavaWizard").toBool(); if (ignoreJavaWizard) { return false; } QString currentHostName = QHostInfo::localHostName(); QString oldHostName = settings()->get("LastHostname").toString(); if (currentHostName != oldHostName) { settings()->set("LastHostname", currentHostName); return true; } QString currentJavaPath = settings()->get("JavaPath").toString(); QString actualPath = FS::ResolveExecutable(currentJavaPath); return actualPath.isNull(); }(); bool askjava = BuildConfig.JAVA_DOWNLOADER_ENABLED && !javaRequired && !settings()->get("AutomaticJavaDownload").toBool() && !settings()->get("AutomaticJavaSwitch").toBool() && !settings()->get("UserAskedAboutAutomaticJavaDownload").toBool(); bool languageRequired = settings()->get("Language").toString().isEmpty(); bool pasteInterventionRequired = settings()->get("PastebinURL") != ""; bool validWidgets = m_themeManager->isValidApplicationTheme(settings()->get("ApplicationTheme").toString()); bool validIcons = m_themeManager->isValidIconTheme(settings()->get("IconTheme").toString()); bool login = !m_accounts->anyAccountIsValid() && capabilities() & Application::SupportsMSA; bool themeInterventionRequired = !validWidgets || !validIcons; bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired || askjava || login; if (wizardRequired) { // set default theme after going into theme wizard if (!validIcons) settings()->set("IconTheme", QString("pe_colored")); if (!validWidgets) { #if defined(Q_OS_WIN32) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) const QString style = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark ? QStringLiteral("dark") : QStringLiteral("bright"); #else const QString style = QStringLiteral("system"); #endif settings()->set("ApplicationTheme", style); } m_themeManager->applyCurrentlySelectedTheme(true); m_setupWizard = new SetupWizard(nullptr); if (languageRequired) { m_setupWizard->addPage(new LanguageWizardPage(m_setupWizard)); } if (javaRequired) { m_setupWizard->addPage(new JavaWizardPage(m_setupWizard)); } else if (askjava) { m_setupWizard->addPage(new AutoJavaWizardPage(m_setupWizard)); } if (pasteInterventionRequired) { m_setupWizard->addPage(new PasteWizardPage(m_setupWizard)); } if (themeInterventionRequired) { m_setupWizard->addPage(new ThemeWizardPage(m_setupWizard)); } if (login) { m_setupWizard->addPage(new LoginWizardPage(m_setupWizard)); } connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished); m_setupWizard->show(); } return wizardRequired || login; } bool Application::updaterEnabled() { #if defined(Q_OS_MAC) return BuildConfig.UPDATER_ENABLED; #else return BuildConfig.UPDATER_ENABLED && QFileInfo(FS::PathCombine(m_rootPath, updaterBinaryName())).isFile(); #endif } QString Application::updaterBinaryName() { auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); #if defined Q_OS_WIN32 exe_name.append(".exe"); #else exe_name.prepend("bin/"); #endif return exe_name; } bool Application::event(QEvent* event) { #ifdef Q_OS_MACOS if (event->type() == QEvent::ApplicationStateChange) { auto ev = static_cast(event); if (m_prevAppState == Qt::ApplicationActive && ev->applicationState() == Qt::ApplicationActive) { emit clickedOnDock(); } m_prevAppState = ev->applicationState(); } #endif if (event->type() == QEvent::FileOpen) { if (!m_mainWindow) { showMainWindow(false); } auto ev = static_cast(event); m_mainWindow->processURLs({ ev->url() }); } return QApplication::event(event); } void Application::setupWizardFinished(int status) { qDebug() << "Wizard result =" << status; performMainStartupAction(); } void Application::performMainStartupAction() { m_status = Application::Initialized; if (!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); if (inst) { MinecraftTarget::Ptr targetToJoin = nullptr; MinecraftAccountPtr accountToUse = nullptr; qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching"; if (!m_serverToJoin.isEmpty()) { // FIXME: validate the server string targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_serverToJoin, false))); qDebug() << " Launching with server" << m_serverToJoin; } else if (!m_worldToJoin.isEmpty()) { targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_worldToJoin, true))); qDebug() << " Launching with world" << m_worldToJoin; } if (!m_profileToUse.isEmpty()) { accountToUse = accounts()->getAccountByProfileName(m_profileToUse); if (!accountToUse) { return; } qDebug() << " Launching with account" << m_profileToUse; } launch(inst, m_launchOffline ? LaunchMode::Offline : LaunchMode::Normal, targetToJoin, accountToUse, m_offlineName); if (!m_showMainWindow) { return; } } } if (!m_instanceIdToShowWindowOf.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToShowWindowOf); if (inst) { qDebug() << "<> Showing window of instance " << m_instanceIdToShowWindowOf; showInstanceWindow(inst); return; } } if (!m_mainWindow) { // normal main window showMainWindow(false); qDebug() << "<> Main window shown."; } // initialize the updater if (updaterEnabled()) { qDebug() << "Initializing updater"; #ifdef Q_OS_MAC #if defined(SPARKLE_ENABLED) m_updater.reset(new MacSparkleUpdater()); #endif #else m_updater.reset(new PrismExternalUpdater(m_mainWindow, m_rootPath, m_dataPath)); #endif qDebug() << "<> Updater started."; } { // delete instances tmp dirctory auto instDir = m_settings->get("InstanceDir").toString(); const QString tempRoot = FS::PathCombine(instDir, ".tmp"); FS::deletePath(tempRoot); } if (!m_urlsToImport.isEmpty()) { qDebug() << "<> Importing from url:" << m_urlsToImport; m_mainWindow->processURLs(m_urlsToImport); } } void Application::showFatalErrorMessage(const QString& title, const QString& content) { m_status = Application::Failed; auto dialog = CustomMessageBox::selectable(nullptr, title, content, QMessageBox::Critical); dialog->exec(); } Application::~Application() { // Shut down logger by setting the logger function to nothing qInstallMessageHandler(nullptr); } void Application::messageReceived(const QByteArray& message) { ApplicationMessage received; received.parse(message); auto& command = received.command; if (status() != Initialized) { bool isLoginAtempt = false; if (command == "import") { QString url = received.args["url"]; isLoginAtempt = !url.isEmpty() && normalizeImportUrl(url).scheme() == BuildConfig.LAUNCHER_APP_BINARY_NAME; } if (!isLoginAtempt) { qDebug() << "Received message" << message << "while still initializing. It will be ignored."; return; } } if (command == "activate") { showMainWindow(); } else if (command == "import") { QString url = received.args["url"]; if (url.isEmpty()) { qWarning() << "Received" << command << "message without a zip path/URL."; return; } if (!m_mainWindow) { showMainWindow(false); } m_mainWindow->processURLs({ normalizeImportUrl(url) }); } else if (command == "launch") { QString id = received.args["id"]; QString server = received.args["server"]; QString world = received.args["world"]; QString profile = received.args["profile"]; bool offline = received.args["offline_enabled"] == "true"; QString offlineName = received.args["offline_name"]; BaseInstance* instance; if (!id.isEmpty()) { instance = instances()->getInstanceById(id); if (!instance) { qWarning() << "Launch command requires an valid instance ID. " << id << "resolves to nothing."; return; } } else { qWarning() << "Launch command called without an instance ID..."; return; } MinecraftTarget::Ptr serverObject = nullptr; if (!server.isEmpty()) { serverObject = std::make_shared(MinecraftTarget::parse(server, false)); } else if (!world.isEmpty()) { serverObject = std::make_shared(MinecraftTarget::parse(world, true)); } MinecraftAccountPtr accountObject; if (!profile.isEmpty()) { accountObject = accounts()->getAccountByProfileName(profile); if (!accountObject) { qWarning() << "Launch command requires the specified profile to be valid. " << profile << "does not resolve to any account."; return; } } launch(instance, offline ? LaunchMode::Offline : LaunchMode::Normal, serverObject, accountObject, offlineName); } else { qWarning() << "Received invalid message" << message; } } TranslationsModel* Application::translations() { return m_translations.get(); } JavaInstallList* Application::javalist() { if (!m_javalist) { m_javalist.reset(new JavaInstallList()); } return m_javalist.get(); } QIcon Application::logo() { return QIcon(":/" + BuildConfig.LAUNCHER_SVGFILENAME); } bool Application::openJsonEditor(const QString& filename) { const QString file = QDir::current().absoluteFilePath(filename); if (m_settings->get("JsonEditor").toString().isEmpty()) { return DesktopServices::openUrl(QUrl::fromLocalFile(file)); } else { // return DesktopServices::openFile(m_settings->get("JsonEditor").toString(), file); return DesktopServices::run(m_settings->get("JsonEditor").toString(), { file }); } } bool Application::launch(BaseInstance* instance, LaunchMode mode, MinecraftTarget::Ptr targetToJoin, MinecraftAccountPtr accountToUse, const QString& offlineName) { if (m_updateRunning) { qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; } else if (instance->canLaunch()) { QMutexLocker locker(&m_instanceExtrasMutex); auto& extras = m_instanceExtras[instance->id()]; auto window = extras.window; if (window) { if (!window->saveAll()) { return false; } } auto& controller = extras.controller; controller.reset(new LaunchController()); controller->setInstance(instance); controller->setLaunchMode(mode); controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get()); controller->setTargetToJoin(targetToJoin); controller->setAccountToUse(accountToUse); controller->setOfflineName(offlineName); if (window) { controller->setParentWidget(window); } else if (m_mainWindow) { controller->setParentWidget(m_mainWindow); } connect(controller.get(), &LaunchController::finished, this, &Application::controllerFinished); addRunningInstance(); QMetaObject::invokeMethod(controller.get(), &Task::start, Qt::QueuedConnection); return true; } else if (instance->isRunning()) { showInstanceWindow(instance, "console"); return true; } else if (instance->canEdit()) { showInstanceWindow(instance); return true; } return false; } bool Application::kill(BaseInstance* instance) { if (!instance->isRunning()) { qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running."; return false; } QMutexLocker locker(&m_instanceExtrasMutex); auto& extras = m_instanceExtras[instance->id()]; // NOTE: copy of the shared pointer keeps it alive auto& controller = extras.controller; locker.unlock(); if (controller) { return controller->abort(); } return true; } void Application::closeCurrentWindow() { if (focusWindow()) focusWindow()->close(); } void Application::addRunningInstance() { m_runningInstances++; if (m_runningInstances == 1) { emit updateAllowedChanged(false); } } void Application::subRunningInstance() { if (m_runningInstances == 0) { qCritical() << "Something went really wrong and we now have less than 0 running instances... WTF"; return; } m_runningInstances--; if (m_runningInstances == 0) { emit updateAllowedChanged(true); } } bool Application::shouldExitNow() const { return m_runningInstances == 0 && m_openWindows == 0; } bool Application::updatesAreAllowed() { return m_runningInstances == 0; } void Application::updateIsRunning(bool running) { m_updateRunning = running; } void Application::controllerFinished() { auto controller = qobject_cast(sender()); if (!controller) return; auto id = controller->id(); QMutexLocker locker(&m_instanceExtrasMutex); auto& extras = m_instanceExtras.at(id); const bool wasSuccessful = controller->wasSuccessful(); // on success, do... if (wasSuccessful && controller->instance()->settings()->get("AutoCloseConsole").toBool()) { if (extras.window) { QMetaObject::invokeMethod(extras.window, &QWidget::close, Qt::QueuedConnection); } } extras.controller.reset(); subRunningInstance(); // quit when there are no more windows. if (shouldExitNow()) { m_status = wasSuccessful ? Succeeded : Failed; exit(wasSuccessful ? 0 : 1); } } void Application::ShowGlobalSettings(class QWidget* parent, QString open_page) { if (!m_globalSettingsProvider) { return; } emit globalSettingsAboutToOpen(); { SettingsObject::Lock lock(APPLICATION->settings()); PageDialog dlg(m_globalSettingsProvider.get(), open_page, parent); connect(&dlg, &PageDialog::applied, this, &Application::globalSettingsApplied); dlg.exec(); } } MainWindow* Application::showMainWindow(bool minimized) { if (m_mainWindow) { m_mainWindow->setWindowState(m_mainWindow->windowState() & ~Qt::WindowMinimized); m_mainWindow->raise(); m_mainWindow->activateWindow(); } else { m_mainWindow = new MainWindow(); m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toString().toUtf8())); m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toString().toUtf8())); if (minimized) { m_mainWindow->showMinimized(); } else { m_mainWindow->show(); } m_mainWindow->checkInstancePathForProblems(); connect(this, &Application::updateAllowedChanged, m_mainWindow, &MainWindow::updatesAllowedChanged); connect(m_mainWindow, &MainWindow::isClosing, this, &Application::on_windowClose); m_openWindows++; } return m_mainWindow; } ViewLogWindow* Application::showLogWindow() { if (m_viewLogWindow) { m_viewLogWindow->setWindowState(m_viewLogWindow->windowState() & ~Qt::WindowMinimized); m_viewLogWindow->raise(); m_viewLogWindow->activateWindow(); } else { m_viewLogWindow = new ViewLogWindow(); connect(m_viewLogWindow, &ViewLogWindow::isClosing, this, &Application::on_windowClose); m_openWindows++; } return m_viewLogWindow; } InstanceWindow* Application::showInstanceWindow(BaseInstance* instance, QString page) { if (!instance) return nullptr; auto id = instance->id(); QMutexLocker locker(&m_instanceExtrasMutex); auto& extras = m_instanceExtras[id]; auto& window = extras.window; if (window) { // If the window is minimized on macOS or Windows, activate and bring it up #ifdef Q_OS_MACOS if (window->isMinimized()) { window->setWindowState(window->windowState() & ~Qt::WindowMinimized); } #elif defined(Q_OS_WIN) if (window->isMinimized()) { window->showNormal(); } #endif window->raise(); window->activateWindow(); } else { window = new InstanceWindow(instance); m_openWindows++; connect(window, &InstanceWindow::isClosing, this, &Application::on_windowClose); } if (!page.isEmpty()) { window->selectPage(page); } if (extras.controller) { extras.controller->setParentWidget(window); } return window; } void Application::on_windowClose() { m_openWindows--; auto instWindow = qobject_cast(sender()); if (instWindow) { QMutexLocker locker(&m_instanceExtrasMutex); auto& extras = m_instanceExtras[instWindow->instanceId()]; extras.window = nullptr; if (extras.controller) { extras.controller->setParentWidget(m_mainWindow); } } auto mainWindow = qobject_cast(sender()); if (mainWindow) { m_mainWindow = nullptr; } auto logWindow = qobject_cast(sender()); if (logWindow) { m_viewLogWindow = nullptr; } // quit when there are no more windows. if (shouldExitNow()) { exit(0); } } void Application::updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password) { // Set the application proxy settings. if (proxyTypeStr == "SOCKS5") { QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, password)); } else if (proxyTypeStr == "HTTP") { QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, password)); } else if (proxyTypeStr == "None") { // If we have no proxy set, set no proxy and return. QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy)); } else { // If we have "Default" selected, set Qt to use the system proxy settings. QNetworkProxyFactory::setUseSystemConfiguration(true); } qDebug() << "Detecting proxy settings..."; QNetworkProxy proxy = QNetworkProxy::applicationProxy(); m_network->setProxy(proxy); QString proxyDesc; if (proxy.type() == QNetworkProxy::NoProxy) { qDebug() << "Using no proxy is an option!"; return; } switch (proxy.type()) { case QNetworkProxy::DefaultProxy: proxyDesc = "Default proxy: "; break; case QNetworkProxy::Socks5Proxy: proxyDesc = "Socks5 proxy: "; break; case QNetworkProxy::HttpProxy: proxyDesc = "HTTP proxy: "; break; case QNetworkProxy::HttpCachingProxy: proxyDesc = "HTTP caching: "; break; case QNetworkProxy::FtpCachingProxy: proxyDesc = "FTP caching: "; break; default: proxyDesc = "DERP proxy: "; break; } proxyDesc += QString("%1:%2").arg(proxy.hostName()).arg(proxy.port()); qDebug() << proxyDesc; } HttpMetaCache* Application::metacache() { return m_metacache.get(); } QNetworkAccessManager* Application::network() { return m_network.get(); } Meta::Index* Application::metadataIndex() { if (!m_metadataIndex) { m_metadataIndex.reset(new Meta::Index()); } return m_metadataIndex.get(); } void Application::updateCapabilities() { m_capabilities = None; if (!getMSAClientID().isEmpty()) m_capabilities |= SupportsMSA; if (!getFlameAPIKey().isEmpty()) m_capabilities |= SupportsFlame; #ifdef Q_OS_LINUX if (gamemode_query_status() >= 0) m_capabilities |= SupportsGameMode; if (!LibraryUtils::findMangoHud().isEmpty()) m_capabilities |= SupportsMangoHud; #endif } void Application::detectLibraries() { #ifdef Q_OS_LINUX m_detectedGLFWPath = LibraryUtils::find(BuildConfig.GLFW_LIBRARY_NAME); m_detectedOpenALPath = LibraryUtils::find(BuildConfig.OPENAL_LIBRARY_NAME); qDebug() << "Detected native libraries:" << m_detectedGLFWPath << m_detectedOpenALPath; #endif } QString Application::getJarPath(QString jarFile) { QStringList potentialPaths = { #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) FS::PathCombine(m_rootPath, "share", BuildConfig.LAUNCHER_NAME), #endif FS::PathCombine(m_rootPath, "jars"), FS::PathCombine(applicationDirPath(), "jars"), FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging }; for (QString p : potentialPaths) { QString jarPath = FS::PathCombine(p, jarFile); if (QFileInfo(jarPath).isFile()) return jarPath; } return {}; } QString Application::getMSAClientID() { QString clientIDOverride = m_settings->get("MSAClientIDOverride").toString(); if (!clientIDOverride.isEmpty()) { return clientIDOverride; } return BuildConfig.MSA_CLIENT_ID; } QString Application::getFlameAPIKey() { QString keyOverride = m_settings->get("FlameKeyOverride").toString(); if (!keyOverride.isEmpty()) { return keyOverride; } return BuildConfig.FLAME_API_KEY; } QString Application::getModrinthAPIToken() { QString tokenOverride = m_settings->get("ModrinthToken").toString(); if (!tokenOverride.isEmpty()) return tokenOverride; return QString(); } QString Application::getUserAgent() { QString uaOverride = m_settings->get("UserAgentOverride").toString(); if (!uaOverride.isEmpty()) { return uaOverride.replace("$LAUNCHER_VER", BuildConfig.printableVersionString()); } return BuildConfig.USER_AGENT; } bool Application::handleDataMigration(const QString& currentData, const QString& oldData, const QString& name, const QString& configFile) const { QString nomigratePath = FS::PathCombine(currentData, name + "_nomigrate.txt"); QStringList configPaths = { FS::PathCombine(oldData, configFile), FS::PathCombine(oldData, BuildConfig.LAUNCHER_CONFIGFILE) }; QLocale locale; // Is there a valid config at the old location? bool configExists = false; for (QString configPath : configPaths) { configExists |= QFileInfo::exists(configPath); } if (!configExists || QFileInfo::exists(nomigratePath)) { qDebug() << "<> No migration needed from" << name; return false; } QString message; bool currentExists = QFileInfo::exists(FS::PathCombine(currentData, BuildConfig.LAUNCHER_CONFIGFILE)); if (currentExists) { message = tr("Old data from %1 was found, but you already have existing data for %2. Sadly you will need to migrate yourself. Do " "you want to be reminded of the pending data migration next time you start %2?") .arg(name, BuildConfig.LAUNCHER_DISPLAYNAME); } else { message = tr("It looks like you used %1 before. Do you want to migrate your data to the new location of %2?") .arg(name, BuildConfig.LAUNCHER_DISPLAYNAME); QFileInfo logInfo(FS::PathCombine(oldData, name + "-0.log")); if (logInfo.exists()) { QString lastModified = logInfo.lastModified().toString(locale.dateFormat()); message = tr("It looks like you used %1 on %2 before. Do you want to migrate your data to the new location of %3?") .arg(name, lastModified, BuildConfig.LAUNCHER_DISPLAYNAME); } } QMessageBox::StandardButton askMoveDialogue = QMessageBox::question(nullptr, BuildConfig.LAUNCHER_DISPLAYNAME, message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); auto setDoNotMigrate = [&nomigratePath] { QFile file(nomigratePath); if (!file.open(QIODevice::WriteOnly)) { qWarning() << "setDoNotMigrate failed; Failed to open file" << file.fileName() << "for writing:" << file.errorString(); } }; // create no-migrate file if user doesn't want to migrate if (askMoveDialogue != QMessageBox::Yes) { qDebug() << "<> Migration declined for" << name; setDoNotMigrate(); return currentExists; // cancel further migrations, if we already have a data directory } if (!currentExists) { // Migrate! using namespace Filters; QList filters; filters.append(equals(configFile)); filters.append(equals(BuildConfig.LAUNCHER_CONFIGFILE)); // it's possible that we already used that directory before filters.append(startsWith("logs/")); filters.append(equals("accounts.json")); filters.append(startsWith("accounts/")); filters.append(startsWith("assets/")); filters.append(startsWith("icons/")); filters.append(startsWith("instances/")); filters.append(startsWith("libraries/")); filters.append(startsWith("mods/")); filters.append(startsWith("themes/")); ProgressDialog diag; DataMigrationTask task(oldData, currentData, any(std::move(filters))); if (diag.execWithTask(&task)) { qDebug() << "<> Migration succeeded"; setDoNotMigrate(); } else { QString reason = task.failReason(); QMessageBox::critical(nullptr, BuildConfig.LAUNCHER_DISPLAYNAME, tr("Migration failed! Reason: %1").arg(reason)); } } else { qWarning() << "<> Migration was skipped, due to existing data"; } return true; } void Application::triggerUpdateCheck() { if (m_updater) { qDebug() << "Checking for updates."; m_updater->setBetaAllowed(false); // There are no other channels than stable m_updater->checkForUpdates(); } else { qDebug() << "Updater not available."; } } QUrl Application::normalizeImportUrl(const QString& url) { auto local_file = QFileInfo(url); if (local_file.exists()) { return QUrl::fromLocalFile(local_file.absoluteFilePath()); } else { return QUrl::fromUserInput(url); } } const QString Application::javaPath() { return m_settings->get("JavaDir").toString(); } void Application::addQSavePath(QString path) { QMutexLocker locker(&m_qsaveResourcesMutex); m_qsaveResources[path] = m_qsaveResources.value(path, 0) + 1; } void Application::removeQSavePath(QString path) { QMutexLocker locker(&m_qsaveResourcesMutex); auto count = m_qsaveResources.value(path, 0) - 1; if (count <= 0) { m_qsaveResources.remove(path); } else { m_qsaveResources[path] = count; } } bool Application::checkQSavePath(QString path) { QMutexLocker locker(&m_qsaveResourcesMutex); for (auto partialPath : m_qsaveResources.keys()) { if (path.startsWith(partialPath) && m_qsaveResources.value(partialPath, 0) > 0) { return true; } } return false; } PrismLauncher-11.0.3/launcher/tasks/0000755000175100017510000000000015224505336016711 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/tasks/SequentialTask.cpp0000644000175100017510000000410315224505336022350 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "SequentialTask.h" #include #include "tasks/ConcurrentTask.h" SequentialTask::SequentialTask(QString task_name) : ConcurrentTask(task_name, 1) {} void SequentialTask::subTaskFailed(Task::Ptr task, const QString& msg) { qWarning() << msg; ConcurrentTask::subTaskFailed(task, msg); emitFailed(msg); } void SequentialTask::updateState() { setProgress(m_done.count(), totalSize()); setStatus(tr("Executing task %1 out of %2").arg(QString::number(m_doing.count() + m_done.count()), QString::number(totalSize()))); } PrismLauncher-11.0.3/launcher/tasks/SequentialTask.h0000644000175100017510000000424615224505336022025 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "ConcurrentTask.h" /** A concurrent task that only allows one concurrent task :) * * This should be used when there's a need to maintain a strict ordering of task executions, and * the starting of a task is contingent on the success of the previous one. * * See MultipleOptionsTask if that's not the case. */ class SequentialTask : public ConcurrentTask { Q_OBJECT public: explicit SequentialTask(QString task_name = ""); ~SequentialTask() override = default; protected slots: virtual void subTaskFailed(Task::Ptr, const QString& msg) override; protected: void updateState() override; }; PrismLauncher-11.0.3/launcher/tasks/Task.h0000644000175100017510000001555615224505336020000 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * PrismLauncher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include "QObjectPtr.h" Q_DECLARE_LOGGING_CATEGORY(taskLogC) enum class TaskStepState { Waiting, Running, Failed, Succeeded }; Q_DECLARE_METATYPE(TaskStepState) struct TaskStepProgress { QUuid uid; qint64 current = 0; qint64 total = -1; qint64 old_current = 0; qint64 old_total = -1; QString status = ""; QString details = ""; TaskStepState state = TaskStepState::Waiting; TaskStepProgress() { this->uid = QUuid::createUuid(); } TaskStepProgress(QUuid uid_) : uid(uid_) {} bool isDone() const { return (state == TaskStepState::Failed) || (state == TaskStepState::Succeeded); } void update(qint64 new_current, qint64 new_total) { this->old_current = this->current; this->old_total = this->total; this->current = new_current; this->total = new_total; this->state = TaskStepState::Running; } }; Q_DECLARE_METATYPE(TaskStepProgress) using TaskStepProgressList = QList>; /*! * Represents a task that has to be done. * To create a task, you need to subclass this class, implement the executeTask() method and call * emitSucceeded() or emitFailed() when the task is done. * the caller needs to call start() to start the task. */ class Task : public QObject, public QRunnable { Q_OBJECT public: using Ptr = shared_qobject_ptr; enum class State { Inactive, Running, Succeeded, Failed, AbortedByUser }; public: explicit Task(bool show_debug_log = true); ~Task() override; bool isRunning() const; bool isFinished() const; bool wasSuccessful() const; /*! * MultiStep tasks are combinations of multiple tasks into a single logical task. * The main usage of this is in SequencialTask. */ virtual auto isMultiStep() const -> bool { return false; } /*! * Returns the string that was passed to emitFailed as the error message when the task failed. * If the task hasn't failed, returns an empty string. */ QString failReason() const; virtual QStringList warnings() const; virtual bool canAbort() const { return m_can_abort; } auto getState() const -> State { return m_state; } QString getStatus() { return m_status; } QString getDetails() { return m_details; } qint64 getProgress() { return m_progress; } qint64 getTotalProgress() { return m_progressTotal; } virtual auto getStepProgress() const -> TaskStepProgressList { return {}; } QUuid getUid() { return m_uid; } // Copies the other task's status, details, progress, and step progress to this task; and sets up connections for future propagation void propagateFromOther(Task* other); protected: void logWarning(const QString& line); private: QString describe(); signals: void started(); void progress(qint64 current, qint64 total); //! called when a task has either succeeded, aborted or failed. void finished(); //! called when a task has succeeded void succeeded(); //! called when a task has been aborted by calling abort() void aborted(); void failed(QString reason); void status(QString status); void details(QString details); void warningLogged(const QString& warning); void stepProgress(TaskStepProgress const& task_progress); //! Emitted when the canAbort() status has changed. */ void abortStatusChanged(bool can_abort); void abortButtonTextChanged(QString text); public slots: // QRunnable's interface void run() override { start(); } //! used by the task caller to start the task virtual void start(); //! used by external code to ask the task to abort virtual bool abort() { if (canAbort() && isRunning()) emitAborted(); return canAbort(); } void setAbortable(bool can_abort) { m_can_abort = can_abort; emit abortStatusChanged(can_abort); } void setAbortButtonText(QString text) { emit abortButtonTextChanged(text); } protected: //! The task subclass must implement this method. This method is called to start to run the task. //! The task is not finished when this method returns. the subclass must manually call emitSucceeded() or emitFailed() instead. virtual void executeTask() = 0; protected slots: //! The Task subclass must call this method when the task has succeeded virtual void emitSucceeded(); //! **The Task subclass** must call this method when the task has aborted. External code should call abort() instead. virtual void emitAborted(); //! The Task subclass must call this method when the task has failed virtual void emitFailed(QString reason = ""); virtual void propagateStepProgress(TaskStepProgress const& task_progress); public slots: void setStatus(const QString& status); void setDetails(const QString& details); void setProgress(qint64 current, qint64 total); protected: State m_state = State::Inactive; QStringList m_Warnings; QString m_failReason = ""; QString m_status; QString m_details; int m_progress = 0; int m_progressTotal = 100; // TODO: Nuke in favor of QLoggingCategory bool m_show_debug = true; private: // Change using setAbortStatus bool m_can_abort = false; QUuid m_uid; }; PrismLauncher-11.0.3/launcher/tasks/ConcurrentTask.h0000644000175100017510000000707615224505336022041 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include "tasks/Task.h" /*! * Runs a list of tasks concurrently (according to `max_concurrent` parameter). * Behaviour is the same as regular Task (e.g. starts using start()) */ class ConcurrentTask : public Task { Q_OBJECT public: using Ptr = shared_qobject_ptr; explicit ConcurrentTask(QString task_name = "", int max_concurrent = 6); ~ConcurrentTask() override; // safe to call before starting the task void setMaxConcurrent(int max_concurrent) { m_total_max_size = max_concurrent; } bool canAbort() const override { return true; } inline auto isMultiStep() const -> bool override { return totalSize() > 1; } auto getStepProgress() const -> TaskStepProgressList override; //! Adds a task to execute in this ConcurrentTask void addTask(Task::Ptr task); public slots: bool abort() override; /** Resets the internal state of the task. * This allows the same task to be re-used. */ void clear(); protected slots: void executeTask() override; virtual void executeNextSubTask(); void subTaskSucceeded(Task::Ptr); virtual void subTaskFailed(Task::Ptr, const QString& msg); void subTaskFinished(Task::Ptr, TaskStepState); void subTaskStatus(Task::Ptr task, const QString& msg); void subTaskDetails(Task::Ptr task, const QString& msg); void subTaskProgress(Task::Ptr task, qint64 current, qint64 total); protected: // NOTE: This is not thread-safe. unsigned int totalSize() const { return static_cast(m_queue.size() + m_doing.size() + m_done.size()); } virtual void updateState(); void startSubTask(Task::Ptr task); protected: QQueue m_queue; QHash m_doing; QHash m_done; QHash m_failed; QHash m_succeeded; QHash> m_task_progress; int m_total_max_size; }; PrismLauncher-11.0.3/launcher/tasks/MultipleOptionsTask.h0000644000175100017510000000367215224505336023064 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "ConcurrentTask.h" /* This task type will attempt to do run each of it's subtasks in sequence, * until one of them succeeds. When that happens, the remaining tasks will not run. * */ class MultipleOptionsTask : public ConcurrentTask { Q_OBJECT public: explicit MultipleOptionsTask(const QString& task_name = ""); ~MultipleOptionsTask() override = default; private slots: void executeNextSubTask() override; void updateState() override; }; PrismLauncher-11.0.3/launcher/tasks/ConcurrentTask.cpp0000644000175100017510000001752715224505336022376 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ConcurrentTask.h" #include #include "tasks/Task.h" ConcurrentTask::ConcurrentTask(QString task_name, int max_concurrent) : Task(), m_total_max_size(max_concurrent) { setObjectName(task_name); } ConcurrentTask::~ConcurrentTask() { for (auto task : m_doing) { if (task) task->disconnect(this); } } auto ConcurrentTask::getStepProgress() const -> TaskStepProgressList { return m_task_progress.values(); } void ConcurrentTask::addTask(Task::Ptr task) { m_queue.append(task); } void ConcurrentTask::executeTask() { for (auto i = 0; i < m_total_max_size; i++) QMetaObject::invokeMethod(this, &ConcurrentTask::executeNextSubTask, Qt::QueuedConnection); } bool ConcurrentTask::abort() { m_queue.clear(); if (m_doing.isEmpty()) { // Don't call emitAborted() here, we want to bypass the 'is the task running' check emit aborted(); emit finished(); return true; } bool suceedeed = true; QMutableHashIterator doing_iter(m_doing); while (doing_iter.hasNext()) { auto task = doing_iter.next(); disconnect(task->get(), &Task::aborted, this, 0); suceedeed &= (task.value())->abort(); } if (suceedeed) emitAborted(); else emitFailed(tr("Failed to abort all running tasks.")); return suceedeed; } void ConcurrentTask::clear() { Q_ASSERT(!isRunning()); m_done.clear(); m_failed.clear(); m_queue.clear(); m_task_progress.clear(); m_progress = 0; } void ConcurrentTask::executeNextSubTask() { if (!isRunning()) { return; } if (m_doing.count() >= m_total_max_size) { return; } if (m_queue.isEmpty()) { if (m_doing.isEmpty()) { if (m_failed.isEmpty()) { emitSucceeded(); } else if (m_failed.count() == 1) { auto task = m_failed.keys().first(); auto reason = task->failReason(); if (reason.isEmpty()) { // clearly a bug somewhere reason = tr("Task failed"); } emitFailed(reason); } else { QStringList failReason; for (auto t : m_failed) { auto reason = t->failReason(); if (!reason.isEmpty()) { failReason << reason; } } if (failReason.isEmpty()) { emitFailed(tr("Multiple subtasks failed")); } else { emitFailed(tr("Multiple subtasks failed\n%1").arg(failReason.join("\n"))); } } } return; } startSubTask(m_queue.dequeue()); } void ConcurrentTask::startSubTask(Task::Ptr next) { connect(next.get(), &Task::succeeded, this, [this, next]() { subTaskSucceeded(next); }); connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); }); // this should never happen but if it does, it's better to fail the task than get stuck connect(next.get(), &Task::aborted, this, [this, next] { subTaskFailed(next, "Aborted"); }); connect(next.get(), &Task::status, this, [this, next](QString msg) { subTaskStatus(next, msg); }); connect(next.get(), &Task::details, this, [this, next](QString msg) { subTaskDetails(next, msg); }); connect(next.get(), &Task::stepProgress, this, &ConcurrentTask::stepProgress); connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total) { subTaskProgress(next, current, total); }); m_doing.insert(next.get(), next); auto task_progress = std::make_shared(next->getUid()); m_task_progress.insert(next->getUid(), task_progress); updateState(); QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection); } void ConcurrentTask::subTaskFinished(Task::Ptr task, TaskStepState state) { m_done.insert(task.get(), task); (state == TaskStepState::Succeeded ? m_succeeded : m_failed).insert(task.get(), task); m_doing.remove(task.get()); auto task_progress = *m_task_progress.value(task->getUid()); task_progress.state = state; m_task_progress.remove(task->getUid()); disconnect(task.get(), 0, this, 0); emit stepProgress(task_progress); updateState(); QMetaObject::invokeMethod(this, &ConcurrentTask::executeNextSubTask, Qt::QueuedConnection); } void ConcurrentTask::subTaskSucceeded(Task::Ptr task) { subTaskFinished(task, TaskStepState::Succeeded); } void ConcurrentTask::subTaskFailed(Task::Ptr task, [[maybe_unused]] const QString& msg) { subTaskFinished(task, TaskStepState::Failed); } void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg) { auto task_progress = m_task_progress.value(task->getUid()); task_progress->status = msg; task_progress->state = TaskStepState::Running; emit stepProgress(*task_progress); if (totalSize() == 1) { setStatus(msg); } } void ConcurrentTask::subTaskDetails(Task::Ptr task, const QString& msg) { auto task_progress = m_task_progress.value(task->getUid()); task_progress->details = msg; task_progress->state = TaskStepState::Running; emit stepProgress(*task_progress); if (totalSize() == 1) { setDetails(msg); } } void ConcurrentTask::subTaskProgress(Task::Ptr task, qint64 current, qint64 total) { auto task_progress = m_task_progress.value(task->getUid()); task_progress->update(current, total); emit stepProgress(*task_progress); updateState(); if (totalSize() == 1) { setProgress(task_progress->current, task_progress->total); } } void ConcurrentTask::updateState() { if (totalSize() > 1) { setProgress(m_done.count(), totalSize()); setStatus(tr("Executing %1 task(s) (%2 out of %3 are done)") .arg(QString::number(m_doing.count()), QString::number(m_done.count()), QString::number(totalSize()))); } else { QString status = tr("Please wait..."); if (m_queue.size() > 0) { status = tr("Waiting for a task to start..."); } else if (m_doing.size() > 0) { status = tr("Executing 1 task:"); } else if (m_done.size() > 0) { status = tr("Task finished."); } setStatus(status); } } PrismLauncher-11.0.3/launcher/tasks/Task.cpp0000644000175100017510000001452515224505336020326 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (c) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Task.h" #include #include "AssertHelpers.h" Q_LOGGING_CATEGORY(taskLogC, "launcher.task") Task::Task(bool show_debug) : m_show_debug(show_debug) { m_uid = QUuid::createUuid(); setAutoDelete(false); } Task::~Task() { if (isRunning()) { qCWarning(taskLogC) << "Task" << describe() << "disposed while running!"; } } void Task::setStatus(const QString& new_status) { if (m_status != new_status) { m_status = new_status; emit status(m_status); } } void Task::setDetails(const QString& new_details) { if (m_details != new_details) { m_details = new_details; emit details(m_details); } } void Task::setProgress(qint64 current, qint64 total) { if ((m_progress != current) || (m_progressTotal != total)) { m_progress = current; m_progressTotal = total; emit progress(m_progress, m_progressTotal); } } void Task::start() { switch (m_state) { case State::Inactive: { if (m_show_debug) qCDebug(taskLogC) << "Task" << describe() << "starting for the first time"; break; } case State::AbortedByUser: { if (m_show_debug) qCDebug(taskLogC) << "Task" << describe() << "restarting for after being aborted by user"; break; } case State::Failed: { if (m_show_debug) qCDebug(taskLogC) << "Task" << describe() << "restarting for after failing at first"; break; } case State::Succeeded: { if (m_show_debug) qCDebug(taskLogC) << "Task" << describe() << "restarting for after succeeding at first"; break; } case State::Running: { if (ASSERT_NEVER(isRunning()) && m_show_debug) qCWarning(taskLogC) << "The launcher tried to start task" << describe() << "while it was already running!"; return; } } // NOTE: only fall through to here in end states m_state = State::Running; emit started(); executeTask(); } void Task::emitFailed(QString reason) { // Don't fail twice. if (ASSERT_NEVER(!isRunning())) { qCCritical(taskLogC) << "Task" << describe() << "failed while not running!!!!:" << reason; return; } m_state = State::Failed; m_failReason = reason; qCCritical(taskLogC) << "Task" << describe() << "failed:" << reason; emit failed(reason); emit finished(); } void Task::emitAborted() { // Don't abort twice. if (ASSERT_NEVER(!isRunning())) { qCCritical(taskLogC) << "Task" << describe() << "aborted while not running!!!!"; return; } m_state = State::AbortedByUser; m_failReason = tr("Aborted"); if (m_show_debug) qCDebug(taskLogC) << "Task" << describe() << "aborted."; emit aborted(); emit finished(); } void Task::emitSucceeded() { // Don't succeed twice. if (ASSERT_NEVER(!isRunning())) { qCCritical(taskLogC) << "Task" << describe() << "succeeded while not running!!!!"; return; } m_state = State::Succeeded; if (m_show_debug) qCDebug(taskLogC) << "Task" << describe() << "succeeded"; emit succeeded(); emit finished(); } void Task::propagateStepProgress(TaskStepProgress const& task_progress) { emit stepProgress(task_progress); } QString Task::describe() { QString outStr; QTextStream out(&outStr); out << metaObject()->className() << QChar('('); auto name = objectName(); if (name.isEmpty()) { out << QString("0x%1").arg(reinterpret_cast(this), 0, 16); } else { out << name; } out << " ID: " << m_uid.toString(QUuid::WithoutBraces); out << QChar(')'); out.flush(); return outStr; } bool Task::isRunning() const { return m_state == State::Running; } bool Task::isFinished() const { return m_state != State::Running && m_state != State::Inactive; } bool Task::wasSuccessful() const { return m_state == State::Succeeded; } QString Task::failReason() const { return m_failReason; } void Task::propagateFromOther(Task* other) { Q_ASSERT(other); connect(other, &Task::status, this, &Task::setStatus); connect(other, &Task::details, this, &Task::setDetails); connect(other, &Task::progress, this, &Task::setProgress); connect(other, &Task::stepProgress, this, &Task::propagateStepProgress); setStatus(other->getStatus()); setDetails(other->getDetails()); setProgress(other->getProgress(), other->getTotalProgress()); for (const auto& progress : other->getStepProgress()) { propagateStepProgress(*progress); } } void Task::logWarning(const QString& line) { qWarning() << line; m_Warnings.append(line); emit warningLogged(line); } QStringList Task::warnings() const { return m_Warnings; } PrismLauncher-11.0.3/launcher/tasks/MultipleOptionsTask.cpp0000644000175100017510000000424115224505336023410 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MultipleOptionsTask.h" #include MultipleOptionsTask::MultipleOptionsTask(const QString& task_name) : ConcurrentTask(task_name, 1) {} void MultipleOptionsTask::executeNextSubTask() { if (m_done.size() != m_failed.size()) { emitSucceeded(); return; } if (m_queue.isEmpty()) { emitFailed(tr("All attempts have failed!")); qWarning() << "All attempts have failed!"; return; } ConcurrentTask::executeNextSubTask(); } void MultipleOptionsTask::updateState() { setProgress(m_done.count(), totalSize()); setStatus(tr("Attempting task %1 out of %2").arg(QString::number(m_doing.count() + m_done.count()), QString::number(totalSize()))); } PrismLauncher-11.0.3/launcher/InstanceCopyTask.cpp0000644000175100017510000001736615224505336021527 0ustar runnerrunner#include "InstanceCopyTask.h" #include #include #include #include "FileSystem.h" #include "Filter.h" #include "NullInstance.h" #include "settings/INISettingsObject.h" #include "tasks/Task.h" InstanceCopyTask::InstanceCopyTask(BaseInstance* origInstance, const InstanceCopyPrefs& prefs) { m_origInstance = origInstance; m_keepPlaytime = prefs.isKeepPlaytimeEnabled(); m_useLinks = prefs.isUseSymLinksEnabled(); m_linkRecursively = prefs.isLinkRecursivelyEnabled(); m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled(); m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled(); m_useClone = prefs.isUseCloneEnabled(); QString filters = prefs.getSelectedFiltersAsRegex(); if (m_useLinks || m_useHardLinks) { if (!filters.isEmpty()) filters += "|"; filters += "instance.cfg"; } qDebug() << "CopyFilters:" << filters; if (!filters.isEmpty()) { // Set regex filter: // FIXME: get this from the original instance type... QRegularExpression regexp(filters, QRegularExpression::CaseInsensitiveOption); m_matcher = Filters::regexp(regexp); } } void InstanceCopyTask::executeTask() { setStatus(tr("Copying instance %1").arg(m_origInstance->name())); m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] { if (m_useClone) { FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath); folderClone.matcher(m_matcher); folderClone(true); setProgress(0, folderClone.totalCloned()); connect(&folderClone, &FS::clone::fileCloned, [this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); }); return folderClone(); } if (m_useLinks || m_useHardLinks) { std::unique_ptr savesCopy; if (m_copySaves) { QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft")); QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft")); QString staging_mc_dir; if (dotMCDir.exists() && !mcDir.exists()) staging_mc_dir = dotMCDir.filePath(); else staging_mc_dir = mcDir.filePath(); savesCopy = std::make_unique(FS::PathCombine(m_origInstance->gameRoot(), "saves"), FS::PathCombine(staging_mc_dir, "saves")); (*savesCopy)(true); setProgress(0, savesCopy->totalCopied()); connect(savesCopy.get(), &FS::copy::fileCopied, [this](QString src) { setProgress(m_progress + 1, m_progressTotal); }); } FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath); int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher); folderLink(true); setProgress(0, m_progressTotal + folderLink.totalToLink()); connect(&folderLink, &FS::create_link::fileLinked, [this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); }); bool there_were_errors = false; if (!folderLink()) { #if defined Q_OS_WIN32 if (!m_useHardLinks) { setProgress(0, m_progressTotal); qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks"; qDebug() << "attempting to run with privelage"; QEventLoop loop; bool got_priv_results = false; connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&got_priv_results, &loop](bool gotResults) { if (!gotResults) { qDebug() << "Privileged run exited without results!"; } got_priv_results = gotResults; loop.quit(); }); folderLink.runPrivileged(); loop.exec(); // wait for the finished signal for (auto result : folderLink.getResults()) { if (result.err_value != 0) { there_were_errors = true; } } if (savesCopy) { there_were_errors |= !(*savesCopy)(); } return got_priv_results && !there_were_errors; } #else qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str(); #endif return false; } if (savesCopy) { there_were_errors |= !(*savesCopy)(); } return !there_were_errors; } FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath); folderCopy.matcher(m_matcher); folderCopy(true); setProgress(0, folderCopy.totalCopied()); connect(&folderCopy, &FS::copy::fileCopied, [this]() { setProgress(m_progress + 1, m_progressTotal); }); return folderCopy(); }); connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &InstanceCopyTask::copyFinished); connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &InstanceCopyTask::copyAborted); m_copyFutureWatcher.setFuture(m_copyFuture); } void InstanceCopyTask::copyFinished() { auto successful = m_copyFuture.result(); if (!successful) { emitFailed(tr("Instance folder copy failed.")); return; } // FIXME: shouldn't this be able to report errors? auto instanceSettings = std::make_unique(FS::PathCombine(m_stagingPath, "instance.cfg")); BaseInstance* inst(new NullInstance(m_globalSettings, std::move(instanceSettings), m_stagingPath)); inst->setName(name()); inst->setIconKey(m_instIcon); if (!m_keepPlaytime) { inst->resetTimePlayed(); } if (m_useLinks) { inst->addLinkedInstanceId(m_origInstance->id()); auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt")); QByteArray allowed_symlinks; if (allowed_symlinks_file.exists()) { allowed_symlinks.append(FS::read(allowed_symlinks_file.filePath())); if (allowed_symlinks.right(1) != "\n") allowed_symlinks.append("\n"); // we want to be on a new line } allowed_symlinks.append(m_origInstance->gameRoot().toUtf8()); allowed_symlinks.append("\n"); if (allowed_symlinks_file.isSymLink()) FS::deletePath( allowed_symlinks_file .filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link. try { FS::write(allowed_symlinks_file.filePath(), allowed_symlinks); } catch (const FS::FileSystemException& e) { qCritical() << "Failed to write symlink :" << e.cause(); } } emitSucceeded(); } void InstanceCopyTask::copyAborted() { emitFailed(tr("Instance folder copy has been aborted.")); return; } bool InstanceCopyTask::abort() { if (m_copyFutureWatcher.isRunning()) { m_copyFutureWatcher.cancel(); // NOTE: Here we don't do `emitAborted()` because it will be done when `m_copyFutureWatcher` actually cancels, which may not occur // immediately. return true; } return false; } PrismLauncher-11.0.3/launcher/MessageLevel.h0000644000175100017510000000315415224505336020314 0ustar runnerrunner#pragma once #include #include /** * @brief the MessageLevel Enum * defines what level a log message is */ struct MessageLevel { enum class Enum { Unknown, /**< No idea what this is or where it came from */ StdOut, /**< Undetermined stderr messages */ StdErr, /**< Undetermined stdout messages */ Launcher, /**< Launcher Messages */ Trace, /**< Trace Messages */ Debug, /**< Debug Messages */ Info, /**< Info Messages */ Message, /**< Standard Messages */ Warning, /**< Warnings */ Error, /**< Errors */ Fatal, /**< Fatal Errors */ }; using enum Enum; constexpr MessageLevel(Enum e = Unknown) : m_type(e) {} static MessageLevel fromName(const QString& type); static MessageLevel fromQtMsgType(const QtMsgType& type); static MessageLevel fromLine(const QString& line); inline bool isValid() const { return m_type != Unknown; } std::strong_ordering operator<=>(const MessageLevel& other) const = default; std::strong_ordering operator<=>(const MessageLevel::Enum& other) const { return m_type <=> other; } explicit operator int() const { return static_cast(m_type); } explicit operator MessageLevel::Enum() { return m_type; } /* Get message level from a line. Line is modified if it was successful. */ static MessageLevel takeFromLine(QString& line); /* Get message level from a line from the launcher log. Line is modified if it was successful. */ static MessageLevel takeFromLauncherLine(QString& line); private: Enum m_type; }; PrismLauncher-11.0.3/launcher/FileIgnoreProxy.h0000644000175100017510000000671615224505336021034 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "SeparatorPrefixTree.h" class FileIgnoreProxy : public QSortFilterProxyModel { Q_OBJECT public: FileIgnoreProxy(QString root, QObject* parent); // NOTE: Sadly, we have to do sorting ourselves. bool lessThan(const QModelIndex& left, const QModelIndex& right) const; virtual Qt::ItemFlags flags(const QModelIndex& index) const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); QString relPath(const QString& path) const; bool setFilterState(QModelIndex index, Qt::CheckState state); bool shouldExpand(QModelIndex index); void setBlockedPaths(QStringList paths); inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return m_blocked; } inline SeparatorPrefixTree<'/'>& blockedPaths() { return m_blocked; } // list of file names that need to be removed completely from model inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; } inline QStringList& ignoreFilesWithSuffix() { return m_ignoreFilesSuffixes; } // list of relative paths that need to be removed completely from model inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; } bool filterFile(const QFileInfo& fileName) const; void loadBlockedPathsFromFile(const QString& fileName); void saveBlockedPathsToFile(const QString& fileName); protected: bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const; bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const; bool ignoreFile(QFileInfo file) const; private: const QString m_root; SeparatorPrefixTree<'/'> m_blocked; QStringList m_ignoreFiles; QStringList m_ignoreFilesSuffixes; SeparatorPrefixTree<'/'> m_ignoreFilePaths; }; PrismLauncher-11.0.3/launcher/InstanceImportTask.cpp0000644000175100017510000004167415224505336022066 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (c) 2022 flowln * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "InstanceImportTask.h" #include "Application.h" #include "FileSystem.h" #include "NullInstance.h" #include "QObjectPtr.h" #include "archive/ArchiveReader.h" #include "archive/ExtractZipTask.h" #include "icons/IconList.h" #include "icons/IconUtils.h" #include "modplatform/flame/FlameInstanceCreationTask.h" #include "modplatform/modrinth/ModrinthInstanceCreationTask.h" #include "modplatform/technic/TechnicPackProcessor.h" #include "settings/INISettingsObject.h" #include "tasks/Task.h" #include "net/ApiDownload.h" #include #include #include InstanceImportTask::InstanceImportTask(const QUrl& sourceUrl, QWidget* parent, QMap&& extra_info) : m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent) {} bool InstanceImportTask::abort() { if (!canAbort()) return false; bool wasAborted = false; if (m_task) wasAborted = m_task->abort(); return wasAborted; } void InstanceImportTask::executeTask() { setAbortable(true); if (m_sourceUrl.isLocalFile()) { m_archivePath = m_sourceUrl.toLocalFile(); processZipPack(); } else { setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString())); downloadFromUrl(); } } void InstanceImportTask::downloadFromUrl() { const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path()); auto entry = APPLICATION->metacache()->resolveEntry("general", path); entry->setStale(true); m_archivePath = entry->getFullPath(); auto filesNetJob = makeShared(tr("Modpack download"), APPLICATION->network()); filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry)); connect(filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::processZipPack); connect(filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::setProgress); connect(filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress); connect(filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::emitFailed); connect(filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::emitAborted); m_task.reset(filesNetJob); filesNetJob->start(); } QString cleanPath(QString path) { if (path == ".") return QString(); QString result = path; if (result.startsWith("./")) result = result.mid(2); return result; } void InstanceImportTask::processZipPack() { setStatus(tr("Attempting to determine instance type")); QDir extractDir(m_stagingPath); qDebug() << "Attempting to create instance from" << m_archivePath; // open the zip and find relevant files in it MMCZip::ArchiveReader packZip(m_archivePath); qDebug() << "Attempting to determine instance type"; QString root; // NOTE: Prioritize modpack platforms that aren't searched for recursively. // Especially Flame has a very common filename for its manifest, which may appear inside overrides for example // https://docs.modrinth.com/docs/modpacks/format_definition/#storage auto detectInstance = [this, &extractDir, &root](MMCZip::ArchiveReader::File* f, bool& stop) { if (!isRunning()) { stop = true; return true; } auto fileName = f->filename(); if (fileName == "modrinth.index.json") { // process as Modrinth pack qDebug() << "Modrinth:" << true; m_modpackType = ModpackType::Modrinth; stop = true; } else if (fileName == "bin/modpack.jar" || fileName == "bin/version.json") { // process as Technic pack qDebug() << "Technic:" << true; extractDir.mkpath("minecraft"); extractDir.cd("minecraft"); m_modpackType = ModpackType::Technic; stop = true; } else if (fileName == "manifest.json") { qDebug() << "Flame:" << true; m_modpackType = ModpackType::Flame; stop = true; } else if (QFileInfo fileInfo(fileName); fileInfo.fileName() == "instance.cfg") { qDebug() << "MultiMC:" << true; m_modpackType = ModpackType::MultiMC; root = cleanPath(fileInfo.path()); stop = true; } QCoreApplication::processEvents(); return true; }; if (!packZip.parse(detectInstance)) { emitFailed(tr("Unable to open supplied modpack zip file.")); return; } if (m_modpackType == ModpackType::Unknown) { emitFailed(tr("Archive does not contain a recognized modpack type.")); return; } setStatus(tr("Extracting modpack")); // make sure we extract just the pack auto zipTask = makeShared(m_archivePath, extractDir, root); auto progressStep = std::make_shared(); connect(zipTask.get(), &Task::finished, this, [this, progressStep] { progressStep->state = TaskStepState::Succeeded; stepProgress(*progressStep); }); connect(zipTask.get(), &Task::succeeded, this, &InstanceImportTask::extractFinished, Qt::QueuedConnection); connect(zipTask.get(), &Task::aborted, this, &InstanceImportTask::emitAborted); connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) { progressStep->state = TaskStepState::Failed; stepProgress(*progressStep); emitFailed(reason); }); connect(zipTask.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { progressStep->update(current, total); stepProgress(*progressStep); }); connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) { progressStep->status = status; stepProgress(*progressStep); }); connect(zipTask.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); }); m_task.reset(zipTask); zipTask->start(); } void InstanceImportTask::extractFinished() { setAbortable(false); QDir extractDir(m_stagingPath); qDebug() << "Fixing permissions for extracted pack files..."; QDirIterator it(extractDir, QDirIterator::Subdirectories); while (it.hasNext()) { auto filepath = it.next(); QFileInfo file(filepath); auto permissions = QFile::permissions(filepath); auto origPermissions = permissions; if (file.isDir()) { // Folder +rwx for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser; } else { // File +rw for current user permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser; } if (origPermissions != permissions) { if (!QFile::setPermissions(filepath, permissions)) { logWarning(tr("Could not fix permissions for %1").arg(filepath)); } else { qDebug() << "Fixed" << filepath; } } } switch (m_modpackType) { case ModpackType::MultiMC: processMultiMC(); return; case ModpackType::Technic: processTechnic(); return; case ModpackType::Flame: processFlame(); return; case ModpackType::Modrinth: processModrinth(); return; case ModpackType::Unknown: emitFailed(tr("Archive does not contain a recognized modpack type.")); return; } } bool installIcon(QString root, QString instIconKey) { auto importIconPath = IconUtils::findBestIconIn(root, instIconKey); if (importIconPath.isNull() || !QFile::exists(importIconPath)) importIconPath = IconUtils::findBestIconIn(root, "icon.png"); if (importIconPath.isNull() || !QFile::exists(importIconPath)) importIconPath = IconUtils::findBestIconIn(FS::PathCombine(root, "overrides"), "icon.png"); if (!importIconPath.isNull() && QFile::exists(importIconPath)) { // import icon auto iconList = APPLICATION->icons(); if (iconList->iconFileExists(instIconKey)) { iconList->deleteIcon(instIconKey); } iconList->installIcon(importIconPath, instIconKey + "." + QFileInfo(importIconPath).suffix()); return true; } return false; } void InstanceImportTask::processFlame() { shared_qobject_ptr inst_creation_task = nullptr; if (!m_extra_info.isEmpty()) { auto pack_id_it = m_extra_info.constFind("pack_id"); Q_ASSERT(pack_id_it != m_extra_info.constEnd()); auto pack_id = pack_id_it.value(); auto pack_version_id_it = m_extra_info.constFind("pack_version_id"); Q_ASSERT(pack_version_id_it != m_extra_info.constEnd()); auto pack_version_id = pack_version_id_it.value(); QString original_instance_id; auto original_instance_id_it = m_extra_info.constFind("original_instance_id"); if (original_instance_id_it != m_extra_info.constEnd()) original_instance_id = original_instance_id_it.value(); inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); } else { // FIXME: Find a way to get IDs in directly imported ZIPs inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, QString(), QString()); } inst_creation_task->setName(*this); // if the icon was specified by user, use that. otherwise pull icon from the pack if (m_instIcon == "default") { auto iconKey = QString("Flame_%1_Icon").arg(name()); if (installIcon(m_stagingPath, iconKey)) { m_instIcon = iconKey; } } inst_creation_task->setIcon(m_instIcon); inst_creation_task->setGroup(m_instGroup); inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); auto weak = inst_creation_task.toWeakRef(); connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] { if (auto sp = weak.lock()) { setOverride(sp->shouldOverride(), sp->originalInstanceID()); } emitSucceeded(); }); connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed); connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress); connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus); connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted); connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable); connect(inst_creation_task.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText); connect(inst_creation_task.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); }); m_task.reset(inst_creation_task); setAbortable(true); m_task->start(); } void InstanceImportTask::processTechnic() { shared_qobject_ptr packProcessor{ new Technic::TechnicPackProcessor }; connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded); connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed); packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath); } void InstanceImportTask::processMultiMC() { QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_unique(configPath); NullInstance instance(m_globalSettings, std::move(instanceSettings), m_stagingPath); // reset time played on import... because packs. instance.resetTimePlayed(); // set a new nice name instance.setName(name()); // if the icon was specified by user, use that. otherwise pull icon from the pack if (m_instIcon != "default") { instance.setIconKey(m_instIcon); } else { m_instIcon = instance.iconKey(); installIcon(instance.instanceRoot(), m_instIcon); } emitSucceeded(); } void InstanceImportTask::processModrinth() { shared_qobject_ptr inst_creation_task = nullptr; if (!m_extra_info.isEmpty()) { auto pack_id_it = m_extra_info.constFind("pack_id"); Q_ASSERT(pack_id_it != m_extra_info.constEnd()); auto pack_id = pack_id_it.value(); QString pack_version_id; auto pack_version_id_it = m_extra_info.constFind("pack_version_id"); if (pack_version_id_it != m_extra_info.constEnd()) pack_version_id = pack_version_id_it.value(); QString original_instance_id; auto original_instance_id_it = m_extra_info.constFind("original_instance_id"); if (original_instance_id_it != m_extra_info.constEnd()) original_instance_id = original_instance_id_it.value(); inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); } else { QString pack_id; if (!m_sourceUrl.isEmpty()) { static const QRegularExpression s_regex(R"(data\/([^\/]*)\/versions)"); pack_id = s_regex.match(m_sourceUrl.toString()).captured(1); } // FIXME: Find a way to get the ID in directly imported ZIPs inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id); } inst_creation_task->setName(*this); // if the icon was specified by user, use that. otherwise pull icon from the pack if (m_instIcon == "default") { auto iconKey = QString("Modrinth_%1_Icon").arg(name()); if (installIcon(m_stagingPath, iconKey)) { m_instIcon = iconKey; } } inst_creation_task->setIcon(m_instIcon); inst_creation_task->setGroup(m_instGroup); inst_creation_task->setConfirmUpdate(shouldConfirmUpdate()); auto weak = inst_creation_task.toWeakRef(); connect(inst_creation_task.get(), &Task::succeeded, this, [this, weak] { if (auto sp = weak.lock()) { setOverride(sp->shouldOverride(), sp->originalInstanceID()); } emitSucceeded(); }); connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed); connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress); connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress); connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus); connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails); connect(inst_creation_task.get(), &Task::aborted, this, &InstanceImportTask::emitAborted); connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable); connect(inst_creation_task.get(), &Task::abortButtonTextChanged, this, &Task::setAbortButtonText); connect(inst_creation_task.get(), &Task::warningLogged, this, [this](const QString& line) { m_Warnings.append(line); }); m_task.reset(inst_creation_task); setAbortable(true); m_task->start(); } PrismLauncher-11.0.3/launcher/JavaCommon.cpp0000644000175100017510000001414515224505336020327 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "JavaCommon.h" #include "java/JavaUtils.h" #include "ui/dialogs/CustomMessageBox.h" #include bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent) { static const QRegularExpression s_memRegex("-Xm[sx]"); static const QRegularExpression s_versionRegex("-version:.*"); if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(s_memRegex) || jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize")) { auto warnStr = QObject::tr( "You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" " "or \"-Xms\").\n" "There are dedicated boxes for these in the settings (Java tab, in the Memory group at the top).\n" "This message will be displayed until you remove them from the JVM arguments."); CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec(); return false; } // block lunacy with passing required version to the JVM if (jvmargs.contains(s_versionRegex)) { auto warnStr = QObject::tr( "You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be " "allowed.\n" "This message will be displayed until you remove this from the JVM arguments."); CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec(); return false; } return true; } void JavaCommon::javaWasOk(QWidget* parent, const JavaChecker::Result& result) { QString text; text += QObject::tr( "Java test succeeded!
    Platform reported: %1
    Java version " "reported: %2
    Java vendor " "reported: %3
    ") .arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor); if (result.errorLog.size()) { auto htmlError = result.errorLog; htmlError.replace('\n', "
    "); text += QObject::tr("
    Warnings:
    %1").arg(htmlError); } CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show(); } void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaChecker::Result& result) { auto htmlError = result.errorLog; QString text; htmlError.replace('\n', "
    "); text += QObject::tr("The specified Java binary didn't work with the arguments you provided:
    "); text += QString("%1").arg(htmlError); CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); } void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaChecker::Result& result) { QString text; text += QObject::tr( "The specified Java binary didn't work.
    You should press 'Detect', " "or set the path to the Java executable.
    "); CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); } void JavaCommon::javaCheckNotFound(QWidget* parent) { QString text; text += QObject::tr("Java checker library could not be found. Please check your installation."); CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show(); } void JavaCommon::TestCheck::run() { if (!JavaCommon::checkJVMArgs(m_args, m_parent)) { emit finished(); return; } if (JavaUtils::getJavaCheckPath().isEmpty()) { javaCheckNotFound(m_parent); emit finished(); return; } checker.reset(new JavaChecker(m_path, "", 0, 0, 0, 0)); connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished); checker->start(); } void JavaCommon::TestCheck::checkFinished(const JavaChecker::Result& result) { if (result.validity != JavaChecker::Result::Validity::Valid) { javaBinaryWasBad(m_parent, result); emit finished(); return; } checker.reset(new JavaChecker(m_path, m_args, m_maxMem, m_maxMem, result.javaVersion.requiresPermGen() ? m_permGen : 0, 0)); connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs); checker->start(); } void JavaCommon::TestCheck::checkFinishedWithArgs(const JavaChecker::Result& result) { if (result.validity == JavaChecker::Result::Validity::Valid) { javaWasOk(m_parent, result); emit finished(); return; } javaArgsWereBad(m_parent, result); emit finished(); } PrismLauncher-11.0.3/launcher/java/0000755000175100017510000000000015224505336016505 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/java/JavaMetadata.h0000644000175100017510000000367015224505336021206 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include #include "BaseVersion.h" #include "java/JavaVersion.h" namespace Java { enum class DownloadType { Manifest, Archive, Unknown }; class Metadata : public BaseVersion { public: virtual QString descriptor() const override { return version.toString(); } virtual QString name() const override { return m_name; } virtual QString typeString() const override { return vendor; } virtual bool operator<(BaseVersion& a) const override; virtual bool operator>(BaseVersion& a) const override; bool operator<(const Metadata& rhs) const; bool operator==(const Metadata& rhs) const; bool operator>(const Metadata& rhs) const; QString m_name; QString vendor; QString url; QDateTime releaseTime; QString checksumType; QString checksumHash; DownloadType downloadType; QString packageType; JavaVersion version; QString runtimeOS; }; using MetadataPtr = std::shared_ptr; DownloadType parseDownloadType(QString javaDownload); QString downloadTypeToString(DownloadType javaDownload); MetadataPtr parseJavaMeta(const QJsonObject& libObj); } // namespace Java PrismLauncher-11.0.3/launcher/java/JavaInstall.cpp0000644000175100017510000000352515224505336021426 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "JavaInstall.h" #include "BaseVersion.h" #include "StringUtils.h" bool JavaInstall::operator<(const JavaInstall& rhs) const { auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive); if (archCompare != 0) return archCompare < 0; if (id < rhs.id) { return true; } if (id > rhs.id) { return false; } return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0; } bool JavaInstall::operator==(const JavaInstall& rhs) const { return arch == rhs.arch && id == rhs.id && path == rhs.path; } bool JavaInstall::operator>(const JavaInstall& rhs) const { return (!operator<(rhs)) && (!operator==(rhs)); } bool JavaInstall::operator<(BaseVersion& a) const { try { return operator<(dynamic_cast(a)); } catch (const std::bad_cast&) { return BaseVersion::operator<(a); } } bool JavaInstall::operator>(BaseVersion& a) const { try { return operator>(dynamic_cast(a)); } catch (const std::bad_cast&) { return BaseVersion::operator>(a); } } PrismLauncher-11.0.3/launcher/java/JavaChecker.cpp0000644000175100017510000001527615224505336021372 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "JavaChecker.h" #include #include #include #include #include "Commandline.h" #include "FileSystem.h" #include "java/JavaUtils.h" JavaChecker::JavaChecker(QString path, QString args, int minMem, int maxMem, int permGen, int id) : Task(), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen), m_id(id) {} void JavaChecker::executeTask() { QString checkerJar = JavaUtils::getJavaCheckPath(); if (checkerJar.isEmpty()) { qDebug() << "Java checker library could not be found. Please check your installation."; return; } #ifdef Q_OS_WIN checkerJar = FS::getPathNameInLocal8bit(checkerJar); #endif QStringList args; process.reset(new QProcess()); if (m_args.size()) { auto extraArgs = Commandline::splitArgs(m_args); args.append(extraArgs); } if (m_minMem != 0) { args << QString("-Xms%1m").arg(m_minMem); } if (m_maxMem != 0) { args << QString("-Xmx%1m").arg(m_maxMem); } if (m_permGen != 64 && m_permGen != 0) { args << QString("-XX:PermSize=%1m").arg(m_permGen); } args.append({ "-jar", checkerJar }); process->setArguments(args); process->setProgram(m_path); process->setProcessChannelMode(QProcess::SeparateChannels); process->setProcessEnvironment(CleanEnviroment()); qDebug() << "Running java checker:" << m_path << args.join(" "); connect(process.get(), &QProcess::finished, this, &JavaChecker::finished); connect(process.get(), &QProcess::errorOccurred, this, &JavaChecker::error); connect(process.get(), &QProcess::readyReadStandardOutput, this, &JavaChecker::stdoutReady); connect(process.get(), &QProcess::readyReadStandardError, this, &JavaChecker::stderrReady); connect(&killTimer, &QTimer::timeout, this, &JavaChecker::timeout); killTimer.setSingleShot(true); killTimer.start(15000); process->start(); } void JavaChecker::stdoutReady() { QByteArray data = process->readAllStandardOutput(); QString added = QString::fromLocal8Bit(data); added.remove('\r'); m_stdout += added; } void JavaChecker::stderrReady() { QByteArray data = process->readAllStandardError(); QString added = QString::fromLocal8Bit(data); added.remove('\r'); m_stderr += added; } void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) { killTimer.stop(); QProcessPtr _process = process; process.reset(); Result result = { m_path, m_id, }; result.errorLog = m_stderr; result.outLog = m_stdout; qDebug() << "STDOUT" << m_stdout; qWarning() << "STDERR" << m_stderr; qDebug() << "Java checker finished with status" << status << "exit code" << exitcode; if (status == QProcess::CrashExit || exitcode == 1) { result.validity = Result::Validity::Errored; emit checkFinished(result); emitSucceeded(); return; } bool success = true; QMap results; QStringList lines = m_stdout.split("\n", Qt::SkipEmptyParts); for (QString line : lines) { line = line.trimmed(); // NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux if (line.contains("/bedrock/strata")) { continue; } auto parts = line.split('=', Qt::SkipEmptyParts); if (parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) { continue; } else { results.insert(parts[0], parts[1]); } } if (!results.contains("os.arch") || !results.contains("java.version") || !results.contains("java.vendor") || !success) { result.validity = Result::Validity::ReturnedInvalidData; emit checkFinished(result); emitSucceeded(); return; } auto os_arch = results["os.arch"]; auto java_version = results["java.version"]; auto java_vendor = results["java.vendor"]; bool is_64 = os_arch == "x86_64" || os_arch == "amd64" || os_arch == "aarch64" || os_arch == "arm64" || os_arch == "riscv64" || os_arch == "ppc64le" || os_arch == "ppc64"; result.validity = Result::Validity::Valid; result.is_64bit = is_64; result.mojangPlatform = is_64 ? "64" : "32"; result.realPlatform = os_arch; result.javaVersion = java_version; result.javaVendor = java_vendor; qDebug() << "Java checker succeeded."; emit checkFinished(result); emitSucceeded(); } void JavaChecker::error(QProcess::ProcessError err) { if (err == QProcess::FailedToStart) { qDebug() << "Java checker has failed to start:" << process->errorString(); qDebug() << "Process environment:"; qDebug() << process->environment(); qDebug() << "Native environment:"; qDebug() << QProcessEnvironment::systemEnvironment().toStringList(); killTimer.stop(); Result result = { m_path, m_id, }; result.errorLog = process->errorString(); result.validity = Result::Validity::Errored; emit checkFinished(result); } emitSucceeded(); } void JavaChecker::timeout() { // NO MERCY. NO ABUSE. if (process) { qDebug() << "Java checker has been killed by timeout."; process->kill(); } } PrismLauncher-11.0.3/launcher/java/JavaVersion.cpp0000644000175100017510000001017015224505336021437 0ustar runnerrunner#include "JavaVersion.h" #include "StringUtils.h" #include #include JavaVersion& JavaVersion::operator=(const QString& javaVersionString) { m_string = javaVersionString; auto getCapturedInteger = [](const QRegularExpressionMatch& match, const QString& what) -> int { auto str = match.captured(what); if (str.isEmpty()) { return 0; } return str.toInt(); }; QRegularExpression pattern; if (javaVersionString.startsWith("1.")) { static const QRegularExpression s_withOne( "1[.](?[0-9]+)([.](?[0-9]+))?(_(?[0-9]+)?)?(-(?[a-zA-Z0-9]+))?"); pattern = s_withOne; } else { static const QRegularExpression s_withoutOne( "(?[0-9]+)([.](?[0-9]+))?([.](?[0-9]+))?(-(?[a-zA-Z0-9]+))?"); pattern = s_withoutOne; } auto match = pattern.match(m_string); m_parseable = match.hasMatch(); m_major = getCapturedInteger(match, "major"); m_minor = getCapturedInteger(match, "minor"); m_security = getCapturedInteger(match, "security"); m_prerelease = match.captured("prerelease"); return *this; } JavaVersion::JavaVersion(const QString& rhs) { operator=(rhs); } QString JavaVersion::toString() const { return m_string; } bool JavaVersion::requiresPermGen() const { return !m_parseable || m_major < 8; } bool JavaVersion::defaultsToUtf8() const { // starting from Java 18, UTF-8 is the default charset: https://openjdk.org/jeps/400 return m_parseable && m_major >= 18; } bool JavaVersion::isModular() const { return m_parseable && m_major >= 9; } bool JavaVersion::operator<(const JavaVersion& rhs) const { if (m_parseable && rhs.m_parseable) { auto major = m_major; auto rmajor = rhs.m_major; if (major < rmajor) return true; if (major > rmajor) return false; if (m_minor < rhs.m_minor) return true; if (m_minor > rhs.m_minor) return false; if (m_security < rhs.m_security) return true; if (m_security > rhs.m_security) return false; // everything else being equal, consider prerelease status bool thisPre = !m_prerelease.isEmpty(); bool rhsPre = !rhs.m_prerelease.isEmpty(); if (thisPre && !rhsPre) { // this is a prerelease and the other one isn't -> lesser return true; } else if (!thisPre && rhsPre) { // this isn't a prerelease and the other one is -> greater return false; } else if (thisPre && rhsPre) { // both are prereleases - use natural compare... return StringUtils::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0; } // neither is prerelease, so they are the same -> this cannot be less than rhs return false; } else return StringUtils::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0; } bool JavaVersion::operator==(const JavaVersion& rhs) const { if (m_parseable && rhs.m_parseable) { return m_major == rhs.m_major && m_minor == rhs.m_minor && m_security == rhs.m_security && m_prerelease == rhs.m_prerelease; } return m_string == rhs.m_string; } bool JavaVersion::operator>(const JavaVersion& rhs) const { return (!operator<(rhs)) && (!operator==(rhs)); } JavaVersion::JavaVersion(int major, int minor, int security, int build, QString name) : m_major(major), m_minor(minor), m_security(security), m_name(name), m_parseable(true) { QStringList versions; if (build != 0) { m_prerelease = QString::number(build); versions.push_front(m_prerelease); } if (m_security != 0) versions.push_front(QString::number(m_security)); else if (!versions.isEmpty()) versions.push_front("0"); if (m_minor != 0) versions.push_front(QString::number(m_minor)); else if (!versions.isEmpty()) versions.push_front("0"); versions.push_front(QString::number(m_major)); m_string = versions.join("."); } PrismLauncher-11.0.3/launcher/java/JavaInstallList.h0000644000175100017510000000420515224505336021723 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "BaseVersionList.h" #include "java/JavaChecker.h" #include "tasks/Task.h" #include "JavaInstall.h" #include "QObjectPtr.h" class JavaListLoadTask; class JavaInstallList : public BaseVersionList { Q_OBJECT enum class Status { NotDone, InProgress, Done }; public: explicit JavaInstallList(QObject* parent = 0, bool onlyManagedVersions = false); Task::Ptr getLoadTask(bool forceReload = false) override; bool isLoaded() override; const BaseVersion::Ptr at(int i) const override; int count() const override; void sortVersions() override; QVariant data(const QModelIndex& index, int role) const override; RoleList providesRoles() const override; public slots: void updateListData(QList versions) override; protected: void load(); Task::Ptr getCurrentTask(); protected: Status m_status = Status::NotDone; shared_qobject_ptr m_load_task; QList m_vlist; bool m_only_managed_versions; }; class JavaListLoadTask : public Task { Q_OBJECT public: explicit JavaListLoadTask(JavaInstallList* vlist, bool onlyManagedVersions = false); virtual ~JavaListLoadTask() = default; protected: void executeTask() override; public slots: void javaCheckerFinished(); protected: Task::Ptr m_job; JavaInstallList* m_list; JavaInstall* m_current_recommended; QList m_results; bool m_only_managed_versions; }; PrismLauncher-11.0.3/launcher/java/JavaInstallList.cpp0000644000175100017510000001474515224505336022270 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "Application.h" #include "settings/SettingsObject.h" #include "java/JavaChecker.h" #include "java/JavaInstallList.h" #include "java/JavaUtils.h" #include "tasks/ConcurrentTask.h" JavaInstallList::JavaInstallList(QObject* parent, bool onlyManagedVersions) : BaseVersionList(parent), m_only_managed_versions(onlyManagedVersions) {} Task::Ptr JavaInstallList::getLoadTask(bool forceReload) { Q_UNUSED(forceReload) load(); return getCurrentTask(); } Task::Ptr JavaInstallList::getCurrentTask() { if (m_status == Status::InProgress) { return m_load_task; } return nullptr; } void JavaInstallList::load() { if (m_status != Status::InProgress) { m_status = Status::InProgress; m_load_task.reset(new JavaListLoadTask(this, m_only_managed_versions)); m_load_task->start(); } } const BaseVersion::Ptr JavaInstallList::at(int i) const { return m_vlist.at(i); } bool JavaInstallList::isLoaded() { return m_status == JavaInstallList::Status::Done; } int JavaInstallList::count() const { return m_vlist.count(); } QVariant JavaInstallList::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() > count()) return QVariant(); auto version = std::dynamic_pointer_cast(m_vlist[index.row()]); switch (role) { case SortRole: return -index.row(); case VersionPointerRole: return QVariant::fromValue(m_vlist[index.row()]); case VersionIdRole: return version->descriptor(); case VersionRole: return version->id.toString(); case RecommendedRole: return false; case PathRole: return version->path; case CPUArchitectureRole: return version->arch; default: return QVariant(); } } BaseVersionList::RoleList JavaInstallList::providesRoles() const { return { VersionPointerRole, VersionIdRole, VersionRole, RecommendedRole, PathRole, CPUArchitectureRole }; } void JavaInstallList::updateListData(QList versions) { beginResetModel(); m_vlist = versions; sortVersions(); endResetModel(); m_status = Status::Done; m_load_task.reset(); } bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right) { auto rleft = std::dynamic_pointer_cast(right); auto rright = std::dynamic_pointer_cast(left); return (*rleft) > (*rright); } void JavaInstallList::sortVersions() { beginResetModel(); std::sort(m_vlist.begin(), m_vlist.end(), sortJavas); endResetModel(); } JavaListLoadTask::JavaListLoadTask(JavaInstallList* vlist, bool onlyManagedVersions) : Task(), m_only_managed_versions(onlyManagedVersions) { m_list = vlist; m_current_recommended = NULL; } void JavaListLoadTask::executeTask() { setStatus(tr("Detecting Java installations...")); JavaUtils ju; QList candidate_paths = m_only_managed_versions ? getPrismJavaBundle() : ju.FindJavaPaths(); ConcurrentTask::Ptr job(new ConcurrentTask("Java detection", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())); m_job.reset(job); connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished); connect(m_job.get(), &Task::progress, this, &Task::setProgress); qDebug() << "Probing the following Java paths: "; int id = 0; for (QString candidate : candidate_paths) { auto checker = new JavaChecker(candidate, "", 0, 0, 0, id); connect(checker, &JavaChecker::checkFinished, [this](const JavaChecker::Result& result) { m_results << result; }); job->addTask(Task::Ptr(checker)); id++; } m_job->start(); } void JavaListLoadTask::javaCheckerFinished() { QList candidates; std::sort(m_results.begin(), m_results.end(), [](const JavaChecker::Result& a, const JavaChecker::Result& b) { return a.id < b.id; }); qDebug() << "Found the following valid Java installations:"; for (auto result : m_results) { if (result.validity == JavaChecker::Result::Validity::Valid) { JavaInstallPtr javaVersion(new JavaInstall()); javaVersion->id = result.javaVersion; javaVersion->arch = result.realPlatform; javaVersion->path = result.path; javaVersion->is_64bit = result.is_64bit; candidates.append(javaVersion); qDebug() << " " << javaVersion->id.toString() << javaVersion->arch << javaVersion->path; } } QList javas_bvp; for (auto java : candidates) { // qDebug() << java->id << java->arch << " at " << java->path; BaseVersion::Ptr bp_java = std::dynamic_pointer_cast(java); if (bp_java) { javas_bvp.append(java); } } m_list->updateListData(javas_bvp); emitSucceeded(); } PrismLauncher-11.0.3/launcher/java/JavaUtils.cpp0000644000175100017510000005775615224505336021137 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "Application.h" #include "BuildConfig.h" #include "FileSystem.h" #include "java/JavaInstallList.h" #include "java/JavaUtils.h" #define IBUS "@im=ibus" JavaUtils::JavaUtils() {} QString stripVariableEntries(QString name, QString target, QString remove) { char delimiter = ':'; #ifdef Q_OS_WIN32 delimiter = ';'; #endif auto targetItems = target.split(delimiter); auto toRemove = remove.split(delimiter); for (QString item : toRemove) { bool removed = targetItems.removeOne(item); if (!removed) qWarning() << "Entry" << item << "could not be stripped from variable" << name; } return targetItems.join(delimiter); } QProcessEnvironment CleanEnviroment() { // prepare the process environment QProcessEnvironment rawenv = QProcessEnvironment::systemEnvironment(); QProcessEnvironment env; QStringList ignored = { "JAVA_ARGS", "CLASSPATH", "CONFIGPATH", "JAVA_HOME", "JRE_HOME", "_JAVA_OPTIONS", "JAVA_OPTIONS", "JAVA_TOOL_OPTIONS" }; QStringList stripped = { #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) "LD_LIBRARY_PATH", "LD_PRELOAD", #endif "QT_PLUGIN_PATH", "QT_FONTPATH" }; for (auto key : rawenv.keys()) { auto value = rawenv.value(key); // filter out dangerous java crap if (ignored.contains(key)) { qDebug() << "Env: ignoring" << key << value; continue; } // These are used to strip the original variables // If there is "LD_LIBRARY_PATH" and "LAUNCHER_LD_LIBRARY_PATH", we want to // remove all values in "LAUNCHER_LD_LIBRARY_PATH" from "LD_LIBRARY_PATH" if (key.startsWith("LAUNCHER_")) { qDebug() << "Env: ignoring" << key << value; continue; } if (stripped.contains(key)) { QString newValue = stripVariableEntries(key, value, rawenv.value("LAUNCHER_" + key)); qDebug() << "Env: stripped" << key << value << "to" << newValue; value = newValue; } #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) // Strip IBus // IBus is a Linux IME framework. For some reason, it breaks MC? if (key == "XMODIFIERS" && value.contains(IBUS)) { QString save = value; value.replace(IBUS, ""); qDebug() << "Env: stripped" << IBUS << "from" << save << ":" << value; } #endif // qDebug() << "Env: " << key << value; env.insert(key, value); } #ifdef Q_OS_LINUX // HACK: Workaround for QTBUG-42500 if (!env.contains("LD_LIBRARY_PATH")) { env.insert("LD_LIBRARY_PATH", ""); } #endif return env; } JavaInstallPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch) { JavaInstallPtr javaVersion(new JavaInstall()); javaVersion->id = id; javaVersion->arch = arch; javaVersion->path = path; return javaVersion; } JavaInstallPtr JavaUtils::GetDefaultJava() { JavaInstallPtr javaVersion(new JavaInstall()); javaVersion->id = "java"; javaVersion->arch = "unknown"; #if defined(Q_OS_WIN32) javaVersion->path = "javaw"; #else javaVersion->path = "java"; #endif return javaVersion; } QStringList addJavasFromEnv(QList javas) { auto env = QProcessEnvironment::systemEnvironment().value(QStringLiteral("%1_JAVA_PATHS").arg(BuildConfig.LAUNCHER_ENVNAME)); #if defined(Q_OS_WIN32) QList javaPaths = env.replace("\\", "/").split(QLatin1String(";")); auto envPath = qEnvironmentVariable("PATH"); QList javaPathsfromPath = envPath.replace("\\", "/").split(QLatin1String(";")); for (QString string : javaPathsfromPath) { javaPaths.append(string + "/javaw.exe"); } #else QList javaPaths = env.split(QLatin1String(":")); #endif for (QString i : javaPaths) { javas.append(i); }; return javas; } #if defined(Q_OS_WIN32) QList JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix) { QList javas; QString archType = "unknown"; if (keyType == KEY_WOW64_64KEY) archType = "64"; else if (keyType == KEY_WOW64_32KEY) archType = "32"; for (HKEY baseRegistry : { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE }) { HKEY jreKey; if (RegOpenKeyExW(baseRegistry, keyName.toStdWString().c_str(), 0, KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS) { // Read the current type version from the registry. // This will be used to find any key that contains the JavaHome value. WCHAR subKeyName[255]; DWORD subKeyNameSize, numSubKeys, retCode; // Get the number of subkeys RegQueryInfoKeyW(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); // Iterate until RegEnumKeyEx fails if (numSubKeys > 0) { for (DWORD i = 0; i < numSubKeys; i++) { subKeyNameSize = 255; retCode = RegEnumKeyExW(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL); QString newSubkeyName = QString::fromWCharArray(subKeyName); if (retCode == ERROR_SUCCESS) { // Now open the registry key for the version that we just got. QString newKeyName = keyName + "\\" + newSubkeyName + subkeySuffix; HKEY newKey; if (RegOpenKeyExW(baseRegistry, newKeyName.toStdWString().c_str(), 0, KEY_READ | keyType, &newKey) == ERROR_SUCCESS) { // Read the JavaHome value to find where Java is installed. DWORD valueSz = 0; if (RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, NULL, &valueSz) == ERROR_SUCCESS) { WCHAR* value = new WCHAR[valueSz]; RegQueryValueExW(newKey, keyJavaDir.toStdWString().c_str(), NULL, NULL, (BYTE*)value, &valueSz); QString newValue = QString::fromWCharArray(value); delete[] value; // Now, we construct the version object and add it to the list. JavaInstallPtr javaVersion(new JavaInstall()); javaVersion->id = newSubkeyName; javaVersion->arch = archType; javaVersion->path = QDir(FS::PathCombine(newValue, "bin")).absoluteFilePath("javaw.exe"); javas.append(javaVersion); } RegCloseKey(newKey); } } } } RegCloseKey(jreKey); } } return javas; } QList JavaUtils::FindJavaPaths() { QList java_candidates; // Oracle QList JRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); QList JDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); QList JRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); QList JDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); // Oracle for Java 9 and newer QList NEWJRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); QList NEWJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); QList NEWJRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); QList NEWJDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); // AdoptOpenJDK QList ADOPTOPENJRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); QList ADOPTOPENJRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); QList ADOPTOPENJDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); QList ADOPTOPENJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); // Eclipse Foundation QList FOUNDATIONJDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); QList FOUNDATIONJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); // Eclipse Adoptium QList ADOPTIUMJRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); QList ADOPTIUMJRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); QList ADOPTIUMJDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); QList ADOPTIUMJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); // IBM Semeru QList SEMERUJRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI"); QList SEMERUJRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JRE", "Path", "\\openj9\\MSI"); QList SEMERUJDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI"); QList SEMERUJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Semeru\\JDK", "Path", "\\openj9\\MSI"); // Microsoft QList MICROSOFTJDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI"); // Azul Zulu QList ZULU64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); QList ZULU32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); // BellSoft Liberica QList LIBERICA64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); QList LIBERICA32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); // List x64 before x86 java_candidates.append(JRE64s); java_candidates.append(NEWJRE64s); java_candidates.append(ADOPTOPENJRE64s); java_candidates.append(ADOPTIUMJRE64s); java_candidates.append(SEMERUJRE64s); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe")); java_candidates.append(JDK64s); java_candidates.append(NEWJDK64s); java_candidates.append(ADOPTOPENJDK64s); java_candidates.append(FOUNDATIONJDK64s); java_candidates.append(ADOPTIUMJDK64s); java_candidates.append(SEMERUJDK64s); java_candidates.append(MICROSOFTJDK64s); java_candidates.append(ZULU64s); java_candidates.append(LIBERICA64s); java_candidates.append(JRE32s); java_candidates.append(NEWJRE32s); java_candidates.append(ADOPTOPENJRE32s); java_candidates.append(ADOPTIUMJRE32s); java_candidates.append(SEMERUJRE32s); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe")); java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe")); java_candidates.append(JDK32s); java_candidates.append(NEWJDK32s); java_candidates.append(ADOPTOPENJDK32s); java_candidates.append(FOUNDATIONJDK32s); java_candidates.append(ADOPTIUMJDK32s); java_candidates.append(SEMERUJDK32s); java_candidates.append(ZULU32s); java_candidates.append(LIBERICA32s); java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path)); QList candidates; for (JavaInstallPtr java_candidate : java_candidates) { if (!candidates.contains(java_candidate->path)) { candidates.append(java_candidate->path); } } candidates.append(getMinecraftJavaBundle()); candidates.append(getPrismJavaBundle()); candidates = addJavasFromEnv(candidates); candidates.removeDuplicates(); return candidates; } #elif defined(Q_OS_MAC) QList JavaUtils::FindJavaPaths() { QList javas; javas.append(this->GetDefaultJava()->path); javas.append("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java"); javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"); javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java"); QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/"); QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QString& java : libraryJVMJavas) { javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java"); } QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/"); QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QString& java : systemLibraryJVMJavas) { javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); } auto home = qEnvironmentVariable("HOME"); // javas downloaded by sdkman QString sdkmanDir = qEnvironmentVariable("SDKMAN_DIR", FS::PathCombine(home, ".sdkman")); QDir sdkmanJavaDir(FS::PathCombine(sdkmanDir, "candidates/java")); QStringList sdkmanJavas = sdkmanJavaDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QString& java : sdkmanJavas) { javas.append(sdkmanJavaDir.absolutePath() + "/" + java + "/bin/java"); } // javas downloaded by asdf QString asdfDataDir = qEnvironmentVariable("ASDF_DATA_DIR", FS::PathCombine(home, ".asdf")); QDir asdfJavaDir(FS::PathCombine(asdfDataDir, "installs/java")); QStringList asdfJavas = asdfJavaDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QString& java : asdfJavas) { javas.append(asdfJavaDir.absolutePath() + "/" + java + "/bin/java"); } // java in user library folder (like from intellij downloads) QDir userLibraryJVMDir(FS::PathCombine(home, "Library/Java/JavaVirtualMachines/")); QStringList userLibraryJVMJavas = userLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QString& java : userLibraryJVMJavas) { javas.append(userLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); javas.append(userLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); } javas.append(getMinecraftJavaBundle()); javas.append(getPrismJavaBundle()); javas = addJavasFromEnv(javas); javas.removeDuplicates(); return javas; } #elif defined(Q_OS_LINUX) || defined(Q_OS_OPENBSD) || defined(Q_OS_FREEBSD) QList JavaUtils::FindJavaPaths() { QList javas; javas.append(this->GetDefaultJava()->path); auto scanJavaDir = [&javas]( const QString& dirPath, const std::function& filter = [](const QFileInfo&) { return true; }) { QDir dir(dirPath); if (!dir.exists()) return; auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (auto& entry : entries) { if (!filter(entry)) continue; QString prefix; prefix = entry.canonicalFilePath(); javas.append(FS::PathCombine(prefix, "jre/bin/java")); javas.append(FS::PathCombine(prefix, "bin/java")); } }; // java installed in a snap is installed in the standard directory, but underneath $SNAP auto snap = qEnvironmentVariable("SNAP"); auto scanJavaDirs = [scanJavaDir, snap](const QString& dirPath) { scanJavaDir(dirPath); if (!snap.isNull()) { scanJavaDir(snap + dirPath); } }; #if defined(Q_OS_LINUX) // oracle RPMs scanJavaDirs("/usr/java"); // general locations used by distro packaging scanJavaDirs("/usr/lib/jvm"); scanJavaDirs("/usr/lib64/jvm"); scanJavaDirs("/usr/lib32/jvm"); // Gentoo's locations for openjdk and openjdk-bin respectively auto gentooFilter = [](const QFileInfo& info) { QString fileName = info.fileName(); return fileName.startsWith("openjdk-") || fileName.startsWith("openj9-"); }; // AOSC OS's locations for openjdk auto aoscFilter = [](const QFileInfo& info) { QString fileName = info.fileName(); return fileName == "java" || fileName.startsWith("java-"); }; scanJavaDir("/usr/lib64", gentooFilter); scanJavaDir("/usr/lib", gentooFilter); scanJavaDir("/opt", gentooFilter); scanJavaDir("/usr/lib", aoscFilter); // javas stored in Prism Launcher's folder scanJavaDirs("java"); // manually installed JDKs in /opt scanJavaDirs("/opt/jdk"); scanJavaDirs("/opt/jdks"); scanJavaDirs("/opt/ibm"); // IBM Semeru Certified Edition // flatpak scanJavaDirs("/app/jdk"); #elif defined(Q_OS_OPENBSD) || defined(Q_OS_FREEBSD) // ports install to /usr/local on OpenBSD & FreeBSD scanJavaDirs("/usr/local"); #endif auto home = qEnvironmentVariable("HOME"); // javas downloaded by IntelliJ scanJavaDirs(FS::PathCombine(home, ".jdks")); // javas downloaded by sdkman QString sdkmanDir = qEnvironmentVariable("SDKMAN_DIR", FS::PathCombine(home, ".sdkman")); scanJavaDirs(FS::PathCombine(sdkmanDir, "candidates/java")); // javas downloaded by asdf QString asdfDataDir = qEnvironmentVariable("ASDF_DATA_DIR", FS::PathCombine(home, ".asdf")); scanJavaDirs(FS::PathCombine(asdfDataDir, "installs/java")); // javas downloaded by gradle (toolchains) QString gradleUserHome = qEnvironmentVariable("GRADLE_USER_HOME", FS::PathCombine(home, ".gradle")); scanJavaDirs(FS::PathCombine(gradleUserHome, "jdks")); javas.append(getMinecraftJavaBundle()); javas.append(getPrismJavaBundle()); javas = addJavasFromEnv(javas); javas.removeDuplicates(); return javas; } #else QList JavaUtils::FindJavaPaths() { qDebug() << "Unknown operating system build - defaulting to \"java\""; QList javas; javas.append(this->GetDefaultJava()->path); javas.append(getMinecraftJavaBundle()); javas.append(getPrismJavaBundle()); javas.removeDuplicates(); return addJavasFromEnv(javas); } #endif QString JavaUtils::getJavaCheckPath() { return APPLICATION->getJarPath("JavaCheck.jar"); } QStringList getMinecraftJavaBundle() { QStringList processpaths; #if defined(Q_OS_MACOS) processpaths << FS::PathCombine(QDir::homePath(), FS::PathCombine("Library", "Application Support", "minecraft", "runtime")); #elif defined(Q_OS_WIN32) auto appDataPath = QProcessEnvironment::systemEnvironment().value("APPDATA", ""); processpaths << FS::PathCombine(QFileInfo(appDataPath).absoluteFilePath(), ".minecraft", "runtime"); // add the microsoft store version of the launcher to the search. the current path is: // C:\Users\USERNAME\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime auto localAppDataPath = QProcessEnvironment::systemEnvironment().value("LOCALAPPDATA", ""); auto minecraftMSStorePath = FS::PathCombine(QFileInfo(localAppDataPath).absoluteFilePath(), "Packages", "Microsoft.4297127D64EC6_8wekyb3d8bbwe"); processpaths << FS::PathCombine(minecraftMSStorePath, "LocalCache", "Local", "runtime"); #else processpaths << FS::PathCombine(QDir::homePath(), ".minecraft", "runtime"); #endif QStringList javas; while (!processpaths.isEmpty()) { auto dirPath = processpaths.takeFirst(); QDir dir(dirPath); if (!dir.exists()) continue; auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); auto binFound = false; for (auto& entry : entries) { if (entry.baseName() == "bin") { javas.append(FS::PathCombine(entry.canonicalFilePath(), JavaUtils::javaExecutable)); binFound = true; break; } } if (!binFound) { for (auto& entry : entries) { processpaths << entry.canonicalFilePath(); } } } return javas; } #if defined(Q_OS_WIN32) const QString JavaUtils::javaExecutable = "javaw.exe"; #else const QString JavaUtils::javaExecutable = "java"; #endif QStringList getPrismJavaBundle() { QList javas; auto scanDir = [&javas](QString prefix) { javas.append(FS::PathCombine(prefix, "jre", "bin", JavaUtils::javaExecutable)); javas.append(FS::PathCombine(prefix, "bin", JavaUtils::javaExecutable)); javas.append(FS::PathCombine(prefix, JavaUtils::javaExecutable)); }; auto scanJavaDir = [scanDir](const QString& dirPath) { QDir dir(dirPath); if (!dir.exists()) return; auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (auto& entry : entries) { scanDir(entry.canonicalFilePath()); } }; scanJavaDir(APPLICATION->javaPath()); return javas; } PrismLauncher-11.0.3/launcher/java/JavaInstall.h0000644000175100017510000000306715224505336021074 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "BaseVersion.h" #include "JavaVersion.h" struct JavaInstall : public BaseVersion { JavaInstall() {} JavaInstall(QString id, QString arch, QString path) : id(id), arch(arch), path(path) {} virtual QString descriptor() const override { return id.toString(); } virtual QString name() const override { return id.toString(); } virtual QString typeString() const override { return arch; } virtual bool operator<(BaseVersion& a) const override; virtual bool operator>(BaseVersion& a) const override; bool operator<(const JavaInstall& rhs) const; bool operator==(const JavaInstall& rhs) const; bool operator>(const JavaInstall& rhs) const; JavaVersion id; QString arch; QString path; bool is_64bit = false; }; using JavaInstallPtr = std::shared_ptr; PrismLauncher-11.0.3/launcher/java/download/0000755000175100017510000000000015224505336020314 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/java/download/ArchiveDownloadTask.h0000644000175100017510000000254615224505336024370 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include "tasks/Task.h" namespace Java { class ArchiveDownloadTask : public Task { Q_OBJECT public: ArchiveDownloadTask(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = ""); virtual ~ArchiveDownloadTask() = default; bool canAbort() const override { return true; } void executeTask() override; virtual bool abort() override; private slots: void extractJava(QString input); protected: QUrl m_url; QString m_final_path; QString m_checksum_type; QString m_checksum_hash; Task::Ptr m_task; }; } // namespace Java PrismLauncher-11.0.3/launcher/java/download/SymlinkTask.h0000644000175100017510000000203715224505336022740 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "tasks/Task.h" namespace Java { class SymlinkTask : public Task { Q_OBJECT public: SymlinkTask(QString final_path); virtual ~SymlinkTask() = default; void executeTask() override; protected: QString m_path; Task::Ptr m_task; }; } // namespace Java PrismLauncher-11.0.3/launcher/java/download/ManifestDownloadTask.h0000644000175100017510000000256615224505336024557 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include "tasks/Task.h" namespace Java { class ManifestDownloadTask : public Task { Q_OBJECT public: ManifestDownloadTask(QUrl url, QString final_path, QString checksumType = "", QString checksumHash = ""); virtual ~ManifestDownloadTask() = default; bool canAbort() const override { return true; } void executeTask() override; virtual bool abort() override; private slots: void downloadJava(const QJsonDocument& doc); protected: QUrl m_url; QString m_final_path; QString m_checksum_type; QString m_checksum_hash; Task::Ptr m_task; }; } // namespace Java PrismLauncher-11.0.3/launcher/java/download/ArchiveDownloadTask.cpp0000644000175100017510000001103715224505336024716 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "java/download/ArchiveDownloadTask.h" #include #include "Application.h" #include "archive/ArchiveReader.h" #include "archive/ExtractZipTask.h" #include "net/ChecksumValidator.h" #include "net/NetJob.h" #include "tasks/Task.h" namespace Java { ArchiveDownloadTask::ArchiveDownloadTask(QUrl url, QString final_path, QString checksumType, QString checksumHash) : m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash) {} void ArchiveDownloadTask::executeTask() { // JRE found ! download the zip setStatus(tr("Downloading Java")); MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("java", m_url.fileName()); auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); auto action = Net::Download::makeCached(m_url, entry); if (!m_checksum_hash.isEmpty() && !m_checksum_type.isEmpty()) { auto hashType = QCryptographicHash::Algorithm::Sha1; if (m_checksum_type == "sha256") { hashType = QCryptographicHash::Algorithm::Sha256; } action->addValidator(new Net::ChecksumValidator(hashType, QByteArray::fromHex(m_checksum_hash.toUtf8()))); } download->addNetAction(action); auto fullPath = entry->getFullPath(); connect(download.get(), &Task::failed, this, &ArchiveDownloadTask::emitFailed); connect(download.get(), &Task::progress, this, &ArchiveDownloadTask::setProgress); connect(download.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress); connect(download.get(), &Task::status, this, &ArchiveDownloadTask::setStatus); connect(download.get(), &Task::details, this, &ArchiveDownloadTask::setDetails); connect(download.get(), &Task::aborted, this, &ArchiveDownloadTask::emitAborted); connect(download.get(), &Task::succeeded, [this, fullPath] { // This should do all of the extracting and creating folders extractJava(fullPath); }); m_task = download; m_task->start(); } void ArchiveDownloadTask::extractJava(QString input) { setStatus(tr("Extracting Java")); MMCZip::ArchiveReader zip(input); if (!zip.collectFiles()) { emitFailed(tr("Unable to open supplied zip file.")); return; } auto files = zip.getFiles(); if (files.isEmpty()) { emitFailed(tr("No files were found in the supplied zip file.")); return; } auto firstFolderParts = files[0].split('/', Qt::SkipEmptyParts); m_task = makeShared(input, m_final_path, firstFolderParts.value(0)); auto progressStep = std::make_shared(); connect(m_task.get(), &Task::finished, this, [this, progressStep] { progressStep->state = TaskStepState::Succeeded; stepProgress(*progressStep); }); connect(m_task.get(), &Task::succeeded, this, &ArchiveDownloadTask::emitSucceeded); connect(m_task.get(), &Task::aborted, this, &ArchiveDownloadTask::emitAborted); connect(m_task.get(), &Task::failed, this, [this, progressStep](QString reason) { progressStep->state = TaskStepState::Failed; stepProgress(*progressStep); emitFailed(reason); }); connect(m_task.get(), &Task::stepProgress, this, &ArchiveDownloadTask::propagateStepProgress); connect(m_task.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) { progressStep->update(current, total); stepProgress(*progressStep); }); connect(m_task.get(), &Task::status, this, [this, progressStep](QString status) { progressStep->status = status; stepProgress(*progressStep); }); m_task->start(); return; } bool ArchiveDownloadTask::abort() { auto aborted = canAbort(); if (m_task) aborted = m_task->abort(); return aborted; }; } // namespace Java PrismLauncher-11.0.3/launcher/java/download/ManifestDownloadTask.cpp0000644000175100017510000001273315224505336025107 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "java/download/ManifestDownloadTask.h" #include "Application.h" #include "FileSystem.h" #include "Json.h" #include "net/ChecksumValidator.h" #include "net/NetJob.h" struct File { QString path; QString url; QByteArray hash; bool isExec; }; namespace Java { ManifestDownloadTask::ManifestDownloadTask(QUrl url, QString final_path, QString checksumType, QString checksumHash) : m_url(url), m_final_path(final_path), m_checksum_type(checksumType), m_checksum_hash(checksumHash) {} void ManifestDownloadTask::executeTask() { setStatus(tr("Downloading Java")); auto download = makeShared(QString("JRE::DownloadJava"), APPLICATION->network()); auto [action, files] = Net::Download::makeByteArray(m_url); if (!m_checksum_hash.isEmpty() && !m_checksum_type.isEmpty()) { auto hashType = QCryptographicHash::Algorithm::Sha1; if (m_checksum_type == "sha256") { hashType = QCryptographicHash::Algorithm::Sha256; } action->addValidator(new Net::ChecksumValidator(hashType, QByteArray::fromHex(m_checksum_hash.toUtf8()))); } download->addNetAction(action); connect(download.get(), &Task::failed, this, &ManifestDownloadTask::emitFailed); connect(download.get(), &Task::progress, this, &ManifestDownloadTask::setProgress); connect(download.get(), &Task::stepProgress, this, &ManifestDownloadTask::propagateStepProgress); connect(download.get(), &Task::status, this, &ManifestDownloadTask::setStatus); connect(download.get(), &Task::details, this, &ManifestDownloadTask::setDetails); connect(download.get(), &Task::succeeded, [files, this] { QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*files, &parse_error); if (parse_error.error != QJsonParseError::NoError) { qWarning() << "Error while parsing JSON response at" << parse_error.offset << "reason:" << parse_error.errorString(); qWarning() << *files; emitFailed(parse_error.errorString()); return; } downloadJava(doc); }); m_task = download; m_task->start(); } void ManifestDownloadTask::downloadJava(const QJsonDocument& doc) { // valid json doc, begin making jre spot FS::ensureFolderPathExists(m_final_path); std::vector toDownload; auto list = doc.object()["files"].toObject(); for (const auto& paths : list.keys()) { auto file = FS::PathCombine(m_final_path, paths); const QJsonObject& meta = list[paths].toObject(); auto type = meta["type"].toString(); if (type == "directory") { FS::ensureFolderPathExists(file); } else if (type == "link") { // this is *nix only ! auto path = meta["target"].toString(); if (!path.isEmpty()) { QFile::link(path, file); } } else if (type == "file") { // TODO download compressed version if it exists ? auto raw = meta["downloads"].toObject()["raw"].toObject(); auto isExec = meta["executable"].toBool(); auto url = raw["url"].toString(); if (!url.isEmpty() && QUrl(url).isValid()) { auto f = File{ file, url, QByteArray::fromHex(raw["sha1"].toString().toLatin1()), isExec }; toDownload.push_back(f); } } } auto elementDownload = makeShared("JRE::FileDownload", APPLICATION->network()); for (const auto& file : toDownload) { auto dl = Net::Download::makeFile(file.url, file.path); if (!file.hash.isEmpty()) { dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, file.hash)); } if (file.isExec) { connect(dl.get(), &Net::Download::succeeded, [file] { QFile(file.path).setPermissions(QFile(file.path).permissions() | QFileDevice::Permissions(0x1111)); }); } elementDownload->addNetAction(dl); } connect(elementDownload.get(), &Task::failed, this, &ManifestDownloadTask::emitFailed); connect(elementDownload.get(), &Task::progress, this, &ManifestDownloadTask::setProgress); connect(elementDownload.get(), &Task::stepProgress, this, &ManifestDownloadTask::propagateStepProgress); connect(elementDownload.get(), &Task::status, this, &ManifestDownloadTask::setStatus); connect(elementDownload.get(), &Task::details, this, &ManifestDownloadTask::setDetails); connect(elementDownload.get(), &Task::succeeded, this, &ManifestDownloadTask::emitSucceeded); m_task = elementDownload; m_task->start(); } bool ManifestDownloadTask::abort() { auto aborted = canAbort(); if (m_task) aborted = m_task->abort(); emitAborted(); return aborted; }; } // namespace Java PrismLauncher-11.0.3/launcher/java/download/SymlinkTask.cpp0000644000175100017510000000536615224505336023303 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "java/download/SymlinkTask.h" #include #include "FileSystem.h" namespace Java { SymlinkTask::SymlinkTask(QString final_path) : m_path(final_path) {} QString findBinPath(QString root, QString pattern) { auto path = FS::PathCombine(root, pattern); if (QFileInfo::exists(path)) { return path; } auto entries = QDir(root).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (auto& entry : entries) { path = FS::PathCombine(entry.absoluteFilePath(), pattern); if (QFileInfo::exists(path)) { return path; } } return {}; } void SymlinkTask::executeTask() { setStatus(tr("Checking for Java binary path")); const auto binPath = FS::PathCombine("bin", "java"); const auto wantedPath = FS::PathCombine(m_path, binPath); if (QFileInfo::exists(wantedPath)) { emitSucceeded(); return; } setStatus(tr("Searching for Java binary path")); const auto contentsPartialPath = FS::PathCombine("Contents", "Home", binPath); const auto relativePathToBin = findBinPath(m_path, contentsPartialPath); if (relativePathToBin.isEmpty()) { emitFailed(tr("Failed to find Java binary path")); return; } const auto folderToLink = relativePathToBin.chopped(binPath.length()); setStatus(tr("Collecting folders to symlink")); auto entries = QDir(folderToLink).entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries); QList files; setProgress(0, entries.length()); for (auto& entry : entries) { files.append({ entry.absoluteFilePath(), FS::PathCombine(m_path, entry.fileName()) }); } setStatus(tr("Symlinking Java binary path")); FS::create_link folderLink(files); connect(&folderLink, &FS::create_link::fileLinked, [this](QString src, QString dst) { setProgress(m_progress + 1, m_progressTotal); }); if (!folderLink()) { emitFailed(folderLink.getOSError().message().c_str()); } else { emitSucceeded(); } } } // namespace Java PrismLauncher-11.0.3/launcher/java/JavaChecker.h0000644000175100017510000000242615224505336021030 0ustar runnerrunner#pragma once #include #include #include "JavaVersion.h" #include "QObjectPtr.h" #include "tasks/Task.h" class JavaChecker : public Task { Q_OBJECT public: using QProcessPtr = shared_qobject_ptr; using Ptr = shared_qobject_ptr; struct Result { QString path; int id; QString mojangPlatform; QString realPlatform; JavaVersion javaVersion; QString javaVendor; QString outLog; QString errorLog; bool is_64bit = false; enum class Validity { Errored, ReturnedInvalidData, Valid } validity = Validity::Errored; }; explicit JavaChecker(QString path, QString args, int minMem = 0, int maxMem = 0, int permGen = 0, int id = 0); signals: void checkFinished(const Result& result); protected: virtual void executeTask() override; private: QProcessPtr process; QTimer killTimer; QString m_stdout; QString m_stderr; QString m_path; QString m_args; int m_minMem = 0; int m_maxMem = 0; int m_permGen = 64; int m_id = 0; private slots: void timeout(); void finished(int exitcode, QProcess::ExitStatus); void error(QProcess::ProcessError); void stdoutReady(); void stderrReady(); }; PrismLauncher-11.0.3/launcher/java/JavaMetadata.cpp0000644000175100017510000000716115224505336021540 0ustar runnerrunner// SPDX-License-Identifier: GPL-3.0-only /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2023-2024 Trial97 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "java/JavaMetadata.h" #include #include "Json.h" #include "StringUtils.h" #include "java/JavaVersion.h" #include "minecraft/ParseUtils.h" namespace Java { DownloadType parseDownloadType(QString javaDownload) { if (javaDownload == "manifest") return DownloadType::Manifest; else if (javaDownload == "archive") return DownloadType::Archive; else return DownloadType::Unknown; } QString downloadTypeToString(DownloadType javaDownload) { switch (javaDownload) { case DownloadType::Manifest: return "manifest"; case DownloadType::Archive: return "archive"; case DownloadType::Unknown: break; } return "unknown"; } MetadataPtr parseJavaMeta(const QJsonObject& in) { auto meta = std::make_shared(); meta->m_name = in["name"].toString(""); meta->vendor = in["vendor"].toString(""); meta->url = in["url"].toString(""); meta->releaseTime = timeFromS3Time(in["releaseTime"].toString("")); meta->downloadType = parseDownloadType(in["downloadType"].toString("")); meta->packageType = in["packageType"].toString(""); meta->runtimeOS = in["runtimeOS"].toString("unknown"); if (in.contains("checksum")) { auto obj = Json::requireObject(in, "checksum"); meta->checksumHash = obj["hash"].toString(""); meta->checksumType = obj["type"].toString(""); } if (in.contains("version")) { auto obj = Json::requireObject(in, "version"); auto name = obj["name"].toString(""); auto major = obj["major"].toInteger(); auto minor = obj["minor"].toInteger(); auto security = obj["security"].toInteger(); auto build = obj["build"].toInteger(); meta->version = JavaVersion(major, minor, security, build, name); } return meta; } bool Metadata::operator<(const Metadata& rhs) const { auto id = version; if (id < rhs.version) { return true; } if (id > rhs.version) { return false; } auto date = releaseTime; if (date < rhs.releaseTime) { return true; } if (date > rhs.releaseTime) { return false; } return StringUtils::naturalCompare(m_name, rhs.m_name, Qt::CaseInsensitive) < 0; } bool Metadata::operator==(const Metadata& rhs) const { return version == rhs.version && m_name == rhs.m_name; } bool Metadata::operator>(const Metadata& rhs) const { return (!operator<(rhs)) && (!operator==(rhs)); } bool Metadata::operator<(BaseVersion& a) const { try { return operator<(dynamic_cast(a)); } catch (const std::bad_cast&) { return BaseVersion::operator<(a); } } bool Metadata::operator>(BaseVersion& a) const { try { return operator>(dynamic_cast(a)); } catch (const std::bad_cast&) { return BaseVersion::operator>(a); } } } // namespace Java PrismLauncher-11.0.3/launcher/java/JavaUtils.h0000644000175100017510000000260415224505336020562 0ustar runnerrunner/* Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "java/JavaInstall.h" #ifdef Q_OS_WIN #include #endif QString stripVariableEntries(QString name, QString target, QString remove); QProcessEnvironment CleanEnviroment(); QStringList getMinecraftJavaBundle(); QStringList getPrismJavaBundle(); class JavaUtils : public QObject { Q_OBJECT public: JavaUtils(); JavaInstallPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown"); QList FindJavaPaths(); JavaInstallPtr GetDefaultJava(); #ifdef Q_OS_WIN QList FindJavaFromRegistryKey(DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix = ""); #endif static QString getJavaCheckPath(); static const QString javaExecutable; }; PrismLauncher-11.0.3/launcher/java/JavaVersion.h0000644000175100017510000000217315224505336021110 0ustar runnerrunner#pragma once #include // NOTE: apparently the GNU C library pollutes the global namespace with these... undef them. #ifdef major #undef major #endif #ifdef minor #undef minor #endif class JavaVersion { friend class JavaVersionTest; public: JavaVersion() {} JavaVersion(const QString& rhs); JavaVersion(int major, int minor, int security, int build = 0, QString name = ""); JavaVersion& operator=(const QString& rhs); bool operator<(const JavaVersion& rhs) const; bool operator==(const JavaVersion& rhs) const; bool operator>(const JavaVersion& rhs) const; bool requiresPermGen() const; bool defaultsToUtf8() const; bool isModular() const; QString toString() const; int major() const { return m_major; } int minor() const { return m_minor; } int security() const { return m_security; } QString build() const { return m_prerelease; } QString name() const { return m_name; } private: QString m_string; int m_major = 0; int m_minor = 0; int m_security = 0; QString m_name = ""; bool m_parseable = false; QString m_prerelease; }; PrismLauncher-11.0.3/launcher/resources/0000755000175100017510000000000015224505336017576 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/sources/0000755000175100017510000000000015224505336021261 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/sources/burfcat_hat.png0000644000175100017510000002336115224505336024256 0ustar runnerrunner‰PNG  IHDR¡RÌ1q\&¸IDATxìÃ1}äÔA.‰| g»Ûà=Ì®žî~€ËÞ=9–5P�ÝN›c[ͱmÛöÌ·ÛžYÛ¶mÛvÛöýÎMåV½J¥—å>¿ªÿ"/½;>õ^¿¤}QZ€&#ú/Üè<ú M »Žˆˆô(MAw¢?�@eè:…þ©…(/:Ô"¦d„‹%#£Ë#­x¬=‚BùÃNDDfÔ ÝŠ¾G¥†0w¹sü”\[rº0„„Ê1*B Ñß‘„žSãóàîNÙ¥�dÙªq1Å8VƒN jƈˆÈ€F¢_Q­Á^¸pùO‘×Ý‘}÷cBºã`¦fŒö ?3UÉÚ9'±B��jåØ˜/G[øCODDôjDuAËÖü ÆÇ7Ǩñ Þ!Úÿ'T'èõÓ=2Õðh[1Æ3BEh%艈Ȃª‘@5æ–­_�¼ùÞ⦆ȖڿÞû܉H+I�}vmŸlôóíÉù áÖRïÿo艈H�Š‘PɳÇð1%‘7ÞSè;Br p\ݰ…”—ä½p¬[–ïødÞ�Z,/ÃÉãxžj«Žˆˆ‚‘Ô-D©1 ˜1¯ÌwˆÂœüÃ;DW!i¡˜ƒ‹Z”úЩ•­Š4ãóº„V¡8äÑHô’yS‚ûÄ¥L?—åêPkô�¼ N;>oží™)óÐGh""""j’ %£­hº �GårŒ‚–­ÍW#ä>v^� ½,‡Fžñ¨ºwGÇlïø”¡©ˆˆˆ¨IÉè!TeÐE°%B´î+º… 6£S¨­ÊÑqŽŸZ£†Èž1¨VÒ#Äßå·,ÔùEDDÔ½†Dˆ5R¤EOh˜ÕæòœÉ�å�Íx½êLúkÕëº^È3­ºoÄ(a1Ø„º<§†H~l»8»g€®ÛÔ.Osù-Jç‘ ]ÀYO}¼«�¿°ôdúKÅW |_üY3Zo+w˜Ô™‘P#d5ë+Ÿ:Ôõ+ïý„\È/""¢‡‘ˆt$5îíû�f|þºEü¬F5úü½=®×nÆ�ß7áŸw ‰%""Òê�²Ó¢'á²[­¿±ÙÞóæÜ!qsÊS¢Æ mò²�vx| 0‡ˆ¡qsE2žÛ.¸�çù8ãªÂ±‡x‡I´Ùi *ZÖñH…yInPìŒJŸ±)B?ù”‡„,pá í• ‹Ÿÿ->—Tu:ýÕâSé¯TìêsoÉ Ø™õA·zÎGh,jƈˆh2Ê ³ÅTlëqÓ·SZmÊÒ ÏSh¦¿› bÝÖñ=[»~µ[ ¾#Tƒ„Ó,†ÆÏº]U©=³:•þr!np(ÑŒÑM¼L×|õ@ JT�ú õFþ˜0<Ç%‡‰»¯è(t ªT#ä>zN Ëy´C¯ÓaOCpY·hÇèdÚKù8ÓªòŽÑw¨=jfˆˆÈŒ†h.­=ŽlÈŸà»ñY9@w^Þ¡ñËúVÎ!?¦>håzvè´ÀßÕç‰Ò‘ -EyrlÇÍjЙÒÅBœ… ù”Žš""‰ÊÐGÈ…üio4è¾›šŽwÇî]ÿÝM}soÚÚ^tHpTàX±zÍ�-m€:ÊAQj¼ÐuM Ñî>&¸Ú ƒÎPÖ<‡ˆˆˆÚ£$äO’A¯Ëêœè]êUUòpzýóǺÖuméã j„B¶ïR_ôNö2!åtSCt*핊x žSŽzꀈˆÈ…¾jc]Õ«T“ywJݲÑQÂhÔÝï.á3Q½Ÿœg�ð˜ê8‚¿¢¹mwåz?&—ï®MDD&ô\T¨E\³±m�|Kž‚Òj�,i!BœÆ?pÌ�’‘pMŸû‡!Lj±j€TS‘ÖmrˆVv>™å½I¡jqÇC%¶ør«Cí@Í]%h×ÜÄ29@² k[W;�BóŸ©H­Þô�¡�ùKåqb2«*Cí‘âFErˆðBÙŠK„Y)z]†š1""ên2êëÖŒ�©W$›”æV/4UöÈÏE\¼1[�P,nÕ–� �©Ž±Úµ_ÈÎ…”µš·øù݋֣d@ÍéÑ+‰‘VñÃ-ýŠÔ]¿¹]®<3òÞv­Ü§nJÐ&›™$æF·ªÕ\–» )&ô-h¯º-œˆˆh.=Z»ªrïK­V#4c`¸zûR>kj„FºcEÕÐeš³!ÙV¤¤¢!Ȉˆˆˆ<¶¢ ›E_w×êŠJoüíÎäŠ>í|ïv Fuò‹Úi(pÒ Ïå¸I‰BŒX,>N™ø‡fˆêÐ@ä‘�E_Æ„Yª÷-H*Ü=øG‹I¯nHPf"¡Æ'tÇ�zy¤^'´!¡£!Owt�«¢,…ü"""2¢Ù(Ujn�v#å65BŽQã Õø„›mž¿_Õ1U��§ý­{VD[<Çdï!;Ò#ɤ½!�ˆˆÈ�nÕ¼±iÒÊGBSÓ#[ˆ�R&ˆßÌÚª¶Ptv7jžÿ8r"É S£DDDd@ÃÑwH 7‘’¬¹Åº ­E?©RãíxÛ>j|´]‹¬šñÑ#‹ˆˆˆ¬hú ]Ž´:£½( AÓ#$ÏŒl£ˆk#æÞz¢aü¹"¾]œ¡z´�Y�™Ñ_û«ZÛVD·ˆþ?{wÜF²Ea¸Kaf03Ó:–�fÃcfff†effffff '˼ÛÏGåqMÆR,Ec=;ú¿ªkÐŒU*ò©îéÛm§ß¹�!püavjæ$Û±SBaÇ(h€Ši9ß!T2t” |£¹K?ÑÜß}K!¤RãêÈŽðÙ‡øBšŠ B%§ŸjSr§¸�úužŠu$¤ª>ù;-{²s"ë§Ûk`¬‹¸.êgBžšÿÇï8£!õ$•²•À·ºíÀEv`¿~¶î›maChýÉÿüD×;‚H#ªáÆBHõ«ô�†D]FDmGüÁöÛµè ã!¤Dñ(ŒôŒÈ)ý> ýõAý¸ƒ¨ÅwyWõŒH‹´jNßõ»¦ì®©˜÷ªk£Ó7C ±ÄBÑ×A¹Õ;\Aô”s"k�Bª¥½ï ¢‹ €D!•ÞÇõ|èç@¢„Гµ«6zNd š8ÎÏR�^T¿ÙD[ÛkŠ�!¤úCfù›® º‹c!Õ†‰i¹žf$ @molÞæ ©>mü „Ê©Þíõë+xYKL¼„ÐSµ«ì„}ˆµ‘õ¹öaâ „´m�þvcýºXY/3{ @)xöïØ¸ô¢Òàž4²þÚì !ôíéù��ò·¬Ê¨þæéº.�¬óLRüN¡ pØT¿~K¬£ UËÄô¨Ãëì’ÆMžFÖ“´¿vÂä®ê%�D; ÊÉI·gýc­¾ëX‡xY÷7€ä¡‹Kg?1oÌd[?r‚ýô¤ û»Œ2{ZQ½ÂÉnmØ ���Ͷ²(ÏÞtä"ûÆuö°_/ѨH‡ßÅÓÈz”0ÒT[Îàáú½K�¸¯MÙˆ‚'@ª'N[ =T³<¦ÚÑØ¼�FV@—é8oiäsÜV(|T�£ §ôšŽÿÎ4Ôj$õõi¹ö_ÙUö¼’¦P8½l D·T-t7²¾Û^À3!Qíˆñ¡×Oûëj…MÄzðÄEN@©42òŽ¢ÔS6ŒÏ«Ùîidc,Lx³©UA¤PQÐì6ˆ¼##ç=«‡�µ×UÎ�µ‘u °:NA5)ª zþÌùÎHHSsšŽÓ{ÐÈ ˆ1„}aØÚqþ,»bq@ýCÞç>1Õ…¥Aw#ëν¿‘ði'„.( zCH¡¢Ñ�wÔHs½Ï�t¬ƒß'²Þ—Ä�¬@i¤kÚAas×±‹8¡‘Ï­G/v‡�VÀõĉ¬Ç@ò…�žëh��®i:ÎYt°lÜ4ûå)Ùú¹3ˆt­bØh_Bè�¦Ö�žçCŸ7€ä ¡ã êÜA í|´xÁ¹®m}Üá¤Å zÝ—º£z±»‘õCY ÉBÈiƒÒËÊæØH‹ÜAålßãG™Øæ ¢—hd€$ !õøLÙo�=©p¦;º "mxêK…od½ŠFVH‚Òi©±,·>,¯&´Ü÷S ü ¡ð�¬¿3{/ „ö´Î*nТßCè¥Yk7{*,2{À¼ŽìzóL¼�qSÕ}÷½.)›½ÑÓÈšaú<@½BåV?ï€øWͪ|ÝÓÈ:ÄÄ @õþFV!ôV°Õ=R}Ý¡DÕ=3–xY«M!g7‡­žFÖ @%ª~žVòn�7²¡?e–GÓÈú'ð!ôX`…Î4 {í•úu[< –™>PàüÿezÉÆÞBÚ­Û£FØhYßì;�¬€'„¾—R°­7†�vñ6ÆèøðhYŠ»‘@=U»Ê ;y¿A±4²žd„P¼Sq…EùöŒ­}¿q7{Ó½ló6²~Û¡x¦â@:B\ß¿5-o·÷ß?cÙ+žFÖZ‡:*?`?=)#lµLL�~ž:}a(„?m‘­«*×µˆ¥Å žFÖ�4²@¡ûf,µŸŸœ•ìß@è»~¿íÀEq‡Ð‡³?ÝÞtZ¬ñœÐp¦ßT �Súݹ¦û:ÿfÄÀ}užQ¤FÖëhd€>B ˆ¿eU†‚gbêDøFsgéwǯÒKt_Üat]å|'®ÏáL¿u[ºO÷׎¯ƒïºkdý›ô�R)lê¾Ùf§o'ž~‘Sú]¯ëº‚H÷Ç];›u2«F8Î(bézqq�ýqjQÄÜT¿ÞÛȺÊô*€Î?éÏLÎ|×5§�‚F¡©t]#"ÝïÛs¢ÃòjlyIQÄ Òë³*+ìUs»}¯+Ëç¾êidÍ1½ Àª4}åüóÖ3�r¼# oéºîûú´\_,GMŠ8-§×µt;Ú÷úgvÕk4²@ß !->гM·¥û*†�ö-€¶4¬�®Îùï†ÎàÙtN“;„ôùbyO­´ûÀ5-w–éµ€ÒT\L!4dÀ@ßBèȼ@èóÔ”—Ùój…NóÚù¡ïÜܹ*îÉÚ•Q¿ç»ÁO½éy>ô}³wFBºßÇ©8wX„ú… ¬°õ#'¸_�iJNõ`Í2õÐçŸ é~ßVÈ9=Cêý9µhÖ.KÆÿ˜Ynt|Þò¡±OžR4kKïndBH�¨ ƒ¨WÇùѸª:©pfè³hÔóÜÌÕaï¹³z±Í4T÷©?hOö£{ÛD7õ²FV „Têÿ‰¦Oèû)¾MÅ­Ÿ¢�è¶öͦV»nBªV¾E¾/ú·ÿez „ "�t"혠rÃ�º«zIL÷kTÍ}Q4²®3ÿ€�Nù·w\ï¯0�¬&áÏyB(iÊÓÈú�¬@%´~”Zô¾+ˆÎ3‰ „Âì¸ýc“(Bè±À o#kÐÄ@ÅÛÈ:ÅÄ@ÅÓÈz�¬qB(îFÖƒ €JTmmذÕÓÈÚb=æ!Bh׺¦bž·‘µÄ„·,¾cÃ×B]ë ÜêžFÖfWà×ö7z!Y/2»ú´kÊîÛ@!D#ëÏMHhä³ÑõúNg¤ „|«'kWm ÓÈúóáûޱ §aãò´o¾¨Ÿ�1òLÕ©’B=µã÷éEõ›]A´½½^ÏQi¿S|¸=¬þn[66¨×ßíhpÍéè1J1IþÇÞU·±,[™mÙ1³e†8ff;ÌÌÌœ 33ǹá<~—é1^ffføffÛóûl4óÖ9‘Ê…>U§´Ú™Ý8:Õ=§»ï¢­KÎwˆø`¦Ñ¦”BOƒ³�0ÉÞÙG/vÄ~=1íõ’�‹«&Dˆ|:ÄŒ:Žë˜&Í("wÌ×-kßY£%\ÿ’¸\Ç&Ç€Á`0X„0ý¢€Ï;-B {¦ÀBLìyß·} \šà„yÅŠÎqã@\ã"DA¸–¤ýnø¼sL ƒÁpŒ=˜Y…^|ÞñÄBÑiˆX‡°`¿=ïEÿ€ÄH¹iÜ^ñtÝ„ôõbSÙ³bEÑ_µu/áëd��Bj‚=-çqÝ>fÄÞ`Ϩø&>j² ƒÁ`‡D$þ`"!ìÇsg):»Ðf˜¨k7òšwÖ´!v5+!nžbGj‰æ†™2KL'3‚³ÄÅI QnHk¤áêEBNÿY?L,11 Ã1"„ö´HoñÊî6âôß+Å}+; úÀýÄ™öã¹×‹:‹‘IbFlºX™”+Ïn�Æ¥â»Ê>¨ ®îÂLQÖ½•š t²ô¡í”ø¤”j‚„kDH‹ Ë ÿ$F5[£ÝsqrÅçlƒÁ`0n]„PcóßU}oHüh/–® øÕƒíDl¨îÛó|cE…¨p½ÙíŽÃþZŠ~¾®è-ÂÜ=)j1i÷Cé:ÕÛOù‹³¯ð"z:¿½tsÞ®~bhÒ%@ Å'EX¼S´ëDßlz�³º�O·nçÃ`0·3úcvKáïê~£zD?R„@ˆÒ Ÿ vóÀû-BÒ@`O�P÷�õo 78"A¸AÀ‚ÒÅš¼MbPü0Ñ=º·�2I Kmóïlc"üÝCm¬AÌÜå5Ì 5ª�ƒÁ`0nO:nOóþ¢M`d½ßV9iâoÕ]¿}¤­^„ð]¿Žýõžï…÷ÞJÇ"š;&̤´[º·¿H27ÑRƒ§Z÷QtæM©Ây™K5×¢˜/‹v�ó‹w’ª‚ )¸­•¯Š©«D²_ž0»újë`·¸IuëKŸ­,5眿G¨±�ƒÁ`0nU„ô] dW�çvô„ØØÍg¶÷ÄsxïqHï8y¶niZ„ýÚùO™¨è7D NY¬ÖaÁÞQù’˜‘ó+Õ aNîï¥ù€DêM±­òµÚ(ïdm­Y@‰X[úÄç‹ ýz~þŸMËÞõeQXÇúbÕ{M6Á`0Œßtžk3ìTc~ôß-é¦EpÀ}þ@;{û°Ïáy‡ô–C䫵J•¹¸‰�ËzˆwJºŠ®!Ñ"Ø=D4q Pë^®>b^ÞýbeñßH`е{ù!m•ínþ®¨.¡ÎÅ*PcÒÖ}ê]#Ï…lð ·í± ƒÁXNDˆÂç7S˜ —„å¥]m®+@XÇ>ì—¬Žàª¤<Íf=8aŒ“2Yx¹øØ‹D¿‘Tas­­e¨ �Íß»š¿SWÙ[¶ë9fÝ÷¢¾i)70e0Œ;,Búú²t_ñ¿�¶²)@ÿC÷±Ž}·**Íim·‰áHËA0S¨‚S×ÎÖÿ3Œ øôt1£ÈVktAU½>q”Xªµèi=¼my(W§ªnÏÃ`0wI„pö¢¬Ù¶‰uì»å± >Ê`ÐàèI˜ L˜ æ_m½³èB®³/Ë{áßq_Öaÿm1àz~|V½Ñ nÎÔõ`¬ÎqêE0�cÇj‚Ò"²w�F78e‘žºB«‚ñ`DêJ-ò�K{ºÆM¸*\%ÿÖ„,;¸¾ãlì=DOƒÁ`0îŽÉ´–¬ý‘i2I|ק˰ÿv <œœU¡+΀<]¼¯IÇy»ù‹þIsT4í“"{»Õc?Ú3´éRã}«ª ‚„�v³s»¯môÐ¥´>„èbb0 †ãE©5ù<Ì M.Å}¬Ë½/v°Û`01:U´ ŠÔ"Ÿ¹Yë•uŽî¯5mŽ}HÍ¡œêãÖ*ª�f$@gkì3ÇÔù¬Û²%¬Ú%á]4fVœ/iwt`òüï©&èë©Y;¿ wÜ'Ön�Í&ƒÁ`ÜHÅ›ŸË褱©8{jdMö·%Qi¬Á Ô+«1ÃG "ÝÙ F/ %§œlFÃ"Öcš4Sç=©Eêáæø¹cÙ3G=Âa<øžø6ñ+âa«%»Žx�˜Nt61 £QhCÜLàŸsZ}eŒNnÄ(³˜“†k»yF%‰‡Óu ±f‹2t²tW{ù ½¦§5…¨½Û3 wgOÜGc!“l0Š3ycÔh†œ –—åý²ðnˆvÀ1Ä$bkbâââ½Ú=ƒÁ`4 ®Ä‰‡ˆoiºß�\�{ªúaì�­F &Œ®g0À¹Ï†” ôg?0 ȳ…Šb`@Àõü¼Ôèí¸&éjìö¦²ç®¡Ž±£±§[½ûÍ£úa$Ã)§Ïùt‰·¿GÂä=ôž“´Ñ�h„³Õ”àcb0 F£0F Ž®§ÚCtÒ˜š�[2@x*ýÃ`ZÐÎw L“Ò«i¥Rx½À> Á‘Î7¸ØáTFôÂw=ñ ö¢L°c+ƒ‚^¤F§­9à~”Ö&¿±>‘øb´é¶�Á`0þÞ®¾‡ðãovUf[öçÆÖìØÝÁ@o0@wgõ.ñë܉¢[ìPL)Õ¢œkÄcöKNÌØ‘Ášj¯âi!SA%Æ2@h´ûd(@dt�@u�Ÿ¬Ÿ½°•ø‚ü*1×tK`0 >÷Ynà_KÂ:‹u¥Oœ›¶á¨�›¿<�G ˆkôUƒý¹15;v < 8³Ad1¨·^Ö…êuÞ¢µéÂÙä¬"˜ŒéµÉ™Û¥Ø =‡þo²è´¦eTÿcòIN=…¡Ÿˆž¼]ýkgåþæsŒ_­£%. ~Jyk­x ƒ‘D¼`‚¿üÚµ¥ÿ>¶¥âå+á=…q6gWe1ö`QBöuSj-½Áu ²)rà›dF`e½ý4yTßGo•†`À©&ŸÅÙÄJŠþ>™~“vkõü‚üOw‰/B¼¢·ÑPA¼lŒÚpæã¸Ñ ƒÁBĺœà–ú~eo–é)nŽM[/f߃™9B?1ÔÍÙU¬Éœ*vW½ª}Úl¶ËIôÍÂ5Ò_6E=ÞŒ˪â¿#%§ïõ&­Õ’2’’Äßo™ D‹ì �ȧkÜÄ}$pÿ�k­ÆÇa`0 Ædü°·µ ;Ó'q¦“¶ö˜ñÇßâ}m*L P¥î‚=-Úõ¸ôõFáP½Ù `°PÀ¨fbÆfÜWÆD>Ýâ&I!ÁŒŸÇ(à];ªÞ¬£QÚ'zÄO>#Møû!:¨’ç>Ô€ô[ƒÁ÷ï•n@âÃìŸ`“CÀ`0 â öï¬zóÜöÊ7êF¦®6þøÃY¦©.mD˜Å=/‹1£ä!?"XŸ!"¤Ìäšz:R“€À4 lzÆûf@H VØ/I"3ë°Z“M{µæ¦#K9š“ªÂT½Á ì·Á+Ä)ÂD½À`0w;ˆä8ë‘ÎzΓpœFgJMÇ9lÒ²Y§ ôiÃDfcÙ³:oyˆL׊ ú·ÉôY¢_žÓ ñX©å~›MD±Víü�v¢"¢W�µî§ÆÇ- š>axéÂ)Y;÷ߤÁÀ•ø­cG/0 #Õ3 EqX'æ§ÕßTFô>§ï™f$"#½�àÎ6D#p´Ýkè`€uC„ÃŒª tùüQŒO¯†èH7žþÌÇÈ�pâ…[4 ÐÎÆƒÁ`Gè8–x^¦Ã Òu†Anù!mU£Ð é›uG5Ò®gåüVŠŠQÀ !êÑ‹Òv š`XÐ?�ï$ Žó¸ÚE¡ö§c0`0 Æb_· ‚âÇ]ZœaÉÆùŒ¸ãtu=oˆ5%ÿ"“Àµ›ÊŸG=ö¡é' @aD€˜©T¢$V·¸‰tŽ4²nPòÂÓÖç�R8¬!DUX³‚ª.ÀXðÓ40 #˜xi,ˆ ]ƒD/2ª�  T¦âê6”=-ˆo€bP5øÍh@©¬3Jö˫õ‘8×�™@ÿ΄P0 qüé  ƒ±E_¬J�Î’íú<�ý3ÖÝH1é�0ý Ît6—¿t"C=ÙDYxwyŽcL¯gý\"~‰Oݽ‘FÓ ZF 8I„Ÿ°Á€Á`0IÒ `$D¡*²O-ÎzPƒƒ:¤è°[´ÅœršRcH}a`Üe²a_Ò»ÚôLñÏÇŒøü½.}ö<ñA⟟Á€Á`0-®e image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/enderpearl.svg0000644000175100017510000002353015224505336024126 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/creeper.svg0000644000175100017510000006350415224505336023437 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/ftb-logo.svg0000644000175100017510000002342015224505336023514 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/flame.svg0000644000175100017510000000630215224505336023067 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/netherstar.svg0000644000175100017510000002635215224505336024171 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/skeleton.svg0000644000175100017510000003601215224505336023630 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/cattiversary.xcf0000644000175100017510000035551315224505336024517 0ustar runnerrunnergimp xcf v011¡u–B�B�Ígimp-image-grid¬(style solid) (fgcolor (color-rgba 0 0 0 1)) (bgcolor (color-rgba 1 1 1 1)) (xspacing 16) (yspacing 16) (spacing-unit inches) (xoffset 0) (yoffset 0) (offset-unit inches) gimp-image-metadataå 8 8 8 629 417 1 2 300/1 300/1 1 1 LN uburfcat_hat.pngÿ!?€ "     %$ÿÿÿÿ#ÿÿÿÿ\ u˜MïMûNN uØèK[k{‹›$833>3N3^3n3~=7J™J¯J¿JÏJßJïJÿKKK/K?KOK_KoKK�KŸK¯K¿KÏKßKïKÿLLL/L?LOL_LoLL�LŸL¯L¿LÏLßLïLÿMMM/M?MOM_MoMM�MŸM¯M¿MÏMß­üSc(ü  úS®òòi&ø þ}òÿh&ÿþ þÄòÿg&þÿþÓòþà'ý þþ½òýæh*þ þþ…òþŽ-ýÿÿSòýÆS0óŽòòª,î(/  CòòŽ+í;Y„‘�m?6'ò›)üG�—÷W ·*þ� —ÿaÿ(þ;‘—þvC9ÿú D8'$ø e——�A9ÿùE��Ž~A$üuSû2 ý Do—ýŽ?-ù M¨?üCx—ý�9*÷ M¦ÖZþB—þo(öTÓÛŸ)þB—ý%ô,Ïñ¼. þB—þŽ%ô#Ïñæe þB—ÿ\$ú™òíˆü  úh—�b#ú~òò”úý;$þkòÿQøD-úÝòòœÿýT\-û1êòòWÿüH÷,û§ðã5ÿ úQÿÐ,ü‚B÷oÿÿ¥4ù —ÿþƒ7ýVÏãÿþû,7ÿVÿÿ‹8ÿVÿúÏ\N4ÿVÿùd;;3ÿVÿþ0;ÿG3ÿVÿþ;þ0 2ÿVÿþ0;þ81ÿVÿýd;ÿI1ÿVÿýÇ=þY0ÿVÿü=;þ^0ÿVÿüû;ý /ÿVÿ÷Ïd00dǽ.ÿVÿþ“-ÿVÿþw,ÿVÿÿV,ÿVÿþÚ+ÿVÿþ¨*ÿVÿþ…)ÿVÿÿZ)ÿVÿþí(ÿV ÿÿ“ÿþÀ'ÿV ÿþÛÿþ“&ÿV ÿýdçÿÿw&ÿV ÿüç³ÿ&ÿV ÿüp‹ÿ&þèÿûç\ÿ'ÿËÿûp(«(0ÿ'ð´H@`ƒ§Ëëÿï5Ãò—ûÿ'ýËç(ø —òòÙ(Óÿ'õËÿç(5Ÿ——Ãòö—$Hl�¯Ó÷ÿ'ùËÿÿç<§òÿÒ—ÿ(ÿd'ÿËÿüó<(Ò òú—8¿ÿ'ÿËÿüóD(Òòûà—($¯ÿ­ü=H(üN8 ú=±±M&ø`›¨¨|8 þ[±ÿL&ÿe¨þIþ�±ÿK&þ" ¨ÿ:þš±þ¤'ýFž¨þ þбý¨L*þW¨þ„þa±þh-ý8Œ¨ÿ:ÿ=±ý‘=0óe¨¨Šh±±|,îN¨¨=1±±h+í%8=<.8¨¨±q)ü <@÷$¨†*þ 6 @ÿ)1þ=@þ2ÿú$ø*@@<ÿ`ÿ<ý5$ü1#û$`ý/@ý<-ù8z.`‰ý 2@ý;*÷ 8y�Ah†hþ@þ/(ö=šŸt8¨;þ@þ5 &ô–°Š" 8¨vþ@þ; %ô—°¨JK¨¨8þ@ÿ&$úp±­c üV¨¨Gú,@;*#ú\±±l új¨¨^ý$þN±ÿ;ø•¨¨‰-ú¡±±rÿ8¨ý8-û$«±±@ÿ8¨ü8D,ûz¯¦(ÿ=¨úQG9,ü_0¨÷|8GG.4ù¨I*Gþ$7ý9?GþE 7ÿGÿ&8ÿGú9"¢’'4ÿGù!¦¨¨ %3ÿGþ m¨ÿƒ3ÿGþm¨ÿ)2ÿGþ m¨þ¤'1ÿGý!¦¨ÿ†1ÿGý7n¨þ¤0ÿGü#!n¦¨ÿ™1ÿGüE#!mý!/ÿG÷9  74.ÿGþ)-ÿGþ!,ÿGÿ,ÿGþ<+ÿGþ.*ÿGþ%)ÿGÿ)ÿGþB(ÿ Gÿ(Gþ5'ÿ Gþ<Gþ)&ÿ Gý@Gÿ!&ÿ Gü@1G&ÿ Gü&G&þ@Gû@G'ÿ8Gû(�( G'ð1$.8AGB.š±€EG'ý8@ ø€±±§(:G'õ8G@ .…€€š±ö€ '0:DG'ù8GG@бÿ¢€ÿ(ÿ'ÿ8GüC(¢ ±ú€5G'ÿ8GüC(¢±ûª€( 0G­ü(üS< ú+==&øf¥³³…< þ=ÿ&ÿk³þNþ1=ÿ&þ$ª³ÿ>þ5=þ8'ýK©³þªþ/=ý:*þ]³þ�þ!=þ#-ý<–³ÿ>ÿ=ý20ól³³“#==*,î6@ T³³A==#+íOy³ÄÔUI<³³ =')ü'`¿Ì÷u ³.*þ ¯ Ìÿ„þ'þPÄÌþ [Mÿú\M5$øˆÌÌ¿WMÿgù^ÃÂÁªX$üŸqû gý\—ÌýÀU-ù*g’ü\¢Ìý¿M*÷)6n�nþYÌþ•)÷46( <³?þYÌý­(&õ 3<. <³~þYÌþÁ(%û 3<9üP³³<þYÌÿ~$ú&=;!ü\³³Lú�Ì¿†#ú==$úq³³dýP$þ=ÿøž³³’ -ú7=='ÿ<³ý< #-û :==ÿ<³ü<_ ,û)<8 ÿA³úWbP ,ü ³÷„<*bb? 4ù³N:bþ27ý!OWbþ`7ÿ!bÿ58ÿ!búOŒ|4ÿ!bù& ³³—3ÿ!bþ[³ÿo3ÿ!bþ[³þ¦2ÿ!bþ[³þ�1ÿ!bý& ³ÿq1ÿ!býL^³þŽ0ÿ!bü0^ ³ÿ…1ÿ!bü`0[ý/ÿ!b÷O&&LH .ÿ!bþ8 -ÿ!bþ. ,ÿ!bÿ!,ÿ!bþT +ÿ!bþ@ *ÿ!bþ3 )ÿ!bÿ")ÿ!bþ[ (ÿ! bÿ8bþJ 'ÿ! bþT bþ8 &ÿ! bý&Xbÿ.&ÿ! büXDb&ÿ! bü+5b&þ YbûX#b'ÿNbû+#W#b'ðE$2@NZb[*L=N`b'ýNXø N==K#Qb'õNbX*RNNL=öN )6CQ^b'ùNbbXU=ÿLNÿ#ÿ&'ÿNbü]#L =úNIb'ÿNbü]#L=ûIN# Cb.ü+…Ò˜$û_Rý“ßÿþå !÷S¦åÿó› ÷`ëÿÿôíü± õ3¢Üòôêÿÿï`õ »ÿÿèÿÿðùŒóHâúóßëÿÿæÿÿ€üT÷ÿõÿýñôs û »þûÝÿýýÿƒüTûÿæÿýñïh úøÿùÄðÿýüÿPü ÷ÿèÿüìòø~ þðÿþñîÿ÷ðÿó¿ÿçÿþðñÿÿt!ø$`Ïÿÿóÿùåÿ—`ÿòÿùïÿÿÓ–i&ûH¿ÿÿæÿüüû$ÛÿûæÿÿÏP'ê8t€x“ôÿìÿÿåÿÀÿïÿÿèÿóT'ý8¯÷ÿúóÿùÿÿúÿüïÿç0&õ0Ãÿÿýéë÷øëøÿüëÿÏ%ú”üÿòðÿÿôÿûö×ÿß &ü°ÿþð ÿòàÿÿþÿþÿý´ Ÿ|P "ü4èïúÿþêôÿÿþ ÿý÷¯P ørïõÿÿðöÿÿïßÿëÿõñÚ÷ðõñüÿÿÏLöRæõÚçÿÿ·XþNâÿÿùÿÿöÿúîñÿÿŸ ù1Íöõÿ×4û…ÿÿöÿú“|Óÿÿíÿûì÷ÿÛ(úCëÿ´ùºÿþöÿüÿùþ<dëÿýÿüîÿë<üHóÏ ø(Ûÿüêÿõðÿùé›ÿýÿüîÿó<þ( ø çÿ÷üûÿWÿù¿�ÿýÿüõÿô2!õ ×ÿñùûÿ¬ ÿìÿÿ‹ý�ÿýÿüêÿêô»ÿíÿúÿå8ÿåÿÿTö£ÿçÿ÷ñôûÿ|ô—ÿóÿÿöÿLTÿÛÿóó(ïÿõûûò¼xý0ûüÿúÏ€ÿèÿñøÿÿÓ lÿýÚªnôÁÿïÿÿêÿd«ÿõúÿÿ§Úÿ�!ü\ÿ÷øÿûïÓÿÿóÿút:C"ótõûûþõÿ‹ûúÿûåÿÿû@&õLÖøñÞÿLXÿÿüÿûêÿÿç&óZæöûÿ£ÿÿîÿ÷õÿÿË "û¢õÿíÿìÿÿ÷ÿ÷þÿÿ´ !óFÛÿÆ8ÿýólß ÿúš!ü€Óyû,� ßÿöýÿÿüc +ÿß ÿúò-+ÿß ÿûÚ+ÿßÿüŸ+ÿÛÿüþ`+ÿ¿ÿýö1+ÿ¿ ÿûþÿÿÛ+ÿ¿ÿÿ“+ÿ¿ÿÿ\*ÿ¿ÿûæÿÿó,)ÿ¿ÿûîÿÿÛ (ÿ¿ÿüùÿÿ³(ÿ¿ÿÿ|'ÿ¿ÿûçÿÿûD&ÿŸÿûéÿÿë%ÿŸÿûóÿÿË$ÿŸÿüüÿÿ—$ÿŸÿÿìÿ$ÿŸÿýæÿÿ$ÿŸÿþîÿ$ÿŸÿÿù$ÿŸÿ$ÿŸÿ$üƒÿÿêÿ$ü€ÿÿÕÿ$ü€ÿÿÙÿ$ü€ÿÿÕÿ$ü€ÿÿÕÿ$ü€ÿÿÕÿ$ü€ÿÿÕÿ$ü€ÿÿÕÿ @ÿ>ÿV>þÜ=ýÿ¬<ÿþ…;ÿÿZ;ÿþî:ÿþÅ9ÿ÷ÿþ˜8ÿþw7ÿÿV7 @ÿ>ÿ>þ==ýG0<Gþ%;Gÿ;GþB:Gþ69ÿDGþ*8Gþ!7Gÿ7 @ÿ >ÿ!>þT =ýbB <bþ3 ;bÿ";bþ[ :bþK 9ÿ^bþ: 8bþ. 7bÿ!7 €ÿ`>þó0=ýÿß<ÿÿ·<ÿÿ€;ûçÿÿûH:úÿèÿÿë 9ÿûóÿÿË8ÿüüÿÿ›8ÿÿíÿÿ`7ÿûæÿÿ÷46ÿûíÿÿß5ÿûùÿÿ·4 ÿÿ€4'ÿËÿýó—òûÃ(›ûÿ'ÿ´ÿý�(ÜòüîRp÷ÿ'ÿ±ÿý÷—òÿÜòþ—“ÿ'ÿ±ÿô�«òË—RÆòò—dÿ'ÿ±ÿóû$(—(Ph(Æò—4ÿ'ÿ±ÿÿŸ÷@Ëÿÿƒ(—(ÿ'ÿ±ÿü$0·ÿÿ�ÿÓÿ'ú±ÿÿ¯£ÿü�§ÿ'û±ÿÿ³÷ ÿý—tÿ'ÿ±ÿþ«Pÿ'ÿšÿÿÃÿ'ÿ“ÿ'ÿ“ÿ'ÿ“ÿ'ÿ“ÿ'ÿ“ÿ'ÿ“ÿ'ÿ“ÿ'ÿ“ÿ'ÿ~ÿ'ÿsÿ'ÿsÿ'ÿsÿ'ÿsÿ'ÿsÿ'ÿsÿùÏd00dÇ ÿ'ÿsÿüû‹;üûÿ'ÿsÿü‹=;ü;=ÿ'úaÿÿÏ=ý=Ïÿ'úVÿÿd;ý;lÿ'ûVÿÿ0;þ;4ÿ'ûVÿÿ;þ; ÿ'ûVÿÿ0;þ;0ÿ'úVÿÿd;ý;dÿ'úVÿÿÇ=ý=Çÿ'ÿVÿü=;ü;=tÿ'ÿVÿüû;ütûÿ'ÿVÿùÏd00dÇ ÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿ'ÿVÿý‡ÿÿ'ÿVÿüóóÿ'ÿVÿü££ÿ'ÿVÿüPPÿ'ÿVÿûóó'ÿVÿÿ£ÿ£'ÿVÿûX*·*\'ÿVÿú÷*Âò  'ÿVÿÿûßúŸyòòØ'ÿVÿþ«ÿ òÿy'ÿVÿþãDÿB°ÿÛòÿÛ'ÿV ÿü‹¼ò'ÿ8GýC€±ûš(+EG'ÿ2Gý'(¨±ü°MDG'ÿ1GýD€±ÿ¨±þ€(G'ÿ1Gô'�±ž€Mœ±±€G'ÿ1GóE (€((œ±€G'ÿ1Gÿ,÷8GG$(€(G'ÿ1Gü 2Gÿ'ÿ:G'ú1GG0-Gü'.G'û1GG1D Gý* G'ÿ1Gþ/G'ÿ+Gÿ6G'ÿ)G'ÿ)G'ÿ)G'ÿ)G'ÿ)G'ÿ)G'ÿ)G'ÿ)G'ÿ#G'ÿ G'ÿ G'ÿ G'ÿ G'ÿ G'ÿ Gù9  7 G'ÿ GüE&!mü!#EG'ÿ Gü&!n¦¨ü¦n!#G'úGG9n¨ýn9G'úGG!¦¨ý¦!G'ûGG m¨þmG'ûGGm¨þmG'ûGG m¨þm G'úGG!¦¨ý¦!G'úGG7n¨ýn7G'ÿGü#!n¦¨ü¦n! G'ÿGüE#!mü! EG'ÿGù9  7 G'ÿG'ÿG'ÿG'ÿG'ÿG'ÿG'ÿG'ÿG'ÿG'ÿG'ÿG'ÿG'ÿG'ÿG'ÿGý%GG'ÿGüCCG'ÿGü--G'ÿGüG'ÿGûCC'ÿGÿ-ÿ-'ÿGû(�('ÿGúD–±‚'ÿGÿE>ú,^±±£'ÿGþ/ÿ‚±ÿ^'ÿGþ?ÿ0Œÿ¦±ÿ¦'ÿ Gü&“±'ÿNbý]N=ûL# ;`b'ÿEbý6#K=ü?;+^b'ÿDbý^ N=ÿG=þN8b'ÿDbô6W=MN;M==N&b'ÿDbó` #N#'#M=Nb'ÿDbÿ=÷Nbb2#N#b'ÿDbü Fbÿ6ÿQb'úDbbC >bü6@b'ûDbbD^ bý:,b'ÿDbþAb'ÿ;bÿJb'ÿ8b'ÿ8b'ÿ8b'ÿ8b'ÿ8b'ÿ8b'ÿ8b'ÿ8b'ÿ0b'ÿ,b'ÿ,b'ÿ,b'ÿ,b'ÿ,b'ÿ,bùO&&L b'ÿ,bü`5[ü0`b'ÿ,bü5^ ³ü ^0b'ú%bbO^³ý^Ob'ú!bb& ³ý )b'û!bb[³þ[b'û!bb[³þ[b'û!bb[³þ[b'ú!bb& ³ý &b'ú!bbL^³ý^Lb'ÿ!bü0^ ³ü ^,b'ÿ!bü`0[ü,`b'ÿ!bùO&&L b'ÿ!b'ÿ!b'ÿ!b'ÿ!b'ÿ!b'ÿ!b'ÿ!b'ÿ!b'ÿ!b'ÿ!b'ÿ!b'ÿ!b'ÿ!b'ÿ!b'ÿ!bý3bb'ÿ!bü]]b'ÿ!bü>>b'ÿ!büb'ÿ!bû]]'ÿ!bÿ>ÿ>'ÿ!bû! O #'ÿ!bú^ D=H'ÿ!bÿ`Uú=+==F'ÿ!bþAÿH=ÿ+'ÿ!bþWÿ*MÿE=ÿE'ÿ! bü5Q=$ü€ÿÿÕÿ$üdÿÿ×ÿ$ü`ÿÿ×ÿ$ü`ÿÿ×ÿ$ü`ÿÿ×ÿ$ü`ÿÿ×ÿ$ü`ÿÿ×ÿ$ü`ÿÿ×ÿ$ü`ÿÿ×ÿ$ü`ÿÿ×ÿ$üHÿÿÝÿ$ü@ÿÿàÿ$ü@ÿÿàÿ$ü@ÿÿàÿ$ü@ÿÿàÿ$ü@ÿÿàÿ$ü@ÿÿàÿ$ü@ÿÿàÿ$ü@ÿÿàÿ$ü,ÿÿçÿ$ü ÿÿìÿ$ü ÿÿìÿ$ü ÿÿìÿ$ü ÿÿìÿ$ü ÿÿìÿ$ü ÿÿìÿ$ü ÿÿìÿ$ü ÿÿìÿ$üÿÿ÷ÿ%ÿ%ÿ%ÿ%ÿ%ÿ%ÿ%ÿ%ÿ%ÿïÿ%ÿßÿ%ÿßÿ%ÿßÿ%ÿßÿ%ÿßÿ%ÿßÿ%ÿßÿ%ÿßÿ%ÿÓÿ%ÿ¿ÿ%ÿ¿ÿ%ÿ¿ÿ%ÿ¿ÿ%ÿ¿ÿ%ÿ¿ÿ%ÿ¿ÿ%ÿ¿ÿ%ÿ·ÿ%ÿŸÿ%ÿŸÿ%ÿŸÿ%ÿŸÿ%ÿŸÿ%ÿŸÿ%ÿŸÿ%ÿŸÿÿþà6ÿþ²5ÿþ…4 ÿÿZ4 ÿþò3 ÿþÉ2 ÿþš1 ÿþw0 ÿÿV0 ÿþã/ÿþµ.ÿþŠ-ÿÿZ-ÿþô,ÿþÍ+ÿþŸ*ÿþ|)ÿÿ‡ÿÿV) ÿþ×ÿþå( ÿýƒÃÿþµ' ÿý0pÿþŠ& ÿüÛÿÿZ& ÿûƒ-©-Âÿþõ% ÿû0©ò©oÿþÐ$ÿÿ›`ú\-ÛòØ=`ýß#ÿ÷û|-©©ØòôØ©©-«ÿÿ|"ÿü¿©òü©<ãÿÿV"ÿüïP-ØòüØ/xûÿþè! ÿý—3ØòýØ?»ÿþº ÿþD©òþ©ƒ ÿþŽ ÿöê­òòèòò©0 ÿÿ\ ÿõš:¯©[©±-Ú ÿþ÷ ÿÿDý\Ö@ÿƒ ÿþÒÿôï¦ÿÿû†3 ÿþ¥ÿü£@ßÿüÊ$ã ÿþƒÿýTƒûÿýóXŽ ÿÿZÿÿË ÿÿÊ ÿþè#ÿþº$ÿþŽ%ÿþw&ÿÿV&ÿþ×'ÿþ¥(ÿþƒ)ÿÿZ)ÿþë*ÿþ½+ÿþ“,ÿþw-ÿÿV-ÿþÚüKQÿùÏd00dÇÿþ¨ü'q—2ÿüû‹;üûÿÿ¿÷@ HKÿü‹=;ü;=ÿöÛKK2qÿÿýÏ=ý=ÏÿìdX——’h‹�Gÿýd;ý;lÿûçK—•—ôs’^�; ÿþ0;þ;4 ÿövˆ—”R—þnÿþ;þ; ÿõêï2—„%�—þuÿþ0;ú;0ÿô÷þÿøü•“apú1�——nÿ÷ÿýd;ã;dÿÅ`q‚©½Ü¨ ŸJ4*„|bÿŸß ÿýÇ=ï=ÇýÙ(6& ¹ ]ù# þ« ÿü=;ñ;=O«ÿô[=…ZOc^þDù°ÿBþHã ÿüû;Þr+$÷ñ  w””—%*�`% NˆB2òüº‹ÿÛÏd00dÇŸ�ÿÜ:Q[R“qu——”‘)†|xo6Gþ>6Gþ15Gþ%4 Gÿ4 GþC3 Gþ82 Gþ+1 Gþ!0 Gÿ0 Gþ?/Gþ2.Gþ&-Gÿ-GþD,Gþ9+Gþ,*Gþ")Gÿ%Gÿ) Gþ;Gþ@( Gý$6Gþ2' Gý Gþ&& Gü<Gÿ& Gû$.�.6GþD% Gû �±�Gþ9$Gÿ+ú.¨±§6ý6,#G÷E".��§±ô§��./GG""Gü5�±ü�?Gÿ"GüB.§±ü§/!EGþ@! Gý*0§±ý§54Gþ3 Gþ�±þ�$ Gþ' GöA’±±­±±� Gÿ Gõ*3“�W�”.< GþD Gÿý;ÿ$ Gþ:GôB.GGE% Gþ.Gü->Gü8 ? Gþ$Gý$EGýC' GÿGÿ8 Gÿ8 Gþ@#Gþ3$Gþ'%Gþ!&Gÿ&Gþ;'Gþ.(Gþ$)Gÿ)GþA*Gþ4+Gþ),Gþ!-Gÿ-Gþ<ü"Gù9  7Gþ.ü/@GüE&!mü!#EGÿ5#÷ Gü&!n¦¨ü¦n!#Gö< /ÿGý9n¨ýn9Gì#@@<+:6Gý!¦¨ý¦!Gû@ @>@ô0='< Gþ m¨þm Gö# /6@>"@þ.Gþm¨þmGõAB@7;@ÿ1Gþ m¨úm GCDFGøF)(&/û<@@.ÿDGý!¦¨ã¦!G6$/4=.,745)ÿ,> Gý7n¨ïn7F<  3$÷   þ/ Gü#!n¦¨ñ¦n!/GC2!&#þùŒÿ0þ? GüE#!mÞ! DC,-> >@ =( 9±ü’&GÛ9  7,'G= &"=01@@>= 943/bþV 6bþD 5bþ3 4 bÿ"4 bþ] 3 bþM 2 bþ; 1 bþ. 0 bÿ!0 bþW /bþE .bþ5 -bÿ"-bþ] ,bþN +bþ= *bþ0 )bÿ3bÿ!) bþR bþX ( bý2JbþE ' bý+bþ5 & büT bÿ"& bû2+Y+Jbþ^ % bûY=Y*bþP $bÿ;$ú#+K=L3$ýJ= #b÷`/+YYL=ôLYY+Abb0 "büI Y=üYWbÿ!"bü[+L=üL,.`bþY ! bý:.L=ýL4GbþG bþY=þY2 bþ6  böYZ==C==Y bÿ# bõ;2[YIY[+S bþ_  bÿý#Rÿ2 bþP bô[?bb`3 bþ? bü>UbüM W bþ2 bý 2`bý]!6 bÿ"bÿN bÿM bþY #bþG $bþ6 %bþ. &bÿ!&bþR 'bþ? (bþ2 )bÿ")bþZ *bþH +bþ8 ,bþ. -bÿ!-bþT üenbùO&&Lbþ@ ü5˜ÌDbü`5[ü0`bÿI0÷b)fbü5^ ³ü ^0böTffD˜ÿbýO^³ý^Obì&qÌ̹ˆ»¯ `bý& ³ý )bûXfÌÄÌôœÅÂP#,bþ[³þ[ bö0“¥ÌÈoÌþ•bþ[³þ[bõY[ D̲3¿ÌþŸbþ[³ú[b]^abï`98s˜CÂÌÌ•ÿ^bý& ³ã &bK$+1@HT@=VF9²¨¬…ÿ=U býL^³ï^LaSGm&ù/& þA bü0^ ³ñ ^Ab]"+‹^Sjcþ\ùMÿ*þW bü`0[Þ+ ^\=~È&ÈÌ$ 39Ã2j¸YD=üQ5bÛO&&L=6bT\|oÀ™žÌÌÇÄ%8¶§¢•IÿÿçÿÿH3ÿûèÿÿï 2ÿûóÿÿÏ1 ÿüüÿÿŸ1 ÿÿïÿÿd0 ÿûæÿÿû4/ ÿûìÿÿã. ÿûùÿÿ»-ÿÿƒ- ÿÿéÿÿL,ÿûçÿÿï$+ÿûñÿÿÏ *ÿüüÿÿŸ*ÿÿñÿÿl)ÿûæÿÿû8(ÿûëÿÿã'ÿû÷ÿÿ»&ÿÿ‡&ÿÿêÿÿP%ÿûçÿÿó$$ÿûñÿÿÓ #ÿüüÿÿ£#ÿÿñÿÿp"ÿûåÿÿûÿ0¿ÿ>ÿ¿'þõ ÿüË$*Õò(ÿË ÿüó`*Àò(ÿË ÿýƒ:áò(ÿË ÿþdyò(ÿË ÿýû òýËòò(ÿË ÿ÷¿´òÚy:yÚ(ÿË ÿ÷pB¢*L×L*(ÿË ÿû�ÿÿ�(ÿË ÿûÃ$Ïÿ(ýʯ« ÿüp`óÿ(ü­,$÷ÿþoÿ(üg�ÿüã&$÷ÿ(ûïÿüŸ�ÿ#ö 2 [dxÿø÷ïÿ¿#õ8‡e KKãÿ÷ƒ[dxÿÛ&ü&†�<ì @M{ë2——2ãÿd #æ'A K =ŽUWHv—’" ;ã$õxxocŒn �6ÿ2—ÿÿ%ÿ0—ó” (ez…–—ÿ;þ #!ü ETd—ÿú*f—ùW([1i!ü@”’a—éuŽH;Yz”i——–Œ/ î@�z V—R88—zŠ–yü #—– ù 7=þu—–÷’x-*’—ÿn$î!*4^3h——•–—sþ#l—ÿY"ã �`e——’…“‹ ?‹[*-–NHZ"í2e[l*|—–‰‰—–x—ü„ %%úxu÷ Sn�„•:I—ÿ +ñ>sT1—ý&*ûm——nûz——B-û2——”úO—w.ûMV2 ü2——2;üMV'þD Gü8 (¢±(ÿ8 GüC(•±(ÿ8 Gý$+§±(ÿ8 Gþ^±(ÿ8 GýE‚±ý›±±(ÿ8 G÷5�±¥^+^¥(ÿ8 G÷0„(;((ÿ8 Gû'Gÿ'(ÿ8 Gû6 9G(ý80/ GüCG(ü0 DGþG(ü'Gü> DG(ûBGü,'G#ö"&!GøDBG5##õ9*  ?G÷$"&!G<&ü9<í##"A@@?G#ç 6##.@= #?%õ33/*:.;ÿ@ÿ ÿ%ÿ@ù>*/ ý->@ÿþ!ü#*@ÿ ü+@ù$&5,!ü>=(@é1 <%3>,@@?; î;3 $@" @3:?3ü@? ù 5þ1@?÷=2=@ÿ.$î'+@@??@0þ-@ÿ%"ã;(*@@=8>;:&? %"í*&-4@?::@?2@ü7%ú3 1÷".;7?@ÿ+ñ0#  @ÿ,û.@@.û3@@-û@@>ú!@2.û $ü@@;ü $'þ ^ büN F=(ÿN bü]$ D=(ÿN bý2B=(ÿN bþ&+=(ÿN bý`H=ýB==(ÿN b÷IO=F++F(ÿN b÷+*I R (ÿN bû 6bÿ6(ÿN bûJ Ob(ýMCA bü+$]b(üB ^bþ *b(ü'6büV ^b(û[bü=6b#öD dl.bø^ [bI0#õK¶‰!ee!Wb÷2dl.bT&ü3µÁQì00/ZDÌÌDWb&#æ5W!eS—00 !ˆÌÅ& 0W$õ¢¢–†¼• ¿GÿDÌÿ&ÿ%ÿAÌóÈ6ˆŽ%nÃÌÿPþ.!ü^r†Ìÿ&ú5ˆÌùu6|C¬�!üVÇÆ„ÌéŸ #ÁbPx¥È�ÌÌ˾? îW¿¥+uÌoLLÌ¥»Ë¤ü0ÌþËÊ ùJ S¬òŸÌËÊËÅ¢<9ÅÌÿ•$î,9FE�ÌÌÊËÌœ þ0’Ìÿx"ã¿‚ˆÌÌÆ´Æ½ V¼|9<Ëiaz"íDˆ|’9¨Ì˹¹ÌË¢Ìü² 2%ú¢#Ÿ ÷ p•À²ÉOcÌÿ+ñTœrC &Ìý3*û”ÌÌ•û¥ÌÌY -ûDÌÌÈ úlÌ¡.ûiuDüDÌÌD;üiu%ü›ÿÿñÿ%ü€ÿÿÕÿ%ü€ÿÿÕÿ%ü€ÿÿÕÿ%ü€ÿÿÕÿ%ü€ÿÿÕÿ%ü€ÿÿÕÿ%ü€ÿÿÕÿ%ü€ÿÿÕÿ%ü|ÿÿÕÿ$ûD�ÿÿØÿ#úÏéÿÿæÿ#ÿdÿÿûÿ"þéÿÿD€ü¯ÿöìÿþ$÷ÿýýíûÿÿƒÿø ãÿÿñÔÔäÿý\ÿþÿûlÿóÿíÿûïÿõþÿù“ÿøöòÿîÿþ(ûÿÿõÿÿ4`þeöÿ!ý|ÿý ÿÿý ÿþþò þï ÿüþÿúþ ÿÿþÿ ù�ÿñÔôöíÿýûÿú ÿýù×ùþ$ûÿÿýÿûòïÿúùÿúùÿùžçÿÿ@€ý»ÿæÿÿýÿ&þ÷ÿñüÿÿù˜–ÖÿúÿÿåÿËŒ€'ó€ÿåßÿÛÿûL÷ÿþû$,øçÿÿïSÿlú€ÿåßÿ�.úlÿÿ|xúçÿÿï.üÛãülÿÿ|0þX\üÛã<þX\òüÕ*$Ëÿå÷ÑÊM w‚)3——–ŒdûòÀ*`óÿûƒ[d þ#-øE—–”•=ýà:ƒÿþ¯«ÿèë2——2u|%*’F ýòydÿúû,$÷ÿ¿ñHYz—€%”——”hþD üò û ÿùŸ�ÿÛ÷+=——}0—û1q^ÿ üò²Ã ÿö÷ïÿdŽè‡ˆ—†R——••[’—4j ü¢Bp ÿòƒ[dxÿç%;*ìK—@[_”–`^ M—†/—F ùÿ¯«ÿõë2——2ãÿú WþV]ùˆ—M�tE öÏ$¿,$÷ÿ¿íiFv——K@ïBDn—ñ1 -88,4Ydn õÿó`E�ÿÛó„�1“D—òˆ’—(——Šÿï¡ïÿdŽŽJ ù…ŽŽ’8—óD†——0——:ÿåƒYdxë‰2——`x—” ‹—ýI4—ù?#LPÿèë2——2=j—n†[”* D=ô”|MR‹——cðHv——€šNbx—ôi-YÿO—ÿ2û8ROù8–R„—ìlu—•A1 sxxü„ŽŽbã? @ B�——q/‹——Œ‹——B õz——’4-=—ên„n|•––•t V——wÿ,—ý6M—íV#i44 ——yLü2——2ÿB—ý6%—úƒ K—Iÿü”——2üMVúMNz—ï1(“ŒPT”`_xx#ÿý!`—øT-b x—þB 'ÿlð—O3<  m——w(÷8gO Iü2——2*ÿ%—ÿBüBI/üMV*þˆ—üx.xÿ3þ B—ý 8úm——A :ü2——2;üMVø±ü¢( 8GæD:827@@?;* û±•(CGû$"&þþ@?ýý§+$Gþ0/GñA@@14ú= ý±^GúE DG5#ñ"3@2 ;@@>+þ ü±‚E Gù,'G<÷@@/@û /'ÿ ü±�6 GöDBG 6è3 9@2"@@?>&=@- ü„0 Gò$"&!G@ì@&(>?(' @8@ ùG0/GõA@@?G#ú$ þ$'ù9@ 61 ö9 5 DG5#í.@@#B.@ ñ %*. õGC'G<ó 7;(@ò9 =@ @@:Gï,BG 66ù566-@ó8@@5@@Gå$"&!A&@@( 2@> :@ý@ù "GèA@@#*@. 8&>ô >4 ":@@*#ð.@@21)2 @þ, ù %ÿ!@ÿû "!ù?" 7@ì-1@?  133ü)66(ã;@@/:@@; :@@õ.@@=@ ÷.7.4 ?÷1$@@2ÿ@ý @í5$,@@3 ü@@ÿ@ý@ú7@ÿü>@@ü $ú 3@ï>;!#>( (33#ÿý(5@ø#)2@þ'ÿ-ð @!.@@2(÷5+!ü@@*ÿ@ÿüü $*þ9@ü23ÿ3þ@ý 8ú.@@:ü@@;ü $ø=üF Nbå^ PM ¡°7EÌÌ˾‡û=D $]bû2dl þ0<ø]ÌËÉÊSýB2bþCAbèZDÌÌD Ÿ¨29Å_#% ý=+&bú` ^bI0ñh¥Ì”¸ÌÌÉŒþ\ ü=H` bù=6bT÷;SÌ̃ 8ÌûC(˜ÿ ü=NJ bö^ [b&—è�#¸Ì‘oÌÌÉÊ|ÅÌF� üI*+ bò2dl.bX3P9ìeÌS{�ÈË‚ h̵@Ì_ ù bCAbõZDÌÌDWb0 úu&þu~ù ¸Ìh¯œ] öO H ^bI0í(ˆÌÌW0[T\•Ì ñC ¢ÿ3þYÌý8ú”ÌÌX:üDÌÌD;üiuø3ÿûþÿÿÕüÿÿû3ÿúþÿÿíÿ¿€2ÿùýÿþÿÿü$2ÿÿýÿþï3ÿúüÿÿóO3ÿüôÿÿ|#ÿÿþÿýêã3ÿüå~\.ÿ÷öìÝÙÖÒ÷¦ *ÿòüÿÿëóÝÉØêüþè+*ÿóúÿÿ÷÷d$CW}�ŸC(ÿøñÔóæãðwý 'ÿøéìjÚÿø²)ÿû�¢ûÃ1ÿþüíÿöýÕÔÔê§yÓa#ÿÿýÿý“XÿòýþþýÐ…âÿëÿÿåÿ¿€ÿHÿúòÿ‘<úÿþû$ÿ÷÷€ÿåßÿ� ÿýþïóÿóÛÔÔêÿŸçÿÿïÿÿýÿùû0lÿÿ| ÿóüÿüŒ€¾ÿæÿÿåÿ¿€úHÛã ÿü´÷ÿþû$þX\ ÿ÷ý€ÿåßÿ�(ýÿùêÿÿ÷ÔöêÿŸçÿÿï(ÿÿý ÿùû0lÿÿ|)ÿøÏÿæÿÿðÿ¿€úHÛã)ü½£z÷ÿþû$þX\*÷û0€ÿåßÿ�6÷€Hçÿÿï:ülÿÿ|;üÛã<þX\ùÿ0>ÿH¿ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @    Ð:h�4N'¡ucatbgrnd2.pngÿ!?€ "     %$ÿÿÿÿ#ÿÿÿÿO%¡uOaÛÛ'Û3Û?¡uQ¡Q±QÁQÑQáQñRRR!R1Z!\!\1\A\Q\aj{}Š1˜´˜Ä˜Ô£¯i´ƒ½ÔÎdϲÏÂÚLâ|âŒÿ•t§·Ç{,6ôGÓPŸP¯P¿PÏ[¨[¸oKqvq†q–qä~‡#‘É“•3•Ñ—&¡É­¸Ÿ½Š¿%¿5¿EÇÖÌÙFÛ @@@@uü&:79øLM[t“ƒI6ö�Y`šáüþÄV+5öR^€çûÿÿá…[4û5Qk¹÷ÿü÷¦tY3ü*\‰êÿý·��3ü*n¾úÿýÛ‡”3ýA�ëÿý÷’€2üoÀúÿüþ¨q1ýQ”íÿüÝx//ü;sÅûÿûú–]-ü,[–ï ÿüÌlF ,ûKpÈû ÿüøƒ]"+û 9P~æ ÿý·e=+û#7Tšú ÿýîzQ*ú (0]¼þ ÿþ¢k)ü*^ä ÿþׄ(ù3|ú ÿþü’(ú(V¯ÿÿŸ'ù=!>zçÿÿ¯&ø"C% +[£ýÿÿÂ%øCC74ƒ×ÿÿ×$÷#PeJC9T¦õÿÿð#öDv…aCLsÏûÿÿû"ò �öÿüõ¡¡óÿuü&:79øLM[t“ƒI6ö�Y`šáüþÄV+5öR^€çûÿÿá…[4û5Qk¹÷ÿü÷¦tY3ü*\‰êÿý·��3ü*n¾úÿýÛ‡”3ýA�ëÿý÷’€2üoÀúÿüþ¨q1ýQ”íÿüÝx//ü;sÅûÿûú–]-ü,[–ï ÿüÌlF ,ûKpÈû ÿüøƒ]"+û 9P~æ ÿý·e=+û#7Tšú ÿýîzQ*ú (0]¼þ ÿþ¢k)ü*^ä ÿþׄ(ù3|ú ÿþü’(ú(V¯ÿÿŸ'ù=!>zçÿÿ¯&ø"C% +[£ýÿÿÂ%øCC74ƒ×ÿÿ×$÷#PeJC9T¦õÿÿð#öDv…aCLsÏûÿÿû"ò �öÿüõ¡¡óÿuü&:79øLM[t“ƒI6ö�Y`šáüþÄV+5öR^€çûÿÿá…[4û5Qk¹÷ÿü÷¦tY3ü*\‰êÿý·��3ü*n¾úÿýÛ‡”3ýA�ëÿý÷’€2üoÀúÿüþ¨q1ýQ”íÿüÝx//ü;sÅûÿûú–]-ü,[–ï ÿüÌlF ,ûKpÈû ÿüøƒ]"+û 9P~æ ÿý·e=+û#7Tšú ÿýîzQ*ú (0]¼þ ÿþ¢k)ü*^ä ÿþׄ(ù3|ú ÿþü’(ú(V¯ÿÿŸ'ù=!>zçÿÿ¯&ø"C% +[£ýÿÿÂ%øCC74ƒ×ÿÿ×$÷#PeJC9T¦õÿÿð#öDv…aCLsÏûÿÿû"ò �öÿüõ¡¡óÿõú 8ø -FII26ö Y¡¿ÁªŠv-4õuÕòùøóÖ±i3üUÜýÿüþë™3ýÂýÿýÚ-1ýŠêÿýýi0ü\¾úÿþ°!/üK¥Ë ÿýê<-û>¥¼ã ÿýö^,ú/¤ÏÄþ ÿüù‘H*ú&˜ÍËö ÿýÃ~ *ûŽÇÓð ÿýù­J)ûƒ¿ÔñÿþÜŠ(û oºÒëÿþû¯'ûU±ÎßÿÿÑ&ú:¥ÊÕ÷ÿÿê&û‰¾âðÿÿ÷%û i©Úúÿÿý$ûL�¶èÿ$û.�­¼þÿ#ût¥¼øÿ"û^¢»Ùÿ"üAŸºÖÿ!û!˜ºÚýÿ û ‘¹Éýÿû…¸Óþÿû~¸ãþÿü €Éøÿý0žðÿú3`°ú ÿõ ,>P`o…¬Âû!ÿóÿ>ÿ>ÿ>ÿ;>ÿO>ÿk>þo =þp=þq=þr=þ„=þŒ,=ý”I <ü”Y+ ;ûvW+":úÊg29øÿÑsO<.7ÿùûá�V+(6 @ÿ>ÿ>ÿ>ÿ>ÿ;>ÿO>ÿk>þo =þp=þq=þr=þ„=þŒ,=ý”I <ü”Y+ ;ûvW+":úÊg29øÿÑsO<.7ÿùûá�V+(6 @ÿ>ÿ>ÿ>ÿ>ÿ;>ÿO>ÿk>þo =þp=þq=þr=þ„=þŒ,=ý”I <ü”Y+ ;ûvW+":úÊg29øÿÑsO<.7ÿùûá�V+(6 ÿ >ÿB>þz =þž9=ý«g <ý¢x.<ü±z\;üÓz�;üð€•+;ü÷‡­V;ûø‰»…:ûù“¼©8:úù£¼»9ùý·¼¼µ`8øÿßÿÁ³b7÷ÿþðßÖÚ¶6ÿùûìëÕ­ 5ÿùýì×ɬ�4ÿùþñÜÁ´Š$3ÿúýéÍLJ 2@@@@ö)9q„‚_- 4ó(&9b…�aW3(1ê)4APn�±¯¨”�jI>71-/@1 ü!B8@è32CL]j¨øýüúøóèÜʹŸ‹~pbB ú;U]FEöE?C:6Ga‰çüÿóþýûðãÕ´‹sa< ù@ez›ÂÖóLCCEWIDRe”ÙõþÿïþûòÚ´‰`C,aµíýÿñFC1C``WGFVk‹ºâù ÿöüòÏ€jG.aÎùÿî'9*DalqhVIIV`lƒ¯âû ÿúýöñëñùÿì"/Flš˜’‡vaK:(#)6FpÌýÿê3^¾þüîÕµ�d8')DX|¼þÿþ­ûÿùþøå¾ƒXFCû73K©ûÿþ‡òÿóýõÕ�[IC6*4]˜õ ÿþhÚ ÿõúá�V8*+=Tî ÿý7±ý ÿöüëx@+*/-`É ÿý�÷ÿ÷ÉhG+&+^³ ÿþ\Þÿöøƒ`K/!IbÌÿý3­ýÿ÷½vpWCC71-/@1 ü!B8@è32CL]j¨øýüúøóèÜʹŸ‹~pbB ú;U]FEöE?C:6Ga‰çüÿóþýûðãÕ´‹sa< ù@ez›ÂÖóLCCEWIDRe”ÙõþÿïþûòÚ´‰`C,aµíýÿñFC1C``WGFVk‹ºâù ÿöüòÏ€jG.aÎùÿî'9*DalqhVIIV`lƒ¯âû ÿúýöñëñùÿì"/Flš˜’‡vaK:(#)6FpÌýÿê3^¾þüîÕµ�d8')DX|¼þÿþ­ûÿùþøå¾ƒXFCû73K©ûÿþ‡òÿóýõÕ�[IC6*4]˜õ ÿþhÚ ÿõúá�V8*+=Tî ÿý7±ý ÿöüëx@+*/-`É ÿý�÷ÿ÷ÉhG+&+^³ ÿþ\Þÿöøƒ`K/!IbÌÿý3­ýÿ÷½vpWCCG&þKïÿûþ¶R3U&ý,»üÿüÑV0T&ý�ùÿüóq6:&ýXñÿýßV<'þ4ºÿýþÉO'ýdõÿþýÁ'ý2Öÿÿþ(þ“ÿ(ý eîÿ(ýSÁÿ)þ.…ÿ*þjðÿ*þ<½ÿ*þ†ÿ*ýnúÿ+þSÒÿ+þ¥ÿ+ýVûÿ,þ=ôÿ,þÕÿ,ý¯üÿÿï-þ{õ ÿþã|-þ:× ÿýÒp:ö)9q„‚_- 4ó(&9b…�aW3(1ê)4APn�±¯¨”�jI>71-/@1 ü!B8@è32CL]j¨øýüúøóèÜʹŸ‹~pbB ú;U]FEöE?C:6Ga‰çüÿóþýûðãÕ´‹sa< ù@ez›ÂÖóLCCEWIDRe”ÙõþÿïþûòÚ´‰`C,aµíýÿñFC1C``WGFVk‹ºâù ÿöüòÏ€jG.aÎùÿî'9*DalqhVIIV`lƒ¯âû ÿúýöñëñùÿì"/Flš˜’‡vaK:(#)6FpÌýÿê3^¾þüîÕµ�d8')DX|¼þÿþ­ûÿùþøå¾ƒXFCû73K©ûÿþ‡òÿóýõÕ�[IC6*4]˜õ ÿþhÚ ÿõúá�V8*+=Tî ÿý7±ý ÿöüëx@+*/-`É ÿý�÷ÿ÷ÉhG+&+^³ ÿþ\Þÿöøƒ`K/!IbÌÿý3­ýÿ÷½vpWCCFOKB@?:3+*/?nëÿÿþÿ îCSNE?95$!"$*Jp’ƒH4>WÁþ-ÿî¡¿ÀÀ½�4('0jž‘M9Cbì-ÿím·¿À»`'%&*G¡À�>CD‚þ,ÿí3y´½¶h&%'-p·¾z8CCaô,ÿí3D|ª±•P/+>‚˜p=6Rlªþÿóüéų¦zUk³òúùùøëñчA3,Lw{jgbpc<1(Ax¨øÿäûИqZRk…z]FV¡×ñßÊ‹L71/²M")$4ú2'3‡ÍþÿÚÖƒj`bpƒ˜¥´ÆÅ¾ËßîÅ£S6337ÿÙxFCCA5,)7GxÓüÿÿþÿùäkgv“ÂìÿëþýüøîǽàùÿÿøµfJCDNmÂö ÿøù·Y:8PŽìÿüιÒïÿüùáýþ ÿõýÙ[! @ƒøÿûôññ÷ü(ÿôý±I) +`¾ÿÿõñóÚij¬¯ºÉÖåóøüýÿòþ´K$-G‚�mJ&8�ûÿèðÀ¡€g`SJE''/51&ÿùùà–§Þþÿñý�I@¥ùþð­K…· ÿïþýúãɯ™…xh[I/ ÿöê­|T:448OÈÿñ÷–KG¦üÿî�.€Õÿ÷þõáʹ´±¡‹ÿúó­hA)íJéÿÿùÊq 5�ñûéŠ%*’üÿùú”€Š°àüÿòÕb=1024248>R¡óÿòü»V)-b´ÓÍ€GC£üÿôljX>KPZlšÎîý ÿóþ·NCCE[nƒªÌáõÿóþÎX11PkwH3=�úÿñîÈš,7C<7?Na‹¾Ýö ÿöüžG77O™ÿýþ²^CøA610>^Çý ÿ÷óªsL8-*&!ï0>FOKB@?:3+*/?nëÿÿþÿ îCSNE?95$!"$*kîÿý¿~d`ûLCCT«ÿñù—jJ,%$*10D…Áùÿôù´�~skgdWg¯üÿðû�[BIm›¨¹¯´²to”ÌÿöýõãÝÚÏÚæöüÿðÉ`NÌË»ÃÈÈÍÏ·n…È ÿÿþ ÿïõxHƒâóÎm`·ÉÏÀo‹ç.ÿïÊSNÏùùŠ=46mÂοv�ü.ÿð«F^òüøe=73D¯Ì¤wÅ/ÿð‰Deðüõl=;3@¤œp™û.ÿðô–G[ÚóöÉfH:g|clí/ÿñ÷š`a–ÔÞãרhVad»ÿùûì×»¿áùÿòý¯g]\it\L72GxÃÿôýçÉ£���…‡šÈ÷ÿôãlG51&ÿùùà–§Þþÿñý�I0|¼Àµ‚8…· ÿïþýúãɯ™…xh[I/ ÿöê­|T:448OÈÿñ÷–K5}¾Á´j"€Õÿ÷þõáʹ´±¡‹ÿúó­hA)íJéÿÿùÊq (v¶½°h’üÿùú”€Š°àüÿòÕb=1024248>R¡óÿòü»V"JˆŸ›`54£üÿôljX>KPZlšÎîý ÿóþ·NCCE[nƒªÌáõÿóþÎP%%7O™ÿýþ²^CøA610>^Çý ÿ÷óªsL8-*&!ï0>FOKB@?:3+*/?nëÿÿþÿ îCSNE?95$!"$*e¢¡”›ŸŸ£¤‘W…È ÿÿþ ÿïõo:h³Á£VLe‘ ¤˜X‹ç.ÿïÊG>¤ÆÆm0)*Vš£˜]�ü.ÿð«;JÀÈÅP0+(6‹¢‚jÅ/ÿð‰9P¿ÈÃU0.(2‚|]™û.ÿðô–?H­ÁàQ9.QbUlí/ÿñ÷š]Rw¨°´«…RG[d»ÿùûì×»¿áùÿòý¯gYOT\I<--GxÃÿôýçÉ£���…‡šÈ÷ÿôãlG:+#?téÿó¶‹bSJEDGOXj–ßÿõûÀkegehv§çþÿñZ8+"'/FnÈýÿüúùúþÿð7'N�¯´¥“oQ4$=[³ü/ÿïJ”ÅÉÈÈ¿£§”yE5;S³ü.ÿî�ÇÊÊÉÀ|ANu™ŠL7?WÁþ-ÿî©ÉÊÊÇ”6*)2p¦˜Q;Cbì-ÿísÁÉÊÅe)'(,KªÊ˜ACD‚þ,ÿí6€¾Ç¿m(')/uÁÈ€;CCaô,ÿí6Hƒ³ºœT2.A‰ v@8Rlªþÿóüéų¦zUk³òúùùøëñчD6.P}�pmguh?4*Bx¨øÿäûИqZRk…z]FV¡×ñßÊ‹L71/³O#+&6ú5)4‡ÍþÿÚÖƒj`bpƒ˜¥´ÆÅ¾ËßîÅ£S6337ÿÙxFCCA5-*9IyÓüÿÿþÿùäkgv“ÂìÿëþýüøîǽàùÿÿøµfJCDNmÂö ÿøù·Y:8PŽìÿüιÒïÿüùáýþ ÿõýÙ[ @ƒøÿûôññ÷ü(ÿôý±I!#`¾ÿÿõñóÚij¬¯ºÉÖåóøüýÿòþ´K"5blR80�ûÿèðÀ¡€g`SJE''/51&ÿùùà–§Þþÿñý�I0|¼Àµ‚8…· ÿïþýúãɯ™…xh[I/ ÿöê­|T:448OÈÿñ÷–K5}¾Á´j"€Õÿ÷þõáʹ´±¡‹ÿúó­hA)íJéÿÿùÊq (v¶½°h’üÿùú”€Š°àüÿòÕb=1024248>R¡óÿòü»V"JˆŸ›`54£üÿôljX>KPZlšÎîý ÿóþ·NCCE[nƒªÌáõÿóþÎP%%7O™ÿýþ²^CøA610>^Çý ÿ÷óªsL8-*&! ô$c�™·ÌÚëùþþ'ÿ÷7kŠÉëýþ-ÿõ ?ez¼óþ0ÿô3Y��—�¢ªæ3ÿõ—§°¸ÂÉÖÖæòþ4ÿø°Ñâóùüýþ7ÿÿþ¢ÿþûõ=ÿþüøPÿÿþÄÿÿþ>ÿþýþ>ÿþãþ>ÿÿß>ÿþþå>ÿþûò>ÿÿý ÿÿùýîu15ÿùþ…4 4ÿú�= 4ÿþÃ@þ3ÿùëL*,"3ÿøüW"2=C=2ÿþa3Cþ@2ÿù~DCA(2ÿø¾JA/1ÿøûT4*)01ÿù�2*8JB#1ÿùèK@`RC+1ÿùýŒZ`OC#1ÿúön`L<#1ÿúÊeO4*0ÿúü«Z6*0 ÿúú”J**/ ÿûø…9*/ ÿúòv5" - ÿùþèn6(, ÿùýÚeA2+ ÿúûÅaE@+ÿúõ–]D5*ÿúüȃVB*ÿúñ»pJ1)ÿúö¡[<(ÿúýägL!(ÿúõz[8!'ÿúþî�N8&'ÿúþïlCC&ÿûø�E:)&ÿúûšJ1'%ÿû¨O1* %ÿûÄV3' %ÿûßb6# %ÿûøs@%ÿü N%ÿüéh% ÿýû׿ÿü³O*$ ÿûþß®³üÿüü‘A$ÿûï¶‹”ô ÿüìo #ÿúýË|dwî ÿüÏ\"ÿúÑyPI{ö ÿûþ°S!ÿøþÃmODK”ýÿüô–D !÷þí¢aKCCXÃÿüà~' ø^LDCCN‰ñÿýÆ` ú'#5R~Øÿýè‘9 ûEb´ôþÿüú£EþûýÿùüöÛ�R"ÿõõÒ¥yR3!#*ÿô±x?0*!  *ÿùþùåȲÈðÿôþ‰=(&'' òËÏÏǸ¢Œ~uv…’ÊûÿúØvÿ ô /@CCFeªéÿöýä˜G, ö+RuÁö ÿøûÐsL+ùz†®ËâòüÿùþÞxR6 ÿú¦Z:ÿúœT%ÿùø€6ÿù±Sÿøñ‹2!ÿùËa-(ü¯åùþÿüþøîýÿù™A'ò$*3@S_m}�£¶¹¶ÙÿøÃV% ÿùýîu15ÿùþ…4 4ÿú�= 4ÿþÃ@þ3ÿùëL*,"3ÿøüW"2=C=2ÿþa3Cþ@2ÿù~DCA(2ÿø¾JA/1ÿøûT4*)01ÿù�2*8JB#1ÿùèK@`RC+1ÿùýŒZ`OC#1ÿúön`L<#1ÿúÊeO4*0ÿúü«Z6*0 ÿúú”J**/ ÿûø…9*/ ÿúòv5" - ÿùþèn6(, ÿùýÚeA2+ ÿúûÅaE@+ÿúõ–]D5*ÿúüȃVB*ÿúñ»pJ1)ÿúö¡[<(ÿúýägL!(ÿúõz[8!'ÿúþî�N8&'ÿúþïlCC&ÿûø�E:)&ÿúûšJ1'%ÿû¨O1* %ÿûÄV3' %ÿûßb6# %ÿûøs@%ÿü N%ÿüéh% ÿýû׿ÿü³O*$ ÿûþß®³üÿüü‘A$ÿûï¶‹”ô ÿüìo #ÿúýË|dwî ÿüÏ\"ÿúÑyPI{ö ÿûþ°S!ÿøþÃmODK”ýÿüô–D !÷þí¢aKCCXÃÿüà~' ø^LDCCN‰ñÿýÆ` ú'#5R~Øÿýè‘9 ûEb´ôþÿüú£EþûýÿùüöÛ�R"ÿõõÒ¥yR3!#*ÿô±x?0*!  *ÿùþùåȲÈðÿôþ‰=(&'' òËÏÏǸ¢Œ~uv…’ÊûÿúØvÿ ô /@CCFeªéÿöýä˜G, ö+RuÁö ÿøûÐsL+ùz†®ËâòüÿùþÞxR6 ÿú¦Z:ÿúœT%ÿùø€6ÿù±Sÿøñ‹2!ÿùËa-(ü¯åùþÿüþøîýÿù™A'ò$*3@S_m}�£¶¹¶ÙÿøÃV% ÿùýîu15ÿùþ…4 4ÿú�= 4ÿþÃ@þ3ÿùëL*,"3ÿøüW"2=C=2ÿþa3Cþ@2ÿù~DCA(2ÿø¾JA/1ÿøûT4*)01ÿù�2*8JB#1ÿùèK@`RC+1ÿùýŒZ`OC#1ÿúön`L<#1ÿúÊeO4*0ÿúü«Z6*0 ÿúú”J**/ ÿûø…9*/ ÿúòv5" - ÿùþèn6(, ÿùýÚeA2+ ÿúûÅaE@+ÿúõ–]D5*ÿúüȃVB*ÿúñ»pJ1)ÿúö¡[<(ÿúýägL!(ÿúõz[8!'ÿúþî�N8&'ÿúþïlCC&ÿûø�E:)&ÿúûšJ1'%ÿû¨O1* %ÿûÄV3' %ÿûßb6# %ÿûøs@%ÿü N%ÿüéh% ÿýû׿ÿü³O*$ ÿûþß®³üÿüü‘A$ÿûï¶‹”ô ÿüìo #ÿúýË|dwî ÿüÏ\"ÿúÑyPI{ö ÿûþ°S!ÿøþÃmODK”ýÿüô–D !÷þí¢aKCCXÃÿüà~' ø^LDCCN‰ñÿýÆ` ú'#5R~Øÿýè‘9 ûEb´ôþÿüú£EþûýÿùüöÛ�R"ÿõõÒ¥yR3!#*ÿô±x?0*!  *ÿùþùåȲÈðÿôþ‰=(&'' òËÏÏǸ¢Œ~uv…’ÊûÿúØvÿ ô /@CCFeªéÿöýä˜G, ö+RuÁö ÿøûÐsL+ùz†®ËâòüÿùþÞxR6 ÿú¦Z:ÿúœT%ÿùø€6ÿù±Sÿøñ‹2!ÿùËa-(ü¯åùþÿüþøîýÿù™A'ò$*3@S_m}�£¶¹¶ÙÿøÃV% ÿûïØËT1ÿûþñÛ­1 ÿýêÇ;1 ÿüéǃ0 ÿü꼫0 ÿûøÀ¹^/ ÿûþÒ¼˜ / ÿü÷׳8/ ÿûýçÉz. ÿûþàʤ. ÿûþÚ¼¶U. ÿû引 - ÿûí䨵C- ÿúúëëÓ—, ÿëýçÀL, ÿúöêëÚ¡+ÿúçëãÈ[*ÿú÷ëéÒ¨!*ÿúðéÕÂo)ÿþÿûò×̯')ÿþÿúþè̼x (þÿþÿûûȼ­/(ÿûî¾»x 'ÿûþÝ¿©2'ÿûúÖº| &ÿÿþÿûøÌ¨;%ÿÿþÿüóÀŠ%ÿþþýÿüׯN$ÿü÷¼˜$ÿü͵`#ÿüí¼§#ÿûüκr"ÿüäŤ"ÿüôÒ±"ÿü÷ÞÁ("ÿüúáÌR"ÿûúÞÔ—!ÿûúÖÕ» !ÿûú×ÕÌn!ÿúüÖÕÔ³& ÿùýßÙÕΔÿúöêæÙÂbÿúøîìÛ¯-ÿúñëëØ• ÿùýíéêÏzÿùïÑæé¾QÿøýÍÊêÞ«ÿùØÏçåÐeÿøìÚæá¶o ÿøóÚá¿—gÿùðʼ•v7 ÿùò¼«zRÿúú׳|?ÿûûä­mÿúýüîÃrÿúûýé¸R!ÿûúÖ§<"ÿûðÍ•4#ÿûöÉ•4#ÿúþäÊ¥Z$ÿúõ×ÓÁ†6%ÿûìÕÍŸ~%ÿûþÖ­�H&ÿûûÀ‡K $ÿ 8ö/7>;>:3!2ñ 1Êêóùø÷ðÜȰ‘lB.ý¡û ÿûýõÜ•+þpüÿýø�(ý³ýÿýÀG&ýÐýÿüÀY3$ý�ùÿü½bB/#ûfšáýÿú¿jFA9 ôH`jph[gx~‚ß ÿøþÃnLCIZ=ñ Fj`VICLlpjITºü ÿ÷ûÊz]EG__4ðI�–lICG…»Õ²xFX¯øÿöýߣ’ƒvj`Hï>¯ÚɃRCkÐÿÿþÆ^EjÏ ÿ÷úóîçà¾xW*ö ÇûÿûŸ^S�ùÿûâsGRÿûþÏsKö”ùÿÿý¶``€üÿûæpKP¹ÿÿþÿüú¦a3þ"æÿûÃb`qðÿûÊSFƒôÿøóÚ€k€±ÛùÿüÒsGýeýÿôÍVObÆýþç£OlÙÿíüØx‰g`j…‡’Äòüÿÿð‰KýN¾ÿÞênRFkäß¶‡lÃþÿÿ÷¼lZ‡¦Âͱ‰dP­òÿÿýªL*ü&˜ôÿìý�VCDj¯š�±ûÿÿò¯nKPÁûÿûÒlD^ÕÿüÈ`>ý|ÚÿîèhGCDZv®úÿÿü¦kSC^öÿü�SJÁÿüáu\ýR¶ÿùßqI7@­ýÿùsa`=AuúÿüŽ`a½ÿûó‡_5ü3•ø ÿýóÐìÿù@S^-3šüÿûüx`kÉÿûýªaB ý'yâÿù,/KAC¯úÿúû®e`jìÿüÇZ> üX±ýÿð<*,?D¯ÓôýÙ’g``góÿûÙG+ý#‡ôÿð¿>20F�Ÿ�vRKLJL`óÿûæZ  ü _Êþÿðó‹PFOgaE,!9K[bôÿûõ…  ü E�ùÿñËn``]K-*)@Tãÿûý¹" ü'eÚÿñæ~`V@:7,*)/?L`½ÿýÆx üI�ûÿð¾aF0**#6CT`�üÿýʱ$ ü Keéÿïåk=*/7=CJLGDCDX‡ñÿý¿¨ ü$V£þÿï‰R5L}Åëùûùæ¦jGJ|Óÿýý·v ü3jôÿû’jg�õÿõË|Rj­íÿý×e ýLÅÿüb`zÞÿ÷‘n`€²èÅo! ü @ŠýÿúýDN€Øþÿ÷¤jZaz‡}' ü =qôÿöýOGZ�´¸ºÈøÿ÷ù�WCIR[( ü6fàÿëþo]N`[L!)sÂæÜ�eNCC; $ÿ 8ö/7>;>:3!2ñ 1Êêóùø÷ðÜȰ‘lB.ý¡û ÿûýõÜ•+þpüÿýø�(ý³ýÿýÀG&ýÐýÿüÀY3$ý�ùÿü½bB/#ûfšáýÿú¿jFA9 ôH`jph[gx~‚ß ÿøþÃnLCIZ=ñ Fj`VIBIfjfHTºü ÿ÷ûÊz]EG__4ðI�–lIA@j�¡�lDX¯øÿöýߣ’ƒvj`Hï>¯ÚɃQ;S�ÁÁÀšSDjÏ ÿ÷úóîçà¾xW*ö ÇûÿûŸVBa¼Áû«\ARÿûþÏsKö”ùÿÿý¶TH`¾Áû®TAP¹ÿÿþÿüú¦a3þ"æÿûÃVHUµÁû˜>=ƒôÿøóÚ€k€±ÛùÿüÒsGýeýÿôÍL’½ÁûžS?^ÕÿüÈ`>ý|ÚÿîèhC96Hd¥úÿÿü¦kS6GºÁüaCJÁÿüáu\ýR¶ÿùßqI7@­ýÿùsa`=1X½ÁükHa½ÿûó‡_5ü3•ø ÿýóÐìÿù@S^-&t¾Áû¾ZHkÉÿûýªaB ý'yâÿù,/KA6„½Áú½ƒLNjìÿüÇZ> üX±ýÿð<*,??‡Ÿ¸¿¤nMJYgóÿûÙG+ý#‡ôÿð¿>20F‚}jY>8;>:3!2ñ 1Êêóùø÷ðÜȰ‘lB.ý¡û ÿûýõÜ•+þpüÿýø�(ý³ýÿýÀG&ýÐýÿüÀY3$ý�ùÿü½bB/#ûfšáýÿú¿jFA9 ôH`jph[gx~‚ß ÿøþÃnLCIZ=ñ Fj`VIBIfjfHTºü ÿ÷ûÊz]EG__4ðI�–lIA@j�¡�lDX¯øÿöýߣ’ƒvj`Hï>¯ÚɃQ;S�ÁÁÀšSDjÏ ÿ÷úóîçà¾xW*ö ÇûÿûŸVBa¼Áû«\ARÿûþÏsKö”ùÿÿý¶TH`¾Áû®TAP¹ÿÿþÿüú¦a3þ"æÿûÃVHUµÁû˜>=ƒôÿøóÚ€k€±ÛùÿüÒsGýeýÿôÍL’½ÁûžS?^ÕÿüÈ`>ý|ÚÿîèhC96Hd¥úÿÿü¦kS6GºÁüaCJÁÿüáu\ýR¶ÿùßqI7@­ýÿùsa`=1X½ÁükHa½ÿûó‡_5ü3•ø ÿýóÐìÿù@S^-&t¾Áû¾ZHkÉÿûýªaB ý'yâÿù,/KA6„½Áú½ƒLNjìÿüÇZ> üX±ýÿð<*,??‡Ÿ¸¿¤nMJYgóÿûÙG+ý#‡ôÿð¿>20F‚}jY>8ü |Éü.ÿüù¯Uü `¥ë.ÿûþãŸYûC—¾ü.ÿûöÅ«6ü5“§ë.ÿûùÍ¿˜ ü0�§Ò.ÿúöпµvû,Œ²ßþ-ÿúõο¼Ÿ@@@@ _ø   4ù þ.î !'-/+# ,ë %Ls£ÜðøòæÍlL1 )ù6a�äüÿøþöš[&&û*d�óÿüú�O $üJ’í ÿüô~'!ü ü-zÜ ÿøÌaë4A9 Z´úÿûÓtR8{òÿûþ¯SÿèO”ÎͬE vàÿá˜X76A=WËüÿûþ¬Kÿý tîÿìñl+–üæ¡g5L–ÉÌ~Y»øÿü¹Pÿþfôÿëù…>­ñ»€F?¥õúýñ…!ÆûÿüÑ`ÿý+×ýÿêú”(K±×’n3Wâþõí÷ÀQ†Ýþÿüõ˜&ýlõÿêþ�4P¯¦…`2pðþìßú¶2U¾úÿüýÝD ý�üÿë·F2S‰……V2ˆ÷þóóõ™6�òÿü÷“"þµÿûÙZ*­ñ»€F3|¹½¿¶e!ÆûÿüÑ`ÿý+×ýÿêú”(K±×’n,A«À¹³º‘F†Ýþÿüõ˜&ýlõÿêþ�4P¯¦…`%TµÀ²¨½‰%U¾úÿüýÝD ý�üÿë·F2S‰……V%fºÀ··¹s6�òÿü÷“"þµÿûÙZ*­ñ»€F3|¹½¿¶e!ÆûÿüÑ`ÿý+×ýÿêú”(K±×’n,A«À¹³º‘F†Ýþÿüõ˜&ýlõÿêþ�4P¯¦…`%TµÀ²¨½‰%U¾úÿüýÝD ý�üÿë·F2S‰……V%fºÀ··¹s6�òÿü÷“"þµÿûÙZ*[WJ<*4hÒþÿ5ö+?@+>‰ñÿÿý6÷BPd´ûÿÿùË6÷5£Éþÿþé”Z7ø…öôÁ‚TF=8ù8@<852,9ú )*;ü$;þ;ÿ;ü#D;ü!SÊ;ü5Àÿ<ý�þÿ<ýõÿÿ;ÿ¨ÿ;ÿ÷ÿ:ÿmÿ:ÿÝÿ:ÿüÿ:ÿ9ÿsÿ9ÿÛÿ9ÿõÿ9ÿûÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ8ÿÊÿ8ÿïÿý8ù÷ÿÿùÓ±À8ùÈýë´’……8ùd�~ng`Z8ù$! -<8ù :8ÿþ]8ý5Æ7ø Œü7ø )!Nõÿ6ö*,Âÿÿ5ò�ùÿÿ0ù ‹óÿù -ù ‰ñÿôzW@,'ù ‰öÿõýòÛÀ‹bI9/% "ù:&$“úÿñü÷óéѦv]I:/(#ùD94žýÿòþûùÓškTIA6* ÷)C°þÿ-ý ‘ü ÿûûºhC.þAÞÿúä�^DCK.ýdöÿøø¯pG9FpÀ/ð%yðüûûñ°ub`hv¿íþ0ñ.d�–‡e94Nn¯óýÿÿ0õ,EOGA4@z×üÿ0÷ %CCNnºõÿÿú1ú /Ga¤õÿþéx1û6lÎüÿüý½gF2ý'¨üÿúþè‹XEK3þ‰ýÿùò¨g[WJ<*4hÒþÿ5ö+?@+>‰ñÿÿý6÷BPd´ûÿÿùË6÷5£Éþÿþé”Z7ø…öôÁ‚TF=8ù8@<852,9ú )*;ü$;þ;ÿ;ü#D;ü!SÊ;ü5Àÿ<ý�þÿ<ýõÿÿ;ÿ¨ÿ;ÿ÷ÿ:ÿmÿ:ÿÝÿ:ÿüÿ:ÿ9ÿsÿ9ÿÛÿ9ÿõÿ9ÿûÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ8ÿÊÿ8ÿïÿý8ù÷ÿÿùÓ±À8ùÈýë´’……8ùd�~ng`Z8ù$! -<8ù :8ÿþ]8ý5Æ7ø Œü7ø )!Nõÿ6ö*,Âÿÿ5ò�ùÿÿ0ù ‹óÿù -ù ‰ñÿôzW@,'ù ‰öÿõýòÛÀ‹bI9/% "ù:&$“úÿñü÷óéѦv]I:/(#ùD94žýÿòþûùÓškTIA6* ÷)C°þÿ-ý ‘ü ÿûûºhC.þAÞÿúä�^DCK.ýdöÿøø¯pG9FpÀ/ð%yðüûûñ°ub`hv¿íþ0ñ.d�–‡e94Nn¯óýÿÿ0õ,EOGA4@z×üÿ0÷ %CCNnºõÿÿú1ú /Ga¤õÿþéx1û6lÎüÿüý½gF2ý'¨üÿúþè‹XEK3þ‰ýÿùò¨g[WJ<*4hÒþÿ5ö+?@+>‰ñÿÿý6÷BPd´ûÿÿùË6÷5£Éþÿþé”Z7ø…öôÁ‚TF=8ù8@<852,9ú )*;ü$;þ;ÿ;ü#D;ü!SÊ;ü5Àÿ<ý�þÿ<ýõÿÿ;ÿ¨ÿ;ÿ÷ÿ:ÿmÿ:ÿÝÿ:ÿüÿ:ÿ9ÿsÿ9ÿÛÿ9ÿõÿ9ÿûÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ8ÿÊÿ8ÿïÿý8ù÷ÿÿùÓ±À8ùÈýë´’……8ùd�~ng`Z8ù$! -<8ù :8ÿþ]8ý5Æ7ø Œü7ø )!Nõÿ6ö*,Âÿÿ5ò�ùÿÿ0ù ‹óÿù -ù ‰ñÿôzW@,'ù ‰öÿõýòÛÀ‹bI9/% "ù:&$“úÿñü÷óéѦv]I:/(#ùD94žýÿòþûùÓškTIA6* ÷)C°þÿ'øŒØþþîáýÿ(ùiËóñëåøÿ(ø<¸èëëîéþÿ(ø ›Ýëùþìôÿ)ø]¿éúþúàþ ÿ)ø,ŠÜìûþöñ ÿ)ø6‘Ïéìùþù ÿ'õ …ÃÕçëëöþý ÿ$òJ“¯ÆÕÕëõùúüð ÿ í'p§ÇÛáɼ¾º´Íåñüþñé ÿ넬Íßêëß¹©~C?b¿åõþùØð ÿë§ËãëäΚJŒÔòþþê¼ ÿ øj«´¥f÷1µêûþ÷Ðå ÿ"÷�ÊìþþÞÕúÿ-ö¿ÞùþíÕÆýÿ,õ t¼ÞøþôÔ«±ÿ+ó +g»ßøþøâ°˜áÿ)ð!uoRI\­ÝøþøëßÖÌøÿ'톻ʨ^9*tÂïýóëòøùùûÿ%í#šÊÕÏ~ “Ëäëìþþûþÿ%ùg´ÕÇ€ õ%¥ÊÝêóôìýþþÿ%û Iov#õ�¹ÇÖçëòûþþÿ&þø“±»¼Æäûþüûþÿÿ/ðV—žŽi|Êîþþöíöÿÿ-î4ezŠR1¼Øñóë×Íÿÿ*ëoŸ¥Š`;³Ûæë屟ÿÿ)øOxš¡”8ýRºëû׃Äÿÿ)ù•§žl øžÃëëé¶šÿ)û)8 øp¼ÇõóÞ�Áÿ3ø³ÓÕùøÏpÿ3ø-Ïëëúöº‰ÿ3øtâëëóéžÀÿ2ø¤æÞæëßvÿ2ø¶æØèëÖlÿ2ø(Àêêëëʼnÿ2þLÇëþ³‚ÿ2øhÎëëìòž�ÿ2þxÕëýð¸ÿ2ø|Ûùîëå^àÿ2ø�éþûëãOÐÿ2øƒîþúóà>Àÿ1÷‡íùóöÜ,Ûÿ1÷‡ÞíüöÖ"îÿ1÷‡ÝøþòÎÞÿ1÷‡Þþüëà Ïÿ1÷‰ãþòë¹'èÿ1÷…ßõëë°Cöÿ1÷ƒÜëèë­Aùÿ1ø~Ûëäë°Pÿ1øtÚëà뺼ÿ2ùeÙéÞëÜôÿ2ùTÖåÞëëúÿ2ùGÔâÞëíþÿ2ú7ÍÞÞïüÿÿ 1ù.ÅÛßöøþÿû¼¡o5 -ú,¼Òàëñÿ÷ëéçâÜΨV)ú)¼Àáïýÿôúìãèëõõèʲ”1&û*¼¼äû ÿòþ÷ðîüþôâÒÆ¬z9"üQ¼¿ìÿóýþþýúöñÛÆ°{(ûŽÃáýÿ÷þûëãÜ˵lü†ÇëÿöýöìëåÛÜ<ü„¡óÿ÷óçéèÞÆ¨g ü1�¨ûÿõþèÞÜÙÉÁ»‚üZ¼ñÿü[¸÷ÿöñ£J)!!)2P©ÿñüÒ‰Z<31>C2+3F›ëÿ÷è™bA2%xÇøÿõú·S4*/8Gd˜êÿðõ°šyhbT!#*-AK^�ïÿüÕ€Síÿõþép@/+9^–Ò÷ÿóÓ‹]CE^L-&L‹üÿÿýÿöü½]8,3T”Ñöÿõð–RA59CER‚ñ"ÿöô�[DEX‹Ëóþÿøõ‹4*7^Šå"ÿöþ×sXFSx´íýÿûüëäÝðÿýû×÷%ÿíœE9Gg–âýÿÿùÞµzE06kÒ,ÿíIEVzÀûÿÿëʘX9Z¾,ÿíGb˜éÿÿýÕ�S3*(C³ü,ÿîpÂûÿÿõ³]!*+29SÌùþ-ÿñèÿÿþæ–6  4J‰íú2ÿõüÒ�: K�óü3ÿöóÃqG)$E�ëý4ÿ÷å¯bECGhÕþ6ÿù€KCCR‰ò8ÿú63Fa³û9ÿü*6bÌ;ÿü*@´ý;ÿý*PÑ<ÿý$jà<ÿý"…ô<ÿþ>Î=ÿÿÍ?ÿÿù>ÿÿ¾>ÿÿm>ÿÿn>ÿÿ®>ÿÿöÿÿü[¸÷ÿöñ£J)!!)2P©ÿñüÒ‰Z<31>C2+3F›ëÿ÷è™bA2%xÇøÿõú·S4*/8Gd˜êÿðõ°šyhbT!#*-AK^�ïÿüÕ€Síÿõþép@/+9^–Ò÷ÿóÓ‹]CE^L-&L‹üÿÿýÿöü½]8,3T”Ñöÿõð–RA59CER‚ñ"ÿöô�[DEX‹Ëóþÿøõ‹4*7^Šå"ÿöþ×sXFSx´íýÿûüëäÝðÿýû×÷%ÿíœE9Gg–âýÿÿùÞµzE06kÒ,ÿíIEVzÀûÿÿëʘX9Z¾,ÿíGb˜éÿÿýÕ�S3*(C³ü,ÿîpÂûÿÿõ³]!*+29SÌùþ-ÿñèÿÿþæ–6  4J‰íú2ÿõüÒ�: K�óü3ÿöóÃqG)$E�ëý4ÿ÷å¯bECGhÕþ6ÿù€KCCR‰ò8ÿú63Fa³û9ÿü*6bÌ;ÿü*@´ý;ÿý*PÑ<ÿý$jà<ÿý"…ô<ÿþ>Î=ÿÿÍ?ÿÿù>ÿÿ¾>ÿÿm>ÿÿn>ÿÿ®>ÿÿöÿÿü[¸÷ÿöñ£J)!!)2P©ÿñüÒ‰Z<31>C2+3F›ëÿ÷è™bA2%xÇøÿõú·S4*/8Gd˜êÿðõ°šyhbT!#*-AK^�ïÿüÕ€Síÿõþép@/+9^–Ò÷ÿóÓ‹]CE^L-&L‹üÿÿýÿöü½]8,3T”Ñöÿõð–RA59CER‚ñ"ÿöô�[DEX‹Ëóþÿøõ‹4*7^Šå"ÿöþ×sXFSx´íýÿûüëäÝðÿýû×÷%ÿíœE9Gg–âýÿÿùÞµzE06kÒ,ÿíIEVzÀûÿÿëʘX9Z¾,ÿíGb˜éÿÿýÕ�S3*(C³ü,ÿîpÂûÿÿõ³]!*+29SÌùþ-ÿñèÿÿþæ–6  4J‰íú2ÿõüÒ�: K�óü3ÿöóÃqG)$E�ëý4ÿ÷å¯bECGhÕþ6ÿù€KCCR‰ò8ÿú63Fa³û9ÿü*6bÌ;ÿü*@´ý;ÿý*PÑ<ÿý$jà<ÿý"…ô<ÿþ>Î=ÿÿÍ?ÿÿù>ÿÿ¾>ÿÿm>ÿÿn>ÿÿ®>ÿÿöÿÿÿò! !(9CCEW‘ÿ÷Þk3%( ò9/**'&&%,>EW‰øÿöø«F*#2& ôÇpL7%?hŸìÿ÷û·-  ÿÿûþ÷ëôýÿøÎa1  þ'$ÿøèxF*ý1g%ÿùèp< ! ü(W�&ÿùÁO)! û'Bh³&ÿùû�1*+:) ú"Ql›ü'ÿùáO2**+ ú /S‰ßþ(ÿøý°[C/û:[ ÷+ÿùþ²^A( û 'NuÁ.ÿùú{N+!ú Dh¦õ0ÿó�O/  6b�à2ÿôŽI* R—Åõ2ÿõû~6*&(W¨à4ÿöØZ+*$!*@£ë3ÿûþò‡3&*ý,zã3ÿõûé’8#**+JÓ3ÿõè˜R8*2:EW‰øÿöø«F*#2& ôÇpL7%?hŸìÿ÷û·-  ÿÿûþ÷ëôýÿøÎa1  þ'$ÿøèxF*ý1g%ÿùèp< ! ü(W�&ÿùÁO)! û'Bh³&ÿùû�1*+:) ú"Ql›ü'ÿùáO2**+ ú /S‰ßþ(ÿøý°[C/û:[ ÷+ÿùþ²^A( û 'NuÁ.ÿùú{N+!ú Dh¦õ0ÿó�O/  6b�à2ÿôŽI* R—Åõ2ÿõû~6*&(W¨à4ÿöØZ+*$!*@£ë3ÿûþò‡3&*ý,zã3ÿõûé’8#**+JÓ3ÿõè˜R8*2:??EW‰øÿöø«F*#2& ôÇpL7%?hŸìÿ÷û·-  ÿÿûþ÷ëôýÿøÎa1  þ'$ÿøèxF*ý1g%ÿùèp< ! ü(W�&ÿùÁO)! û'Bh³&ÿùû�1*+:) ú"Ql›ü'ÿùáO2**+ ú /S‰ßþ(ÿøý°[C/û:[ ÷+ÿùþ²^A( û 'NuÁ.ÿùú{N+!ú Dh¦õ0ÿó�O/  6b�à2ÿôŽI* R—Åõ2ÿõû~6*&(W¨à4ÿöØZ+*$!*@£ë3ÿûþò‡3&*ý,zã3ÿõûé’8#**+JÓ3ÿõè˜R8*2:¥¾Þ*ÿüïËt úM¬ÀÚþ*ÿüýáºA ú g¼Àßþ,ÿýõÒtù�ÀÓÞþ.ÿýãŸ$ù;—¹Ììþ/ÿòýÙŽ%x¯¼Ãê2ÿôüæ³R28b¨ÁÈÑð4ÿö÷辡—ž¡¾Þø5ÿýþðؼýÔïý7ÿúøÙ¼Ìæû9ÿûý×Óðþ;ÿþî÷=ÿÿþ ËÿþýþAÿû$J¼ÿîúŸX`I70Oeh``NC2 ü3‡ÿöú›kE7))*4L`þQ ü/qúÿòû»T?-**3CX`]F ýKƒï"ÿõØaG@ENX`g>þ‡á$ÿ÷þ¾�|lZN= ÿÍ%ÿøí±|ND;"  ÿðýúçÙÎÄ»´²»ÈÓàéòúÿùö£X=B6! ÿñôpNK:APZsƒ…�­Íöÿùü•T0/5 ÿòý±^76Ra``au…ªÞÿùþšV70/  ÿôò¥Z2?T`lƒ´Òñ ÿúº]CH6!ÿ÷úÔ�TFg˜èú ÿúÒkFOR3ÿüþØëþ ÿúî|LJ^  ÿúþŸ]DVR ÿûÜk=CPÿûö�`)-ÿû¼a^ÿûòsM; ÿûþ±?$ ÿüî[!ÿüþ¢2 ÿýéVÿüþ¼?ÿýæ‰= ÿüý¶o! ÿüß”^ ÿüü´qE!ÿüÛ’Y"!ÿûú¶p@ !ÿûÖŸV+!ÿûö¹‹D"ÿüÒ¨}(#ÿûò¹”j#ÿüͶs4$ÿûî¶§O $ÿûþǯ€&$ÿÿûß–^:"ÿûó”b&"ÿ"ÿüŸdB"ý 2hÿüÖpD<"üKr�ÿûó–KC#!û'Y~®šÿûýÀbC8!ú&^…²©‰ÿúþÍvL@ ù&a‹µ«‰‡ÿû΋WB/ ø+d�µ«‡‡ÿúÑŸZ= ÷+x�µ¤`^zS ÿúÓ£^,ö;|‡©žsIdS ÿùþÕšX4õ+S‚‡‡ncbg ÿùö¬vK4ô@G]aRQRD ÿúè’[7(!÷1FF=53Aÿùþ΀N*"!÷B:0./C?ÿúø¬aE1"÷:1$%0BUÿúäu@9%#ö?D/GJÿùþ£T>#öA?5.6O_ÿúòq9$ö <4.3D_høóÚžH)%ö36.7Oakhù¥ %ö >/.<]\Pÿÿ'÷+E0.@]5÷?F;9LP ý%ø3(CFL]C "÷#$9FZ\ #ø1-,7GcJ "øþ‡á$ÿ÷þ¾�|lZN= ÿÍ%ÿøí±|ND;"  ÿðýúçÙÎÄ»´²»ÈÓàéòúÿùö£X=B6! ÿñôpNK:APZsƒ…�­Íöÿùü•T0/5 ÿòý±^76Ra``au…ªÞÿùþšV70/  ÿôò¥Z2?T`lƒ´Òñ ÿúº]CH6!ÿ÷úÔ�TFg˜èú ÿúÒkFOR3ÿüþØëþ ÿúî|LJ^  ÿúþŸ]DVR ÿûÜk=CPÿûö�`)-ÿû¼a^ÿûòsM; ÿûþ±?$ ÿüî[!ÿüþ¢2 ÿýéVÿüþ¼?ÿýæ‰= ÿüý¶o! ÿüß”^ ÿüü´qE!ÿüÛ’Y"!ÿûú¶p@ !ÿûÖŸV+!ÿûö¹‹D"ÿüÒ¨}(#ÿûò¹”j#ÿüͶs4$ÿûî¶§O $ÿûþǯ€&$ÿ/ÿûß–^:"/ÿûó”b&"/ÿGÿüŸdB"/ý6T�ÿüÖpD<"/üDjŠ ÿûó–KC#!/ûKu”¼ªÿûýÀbC8!/úKy™¿¸�ÿúþÍvL@ /ùK|žÁ¹�›ÿû΋WB/ /øN~¢Á¹››”ÿúÑŸZ= /÷N� Á³zy‘p ÿúÓ£^,/ö\’›¸¯‹h~p/ ÿùþÕšX4/õOp—››‡}|€/ ÿùö¬vK4/ô=`fx{onoc/ ÿúè’[7(!/÷See^VUa//ÿùþ΀N*"!/÷@b[RQRc_/ÿúø¬aE1"/÷=[SIIRaq/ÿúäu@9%#ö/D_dR@Bfh/ÿùþ£T>#ö/Aa_VQWlz/ÿúòq9$ö/E]VQUdz�/øóÚžH)%ö/VWQXl{„�/ù¥ %ö/6^RQ]xwm/ÿÿ'÷/OdRQ`x?/ þ)÷/D`e\[jm/ ù'+*!$ø/ULcejxc +ÿ./"÷/HHB[evx/ .þ-,/#ø/TPOYf}i ./"øA]RQci|/ ./ ø/OaQUeld ./ø/RZPXda/ þ-. /ø/WVIY[e/ û$J¼ÿîúŸX`D+$;M[``NC2 ü3‡ÿöú›kE7))*4L`þQ ü/qúÿòû»T?-**3CX`]F ýKƒï"ÿõØaG@ENX`g>þ‡á$ÿ÷þ¾�|lZN= ÿÍ%ÿøí±|ND;"  ÿðýúçÙÎÄ»´²»ÈÓàéòúÿùö£X=B6! ÿñôpNK:APZsƒ…�­Íöÿùü•T0/5 ÿòý±^76Ra``au…ªÞÿùþšV70/  ÿôò¥Z2?T`lƒ´Òñ ÿúº]CH6!ÿ÷úÔ�TFg˜èú ÿúÒkFOR3ÿüþØëþ ÿúî|LJ^  ÿúþŸ]DVR ÿûÜk=CPÿûö�`)-ÿû¼a^ÿûòsM; ÿûþ±?$ ÿüî[!ÿüþ¢2 ÿýéVÿüþ¼?ÿýæ‰= ÿüý¶o! ÿüß”^ ÿüü´qE!ÿüÛ’Y"!ÿûú¶p@ !ÿûÖŸV+!ÿûö¹‹D"ÿüÒ¨}(#ÿûò¹”j#ÿüͶs4$ÿûî¶§O $ÿûþǯ€&$ÿrÿûß–^:"rÿûó”b&"rÿ‚ÿüŸdB"rýv‹ªÿüÖpD<"rü€š¯¾ÿûó–KC#!rû…¡¶ÑÆÿûýÀbC8!rú…¤ºÓμÿúþÍvL@ rù…¦½Õм»ÿû΋WB/ rø‡§¿Õл»·ÿúÑŸZ= r÷‡³¾ÕÌ¥¤´ž ÿúÓ£^,rö�µ»ÎȰ˜§žr ÿùþÕšX4rõ‡ž¸»»­§¦©r ÿùö¬vK4rû{“—£¥�ü•r ÿúè’[7(!r÷Š——’�Œ”rrÿùþ΀N*"!r÷}”�Љ‰•“rÿúø¬aE1"r÷{�Šƒ„Š”Ÿrÿúäu@9%#ör€’•‰~—™rÿùþ£T>#ör~”“�‰�œ¥rÿúòq9$ör�‘Œ‰‹•¥ªrøóÚžH)%örŒ�‰Žœ¥«©rù¥ %örv’‰‰‘££œrÿÿ'÷r‡–Љ“£}r þ )÷r€“—��šœr ù_ihP<*$ørŒ…•—𣕠iÿpr"÷rƒƒ�—¢£r pþolr#ør‹ˆˆŽ—§™ qr"ø~‘‰‰•™¦r qr ørˆ”‰‹—›– qrørŠ�ˆŽ•”r þoq rør��„Ž�–r ý�Ãæ-ÿøþêʼ¹�;þÏì-ÿøüË™Œ~tQþæþ,ÿùûÁ}gF- ÿþ,ÿúù«p:  ,ÿûøªi) +ÿûõ·[)ÿûþì¯B(ÿüþã†('ÿüþÕb&ÿüþÁE&ÿþÓ2%ÿþç'$ÿþôM#ÿýûœ"ÿýýÎ!ÿüýß_ ÿüþÏ“  ÿüé£Nÿüó¾‰ ÿüùÖ¯Kÿûþ翎 ÿüõÀ¤JÿüÔŸ…ÿüý·›Iÿüö¨…ÿýå©FÿüþØ�ÿýø´E ÿýã� ÿýùÎC!ÿüþÞ­!ÿýñ¼["ÿüýÈ�"ÿüþæ¡>#ÿüõ»�"ÿÿüΜD ý `ÿüúă ü(~²ÿûþéÉJû=“ÂôÿüöÌžû P¦ÞýÿüÚ³Qü Y°ïÿüò¯‡ü`²ôÿûüÌžTü`²òÿûþÞ©‘ü^«îÿÿïÿûòŸeüY�ìÿþçcÿûùìÅ� üb¡êÿüþîcÿûøîëÅZü VÃûÿüçpÿúôìæÉrüAªõÿúþÕ[ ÿùüèêèÈvý+”âÿüþÚF ÿøþöÚÞëÓXü…Éüÿýïb ÿø÷ãÕÕäß7üj¾ôÿýõ}ÿ÷þãÏÕÕÇ›*ýjÈõÿýû… ÿøüðçØÕÆjý$Íøÿþ«ÿøþûêÛÈŸ4ý¦øÿýþËÿùüóÜÅ¢X þ7àÿýÞDÿùþôáË«v!ý“ôÿüþ×SÿúüÞ—<"ýBÏýÿýëd ÿüþÔ#ý“öÿýþ¶ ÿýì«,$þ,×ÿþó\ ÿüîºJ"ý‹úÿýþ¼ ÿüýêµ#ý¸üÿþôV ÿûþùÜv"þZÜÿýþÍ ÿþü÷×� ý öÿþú} ÿþûùáÁ} ý»ûÿþÞ' ÿþùûîÞÅ�4þm×ÿþÒ A:9ü++8ùR`_F8ùrcc^>8ú…ecjl9û‡xw‘´:‡þ™Ò;ý…‹Í<þt=ÿ?A/:/9ü=OO;/8ùo{ze?//8ùŠ}}y_//8ú™~}ƒ„/9û›�ޤÀ:›þªÙ;ýšŸÖ<þ‹/=ÿ/?Ar:r9ü{‡‡zr8ù�¥¤–}rr8ù¯§§¤’rr8úº¨§«¬r9û»³²ÁÔ:»þÅå;ýº½ã<þ°r=ÿr?Aü:ú*m�z29ùŸ¼¼©18ùçûùטP8ÿü÷­78ÿýû” 9ÿþç2:ÿþìT;ýüÁ3<þ‡=ÿ?@@@@î9¨ÿÿó±G7®ýÿÿú—!0zþ&ÿîvëÿüîš6'­ýÿÿðr8�'ÿí R«þÿÿóª4kîÿþ÷‰*Iì'ÿý5péÿóûÉJ*�éôÔ^:~û'ÿü@©þÿõî…$^€V5[Ý(ÿü&KÙÿ÷äe' 0YÃ)ÿü,Xýÿùü³Ne„Ô*ÿý0b ÿþüö,ÿü3kù8ÿü7sî8ÿüÿ@î9¨ÿÿó±?*ƒ¿ÁÁ½r.zþ&ÿîvëÿüîš+‚¿ÁÁµV5�'ÿí R«þÿÿóª)P´ÁÀºgEì'ÿý5péÿóûÉ;l°¸ G+wû'ÿü@©þÿõîw G`A(MÖ(ÿü&KÙÿ÷äU $G¼)ÿü,Xýÿùü c;LpÎ*ÿý0b ÿþüö,ÿü3kù8ÿü7sî8ÿü�À¿»º´l2Ã"ÿ ñK±ñ¾E%M‘¤¥ˆ_0fú"ÿ ò–ñÊI5C&#WÓ#ÿ óxìé”1 0i½$ÿ ôAÔñá¨X>Hb‹Þ ÿøþûúðäÛìþÿ÷“ç÷ùøÑÞýÿñýúå´qK71"@|Ìþÿý µôÿëöÚ©f+ #WµóÿòSÙöÿùâµ’s^R=) ó  8a®ö ÿï3=BCA;#   ú3Gb¶ù ÿ0ú(9jÈüÿ2ú /sÙþÿ3ú D…ëÿ4ù *1OŸöÿ5ö!%6[¶ûÿÿ8ù%ÿ@î9¨ÿÿó±?*ƒ¿ÁÁ½r.zþ&ÿîvëÿüîš+‚¿ÁÁµV5�'ÿí R«þÿÿóª)P´ÁÀºgEì'ÿý5péÿóûÉ;l°¸ G+wû'ÿü@©þÿõîw G`A(MÖ(ÿü&KÙÿ÷äU $G¼)ÿü,Xýÿùü c;LpÎ*ÿý0b ÿþüö,ÿü3kù8ÿü7sî8ÿü�À¿»º´l2Ã"ÿ ñK±ñ¾E%M‘¤¥ˆ_0fú"ÿ ò–ñÊI5C&#WÓ#ÿ óxìé”1 0i½$ÿ ôAÔñá¨X>Hb‹Þ ÿøþûúðäÛìþÿ÷“ç÷ùøÑÞýÿñýúå´qK71"@|Ìþÿý µôÿëöÚ©f+ #WµóÿòSÙöÿùâµ’s^R=) ó  8a®ö ÿï3=BCA;#   ú3Gb¶ù ÿ0ú(9jÈüÿ2ú /sÙþÿ3ú D…ëÿ4ù *1OŸöÿ5ö!%6[¶ûÿÿ8ù%ÿ@úo¾×èÜë7ÿù”ÂÕÕÜ8ÿù¥ÈÞÜý8ÿú&±Ûãý9ÿû9µãñ:ÿüF¾ê;ÿüRÈï;ÿüUÍð;ÿüRÇò;ÿü!RÅð;ÿü$PÁì;ÿûKµßý:ÿû?¤¿ß:ÿù(”ÅÂéþ8ÿó ŽÔÕÚéùÿÿèÉþ2ÿü8¼ÕéëüæÞõÿÿüÿôfÆßðùýþúòâö1ÿûŠÂèúþþøä1ÿú~¹×òýþÿó1ÿö8˜Áæöþþýí0ÿø[¡Úïýþî0ÿøE�×òþñÚ/ÿø^°çïâÀï.ÿ÷DžØëß̰ì-ÿ ÷,ƒÇëÚÕ̵À ÿÿþ ÿøýùùþþõçøÿ ëb³×ÕÖÚÛÓÌÖëôûýüúñæé÷ÿþöù×ÛêëðëÞØóÿ ï;�Ëëùüþþýüûùöòíéëñ÷þûõêÕÕÚïóñîêäì ÿ öb­ÕÞëîêïùþþüïëòãÞÖÏÉÇÅÇÊÌÎßõýþýû÷þ ÿ ×^¶ÞëëåÙÞóýþýûùøõñãÕ󧘅gUA5P~•¬Êåóúþ÷ëý ÿ Ö2¼áæèéççë÷÷õòçÕµŽ^9 3d�´ÏÖÖ×íþÿï.s™¢¥¨¢™�\@+  ö >|¼çïïîøÿýö5”Óìöúýûûÿ2ó?�ÆÛçîííïýÿÿ4õa³Îíôóñëõÿ6÷•Ïé÷þþ÷ð7øo¬Öëõýô8ù5‘Âßêë:ûm°Èà;üIޱ<ý(g>ÿ À ÿõþù×£uVC1*%ø XÌÿõþùÊ�^=" ø +qèÿ÷ûÝŸ`7 ü<”÷ÿ÷þì¬a1 ø `Üý#ÿøð±[*  ÷' I¶öþ%ÿ÷þí§S% ô  *3J˜ðü)ÿøþñ¥O ù ,d¼öû.ÿòøÒŸmP:/*2A_¶öüÿþîþ=ÿýO©ù<ÿû]Óý:ÿü'yî9ÿû ?žù7ÿûXÊþ5ÿülî4ÿü�ú2ÿ ý Ñþ0ÿ þgì/ÿý®ù-ÿþ'Ñ,ÿþNí*ÿý §ù(ÿý=Óþ&ÿýeê%ÿý —ø#ÿý)Ìþ!ÿý _à ÿü"€ìÿý9 ðÿ üIŸóÿ!û !I‘òÿ"ù E…èþÿ#ø @y×üÿ$ú>nÄöÿ%ù 7^¥íýÿ&ø)K€Òú ÿ(ö !6^˜ãü ÿ+ó (Ep«íýÿ/ö 1Rx»ôþÿ5ö 4X€Ê÷þÿ ÿõþù×£uVC1*%ø XÌÿõþùÊ�^=" ø +qèÿ÷ûÝŸ`7 ü<”÷ÿ÷þì¬a1 ø `Üý#ÿøð±[*  ÷' I¶öþ%ÿ÷þí§S% ô  *3J˜ðü)ÿøþñ¥O ù ,d¼öû.ÿòøÒŸmP:/*2A_¶öüÿþîþ=ÿýO©ù<ÿû]Óý:ÿü'yî9ÿû ?žù7ÿûXÊþ5ÿülî4ÿü�ú2ÿ ý Ñþ0ÿ þgì/ÿý®ù-ÿþ'Ñ,ÿþNí*ÿý §ù(ÿý=Óþ&ÿýeê%ÿý —ø#ÿý)Ìþ!ÿý _à ÿü"€ìÿý9 ðÿ üIŸóÿ!û !I‘òÿ"ù E…èþÿ#ø @y×üÿ$ú>nÄöÿ%ù 7^¥íýÿ&ø)K€Òú ÿ(ö !6^˜ãü ÿ+ó (Ep«íýÿ/ö 1Rx»ôþÿ5ö 4X€Ê÷þÿ ÿõþù×£uVC1*%ø XÌÿõþùÊ�^=" ø +qèÿ÷ûÝŸ`7 ü<”÷ÿ÷þì¬a1 ø `Üý#ÿøð±[*  ÷' I¶öþ%ÿ÷þí§S% ô  *3J˜ðü)ÿøþñ¥O ù ,d¼öû.ÿòøÒŸmP:/*2A_¶öüÿþîþ=ÿýO©ù<ÿû]Óý:ÿü'yî9ÿû ?žù7ÿûXÊþ5ÿülî4ÿü�ú2ÿ ý Ñþ0ÿ þgì/ÿý®ù-ÿþ'Ñ,ÿþNí*ÿý §ù(ÿý=Óþ&ÿýeê%ÿý —ø#ÿý)Ìþ!ÿý _à ÿü"€ìÿý9 ðÿ üIŸóÿ!û !I‘òÿ"ù E…èþÿ#ø @y×üÿ$ú>nÄöÿ%ù 7^¥íýÿ&ø)K€Òú ÿ(ö !6^˜ãü ÿ+ó (Ep«íýÿ/ö 1Rx»ôþÿ5ö 4X€Ê÷þÿÿöýïÞ×ÔÇŸd* ûB¤Ýý'ÿõöãÚÓÆ­ƒR'ù‡»Øî+ÿíøúùòæ×ɸ¡‡�‡“¨ËÝæëö/ÿþóúóëèæäâæóíåâõ1ÿóüþþý÷óëëöóëæü5ÿùþõëíðõý ÿþ ÿÿø>ÿýëþþ<ÿýîþþ<ÿþëùþý8ÿýÔëýþýøèî5ÿü�ÁåûþüüíÈÅ3ÿû/x¬ØõþüøÜ¨á1ÿð?‰Àä÷úüýþüíÌÅýÿÿþÿøT�ÍãëõþüôáÈå.ÿù$g°ÙëýþúùëØ®Þþ+ÿú6ÂçüþúûîåǦó*ÿûOšÕ÷þúüïêÞ¼Û)ÿ òi´ëýþþýíëæÓÒö'ÿ ñ4ŒÓóýþõéëêàÖÖú%ÿ ò S«ÜêÝÛëëññáÃÞ$ÿ ø|ÁÔÕçíþüöÝÑú"ÿú5¡ÌÛëþüýòÞô!ÿùo»ØëòùþüüîèþÿùG—ËÝæ÷þýùæøÿù!]´ÐÞõþþõüÿú)‚ÅÙòþþýþÿú9¡Ííýþÿû Q¶àùþÿûsÈìûþÿû‚Éëùþÿø~Ãàîõüÿ ø!¼Ûèôþÿ"ó&|ºÛîüþþÿýýþÿ$ò%z¼àøþþöïôùýþ ÿ&úwÁìý þÿÿÿÿÿÿõùÄa<% ø )AD<+þ!ÿøå‚H, þñ+Ba]J- %$.ÿùýÇ^<)æ%$ .7QccM+%-)-ÿùø›F5+æ*77/$*..Eÿøé—gOD:51/þ10/ñ>Nb{xhP@?EIIB@QÿùýÐy]M=2/æ7>AII?96227@QYn}}kN?@IPM?Pÿùù¬eVN@1/æ>?MYYRI@??@MQQ]lzxaI??@A?JÿùòmVLD?8/é=@QZkhUOIJVZVQQ\fhe[E8:?ÿýîg>?þ=3/é?ÿòñvM@3//7?DOG<3/ÿ8?íKZ{uZBISq}vXQQ[lr^C?ÿúòƒN@>0/ç4?FQMC5//7??@OQ_{hM?AK\`SQù^eeR?39ÿúôŒQ@?7/ô3=EPQJ919??EQídlaF?8?NdkgR@ON;/4ÿøù§`QQJ@3/ø1?:3//4<?êKQQMMVe`I?8Gap}eGA@7/3ÿýú¹dQùPI<2//2?ÿ7/ý37<?êBPQJ?IYeZD47Qh}}X@>0// ÿñýÐjN>@IX`a[M=23?þ;1/ÿ7?îHQQC?I[dP=/=[r}uI?/ ÿñçuL9/8Cb~›˜~`QE?þ>3/ê3;MQN@?La[G53Fd{}aA1// ÿìöƒM?5/3?Ib~›’zh\I?<<?ÿ6/ì9QQI?DVdQ>08Mq�zM7// ÿûü˜U:3/î3;EVhu}{udE1/5=??7/í@RVC?N_[H70>Y•‹kA0/ ÿý¸^@3ì1//3:FSaoyvQ9//0>??7/í3VoR?EVdR?07Fo~{R6/ ÿãàkC??JVUN?4//28CL[`M?3/3??FJ9/îFz_G?IZYI72?Vz}oA/ ÿáüƒH>?G_uŽ�jVD4//17>@@?=17??IVQ8/î7acP@BQQPA1=Eh}{X5 ÿì³[70?EfŒ�Vÿý¼Z3/ó;@FPZgu}~zaM8/ÿ7?úKXe[C1/ï2IXQE?Nk}lJ88Qƒ³qÿýö€I/ñ8?7?BFJS]kcQI;0/þ3>?úAQbeR8/ð:;;>?@@??=2/ö380>DPY[WM?1/ø13683/ù3>IYeN8/ñFJOaeüRB<2/üAEB@??7/û6LnZ3/ù3EP;//7ÿùílI/;?3 /ÿ0/û1Igf9 /ú780//3ÿñÁXA4>MC>=<<=<72/ü9V`?/ÿ1ÿýý†I?õPQQV]enl_O</ý8@2/ÿýèpE?õMR_eel}}u[E+/ÿýÀfG?öFJLMOPLI@5,/ÿúüŒeG?<?þ:4./ÿúñu[D>8?úDGA:83/ü3994/ÿïÚcI?89??AMUX[SI@2/ú8?DC?7/ÿðÄU@?2:?@Vcee^SI;/ø0??@@?>7323;?ÿ:/ú2/ü37?@?û<==72/üÿõl4/õ7?@[~›z^M;0/ñ023578:<=??FQD9/ÿþ~7/ø2%/ý3?</ÿûæP832/ü07<4'/ý7?:/ÿûýuD?;./ý040/ÿû©hI?07/ÿúæzS?<2/þ30./ÿúü�_??:/ý9?0./ÿû¸j?B?/ý8?8/ý273)/ÿîôrHPJ2//8??1//3DB8)/ÿõûÝ¥‘„}}{xusrùt}†”•‘‡}ý|{~ÿøð¸˜ˆ€yvsrsrñ|‡”¥£™ˆ}}�„ƒ~‰ÿùýߤ‘†{træx|~„ƒ}yvttw~‰Ž�§§š‡}~„ˆ†}ˆÿùûÇ—�‡}srù|}‡ŽŽ‰ƒ}ö~‡‰‰‘œ¥£”„}ý~}„ÿùöœŒ…€}xré{}‰�š˜‹ˆƒ„Œ�Œ‰‰�—˜—�€xy}ÿýó˜|}þ{tréz}…‰˜§šŠ‰‰“šš”‹‰Š’—š‹yu}ÿõõ�‡vsy}}~}xréy}}…Œ ¥”‰ˆ‹�§¥›Š‰Š•¤š…|}ÿòõ¢†~urrw}€ˆ‚{trÿx}í…�¥¡�ƒŠŸ§¢Ž‰‰�œŸ’}ÿúöª‡}|sröu}‚‰‡vrrx}ôˆ‰“¥˜‡}~…�“Љù’——‰}tyÿú÷±‰}}wrôt{€ˆ‰„ysy}}�‰í•œ”‚}x{†‰‹‹‰‡‹—“~rvÿùø·Œ~}}srùsz…‰†~}ÿ„‰î•—�~st‰••�…Ž”}rvÿùùº�‚}}xrøtrrsy}~€}ÿ‰îŠ••ˆ{|}‡•š˜‰~ˆ‡zruÿøûÓ‰‰„~urøs}ytrruz}ê…‰‰†‡�—“ƒ}x‚”ž§—‚~}xruÿýûЕ‰ùˆ„ztrrt}ÿxrýuxz}ꈉ„}„Ž—�€ux‰˜§§Ž~|srr ÿñýßš‡|}„Ž“”�†{tu}þzsrÿw}‰}ƒ�•ˆ{r{�Ÿ§¢„}r ÿñî¡…yrx”§»¹¨“‰�}þ|urêtz†‰‡}}…”�‚vt‚•¥§”~srr ÿìø«†}vrt}ƒ”¨»µ¥˜�„}{{}ÿvrìy‰‰„}€�•‰|sx‡Ÿ©¥‡xrr ÿûý¹‹yurîtz�Œ˜¢§¥¡•€srv{}}wrí}‰Œ}‡“�ƒws|Ž·°š~sr ÿåÎ’~ututsrrty‚Š”�¤¢‰yrrs|}}xríuŒ�‰}�Œ•‰}sw‚�¨¥Švr ÿãéš}}„Œ‹‡}urrtx…�“†}uru}}‚„yrî‚¥“‚}„�Žƒxt}Œ¥§�~r ÿíýªƒ|}‚“¢²©šŒ€urrsx|}÷{sx}}„�‰xrîw”•ˆ}‰‰ˆ~s{�™§¥Žv ÿìË�wsz†”ªÍÖ´ŸŠ{srrsw}÷{x}}‚‹•‰vrîs„—Œ‚}…‰�‰yx}‰¤²¡~ÿî÷¢}rrt€ˆ��«´¸¹§•ƒvrþtz}ú‰““„urïy�އ}‰˜š‰|}€—±³�ÿýÑ�uróz}‚ˆ�˜¡§¨¥”†xrÿx}ú…Ž—�srït„މ�}‡š§œ„xx‰«ÌŸÿýø©„rñx}x}‚„Š‘š•‰ƒzsrþu|}ú~‰”—Šxrð{ˆ‰ˆ~„˜§¨•}r~šÎ½ÿèþΛ~rsut{}x}|yzz|}~~}}{tröux{}‚‹•”€trðu‚‰‰…ƒ•£µ§ŠvuŒ·Õÿò沑|s|€ˆŽ��‡}srøsuvxz}|trùu|„Ž—‡xrñ{‡‰‰Ž—�´º�yr~ŸÓÿñþ΢‰{v}†• ¥¥—ƒzrýtvsrùsz„’’}srñu€‰�‘�—­È•}rv�¶ÿðõ½—�}z}~„‰�Š…~}srús{ƒ�‚vròz„�‰„Œ Çš„urƒ¥ÿý娇}þxz}ÿtrûu}�zròu~‰‰}€’³ž…zry—ÿþÌ�}þru}þztrÿx}ÿtróx€„}}ƒ ›‡}tuŒÿûõ­{xz}ö��„……ƒ€}vrÿt}ÿxrÿz}ø{Š•ˆ}us„ÿóà”svtsvy|‚„ˆ”—üŠztrüzƒ�~rôsxyur{މ~sr}ÿúþ³ˆr{zrôtvxy|~€~}}xrûv…��urùu€ˆzrrxÿùó›„rz}t rÿsrûsƒ˜—y rúwxsrruÿøÕŽ~u|‡|{ýzwtrüyŒ“}rÿsÿýý­„}õˆ‰‰�‘—�›“ˆ{rýx}trÿýðž�}õ‡Š“——œ§§¡��+rÿýÔ—‚}ö‚„…‡ˆˆ…ƒ~v,rÿúý±—‚}{}þyu.rÿúõ¡�€|x}ú€‚~yxurüuyyurÿïæ•„}xy}}~†‹Ž�Š„}trúx}€}wrÿð׋}}ty}~Œ•——’Š„zrøs{€‰ˆ„}s rÿð½ƒ}yrz}„”•‘Š…ysrøs{‚ˆ‰‰…u rïÿþ¨}trz}ƒ‰ˆ„}zsrùsy~ƒ„‚x rðÿöŸ}vrrzuuz{{}}urýswurøuxyy}}xs rüÿæ˜yrûtrrsx}þysrüuxwtrûx~}ysrùz}�Œ‰€urüÿØ“tróv|}}~~}|xutuz}ÿyrúz…ˆ„~vrøszˆ•”‰€t rýÿÔŽr÷u{}~ƒˆ‰ˆ„ù€‚~}}{trþxƒ‰ý…ztrúv„Љ‰{ rýÿÔ�r}û€�’‰‡‰ù�‹‰‚}ysrýs{ƒ‰ý„zurütyyt rüÿÜŽsrðz�”Œ„„‰‰Š•šŠ…}xrôsu{„‰‰��…{xurüÿèŽtrñu‚Љƒ~Š”——¥’†}u rùsx€‡’š‰}ûyxvut rüÿð“urÿy}÷„—©¥—“†|trütx}~}{þwtrüÿøœurõx}}�¨»¥’‡zsrñstuvxxyz{}}‚‰€yrÿþ§wrø{‚š­¬�„xrúsw}~ysrÿþ³{rùz„¤ž‹€u"rûusruurÿýÍ�srúyƒŒ‡|t%rýu}{rÿûîˆxutrüsxzu'rýw}yrÿûý¡€}z.rýsusrÿûĘ„}s7rÿú}ztrþus.rÿúý³“}}yrýy}s.rÿûΚ}}rýx}xrýtxt)rÿî÷Ÿƒˆ„trrx}}srru€x)rÿþ ù1.#5;Dú.-$ ø+.$/A$.ûC7 ø:%.%)1.ûY]; ø4"-%&ù'Aca1÷/%&#ø 1JWH# ù=)$2÷+.7FF3þùF3.7"<ÿ.ú=F4% ù0 ùGDD6#5ö".6=CF0,÷&.;B.  ùGFB.&#ñ*CFF<..!.û0CD1 ÷GF7./è1:9.+., 55/.6FI3 ÷GC/./ÿ!.í&0A" ?^G0.8QR2 ü/F9.þé'...IQIMT/Qûux\@8 /øVG@PIJA/ùEKa}{S=/÷/3/ù0//7ERA/ /ùfddWHW/öGQW^ceRO?</÷=?BJQ\bQ4/ /ùfebQJH/ñ@Mcee]QQA?1//8FQûRcdS;/ ÷/7feYQR@/è?BS[[QNQOE<1/0CVVRQWehU9/ ÷/DfcRQR@/?ÿFQí@JRaG?CQQNNQUe[?0/3Msx]QQ`xjG/÷/H[AKQQ;/û/1:?NQï^eQ7///÷/BI4IQQ6//þ:EQíPUeaI2/3E[edRLScdO4//øG;1IQQ://æ0=IQQM@I]e[=//9L^e^KERcZE3//øI33NQQ=/ß54//3?MQI?04EZ†~Q37>@@4//AJ[P/ø3?JƒÃ Y9/ú3PSl|g/ø4IpŒŒ`//ÿ3/ë7@`qbK?E\e[M??EpÁ¿sL3/ùENXRD./ü~†P2/ß;^]O33DeuO2/;‡øú�O>@Q^N?//5;@<3/./üu}[9/è5M[N96Ad€c7/3~ïû¯Q28DRJ</ÿ0/./ûj}fB3/è1BVM??@`Žs?/2láûÁP//3<>4/ÿ0./ûUyoF6/î:KL??@[ŽxE63VËú¶K1 /þ6<./ûMnqI5/î3CI?8H¯ù§F5/ý383/ù;>0..-ûInyK4/è4<4/1IurL;9@rñ’@?2/2/þ87 /ö0FpßÕ9  ü/140'/ö?nãÿ÷�- */õ3Geäÿÿ÷…( */ü>Icãÿü÷| !/ÿ1/ü>Gaåÿüîl /þ3</ü8D_æÿüâ[/ý1>8/ü0;VëÿüÐG /ý;=1 /þ<îÿûþ²4/þ30 /þ5ïÿüû“!,/þ7ðÿüón/û14753 /þ<öÿüä?/ø3:AGB??< /þIþ ÿþÏ/ÿ0/ý2=LQüJ?<2 /ÿn ÿú¬/ô?=3/· ÿûñ]/þ23/þ30 /ÿ./þMÚ ÿüâE'/.ü/5dòÿýÇ6'/.ü/Bsùÿýþ¤,'/.ýD~ûÿþù‚'/.ýD�þÿÿî'/.þ;ìÿ'/.ÿìÿ'/.þ¡ÿ/ù3688940 /ù059@@91/.ý!ÿþun rùw‹‰ƒ��–ú‰ˆƒ|ys røry‡‰ƒ‰”„‰û•Ž~{t rø�|„‰„†‹r‰û¡£�}x rø�‚~ˆ„„~rù€…”§¥Š{r÷r{‰}„„ƒrø}�Š™ ˜ƒtrrù‘~†ƒ‹r÷}‡‰Ž——‹{srþwsrrù–‹‰Ž‚‘rÿ€‰ú’—Œ„|urùsrrw€Š~r rù—••�ƒ�rö‚‰�’•—Šˆ}zr÷{}„‰�”‰ur r—û”‰„ƒrñ}‡•——‘‰‰~}srrx‚‰ûŠ••Šzr ÷rx——މ‰~rè}Š��‰‡‰ˆ�zsrs��‰‰�—˜Œyr ür€—•‰þ~r}ÿ‚‰í}„Š”‚zrrv“¤—Љ���‹wr ürŠ—�‰þ}r}é…‰‰~‰‘”‚urr~Ÿ§˜‰‰‘¢œˆurýr‹•‰þ}ræu|}‰‰‡‡‰‹—�}sru‡ £‘‰‰“£š‚r÷rƒ�~…‰‰zrûrsy}‡‰ï’—‰xrrz‹˜—މ‰•›�|r÷r~„u„‰‰wrrþy€‰íˆ‹—”ƒtrt€�—•Š…Š••ˆvrrø‚zs„‰‰zrræs{ƒ‰‰‡}„‘—�{rry…’—’…€Š•��trrøƒtu‡‰‰{rßvurrt}†‰ƒ}{„••‰urt~Š——��~†�†yrrwvrrý}ry‰þyrþvzrây�‰„xrx‹—��sry„“�”‚}}€ˆ~tru|~|rúrrwsƒ‰þtrþrsrát}ˆˆ{st‚••Š|su€�­¨‰uw|}}urr~„�ˆrøuzˆ””‰rrráz„‰ˆ}{~�—�‡zt}ŠµÎŸ{rsvytrs…‰™Ÿ~røy€�¤¡�rrrìx��š‰~}ˆ••Š�|}„ªÖ¾ŽyrúuˆŠ›¦˜røu„ž±±“rrÿurëx}“Ÿ”…}��—�†}}�žÕÓ …urùz‰Žš§•{røw‚¨»´”trÿ}rëv}ˆ�Ÿ�€}‡—•‰w}•ÐÖº�{røv�‰Ž˜§˜‡rø{´»ª”wrþ‰urÕs{‚—¥“…}€•—Œ‚twŽÁáÓšƒrru€ˆ‰Š—§¢Œ~rrƒu¸ºŸ�yrþ•}rés}�¥�‰~~�—�„ts„®çí§�sx‰ò“¥§“‰rt„v”©—„wý¬‡srêt…ž¥�‚}Œ—“‡ur{¥ã÷¿�~}ƒ‰ò�Ÿ§žŒyw„u{“Švuýº�xréz”§‘„}†•—‰wru�Õ÷à•‚}„‰“‰ó“§¤“‡ur{xrrý»˜}rÙu�¥•…{€�—Œ{rs“¾úò�„}„Ž•‰ƒˆ”§˜Ž~urwtsrrý±§„ràƒ�˜‡sy‡—“€rrƒ³üû§…}€Ž”‰}x|�‡Ž‰€qrü§­ˆtrßz’‘ˆuu€—¡ˆtrz­úü´ˆ|}‰’‡}rrvz}{uqqrü¡§�yrèv‡�‡yv~•©•xru§ôüȉtx€Š„{rÿsrqrûš§—urüsŒ‡} }rt›ëüÕˆrru{|urÿsqrû‹¤�‚vrýy……}ô�²£€vuŒÜüÍ…s røvzpqqprrû†�Ÿ„vrîtƒ}x{Šª¢ƒ}|ƒÈûÂvrýuxurþz|qþo9ûƒ�¤…urèu{urs„¢Ÿ…zy~Ÿõµ~}trtz}}zrùz}wrqqXû~�¥‡urÿtrñ{”•†srv}Œƒxtrrx}ÿtrüx}}wqÿûzšŸ‰trþ{urüu†‹}rÿsrúz�†„~vr÷tvz{km öx�™Štrrt}xrýz€trùv„—•‹wrøv~zS# öu„—‰srrt}yrþst rùsy�~zxsr÷u‚žŠ# üs{€|rþxw rös‚žéÕ9  ürsus'rö}�ìÿ÷�- *rõu‚—ìÿÿ÷…( *rü|ƒ•ìÿü÷| !rÿsrü|‚”íÿüîl rþuzrüx€“îÿüâ[rýs|xrüszŒñÿüÐG rýz{s rþzóÿûþ²4rþts rþvôÿüû“!,rþxõÿüónrûsuwvt rþ{ùÿüä?røuy~‚}}{ rþƒþ ÿþÏrÿsrýt{…‰ü„}zt rÿ� ÿú¬rô}{ur{€‰‰ˆ„}x rÿqrþu¹ ÿúú�rõx}{rty~€}xt rÿqrþ|Î ÿûñ]rtrþus rqrþ‡æ ÿüâE'rqürv•öÿýÇ6'rqür ûÿýþ¤,'rqý€¨üÿþù‚'rqý}½þÿÿî'rqþuòÿ'rqþOîÿ&rqþ¡ÿrùtvxxyus rùsvy~~ysrqýH!ÿüÿûïøþùûõêʘNþÈøÿþþ¸ÿýþüûþú÷æÒ•Dýåüÿþö… ÿþúýùóØ|$þ/îÿþàT ÿþüöÅ_þ^òÿýþÇ= ÿþüûß…#ýiñÿýýÄ7ÿþúûýé›8ýoóÿþÖ3ÿøýíðùë­MýzýÿþÝ-ÿùïåïæ¿_ þ+ŠÿþÔ'ÿùùæëåÊr þ@ŠÿþÃÿùúêñèÐz þV‹ÿþ¹ÿùüëûðÒ� þ‹±ÿþ¹ÿùýñýòÓƒ þ ÀÿþºÿùûõþóÔƒþÅËÿþ¼ÿùú÷þóÒ…þÅÏÿþº ÿùøûþîЇþ¶Ïÿþº ÿùöþüëÎŽý¹àÿþÀÿþûúèË�ý»ðÿþÆÿûûïÆ“#ý8¾øÿþÄÿöýõµ•,TÃüÿþÁ!ÿøï½˜>2ÕüÿþÀ!ÿøþê®›R ÖøÿþÂ"ÿùþ櫞o¼ûÿþÃ#ÿúüâ´§¼ûÿþÉ$ÿûûÆÉÕùÿþÏ%ÿüêáç÷ÿþÓ&ÿýêîûÿþûÏ&ÿýýîýÿýúñ¯'ÿøôýÿÿýéãT(ÿùýÿÿýîÒ%*ÿûþüé¹*ÿþýéž +ÿüþðŠ,ÿýú…,ÿýýš-ÿþ÷X.ÿþêF/ÿýêN 0ÿýóŽ 1ÿýú˜ 2ÿýù¢ 3ÿýó©#4ÿýè«4ÿüþå¥ 5ÿüûå�6ÿüûã’7ÿüûÞ}8ÿýùÓP9ÿýôÃ+9ÿúþî©:ÿûýè… ;ÿüûÞƒ<ÿýúÎh=ÿþñÀ=ÿþþÞ>ÿÿð>ÿÿþ«ÿÿ÷>ÿÿöÿ Àÿ>ÿ>þ=ý&<ûd!:úÝL9úÿ»8+9ùÿû‹*(8ÿûìb( <8 Àÿ>ÿ>þ=ý&<ûd!:úÝL9úÿ»8+9ùÿû‹*(8ÿûìb( <8 Àÿ>ÿ>þ=ý&<ûd!:úÝL9úÿ»8+9ùÿû‹*(8ÿûìb( <8 Àÿ>þI=ý¶-<ýÔ¨<üÚД ;ûüåÓz:úÿùîÞW9ÿþþË89ÿýê¤8ÿüüÆ�7ÿýñ¹[7ÿýͯ6ÿü÷¼�5@@@@8ù 3X…Ô;ü,€8ù 3X…Ô;ü,€8ù 3X…Ô;ü,€(úkÁëú þþýþÿ+úL±äôýþþøýþÿ-÷2“àýþþîðþ/øŒÖåæëûþ2ø 5’Åßîöüþ4ùS¡Èãüþ7ø %^¢Òäîó9ú+eš°;ü 0 Àÿú>ÿüV�àü;ÿû!|èý8ÿû 9¾þ5ÿýfø4ÿüIªô2ÿú(V¬ú0ÿû 6qä/ÿú &WÂþ-ÿú "Jšõ,ÿú>‡ó+ÿÿ ù 6Žú*ÿÿù 7œý)ÿÿý7¨)ÿÿ ö <Á(ÿñ:OJ0OÔ'ÿð (WÄç^/bè&ÿï ,déûÖW*$lô%ÿî#dàÿü·A) *yú$ÿíT¸þÿñ~3* -Œþ#ÿí5vðÿýÒS&  1›#ÿìLºÿÿù�93ª"ÿÿ÷%súÿÿÛnû4¯þ ÿ ÷9çÿÿó¦9û 3¨ýÿ ÷)çÿøñç‡ú3¢ûÿ ÿö8çÿüõñ»[ú4—ùÿ ùVëÿûûóª0û3ˆ÷ÿ übíÿûþò‹ù 2{ôÿ ü !`éÿñþñe,oñÿ úVßÿùïIü1eêÿ ùGÅþÿýç>ü1]Úÿ ù 8‹üÿýãDú 0TÂÿ ù !eêÿòßK*8XÖÿ ù SÏ ÿò×P#"/@Fkìÿ ø  @²ü ÿòâb4*'-ACCP¨ûÿ ù  (–á ÿóð€E! $00@˜÷ÿ ùdÁ ÿôù¤Z !=Çþÿ ÿü =–ò ÿöþÜ}7 4RÀüÿ ü#nÖ ÿúþëÉÁÖ÷ÿ üN¹ý*ÿ üC¹ü*ÿ ü :¶ý*ÿü ,sý*ÿü!Kç*ÿü>«þ)ÿü+lï)ÿýWâ)ÿý JÐ)ÿý9µ)ÿü(‚)ÿþb)ÿýOý(ÿý7Ö(ÿü%Œ(ÿý[õ'ÿý >Ô'ÿý0¢'ÿý!pû&ÿýSë&ÿûGÁ&ÿú A�þ%ÿú?qû%ÿû2[ë%ÿûIÉ%ÿÿú>ÿüV�àü;ÿû!|èý8ÿû 9¾þ5ÿýfø4ÿüIªô2ÿú(V¬ú0ÿû 6qä/ÿú &WÂþ-ÿú "Jšõ,ÿú>‡ó+ÿÿ ù 6Žú*ÿÿù 7œý)ÿÿý7¨)ÿÿ ö <Á(ÿñ:OJ0OÔ'ÿð (WÄç^/bè&ÿï ,déûÖW*$lô%ÿî#dàÿü·A) *yú$ÿíT¸þÿñ~3* -Œþ#ÿí5vðÿýÒS&  1›#ÿìLºÿÿù�93ª"ÿÿ÷%súÿÿÛnû4¯þ ÿ ÷9çÿÿó¦9û 3¨ýÿ ÷)çÿøñç‡ú3¢ûÿ ÿö8çÿüõñ»[ú4—ùÿ ùVëÿûûóª0û3ˆ÷ÿ übíÿûþò‹ù 2{ôÿ ü !`éÿñþñe,oñÿ úVßÿùïIü1eêÿ ùGÅþÿýç>ü1]Úÿ ù 8‹üÿýãDú 0TÂÿ ù !eêÿòßK*8XÖÿ ù SÏ ÿò×P#"/@Fkìÿ ø  @²ü ÿòâb4*'-ACCP¨ûÿ ù  (–á ÿóð€E! $00@˜÷ÿ ùdÁ ÿôù¤Z !=Çþÿ ÿü =–ò ÿöþÜ}7 4RÀüÿ ü#nÖ ÿúþëÉÁÖ÷ÿ üN¹ý*ÿ üC¹ü*ÿ ü :¶ý*ÿü ,sý*ÿü!Kç*ÿü>«þ)ÿü+lï)ÿýWâ)ÿý JÐ)ÿý9µ)ÿü(‚)ÿþb)ÿýOý(ÿý7Ö(ÿü%Œ(ÿý[õ'ÿý >Ô'ÿý0¢'ÿý!pû&ÿýSë&ÿûGÁ&ÿú A�þ%ÿú?qû%ÿû2[ë%ÿûIÉ%ÿÿú>ÿüV�àü;ÿû!|èý8ÿû 9¾þ5ÿýfø4ÿüIªô2ÿú(V¬ú0ÿû 6qä/ÿú &WÂþ-ÿú "Jšõ,ÿú>‡ó+ÿÿ ù 6Žú*ÿÿù 7œý)ÿÿý7¨)ÿÿ ö <Á(ÿñ:OJ0OÔ'ÿð (WÄç^/bè&ÿï ,déûÖW*$lô%ÿî#dàÿü·A) *yú$ÿíT¸þÿñ~3* -Œþ#ÿí5vðÿýÒS&  1›#ÿìLºÿÿù�93ª"ÿÿ÷%súÿÿÛnû4¯þ ÿ ÷9çÿÿó¦9û 3¨ýÿ ÷)çÿøñç‡ú3¢ûÿ ÿö8çÿüõñ»[ú4—ùÿ ùVëÿûûóª0û3ˆ÷ÿ übíÿûþò‹ù 2{ôÿ ü !`éÿñþñe,oñÿ úVßÿùïIü1eêÿ ùGÅþÿýç>ü1]Úÿ ù 8‹üÿýãDú 0TÂÿ ù !eêÿòßK*8XÖÿ ù SÏ ÿò×P#"/@Fkìÿ ø  @²ü ÿòâb4*'-ACCP¨ûÿ ù  (–á ÿóð€E! $00@˜÷ÿ ùdÁ ÿôù¤Z !=Çþÿ ÿü =–ò ÿöþÜ}7 4RÀüÿ ü#nÖ ÿúþëÉÁÖ÷ÿ üN¹ý*ÿ üC¹ü*ÿ ü :¶ý*ÿü ,sý*ÿü!Kç*ÿü>«þ)ÿü+lï)ÿýWâ)ÿý JÐ)ÿý9µ)ÿü(‚)ÿþb)ÿýOý(ÿý7Ö(ÿü%Œ(ÿý[õ'ÿý >Ô'ÿý0¢'ÿý!pû&ÿýSë&ÿûGÁ&ÿú A�þ%ÿú?qû%ÿû2[ë%ÿûIÉ%ÿ€ÿúûóîíñû9ÿþûüöò÷ü6ÿþýíàµ5ÿþýùòí5ÿÿüþÿý5ÿýÉðüþ6ÿûi�Éëûþ6ÿ÷8x±äùþþ6ÿû G�Öó6ÿü`¿ô5ÿý¡ñ5ÿý�ô5ÿýžó5ÿý¬ñ5ÿý ¹õ5ÿýÈø5ÿýCÓ÷5ÿýdÛö5ÿüƒàõ5ÿüšåõ5ÿü§éò5ÿü¯ëï5ÿüŸëë5ÿû‰Ðâý4ÿüeÇÝù4ÿü1¹Ùó4ÿü °Îî4ÿû¥Èçü3ÿü“ÃÛö3ÿüpÀÕå3ÿû>»Õàý2ÿû&µÙìú2ÿû ­Ýìø2ÿû¢Ýëõ2ÿü’Ùëó2ÿüyÔë÷2ÿüLÍüþ2ÿþ/Ìþ1ÿþÃþ1ÿû °úþþ1ÿý¡çþ0ÿ ûˆÔðþþ0ÿ úgÒÚóþþ/ÿ úCÏÕàúþ/ÿ ùËÞêðþþ.ÿ ùÍüìëüþ.ÿ ùÀüõëõþ.ÿ ù¢öôëôþþ-ÿ ùzæëëöþþ-ÿ ùGÞêâóþþ-ÿ ùÔêáëûþ-ÿ ø ¿ëéëôþþ,ÿ øžèëìüþþ,ÿ übáëúþ,ÿ ý&Òðþ,ÿ ý¬òþ,ÿ ýxèþ,ÿ ýAËüþ+ÿ ý!¥îþ+ÿ ü �Ðüþ+ÿ ýQ¶óþ*ÿ ü$§ßýþ*ÿÿõþk7.# ü3$)ÿüµA.ÿû;; )ÿýóa.ÿ ûS:)ÿþ¨1ÿû (S3'ý ÿùöhû7G& &ü  ÿûÁ/ ûC@&ü ÿóþƒ "F6+ÿôëS  %C(+ÿõÃ.&/ö   ÿýý”üþ ô-../443- ü   ÿ÷ð^ ýó #..+)&"ÿü  ÿùÞ;û-5üõ#-+  ÿ û  ÿúÀ%  ú.D:û,71.û,#ö #$ ÿýüŸù-JC!õ ##  ý*.ý0,# ÿýùlû(;@ ü õ #%%).& ÿýóWú'# ÿýìGÿÿ õ   ÿýíVý  û,9;<ù81+! ÿøôiñ"1=@ACFF]cp‡Oÿ÷ù®7  ò#(1574'ÿöýÕg û%.ýÿöíœ)  ü&/0.ö+'&%%#!#%'ÿöüÑM  ð %6<@BFFB@<:760.ÿ÷ï—6  ì"$&),..0-'%$ÿøÝs(   ö ýÿùýÍ] ù!ÿûúÀV ù*440&ÿúüÂb( ù$ !ÿú߃9 ü #ÿùø¯P& &ÿú÷¤X-)ÿöþÏu9   .ÿïäšmYMC;6-(%#! !$'4ÿõþêÒº´··¸¸¿Ó€ÿÿõþ„XQH<7?<0/û8XfJ6&/ü5??3ÿûËR4<2/û@c`E1&/ü3552ÿóþ˜?//43/2GeW=+/ÿôîp5/794/ ÿ÷òy7//:851/ý8C7/ó3??HQQNMJG@80/ÿ<?ü=6// ÿùä\2/:<0/û7PV@1/ü09>>=õ<@DHPNE?<73/ÿ6?û;51// ÿúÊI/5?3/ú3/ý8?MQýROH ÿýú…8/?ûL\`B3 /ü0231/õ39<@EHIIMQJ ÿýõs8/ú;?AKH4/0/ÿýïf8/ÿ2?ÿ7 /õ3:;98753321 /ÿýðr:/ý5?< /û083/ÿøöƒB1//:;/ñ9GS^`aceex}ˆ›l;/ÿ÷ú¼X5/1<83/ò7?@CDCHLSVXVK7/ÿöýÜ€>003 /ð6?IW]`beeb`][YWRQÿ÷ñ¨W803:?3 /ì17;?CGIJMOQQRPKII@?@ÿøã‹L5/381 /ö03356789;<?ý=1/ÿùýÕxE3/0/ù9BFC?<1ÿûúÊr@3/ù8MVVRJ8ÿúüÌ|L93/ù7@EIE<0!ÿúä˜[B83/ü0352/#ÿùù¼mJ=50/&ÿúø³tP90/)ÿöþ׌[E;4310 /.ÿï誆ukc\WPLIHFEFIK4ÿõþíÚÅÁÃÃÄÄÉÛ€ÿÿõþ¬Ž‰ƒ{xz}}vrü~‹ƒx)rÿüÕ”‰~}ÿ{rûw��€u)rÿýø¥‰}ÿurû~ž�}s)rÿþΊ}ÿxrûu…ž‹y'rýutrÿùú©~|}{srûxŽ—„v&rüv}}uÿû܉u{trû}•“€s&rütvvtÿóþ¹}rruurt‚—�{+rÿôóžvrwzru„•…x+rÿõ݉srtrx„‰€trösuvxyxxvut rÿýýÂ}rüy}zrþutrý{~ˆ‰Œü‹ˆ€urüu|yur ÿ÷ö¤wrryxvsrýxxróu}}ƒ‰‰‡†„‚~xsrÿ{}ü{vrr ÿùì�try{srûxˆ�~srüsy||{ö}€ƒˆ‡�}{xtrÿv}ûzvsrr ÿúÛ„rv}urú{‰•�}srûs{ˆŽŠ‰ûˆƒ}xtröv}€ƒƒ~zwvv ÿýýÉzrùx}ˆ™•‚{rõsvz~€ƒƒ�}|trýx}‡‰ýŠˆƒ ÿýû¬xr}û…�“u rüstusrõty{}€ƒ„„†‰„ ÿýø xrúz}~…ƒursrÿýô—xrÿt}ÿx rõuyzyxwvuuts rÿýõŸyrýv}z rûs{ˆ��‘ù�Ї‚|xurÿøøªsrryzrñy‚Š’“”•——£§®»œzrÿ÷ûÑŽvrs{xtròx}}€ƒ…Š�ŽŒ…wrÿöýç©|ssz}zv r}û{y{}„‰ý~xsrÿöõdžusy}}zs rüs}„‰Š‰þ‡…„ûƒ‚ƒ„…ÿöýåš{suz}|u rðv}„�‘“”——”“‘�Ž�Љÿ÷öÄ�xsuy}t rìswz}‚ƒ„†ˆ‰‰Šˆ…„ƒ~}}ÿøì°…vrtxs röstuvvxxyz{}ý{srÿùýâ£�ursrùy‚}{sÿûüÛŸ~urùx‡ŒŒŠ„xÿúýܦ…yurùw}€ƒ�{s!ÿúí¹�xtrüsuvtr#ÿùûÒœ„{vsr&ÿúúÌ¡ˆysr)ÿöþä±��zutss r.ÿïðÆ­¡š•��ˆ…„ƒ‚�‚ƒ…4ÿõþóæØÔÖÖ××Ûæ€ÿÿõ &..+ ø*DF@%üÜÿã#+..)   *9F?/ ÿ`ÿþ é./.  ! þÿ ÿó  )8F<&ÿ ÿ þÿìÿ÷*.170 ÿÿ ÿÿ�ÿú ÿÿ þ*øÿý û  þ×ÿö þ ÿ‘ÿú ÿþ@ñÿø#(*+'ý þÙÿõ(..'  þ •ÿüÿ  ý@ù ÿ ù!&" ýã ÿÿú).DA&  þ « ÿö  þ\ ÿ! ý)ø ÿ ýå ÿý ± ÿ ý ýk ÿõ "' ü>þ ÿü ü+)ÿþ&ü ÿò+,,/41*% þã ÿ.ø084+  þý¶ ÿú þý‘ ÿü ý � ÿü(qý � ÿû /né{ý ‘ ÿü ú:šøþwý � ÿü ö !lËþÿÿ©ý � ÿ û 0^žèÿüøl ý � ÿ û #oºéÿüè4þ • ÿõ .’éú ÿüüzþ¡ ÿö*.17Dd‹Øúþ ÿüÚ4þà ÿþóýÿüúaþ,Þ$ÿü¼-þGð$ÿüöK þgö$ÿüý~"þxú%ÿýÀ:ý’ü%ÿüöK ý´þ%ÿüýeþTÒ'ÿý•(þ‚æ'ÿýá=þ­ú'ÿýýTÿÒ)ÿýp ÿò)ÿý© *ÿýæ=ÿ¡*ÿüýLþšý+ÿýi þoø,ÿü‘" þ7Ù-ÿüÆ, ý¶þ-ÿüï9 ý’ø.ÿûþJýnñ0ÿüfüZâ1ÿûœ üOÏ2ÿûÏ&üI¾3ÿôò36‚þ4ÿöDCº5ÿþ^ ûAá5ÿþ‘û1×5ÿþÎû ½5ÿþó)ýš5ÿýý=üq6ÿþXûNþ5ÿþ‰ û3úÿ/õ4>?AJQQNA80 /ø7AMde`I8/.üÜÿ/ã>??AHNQQM?<0//248;6/4?M[e_RD5/ú.*`ÿ/þ38?éBQRQC?>;>?<83//7?@EFD@: /þ.$ÿ ÿ/ó3?EMZe]J??<72/ÿ4?ÿ1 /þ,ÿìÿ/÷:MQSXRB61/ÿ>?ÿ6 /þ,ÿ�ÿ/ú1;>>71 /ÿ8?ÿ< /þ*øÿ/ý032/û3??;2 /þ×ÿ/ö13579<>??</3 /ÿ‘ÿ/ú2368:<?<ÿ8/þ@ñÿø/7@HLMNK?ý=73/þÙÿõ/1=LQQK@?>5/ þ •ÿ/ü1=BA?ÿ6/ ý@ù ÿ5ùþ ÿü038=?üANM8/./þ&ü ÿòNOORVSMI@??;83/.ü)'//þã ÿQøRZVNE<63 /.ü-#ý¶ ÿú?><852/ý, ý‘ ÿ/û144ý � ÿ/ü?L‰ý � ÿ/û6R‡í{ý ‘ ÿ/ü0330/ú2[ªùþwý � ÿ/ü1330/ö37F…Óþÿÿ©ý � ÿ /û3Ry¯ìÿüøl ý � ÿ /û5H‡Åíÿüè4þ • ÿ/õ023368>Q¤íú ÿüüzþ¡ ÿöMQSXd~žßúþ ÿüÚ4þà ÿþõýÿüúaþ,Þ$ÿü¼-þGð$ÿüöK þgö$ÿüý~"þxú%ÿýÀ:ý’ü%ÿüöK ý´þ%ÿüýeþTÒ'ÿý•(þ‚æ'ÿýá=þ­ú'ÿýýTÿÒ)ÿýp ÿò)ÿý© *ÿýæ=ÿ¡*ÿüýLþšý+ÿýi þoø,ÿü‘" þ7Ù-ÿüÆ, ý¶þ-ÿüï9 ý’ø.ÿûþJýnñ0ÿüfüZâ1ÿûœ üOÏ2ÿûÏ&üI¾3ÿôò36‚þ4ÿöDCº5ÿþ^ ûAá5ÿþ‘û1×5ÿþÎû ½5ÿþó)ýš5ÿýý=üq6ÿþXûNþ5ÿþ‰ û3úÿrõu|}~„‰‰‡~xs røw~‡•—“„xrqü%Üÿrã|}}~ƒ‡‰‰†}zsrrtuxzvru}‡�—“‰€vrúqe `ÿrþtx}ÿ‰í}|z|}{xurrx}~�‚€}y rúqW ÿróu}�†�—‘„}}zwtrÿu}ÿs rþm/ÿìÿr÷y‡‰ŠŽŠvsrÿ|}ÿv rþk ÿ�ÿrúsz||xs rÿx}ÿ{ rÿþ*øÿrýsttrût}}zt rþ×ÿrösuvxyz|}}zrþut rÿ‘ÿrútuvxy{}{ÿxrþ@ñÿørx~ƒ…‡‡…}ý{wtrþÙÿõrs{…‰‰…~}|vr þ •ÿrüs{~}ÿvr ý@ù ÿvù{~‚„‚~wr ýã ÿÿ~}ú†‰•”„vr þ « ÿþ€{z{ýyusr þ\ ÿ!r ý)ø ÿ rýå ÿrý ± ÿ rýstu rýk ÿrõstuvxyz}‚…yrÿqrü>þ ÿüsux{}ü~‡†xrqrþ&ü ÿø‡ˆˆ‰ŒŠ‡„}ýzxurqüe^rrþã ÿ‰øŠ�Œ‡�zvt rqünW&ý¶ ÿú}|zxvtrüqlPý‘ ÿrûsutDý � ÿrü}…¯ý � ÿrûv‰­ò{ý ‘ ÿrüsttsrút�Æûþwý � ÿrüsuusrötx‚¬áþÿÿ©ý � ÿ rûuФÈòÿüøl ý � ÿ rûvƒ®Øòÿüè4þ • ÿrõstuuvx|‰Áòü ÿüüzþ¡ ÿö‡‰ŠŽ•§½éüþ ÿüÚ4þà ÿþøýÿüúaþ,Þ$ÿü¼-þGð$ÿüöK þgö$ÿüý~"þxú%ÿýÀ:ý’ü%ÿüöK ý´þ%ÿüýeþTÒ'ÿý•(þ‚æ'ÿýá=þ­ú'ÿýýTÿÒ)ÿýp ÿò)ÿý© *ÿýæ=ÿ¡*ÿüýLþšý+ÿýi þoø,ÿü‘" þ7Ù-ÿüÆ, ý¶þ-ÿüï9 ý’ø.ÿûþJýnñ0ÿüfüZâ1ÿûœ üOÏ2ÿûÏ&üI¾3ÿôò36‚þ4ÿöDCº5ÿþ^ ûAá5ÿþ‘û1×5ÿþÎû ½5ÿþó)ýš5ÿýý=üq6ÿþXûNþ5ÿþ‰ û3úÿfÿÿþ=ÿþ=ÿýþýüÿÿþ5ÿüþýëØÿÿ×5ÿûýí¸mÜÿþþÚ4ÿõþóÃMµþÿÿþæ4ÿùùÜ\³òþÿì3ÿøþê•lÝþÿö3ÿýóÀýºùþ2ÿýûÙWþ‚ðþ1ÿüþé—þ(Üþ0ÿüþô¼û°ûþþ/ÿüþûÖFþ|óþ-ÿþýë|þ6äþ,ÿþýõ�þ Ëþ+ÿþýõ­ ü¤úþþ*ÿûþûî¶( ü`öþþ)ÿûþùç¼< ü,ðþþ)ÿûþïÊR ü çþþ)ÿüþÇg üÜþþ)ÿý³>ýÖýþ)ÿýë üÔüþþ)ÿþ¤ üÐúþþ#ÿÿþÿþüiüÍùþþ#ÿþûöÿþ¶ üÉøþþ$ÿÿúÿþábüÅ÷þþ*ÿý÷° üÄøþþ*ÿüþÝW üÆùþþ(ÿþýô« üÈùþþ)ÿþýýÖJ ýÊûþ(ÿþýð¤ ýÍüþ)ÿþýûË< ý Ôýþ)ÿþýëŸ þ%Üþ*ÿþýùÁ þCæþ*ÿþýâý`îþÿü*ÿþýõ« ý …õþÿù*ÿþýýÎ;ý ¢ûþÿò+ÿþýìŒþIÊþÿî+ÿþýú°ýváþþúõ+ÿþþÚAý!¢ïþþïþ+ÿþýï‰üUÇöþÿÚ,ÿþýù«ø”ßüþþù´-ÿþþÐCý^Âðþýëþ,ÿþò달ÞùþþýÆ<-ÿþøú¿)‰Ïñþýô�&.ÿþùå¨ yÂéýþýÖ‡û.ÿþúùÐÀÆåùþþøæ1ÿþüïâëúþþýü1ÿþþ÷ûþ3ÿþþýþ5ÿþþýü6ÿþÿü8ÿþ:ÿþ<ÿþ=ÿþ>ÿÿþ>ÿÿþ>ÿÿþ>ÿþ>ÿÿþ>ÿÿþ>ÿÿþÿÿûÆG>A7ÿû‡D>6ÿûÞV?6ÿü‹E6ÿû×V! 5ÿûw'4ÿû¾74ÿûõW 4ÿü‡4ÿû½23ÿûë` 3ÿûü€' 3ÿü©=3ÿüÜN3ÿüõ[3ÿüýn$3ÿý‘)3ÿý¿/ 3ÿýè6 3ÿýø>3ÿýþI3 ÿþT3 ÿþ_3 ÿþj3 ÿþu3 ÿþ€3 ÿþŒ3 ÿþ—3 ÿþ¦3 ÿýµ!2 ÿýÅ 2 ÿýÍ!2 ÿýÈ 2 ÿýÁ 2 ÿý³2 ÿý¡ 2 ÿý� 2 ÿý…2 ÿýy2 ÿýt2 ÿýb 2ÿüùS2ÿüïC2ÿüã22ÿüÉ 2ÿü›2ÿým 3ÿüôP3ÿüà83ÿý¸!4ÿý�4ÿüõR4ÿüâ04ÿü¼4ÿûþy 4ÿü÷F5ÿýÙ(6ÿý•6ÿüýb 6ÿýæ>7ÿý¬"7ÿýq7ÿýëO 8ÿý¶68ÿûÆG>A7ÿû‡D>6ÿûÞV?6ÿü‹E6ÿû×V! 5ÿûw'4ÿû¾74ÿûõW 4ÿü‡4ÿû½23ÿûë` 3ÿûü€' 3ÿü©=3ÿüÜN3ÿüõ[3ÿüýn$3ÿý‘)3ÿý¿/ 3ÿýè6 3ÿýø>3ÿýþI3 ÿþT3 ÿþ_3 ÿþj3 ÿþu3 ÿþ€3 ÿþŒ3 ÿþ—3 ÿþ¦3 ÿýµ!2 ÿýÅ 2 ÿýÍ!2 ÿýÈ 2 ÿýÁ 2 ÿý³2 ÿý¡ 2 ÿý� 2 ÿý…2 ÿýy2 ÿýt2 ÿýb 2ÿüùS2ÿüïC2ÿüã22ÿüÉ 2ÿü›2ÿým 3ÿüôP3ÿüà83ÿý¸!4ÿý�4ÿüõR4ÿüâ04ÿü¼4ÿûþy 4ÿü÷F5ÿýÙ(6ÿý•6ÿüýb 6ÿýæ>7ÿý¬"7ÿýq7ÿýëO 8ÿý¶68ÿûÆG>A7ÿû‡D>6ÿûÞV?6ÿü‹E6ÿû×V! 5ÿûw'4ÿû¾74ÿûõW 4ÿü‡4ÿû½23ÿûë` 3ÿûü€' 3ÿü©=3ÿüÜN3ÿüõ[3ÿüýn$3ÿý‘)3ÿý¿/ 3ÿýè6 3ÿýø>3ÿýþI3 ÿþT3 ÿþ_3 ÿþj3 ÿþu3 ÿþ€3 ÿþŒ3 ÿþ—3 ÿþ¦3 ÿýµ!2 ÿýÅ 2 ÿýÍ!2 ÿýÈ 2 ÿýÁ 2 ÿý³2 ÿý¡ 2 ÿý� 2 ÿý…2 ÿýy2 ÿýt2 ÿýb 2ÿüùS2ÿüïC2ÿüã22ÿüÉ 2ÿü›2ÿým 3ÿüôP3ÿüà83ÿý¸!4ÿý�4ÿüõR4ÿüâ04ÿü¼4ÿûþy 4ÿü÷F5ÿýÙ(6ÿý•6ÿüýb 6ÿýæ>7ÿý¬"7ÿýq7ÿýëO 8ÿý¶68ÿýã¼35ÿüýÖš4ÿýòÂ74ÿýÕ›3ÿýóÁ43ÿýþÓ�3 ÿýæ´2 ÿýøÎ`2 ÿýä›1 ÿýù¬%1 ÿýó±j1 ÿüõŠ0 ÿüúØ¢+0 ÿüýâÁP0 ÿüîÒr/ ÿüûÜ› / ÿüþç±'/ ÿüþøÀK/ ÿþþ×k/ ÿþýä…. ÿþýé—. ÿûýþìž. ÿûúþï§. ÿûùþï±$. ÿûûþî¹.. ÿþýíÀ6. ÿþýìÊ;. ÿþýëÓA. ÿþýéÕ}. ÿüþãÕ². ÿþýÝÕÁ. ÿûþýØÕ­. ÿûþüÕÕ’. ÿüûÖØn. ÿûþúæÙL. ÿûþúëÓH. ÿûþùëËB. ÿüøëÃ9.ÿÿýÿü÷ë¼0.ÿÿùÿüöê¹&.ÿÿõÿüõé´.ÿÿïÿüôè¯.ÿÿíÿüóæ©.ÿÿøÿýòå¢/ ÿýñã˜/ÿÿþ ÿýðá…/ÿÿþÿüþïÚo/ ÿüþíÉB/ ÿüþåµ/ ÿûþýÚ�/ ÿüýûÐy0 ÿüíóÁE0 ÿûþàÒ¬0 ÿûýÙ¿“0 ÿüüκ`1 ÿüûǰ,1ÿûþûž1ÿûþû¾x1ÿûþúµA1ÿûþñù¨ 2ÿüùëì‹3ÿüôÜÚP3ÿûþíÕº3ÿûþæÓž3@@@@ý+ %ÿý p÷$ÿýWñ$ÿüCâþ#ÿý,‹ø#ÿüGÅþ"ÿü jò"ÿý GÅ"ÿý6´"ÿü+¦ý!ÿû#‡ø!ÿû![è!ÿüAÔ!ÿû4¦ý ÿû &hö ÿüLÝ ÿü7“ ÿü$güÿüJÖÿý 5šÿü#výÿüLçÿý4Åÿü$–ùÿýeÛÿüTÀýÿü E£åÿû5g¼ùÿû8g®ñÿ û $K�Ûÿ ù1[ªðÿ ú6qÅûÿ!û %aÒúÿ!üqòÿ"þ1Ìÿ"þ§ÿ#þ|ÿ#þcÿ#ÿIÿ$þ0þÿ$þ&ýÿ$þ!öÿ#þíÿ#þßÿ#þÓÿ#þ$Èÿ#ý1ºÿ#ü 4ªþÿ#ü3šúÿ#ü/…ôÿ#û#XÊúÿ#úLšæþÿ#û$lÅôÿ# ü F£ï ÿ# ü 8�é ÿ# û(eÕþ ÿ# ûK©ü ÿ"þ û $`Ûþÿ"þ ø7d¥ñþÿ"þù#>€úÿ"ÿø>Ÿýÿÿ"ÿú %7+ øWäÿÿ#ø fã�R!ø®ÿÿ#÷FÖÿÿËs2 û  §ÿý+ %ÿý p÷$ÿýWñ$ÿüCâþ#ÿý,‹ø#ÿüGÅþ"ÿü jò"ÿý GÅ"ÿý6´"ÿü+¦ý!ÿû#‡ø!ÿû![è!ÿüAÔ!ÿû4¦ý ÿû &hö ÿüLÝ ÿü7“ ÿü$güÿüJÖÿý 5šÿü#výÿüLçÿý4Åÿü$–ùÿýeÛÿüTÀýÿü E£åÿû5g¼ùÿû8g®ñÿ û $K�Ûÿ ù1[ªðÿ ú6qÅûÿ!û %aÒúÿ!üqòÿ"þ1Ìÿ"þ§ÿ#þ|ÿ#þcÿ#ÿIÿ$þ0þÿ$þ&ýÿ$þ!öÿ#þíÿ#þßÿ#þÓÿ#þ$Èÿ#ý1ºÿ#ü 4ªþÿ#ü3šúÿ#ü/…ôÿ#û#XÊúÿ#úLšæþÿ#û$lÅôÿ# ü F£ï ÿ# ü 8�é ÿ# û(eÕþ ÿ# ûK©ü ÿ"þ û $`Ûþÿ"þ ø7d¥ñþÿ"þù#>€úÿ"ÿø>Ÿýÿÿ"ÿú %7+ øWäÿÿ#ø fã�R!ø®ÿÿ#÷FÖÿÿËs2 û  §ÿý+ %ÿý p÷$ÿýWñ$ÿüCâþ#ÿý,‹ø#ÿüGÅþ"ÿü jò"ÿý GÅ"ÿý6´"ÿü+¦ý!ÿû#‡ø!ÿû![è!ÿüAÔ!ÿû4¦ý ÿû &hö ÿüLÝ ÿü7“ ÿü$güÿüJÖÿý 5šÿü#výÿüLçÿý4Åÿü$–ùÿýeÛÿüTÀýÿü E£åÿû5g¼ùÿû8g®ñÿ û $K�Ûÿ ù1[ªðÿ ú6qÅûÿ!û %aÒúÿ!üqòÿ"þ1Ìÿ"þ§ÿ#þ|ÿ#þcÿ#ÿIÿ$þ0þÿ$þ&ýÿ$þ!öÿ#þíÿ#þßÿ#þÓÿ#þ$Èÿ#ý1ºÿ#ü 4ªþÿ#ü3šúÿ#ü/…ôÿ#û#XÊúÿ#úLšæþÿ#û$lÅôÿ# ü F£ï ÿ# ü 8�é ÿ# û(eÕþ ÿ# ûK©ü ÿ"þ û $`Ûþÿ"þ ø7d¥ñþÿ"þù#>€úÿ"ÿø>Ÿýÿÿ"ÿú %7+ øWäÿÿ#ø fã�R!ø®ÿÿ#÷FÖÿÿËs2 û  §ÿ ü ‰Ïøþ*ÿ übÇðþ)ÿýE¼èþ)ÿü.¬àýþ)ÿü—Þùþ(ÿü �Úöþ(ÿürÔóþ(ÿý\Ëïþ'ÿýHÀëþ'ÿü2³êûþ'ÿü¤æøþ&ÿüŽàôþ&ÿüƒÙñþ%ÿütÊíþ%ÿüV¸çýþ%ÿü*¥ßùþ$ÿü›Ïöþ$ÿü…Àòþ#ÿýA³çþ#ÿü •Ùúþ"ÿý[Èòþ"ÿü¤áýþ"ÿümÇïþ!ÿü§Þóþ!ÿûxÆêöþ ÿü(­ßîþÿüÿú‡Éëóýþÿöÿû7¯äëùþþõýÿû ŽÕë÷þþüúÿü2±èõþÿücÚóþÿý ¬ñþÿüNàþùþÿû¨öëùþÿü<ÂâñþÿühµÞþÿüÅûþÿý8ŸóþÿügÛùþÿû$§àíøþÿüZ¹æðþÿüiÇêþÿû oÔ÷þÿüˆâþÿ ý¬úÿ!þ_ôÿ!þdôÿ ýiõÿ ý�÷ÿ ý—ùÿ ý$«ùÿ ý5ºûÿüDÇüÿüNÏýÿüRÖþÿüiÜþÿ ý~Þýÿ ý�âôÿ ü�åðþÿ ü¦èðþÿü­ëóÿü*¿ëûÿüXÍñþÿü}ØùþÿÿÿÿÿÿþÆý ð5ÿþñ ýã5ÿþû5ýÐ5ÿöýT ³6ÿþ‡üž6ÿþ¼ü~6ÿþìûaþ5ÿþû&ûLþ5ÿþþIû?ý6ÿ÷‡4ú6ÿ÷´ +õ6ÿþåþ$ñÿÿþ!ÿ÷ýEìÿÿô"ÿøj æÿÿ¿"ÿøuÚÿþý|"ÿø€Ïÿþå/"ÿø„½ÿþ©"ÿøt ™ÿýû`"ÿøR zÿýÓ'!ÿþæ%þ dÿüþ�!ÿþ‘þ WÿüôN ÿýþJþLÿü¾, ÿþÅ ýAþÿûünÿýûf ý4àÿûå@ ÿüþÀ3þ… ÿúþ¢&ÿüýÝký7± ÿýøXÿüø�6ü N ÿýÏ/ÿý×[üüaÚ ÿüý€ÿíò�0 <€r. sñÿüñ?ÿüú¯Cóa¶ÿ¼33 ÿü³ÿêüÁa$*l×ÿÿ¯WýÿüúdÿûýÖx4 ñ6uøÿÿ÷M5ÕÿüÖ=ÿûö”Iý ?~ÿýþ— ý†ÿûý‡!ÿüëq( ýG‰ÿýí;ý LùÿüìKÿýâjü2N»ÿ÷úg&Åÿü¥1ÿýÛdüCwøÿýþ§ýiüÿüù]ÿýÚVü+eâÿþç ý/ÏÿýÁ4ÿýÇKýI«ÿýúRùzòÿòjÿöüš:  %wüÿýþ·ú3®üœ6ÿöþÇs) @Çÿýñ5ûS…N ÿöõ�K!qüÿýüq ü9 ÿöþàb(9¼ ÿýË'ý  ÿöûŸ@ hö ÿýøP  ÿöâe$ 1±þ ÿüþŽ ÿöñ�6]ò ÿýÜ<  ÿõû˜S!  (¥þ ÿý÷W ÿöÜh5Rï ÿýúv ÿöü¯Sšÿüý¨$ ÿöð�3  7æÿý¾* ÿöûÊR ‹ÿýÊ0 ÿõüåk'1ÛÿýÓ4 ÿõüè€3€ÿýÚ5ÿÿúùÓ|2 ý 7ãÿýÞ4ÿÿûè¶k<þ§ÿýä5þ  ÿúõÊ”[5ý Xúÿýæ4þ ÿøýÙ¡aF)ý1Ùÿýå4þ ÿøä«hG8 ýŸÿýå4þÿöû¿j7& üfûÿýä4þ ÿúý¸W ù Jðÿýä3þ ÿüÛS'ûE×ÿýá3þ 0ÿý/ü !G¸ÿýÝ2þDÿþ=û"O²þÿþÚ1þAÿýó- ú:bäþÿþÖ*þ8ÿóê21P³ûÿþÔ'þBÿþÆý ð5ÿþñ ýã5ÿþû5ýÐ5ÿöýT ³6ÿþ‡üž6ÿþ¼ü~6ÿþìûaþ5ÿþû&ûLþ5ÿþþIû?ý6ÿ÷‡4ú6ÿ÷´ +õ6ÿþåþ$ñÿÿþ!ÿ÷ýEìÿÿô"ÿøj æÿÿ¿"ÿøuÚÿþý|"ÿø€Ïÿþå/"ÿø„½ÿþ©"ÿøt ™ÿýû`"ÿøR zÿýÓ'!ÿþæ%þ dÿüþ�!ÿþ‘þ WÿüôN ÿýþJþLÿü¾, ÿþÅ ýAþÿûünÿýûf ý4àÿûå@ ÿüþÀ3þ… ÿúþ¢&ÿüýÝký7± ÿýøXÿüø�6ü N ÿýÏ/ÿý×[üüaÚ ÿüý€ÿíò�0 <€r. sñÿüñ?ÿüú¯Cóa¶ÿ¼33 ÿü³ÿêüÁa$*l×ÿÿ¯WýÿüúdÿûýÖx4 ñ6uøÿÿ÷M5ÕÿüÖ=ÿûö”Iý ?~ÿýþ— ý†ÿûý‡!ÿüëq( ýG‰ÿýí;ý LùÿüìKÿýâjü2N»ÿ÷úg&Åÿü¥1ÿýÛdüCwøÿýþ§ýiüÿüù]ÿýÚVü+eâÿþç ý/ÏÿýÁ4ÿýÇKýI«ÿýúRùzòÿòjÿöüš:  %wüÿýþ·ú3®üœ6ÿöþÇs) @Çÿýñ5ûS…N ÿöõ�K!qüÿýüq ü9 ÿöþàb(9¼ ÿýË'ý  ÿöûŸ@ hö ÿýøP  ÿöâe$ 1±þ ÿüþŽ ÿöñ�6]ò ÿýÜ<  ÿõû˜S!  (¥þ ÿý÷W ÿöÜh5Rï ÿýúv ÿöü¯Sšÿüý¨$ ÿöð�3  7æÿý¾* ÿöûÊR ‹ÿýÊ0 ÿõüåk'1ÛÿýÓ4 ÿõüè€3€ÿýÚ5ÿÿúùÓ|2 ý 7ãÿýÞ4ÿÿûè¶k<þ§ÿýä5þ  ÿúõÊ”[5ý Xúÿýæ4þ ÿøýÙ¡aF)ý1Ùÿýå4þ ÿøä«hG8 ýŸÿýå4þÿöû¿j7& üfûÿýä4þ ÿúý¸W ù Jðÿýä3þ ÿüÛS'ûE×ÿýá3þ 0ÿý/ü !G¸ÿýÝ2þDÿþ=û"O²þÿþÚ1þAÿýó- ú:bäþÿþÖ*þ8ÿóê21P³ûÿþÔ'þBÿþÆý ð5ÿþñ ýã5ÿþû5ýÐ5ÿöýT ³6ÿþ‡üž6ÿþ¼ü~6ÿþìûaþ5ÿþû&ûLþ5ÿþþIû?ý6ÿ÷‡4ú6ÿ÷´ +õ6ÿþåþ$ñÿÿþ!ÿ÷ýEìÿÿô"ÿøj æÿÿ¿"ÿøuÚÿþý|"ÿø€Ïÿþå/"ÿø„½ÿþ©"ÿøt ™ÿýû`"ÿøR zÿýÓ'!ÿþæ%þ dÿüþ�!ÿþ‘þ WÿüôN ÿýþJþLÿü¾, ÿþÅ ýAþÿûünÿýûf ý4àÿûå@ ÿüþÀ3þ… ÿúþ¢&ÿüýÝký7± ÿýøXÿüø�6ü N ÿýÏ/ÿý×[üüaÚ ÿüý€ÿíò�0 <€r. sñÿüñ?ÿüú¯Cóa¶ÿ¼33 ÿü³ÿêüÁa$*l×ÿÿ¯WýÿüúdÿûýÖx4 ñ6uøÿÿ÷M5ÕÿüÖ=ÿûö”Iý ?~ÿýþ— ý†ÿûý‡!ÿüëq( ýG‰ÿýí;ý LùÿüìKÿýâjü2N»ÿ÷úg&Åÿü¥1ÿýÛdüCwøÿýþ§ýiüÿüù]ÿýÚVü+eâÿþç ý/ÏÿýÁ4ÿýÇKýI«ÿýúRùzòÿòjÿöüš:  %wüÿýþ·ú3®üœ6ÿöþÇs) @Çÿýñ5ûS…N ÿöõ�K!qüÿýüq ü9 ÿöþàb(9¼ ÿýË'ý  ÿöûŸ@ hö ÿýøP  ÿöâe$ 1±þ ÿüþŽ ÿöñ�6]ò ÿýÜ<  ÿõû˜S!  (¥þ ÿý÷W ÿöÜh5Rï ÿýúv ÿöü¯Sšÿüý¨$ ÿöð�3  7æÿý¾* ÿöûÊR ‹ÿýÊ0 ÿõüåk'1ÛÿýÓ4 ÿõüè€3€ÿýÚ5ÿÿúùÓ|2 ý 7ãÿýÞ4ÿÿûè¶k<þ§ÿýä5þ  ÿúõÊ”[5ý Xúÿýæ4þ ÿøýÙ¡aF)ý1Ùÿýå4þ ÿøä«hG8 ýŸÿýå4þÿöû¿j7& üfûÿýä4þ ÿúý¸W ù Jðÿýä3þ ÿüÛS'ûE×ÿýá3þ 0ÿý/ü !G¸ÿýÝ2þDÿþ=û"O²þÿþÚ1þAÿýó- ú:bäþÿþÖ*þ8ÿóê21P³ûÿþÔ'þB$ÿþ=ÿþ=ÿþ=ÿþ>ÿÿþ>ÿÿþ>ÿÿþ>ÿÿþ>ÿÿþ>ÿÿþ>ÿÿþ€ÿÿþ>ÿÿþÿÿñ4ÿÿþÿÿî&ÿÿþÿÿå&ÿÿþÿþþð&ÿþÿþüù&ÿþÿþûþ&ÿþÿþûþ&ÿþÿþüþ&ÿþÿþ&ÿþÿþ&ÿþÿþ&ÿþÿþ&ÿþÿþÿü&ÿþÿþÿù&ÿþÿþÿô&ÿüýûþþÿþÿï&ÿüþóûþÿþÿç'ÿüùìýþÿþÿÛ'ÿûþìóþþÿþþúÅ(ÿþþíþ ÿþþð‘)ÿÿýþ ÿþýüÚR+ÿþ ÿûþüí°$9ÿþüðàx7ÿÿýþüìÅD6ÿÿûþûýæ” 5ÿþþúþûùÖV5ÿÿýþû÷±5ÿþûíy5ÿþþÓ 4ÿþýø¥3ÿþþéD3ÿþýûÁ3ÿþþîr3ÿþýûÏ4ÿþýê—4ÿþýõÍE4ÿûþûã­ 4ÿûþóÏ…5ÿýì¼Q5ÿýæ°4ÿüþà¡ 4ÿüþׇ4ÿýþÜX5ÿþÜ44ÿýþÐ"4ÿýþ½4ÿýþ·4ÿýþ» 4ÿýþ®4ÿýþŸ4ÿýþ¥ÿüþ~%8ÿýð[9ÿýÂC9ÿýþ‰(:ÿýóc:ÿþÄ&;ýÿý…<ýÿè)<þÿ¬=þûF=ÿÑ>ÿ|>ÿ# ÿÿüþ~%8ÿýð[9ÿýÂC9ÿýþ‰(:ÿýóc:ÿþÄ&;ýÿý…<ýÿè)<þÿ¬=þûF=ÿÑ>ÿ|>ÿ# ÿÿüþ~%8ÿýð[9ÿýÂC9ÿýþ‰(:ÿýóc:ÿþÄ&;ýÿý…<ýÿè)<þÿ¬=þûF=ÿÑ>ÿ|>ÿ# ÿÿûüþÝËt4ÿúüþú׿24ÿúòþóÓ«4ÿúôóþêË�5ÿùþßõüßÀ[5ÿùÜßööÚ¯5ÿøðÌà÷îÍ—5ÿù¾Õáñè¼c6÷ÿöÆÖÞëÞ«6÷ÿµãáÕÝÊ�6øô°éæÕÕ¹^7øÅÎëéÕЧ7ø³ÜëêÕLJ7ùÁåëçÕ¶U8ùÞëëäÏ¡8øûëÛÆ�8úþüæÕ¯N9úþûÜÌ“9úþùظz9ûþóÕ¡E:ûýéË‘:ûøä¾v:üñØ«>;üãÏ“;üÚÀo;ýÒ¯9<ýÅ� <ýÀo<þ³4=þ—=þ^=þ-=ÿ >ÿ>ÿ@@@@£ô ì%2¶úÿ"û[Àÿ"û 1nËÿ!üAvØÿ!ú CxÜÿ!ú7qÝÿ!ü )eÛÿ!ýXÚÿ!üGÚüÿ!ü 2Íúÿ!ü :Úøÿ!üPÜúÿ!ûbÙþÿ!ü(qÞÿ!ü3�Øÿ ü6ƒÈÿ û"L¹úÿ ü /€íÿ ýAÖÿ ý%xÿ ý!gýÿ ü'sûÿ ü+~úÿü4�øÿÿü ?�÷ÿÿü 6uñÿÿ ü!bÝÿÿ"ý CÉÿþEý&¡ýÿþHý€ìÿýRý�äÿýQ)ý’ñÿý02 ýŸýÿý6 þ"²ÿ þ6 þ,Êÿ þ0 þ9ãÿ þ"ýCõÿ þýLùÿ ÿýVùÿ ÿ ý `úÿ ÿýgûÿ#üxøÿúè�F ü Ÿÿÿ#û#¢ÿúù¥e+üuýÿ"ú /ÂÿúþÄ�E ûSñÿ"ú  CÞÿûò°d3 úIêÿ"ú sê ÿóéŽJ' @Üÿ"ú )˜ö ÿôþÒeA, LÂÿ"ú@¨ý ÿöø¦R?4-*[Çÿ"û[ºÿ÷þçzFCJhôÿ"ý pÓÿùøÁps³ÿÿ"û+�áÿúþôðþÿÿ"û1‰åÿ!ú #4�êÿ!ú*5�îÿ!ú*4’óÿ!ú(3”÷ÿ"û 2”ûÿ"û0�øÿ"û,€ñÿ"ûgèÿ"û'Ñÿ"ú¹üÿ"ý¡ãÿ"ü'¨èÿ"ü >¶úÿ"û[Àÿ"û 1nËÿ!üAvØÿ!ú CxÜÿ!ú7qÝÿ!ü )eÛÿ!ýXÚÿ!üGÚüÿ!ü 2Íúÿ!ü :Úøÿ!üPÜúÿ!ûbÙþÿ!ü(qÞÿ!ü3�Øÿ ü6ƒÈÿ û"L¹úÿ ü /€íÿ ýAÖÿ ý%xÿ ý!gýÿ ü'sûÿ ü+~úÿü4�øÿÿü ?�÷ÿÿü 6uñÿÿ ü!bÝÿÿ"ý CÉÿþEý&¡ýÿþHý€ìÿýRý�äÿýQ)ý’ñÿý02 ýŸýÿý6 þ"²ÿ þ6 þ,Êÿ þ0 þ9ãÿ þ"ýCõÿ þýLùÿ ÿýVùÿ ÿ ý `úÿ ÿýgûÿ#üxøÿúè�F ü Ÿÿÿ#û#¢ÿúù¥e+üuýÿ"ú /ÂÿúþÄ�E ûSñÿ"ú  CÞÿûò°d3 úIêÿ"ú sê ÿóéŽJ' @Üÿ"ú )˜ö ÿôþÒeA, LÂÿ"ú@¨ý ÿöø¦R?4-*[Çÿ"û[ºÿ÷þçzFCJhôÿ"ý pÓÿùøÁps³ÿÿ"û+�áÿúþôðþÿÿ"û1‰åÿ!ú #4�êÿ!ú*5�îÿ!ú*4’óÿ!ú(3”÷ÿ"û 2”ûÿ"û0�øÿ"û,€ñÿ"ûgèÿ"û'Ñÿ"ú¹üÿ"ý¡ãÿ"ü'¨èÿ"ü >¶úÿ"û[Àÿ"û 1nËÿ!üAvØÿ!ú CxÜÿ!ú7qÝÿ!ü )eÛÿ!ýXÚÿ!üGÚüÿ!ü 2Íúÿ!ü :Úøÿ!üPÜúÿ!ûbÙþÿ!ü(qÞÿ!ü3�Øÿ ü6ƒÈÿ û"L¹úÿ ü /€íÿ ýAÖÿ ý%xÿ ý!gýÿ ü'sûÿ ü+~úÿü4�øÿÿü ?�÷ÿÿü 6uñÿÿ ü!bÝÿÿ"ý CÉÿþEý&¡ýÿþHý€ìÿýRý�äÿýQ)ý’ñÿý02 ýŸýÿý6 þ"²ÿ þ6 þ,Êÿ þ0 þ9ãÿ þ"ýCõÿ þýLùÿ ÿýVùÿ ÿ ý `úÿ ÿýgûÿû�Þüþÿû¥âþþÿû=³åþûÿú`ºæù÷ÿúzÆëëõÿúŠÔëëøÿú˜ßëëûÿú6§ëóëýÿúHÇôþõþÿûXØûþþÿþkæþÿþzîþÿýÿù‘òþþñüÿù «óþýòüÿù¼óþþùþÿý&Æñþÿý/Çîþÿý7Èìþÿú=ÉëüþþÿúEÉëýþþÿýKÊóþÿýQÊýþÿþWÒþÿþ\ÌþÿýaÈôþÿúfÈëýþþÿûhÇëùþÿûlÅëøþÿûgÃëùþÿûbÂêùþÿû[ÁéùþÿûV¿èúþÿûP¼çûþÿûJ»æüþÿûH¹æþþÿûBµæþþÿû?³çþþÿû9°çþþÿü3¬èþÿü'¨çþÿü¡ãþÿü —ßþÿüƒÛþÿüiÔþÿýKÎþÿý$Åþÿý ¯üÿþˆøÿþGóÿþëÿþÖÿþ¬þÿþ�ýÿþ‚ûÿþƒûÿþy÷ÿþoïÿþqïÿþyôÿþ�ñÿþ‹íÿý—òÿýž÷ÿý6§øÿžÿýöÆæ;ÿú¿kSOŽ÷8ÿøÜkDA@Gœþ7ÿø¶6)*+EÉ7ÿ÷Ÿ**/ƒý5ÿöþ�(**KÚ5ÿõý…4¡ý5ÿö‰ !nù5ÿö”Pñ5ÿö©  7Ø5ÿþÕ6û&§5ÿýóVû nû4ÿýýn ý Sê5ÿþ–ý4Ò5ÿýÇ,þ!˜5ÿýêDýeö4ÿýþ^ý FÞ5ÿý€ ý*´5ÿüÐ2 üvü4ÿüýK ýTç5ÿýx!ý 4Â5ÿýÏ2ü ~þ4ÿýûTýTì5ÿý�!ý3´5ÿýÜ5 ü xý4ÿýùbýFä5ÿý«#ý/¤5ÿýíA ý aû4ÿýýxý?Å5ÿýÌ0ü/�þ4ÿýùPý[à5ÿý˜#ý :§5ÿýç? ü'uí4ÿýüuýA»5ÿýÇ7ü+€ñ4ÿýùhýJË4ÿüþ²,ü,‡õ4ÿýð[ ýOÌ4ÿüü¡$ü)�÷4ÿýàO ý TÍ4ÿüú� ü5€õ4ÿýÏC ýa»4ÿüù€ü?uí4ÿýÀ<ü[¤þ3ÿüün"ü 7kÞ4ÿýÞDüJ�ø4ÿý•'üVÇ4ÿýñ`ü vï4ÿýÔ<ü? þ3ÿüþ°&ýRÑ4ÿýö~ü jô4ÿýã[ ý*�4ÿüþ´? ý ?Ø4ÿüùƒ(ýbø4ÿüâeý#¸4ÿüþ–?ý Rî4ÿý¾W ý%ª4ÿýèsý Wê3ÿýþ‰"ý*®4ÿý¡-ýgó3ÿýª2ýGÉ3ÿý«-ý'’úÿžÿýöÆæ;ÿú¿kSOŽ÷8ÿøÜkDA@Gœþ7ÿø¶6)*+EÉ7ÿ÷Ÿ**/ƒý5ÿöþ�(**KÚ5ÿõý…4¡ý5ÿö‰ !nù5ÿö”Pñ5ÿö©  7Ø5ÿþÕ6û&§5ÿýóVû nû4ÿýýn ý Sê5ÿþ–ý4Ò5ÿýÇ,þ!˜5ÿýêDýeö4ÿýþ^ý FÞ5ÿý€ ý*´5ÿüÐ2 üvü4ÿüýK ýTç5ÿýx!ý 4Â5ÿýÏ2ü ~þ4ÿýûTýTì5ÿý�!ý3´5ÿýÜ5 ü xý4ÿýùbýFä5ÿý«#ý/¤5ÿýíA ý aû4ÿýýxý?Å5ÿýÌ0ü/�þ4ÿýùPý[à5ÿý˜#ý :§5ÿýç? ü'uí4ÿýüuýA»5ÿýÇ7ü+€ñ4ÿýùhýJË4ÿüþ²,ü,‡õ4ÿýð[ ýOÌ4ÿüü¡$ü)�÷4ÿýàO ý TÍ4ÿüú� ü5€õ4ÿýÏC ýa»4ÿüù€ü?uí4ÿýÀ<ü[¤þ3ÿüün"ü 7kÞ4ÿýÞDüJ�ø4ÿý•'üVÇ4ÿýñ`ü vï4ÿýÔ<ü? þ3ÿüþ°&ýRÑ4ÿýö~ü jô4ÿýã[ ý*�4ÿüþ´? ý ?Ø4ÿüùƒ(ýbø4ÿüâeý#¸4ÿüþ–?ý Rî4ÿý¾W ý%ª4ÿýèsý Wê3ÿýþ‰"ý*®4ÿý¡-ýgó3ÿýª2ýGÉ3ÿý«-ý'’úÿžÿýöÆæ;ÿú¿kSOŽ÷8ÿøÜkDA@Gœþ7ÿø¶6)*+EÉ7ÿ÷Ÿ**/ƒý5ÿöþ�(**KÚ5ÿõý…4¡ý5ÿö‰ !nù5ÿö”Pñ5ÿö©  7Ø5ÿþÕ6û&§5ÿýóVû nû4ÿýýn ý Sê5ÿþ–ý4Ò5ÿýÇ,þ!˜5ÿýêDýeö4ÿýþ^ý FÞ5ÿý€ ý*´5ÿüÐ2 üvü4ÿüýK ýTç5ÿýx!ý 4Â5ÿýÏ2ü ~þ4ÿýûTýTì5ÿý�!ý3´5ÿýÜ5 ü xý4ÿýùbýFä5ÿý«#ý/¤5ÿýíA ý aû4ÿýýxý?Å5ÿýÌ0ü/�þ4ÿýùPý[à5ÿý˜#ý :§5ÿýç? ü'uí4ÿýüuýA»5ÿýÇ7ü+€ñ4ÿýùhýJË4ÿüþ²,ü,‡õ4ÿýð[ ýOÌ4ÿüü¡$ü)�÷4ÿýàO ý TÍ4ÿüú� ü5€õ4ÿýÏC ýa»4ÿüù€ü?uí4ÿýÀ<ü[¤þ3ÿüün"ü 7kÞ4ÿýÞDüJ�ø4ÿý•'üVÇ4ÿýñ`ü vï4ÿýÔ<ü? þ3ÿüþ°&ýRÑ4ÿýö~ü jô4ÿýã[ ý*�4ÿüþ´? ý ?Ø4ÿüùƒ(ýbø4ÿüâeý#¸4ÿüþ–?ý Rî4ÿý¾W ý%ª4ÿýèsý Wê3ÿýþ‰"ý*®4ÿý¡-ýgó3ÿýª2ýGÉ3ÿý«-ý'’úÿÿÿõþG  +IƒõÿþÍ"þSÿ÷€-/RuÊýÿþÈ þOÿùëwJdn óÿþÅþAÿüõľé!ÿþºþ@-ÿþ®þ!>-ÿþ þ"'-ÿþŒþ#-ÿþ} ÿ& -ÿþn ÿ+ ,ÿýüaþ) ,ÿý÷Wþ" ,ÿýòLþ# ,ÿýèEþ% ,ÿþÝ6þ  ,ÿþÇ&þ  ,ÿþ±ÿ ,ÿþŸÿ +ÿýý…ÿ +ÿýùb þ +ÿýÞE þ +ÿý»2þ  +ÿýš&þ *ÿý÷eþ *ÿýÏ7 ý *ÿý©ý )ÿýý~þ )ÿýã^  )ÿý·: )ÿþ‰ (ÿýöd  (ÿýÐ7 (ÿþ  'ÿýýh 'ÿýöG 'ÿýè3'ÿýº'&ÿýþ‡&ÿýül&ÿýùX&ÿýöK&ÿýñC&ÿþá3%ÿýþÈ%%ÿýý¯%ÿýüš%ÿýû�%ÿýúp %ÿýùb%ÿýôT%ÿýïJ%ÿþé>%ÿþÛ/%ÿþÐ$%ÿþÄ%ÿþ±%ÿþ�$ÿýþ… $ÿýýv$ÿýüd$ÿýûW$ÿýûN$ÿý÷F  $ÿþñ=  $ÿþè1  ÿõþG  +IƒõÿþÍ"þSÿ÷€-/RuÊýÿþÈ þOÿùëwJdn óÿþÅþAÿüõľé!ÿþºþ@-ÿþ®þ!>-ÿþ þ"'-ÿþŒþ#-ÿþ} ÿ& -ÿþn ÿ+ ,ÿýüaþ) ,ÿý÷Wþ" ,ÿýòLþ# ,ÿýèEþ% ,ÿþÝ6þ  ,ÿþÇ&þ  ,ÿþ±ÿ ,ÿþŸÿ +ÿýý…ÿ +ÿýùb þ +ÿýÞE þ +ÿý»2þ  +ÿýš&þ *ÿý÷eþ *ÿýÏ7 ý *ÿý©ý )ÿýý~þ )ÿýã^  )ÿý·: )ÿþ‰ (ÿýöd  (ÿýÐ7 (ÿþ  'ÿýýh 'ÿýöG 'ÿýè3'ÿýº'&ÿýþ‡&ÿýül&ÿýùX&ÿýöK&ÿýñC&ÿþá3%ÿýþÈ%%ÿýý¯%ÿýüš%ÿýû�%ÿýúp %ÿýùb%ÿýôT%ÿýïJ%ÿþé>%ÿþÛ/%ÿþÐ$%ÿþÄ%ÿþ±%ÿþ�$ÿýþ… $ÿýýv$ÿýüd$ÿýûW$ÿýûN$ÿý÷F  $ÿþñ=  $ÿþè1  ÿõþG  +IƒõÿþÍ"þSÿ÷€-/RuÊýÿþÈ þOÿùëwJdn óÿþÅþAÿüõľé!ÿþºþ@-ÿþ®þ!>-ÿþ þ"'-ÿþŒþ#-ÿþ} ÿ& -ÿþn ÿ+ ,ÿýüaþ) ,ÿý÷Wþ" ,ÿýòLþ# ,ÿýèEþ% ,ÿþÝ6þ  ,ÿþÇ&þ  ,ÿþ±ÿ ,ÿþŸÿ +ÿýý…ÿ +ÿýùb þ +ÿýÞE þ +ÿý»2þ  +ÿýš&þ *ÿý÷eþ *ÿýÏ7 ý *ÿý©ý )ÿýý~þ )ÿýã^  )ÿý·: )ÿþ‰ (ÿýöd  (ÿýÐ7 (ÿþ  'ÿýýh 'ÿýöG 'ÿýè3'ÿýº'&ÿýþ‡&ÿýül&ÿýùX&ÿýöK&ÿýñC&ÿþá3%ÿýþÈ%%ÿýý¯%ÿýüš%ÿýû�%ÿýúp %ÿýùb%ÿýôT%ÿýïJ%ÿþé>%ÿþÛ/%ÿþÐ$%ÿþÄ%ÿþ±%ÿþ�$ÿýþ… $ÿýýv$ÿýüd$ÿýûW$ÿýûN$ÿý÷F  $ÿþñ=  $ÿþè1  4ÿþþ¯4ÿþý¤4ÿþû•4ÿþû’4ÿþû”4ÿþù†4ÿþõu4ÿþóe4ÿþõX4ÿþîI4ÿþäB4ÿýà?4ÿýß<4ÿýÍ74ÿý¸03ÿüý³(3ÿüû¯3ÿýò¬ 3ÿýà§ 3ÿýÙž2ÿüþו2ÿüüÏ—2ÿüøÆ§2ÿüõô2ÿüòλ2ÿûòèá~2ÿþýöÀ92ÿþüýáž2ÿþüïÔ 1ÿþûúìÓ| 1ÿþûýó×… 1ÿþüøÜ‘ 1ÿþúüä›0ÿ þûýè§0ÿ þýì±#0ÿ þþïº0ÿ þÿó0ÿþ0ÿþ0ÿþ0ÿþ/ÿþ/ÿþ/ÿþ/ÿþ.ÿþ.ÿþ.ÿþ.ÿþ.ÿþ-ÿþ-ÿþ-ÿþûýøõôù þ-ÿþþùëýÚÙö þ-ÿ÷þýðëëÞÕáúþ-ÿþùöïêרíýþ.ÿþûóàÕãõþ/ÿþúùåÕÛìýþ/ÿþúûéÕÕéöþ0ÿþûë×ÕÛñþ0ÿþûîÛÕÕÞþ1ÿþûñçÞÜíþ1ÿþûóëëðüþ1ÿþýýøúþÀÿ4>þÀL=ýôÇR<üþöËT;þüøÒV9þüùÔX8þüú×V.÷(Oh‚�—œ¨þüûÚU&ð 2e‘¨ºÃÎÖÙÝÞàòþüüÜTñ 5Y|�¤µÈØàåéëþìöþüýß\é'Io‡˜¬ÀÎØàéîñòóõ÷ùúü þûýái ð9eФ¿Öãìó÷ùüýþûëšõ 2q›¹Íáðøû!þæøÅ�Y8+($ ")2;X{žºÍÜçîôøü&þíýèǼ¸³±³¶»ÁÊ×áëò÷ùü.þöùöôóóô÷ùûý§þÿú>þÿý3þÿý0þþùýþúýôîÞÝñ/þþóôþúñëÜÈÏí þú@@@)û ü¥¨««©õ¨¤£žŽu_? çùëèéëîòóõøùúúù÷õóñíéܸtþæ÷ëëçÖÕÕÐÈÍÛäëïððòñïéÝÒ¯dþúøëëæÜ×ÕòÖÕÃÀÃÆÃ¾¼¼°Ž=þÿñëûèäßÜ×Õðξ´°±¸¼º¢�Ž^"þÿóëúéá×Õϼ÷¨…Š�zW4þþýíëäíðóóñíëçÖÕÕÎü¹Ÿ“ž�`II&þþýòëüîûþûþêýïëçÞÜÙÖÐŰŸŸŽ`II-þùíëúóýþðëíþüûöóñëôÞÕǯŸ”bIFõëÿöþý÷ðõþò÷ëëä×μ°ŸrFëþìùþüýííùþþûòëòéʼ¸§oóñõýþíøëëíþþóäæëëåÖ¿´Ÿƒ þÿùëòûýëØÕÔÀ¬«­¥v þñïëëòóáÕγŠ||vL  þòûñëëçÕÈ´“zvR þ÷üæÕÁ³‘oG þÿýþøüèɱ“]# þõùïñþùíϰ‚3þõúóìèåáÛäW þõöä×ÓÎËõ”0õþóäо¼»¬€!÷÷ëè×È«sùëäÔ¾y=ýk:û C�¼Þ8ù_ŸËëøþ6ú MŸÏïûþ4ú9”Ééùþ3û W­Ûóþ3ü6­ßòþ2û^Áìýþ3ýCÂíþ3ü Žãõþ4ûÕìúþ5ù9”Åéöûþ7ø5…§Èáðö9ú"UŸ¼<ý3 @ è I„ž³ÇÓÜãçêëíïðòóôõöö ÷öïóñíéäÞØÏÁµ§ž”~Z8õ(b´Þïöùûý"þôöñïìæáÜÖÐÈ¿¶ù ,c©Þôü.þòýüúùøöó@´Øîù8þû¤Íé÷ý:þþóû¾þÿú>þúÒßëôùý9þõW~”¤¿Ðáìôùý4þí #6MkލÁÖáëñöùü,þí!.>Yx˜¶ÏÞéïôùû$þì &6Heƒ¥ÃØãìó÷úýþî $4KoްÌÝèðõùýþ"ð$Ep‹¬Å×ãíôú þ+ô@q“¬Éàïøþ1ò0P�Ÿ¿Üîøýþþ5ö 7WžÌå9ú #A=þÀ ÿýnûÿ ýqüÿ!ýsýÿ!ýnüÿ!ýbëÿ!ýPÖÿ"ý7¬ÿ"ývîÿ"ýGÀÿ"ü+vìÿ#üA«þÿ#ü$`æÿ$ü 4‹ûÿ$üIÂÿ%ü"båÿ%ü*€úÿ%ü 5¢þÿ&ýEÁÿ'üX× ÿ(ü!pç ÿ(ü+‡ï ÿ)ü 7™ö ÿ*ü >¦ú ÿ+ü A±ýÿ,ü E¹üÿ,ü IÀúÿ-ü KÉüÿ.ü NËþÿ.ý OÌÿ/ý >Åÿ0ú)©þÿÿ1ûcèü1 ý=2 ÿ3 4 5 677665þ4ý 'X3ûAk¡2ù *^�Ûý1øK~Éùÿÿ0ú9n³òÿ0ú#]�èþÿ/úA‰Øýÿ,û )nÆúÿ(ûX¬ö ÿ%ù NŸíþ ÿ$ø :n¦äý ÿ"÷">X€ºïüÿ!ö 1OnšÍóþÿ ö @^~ºðýÿø "Dh”ãùÿø=bšÔ÷þÿù-R‡Íñýÿú0g�á÷ÿû`¨Ôûÿü,‹¶÷ÿûX¡ùÿ ÿýnûÿ ýqüÿ!ýsýÿ!ýnüÿ!ýbëÿ!ýPÖÿ"ý7¬ÿ"ývîÿ"ýGÀÿ"ü+vìÿ#üA«þÿ#ü$`æÿ$ü 4‹ûÿ$üIÂÿ%ü"båÿ%ü*€úÿ%ü 5¢þÿ&ýEÁÿ'üX× ÿ(ü!pç ÿ(ü+‡ï ÿ)ü 7™ö ÿ*ü >¦ú ÿ+ü A±ýÿ,ü E¹üÿ,ü IÀúÿ-ü KÉüÿ.ü NËþÿ.ý OÌÿ/ý >Åÿ0ú)©þÿÿ1ûcèü1 ý=2 ÿ3 4 5 677665þ4ý 'X3ûAk¡2ù *^�Ûý1øK~Éùÿÿ0ú9n³òÿ0ú#]�èþÿ/úA‰Øýÿ,û )nÆúÿ(ûX¬ö ÿ%ù NŸíþ ÿ$ø :n¦äý ÿ"÷">X€ºïüÿ!ö 1OnšÍóþÿ ö @^~ºðýÿø "Dh”ãùÿø=bšÔ÷þÿù-R‡Íñýÿú0g�á÷ÿû`¨Ôûÿü,‹¶÷ÿûX¡ùÿ ÿýnûÿ ýqüÿ!ýsýÿ!ýnüÿ!ýbëÿ!ýPÖÿ"ý7¬ÿ"ývîÿ"ýGÀÿ"ü+vìÿ#üA«þÿ#ü$`æÿ$ü 4‹ûÿ$üIÂÿ%ü"båÿ%ü*€úÿ%ü 5¢þÿ&ýEÁÿ'üX× ÿ(ü!pç ÿ(ü+‡ï ÿ)ü 7™ö ÿ*ü >¦ú ÿ+ü A±ýÿ,ü E¹üÿ,ü IÀúÿ-ü KÉüÿ.ü NËþÿ.ý OÌÿ/ý >Åÿ0ú)©þÿÿ1ûcèü1 ý=2 ÿ3 4 5 677665þ4ý 'X3ûAk¡2ù *^�Ûý1øK~Éùÿÿ0ú9n³òÿ0ú#]�èþÿ/úA‰Øýÿ,û )nÆúÿ(ûX¬ö ÿ%ù NŸíþ ÿ$ø :n¦äý ÿ"÷">X€ºïüÿ!ö 1OnšÍóþÿ ö @^~ºðýÿø "Dh”ãùÿø=bšÔ÷þÿù-R‡Íñýÿú0g�á÷ÿû`¨Ôûÿü,‹¶÷ÿûX¡ùÿýýJ¸ùÿô«¡”€jT9$ýkËûÿåðìèáÙÍÁ´¨ž”ƒr`I4#ü›ÞþÿþäüùõðêáÙÓËÁ³¨¢›‘…zmb\YPYt—Æôþÿ þêýú÷ôñíèäáßÝÛØÖÔÕÞéòùþþÿþ÷ûù÷øøùùúýþÿ"þÿ"þÿ"þÿ"þÿ#þÿ#þÿ$þÿ$þÿ%þÿ%þÿ%þÿ&þÿ'þÿ(þÿ(þÿ)þÿ*þÿ+þÿ,þÿ,þÿýòùü*þÿøb�¿Ùêóùü&þÿó.Ko§ÉÞìôùý!þÿó/J|¬Ìáíöúþÿ õ*J|¬Ìáîöûþÿô'Cs�Åßíôùûþ ÿð# üZ×+ÿýý¶F ýKÃ+ÿüû­J ý 9¦+ÿüù­R ý'…+ÿüü¾l þe,ÿýô… þ?,ÿýýÉ$ÿ-ÿýðp ÿ-ÿüþ°2 ÿ.ÿýÃA ÿ.ÿýº7 ÿ-ÿüþŸ# ÿ -ÿý÷l ÿ-ÿýÏG ,ÿüúz!,ÿýÑJÿùþøâÖÏÉïÿýüh ÿöúäÆ˜jG/'CÑÿý©9 ÿòøàÁ–a@0 /ƒþÿýîRÿìþöëÙÈ®�X=( 0súÿýýw%ñ]eklhb^[O>,úK�öÿý¯@õ ú >xÊ÷ÿýØX$ø$R~»óýÿüäp"÷'GqŸÈñýÿüë‹!ô (Ca‡¨Ëîýÿüû©< ó$:Jez�ÀàòûþÿûþÌ]õ2Fb~¡Èéúþÿüév!÷)Gg‰Âêüÿü÷”7 ø !Dj”Òúÿüü¹Rù>e”Òøÿûþßgû:`…Ãôÿüò€1ývµîÿüùšA þéþÿüüºWÿûþØe!ÿüîv/ÿüù‹< ÿüü©JÿûþÍXÿüâh%ÿüò~1ÿüù’< ÿüý«GÿüÄSÿüÖ`#ÿüÞl,ÿüèx5 ÿüîƒ>  ÿüô�E ÿüøšK ÿûû§L  ÿûý¯L& ÿûý°L* 0ÿý­)ýdê1ÿüõŸ!üLÃü/ÿüûšTý7�ø.ÿüþo1 ý(€õ.ÿý©2 ýkñ-ÿýûƒ ý]ç-ÿýöx ýSÖ-ÿýöp ýGÀ-ÿýë[ ü<¯þ,ÿýÞK ü 1šü,ÿþØC ü(€ò,ÿþË= ü"kã,ÿþÀ> üZ×+ÿýý¶F ýKÃ+ÿüû­J ý 9¦+ÿüù­R ý'…+ÿüü¾l þe,ÿýô… þ?,ÿýýÉ$ÿ-ÿýðp ÿ-ÿüþ°2 ÿ.ÿýÃA ÿ.ÿýº7 ÿ-ÿüþŸ# ÿ -ÿý÷l ÿ-ÿýÏG ,ÿüúz!,ÿýÑJÿùþøâÖÏÉïÿýüh ÿöúäÆ˜jG/'CÑÿý©9 ÿòøàÁ–a@0 /ƒþÿýîRÿìþöëÙÈ®�X=( 0súÿýýw%ñ]eklhb^[O>,úK�öÿý¯@õ ú >xÊ÷ÿýØX$ø$R~»óýÿüäp"÷'GqŸÈñýÿüë‹!ô (Ca‡¨Ëîýÿüû©< ó$:Jez�ÀàòûþÿûþÌ]õ2Fb~¡Èéúþÿüév!÷)Gg‰Âêüÿü÷”7 ø !Dj”Òúÿüü¹Rù>e”Òøÿûþßgû:`…Ãôÿüò€1ývµîÿüùšA þéþÿüüºWÿûþØe!ÿüîv/ÿüù‹< ÿüü©JÿûþÍXÿüâh%ÿüò~1ÿüù’< ÿüý«GÿüÄSÿüÖ`#ÿüÞl,ÿüèx5 ÿüîƒ>  ÿüô�E ÿüøšK ÿûû§L  ÿûý¯L& ÿûý°L* 0ÿý­)ýdê1ÿüõŸ!üLÃü/ÿüûšTý7�ø.ÿüþo1 ý(€õ.ÿý©2 ýkñ-ÿýûƒ ý]ç-ÿýöx ýSÖ-ÿýöp ýGÀ-ÿýë[ ü<¯þ,ÿýÞK ü 1šü,ÿþØC ü(€ò,ÿþË= ü"kã,ÿþÀ> üZ×+ÿýý¶F ýKÃ+ÿüû­J ý 9¦+ÿüù­R ý'…+ÿüü¾l þe,ÿýô… þ?,ÿýýÉ$ÿ-ÿýðp ÿ-ÿüþ°2 ÿ.ÿýÃA ÿ.ÿýº7 ÿ-ÿüþŸ# ÿ -ÿý÷l ÿ-ÿýÏG ,ÿüúz!,ÿýÑJÿùþøâÖÏÉïÿýüh ÿöúäÆ˜jG/'CÑÿý©9 ÿòøàÁ–a@0 /ƒþÿýîRÿìþöëÙÈ®�X=( 0súÿýýw%ñ]eklhb^[O>,úK�öÿý¯@õ ú >xÊ÷ÿýØX$ø$R~»óýÿüäp"÷'GqŸÈñýÿüë‹!ô (Ca‡¨Ëîýÿüû©< ó$:Jez�ÀàòûþÿûþÌ]õ2Fb~¡Èéúþÿüév!÷)Gg‰Âêüÿü÷”7 ø !Dj”Òúÿüü¹Rù>e”Òøÿûþßgû:`…Ãôÿüò€1ývµîÿüùšA þéþÿüüºWÿûþØe!ÿüîv/ÿüù‹< ÿüü©JÿûþÍXÿüâh%ÿüò~1ÿüù’< ÿüý«GÿüÄSÿüÖ`#ÿüÞl,ÿüèx5 ÿüîƒ>  ÿüô�E ÿüøšK ÿûû§L  ÿûý¯L& ÿûý°L*  ÷ÿþ<ÿþ;ÿþ;ÿþ;ÿþ:ÿþ:ÿþ9ÿþ9ÿþ9ÿþ8ÿþ8ÿþ8ÿþ7ÿþ7ÿþ5ÿ þ5ÿ þ4ÿ þ4ÿ þ4ÿ þ3ÿ þÿ$ÿþÜ)  $ÿþÎ"  $ÿþ¸  $ÿþ   $ÿþŒ  $ÿþ{  $ÿþp  #ÿýþd  #ÿýýW  #ÿýüO  #ÿýøG  #ÿýò?  #ÿýë8  #ÿýÝ2  ÿþ"ÿýÏ,  ÿø"ÿþ½(  ÿé"ÿþ«!  ÿÎ"ÿþœ  þ¡þ!ÿþ‰  þ~ú ÿýþz  þnö ÿýüj  þbÜ ÿýû^  þO¹ ÿýúR  þ=œ ÿý÷J  ý,ƒøÿýðC  ý`áÿýã>  ýA¾ÿýÔ7  ü/˜üÿýÃ1  ü#nïÿý­,  üPÜÿüþš(  ûDÍûÿýü‡"  û=Àöÿýûu  û 9¶ðÿýùh  û9¶óÿýøV  û:¾ùÿýóJ  û9Ãþÿýç@  ü8ÊÿýÖ9  ü8ÒÿýÄ1  ü7×ÿüþ¯*  ü6Õÿýý�# þ6Ïÿýûu þ4Éÿýú^ þ1ÂÿýóL þ-¼ÿýçC  þ+¶ÿýÙ9  þ)°ÿýÆ/ þ&ªÿýª' þ$£ÿüý�! þ!�ÿýôu þ –ÿýç` ý’þÿýÜNÿ ý�ýÿýÒA   ý‰þÿýÄ4 þ  þ„ÿýµ*þ  þ�ÿüþŸ#þ þyÿýý‹ÿ  þuÿýüvÿ þnÿý÷d þ jÿýëS  þ eÿýÔC  þ `ÿýÂ6 ý S÷ÿý³, ýGäÿý±, ý9ÁÿþÃ+ $ÿþÜ)  $ÿþÎ"  $ÿþ¸  $ÿþ   $ÿþŒ  $ÿþ{  $ÿþp  #ÿýþd  #ÿýýW  #ÿýüO  #ÿýøG  #ÿýò?  #ÿýë8  #ÿýÝ2  ÿþ"ÿýÏ,  ÿø"ÿþ½(  ÿé"ÿþ«!  ÿÎ"ÿþœ  þ¡þ!ÿþ‰  þ~ú ÿýþz  þnö ÿýüj  þbÜ ÿýû^  þO¹ ÿýúR  þ=œ ÿý÷J  ý,ƒøÿýðC  ý`áÿýã>  ýA¾ÿýÔ7  ü/˜üÿýÃ1  ü#nïÿý­,  üPÜÿüþš(  ûDÍûÿýü‡"  û=Àöÿýûu  û 9¶ðÿýùh  û9¶óÿýøV  û:¾ùÿýóJ  û9Ãþÿýç@  ü8ÊÿýÖ9  ü8ÒÿýÄ1  ü7×ÿüþ¯*  ü6Õÿýý�# þ6Ïÿýûu þ4Éÿýú^ þ1ÂÿýóL þ-¼ÿýçC  þ+¶ÿýÙ9  þ)°ÿýÆ/ þ&ªÿýª' þ$£ÿüý�! þ!�ÿýôu þ –ÿýç` ý’þÿýÜNÿ ý�ýÿýÒA   ý‰þÿýÄ4 þ  þ„ÿýµ*þ  þ�ÿüþŸ#þ þyÿýý‹ÿ  þuÿýüvÿ þnÿý÷d þ jÿýëS  þ eÿýÔC  þ `ÿýÂ6 ý S÷ÿý³, ýGäÿý±, ý9ÁÿþÃ+ $ÿþÜ)  $ÿþÎ"  $ÿþ¸  $ÿþ   $ÿþŒ  $ÿþ{  $ÿþp  #ÿýþd  #ÿýýW  #ÿýüO  #ÿýøG  #ÿýò?  #ÿýë8  #ÿýÝ2  ÿþ"ÿýÏ,  ÿø"ÿþ½(  ÿé"ÿþ«!  ÿÎ"ÿþœ  þ¡þ!ÿþ‰  þ~ú ÿýþz  þnö ÿýüj  þbÜ ÿýû^  þO¹ ÿýúR  þ=œ ÿý÷J  ý,ƒøÿýðC  ý`áÿýã>  ýA¾ÿýÔ7  ü/˜üÿýÃ1  ü#nïÿý­,  üPÜÿüþš(  ûDÍûÿýü‡"  û=Àöÿýûu  û 9¶ðÿýùh  û9¶óÿýøV  û:¾ùÿýóJ  û9Ãþÿýç@  ü8ÊÿýÖ9  ü8ÒÿýÄ1  ü7×ÿüþ¯*  ü6Õÿýý�# þ6Ïÿýûu þ4Éÿýú^ þ1ÂÿýóL þ-¼ÿýçC  þ+¶ÿýÙ9  þ)°ÿýÆ/ þ&ªÿýª' þ$£ÿüý�! þ!�ÿýôu þ –ÿýç` ý’þÿýÜNÿ ý�ýÿýÒA   ý‰þÿýÄ4 þ  þ„ÿýµ*þ  þ�ÿüþŸ#þ þyÿýý‹ÿ  þuÿýüvÿ þnÿý÷d þ jÿýëS  þ eÿýÔC  þ `ÿýÂ6 ý S÷ÿý³, ýGäÿý±, ý9ÁÿþÃ+ 2ÿ þ2ÿ þ2ÿ þ2ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ4ÿ þ4ÿ þ4ÿ þ4ÿ þ4ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ1ÿ þ1ÿ þ1ÿ þ1ÿ þ1ÿ þ1ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ2ÿ þ$þýíëûþúý÷ñëòùþþûë#þûúëëîûþýöâÏ"þùùæàãëëõþûúëÒ¶Aþý÷óúþ÷îÓÊÌÔÝÛêóþú÷Ö±eþþüïëþðùþõùÓ¾»º¾ÊÔÝêü þùúרAþöýîãרÛâêïùþôêűŸ�¢¶¾ÉÖëú þüó´SþüóæÕÔÕíÖÝäïõï×¶Ÿ•z“Ÿ«³¼ÐÝñ þýñ”+þúìÚǼÁÐÕùÇ¥Ÿ‘z|›Ÿü¡Âáû þý÷¤* þýùöúþý÷çÓ¼þ¾ÒÕðÔ¬ŸŸ˜zz~ƒŠ�Ÿ¨Ãó þýÛM þÿøëÿôþüøìãÔ¼ôÇÕÖÕÕŤЉ˜Ÿ~zû‡¨¼Ýù þúù¹2 þþúîëøîóîëëå×Á¼ôËÝæÖÕ”YPv˜‘zû�ºÒëü þûï«8 þüñëêéëîâʼ¼ÃÖìûöÖÆ§`IU…Ÿƒzù~…—¬Ïåò þüõÀJ þúüëàÖÕÜëäç×ÊÉØêûþýäÓ»Že^|ŸŸ—�«­­°»Ïáí þýúØ‚ þúôë×ÕÕàëûéãäîüþíîäι—‡“Ÿ¤³¼ÃǾ¼Îßë þþýê þõîëàáèðóøûýýþõøëå×Á¯«±¿ËÓÕûÓÉÔçïþüùôõùþîøëëæãààââáâåçèêëë÷!þþúï þÿùëõíöýõëùõëëïù"þþöó þñùëÞëëîýþþüëóëëó)þý÷ðõþúùë×ãëùþÿúëÿùþüûø÷ü þüöëëúþöùëáäëòøú÷ïëÿ÷þÿîëþñüþýóëóþÿîë÷éæääåæéëñþÿîëÿüþþüóþûúìëãØÕü×åëûþþùìëÿ÷þ÷÷ëÚÕÕÖãçÛÕþèóþúüóíëñýþúùëÞÕÕÝëûáÛÛæì1þúöëæÕÕãëÿù0þûóëëåâëÿò0þÿïëúìõûüûóëÿïþÿúþëþóýþüöëëîþÿõþûúêëëðþüýëëöþÿöþû÷ÚÛèðþ÷þþøôþÿý þÿýþúóØÕÖéýþýöëñþûôëëíöþúîáÕÕÜøþþõóþûñëäãîþùýëëåßßóþýüìúþúðëÜÕäýþÿùëÿôþþ÷õþùúìëàÕÛùþùöëàÖæëú+þÿöëüéÖÚ÷þúôëÙÕÞí,þÿûëýÞÚôþúõëÖÕâô-þùúîëëéÞòþúùëØÛëü/þûöòíïùþüïäëô;þýýóô±þÿý<þüùìëö;þýùëó=þÿúõþýòïü<þýòëô1þþüéþýý÷ü/þüýóÔŸ9þúýóÖ¥P7þøýôÛ¬Y5þùýõß¶k4þú÷äÂ|2þùýíÌ�#1þûøÚž,.þúúôç±? +þùùíÍ¡t! )þùýðËŸo#(þúù溌@ 'þúôÜ©m%þúýïÓšN@@@ûÜÆˆýœ(ÿIÿþ°3üõÏwûþúä¨"þüýòÇVþýù×nþüûâ„þüüê� þüýî°"þýòÁA þüóÐp þüòÙ› þûñÞ´B þùýðàÀm þùýìãÊŽ& þùõëèך+ þùýíëëà“ þÿõëýâk þÿûëýä³6 þúîëëæÇq þÿùëýçž þûòèÞݰ þûýèÎÇœ þüøÝº$ þýôÒŒ þüöÞ§ þüøÞ±$ þüù߯; þüõؤC þüðÍ“7 þûøæ¿& þûîÙ¯g þûïË”Aþûüç´kþûóБ@þû÷Þ°`øþüöäÀ|/úîßÉ�4üÍ�= þHÿ‹ @ @ @ @ @ @ @ @ûT�óþÿû1|Çøÿ÷3[¶ùöòøÿ ë,8@GUg}—·ÂÂÁÃÅÇÃðøÿ ù   ýTÞÿýOÓÿü ý RÓÿú4R5ýSÅÿ÷S‰¶g*ý üN¨þÿ÷N�çñÏG û"F=üúEŠÎäãü7kÈñþWú]¨�@û4a2û(AWR÷I|Ðýþöbù5xìçXúd»q( ü ÷?qÒûÿûbù S�ññdù:…ö²/÷ W­îöøWù`¡ñôsù F¡ÿÑ6÷ +Z±ä¾?ùbŸòø�ùL�÷Á1ù*=`GùS�îõxù Aƒ×�)ûù8ƒ¾Íjú G`1 ù S‡’Sü û &GP&ü  !!"$ÿ&þ/ŽûT�óþÿû1|Çøÿ÷3[¶ùöòøÿ ë,8@GUg}—·ÂÂÁÃÅÇÃðøÿ ù   ýTÞÿýOÓÿü ý RÓÿú4R5ýSÅÿ÷S‰¶g*ý üN¨þÿ÷N�çñÏG û"F=üúEŠÎäãü7kÈñþWú]¨�@û4a2û(AWR÷I|Ðýþöbù5xìçXúd»q( ü ÷?qÒûÿûbù S�ññdù:…ö²/÷ W­îöøWù`¡ñôsù F¡ÿÑ6÷ +Z±ä¾?ùbŸòø�ùL�÷Á1ù*=`GùS�îõxù Aƒ×�)ûù8ƒ¾Íjú G`1 ù S‡’Sü û &GP&ü  !!"$ÿ&þ/ŽûT�óþÿû1|Çøÿ÷3[¶ùöòøÿ ë,8@GUg}—·ÂÂÁÃÅÇÃðøÿ ù   ýTÞÿýOÓÿü ý RÓÿú4R5ýSÅÿ÷S‰¶g*ý üN¨þÿ÷N�çñÏG û"F=üúEŠÎäãü7kÈñþWú]¨�@û4a2û(AWR÷I|Ðýþöbù5xìçXúd»q( ü ÷?qÒûÿûbù S�ññdù:…ö²/÷ W­îöøWù`¡ñôsù F¡ÿÑ6÷ +Z±ä¾?ùbŸòø�ùL�÷Á1ù*=`GùS�îõxù Aƒ×�)ûù8ƒ¾Íjú G`1 ù S‡’Sü û &GP&ü  !!"$ÿ&þ/Žü…Áóþ#ÿûNŸÝýþ%ÿü v¿íþ&ÿù4•Ïøþþ'ÿùW¬ßýþþ'ÿü~¿îþ'ÿû”Ëöþ'ÿû-¨Öùþ'ÿû <±Üûþ'ÿü D¶áþ'ÿü I¼æþ'ÿüWÁåþ'ÿüA¾áþ'ÿû7¶ßüþ'ÿü-¯Ýùþ'ÿü”Îôþ&ÿû ^¿ãüþ%ÿü-§Ïóþ!ÿû bºáöþ ÿú �Âàðüþÿ÷ƒµØêó÷üþÿò7o­ÈØàêóøüþþÿó "2De‰¥»Ëáñýÿô(2U|§Êïýÿô $o¬Øõþÿ ø R§áíúþÿþ÷ P�×ëëùþÿ þ÷0g�ÉçóüþøNµØñüþù%^˜Ììüþú+r¶åùþû!…Ïóþü‘Û÷ þü ‡×ù!þümÈõ"þý³ê#þüzÊø#þû©Øû#þû:ºÚü#þû K­Ëí#þú7¥Íæù!þø k¶Ïéøþ÷(~°ÆØîùþó #\�¥¶ÈÞèïõø÷öôõôóóðíëæâÝ×Îò (AWbbkt‡ŠŒ‘òŒƒ~xoe^UI>7/*% þ ö ,ù  9ü Vÿûù¨L-  ÿûó›J+  ÿûë‹E$ ÿûßv= ÿûüÅ]* ÿûòšFÿúûÍl-ÿúúÖz< ÿøýöÏzF! ÷ßÇ�Z7! úE1 ÿ! "$')+. 1 48;>ÿ>ÿ€ÿûù¨L-  ÿûó›J+  ÿûë‹E$ ÿûßv= ÿûüÅ]* ÿûòšFÿúûÍl-ÿúúÖz< ÿøýöÏzF! ÷ßÇ�Z7! úE1 ÿ! "$')+. 1 48;>ÿ>ÿ€ÿûù¨L-  ÿûó›J+  ÿûë‹E$ ÿûßv= ÿûüÅ]* ÿûòšFÿúûÍl-ÿúúÖz< ÿøýöÏzF! ÷ßÇ�Z7! úE1 ÿ! "$')+. 1 48;>ÿ>ÿ€,ÿ þ1ÿ þ0ÿþþü÷þ/ÿ þþôîþ-ÿ þþóíþ,ÿ þþ÷ðþ)ÿþÿùþ(ÿþ&ÿþ%ÿþ#ÿþ"ÿþ!ÿþÿþÿ þÿ"þÿ$þÿ'þÿ)þÿ+þÿ.þ ÿ1þ ÿ4þÿ8þÿ;þÿ>þÿÿ>þÿÿ©þøüùõóñòöû1þïýùôîæÛÓÍÌÊÈÈÎÙàïû'þæüùöòëâÖŸ«—ƒ{tne_URRm“¶ÐëùþíûùöñìäØÊ¸¢‘~i\P0øI¤ÓíýþïýúùõòìåÛÌ¿­�ŽvUH:3ü4- ú\¿áùþæüûùùöóïëãÛο­ž‘~cH5$ û4ŸÓóþðž´«¢”ƒoY<+ %ú%Âèüþû  þ +úb©Üùþü .øFŒÅîûþÿ1ú$k¤Õñ;ü&e¢=þü0¡ûÿýÛ2 ýlæÿýùT üA¹üÿýûx ü*€çÿüý±# üI»ûÿýä= ü'�çÿýùj üDÃûÿüþ®$ ü"�æÿýæE ý@Îÿýûx ü (ªüÿý½( üqìÿýìK ýO×ÿýþƒ ý 6ÓÿýÈ+ ý'ÑÿýðS ý#ÈÿþŽý'ÂÿýÐ1ý-Ôÿýó[ ýAäÿþ—ýZíÿýÔ4ý&~÷ÿýô^ü ?£üÿýþƒý[Ðÿþ»!  ü2�ñÿýà>  üF»üÿýîV  ü)gî ÿüþüüþÿúýïØ©R  ü:©þÿèþö±”‰ƒ~xuqmldYQI@:51-(%  ü Pöÿìú¼h1  ü ,eþÿûú­j*  ü /pýÿûý¼p)  ü (qýÿüÓx,üû ]ðÿüì‡:ú ,OnJú$ ü >´ÿü÷˜Oø /n�­…=øCu€R%õnðÿÿþõ´b÷(lÄýÿôe ÷:‡ÓÕÄOö4—ùüó×q-ýRÈÿþ�ü&pöÿý /øH¯îì‡A ü6—ûÿþ¯!ý =¨ÿý»E úG–šR ýPÙÿþÖ+ýLÞÿýÌRüE,ü%dêÿþà2ý`òÿû¾Tþü)€óÿþÁ-ýpîÿý«E #û'–óþÿþ ýsèÿüü�/$û”ì÷ÿýòxýaáÿþÂ<(übæÿü÷±JùGÐûýÂ2*÷mÇÙ’hDü&hžl.ù&1#þ 5bü0¡ûÿýÛ2 ýlæÿýùT üA¹üÿýûx ü*€çÿüý±# üI»ûÿýä= ü'�çÿýùj üDÃûÿüþ®$ ü"�æÿýæE ý@Îÿýûx ü (ªüÿý½( üqìÿýìK ýO×ÿýþƒ ý 6ÓÿýÈ+ ý'ÑÿýðS ý#ÈÿþŽý'ÂÿýÐ1ý-Ôÿýó[ ýAäÿþ—ýZíÿýÔ4ý&~÷ÿýô^ü ?£üÿýþƒý[Ðÿþ»!  ü2�ñÿýà>  üF»üÿýîV  ü)gî ÿüþüüþÿúýïØ©R  ü:©þÿèþö±”‰ƒ~xuqmldYQI@:51-(%  ü Pöÿìú¼h1  ü ,eþÿûú­j*  ü /pýÿûý¼p)  ü (qýÿüÓx,üû ]ðÿüì‡:ú ,OnJú$ ü >´ÿü÷˜Oø /n�­…=øCu€R%õnðÿÿþõ´b÷(lÄýÿôe ÷:‡ÓÕÄOö4—ùüó×q-ýRÈÿþ�ü&pöÿý /øH¯îì‡A ü6—ûÿþ¯!ý =¨ÿý»E úG–šR ýPÙÿþÖ+ýLÞÿýÌRüE,ü%dêÿþà2ý`òÿû¾Tþü)€óÿþÁ-ýpîÿý«E #û'–óþÿþ ýsèÿüü�/$û”ì÷ÿýòxýaáÿþÂ<(übæÿü÷±JùGÐûýÂ2*÷mÇÙ’hDü&hžl.ù&1#þ 5bü0¡ûÿýÛ2 ýlæÿýùT üA¹üÿýûx ü*€çÿüý±# üI»ûÿýä= ü'�çÿýùj üDÃûÿüþ®$ ü"�æÿýæE ý@Îÿýûx ü (ªüÿý½( üqìÿýìK ýO×ÿýþƒ ý 6ÓÿýÈ+ ý'ÑÿýðS ý#ÈÿþŽý'ÂÿýÐ1ý-Ôÿýó[ ýAäÿþ—ýZíÿýÔ4ý&~÷ÿýô^ü ?£üÿýþƒý[Ðÿþ»!  ü2�ñÿýà>  üF»üÿýîV  ü)gî ÿüþüüþÿúýïØ©R  ü:©þÿèþö±”‰ƒ~xuqmldYQI@:51-(%  ü Pöÿìú¼h1  ü ,eþÿûú­j*  ü /pýÿûý¼p)  ü (qýÿüÓx,üû ]ðÿüì‡:ú ,OnJú$ ü >´ÿü÷˜Oø /n�­…=øCu€R%õnðÿÿþõ´b÷(lÄýÿôe ÷:‡ÓÕÄOö4—ùüó×q-ýRÈÿþ�ü&pöÿý /øH¯îì‡A ü6—ûÿþ¯!ý =¨ÿý»E úG–šR ýPÙÿþÖ+ýLÞÿýÌRüE,ü%dêÿþà2ý`òÿû¾Tþü)€óÿþÁ-ýpîÿý«E #û'–óþÿþ ýsèÿüü�/$û”ì÷ÿýòxýaáÿþÂ<(übæÿü÷±JùGÐûýÂ2*÷mÇÙ’hDü&hžl.ù&1#þ 5b2ÿ þ3ÿ þ3ÿ þ3ÿ þ3ÿ þ4ÿ þ4ÿ þ4ÿ þ5ÿ þ5ÿ þ5ÿ þ5ÿ þ5ÿ þ6ÿþ6ÿþ6ÿþ6ÿþ6ÿþ6ÿþ6ÿþ6ÿþ5ÿ þ5ÿ þ5ÿ þ5ÿ þ4ÿ þ4ÿ þ4ÿ þ2ÿ þ1ÿþ0ÿþ.ÿþ,ÿþÿý)ÿþþúö&ÿþýóåö#ÿþüóÊáüÿþýøãôÿ$þÿþôõ$þÿýÏÙûþÿóþÿüË¡ÍìþþóÝþþûøÿúÓ³—Îçúþüýñß�þüùôîò ÿùúàÆÇâóüþûùîØŸCþÿþýøõùþùúî×°‰+6þ÷ùëͤ‡A 4þøøåá7ÿü1þ÷óÙºšv2 ýÙóú,þöøëÌ«‘g-úT•Éæôû#þóûøóíÜÁ¨•U# ÷R“ÁÞíõùþìýüú÷óíåÜ̺©Š|kD#  Ö-Kz—­ÊÓÛàäçëìïñóóòïíêçäàÜ×ÒÍ󢕅hL3þà %4AO^kv|}}{zrfZM=0%ý ÿ5 @ @ @$þúû鯇5#þúù߸r "þúõרY!þúðÌ•7þùüèÀ… þúúà´i þû÷Ø¥MþûóÌ”9þúýíÀ…1þúüç´m'þúúߨI þúøÙ›- !þûôÐŒ(#þûñËt'$þúüëÃJ%þüûç¼2(þûûã¶4(þûûã±?)þûûã¯K *þûûâ¬P+ þúûã¬R!+ þúûã¬R, þûûâ«K. þûûâ¬@/ þûûã­=0þûûæ¯=1þûüç°H2þûüé³T3þûýë¶a4þûýí»r5þüîÀ 6þüñÈŽ7þû÷ט-7þûûä¡P8þüì¹| 9úþùÔ“"9ûþð¸^ :ûüÕ�:üê¶m ;üÍŽ0;ý�D <þR =ÿ�ý <ý <ý =þmÕÕÕÕÐ:h�4N'PrismLauncher-11.0.3/launcher/resources/sources/magitech.svg0000644000175100017510000012035515224505336023571 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/squarecreeper.svg0000644000175100017510000006751415224505336024665 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/pskeleton.svg0000644000175100017510000003363115224505336024014 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/gear.svg0000644000175100017510000004100615224505336022721 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/herobrine.svg0000644000175100017510000003600615224505336023764 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/steve.svg0000644000175100017510000003340415224505336023134 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/clucker.svg0000644000175100017510000002762215224505336023443 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/sources/meat.svg0000644000175100017510000002216715224505336022740 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/OSX/0000755000175100017510000000000015224505336020247 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/OSX/OSX.qrc0000644000175100017510000000326315224505336021433 0ustar runnerrunner index.theme scalable/about.svg scalable/accounts.svg scalable/bug.svg scalable/centralmods.svg scalable/checkupdate.svg scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg scalable/jarmods.svg scalable/java.svg scalable/language.svg scalable/loadermods.svg scalable/log.svg scalable/minecraft.svg scalable/new.svg scalable/news.svg scalable/notes.svg scalable/proxy.svg scalable/refresh.svg scalable/resourcepacks.svg scalable/shaderpacks.svg scalable/screenshots.svg scalable/settings.svg scalable/status-bad.svg scalable/status-good.svg scalable/status-yellow.svg scalable/viewfolder.svg scalable/worlds.svg scalable/delete.svg scalable/tag.svg scalable/export.svg scalable/rename.svg scalable/launch.svg scalable/shortcut.svg PrismLauncher-11.0.3/launcher/resources/OSX/index.theme0000644000175100017510000000022115224505336022375 0ustar runnerrunner[Icon Theme] Name=OSX Comment=OSX theme by pexner Inherits=multimc Directories=scalable [scalable] Size=48 Type=Scalable MinSize=16 MaxSize=256 PrismLauncher-11.0.3/launcher/resources/OSX/scalable/0000755000175100017510000000000015224505336022015 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/OSX/scalable/settings.svg0000644000175100017510000000426015224505336024400 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/shortcut.svg0000644000175100017510000000156615224505336024421 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/screenshots.svg0000644000175100017510000000246215224505336025102 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/notes.svg0000644000175100017510000000271215224505336023670 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/status-good.svg0000644000175100017510000000234015224505336025006 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/jarmods.svg0000644000175100017510000000262515224505336024202 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/status-yellow.svg0000644000175100017510000000211215224505336025366 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/java.svg0000644000175100017510000000361415224505336023463 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/bug.svg0000644000175100017510000000510515224505336023314 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/custom-commands.svg0000644000175100017510000000660515224505336025656 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/OSX/scalable/log.svg0000644000175100017510000000225315224505336023321 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/checkupdate.svg0000644000175100017510000000272715224505336025026 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/export.svg0000644000175100017510000000420015224505336024053 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/copy.svg0000644000175100017510000000225315224505336023512 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/minecraft.svg0000644000175100017510000000156715224505336024517 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/externaltools.svg0000644000175100017510000000156615224505336025451 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/instance-settings.svg0000644000175100017510000000375515224505336026212 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/language.svg0000644000175100017510000000651615224505336024331 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/OSX/scalable/centralmods.svg0000644000175100017510000000236415224505336025056 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/news.svg0000644000175100017510000000224215224505336023512 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/refresh.svg0000644000175100017510000000211115224505336024167 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/new.svg0000644000175100017510000000240215224505336023325 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/rename.svg0000644000175100017510000000213115224505336024002 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/patreon.svg0000644000175100017510000000215515224505336024211 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/delete.svg0000644000175100017510000000431315224505336024001 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/resourcepacks.svg0000644000175100017510000000274215224505336025414 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/about.svg0000644000175100017510000000222315224505336023647 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/proxy.svg0000644000175100017510000000244715224505336023726 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/loadermods.svg0000644000175100017510000000215715224505336024674 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/coremods.svg0000644000175100017510000000224615224505336024355 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/launch.svg0000644000175100017510000000340715224505336024014 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/shaderpacks.svg0000644000175100017510000000302215224505336025023 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/tag.svg0000644000175100017510000000237715224505336023322 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/viewfolder.svg0000644000175100017510000000210715224505336024704 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/worlds.svg0000644000175100017510000000572015224505336024054 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/OSX/scalable/help.svg0000644000175100017510000000721615224505336023474 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/OSX/scalable/accounts.svg0000644000175100017510000000202415224505336024353 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/OSX/scalable/status-bad.svg0000644000175100017510000000175015224505336024610 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/0000755000175100017510000000000015224505336022241 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_light/breeze_light.qrc0000644000175100017510000000364315224505336025421 0ustar runnerrunner index.theme scalable/about.svg scalable/accounts.svg scalable/bug.svg scalable/centralmods.svg scalable/checkupdate.svg scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg scalable/datapacks.svg scalable/discord.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg scalable/jarmods.svg scalable/java.svg scalable/language.svg scalable/loadermods.svg scalable/log.svg scalable/minecraft.svg scalable/matrix.svg scalable/new.svg scalable/news.svg scalable/notes.svg scalable/proxy.svg scalable/reddit-alien.svg scalable/refresh.svg scalable/resourcepacks.svg scalable/shaderpacks.svg scalable/shortcut.svg scalable/screenshots.svg scalable/settings.svg scalable/status-bad.svg scalable/status-good.svg scalable/status-yellow.svg scalable/viewfolder.svg scalable/worlds.svg scalable/delete.svg scalable/tag.svg scalable/export.svg scalable/rename.svg scalable/launch.svg scalable/server.svg scalable/appearance.svg PrismLauncher-11.0.3/launcher/resources/breeze_light/index.theme0000644000175100017510000000023115224505336024370 0ustar runnerrunner[Icon Theme] Name=Breeze Light Comment=Breeze Light Icons Inherits=multimc Directories=scalable [scalable] Size=48 Type=Scalable MinSize=16 MaxSize=256 PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/0000755000175100017510000000000015224505336024007 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/settings.svg0000644000175100017510000000133015224505336026365 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/shortcut.svg0000644000175100017510000000172015224505336026403 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/screenshots.svg0000644000175100017510000000117515224505336027074 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/notes.svg0000644000175100017510000000122215224505336025655 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/datapacks.svg0000644000175100017510000000226715224505336026472 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/status-good.svg0000644000175100017510000000102615224505336027000 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/jarmods.svg0000644000175100017510000000270215224505336026170 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/status-yellow.svg0000644000175100017510000000113215224505336027361 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/java.svg0000644000175100017510000000415515224505336025456 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/reddit-alien.svg0000644000175100017510000000132015224505336027065 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/bug.svg0000644000175100017510000000070515224505336025307 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/custom-commands.svg0000644000175100017510000000107115224505336027640 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/log.svg0000644000175100017510000000067515224505336025321 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/checkupdate.svg0000644000175100017510000000110115224505336027001 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/export.svg0000644000175100017510000000112315224505336026046 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/copy.svg0000644000175100017510000000103115224505336025475 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/matrix.svg0000644000175100017510000000265615224505336026045 0ustar runnerrunner Matrix (protocol) logo PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/minecraft.svg0000644000175100017510000000344115224505336026502 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/externaltools.svg0000644000175100017510000000111015224505336027424 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/instance-settings.svg0000644000175100017510000000103715224505336030173 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/language.svg0000644000175100017510000000112115224505336026306 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/centralmods.svg0000644000175100017510000000042115224505336027040 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/news.svg0000644000175100017510000000063715224505336025512 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/refresh.svg0000644000175100017510000000315115224505336026166 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/new.svg0000644000175100017510000000104215224505336025316 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/server.svg0000644000175100017510000000075715224505336026047 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/rename.svg0000644000175100017510000000156315224505336026004 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/discord.svg0000644000175100017510000000173415224505336026164 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/patreon.svg0000644000175100017510000000040515224505336026177 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/delete.svg0000644000175100017510000000111515224505336025770 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/resourcepacks.svg0000644000175100017510000000125315224505336027402 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/about.svg0000644000175100017510000000075715224505336025653 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/proxy.svg0000644000175100017510000000127215224505336025713 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/loadermods.svg0000644000175100017510000000060215224505336026657 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/coremods.svg0000644000175100017510000000057315224505336026350 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/launch.svg0000644000175100017510000000042315224505336026001 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/shaderpacks.svg0000644000175100017510000000120115224505336027012 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/tag.svg0000644000175100017510000000114215224505336025301 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/viewfolder.svg0000644000175100017510000000060715224505336026701 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/worlds.svg0000644000175100017510000005032015224505336026042 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/appearance.svg0000644000175100017510000000160415224505336026630 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/help.svg0000644000175100017510000000125015224505336025456 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/accounts.svg0000644000175100017510000000234215224505336026350 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_light/scalable/status-bad.svg0000644000175100017510000000072715224505336026605 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/0000755000175100017510000000000015224505336021203 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/pe_dark/pe_dark.qrc0000644000175100017510000000347115224505336023324 0ustar runnerrunner index.theme scalable/about.svg scalable/accounts.svg scalable/bug.svg scalable/centralmods.svg scalable/checkupdate.svg scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg scalable/datapacks.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg scalable/jarmods.svg scalable/java.svg scalable/language.svg scalable/loadermods.svg scalable/log.svg scalable/minecraft.svg scalable/new.svg scalable/news.svg scalable/notes.svg scalable/proxy.svg scalable/refresh.svg scalable/resourcepacks.svg scalable/shaderpacks.svg scalable/screenshots.svg scalable/settings.svg scalable/status-bad.svg scalable/status-good.svg scalable/status-yellow.svg scalable/viewfolder.svg scalable/worlds.svg scalable/delete.svg scalable/tag.svg scalable/export.svg scalable/rename.svg scalable/launch.svg scalable/shortcut.svg scalable/server.svg scalable/appearance.svg PrismLauncher-11.0.3/launcher/resources/pe_dark/index.theme0000644000175100017510000000023615224505336023337 0ustar runnerrunner[Icon Theme] Name=Simple (Dark) Comment=Icons by pexner (dark) Inherits=multimc Directories=scalable [scalable] Size=48 Type=Scalable MinSize=16 MaxSize=256 PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/0000755000175100017510000000000015224505336022751 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/settings.svg0000644000175100017510000000230515224505336025332 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/shortcut.svg0000644000175100017510000000215615224505336025351 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/screenshots.svg0000644000175100017510000000231615224505336026034 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/notes.svg0000644000175100017510000000237415224505336024630 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/datapacks.svg0000644000175100017510000002617115224505336025434 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/status-good.svg0000644000175100017510000000170615224505336025747 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/jarmods.svg0000644000175100017510000000503715224505336025136 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/status-yellow.svg0000644000175100017510000000205415224505336026327 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/java.svg0000644000175100017510000000572115224505336024420 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/bug.svg0000644000175100017510000000270015224505336024246 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/custom-commands.svg0000644000175100017510000002537515224505336026617 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/log.svg0000644000175100017510000000216115224505336024253 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/checkupdate.svg0000644000175100017510000000210215224505336025745 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/export.svg0000644000175100017510000000406415224505336025017 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/copy.svg0000644000175100017510000000167215224505336024452 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/minecraft.svg0000644000175100017510000000173215224505336025445 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/externaltools.svg0000644000175100017510000000215615224505336026401 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/instance-settings.svg0000644000175100017510000000230515224505336027134 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/language.svg0000644000175100017510000000642615224505336025265 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/centralmods.svg0000644000175100017510000000170715224505336026012 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/news.svg0000644000175100017510000000126315224505336024450 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/refresh.svg0000644000175100017510000000154715224505336025137 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/new.svg0000644000175100017510000000174215224505336024267 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/server.svg0000644000175100017510000000134015224505336024776 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/rename.svg0000644000175100017510000000167015224505336024745 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/patreon.svg0000644000175100017510000000172615224505336025150 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/delete.svg0000644000175100017510000000621715224505336024742 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/resourcepacks.svg0000644000175100017510000000211115224505336026336 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/about.svg0000644000175100017510000000146615224505336024613 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/proxy.svg0000644000175100017510000000224415224505336024655 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/loadermods.svg0000644000175100017510000000212415224505336025622 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/coremods.svg0000644000175100017510000000200615224505336025303 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/launch.svg0000644000175100017510000000213715224505336024747 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/shaderpacks.svg0000644000175100017510000000542715224505336025772 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/tag.svg0000644000175100017510000000302715224505336024247 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/viewfolder.svg0000644000175100017510000000153215224505336025641 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/worlds.svg0000644000175100017510000000544315224505336025012 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/appearance.svg0000644000175100017510000000534315224505336025576 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/help.svg0000644000175100017510000000606415224505336024430 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/accounts.svg0000644000175100017510000000273615224505336025321 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_dark/scalable/status-bad.svg0000644000175100017510000000146315224505336025545 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/0000755000175100017510000000000015224505336022053 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_dark/breeze_dark.qrc0000644000175100017510000000364215224505336025044 0ustar runnerrunner index.theme scalable/about.svg scalable/accounts.svg scalable/bug.svg scalable/centralmods.svg scalable/checkupdate.svg scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg scalable/datapacks.svg scalable/discord.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg scalable/jarmods.svg scalable/java.svg scalable/language.svg scalable/loadermods.svg scalable/log.svg scalable/minecraft.svg scalable/matrix.svg scalable/new.svg scalable/news.svg scalable/notes.svg scalable/proxy.svg scalable/reddit-alien.svg scalable/refresh.svg scalable/resourcepacks.svg scalable/shaderpacks.svg scalable/shortcut.svg scalable/screenshots.svg scalable/settings.svg scalable/status-bad.svg scalable/status-good.svg scalable/status-yellow.svg scalable/viewfolder.svg scalable/worlds.svg scalable/delete.svg scalable/tag.svg scalable/export.svg scalable/rename.svg scalable/launch.svg scalable/server.svg scalable/appearance.svg PrismLauncher-11.0.3/launcher/resources/breeze_dark/index.theme0000644000175100017510000000022715224505336024207 0ustar runnerrunner[Icon Theme] Name=Breeze Dark Comment=Breeze Dark Icons Inherits=multimc Directories=scalable [scalable] Size=48 Type=Scalable MinSize=16 MaxSize=256 PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/0000755000175100017510000000000015224505336023621 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/settings.svg0000644000175100017510000000133015224505336026177 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/shortcut.svg0000644000175100017510000000172015224505336026215 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/screenshots.svg0000644000175100017510000000117515224505336026706 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/notes.svg0000644000175100017510000000122215224505336025467 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/datapacks.svg0000644000175100017510000000226715224505336026304 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/status-good.svg0000644000175100017510000000102615224505336026612 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/jarmods.svg0000644000175100017510000000270215224505336026002 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/status-yellow.svg0000644000175100017510000000113215224505336027173 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/java.svg0000644000175100017510000000415515224505336025270 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/reddit-alien.svg0000644000175100017510000000132315224505336026702 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/bug.svg0000644000175100017510000000070515224505336025121 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/custom-commands.svg0000644000175100017510000000107115224505336027452 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/log.svg0000644000175100017510000000067515224505336025133 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/checkupdate.svg0000644000175100017510000000110115224505336026613 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/export.svg0000644000175100017510000000112315224505336025660 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/copy.svg0000644000175100017510000000103115224505336025307 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/matrix.svg0000644000175100017510000000265615224505336025657 0ustar runnerrunner Matrix (protocol) logo PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/minecraft.svg0000644000175100017510000000344115224505336026314 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/externaltools.svg0000644000175100017510000000111015224505336027236 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/instance-settings.svg0000644000175100017510000000103715224505336030005 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/language.svg0000644000175100017510000000112115224505336026120 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/centralmods.svg0000644000175100017510000000042115224505336026652 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/news.svg0000644000175100017510000000063715224505336025324 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/refresh.svg0000644000175100017510000000315115224505336026000 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/new.svg0000644000175100017510000000104215224505336025130 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/server.svg0000644000175100017510000000075715224505336025661 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/rename.svg0000644000175100017510000000156315224505336025616 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/discord.svg0000644000175100017510000000173415224505336025776 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/patreon.svg0000644000175100017510000000041015224505336026005 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/delete.svg0000644000175100017510000000111515224505336025602 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/resourcepacks.svg0000644000175100017510000000125315224505336027214 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/about.svg0000644000175100017510000000075715224505336025465 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/proxy.svg0000644000175100017510000000127215224505336025525 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/loadermods.svg0000644000175100017510000000060215224505336026471 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/coremods.svg0000644000175100017510000000057315224505336026162 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/launch.svg0000644000175100017510000000042315224505336025613 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/shaderpacks.svg0000644000175100017510000000120115224505336026624 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/tag.svg0000644000175100017510000000114215224505336025113 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/viewfolder.svg0000644000175100017510000000060715224505336026513 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/worlds.svg0000644000175100017510000005032015224505336025654 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/appearance.svg0000644000175100017510000000160415224505336026442 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/help.svg0000644000175100017510000000125015224505336025270 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/accounts.svg0000644000175100017510000000234215224505336026162 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/breeze_dark/scalable/status-bad.svg0000644000175100017510000000072715224505336026417 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/0000755000175100017510000000000015224505336021711 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/pe_colored/index.theme0000644000175100017510000000024415224505336024044 0ustar runnerrunner[Icon Theme] Name=Simple (Colored) Comment=Icons by pexner (colored) Inherits=multimc Directories=scalable [scalable] Size=48 Type=Scalable MinSize=16 MaxSize=256 PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/0000755000175100017510000000000015224505336023457 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/settings.svg0000644000175100017510000000260215224505336026040 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/shortcut.svg0000644000175100017510000000200315224505336026046 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/screenshots.svg0000644000175100017510000000213515224505336026541 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/notes.svg0000644000175100017510000000246515224505336025337 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/datapacks.svg0000644000175100017510000002640215224505336026137 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/status-good.svg0000644000175100017510000000221415224505336026450 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/jarmods.svg0000644000175100017510000000367315224505336025650 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/status-yellow.svg0000644000175100017510000000211215224505336027030 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/java.svg0000644000175100017510000000574315224505336025132 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/bug.svg0000644000175100017510000000250215224505336024754 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/custom-commands.svg0000644000175100017510000002577615224505336027332 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/log.svg0000644000175100017510000000215015224505336024757 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/checkupdate.svg0000644000175100017510000000212315224505336026456 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/export.svg0000644000175100017510000000416415224505336025526 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/copy.svg0000644000175100017510000000213115224505336025147 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/minecraft.svg0000644000175100017510000000165115224505336026153 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/externaltools.svg0000644000175100017510000000200315224505336027076 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/instance-settings.svg0000644000175100017510000000260215224505336027642 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/language.svg0000644000175100017510000000635415224505336025773 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/centralmods.svg0000644000175100017510000000233415224505336026515 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/news.svg0000644000175100017510000000126715224505336025162 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/refresh.svg0000644000175100017510000000156615224505336025646 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/new.svg0000644000175100017510000000176415224505336025001 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/server.svg0000644000175100017510000000135615224505336025513 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/rename.svg0000644000175100017510000000205415224505336025450 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/patreon.svg0000644000175100017510000000162615224505336025655 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/delete.svg0000644000175100017510000000621115224505336025442 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/resourcepacks.svg0000644000175100017510000000241615224505336027054 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/about.svg0000644000175100017510000000221215224505336025307 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/proxy.svg0000644000175100017510000000220515224505336025360 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/loadermods.svg0000644000175100017510000000212715224505336026333 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/coremods.svg0000644000175100017510000000216415224505336026016 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/launch.svg0000644000175100017510000000323215224505336025452 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/shaderpacks.svg0000644000175100017510000000561115224505336026473 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/tag.svg0000644000175100017510000000352215224505336024755 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/viewfolder.svg0000644000175100017510000000215115224505336026345 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/worlds.svg0000644000175100017510000000504015224505336025511 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/appearance.svg0000644000175100017510000000607415224505336026306 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/help.svg0000644000175100017510000000674215224505336025141 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/accounts.svg0000644000175100017510000000264615224505336026027 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/scalable/status-bad.svg0000644000175100017510000000150415224505336026247 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_colored/pe_colored.qrc0000644000175100017510000000347415224505336024543 0ustar runnerrunner index.theme scalable/about.svg scalable/accounts.svg scalable/bug.svg scalable/centralmods.svg scalable/checkupdate.svg scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg scalable/datapacks.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg scalable/jarmods.svg scalable/java.svg scalable/language.svg scalable/loadermods.svg scalable/log.svg scalable/minecraft.svg scalable/new.svg scalable/news.svg scalable/notes.svg scalable/proxy.svg scalable/refresh.svg scalable/resourcepacks.svg scalable/screenshots.svg scalable/settings.svg scalable/shaderpacks.svg scalable/status-bad.svg scalable/status-good.svg scalable/status-yellow.svg scalable/viewfolder.svg scalable/worlds.svg scalable/delete.svg scalable/tag.svg scalable/export.svg scalable/rename.svg scalable/launch.svg scalable/shortcut.svg scalable/server.svg scalable/appearance.svg PrismLauncher-11.0.3/launcher/resources/backgrounds/0000755000175100017510000000000015224505336022100 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/backgrounds/rory-flat-bday.png0000644000175100017510000013555115224505336025454 0ustar runnerrunner‰PNG  IHDR’š%ÑÅ—»0IDATxÚì�tcÛÛ‡÷µmÛ¶mÛ˜:ÅØÂµmcl×JS\cj¦é¾v;Vû~¿³¿“iþ¥½]·“œß³Ö³2©–çYïÞÙû(B!„B!„B!„B‰tAâîðx¼Þ¶¿·…"„B¢±<î 7‡§Â7àWðm8·=m_�—(B!ÄÉ1ðc8�ã¡6ÃzXj¿þÿ‚K`< n®!„8;Yv8~·c±.°¶øµÆ]p'E!Ä‘Ù^?˜@–ÁU° Šýêw5\nG¦ÅL<^BqdH¶€)ð{Y %WÛi�MpL„g8o#žB¸¤õ6ü®‚ÒWÃEðoXjÇè-¸�"„â˜�Üç6¹]V ¤®„Íð7X§À›!„G,iíæËwõDDt}vÜ*¼v&$m°ÅžJæÁSà.ŠBHćä0„ã회˜oê²bWÖdÆHCn¼tr*Y›áD¸«"„âˆ�ÜïÍ�ÿ¦lþÃÞs\]™%µY±� ÉRø'|n©!„D|D¶‡�ÕçÄ}óÝìôSïiý~öƒR•%�ù � É·ð\¸™"„ÙØÑu7æ%üðí¬ZJ&ÝÕúå´{¥tÞC‚¸t4"­pü > wS„B"¾íËO˜üõŒû<ãoo-žx§|;ó~©ËîðòV3Ô°¦8di‹Bø‰­tð÷s,ülòÝ«Ýco31Á¿ML°¿± ´ÁeðWx­"„â<ݱ;ÔdľüýôèåÅãï•‚±·‹ÛžLðI.©Îˆ6›ïˆH \çÃã!„ç¡_¯—÷ƒgW–~Ü_>›x¯xÆY1¹U¬=“ïf= 8¨¸®kQ–ÀfÛFøhM"ÕˆHöJ|É"x­"„âØ�œý!±^Û£RìÿÚ†l�W(B!Ž ÉÈ5ñ€ºã.ƒ·)B!Î<7‚xdû\í“GH¦í÷KàMŠBˆ#Cr6þ‚ý³ÉrLÆÌ=z¶ÿý?ð"E!ÄYèäÏ·@^öºŠ¤:&O*¢²¥66Ę<3Aô“Süï†')B!Ž›FŽÃfº§ØÍáCÄÄÒiö>Fû}5/澬ñψÎê+&(fÐuî´û³‚¯K§!„„?$¾/‚Qe-eUFåHyT¶ &ÿF{»½=Ö]YæS[æ"Æ÷_ó<’~ù�ˆÿfà9æQ½_Ÿ7S„BÂf"¹û ÿTÇäIUL®àÕ^ÖòøÏŽi®ŒzþºyßUÎ…[)B!ábÑ›ë˜B²¥V#&�.³LÕ]Â]!„�°XÖÚ!ɱ&�Ѝì5û#xï_Öêká!ŠBHX,kÅÀ¥>W±ù¸/"â_ÚêΩäWx¬"„²I³EîCc/ÄEŒ•f/ÿ�[11עķï�t“ áŠBȦŕg»<ü²³Žz8öÒkÇ{zLú{#fI ˆÿ¡UÝc+t)B!›L@ö„·Â÷¯8û˜Ú›Î;mÕ”»_4ÿicùÊ,g•õÈ4K[Ý;`þSôxÿ¼"„ÒíÙö†Ep ”ËÏ:ZF^- .�ü`d×:½^…˜X“ –ºº%"¾D³¬ø°¬¹p;E!¤[²¼fÁÕP,1�È­çŸ%Y~,?&Ñþ¹=™`™ËDå?ŸD’JÌ™ïÿnî—½!„„#Ï%_¿-¼¾“à¹ð¸=ÜbÈ6ðXø"üJ`D®;÷$yýÖ¡RŸ`ßæko°Û× tŸëÞ—ù ÓyŠBÂ4$Á ÀVØ`œƒWÂàpG¸ù�ÍàÖp'xŒ…3`l ŽÈ-çŸ!ïÞ6JjâóÌÕ'˜>ìI¤8è™#]drq§b”ªøœDE!a‘-íXÈFl�uð;øì�tTçööSÿ_w—^¿uÇ]c@®A ‚·$Á�àîîPÚ•[wê…º+(Ùßó[3ë]óAB&ÉLHÚ³×zÖ™Lf&çœ�÷y÷³í€0Cè+tZÍsS¢«wh|ëu *ý7Ò«"d Ó…¡¯ÐVhçG¡—�*Ä7 5…æBw!Eè- f «…ƒÂ1á”`àl‰—œµºõD<‚ÙÔ‰`GNbÁ&62¡mÊ5uœ¹ØN,›Ê,ž šD'1¿Y×fÊsŠéè¾ü÷§žyæ™gŒL¦V|+|-œôãÝÁíë¼]íš¼„@|+| àŒÿµŸ o Ÿ_ §ù� ‘5*ÛšÖ“ŒöðD‚”åk·[ÄIÿå9ó“ÇiËìÄÁ>vâÞžvbU.Mù]ÐÞRwˆdW‡ùYåÚ÷t-Û„B¼PGø«€Äè™gžyVn‰$RøB°ÒB^‰õŒ«j�XÜË�H«UmcÛéF � ú³þ޾�‹vàBîŸEÂk‹O"t¦•ü¾ÁÆ|7ìjnž›5RÜyBtɃ]7ëª×â'ÐÏ„…'… á¿’¢gžyæY¹#’Ÿ Û ²zFYç¨ÛÊ”DâªÝjkÛLÆÃÀÓð «žézì)sÀãâÅC–L‡<ÜôDà_Í^HwàbÜIQبµ‘²\ˆ7÷˜0Pøcÿ-\.ü^¸A¸Yøm°ïõÌ3Ï<2©#¼ "™,LèiÝb+[ãªW— ‰,i‘IL„>íáZÅ‘øˆ“µB‚TÉWëÇC"nŽ;pIJlJ‰>÷�”‡l^ó‘ŽH Á7¡�py¾÷‹„_ 5…‘Â.áˆð¶ðžð€ÐE(òoyæ™g‘\&Ì,Td’“m)ñÕ-2 ‰4«v‹-�Ÿ`/$”rØŽú‰@$O'Ü‘0À*´Å‡9kíÄÎáÆwÄ�àK§Ék ÞÃÁ#"àDòÄw‚½þ·…,á÷%ø®¯þ.´f   V>ºóÙžyæ™G&7 ¯*äŠL›V ‰˜¶™qCíùDßpªgE"׈‘˜²òd‚w’Db$Ì]Ï›k'ökvû¦qæb%ûÙ‰¬ Ūng rÏNX£"ÊÛ¸¾`îÃia¹p#ÞI!ßé„  c…mÂËÂׂ‰=|N„gžyæYDr±�昙«}ã[BJ"�«\c1=í¹w°Óò„Ù"ÄFü ô=®ð�¬(ž,ÜÇC"o‰�²Öۉɫ|csç,°[ÆÚ‰yyLQ 6˜”dGzüüRÏC–Ö¸½“·‚À)áA![èÒ ÒU�’šV­"oð_º÷•„ Hâ(eBÅëBÍÏ<ó̳ ÈägÂ~ÁB)se&5¶¤fU-¦Æµ¥&êDfÇ ·g’n‡ð6�±8Ò?ËeB;+K‹ûhˆ[¢¸Tß¾÷‘ú ‰·(rã:|éÊ=îUpžÅV½Ñy%AâL£*W}�Séíñ="ßÒ}?.¼#X±ô;+ñÌ3φ$DýŸð+áOÂ_…ß WòÚ‹„Ÿ ¿~ÌÏdÒܯ‹[(1QÚ±®5¯sƒ5(!‘ÄT½Áˆ#@Ó�÷A’­|ìÏú%¬c=µÃïåz·hû½”rH�ó=â›ÔèëÖë° jÔ¦8^‰EU»Æ’$!fõŠ2ˆ{Rxð²ðÏÏ<ó¬BÅÅÂ/„«…*B¡ƒ�),¶  /Ç„§…­þß-¦ Âa±pHøŸp»0[h�–õÛœä4õÕ‚…“ýd+ϤA ¼‘65kØÿ:o4¤ ˆãñŽ{X€‘·¸ûvöÝÙÑA{íø<Å0¦¬(ÊK 0Îk@Y ²ç�¹ ;LŠ,'"ßyO$eY“š×YßV5,G$Â=#ÞªDxæ™gŠ8.~'Tz KüDñºð©ð¥ð­`!ÂW£Âü±‰ÑySb¾ ×¢”Úº&Ù\Å&’εêÛ�·�•å'’ÝÌqòÐ+IŠ9¤´×–NöU›ïM³ãã¶^mŽ$5Ku3|)½W_�–òGB$x$'j^‘òV›7ÛÈ„úÜÓ²ÀÛÂmžyæYù6d&¿ÇÑ^Xî÷*>ò+CœÎìým¸%Rƒ»ÄT*‘ õ$Õ�¶Çv^È�öûHdÔf;N«’»RÉ¢Ò±¯_3ÁŽ�Ù^p|cÜv_æÕ==}5 ;F؉{ù]Yuv½¿ˆé uA$�ußimkÖ,”H¢«_“ß½IåüñI‘6¹O™�xAø{„gžyVn ä_Bšp@xK8%؅ĨnÑ&¯ÄÂ%qe$6¶ÖõoBž ’Hþm© Z(¦à ªC&O'ì7¼~>Ú[íPÖŽ7<WÛq¤ØñÕYöZï{Í™VóçÄÁk  òf†í/+oÄMI$ÓŒÇɉ»­}­ZI«ú7åK<¥v4ù.R6Ø ü(Â3Ï<+_6´K Hwáˆó:Ê †v‰² ½¢Ã¶0±“ݵAÐÁ÷z"’þ [I:ì'‘Û�”ßÇ;í1å¥ä;ìøÊ\G"d«ã©Ì%ñ/âsó\%º#^ h¸n‚jEÑê¤lÅ×HbqI»³<—ØÒðÎõuïbË’@À·ÂàÏ<óìÂÛ‹w͸(+µÕ�D × )Z°÷ '+�“@$a ¾_g ‚�¶RÄÛK"¥ÊBd; ·»ì­c#¶ç4P<¾o ëq¸jsŽË¦:�‡#Ž»†Ñ˜±Ì$.=‰k)ÿH÷í’¶j8"i @"ýÛÔâ¾]¼'Ô�ðÌ3ÏÊÆNÜŸw™ð áWo<¸à¯�îÈ©~û²­÷-ÞqQfb¿)ƒÛ-Ú%ú¸ó@Ê1†É+ÉNÿBÕ¯u-߯)2ØŽÜóH·íH@>¯D$"RÁ3î°—S÷I²šm¯e¯²W2VÙñÝCìÄê;1èàÿO [ ‹À^YÀýŒä…¼"á3@áqKûû‘<Ú}‡#H¤i­ëlH‡ºŠDÀážyæYXÉãbá/B;a¹°UØðúóïyîŽion™=àä�å#O.HïþMî€6–ÆB]A�žV¯ÄµŸOޝVd&WT•ël{û¹HTt÷uÕììæ}3F”±•zØ·ËOQר v|àçJT}/Y42!.âï••bÎ+Y1I†!hôˆ÷3v‡�HßJzqa)À�}À|Ò–�HðÂD"×çëXïB’Ø-ü,Â3Ï< ‹çñg¡™0WxNøF°@¼|x–­™ÜÛÖLéc"™Ø¤Â�Þ5ÊrÊÀ+¡/WרÊEÊ[éÑIv´×½Î+Ün^Gמ²y%é^iž;·ü”•¾XÈÚ,I_ó•*"æ$ó�ñ3(õ÷“á™gž•˜@~/´Öï V\@$�í̵¹É6¢{¬Òj+‘� œÕ«ÔÒU‘¯Á#i×è– ÆêÞÙy‹.rÁj<7��þUx+Ž@ÊÉÜ<ˆÃï�ø3Âx¼u4-èÏ›VŒ§µµk®Mè©bÃ"ˆ—i”�Fzb#Ý»†Æs{ªq£�ëÞ"¢û2÷¢�\x/Ïgc�/¤Fxæ™gÅ&�+…!ÂCÂW‚•÷®Ï°¼qÝ\|¤"bl)R�YÄXܲ´8>ç ,lc´6«u}PUîCÔnýùžw²ðâ�8Ò�—±à¥ y¹øIÙáAä2â/ÎqD²:—.ÁçON¾Û–õHÕbà]øˆÀy~2Û­¡�îÒÀÆŠ4h;“Ù£±A@£ôœÒª!È™×ñéÄ,’iTœïï !&Â3Ï< š@~' žò îÛ�aS‡t°aá��„?¸WÐYXnçËb‰Œè\ŸÌý‹E�…áu¼" ¦@‘ÁV âÇ–Rëkˆ˜è«9 aûøàç•Ì\Ìlwf•À÷aìöóJg�âÃI›?›˜Ôü3¼ î÷ Îê…÷á< bÐ=«giê(žR͵«ccôõ&ÛÕæÞ’6Ìc¾ «ÁcMÙ¥~qº?!ü9Â3Ï<+’@~*tN J<²=Û¦ íÈ‚\¡1.ˆ ûDa¼vÆý’ ‹Ï³Ø Ñ.9Û¿ "¹°à±ø¥‹T2´0önQƒÂ» ›8¶¬QŶµ›]D/«{ðL8–­ÔEŠ1óÝ‘º†ï<.ªeÊ™åíÇ==<¡ÞûÊÖ2î÷ "ÜKî�ñÞ¸¿xÜ7æ½0�2îWÕ¯ï¡çŸ•¬ÑÇ�£oCö ZÖ .>â™g^V-a§  ‡ÏÝ1Ýæ�K´´Î—D\*pJÑDÂn�ƒÎ´ì‚‘UX{ÄU…0œÌ5 mmKlRÅú¶¬a,œ#oe¶DQ,2YÕ:׎%û:ç:Ö6´ƒ­ð,BU”輑ºoú¨oÓ†oõnYÝR[Õ„,t�jY†Ÿ�y qðü yxxÔàtkRÙ˜DÉó ��»ëÞ¶¨{£uÐ}Eâ"‘!ADÂý’HÞjExæ™g…ÈõÂ$ámÁ‰Wïžm+&õ­ðD2{DTgÇ;¬S=CFaQ„< ˆb¸¤˜tÉ5!™v of�cÑCÖÒŽâ·—oVí[Ò2Ó‘Éy<”ÐqŽŒ-Ṫ„”8çç{Ü‘?®iÂÉö�o>ƒ×9@}D²Ü»òÞú �Ä ‘nR³*6PDŒ¤‰ðžø:7@ÖÜO �>fñ"’Ö"}:ÞdÝb+»Ä‡ ðˆð‡Ï<óìœ ô›„ÉÂk‚…Çî�kÔ’ìZ8Ô†w�­ðDrvW`<‹Ààm®?¨Ë°%8Ûn¶Ö²ÞMñFJ<‚·iµ›ma‹qJ¾Ç‘IX@ÍIöz¡:^?‡ÆËÉ‹iMj\oqµ¯·�o5ˆáB�£n“„UO RF¾‚xÌóŒ2Ö½¼ÑðL�ù¹­î9÷6!º’A*}NÞ#YäÆëzæY„G —· ³„×++Ù?Åž¹}ª=¸e‚©a#é¿ ÂÉPa|O·£5He¸ˆÂ6d¤vÎ,b,rì’YÙ%wŒº‹…‘×°kÖâ ®w-RJJ&Ì:'4ùÞð� 5 æPhHcHÈ„ÁX�K‰?“æÿuÝhôkTõ*ÈT÷ÄçMàYt�­dÞäQ@´àï¤{Hº4ÏQÈI¿²˜ê×â™ð9� ¿ç³Áð��ÕD°ý¤ÐÖ[=<óÌG"ÿ¦”5�€£÷̱'vO´‡¶eD2%­=$¢¢ÄŠš½å E®ƒ¯‘}E¼ƒØú=ÇžÚE79x",h dbQƒT´;¾�ÇzN;gYË5n,™4©v“Íi>Â^êy(´dÂg PkùMéôãòÉ�vbÛ(¥ûÞU¢BFÎïÕä{lBL²5Òù7‘ �Þiдё„Ò´æuùqu®?ÃsüÂàÞ! ò^î)�ù ^ÃýåØ3®^‰šdÖ Æ9,\á™gßsù¥ÐWx^°²†6Úó ²ÿoc¦\=ÆÔ´ÑæŒîÂB ™p̯°^ ©À)MljjÃ#!ÞAFÍÑó‡vª‹¤Å‚æß_g,xxτů±ÿçFU|‹¨‹�”’Lhî˜ÛÛŽ$ígÇZ¯dÒj_ï¬}ƒiøÈ¬Á¤”ŒHÖ·™Š'Źsü¸ÚyºO0d‹çÆ=åwŽ48òÞé#"GHz/Ïñ9.OÀ½ˆªù÷…žßçþZžyò!FØ+œìBÉ‘}SLíãmÿÒ¶J�sú·6?‘äWd"©XÏ‚Ñlí„N6­_SbËÑô!Žx ,†,h�„x iø~.�Ò•o!s\ 9†,Ð µëÛŠVÙöl�x' ”�÷b“Ÿì²MmgøIä?Á\‹óÚ÷Ó=Çã^ Î~/ÞLŠÃáÍÞϽÔë�è´Lq›>dg9‚ãXFD‚,æâ.� ñjSrü5%Ì‚÷Oa|D¸>Â3Ͼ'C¥w¸Šôr€×…—Í´Çvå$B¶’Ö¨¤&.&R‘%­%é öÅ£ÓìÔS3íí»³O½ypÂGs‡µÊ—¤EV� G–*p~Ráq\u¥&׋± ±)¶¬åx#f�Çðdâ¤0 Þid½Üë°¼˜»I+¦JÞ‘„LÅïx-#rïé²ÎÀÁÎ+ms»™Ê n¶¢h’¿ë¤¬�´¹ó!qAüÔ§à•d'G!u‘(‘ìÿ¾!zæÅA渶îå Þ½nœÝ«F�Ûò†(.Ò,ŸàzE'‘Ü~-ìØ�ñöí‘YöÕ3ìñMÃíÕýé§¶OO:“Sñ€Ãà‰„—T€«CÁcéP«¶u¯i½êÅZjƒ6°Qk#p?"²³��N$]Wä“lé1=ldd‚¥5jg¼–ñ¸Í«+>T½’!]!©ÕW 䈃/ 2¹ÖeƒQ‡‚2¦kj}HÛ&�{�ˆä§žyön¬˜"¼X äÅ»fØË‡f)¿ÔŒ¬œÔÇÆ&ÇWt9Ëei=´v¨Ù³sì«Ç§ÛgO±'h¿sq?›:8^En·øRP+‡9~bŽ`ÎF½Jÿ.�dˆòtm‰ËžãáÇ¢§Ýz t`‰,Ó´Ê‹"<óì;H"· ›œŒUñÌþ©F;’Ö]«ÆØÄAíÙÉ'²´VLèj_Ë ±çæØé§fÙ;wgÛ]KúÙªœ.Ö³eMv·.“¨œ!°>åû —ô˜I‡gB;Ú§ U ™K$Ò'Â3Ͼƒ5!ÉÂÑr)ciú!½´ðD^�G‰<.,ÈH²Ñ=ã*<‘ÐdrlϦöâîq"‘¹x$òF¦ÚÑÛ3l»²µ2zÇZ—¦U]�¬òˆØšT×ߘ6ËñûI& Šƒ+TT»ÚùÓ\óÓÉ}b½n¿ž}ç2²f _ VÁ]$-¼‘çïœn÷¬O·CkÆÚŒá�¿�~¹†�SzÁuHœ~z–½´g¬m™ÞÃvªo-êß„LR>¼Gþ">ZƒÔUŸª†·ZËú·ðØI<~ Ž×}�ˆÅÕž¸£È„À;�…©v'>òËÏ<ûŽ�ȯ„eÂÁÊ3^:8S1‘,cœîa‘ȦY,w`Ûï€7isGv°÷ï›è¼pZ¤òÑ“ìвþ6ºG”ŸH®.»í˜´a!3éZy!7êÜn¶ö‘•­SLUë,tŒ®bMä¡@0¼–…´i­¾gŠ“·á*ãŽÉcª%©á™gß¡ ú7ê¶œ)‹:²³^9<Ëhʈ²e¶*Øgô³¼qÝ•îÛ´"H"Ù©-$aeæ’Èf¶}úлgÅ@ÕÇ4±6ÒØ_ " $È£©$,Ï£mãJ�ôˆ¯e=ãk[J«ºÖ«EƒPÚGU¶6�nƒTü^Ê÷VæB–ÌW�ÉkªýiwóUW^á™gßù¡0µãnÃZhø²ˆƒ!UÈX $B/­msÙfy"sFu±ñ}ZVˆ6ñ#ºÇB"´m¡X2PÎRì£åGG¶�;@ �Ò–¼”\#ØŽ´Õ.òV*¦/Ä®šØGG$­ÜbÈX {³ÖCÒ·m}ëß¾¡��Yj»þÇ�-±yMˆ$Ü™RÁ¾\©ë”ŽOiN~¼Ž?‰ð̳ï@‘ááëp“›-´7\X"‹˜©^;Ö襵mÞ [‘›lóÇu³‘‰åÓ l_Ÿ&L”ü6UmíÓSšÛØ^q6*±‰�ÓQ­\òÛ<æ-{~Þ©‚ˆäÌ‘ÙöʾtÛ—×[ Q"’ÛXÐËÄûht–ÖߢÞ͆‚T™!cáqtkZÃð@úµÓxÚ>ᚇù‰s\rs_Ò\3Þ–óõuåõ¡ˆkœ¿‹×\é !]øy„gžUp"é,|îFŠ�ÈÛ�,ü,BYÀ1þ×.(0¸NŠï!µ…r÷$»{í8£—ÖÞÅÃlê�öÌÑ.?¶Üz G0}XG[=©·Í™`³Î}Qf’íš›šê©Yù�FAøVÉëwM°³z©j¿¹õjUSžÀÍaKÝõ-°®]:��Æß¬Î�†÷�W/Ré*éWÓú´©‡ç!idƒäy î)‚Œ3Hsö¨®6wL7Žºæ6H¿ãsXðcjøúƒ…€D WÎ ùÌwÎ�zÉ$;‹,Ü,‘(HñÂdÁÍ.‰ð̳ <þ¶•p¢,:ò¾ûØR{ÿ‰åöÁ“+ì­‡Gž{çÑ%úypdã�{?1‘å•P¹¾yö»sÅ(-¦mmæˆNÚá¶`§[ne­Ñ=šZv¿VôþR2@[šÕCõ.£mÃôTu'îgHtOï�hŸ<<Ýì¹IÄÅHÞ½'ÇvÏM¶)ƒ[XR| ÍPë÷.hAÄi¡¾¹&7¸`9 5ôv‘• O$™8HË:ÖÏ/] îÔ˜x�¼Ä&’ëZØ‚ô$Í…kw®L—9ÔVN웯 »üÁ]b,©e]ëÞ¢NHÎ�óm‡![¬©ŸT86©u=� Ík¸&ýþ®ñBÄ�v ?Žð̳ H"¦ ‡‹8ÎþÂøøÈjûèéU"”"’•ÆãŸZi~Bq¿Ã[�X8B./©^ä�Íã�Z‘ 3ú1¨Š]¾DÒP±G"å’Hðš�O²µSûÚžECíYyW´ryjÏ$#ôÑcyŽ0ÎK$÷æÚÎÙ½lL¯hëU‰]tHc�»ö&As‚㣪ÞG=f!n©x觹抇@"ýä…ä¬À¬¹±òFæ�ín;ò†)¶µXžåÙ+w/üêÞ Ù§×M�¿vúÀÓiÝ›XƒJ%¯H‡ø $·v:ï.Mªðç1D©@|x@çÄF ‘xf×7ðÅx gî+¿+£8ÊÂï#<󬂑ÈUÂÞ0fg9é*ð1ÞȧϮ5ðùóì“gÖÚ/l´“/n„LðPxÌò€\8:B!>‚”upÕˆ„šørQù$·˜6“¤ÓÅ¶ç ¶û7‰ wåBZLgkaUÜè�<ûæé³2´|R– ²«a£“¶et´Á ´ÐßÊÊq'ý°{gQÅÛ pŽd•ؼ–P“ÅòÐB]͈…‚ê%e$O$°cqî€6¶kÁÍÏŸmŸ=·É¾zi«�>ºÓ¾}m—þ lV1é¼SOì�ùMß�!’RKZmÞfxFÉ‚ï¼k’=&é­º®­áYu,ñ"Gâ5�OWA„™/BréÊ Êl솅”T^þá™gˆDþ-,Ü`ñŒ‹à�|ùÒf‘Å&ûæÕmöÕË›íÔÑm<Ö¢²^ Í:ûú•­"˜5x'Núâ½|‹ï}Ÿ»~z*v<‚ؠܦ÷"·MHmiK&ô U™¤æÉs>~l^��õOœlgä…|þ°ZÀleoœ æ�™6¾oSÚ£Hr y iÇÉV "‹Þ­ëáqûà1i¼Ž<ð@2¸Sä9õ;ÈZx#ò<´™XfÈ™×v›�Øãðí±]gž90÷L‡˜j%&È€E¯¯‰sê/Ϩ·Î™óìÛ¦¾qþI"Ce—á½è=�ÏuFLÒäw\Ye©m@ )©ÊÔÄàÉ ññw ¼˜P’ɇBÕÏ<« $òaµ`eb!x"ÎÛÈ?±[»Q-(ÇwYàñô±€Ç~2Y �ºœìEÀ�ÆŒ[•­µnj_hërG$xHœÓyLY:Çyc»Úy#o˦µËÿ̳SOX§ ‘˜È‰;Ç)¿õµûV ²~êRŒ2©……•Ïòy$×’VçØj� +‹³#ŽÄBð> �‚²ÔD2kd]÷d}‡«,ÿ¸#àðà¶)ÖJžD ä#÷æYc.Åx� s„è8o®™Ëµm!@œ¼¢!AÀ]^ ĉAq_ˆAX� d""9-tŠð̳ B$-…“‚�ðgiùèï=¾Ü�° {}O0€PðV�À${ùâ(|æ«wÏ1êF6 óÓIŸ%•´Ü�‘�@š/žÓ~ÓUlçÕ³<ðÉcgú¤¬/›n/ªÊÃë‡Ý~×Mêf4ZÔn+õn¸â—jši1�ñÓÙu[@Êò{�Y`YŒu Ýó§:G‘æ‹ä¨9ùmÔû¬‡=²}Š�|a ¤Q ­›`-›h¯¿ËŽ¿¸DÂuà1t‘tÅ⑜íqþ}ä™ôlQ"Ð\�!á&2Î|ÄpM¼2 �…†Ç‚4†ôá±p¯CÀ¡iSÓ?Â3Ï*‰T.[odÞÒ”â"ë H¢8�ÇâóPDD>¯äþùÒ܇Éi¥”ÒxHœ½˜]�à;»ðŒÞñ¤óe&¥ÙYÌ�?vŸóD Š� iÑœüoõ`Û0%Ñ6NM´y£Û‘î«L­êd•&­—#ðU¢×¹I «/;‹@3ÙN=|Õè,¢ìèû=�Án‘.˜Hø^&ômi¤ù.Ïé-)oV�$‚—²}ÁHK–×329¾$é·� çŒüixn<Ÿ*oŠøÒï!OõýYI€ë�&œÔ‡‡‚<æâ'¥ô——ExæY9&‘ë„'+c K¹L,â#�CI€FïdÇ‚‘È&ì|©�4\ÑŸ_R)óV)þù»Y"8Š#f$ªuËíÆ³‰‰|ož‡rެE,IkßüÞ6sxk#Ckh×Fdi1R}¿¤±Òx�Œ‘tˆª¢EUБ]vBl5S€HOOƒ÷y�iÁ}×[¬”DrT¸:Â3ÏÊ)‰üLX)XÙÃÕ�@Ç« FRb2á½ÊÃ;¦"› ¿K>jg™þ:È#C»aŠüðXÒ"Ëjž:5-Ô‰�MFÛ  ­Lªð ½7êFxæY9-8qáúg¹Ô_ÅI–‘­åˆ¤Äxc¯½óøjÕ’ RW¶Ú¤¤)^2X?$Õ)…ŸiÃQ¦ÙYÔˆÐ8r÷Â!Ô»(Uy’½R‰¸øÈ¼sêD¥K?-$­©i-”®Zb+0ÍÕÕE´òU£û[œÜ€ŒÅΚøÄ!Dæ§•j†JœîE/#[ëÁ­“´�ØaН_Þn{–ޱ%¹}(L Vžƒ¹ä%2±Hó…Ëz„dÄ=„H hîuáIçöõ¹á™gå�H®^Ï<ªÍg“’ËÏɪ*”L|•ì+!²³JE&Ô ¼õÈJ{ï‰5öâ¡<{îN5qÜ3ÃVOIµ;T9½{ÑHâ&èõ²KÇá&ÆÚÎùiöÐÖ,{zߤ,î�»…á‹'Î ´ÓÝ÷ÍCY6ch+ëW-}²\m ‹.H�|­Ý‘°ð>B#Š¢_˜Š.ûÛÖ¹CT'³Àejb'G̱ÝKF[‡XR€ƒò¬ ÒsñD\ê.‹úhùOŸØ�§T˜‰Ï�pÀÙ¤ò™á™gÒô�ú2áGgy#ÃÃå��ÆúèŽÉü¬`²oZaž sÒ€Kí™ðþ/_&Mxƒ},<¹w†Z«4ÙÕ¶~Æä&à¼Ç°À…RÊr1X¾WƒµŽÞëkqì=<ù¤#jGžÛ9ÚF$Fâ1”¶É"�]Åv;¥¹¶�²XüX| @³–6Ý2%N„ÔH+”ÇvNC¶‚,Î êJجž6Àâ´)m!Ñ‘–Ëu�Žì2®.‘�.Ý^ÄF¼æì,®F¾nÉnØWŒk7ã<ð…Ð1Â3Ï.0‘\$\©ÌšÿóIœðž`áÂc"†J!áÙ?Åžôµû(r'î#“MB©ÉäÔ1Ò„wسæ(C*Ã6ÎDC@â%Zà|Áw†]¡”嫪OjbéŠÍä)¸N} d¼ä—g_=u.‘0ŸýðòÖ½yuv¬%˜sá†L¹0^�t‚ËÈ0›‰'„hO’÷A„ÚÊ�µÛ5MÉkøŽ ‹"=’7^aÄIZK¦Š‘÷«Eö<$é†gßA–#IëB iÅÔ›�¨Ð¶ñm�ßE I@0œ³<—JxSHŒ�K`-Êia€pQ„gž]ù�ðWáráß)-ëþæ�æ×Òbõ2 :ÐcŽN†  Ò[w{çÊÑ4d~:ÕÛü=õTšÃß,È;!‹‹ŠöR{&àŒä“÷Ÿ\koŸlÈ*ìŠ ÄSË0RX3µŸeªq`Z‰$§ký ‚Ê݈‹èZƒöD\!â·G�8œT+ziµi| ²GÐy�F”¿¡"‹‹Y¼¯3/ mCÚ»;/$d˜¹Ú*Ã,ÁîX1Nßùbûæ•íE‘8õêÉ“ómÍô�FãÆþú¼vQU¨r/ìzÙÕS BïôF dÍÕšÈûã{Žà‘yI¯y¯ê¬l¯¹Âžyv¼�? M„fƒ:6ªºuî 6ê’û„!7$'?w`D,x!“¶Ùž£šŽ4»kåh‡(ÈÝ_’F®͹ü-ˆ¹‹Ýwd²€l. åQl/5™œV�Ê-S¬b¸-ËI±U“Sýi¨ímÏâ‘!•¶†K*›:¤ƒêD’µpŽ"6â;X�ú{æìÔßç|1’¼Ñí´XÞXtš¯k�î«cP„L,YW¢ô^$·s‡DXøBë�532µVLì£ÑLÅÂÖºØH0ÉËw/´Ms‡ÚôQÝlnFOk¥ó>‘àa± »*öò0£Ÿ{K¼ ¦6…ï€z²Ê¢Èò@VìÞ¬¦QØ^ç'“tôznyVæDr…Ð@h.d¨UùR�uŸGiüïŒbÞ?×¥ÀÉò¬Î´î8oŒ/„Ý.Gȃ]0ä‰$Å×bAc±bÑr“ !‘PîÞ‰‰d*ÍzÚÐN†¤x߆lyÀ‹  ƒHö]€ïÛœkg§YJ»†çk9¹²PhGJ Þ» ¬²¦ã0$âš=’ž ¡CzÄRøZ±øû5„Xxž‚P¼““úÞb#<ó¬,�˜ˆ°rDRómC»Æž"°<[³Ë©gX¤ºnœÙßÖNéCo*Z°;¯ €¬æª�bC�6ÍìOìD„ÒO�{ûKrfÈ^ñÑZOByîŽix*tø±�ÙµJÞÅz4tÉa‹ðN‚&ðõ+Û‘ZÈâ ×bª…4A^ÏâOA§ÿÒ¬ñÔ¹[®iケí-®Ð`»Kñ¥¿C›XTò ý¦Ü¼ôp.š4g\2¡—<ÝÑL=t’Vq‘/0ôêàÚ 6:¥…5c4®b ‘’í¤<È“šhd»òH$€û‰ãmð}€`%¸O…úžyV–¦ñ­CºD’�vÁ² ;F¢3†uT t0w‚íŒxeÑvÙT%�RŒ‰Áä‹@òGto’/q»tߌ�hÒBÓI ÆCòz)†1É�ÏÀS¢Á!3;�߈éð¼<ŠlIF‹¨\ñÍsÂ1Øv*ï>¾Æ¦ K‘@¢ Ú"s+x.\Ú‚H–Žïļ²} $@€=Á—þêKéõ‘GQý£B'Z7­?­Pð!…’B™^ëô]/µ-yÃ,…Ø�d – oƒ<Üà*b$oH he_ÎQI1_èá™geicz·k5´kÌÉÂd¼xd®Ý™ãA¬OBi_öV c&ªRŸŸ¿=/í+¹èŽÄÎjGâ:òBh3Gvñ5ôÞÑ8ŸycºÚ =^ªà<çCàš)‚î‰íÌÑÉ`õà–,y.à "ùæè2³�ºŠE&GÌSÝæaY`‘óë¼'‘'5;hÉð½B:ÿ"mݳb $‘Z ™*0V@ž€s/_6V©2±†r,H©¦ÇàÒ}K ªàiysךñ¶0+Ŧ�èbÃäÕö”§+Ò¡¸É�W"ã9�ë»…éžyVV6=gì/Æôi¿7_iÁ€]eÖd°pC Ì Gâ*±G²{á°3ò†J0ø© äÂBÄüôøI‚jcHqœ+ñ�-"“Eã{ØSûD|,Õp¨…HV쀃¾pd£eh¶8Él%ܽnI®×VQ÷ï�‡ ¨#ÏÍe¬®ä©zFAmÓñF˜/îâ % ¢÷ÓßèѪGî�ŸX¢ý Ð®±“gðtØ p½�ÉòÜÞ|/�A©ñ…†_^Ÿm÷mÊ5<“ÍʼKï×ÚÚFUq£n)æCÎãœy~·pPøq„gž…Û6nÛuÉø´c†ukr:˜…Ž˜qj>ˆMÐÞœº‡·LPUú´ý’ÄH`_êé„C¹øb+þѹ®ýˆùùY©­ó™Ar`EºÑ~ãÅLX¼ðN|RØ](‘|("É cœdú°N’xRI&I‰ëH ’÷îͱÆ!mA&HYnp?#ñP‰”¦ª»oû:ýošÝ`ÝZT²­•ªÚRu(ñ·Zj·fÖ'±£ý÷ï¶kÿùG«|íßÕÎþVql_˜é›€XZ�$°ïÖKj=ÿ�êQ ºKjf ÍjB$x$xb�Üw‘DÀ³Â¯"<ó,Ü–32µþˆ¤øw‚–_|­B�Žp#ÃPyNÖU‰=tþgLSüÁÉbNc™ØT«3õ!ŠñdÛ[&’ÕY ‹ÈKÙV(‘|¦:…Iƒ;„�H�·fŽH°eÙ=•Á5&¨û÷¾#’‚kIöÎKQ#Àʾêh‘Y\d.A.IJëºn&yIÏ›y-ãn´ØF³ØÆ³&�ÿîƒÇÇþ×¶l\jO=ñ¸Õ©]ÛôÏÎÀþïrû×_~«4ݪÆ&‚ÂÏû7纴ßÒBúZß' T¼C(+”6EíWz´¬K:-Z¬ŽÄúDBÏqí.–TÞð¦p]„gž…ÓfNÊüÙØ>íw*È^¬E &Ãèñ]¹ ºÝ3Ú~qÛ|�òºaæ eŠÅ–Ù<ôÉi˜uA5™>ÄKÔ®eI¡D¾ÔÂ4cd×°¯�U †D‚Ç]öVɤ-ÀÌö#ÛF2ÔŠªhb%‰kÂH¬€´RH¤4q$¬­ªBç yÌmñy‡î²š5k$rÑEùÈä—ZÃÚWZR«*xº ±¢i&DR|õÒ6#-ø…ƒyöè®éН%¸d’ Wéú›×±V Ú7¯w‹Å Ñ5kip�uŠ»ÉÚË ë"Ï i¯ÉW‚WKâYx-½_BWIZ_w�A±"‹M‘¹�¸h޾_ì6�ÏÁÕc-'@6 7Fˆ´X´—dõ2R‚i!ÿèΩÔ"J$ÔŸ,�8 ¬rȘžqT¹]OòF!ÝA¾pôö Å‹šÒs "x#T±“‹G’ëIm_ÏšGýë"ÁK¹õæØUW]eW\q$â'’‹ìÇ?ºÜÔþ‹5�ü‡%h!FŽÜ¨î½'ã¨# ™‡èÉõ¸È„¶7LÃ,ŠD²w³VZú˜Qöë_ýÒ.½äb»ìÒKìâ‹/¶?þþGÝàJw­-¢ÿ£ë¸Eq©úŽ`Á$“\áâÏ< µ j_ï"eh5ÑîÿhI ’ªJÚ-=²ðL­ y�ÉE¼¤DéÀôÒÚ­¦ˆ£\¬$ü X¿ £‡\“iOôÓ‰.Fâ?;9¥Q¯›æ3‘4“´Õ˸�ǂ̄ûôñ¹'Eù‘É'jÞ¸ij’¥uihc*ñºø¶�ªLÅ4n¡ÈZÒg4¶vM¯ ”¶€HâïöÏ¿ýÌy"�DòÃ\fõkýÅšDêu�ÿaÝ[U¢Û²RÊç²ø‡ð‡ÖŒgúe¡—Xçæ·ÙºU ì�Çíá°ë®»Î=ª¿þù'"Ês½°–1WY·–•,¹M #V¹ônWûBÉvÁëìYXŠ£…‚•hÃÔ“'3Gt¦®„@Š0Í.IÐ(“j®-ÎîSf]Xñ®XLeö´ó‡A&ÄB 2¹™|©êvf•�VºaVZX½&Î ™gõäÞ*®œDF\0òÖWß>3ç ÄQ>z`Rþ}«�Û+†>L ºy"¡ì›•T€¼‘ÜtýoØÁJ$¼†×6‹ü§%µ®¢Y00ßN2QÚùrÛ¶+P¦Li[ÓZÇ*!¡î_­OJ7;°Ÿ�ÏL·ŸÿüçŽHÀUÿú…;÷€§å®+±U• A$„DxæYˆIä?Â㥠^Ó\Ï¥Óº„Â?ÒViÿþì�’(B@mŸhc“ãËLââzhOgßæ+¤C$Uíj¸ÉÞyl•-Ÿ¢÷Ä„µè Ʌ̸C«ÇР’™EÆ�>wãv Ìࢭü™y£Ú�îß¾ÝeñBBNØìâ[*&r‘@ÈX�G‘DØo~ýC‹ªï“†´ÃÏÜL)¼›�¼ q=¨D‹œm¯/ÂIt‘õ¯Ô9þØ~÷»ßÙøÃs¼ªk¯úÕy‰àiumq›âP¤zþAá—žyB¹T˜’ÙÝ}m¿I³¥k/•äH\û—ŽpA÷BÅxs•UÕ©L‰„˜ÏæÙƒEˆÙöñ‘ xJ]‘0¡�؉ò%šS²\µ(½Ê@r‹UO³T2ãháOûˆ¤È »¼‘ƼB å‰Í#¾�ûmÏ�Ôßâ$·ÿãçΗ]v‰ÝtÝoÎ}­Ð"ö?vÿŽly‡ûÂF$¯“z¢,Õ˜ðï­—H$^qŽÀóþÛ_~ �œC"—ëü«UúCQD"Rú·Ò£ë^¨XÉ1áßžyB"©,¼J =ÞØ®x%ªèKð½ÔDÂ$Ä9cº•i[o‚î+5L‰êj–'öL§A]„WÓD�ÙÌrG»'m8ìÒAçùé‰Tä#’x�ò m—òÔ|?‘Ìçx%oß�“?upë3á$i¼’VŠœ½ "ýóï?7<“_ýòvóõ¿µ˜†.ž=¥ÍÉêìjJ þƺéý�š—8<‘³¼©[oü­]rɹ²ÜOr¹5¬ó×"½‘ÄÖ•õ…{Sfrm`±çq῞yBod®`¡‰oÆvK£½·ž½}*»çR ºÊKïQ¦D‚@�ÉnßÒÑ"�<˜�¨60yvçª ]W—îF†«&õ&#Ž9î�$ò™p¦ YðËç×™½¸2qžI ¡œQÛ”Ç7ŽP#Ì&a=÷ä65ÏY”AŒ2¸H抬wåywòÉàäúJËv]€Ã/Ù&ϯEô L¨]ýOöW\ê<ÀãßJ–‹nà^_àµvos³ÍÙÖDÞnq3P Èüã1ؤMÏåžy""©!¼žb¿hÈDóJú‰L±ƒvµ$%ë½µ€yH Æ?´ŒÈdBjë3t÷�1¼³ïé9 � 3HTý¾Œ EsªŠÇy•É9e÷oãŠ=RªO _'ynƒÙ±->2y~¡È$Ï�¹Ž\¾U3ÇÝsú„ýÜ©«(8Íñü e8±ÃmöúÃKÃN$õ§§·‡¼ <—(¥÷þãÊŸž©¿$ üäÇ—Û-7üö¼Aö¾ UlýÔ®¶2«›MOkc#Ãôo†,Jþ2pÍuËNmÅs26;Â3ÏBD" ³Â» jB5¶ÆÑ&1SCíRŠç•@®5ºFænŸ?ŒÏ¥h™'B …œ{FÕN«!dþʉ}5Ÿ}”Z;^Í “íñÝÓñLÔË«#sÜùOZféÉ[ç¥)F“c/hz¢?øŒpZ°³ øÎŸtt“ÙË«Í^X"RY¦ã"žŸ/,P×T›6¸MXãP絊½ºpÂ(‚H:·¾ÑŽÝ¿(¬qHê£gÖYjb-‹�ú�˲:HpÔ» sÝ"ðø|çž9°¡=±e¸Z:ÐòFu°ìÔxþ‡%1#³w <ÕÅt Í MÙ ‘s×C›³þdúa„gž…€Hþ!¼n]–f‰¤S®œ˜ÌôBbÂ-0Së�G—ð{KFš£¿Ýø�Çá,ò�‘]è ù� Zp2ï-i;óoÕ®#?£w ²y¬«#o²-Ò¬Œj]‚lÀïÊ0Xªóh'yk4YqÜÏóÆI¸‡Ÿ>³Ö—:{|§Ù+k…uÂËJ?±,Õq�Ý·zXÀ}r÷�cÈdÒh[¸à5p@F:ïbÜ©Õ öúCKÃJ$|öû&æ÷îP‹"B¼¨%9ÐDßãÂ'©ýMöÈÆ4{iOº­Îén“´Ò¬�fá”@E­Q¬M\†²ýú›Ÿsú½ùíKkÚØ›¼~[ž…„H„SBØÄ §bÒá]+G+0=]˜FðÝO í-yþ/ Ö„ѸËìÝÇ–JBZ(/`‚« “)‰¶ñHNxxtõe–žÁ°è9òôÚ¶¥–žÒAÒô±Y>ÇÅEÊ*‰Ñ8=¨ú^óé³ë| åk;ÌŽnôcÄâÃKËì‹'ä/LïzZäÉý"å˜ûÇTÊ�ížÓ„>íjÓBÄ-Ъ© `OÏ]o­›\MV?«nã#NÁïy]RÇJöÎã+ñÂå�Ð×ë›…z|B‹¾_’GRÚÕ¢ñ$iÌ…�Já1‘ñ×ØÎ¼${çî{`Í[0¦“e¦Äq?Ã:Ëe†Z¾–÷üœÚË<¤™.Œ+~l[Ö){mk޽¾ï*]¯WÝîY©HäaYÙ­Y�â.¥*ñ 4u„HT1GÞÈbÅy o=¼B�\ÔXo•j4V*è9Bµ(ƒ!y½ŒXÅþec žS•LcÒ eXÍÝÕØ�‘q5eHÿSP€º/‹z1„#€Š MÞ³!S÷+¸BOîï·ÇvúÉd»Hd3p¤"2Ñq�½°òé¼qÝó§í¨,±$¼?¼À�KwÔPôi_Ûzw¬i)íkªÊ¾±dËv¶(»‡­šÞÛö¯eÛ µÑÉM­Wëê´¤WÇàZá$îÿž¾Ñ"ÿ çÈŽþ5Õ íÒ˜^ BÉ>#m—tæø"¼*'¹Ómvç²ÞöæÁ,{dý0Û——j³‡·W£ÓðÅÓø¾�—f§ñ¼žX+`±QõÎ#˾²»öèZû^u»g¥"’?O Vpl‡w¢ ŽÑ·š«1ÙîYŸ¡zŒ,px x"o?²XÙ9ëi˜( ˜=VÙ_Y�;+u^`Ä(HÃ¥;,±‹;VŽS‹‹Lš.òŸHM#‡’d³zJ?×EwÒàön7X‘�¾rr?Ý£`cN Õ¨0`Ò žÉ±­@�·¹Ç_=¿ÆîX6Z#~‡CÐxs´²wRW¨7yã osžÒ»�Ü>“"O ‘˜ µöŸjx¢CEd¹CZ«ÿÙzž‹7rdÿ,6ĉØÕëçÙfoîÇKaw/Ϻ-ž/÷A$XÇðªðžHé�‹ü—µ‰½ÖÚ7½Ñz¶­ªØ`{{~÷hûྉöÂnæNU¼0½³å¤¶ÐûCì¡t˜€ðHGß½p¤A‚_<¿Y"×êÿÔJu>ÞzR×ú¼°Pøy„gž•‚H _”©ÃDE-äÄLÐõw䥭S>zz•Èd¹«å�¬´O¤åðä ‘Érõ>Ú,=|¹AÌ ydÇc,î+ªéøäÙM4R¤…»Ú�C*ÓŒ:æˆ@*Œg¥oÒJy"ÒîDAsþƒA2nw]‘Áb—ѧ¥®=è™/"êevúh`KöÝfÇw ;ówóîÃKè1FvšæÆw%㙋‘!•=3uþ äõPøÉ†9›$øžYàùî®Í¤søjG¦ à¾:IK›Î@&ü;Ë_’• ±âeãU)æSË’ÛU·‘=#-ot[?)éÿ±÷Pm´_ïý»}îîîî›ôu³º»»»u§.”º@M ¥-.Å,™çîßbú¬\.¥ q˜¬µ×$@i2$gÏ9ûœ}Ì‘¥ÝLÙáѦüÈ“-Ù¿ð+3C%-v:µ"ÙÉý�ÊÂÉDè”äâ Ò;ºn´2û…7–zÕf¯ÑkÜT¯×š#¤žNâÝÂ"’¾‚‰="ðù—ŽûÜ?kP'ö�‹8–B&ªß/ò¯Zm @À`nƒ¬äæ‘ùš _hð·òål¶Xð›’[ÇŠXVáÚ«õ¬sÍIÈ¹í“ D3UW»)¾dÏe.žO‹¢|䯺£_6L�Û«9‘ðØ›¯(“ಶ…`ºY€T•¾®ïŸnö‰Œ×Lî"~…Ì�²!ÉÍÜBÚßÑOWô=®œÉ ÿH31Sõ^XÑZ;.¶þd+QHä}T©ßÏ{cØgÏC"”Qq0°Ä°Éáý•¹q˜:°úšÝs¾0àôšÞZ6ÒÔfL0ìů?5Ñ@$è"'Vô0kƽo†ö4ç-R»t .Žxðœ•ѽm ‘ɽÐΆ¨|<Û�…P®ó]Û‰ð7®1¹©çLÞŽÏuß+my·6“È÷„Íq ~�SÔ3&u^_S�±Xb¹Ä  «Ï ºzZ ©èC»Ñ°¥°ZäÑš�;ÀÖ‚9?Ç"*ˆ…]ߤôY{¦ô“cëG›s;&Sê¢lÁ‡Ñ^Õ€ÖI¾ž¤Â¼ÀʉŸ‹l§µD$tÄ¡7ñ8°�šÒ!¤ÝRPµYŠÿêjS’¹˜ŒP¶,=ÈNh#…LXaLvr`äœÓeG)kî�÷õÜ¿b˜“ò ÿw<Áû† ^+åQ´?R‹?Ï\Îå…Æ\t‡µûÎø:™H$H@ÄteQzä< ¨”Û�#_#“ÂÖrt‰—×Á1µÂ伬þæþz ÞÊ]ïÖf"ù=ëògPbº¡¡>ˆp• ê¯âª»Þºì† KϦ˜’3ËÙ'¢9‹ÙæàêáÏ,¦Ñ×NíJ†bd†µØáÎ}åøî¯Ù ×_¢}"“ e˜É½_×ëšØR§„�îd¾/’™�uG]²’ ÆQf’l¾9¥ÀDÐg&~ŠÊ]èr¨�pÈ'Ïrµ�¸)¿Ø&€x£ñÚF€y^¾²3)õ­þüÕÃ`'38&k÷]Û™)¦RDrn}e*½Ìº ˜!?ÙæY'²#ô=¼�޽ÕdAw"çp§ÖCo�ÛÇS‹ÒîÁ•Ãɺõ·]`XÜåç¼ò9<ç;i~}8êMÙ… æìÄo´5Žx7�Hžê ’�9�…@ ÉB îC m‚ãÖÕY•ËUfñéeò™t—HNlƒ=¼4š¾´ SZA D€·%ž#ƒ†NÂ’ºÅœÁïšìݧÜÉD (ÚÇ  ºãZ ä®v¡(3YdŽJ3Ù2»·‚W®~™M äE�AÏ›@@k§tuT²l´+tœ‹šìµ~'g‹¿µŸãÜø/º38 k¶0Ë�¡4œžh®odÖMü@¥¼ÇZxåïHIŠ Ï’‹Ã’4mîuÑÃù¾ç²0Nud”oO§N„8( Ò�%Ìf ¤h ¨œeôÇ6æîUc*¯O 'Žx7�Hú%HD<%cpÅÌ-Ñ€µ|—¶‚΂vB�ÑÔÍNº„Éeã>§ÞLm™º=õ{&D’ˆ»·mÆD‹îŽ}u=^É“›†ÏiÌ¥q�8• S8¢;Ñȹüÿ§ßÏ,qTqZ.sA&ëMÙ©Å2ª£á„\šÔ4Ñ�N&È�¿)WÍ�Š ˆÍ˃| á~Û¼¾~‘¼ßꉲ4{>îWþ£uÚÉ^`Ì¥EÆpd¸ó’ŽÊP|g&››{†ÉS«õ]þœ+.¬%¦¬ Y�ÏÒr5Þ‡\Ðpѵ?e¨ˆ¼ïKš¸¯îÇI†ò-%,H¹öÒZ‘àj2ûÜ�æFŒù»$r¬¼�$Þ­M$òaµ`â > ¤äd -e'¿®®Ñ[øÿ*U�¿s¢ÉÅ÷Š„xÚK�K?¡½øÂÎ)B‘ �B˜\56 7&àì AÉ‹»¦èÊt¬\Чëµ-�öÔ$¨—žY¢×¼†®8@)‹Ì¤™›À4Í,´�ó~dBkpÅÙeë|ºãsÏ*�Ñž�~ÏP6G™ÐÕž :Š�?›¹Pƒ„8ÿ>[VK2Ð.�Ý À%àòbc®®hz|i¡i8;ÜY?ÀŒ‘å mË�DоÁùpËV¼ß8wÊ8”k÷?¥WÌBiߥC ÷i:ڸʕîw%mÍ "õÑÊ(ç‰8ÖˆDÖƒb»ðϫȺðˆ1—!.¿Ñ–8âÝ<"ùEá‚`â †ÏnŸDÆ@�·ä°�0Zàÿ°YJ¥%aÄÈž‘•Àh¦Ux¿2K2Ê_\i£¥XÁ3>º‰;±ÿ¨mù$ÑÍs¿yx.$¢×´ÊT ” Ô)B xnUg¯ ,s;¦Ø Z#ßµõúù•¦ôôrH;}eEý…~´ñ2w‚ ÎóÃáš�“²Êy‰+o¥[Ž�ìÖ')‘¬w9W#¼C"q ÐQç<{±ÉX7TÙÄË\4‘i@ú¼�xYLjÝi™ˆ8Umëk¦tÕâ²AtÊ¡u¨t5N«ª' ÛÐ�àfkMµÞ· º@ð_ß` Ä>·ÀÇd|‡E"—(gI£Ð©-qÄ»yDò?v~$Πuñઆ@R*Qò€Lj¯¬ $uR©½²ÞÕTÖ«d3Š)z튟¦ ØE¾Zïá§E@´ž^k·{„¨¡”ÑsØÔ¨¬c-Û!G^ $bIÔ]YCwÄb‡>9RÃ:Å îy;Mþ_ë%§Í´’r¬ß±˜Ì„ 'Éi†Ž§º²FkbÖ‚†Š¥c?£K‹€É¹Ef­µèXt%)™lpL 1�€6P-"'[ Å\ÓúiÝ žWdnëU.Ë c!Ùéý:Aؘ(+ÆÜ“ˆò˜Ï¥+ZŸù÷|ÙN¬ÖŸã�t›‰´€±m‰#ÞÍ#’^ Ôm„G £€ ä…™K ÙBUö›™Ä ü¿wN.aøQC�3 bæ¦Y=!êÔœ”%dÃò–é¿N.²®ðy”@ÈììùãèäÚ¬ÎÞwÉD„³Ú�™Z¬ëEÜͦ»•A|ª§;ææ¶ÓÍ"“u¦D�޲K‰2Ê�Ïíóûš šRTÓ‡DÎȲãòÞ†²Cr IL$­ŸHÈÜ ”ùæ¼ü®˜2ÏT–{HP(r°S™D’ž2Œ²“ôdj*!¦˜Z·dÅ969–4‚Çí½FÝY÷#‘Fá“PcˆwóHä[ÂÚD!TÍRŠ¿gé ±½€P˜ó@€G'øÅ’H¬>SqaµÉ=2ßá*› å�æ(hµœë~ø9Ô¼ãÒ¨0ìóç¸ò§~ñ¹o›Z8g–Lø‘Í:e%«TYEÖÒŒD\ÜÚNrÄd(ÁL„8EJëEN+•»y%]ÈÐó42äŸX,Y lgQû"{N,ªUŠ¢£ ¢¥ÕùjÚ,È„Î5ˆ…fÇÎG•vÇø®o°ƒ¡®æá€àÿÿÔ&)= aÜw…ÿ5Žx7�H~M¸,˜DAŸwŸàŠšTBað�’Fsȵx�ŒÈÉWvriß ž W�Ôÿé˜�LȨÈNl‹f Ál Ï�vPÌ+EÄs2‘;×lÃä¡€–HÄ¢à5uEAi_(”ÌÈ)=“B銶kŽ*�-±¾Oú>uþ¦2M2Eè¤Âߢ…, S²üâ¸OFç”+±íÖü½,¶8 $ÉO7¦ì"¿Éÿ„G¼›G$O u‰6L7XÃi”<æH‹Àpaq÷’�†6]“—W2iT èÕô9~ºd¨ùS㦌ÄD1 �6ãQÚš¤ÿSÅÍj%£k1< ¨™ÜZ'’[;Œ)ÏRÀÉÑñ’Ñ44A*œ`jëû<î¨àõ3-Z>aœZ| �Sq¢xîjñn‘ŒJÌ¡ºGÌõÍ�“0K7i>æŒ ú ||Ðl ›cK&×7ù•™øLB)E0<†fB»f¼æGܱ±}a>¦þÚú`ˆ8BPç‘–Q{UËüAÑ Ó¤›$xÈÛáΉ\ †D@–ðË¡Äïæ‘È�{“ˆà ›r Yl„Úkf+86 –n¹†ûâñÈL|7Íó1c‚v3_�HØ…ÌöA܈„@Ê[èE§–†r^ŽA÷â“–L€Zì”­ìN”ê�,ñÎ~J�¡�H5U7=¯-ï‘ü¾pS0‰L&\ecûÎìruöº{\¡x%êëq+sÝ84—®$2Êpd%8áÆ�H¢�–VRDl<Ŭ#rÄ™ º!f �"ÄÛrWR!GÙÙ�ÍI^f#£Ü8­ªr{‡G¼›G$ÏY­'†¶˜×`Êüö‰EÝt#aˆ(Ïý¸ zMaæR´tZƒ™à¹ÆòüX"¡¬Æ ¦‡¸#GõµßÞÕr)„RzÖ%•ñ(y9!�—4å\“ÿé3KŽÌ+i¼º¾1é2�¼í® ~>4ò°È©25ùžÐîÝB&’ñɳ_ãQÃ:Z[ÓR† p³c„n®¸wrAbdIè8 ÛaM?ê«cæÅÅæ>ºÄØ�¿ḑæ‚l28/5Öf&Z h¿Hãœ1w¯×|rós%Ìc:„ yßTÂÚ;·÷NÍ¿½gªS¡U"¾Þú>•¯meÎh…oæ¼þSóz‰LJ“‚@n)û+8b”~SžmËXmBõ­3¦±ê—B‰!ÞÍ#‘_Î& ‘ØÌD<û²SçöÆK»É‡RÚ"3°šIÀä=Cv®RO,ÑcF$èH,…Â;�¼c ˜§µ4Ù6,È›J)Å™Ž)<ì ŸPöB”‡H Sr‰jçÓ�Mõ×7�9{rV—ŠÓ zšìÕCLáA µ^Y×b«QÚZù‰…æÒÍ´ÌéfqrvW“³u\câ–¸˜Ùc8Ïdn6Á±Íp*®™Æòë#�1_ %Žx7�Hµm¿IF&‹F}Šqƒm”µÐȬ°põ¿m~?ÜXi½�é  ®°3¾Ãù ³ ëŒXu²¹%•ýBšHe_Sy¥à€ák"kQÎ1Z¥.²£šó+îžYØ»2™s»›‹+™¢ƒjÐ�ó­ïZÓFÀý­ªU-<0Ë\Y7Ü(iN$"¡¡PÈ>H™Œˆ ªrLuÞQsëÄÂ~IÔ¼›×öüŒ‰<­tµ?±çL’kiÏÚ\Ù_‚§db¯Äc „·Î뇇å¥XO´#²cÝŽQ#ÓÒÖ=9f¸½ÛÅ.Ž–P,™”_$h9d(tø#L$F±ˆ >� ‰¬•ƒÍ͵òv&ä¡áîiæú–1æbÊ@sj~O~®9(qñó TÂÒy-ΰz$ÑX’å/<»¦6çàÔ1¡Äïæ‘È� “¤ Õ•ò™ 3”rØÍ.,`/æDÂ0ßñ ã´¿ä ¼·x~1ÝÏ>¡Çër(‰è/+—•±? ‰b¡ÂÀ"€LÈP@ÑQt·]8Çf'”g"H$â®i>²‰æ¤@frzA/sv±ö³,ék”µˆ@lÒ"ø=W6Œ4þë›âL »)2üY±%­«¦øüzÇu{Jñn‘ü±�/˜$e.•t^ À>×`†ÉH¢ÑÅå€Ö~†,àÀª‘Ø¢ë¹Åö<ÐrLktÙÙ,ðãÔÅ–Šf 73Ik*ÅŸ°n³–Pʳù~¤ˆ„Ž+H €H”É òKGqâÓ»͵y¿Yâ¸{M¸nKZµù™¾¼£sîI�Pâˆwóˆä%¡A0í�LجÇf¸™Þa~¢i`ѵ�'¸£μuUÖ飻Øíw1Ãô~oc#Cy½ˆéÿ¸µC[2A;¹³×˜¢c”·hKE ŽÜÇÒ<DBÙ*’DBé«Üwu}uLIÉ?À¹²Ÿ‘„¯4Ë4ŸW9ëpŠÎo¨�8B½ð|(qÄ»yD2L0íÝ¥`”ÈR,2)$²¼¿R&|© ü¬V”AöC†ç—µEI ¸d²�‘¸Sï‡9¶,(ß½Î÷˜�wÇG£Z‚kÏ-íǼ³!Î)áôÂ^æüÒþJ8F–ÝÚ=¥2ßtß�½Sk”Éøîf.!‰²­ûI;møÊ²ÅUé:¥ûLÑùõj“Ÿk¤‹@Àqá:‡K¼›WÖÊL{‡uþðiCVB¦Àæ9Öä2oÂ#„‚-ý�‹Ð8"B&X¥]7ÚàTÜCÙQ,2°Ñ�_2l‘dû`�Øñk ¦‹ 2ኼì\kAÐmÞVàf:”‚ý34Ò2ÃY8òÑ2•§—‘uàö �,†ûѶu� �@Æ´úúTÒª¸¶Ûäg.Ó{~6äÇ=VÏG¼›G$~Át��Œëöšv¾O1» Àì‘}ëÜG?±Ë²\" ›LU>+>µ òŠÕP"yh$�e­ÄÌL(qA&ÌÙr£uÛƒ÷‡¿'=^[mrÂf!±"’†¢³¦æÖQ•³6››G-‰´„�ÂÏC¼›G"ßV¦#bRoé&C? „é#s'´Ër%ßÂ.“¶ z ¿wÍ”®ØºÇ„(G)#añö‰Å<�„å*È„ÀêÚ¦<�‰]é›|Ë¥Ò¬­{,Iq½Fm¾EçÖZM¤Œ 6Žx7�H~N8×Q‰¤‹0øÓçÌüá™ÙÚÏúÞÜÃóÈJâé¸ {9Ù@ƒˆ„ìgjß·c¦‘ôy÷1ˆDÖ(«›D¬^²3ÐÌ1¸Á;DùÂ#ÉB"®;o¦5WŒH¤ü²a^¤üÊHÄvhÝŽðq°qÄ»yDòB‰`:.˜‚—Kî ÷ÌêÉ� ‡·�/4tq1ƒQ~~EØ�Ÿß;¡Çk”¶¢ê¯Åïg#$OÊõ·*{M¢ p÷X›ù¦6à+dÆýÈ…Ÿ±–* ‹¼í®µÉ%Û‘ël„.­ê›‡Lá™UF³"ÊF*„ÿ6Žx7�H^œŽM$ˆÓLÁ?åh­_{ÍÙi‚ð 1l¡M¸M¢5 ]`tO¡‘ô}÷q•ž¢ã€ÌÆA=M›1{H°†aø2 ˆ¤ÒÁ…C0ƒŠ ã gi‡E7i½äÅÏÜ Ûì1ÂC†¼ž]LïóüI0DB«¯8l¿¹sr±²î"’[ÂG¼›G$ÓÕ%'gØ/T(ØWdlïdlg k/¯Åb…yŒæ$Ä@âf‰è|dÍHö„DMðáS™ÒçMÃŽö¬=ÓL�-Í%™ì¥Ìåf'çš–5•>¨›+ ±ž©ðøîF· ¦2¬íK"")¿²ÓÜ>¹¢x® ¿L ñn‰|CX'¶Ìå×ã­ƒËÇy á�–ÝK{gPê¢=XåûåV@w‡ƒ"èò¾ %2�ˆÄ•$“{¿)Q¿ «‡ia¶OòÀÝ3ŽfÂÞ‚óƒ§)�ñsPÞNÜcæÌ{g?�Õ@˜2bƒLY \¼õºÞ-("ù!Ó#�æ™ÉÃu½Þy"oØç/ÔíXØß°¶wËì^� {M˜›<«ðÐBC &8R^b7Ê,é0è$#µ»=ZÓì‹F}°¥ÄýÉ_¸f�qt ¦-x�…]û@äØy !±W!ÈGšTìï‚°ò’1úb% Ϙ’‹ÍÍ—µÀ]áá`âˆwóˆä/„R�<î«›øµÊ?OíÁþíZ™{påð{ïn¦²Èî…÷çÜ?3A[!;X¢rÓ¶ù}Ù\KyôVúÒuvãàÉ II$�¼-uA Ö:%ðoï �‰Tö’ŠEðÄR!lîéÀ‚à¬þ‘ˆp˜)>'¡ý0úˆ�`“ƒ‰#ÞÍ#’gZõ×ò€¥Š_Sð~Ziç ~_û=:Ñ}Epf7¼He9$Âä:¶ô�Ú °�!&æzDÀùå Iä]é÷AV‹!<ZñÜ’”H¬/—»+¤MÁÔf t~•]D¿ ü…¦B¶Ãp äÀ¢R! ¼½Ü2Û=Ûû#”®ÜŽ2kª˜Ðpôš±ˆ/»¼ÍN±‰ÃÂO¶C¼›G"_&zd4¡ÐÅ?ú³&ÚÅÞ‡ÖZ² ö� ÆÛˆ.Á×'ÛYªÅ¿Û»t: ˆ”ðŽÞ¢²Öc†ößÞAÓÁß‹Ý+¶—´€H € ñå`-ë!—rw±AöRzðÿA^ü ÄHJI€ëÆÁœQCˆU9鲈_n»µ‚Dþƒ:·¼›G$?!ôH"ø@ÝC³ˆätE픕ÊöýDkñë"Ëàêß?†ÉHæHø9²¦Û!:«hÕ�Øó¨Ž-L!!º[Òcx>¬}|Rg%ˆí´‹c€sšáFR‚½ëXÄc‰Rqu—ô½y�C(¨žn-Žx7�HþC(óH"x`m2ªóˆ[ÌÉ,h ¾¸{b<÷[m &c¡ÜÄ= ZÆ Fj¦d¨V Oêõ†Ù½d A��Hª1 ÌMX’pBVÜEY©Õ+p{ß“ìMÚHÞa‰ì›l·VˆèÚZñn^Yk¬Gm*q9ýÞÒa}ïÚ©Ý ž\{¸±V¡+‹’ZX«´4OÂ�ã¼aš�êÛ±°íºF„”ŽøXÛGš|Iùù•¶¼–ìDBV‚à.v´„ÁX‹ùóCו�\”9ã1,Q´´*Rh ·K¼›WÖ:âCÛxe%Îl‰ï«4ÂB¬UÎl›hÈ(s1Àؼk Ë6%²v÷„¬KŒø("�[½¤‹ µ`‰²N„FÇNÈí�L\�ä^‹-ÄqÅXò@ǰíÁ°C©¹ ‰ì4…gWK3[ÐV"s¿8âÝ<"ùs¡À#…6½Ä‘VâP¢ÒQƒ†_²“cëG3 o;¸K]w¥W`·rëøBv ðï"B$}ß{ÜŒøâ3\Ø0£;[Vøo?D‚NB»­ë iØî©«,¼²C€rL]~†!):»F� ¬AcÐï~qÄ»yDòœàó!,8 à¾�=å ‘6±päÇ”·°¢GˆGŸhqw;CŒd-g·OÆ�7%-ˆä 6>’á¨%yÝZV`oW@#¡]ÒÅ'™-A€ç¾�1éè`qۋέaq•J®sÚB .úrN{û~qÄ»yD2Ä#‚ˆÀÁŸ‹aE–H!ÀO“MDÒZfë¾èØ‘Ô#’ŸŒïFDKöñ �ŸâkÍÉŵYo£!÷g �&èq?•wøzŸwÃù–Ç€žð¿@ÿÎ’ß³¿#Î$Á¶ü[^.Äh/8 c½Ò!IÄ ï·÷¢‘t@"QIKñ¾²lñÉ^ ®mÓðaÞ‘™æö±Ù@c9‹õ^z`ÃÆ1ág;n$õˆä·…œxÐA=Ø`O¹‡«së@}’ à÷îÄ^óÇEMdY@.Ƙ®/;C?{¡qö wœéýßv´Ã?gÈ»¾ß­ž9øƒšéßó Qéˆ`ËïåÿáßãI•äîü~Î %-;ìØQAFbíåï^íX™s#%çéÔ¢¤e÷�´%É?1W$²DÖ:Vho �‡7’zDòOBY<‰dðÇOkpûÈùÃÞ“(ýPùç9µÑ>'/«W´æö%ƒÉЉ_˜-sz:›F•flS�µ{¢syï$szËhsxõ`sbÃpçÒÞIÎõÓ¤ÐÕ…ù e=Ïð@PÖÛ*ÙÀs‡@(‹�ïþ*vñ ²yÑ#ÄöÒs\¡w¸.-v–Vœ^aò쾑Ð3’¢Ì…@\ŽóB°çþ¼ðÇ3’zDò�P+˜x€l£¿²„aŸ= ™huí»fȧÏ@ÌBøÓWrŽ®ªÉï‘æÐªÁ&-e�ÿìöqÎ…�¤L59í”Oðß<Ìã&\O·ß·¸¼o’9·}œ:šFŠT:› =_—Hý¤-u’…X8wdl˜3ÒîËòªŒÍãìÕcÇE*“íd$Òá)¹¸ÑÜ>¹¨ÍSì”´ÊÎ.Q‰t…]$Ö ¿Øñ"©G$¯þg!V¿ B£;¿¨€ø¼Y1áSY{¼j–¨‹)k÷$¢EXÂh#ø7L5—÷N6GÖ�0«¦tq�úHDöÏ…ç•è„b§ØgÈSkÇ‚~ÊÌz›K{§óÁ÷2’Â#d$�H°CA`˘"¡¬E6"_­¶87/~¸cER�HúǘD€JS/0O�¢²Ì+FÚ†Ì ßÔlf´.¿·vA$²c«¡ðŒ)>¿^$2·­ŸˆÄmù]ëwr79møÔ ïvœ(ê‘È×…eñ¸’¦”µhäf¨Ž G| ‹�nZÀÔY  F4w!7.€XÀ •ÀÆJ£y?á²tC�)ã¿4{–’¨:O~J :f·Vskùâ“Fhw¤�زVn:»%‘¶IÅùeáf¶™Ç>Å#’ï »¢<b[n ȉ‡}þ¬¼§>—ðý†Œ »h“às~Çx«i$ø0^Û?EV&Ý5ëñd<ÉÄÎÅpNiÀÙO­É½ß0Ø«ìZ$»øôÙ¦äÌr¯´ug¯íØêb;3#"‘‹†ÅUÅÖcô{œÏ[s;”;Ççâ«îßÁ/ ¾Ñþ#©G$¿(œ‹–m WÎ�÷ζdZ})É@ ;ö6‡V–^1�7sÂbß²~h9�I\¥Ÿ¦Ö±@éûîã´:Û¥YëE"»— ¤íWúÈ »Î×#’3ÆTdãþÛ!¦Øko7e—¶ÊSk±ÊZ³ƒ~__Ý?…¦”ÀN-²‘H5lÜþ¦ýGR�Hª#\»‡@t„8%øñ˜ˆ3­® _—±àˆrVzÊ@™N²oä.[ê:½e”Y:æcÊr1)wfql\$ %‡à¹CÞ‡Hhõe•¯ô¤©æ¦¦ÙYÛkr·x[”¶Š3ܬäºÔk·ûFJ.èn%­ »´Ð¯ì›ìb’û^Ÿ¥aÖe¶Ý7BX.üdûޤž>25³ ý �·™BoêÈ%a�N(îo™ÓÃl_ÐÛ]7Ì\I›’hÄa@ö¨6H›òsç¥�ì_>Àhà‘Ù—¨ ¥¬Áj‰†LÈàÊ~²fERçöÁEXzÒ�Ù�ÚÅîؽŽ-ˆ¤ÀÝ��T\ioz bQ_xÚT\ÝRIëJÚdsfëh•“ÇÉ!zœáñU}«²VFº}¼QØ$üIûŒ¤‘ü¨�iM„IqÊ0Ì7 ¦(™9àmôHÍqL ì’JD@–DîŸc§|ˆE÷g2©½Kújÿ™¨‘ 3"L¬Oêõºzš%£?5iˇHSšdÎn›‰”kÑñHäÞN’í €HšJ[eç9¶;"Á mO-ÊZ7ƒl÷%Æ@öè½»wI?s|ýpæ´âU·Ö’h ´î~©ýER�H~^ÈŠ4‘��0`8HSã{¾†P­n¬¯Ì1e 7Z$‘YØ^ú¢ÌVÚúúŽ °„²}~O·ÔQá÷‰ŒŸ6Lâc7¿?e(z$¢:ø|béê±Ô#K$ÛwStܘòlVî¶ËÒVCñ9‘È1S~e'ûFBʺù˜õ‚1 nVRx�´;cF´Ÿú‚Sîðáòß÷3̾¥ýø¬R9Ð<»q^4ó4�ÄæNÞ_e2þ¬Žü ‚:¡—ðM/ÿo>c¾î÷5þˆ¯¾ò¯ü5…/˜šüçL]ÉCÆßø[Æñ3‘‰äw„¼ˆìÂ=a˜‰uH�,®tÎ)…NÐLÄ‚IðXÇS&{±ˆÀ¸®üÜR Ûˆ¥|�ì‚áßÊf{m”šçø4ân” 7kÇÈ®ÅÌé­Í©-üKüZŸëwëÏÆC å-:·ŠO@"d%í¶S«àTJH6(Y»ÆK ™LÓo ¶.Ÿu6H{Û½x ­ä¸HG£ÌU!¼Óщƒ›cÌ�8�Õÿ(âøÌ©-ZèTÝ:èTå:•9�ú7j(¨ÎTߺjªò–˜ÊœgõµŸID"ùáV$ˆ7^ŽL©OêõYÒD¾ öª�Sº„�¸GK´@â3D/=ΧØD`£ÍNvW#Hò3 ™ ? ¡œÝ:FYØ{%Öçî\ÔŸeUÒ—f4V^\U¢L¤Á#�û‰ÍHöˆH2!ÄöÀ«ùöa_~ÉTç‘1ãJm?œ/ÌkÕå7G Åþ”:÷§  ìŒ®×bsǶy} DR½]ÿÂK–@ÿOùkK^sªïlI”ˆ$‚ù»×G„1Â_'‘ü•P"¡s‰lûw¥Çï¢Еå\K¬r–íÆr32 ˆƒŒ‚Ò•Hc%»©E)ˆÈcbëÅ€û¾õs«ùY²ÀïTV2ÿ~F·ð;ß8òA§}šï,©a¥4˜¼-‰ÜnF²Ã]luœ6`2’€’ÐŽí¤í÷¼îîg•.–ñ˜4Ò¹…^¢Òç<»ÌŠ¡Zº²Ø36� sÆEû¾¥Ôåã>‡H¢½Ž Px¥#ˆß˜o8 UO¨lµÏ©Ì­ãïZøÃD!’ÿ*ÛJ�+mÉDúð„Á›jãÌ®d"‰0i4ŸÚeÛÙ™„°€¾y‰Ö‹D)d@ȸ¯è("úíù9ˆB"[©Ý³|DN¿÷Ÿô‡›…Ð>͇š)vfEª/­áùxDLV‘ÜÞmLá!CyËj$—Û•ÿdB‰+p­.mÀ¸ÿ–eoÕR«5"—…´Ùc÷£¡Õaf—€'öx}ä¾1£5«t*uB,þ^ùÂC�DŒ1?lêËz;U7K"ô÷ß%üe"ÉsB}83=jø\5#°ORº|pUBL©Cè.�̶Yˆ>%,ôî“y¸ZÈJH@&�D0�+6C<.;¿:wÃÌ>šâo+‘0¸I×+xiÿÅŒÑ#ˆPˆ„ÒÖ­�Ì’�‘�™�…4µ—_lÇëu¯Ò èèþºü“NqVªôµñ‰Å£>„DZZgm³`f–h1�‘gÛá§Û3‰Ô×USÊš¦2–ÍB"„óÂãÂ7âI$o N¸S×¼)™sÀ;káÈ5ä4&Î2“PéjžÕ?È>€›9PÆ" Aÿ D¡Ø,$€DBG^ªÿNfJæÀO_ºÎîu–wÍú¡Ú~ßÃþäþe'°÷í÷½¡ÄüƆP �&R©¸Ôaœ€ýV,Îvîd¥ù÷®ÓÈP0n­·œÿ//4wà”‹¿—OøªÝf"þú_ñWÝ^îܽáDéïœ/<O"ù,!î)ôÐFh!Ü¢™‘‹;1^œ¯,Ä*±%,,±KN/r;¯–Qº‚@$¦¯†}üym®”m–wÒWáŠ% K0Ü¿½ â± ‚4bɧ‰}½¼Æ;{ÜòVv‡#2_ÙsbWÊÝ^ïÌhLÓ¥‰ìY2È,ó™9¹i¬&ØgË(r¥ñ·ôÜsAªQ3"Ž �Ä‘WÈL¸Oàä{ ã¡�pÒ�H ˜&r):Á}2Å^½£)Pêâ1°C~ÉG&n ò=Ù!ÅöæzIYÞ93sdOʬ!Í5­šô•Ù»t°Á44WKÔjØÄ™õ¿_¥ÐGøvBZ�Ôßýñ%6 ‰ê„ÿ‹‘üª�®5 �L³o�ÛÜNK"±Öîn;o¢î)wó^C©‰,äžXi–ŒëjæÿHv_b/?­†vKDx¿vË\·÷ŠÜ (B�@,‘Üå>¸ ˜É< È"�xøZ“½z©@�…dÜrÄbË_V�ßÙ ‘¤&ry "å5r>ì°#“Éͬcf\ïM—Wÿ'èÏûÌïÐÉeØÌÉ^œâÓËl#H”Q!¼›H»ßãÏøkŠ>”Éâa‰/ §`¾Jø‡xÉŸùáîeï«ößɽ^—8Ü?Ö–(´ùB" &(‰¤ÚÒWû\ýÛtSwm‹APGĬÊ^ƒ IÕœ4Z&~2±[ÿ®S²i.É@:å 2ák6“Ñ÷ø÷ÝRØSêÙÈ,»3Û²ö¸tµ°„îÜÂ1º£hÏ¥¬d´îC(‹[jÉáˆVA<€H,™�ÕðûÜ=N¨û,€ÍX*…2W¤ç¹p¤ôUpÀvyAˆdTv¸‘ǼV2;‡Òb¦Ѹ™ÍöÀn0À¹²?gKiQiÞIÙŽŒ‹×šÎ�Wæòç_>Z»qÁÿÀ�ŸoÐgÜ÷ Bé"`(:±çë†öà¹j[O™ð…ÚÖǰÅS•sÍy ÔæŸ\lgO€?'ª 0×…1Â+Â_ Ðó×Wý±¿º`¹ÎSCäÝ›¯ë£¶¿!kýø‹"Ž*È¢92çõ°÷qrVscçuu_“½nì7âA"?¶†»¤—0â‹çÍÌ��¤‘ ˆ…¿–�\w-Pl{oì‰d—±Wæ¶4å’H~º�¢vEo»Ã^ñƒ–†ãJ “�Á”·øÿŒ|Þàá ~ö¹Y"á1º„RtÜÒBÌsäk çÂάäY¢°íÄöœA$|ífSÆf'ë!«<—Tø?î¤EožÄj%WšÙÉ{™ �7/(^7wÄå¾ï=ù 2T%œáŸ?ï0…c0­ì+Ô<²ca³fJ“½wº�¶ûDðÒ¢cËê#±…Ó›Ö¹uƒäQ®Î)O…éé”cœÒ ޹¹Í †L¸â‡xÒNéYK&‚Íž\òpÛöblé®ì^¶r† ‚ƒ08W<&ƒá¹òu4“™Ø,…ïñz¢F$ü½8Gš”G­ZªÜÎ:`¦þ”rvŒh(¶ÃËQ˰³@óR”½¶ÏïghÜ¥™©KûfX %Xø —ã«ÿe êcµÁ°4¥Ä»WaÜn�DJÏ¥ ?œbóåÊk‡ Ï,êWƒß,;9"ün¼7#ö ÓF�ÓÆhùlYa]%-ë«ÅvC&Ù1cŒÏêY2’=¶�â°».¦ÌÃáSƒÈ#2!#‚H 22‡(-йnIE°ba3•KM(=GæÈ<Üœž§{ÞNYmÅ–t•û"7!Iþ· ;îßõÐzÀ+¿™iÒÖÏ6â)u…/1Ã?Ak~_GœG˜× ŸáM€2W4« w…Gü¨¯¶äi!\ÄÅp騵K&¦K4oI¸YH/çÚŽ¹™¾ŠœL•Õê�Šk¾‚ãë}ÓÑCÐS 2æt/ùƒx“È7„¹‘"’¡*m­›úˬ¢%®“•¶"´C$ !â§˲–l­.y†8„snÇÓµH˜ýÎ�ýõÍȤrÛí¶è¦9Néy_”È„cKDbµŽ¶Å˜Ì„ÀÍ‘Ç|�û� l3¹c.™ì ÜŸÒ:�Xµ›Äf‘vž'h±Ý#þ¾7/0c»¿m$Ä“�„5:À£Eš�¿–>›¬„ù†sÕοR±Îþ|Ïm>o|õè¯ÎOqªrëâq^ë Î]¼ºuúÅsË•_Ú8ÑWva�_–ÿµ|�Ê�bڬب9SŸ¶6ÿøº÷‡ýâÿóõDØÓþ]Û±~iKÉsfÁpÖëŽUÆѲ€H˜ìíì±{E¢¼÷»¹áNyos»N›�D£ÞîT\®7·v×4míî:u`EùÃqØ}áÍK`vð‘ûœ#H¤ÒGR.Ùœ¶b¼Î­KØÛ‚ ‹à–\ÙáÐÓÞ”{ÈWÐ×ÍÝ[gÌùƒÌø^ï„E&ü[º>]Û瀮¶iÎÏX"Q~Ý]úœ¯f;#ä¢ÏúÆHliÜ'üè½X諯þ/S[˜g’v”uÜm(8w·±$Ëׂ-ŠÃÑ�¢?¦J…�•‰7‘ü°p8D•Å`-fÂýÜöñI¤&×­�–†uä�µHŸý&‘�õ¸ÑÏF¬·„"l…H¸zv‡ X']ì¨%§èdCYIs8d3š|ubó¡ÌLÀU;=oK^–h®û}Ždt‰-{ÝÞegN@XDbmRöA°6‹ôˆ¤�Ù‰´“›çÓÌÌ!Ÿ[RàØF0JàŒîüRƒZ†Ïi¨1sËxí6fp¼}|!N؈òXØó·´™ C�€ÇAbƒ¹±ú{ÄA§®äåÍk t^[û~­°Føw�ûo% ‘ü„pZ0‘@/µOë÷–ZEn+"e,2ˆ£à$šÈDXN6bw«Gyb=pÖApá¢c®p|¡˜2IÔ�ùýÊJB! ÁQÇŠï±Aà�£�� xÍu£ë _pNÝÁÅ]ÖbFDÎk“LR\yD�ÀWzã¤I™:Àô ØþÆ�sãˆ/_8»`ÄG·pdÐqéØÏLº%có8ÖýRÃÐÔ]·�®/É\)¸yQ¹y`½1ß“WÖW¦:¯0Iε#lþZ°c‰@$¿dgH"TÞb ñâ® %ÊXn‹¯°Œ-t»c$:ÙHj ÕÄaÛRÝ«YW9bÅZÄãX¼¡øÿ°�•L ÿù‹ÏÄá`3ÇGór˜}|ÍõøÊ$3�L8×®[ï%㔜¡ƒ-2Y äDFRz"±ÄÖ6RñȤ®8Ëٺ،ìòzÈä¡»=ßz$Wó(5ýÞ‚AGâ·móûÚ‡�¬©·Ç C¹‹²ÄBçWaæRU($Ô·N"Õº|W]YœÊ›uItžó…«í$ ‘ü†p5rDò°ßýý‘‡ER`·®¾î²*)ia‰ÍN-›�¸$b`»a°8í±gr¥»9þ¯Û{ÛÔ±ä×U¸¿,n²Z  y ‘$A�ÅXûʉ¹›Ã �j×Kìwç_ ±óž½|˜%™¼ éfÑø^lÿ —PJ£X; ™Lï×ÉÌüžæÕ>6»Ôê߉ºp�ª½=swF†o™™Ü�DøL_4Õ·và®›dçxŽ`u�D"’ßr#E$L¸�ëÆ>’žáL¶£‰@ ]„ö^HSFWB ÷v®;d#ÑØíÀ\~º­Û»ÛÝ w6>?%§ÈJÚ@&²ö¾sÀ8‰äxkI4ëøjéy ?#±N{ù›Lì�€‰£7sR]pÞl]6QƒÊ�›®�IDJç�–¿ Ô&QlX&õ|Ãà<Ì�#먷è¸~z7“¾b(å¯fö›ï•¨kôyÍU”/ÉÎëiáψۉH$ ÜŒ‘à<½ÿ[Úé,ß—�ãÂ"Z{!ÊZ,ª" �<8B$îöC_„õ+¤»{+¬˜n§Ÿ!ˆ£ìˆßÕ+öÎ~fÛJ\%g�í“O*äÎ{ÄÊ[d�ùiní$åÊ qzÙIcée³iá"YB§Ë‹�(*iìà9t}Tyra¯gÇw¯':‘ü—P#˜H–·æ{ß,õ¡Ù»¤¯ÙŸ2Àì[ÚÏdïž L#茄v_4‘X­Îu'Ô÷Y÷\;#Rz6áÛA�òK *Ç…F&”·nïoÇå­ ×!7Ms;¸.FAûò2“c;–9½:=îD“Lp(>¹{…ñ•%õ…@–`÷®'‘<)4 DjÂ}¼:·RÆ}jVMúܬžü…Ù<«›¹’Æ�bHDÛEUv#_ºp Ðò$ Ê9ޝ\úNH%.G]Kþ²ì¤ÊJ‚ïÞ ¾ÉÂvèp]‰Of$òKóÐPzÙÙ´h¬ß¶G®”… ¤Ã@dñµãÉ~ž*õy|¿±äÂ×’‰H>ŒE­R ß°›„ìdÃŒ.æzúÔ�ˆ¤ü\L‰Äî>·ÙHqB»ÕF9uNþþ*2��È ¨âJr–·Jϱa±­$ba·8±ºX€ e„½Ó¼¡Å­K'e&#‘~ï?YqpÓ¼‚ºâlÊYI|ްœ¹=Û_×îI"�Ö<\ÍÔÉ@$KÇ|¬}}LNˆí¿d$n‹o숄îêä%îTuÉé$*qäÉ™EЯ9¤<“sš›Ÿ·#|"±Ã§{ì\ ¢»ÝQÇ�LJ.™õsG†ß©%=dÄ—/ß”þrA¿·>éI¤¶p·ñ×ÿ ±9ÙˆdR42’!Ÿ<Ã^-¯ù̬QiëØº¡êÚšn®ì›D÷V�D²¼†ˆ °ZYþ~w×ü!²µ(œ¾ŠëuçmÎ_0¶G™6.Ö(Ûðw�T^\réó¥ûÔjÍoƒˆÃ¯ìÃ(‘µ“ììV‰©/†xœÌD2H0‘égŸwƒL”•|d¶ÍëaRçt7G× Vtgÿî¾I7î„@ܺøa¬O’>`à¡å¿�æ„\ÞÊÛé“àK:")¿ÈèEƘŸIr ±DòCÂQÁ€¨tp}ú ¢»–Ò¼gÒSÓ±…ØŽû/¶ñÑëÚ"@@"EÇ ’vuµ‰{­Ÿ¡Êл·0¸«Nª¬Än�ÜA"Ùf¡ÌUØÙ”™”dx‹¯<„-Òªò׌6Æü1¸½É÷…=Ñ"»£¤ëËš+yÇœÚ2 ² ¼Ep«»HòOÌÃk+zDB� ¬UrŠ:{ûêÊa¦¤ø”qŠ¡f%ùéUú�IÚ½q"±ÍJá!²!ÛëâòЖaÃ,S_ñj½c¾CümODòMaq´HĶkÒ}ÎàwL–Z€Ï¤ŽÆÄÑINËëuÑH¢K$·¶Û5¹í±dá”_6έ]¼ÖP³’:u~ÝMº×Û*‘„O&¶ÄUr:p5¯A ×çÔäop|õÞžø£9™ ‰6‘ øðIˆ„9mOìn3“¦!ÅÉ:ÚùH¸b{´J[ ÕÝCq®ÝЍNá1‡`bVâwŠNÜ2•ɳ†Ô¡í9bó$͵kìHIÔf±­µ‹{ðP}ë¶©/ëã÷ù~ŠxÛž‰äKÁD½µÍŒ™’µS¾2ئœÝ6ưŠ7«Ù�"DBY«(s!ûH¢�‘¤Þ'8X‹øö˜J;ŧ| ¤~!Ä™’½%"×Úä*mˆ<‘X¤Ú¬DpK¢™h3´Œ{Ù‰‡�Sí¯º³Êi¬þGcŒÝ'Òž‰¤“àL´µ&Þ§õ{Ë_?Ü\Þ;ÉÔ�"Ù ¨� ´ß½�bÅöÈoAÜBÝ[Ø‘´ßšlÙ…:•ªØ ºèά䱔÷éoÙÞ`bð]Nþדí-¥Q/3ñмµwˆ1æû6Ðv"yL¨L ½äCfrnûX†ÑKìžåÎñ¹ #bÚHûo$Ýí•¥� ´UœÙnßÐÊ*ü °�öJ=xËGâuCumù%†7Ø×Ð Qiç}”oñY&™ Ïч•³fØ®¬D$Ñú–ÄÈO½Ï å3GH/éa®¥Oa{"° e­Ê‹MId‰$ÐåuŸÝˆ×ŽÓkǯ‡B$Úi™tw’„H‰/FDbaw˜ÜIs‚¯{kz;6Š„O�ª[Ö¾#É/ Ù±"’o=lFu~Éì]Ößà½uU’±i$z D¢¶ß¹¡½*k#J$v.€²V¡ÛúkÛmy«A¥*è¢{*çÇI‚™6D: êþ–‰D}ç B³kz=2ép¨>¾A\íˆDòÛÂõXI¿÷ž0 eæ‘PÚÚ2»¢»ÛÝŒ$ÂDRÝl$­)#)kç®®öj}Db@h“îIbäXžÝD$¹ñ!ÀŠStœó-\jvÞ®{‚|ð'ßúç:a´ð]bjG%’? bE$´÷PykàGO›ýšt?²fˆIÿ©¹ •(+‘¬p5’ ¿z¤¤ÄXíÿƒIË?èY'˜!–%Aé¯8ƒ *^$b!2#3�ØÜ¬Ä%ô“ 3_D’<­ç·œ»9]UÎú‘ŽÊ!�¥­ó‚±ÔJÖNëjÖOël–�ýØ\R—K$V'©ÕIc$3’[VhgÑÉö�aIªT`Ím†¢I°»ýhœIÄ‚Ž.ôkò¼Ævˆª›wÕ™µÌ_SôÏw«êm{oG&’o KkŒíöª™;ä]³if×{¶)BS÷:Iõ¥U"’ ‘õ×”·ì:Õí—(o‰ó`"±FŽÃ7m<™$ðürô|ìÞ÷Šlºº<ͤ]•Þròüµ%¯ÓÜêÄ#“7„úXÉ€Ÿ2 †¿oÒW 4vŽ7×:E³$ó!ÖíF2#Ü%VØÆÛºÝƒÀæ·Ý[Á–·.°˜HN%‘XÎÝcR~‘ìÄ[Õێૼy½¡ªð �5Z&’_NŃHØíNF²gq_uo�€D¬NÂd{ýµu‘ÛIBY+¿�f'Ú;Œa\éYGÝ[ÁÜæß¿“žÀ¥^Û9†(€H¬ç®ëÍ€HའÜPSpÂßPóßc´N&Cã¡“ ûüyÃò«eÚí8å^”¹€9’-·Jµp‘ˆ»¨èlG*7Ì(é9Ai ¹Í‘Êàfb¿¶¼� C$vqZ~ZûØ®èé!uNmÑ|CõïyLñ`"ù{¡ d2ü‹ç%¸BiËN¸—�]Jû¯J[aïm4Þ³»¸;ä$rÉ)›•O&Ö+�³’ëø_ñ<�HDb}¹’ÚvÞ³;©ÒªÞƘïy,‘|/>¢;³%�v»cæx%m2ž[òÚZŽ>‚×V„¦Ùw¹ÝZGiý¥îßá>Á¯¥mé{[eq¾„ÕJÊΣç$RyË%’ƒÉë0í‘H�H¤»1æ[C„F&ÿfíRbˆ.¯ý¯²’ÌæYÝÈJd›2Më×A"d#‘ va>BF‡$@+j³ÇÉ?Œÿ–“¸YÉ‘„!k Ê�bqDxžÉ‘�xå¬FSWŠñâ·=f™HúŽûÁ´ FšLø�ã{¼fVMéj¦|ßl]ÐßTg¯‰`' »$Ò™‹€D:¬J0ã<´œ•€V²’¼�>-Ìò'î„{C�‰E$d$Å'ífEŽÞLIì·©-YlŒù1�Úž•ü£p[0ñÐKÀ€žÔÞ’�ÔÕ¹L“í›"×ú›Ÿf³‘Žùa¶\mÌJR§ätâ–·ø›¢å’äÍxwoíáâ…}ïî âÙ/syð�Uù+êj~ÑcƒðˆäÂhÁ€x”¹Ëj~ñèOLaæÓpmCDº¶ìD{éo¯ýö>mˆYÉfìV´¼°¼µ›¬D8„VBÃC"—¶~öß¿Ýï½ÇG�úê߀Ÿ4#¿|^xÁŒëö2†% нéè’ÍcÒ9Â#~ÿ®ELÅ…•h$mÜ`gAO?"¬]@äÕ¨mˬ§[@köòÕ †�É`â2±2¶¼å]È$ªòn›úŠ/ü~4Qy·þï?ñ?c»¾\>¶ëK"�WÌ"íY4â3gð;¦ï»��é£#™ $2Bƒ…Ã>{V¥©' –6íu�Ñ®Å4”˜bü9›ÃYb%¸ó# ?Ý»"lŽ¢ã¡f%F¿d_bžÇ2‘Ê[À½�ÙkÍB½‹™8‚®¬šü mÙÿÄ Œz·¯^ùŸß˜ÐãÕ3s‡¾k–ŒþÈl˜ÞÙ0ë±sa/ƒ­ÉÌ�o›yúÞįšáŸ?gfé±~ÞŒîü¢!‹¡ä ¡PFc§û�OŸÕÿÑ_³$Ëà w‰UÁ—Dt,>Aûes"1^‰+­u2iy@�%©/ãßÁµ‹çEÃïCo¦$^¨Éßmü�£0êÝ:¿õô�ÎüîÎ5“¿0l1Ä\qßÒ~æøúafÇ‚^£Åíó{šõ"˜¥òÊZ7í+Ü|E.ï™q]_6�HïN�†D&d#Óûw2»— Dlo‘Ør容ê.8 ´ßð`‘ÓòT¸E’Ú¦”œá5%˜V¢¬¤ð0:�ƒ¼½…W±ÇSsçobF½Û»õüƺéÝ{í]Ú¯>=e€Ù¿¼¿9¶n˜Á\ñ䯿ŒÖãòøÐªÁúÞ“¦ïoÙ¤Îénf x­B¡ ™M¢}kkxûˆH–�ýÜ[?šŒ$<�_-:fJÜn�HZóª =+±só5gøôƒH@*åUÞ�.NÇ~'Ž‡Éºøñ¦Õc};¾yü�gl5Fï•ìU‡D°1ÉÞ3Q[ 'š¬] {×ñÉ:ºvˆ9¼z°a}. ¥¯…ÒTêàÑN©#ë>D‚h¯ÙóÊlºA$ÛW…ù¡Ýïzk^zW€÷Ÿ ßaI"8¤®ü·ƒëª£._Ĉ$7cæH–Lɵð¨wa[¬¼‘xÝ®¦Oû1‘Å Èî æÆ�©¸ôÝŸf®íŸb®ì›l ˜‹"‡¬dû¼žfëÜfªô”�ÒLfzRaè°ÁýQà ÉÅÝÓd‘²Ö˜¼¶ nu5’�´\ ÇÉF(á´VŠñPx¬ DrÊ$ö¤ûû|Ã'�H�I*Y29¼7=�$vX/ürœC©w»qpú_Û?5Ò€@šƒ¹¬Ë½š6™lEKªF #ÌÁ•ƒ š ÙÉÚ)_ˆ¤g+å-0ºóK&{ "º$²�z4A6#ñ>¸­®ä¥Ë-„òDr:�‰ä:MVtŸ@"�ÑØ²k:ïËèŸUÂ,Á³`U®eй¼w’¹ ’Zʺ©_20¹÷ëfè§ÏiõíÕ‰vßG!‹qÚå~íÀ\²Š0„öív‰ÁŽþý v�x(:•÷V:q‰ðüòv8!At×öæºå-º·J¢¸,̃#ìž¼iõDº‰(~IØ)ð B!;¡Üujó(sbÃp“¶¬¿Y9ñsC‰‹–aæNÆvyÉ þäiÄbJßN¾²ó«üI[�~éÛ·W}¥§1jôˆ$Ømƒ·vK$~�×úD&àŸ®WödE÷(f!Á7‚�~Ç–WÊú¹Ä�¦™¼(” & ¡\U9,kw“(¿_Ý_gt1ˆðó‡½ÇŠ]¦åmvB‡×œ¡Õ7\ÛØ–QÙ‚fI¦gÔ8g�D0I£²½ŠÄ&’=W¿}Þq$K$¥g¢÷š=ì~3±#©G$?/¤ &两rIúÉ!uv1—B©‹†if�qŒ2”EãºV5\ßèsrØj§6^x„²–�é‘H˜K¢ZÆf¿SœY.¿"'¡‰¤ød¸‚yd÷â�@�ŠÞ9óHä�’#šzdÒMð ¦-@?9³u´‡›½Kú:¼ Dy4” ½ÞôŸ^æ@$a9®2üUv®M–Þ�âEʃ"ç¶þMC‚g$ñ’†E�pQØ*Œ�ðD•‹£ò:=” “…_OžHêɯ k§­dBvB¹‹¡FôJ]Tò’·ÐŒÊ4Óf"A̤c«ø„§‹´t<| ‘8·öj£\UbÏÈd·<#:)\Ò…Tá”pAÈޏd±ZX(Lz �¿)ûQáë²*ÿžžO'á°¡ÔEäõy¢úáÁÛ­ž„dò‡ÂFÁ™\kÒOš†5È8µÏfñ¨�LÆæ1¦îʺ¶ÙÇC$d$EGÉD¼–ß¶¢ô\kÖ)€¬¥Ì©¸\hÿMûÛœX'lž~EøqáûÂÏ ¿ ü¬ð“„o _kís£!ɯ1Ï üŸð²ð±°LHf«„tá¢PÓêëò�#ŒLþù�L~[X$Ô‡@ ͳ;”ü®…½ÍNáÂŽñZµ»X6òB²�‡LìôpI¦WÒŠÈ~÷-­¡Á)=_L )ñ¶K"µÂ —@: QÝÝ­ç÷má»Â7Üã�ºdó¿.ÑL6G…+B½`:0® ƒ…?朵�hê‘ÉÏ ÃBèär@KíÂd'vŽ3LÆ_I›òÿ²w@Žt]ÎoÛ¶mÛ¶mÛ.þ6×¶9ksŒµbL4;éî{þ·Ïô­Jíד!›=oÕStúãÞ§Î=÷vS²s1•¯lÓTÝí²‡÷�Èí'&/›Žî«7½¥T²µÓ[V£OÕ©t§M‘uƒÿŸ:Á‡ÀãÁ=} –Í]ÀÀãÀ§ÁZÐzÀ°¬‡Á9lBá$À_Á3|’¦”É�Á‡ÁY@“Bá)¯È©¹4tvU®mæ^ɘz"±=®H¦h¥–�í©¿8v ¯†.7þ 5|UQüdÂÿ?i0€ü<Õ×øÑb¹¸?¸7¸#¸³ûú!àñà“`5¸rÀ¾‰w§¯¯¼5*Ê3Áÿ@Ðdˆ·- Òå ¨H6ÞX$¼ùð(?ï�¥’8Äëô¥72•Ï÷8ª{ Þ$Ž©›BÜÙîE[RÙ™‹À·À{ÀÁ}MW.�¯_ÿëÀ1�¸ äÒ > îæ“ÜR2¹;xhy@!zz.e{—Sá⺱õGâX \™¤Nº SBH7ÞUÝ8¦;n†–a•íû…·½Ðišûn©°XnWSͼ|×K+‚½bk¦í²·$ÕjÕm¼§ÛoŠ —ªœ™[U$�_‰¤Édr;ð|0'Ѿ 1tv%Om�öÔ9î‘d:gX$?é/vЮ³‚«§!À\àº~I$b9¹ DÒŒÁMïo›ÿÙÜù5½f`‹·Hú��HdèÂl LÂà9ýcôª¤£1ä1|�ôѺBJ?«&çˆ%¨¬bÿW}‰¤93зüŽUÿæ×£Ñ~|Ôå¿ÙžÙkì ÊNw§(ÜRõnºïo€{ŸHËøDöà²/㳫Œö“]Ll6lº³O"‘4o ާ�9 rýmR¸Ù>xn¶*!ç7(~ÈuIpªíºóÞïÇWjì{UX æÀEª¤ÏR5sŽÌÌy¼>ÃBQÃ�I>2lWòß ¢;ú$ISËä>à`/¨ÔÞ�]é‘ÌfÃ:{¦@‘ÝÆ(7tT4pÎrÏçÁÛ̞׿çjŸ±P]E¸ÓQµÂÑò0³ð›Kîïkú0Þ‚âë—â � ‘STIõQ1ÖæÈ…¿ãóŠñ„2K¯ö!�sïnï“H$M)“Û�'€ßƒÔˆLvp¯d6wS ¡ªJw†!s”}%Ê�âb`g�hÉ^®t¥Q¸² 0ú< üZ ú5 ç»â‘��ïq}¾ž…óÊý]”Ÿ !ÿaÊ^ÚM¹à1®Nʉ.=Åå¬àú{2…Dæ<"‘•\I“ å^àËà P¼1qèÂ,î[”sëñø‘4¦¸”÷­SNé7W,L+A,’áÀØ>+FÛø5x~O9?ÎéÁggµ,ø{¼f‘àz,œÏG>7}Ö¹6W!Wö²8Rç¶RòìJ�Ì…�ü»!ÿ!-#gÇûéôåƒOóÿß‹ ’Ûù$IÓËänà}`#Ew')Û«f¹*†¯¤)ºoØ{Šk7Ñî\ ^ÙÇUH1ÖŽ÷'Q)ìrtžvºv¢8�J¢ÛéopU‘ cáèÆ9~ËßA8ÿ ‹¥2"–DÅ×¼ºÙFéóÛ)Ñ»žâ]«)Þ½¬e™d.¶p•‚Ê×ê»”èÛüˆä•º"i~¡H$"“;Rxû£©ÿÈGhèâ ˆä ´ a(„-•éÉP¸ÅòÞ[rˆŒ„S!ìãA|ؘÁ`΃½žrÊ^ÞãNG�dy@0Ž\øh¤zyꪔèäsPqà÷»¸º(DOó9éó;¸Ò(DNSß¾~¬sn]B‘öeíXÎBéïÝ@ ᳕™àéEÀÔÖó �‡ÞB"‘H$è‘Ü‘r×îÞºúoà,0 3~»ù*%޽Wqµ�ÜgõŸÙ¤úû6ºÓL[¹Rp«>–M.tœò`ðÚAÃÀå½Üç@µÁ’EÅq€E€ï¹ú€pøzøkðõ¢+œŠƒån[J¡Ó‹(tj %Ôºˆ¢�+)†*%ѳžüÇ瀹ó!�·‚'ú$É-ù<„ÛƒûƒW�?Ì’P„¡K%ì!)zU%f`»m_Z µ.ÖUXÉ•C¤}9¿F¥À JàÊ"àÊeD2[�ŠƒûésÛYËrâ)«Ö° Âøë êàs ˆd!NÎãcpD(|`‘€�È÷�ÜÐQ"ñ‰Tî ž þ† 3EH¡2è5Å¥B;høÜZ b0‡LLL'™ òá6g@_ÄG¼ç ràŠGžžŠ÷¬e9@zzŠ¿×B€,ø9ñ÷xÍßéJQÁ“óŽÎç`>ä²pD.'æi‘TÁ\ˆän>‰D"qBŨ#”/€ó  H˜r×LŠ.yU%vp;ÅÛ¹(c`Ïb /ð€ÏGHE RàÊG– Wm, þ×pª –BØ=Ÿ[ aYø¾Œcr°�ÅGÈÄßÙÀÂß‹ä¯öI$‰å·Ãàöx*„>ˆ†ðV¼Î¦™¡ 9Šì®xT%Z$ÎÀ>8¼<ôäP�,àˆEá}í¿æ*¢æˆßRpD ÎÑùM Ÿ¸î¨hjERƒ ªZ$5¬R•H$’ÛÆ®ä¨Jý?€P"Ó=� K%Ž·í“l£HëB=`0˜; »äP-ä!…Že¼¯B &DQÅ{XA€©)çèÈ@»†20A T�å¾·GÁòI<Ë'‘H$^!¢Û+cð%V!1ÏÎGÓº‘Qö–; Wqi‘¨Hë‚ën Êy0ìK.`¸”@Y‹¢æhkq¸Ô~f�†þû¨ÁŸ÷I$I½XDw²*ù’1ð[*Æ/‹P¦ƒ�Âޒꪣ«F,v­Lïi-�ÿ²�D"‘Œ-dWžHFöTŒ�Åàg‹¦“ÒÙš{q©Üùu)-’ ¥`h‘hj+™˜ZµŸÕ¢¯£ååA'�eÀ‰d|Qf鱪”þ1„rBQ"�©"`Pª5£Ÿ]R½¶Mõù,’1¢¥`x¤âѱôQ_[ѸTn ³"ø O"‘H&²Œ'¨rúgªë¦BÄL eºÒÙUq¦·Ò=ˇt50¡h)˜£4Ë•¾«=_O‘•ôtÖXîà“H$’‰FYÕ‡©ÊЇìbb5ª”ÿ·w@Ò$Q€ëlÛ¶mÛ¶CgÛ¶mÛ¶m[;ÛÝc©»ÚõÞÕý–f9È/"—FFÙbÇÄ´W¯bDTøVê2‰Ã®l½{Ëo°HF-”ðÿL¬Œ†•I¨ãüŸa é<‰ù^g^Ð[Ì< ÇîŠTŽ`/÷ ËT¥ÒÃÔD¥CN¿oË?Ÿ-Ÿß5dtÐÃPÅãêï�7�’Î: ïKÅR*Jf#'õ7;¦ßð®/\ð(¹òÛߥß_ügèÕ$·³þÞŸ¡Šg`¶óTqè.FAmOöòW±›{�ËdÇÄe‘“xõ|\K ¾%�/î�†,~+”&Êý:S €�ÀÌ3r$—%¿z rÒ/�mU0R™x¨ÞÅ~áW¿f~‘°¾y,;d$Ð<…òã lˆ‰§W~}3åæ®"™ú‚l·O(Ž¡cÆQõßZùßëæ×�Œ²�Þ‹ô~Í%£³¬Lûs(¿²¹’¹‹•“úŒl«†‘Ê„bPPü-.üþz¤_-Œ»>¾}0‹ÄÕÙI4 G³Ç^eSåÎ''õÙf}\¥‚LõyÙÂÌÏ/ü£ï`®Ÿ\&šQ…³ª ¶-ûŇÙI¦iœ…‚)/UëŠjæ—9㫇ózt¢¡H¾Ò™K4+fž:öë«*¯x*»¹WX¦ì˜r”‘ â˜Ê/þa¥|®K�Lâ>žÎ 'òWgkÐ ˜yz‹SPÝŽ¼â¹äf_#Û2Ù±"Æè„£Ê?õܯ¯”»>½+裩.Ò™”ög €V¤˜§W¡½‡µØ/ÞLNúmv’ ²� SG,ªÖUŸVŒ/pp!þ©¶¸w @)šNEÞRÊ+íA^ñfv3ß³cÙX*äf~ðR?��mÉ@–ÖÐþØ+ÌÆnþ�¾Ü"Lõî0ª'·Ðâ(X(¶So÷݈¤;RNf7�CõåX¦?飓ë2òj è,9+±LÐëi.™²XEË èÊÏ>Ø!CUĶT̵jò&clÅm)Çñ°ùQ6éþ8±N‚>­ÏëR»* CöölYþÒhq��p—ѱª‹j¦tðÁ ©QS—öSQë¡®,rÖRoªŠUåøc^·êòº•”, ¾|ÛgîÍ——ÝîÔ7ßîÔÙ÷{õ1ºZù„ã^¯b¦¨JbÌ�€B S³Ÿ}ñuÂÐeoCe,Fc‘s¾VãžêËÚë’¬¹*®ô4¥ôÁ6[ª,‹4:BЧÓßbGžûëÊ¢ù‚À¾?3I3€fH –ã¸)Ë�KÊ£Ù¡v›£±øšÆC.Íè�•1¥·9-÷4¥¥Î†”\”ë7tUÔ_Èq^–y¾µ&îÌf-]f¤Œê§,ð¿ðPƒŽã¤éêBÝ›¯ø|¤3DZnUy•þyžãhdXȯs.Sæúþò;¥Pšã0ÑФo†ÚìoZjb2j…C>½Wø*ú_`ÿë!€bªZ–�®:¾j¨ˆêª"xê‡ã°ï(Õ¡÷hÈ”÷*|ÕÅa3±Šƒ>­�þ3$‰g€_ã;öîFŸ#Œãøwfw_œ]Û¶í8ÿØnl±Ñ¸QmÛ¶mó,½Æjf:MÃú.¼7Ïg;¿ñ³óŒ§å=&ûã Oßw%ŠtŸs„lM}Ô!ãƒûí5¬£P÷û!°Ã´RSYf5Bô\�!4pœ‚3‚@Ù³NÚ«yÌa“¥i>àœcB@9Gà‡¿ê3‹õw>ýfõëœ,Bô\�!vÎè/ŒŸ}ÒÞûïºøà}‹‘žÊ��üÃ6( ´Ò¿I7Îk�¤¬#Dωb8ØûȃÇñ!Âþ{Úv7VÆXüÃ6)ÀlTºµV'­³@!z*H„°7pHêÑS�ÝCù�‡ÍBAqœé4Í1Æ`¬e«â$gz±^}ñ�™uçÀ[é½a-!A"„(% õËyË~×ú^þܬqgݰ1–4ËÉr³å^‰»°Òro~¸0Y*†‡ûá­ PEˆž !D4ðâ8O~œ©V›�ìkí†1–,ËÉ3ÃV'Ü�s­ï~Ýlýãº2Æýš÷:Ð@ˆž !„Ö�¥j#î<øì÷K·?òåsqšÏkU–ºIJšæ[ 笫øUZï¿óÙâ[ßý²qOœäÓZ+ Bü!Bˆ�"Vœ£²´ÖšxôÅŸ�?ì€ÑQ…sÃSãeÕluÁ+— �F)Å?p ëèët³ÊÊz{øXkÕ±Ö!DoöH„0 üt|˜ÿàs?õüå†ýü»ŠZÙèÐlÅÔmÿíÒ�SŒµÿ"�8O¿úa½ßZw¾?r>6 Q€½$BˆyàEàý,·«?þØrsÓ&øñ§.ï~ºÊ÷¿Ö¨5bÚ�ä� É-b”¢§&ùà‹¥à™×±k•Neb´ï=çhû£èb«„;J¤ÃMëìôpqh¯±ÒÈÅš¨\ŒBœ6ä¹" Œ³lÖ6ê1qbPJQ ‹¥ZÍv’¾õñÂÆ+ïÎå}½òéôBýÚ¹¥Æ³7]÷¼õE°ðÿÑ£s$B¥é5Å«‹úÔ=Ž;:³ÙÈPÐÏpZ¦§�%ü<Óef±C èQFe¥é�4³éOÓŸÿ²°´¼Þn´’ÆürãÎF+}yd°˜ù¥¿ø€Aˆ^!„u…*7uÄ®rX¼t�ÒÇMA'�iê%T»M#m³^ob]ÊPI3’’% õ¤Â»‹_'¿V6| Þ�±áòÓ>DžâZ3Á? Dïm !êÐý†÷º8áø©#Ùuèeì98 CuT!#ÊÑÕI²n€²šÔ�qÔ’†Zi­Wü÷�j#yØOÖ¿qÌ¡“ÝÍjçbÛ~cï.€ä8–?�³ª¦‡h™H+­Ø¶À$ëùÏÌÌÌ /è˜(ˆŽƒŽ™™™‰d’Ù’g¦»òÖÚ‰p8zÆ;Q~"RÙ»;üSV5Œ1w’F‘äVåÉ"Àzg…z¨QOj´GŸa3^eØ"Í PªÙnñä•gxvóryeïê~úêsø?ÍzÊÿü+cŒ¹G8q§gSúìÌ©½ï>þõú¯¿ó/ë“?ò/ô™ÿ×o«gâÍú7úÜOü[ý·ßóWõ·^üEý�Sßri©5ÿ³@cÄ5úÔ''ºYûT��é®ÑÍÛ8çnòDDEQvF» «Ïm½8xnóò.°‡1÷Öu$ƘoÞø ši}b¹µð™"©­÷ó'úG(Ë’ª*QnNU)cÅ«;¯WQ£Œ¹·‚Äóç?÷·ÒÙÆô7t²ÖW‹HvrrƒÅæ,¥Œ%1FnfX�xcïª^ní bÌ=$ÆÉCv48ÿm׆›ýzRpnö½¢KRœóÈÍ—ÃØ-÷¸:ÜŒ[Ãí €Ã˜{j�ÄÓéæí¯SÕ3£8b­³ÈáîiHêQAn$Û£®6V#"ÆÜSAbŒq"r"÷Ù§Õ°‘ûœ�î�¼‰¢ˆ‚p3q¼?2(Î;ç�=@1æžYÚ2Æ´R—<œ¸°p¤·Ê£ çxGNQWRƒ̧\ؽ÷–¶Œ‰1vÝÈ|²sÞù^/ïðñÅGYhÌ¢(ïD&^fÔ¹ÌÕÁ»å@«X½ 1æÞcLÔ˜{qg»yûLâ’prbãú}µFÕUå;캫TZTÃj´ Äqs—‰1F€£­¬ùñý Yèd-NOe²èc¤Œïè…yÊ—&q"´²ú ·€s×o¶c:À…fÚ8¹"ùZ{‰#ÝUŸà�Ç;Ç;fÔµC2R—nW1Ž€�Œ¹kƒÄã�"r3­O§>•C�efŠIʪĉ xÞ‰¢×C¤›µÙïƒJ«ä@‚1wõÒ–1¦ Lf>-IÑžkL±Ñ[£‘ˆQEy'eUcd­³ÄRk.$.©€ìîcAbŒI�ãÀâL}rqªè7Ö»«,´æ>�øäzwâøRbŒD�$>ÐLôòN^ Y@1æ® cÌðX+m<¶Ú^<¾Ü^pg¦NÐÍZ‚sŽà<ïÄ9GRœ8ªX±3ÚmÔ“bˆÀ.ÆÜ•{$Ƙ¸èÄ�9Ü[}h­³Ühöê3ÄX’0‘wOQ>¤õ¤˜1殜HŒ1G€�ÌÔ'7š³³§§ŽrÿôqŠ´@p¨*ï•Oâ™Oe¿·€Y À˜».HŒ1-àõ¤Ø˜mLškLùãýôÒ&I{�¼¥xG.Þ0‘‚ª’ù´ú@ cîº 1ÆrâÎOýC½¼S;=yŒ•ö<Ò¹ Gÿ'²ø ’”Ü” ̼gþ´ßK\B3kPOj©7cî– 1Æ0œ™¨õ6¦ê“ûW°óÐÜd¹‡�ÿ3ÏTçuPá.Bï58ù_áÐç`í H‡0&" «!Q5ˆHèaÌ]$ƘðP;k~|®1µÑÏ;þáý™¬õ ôp¥[-ˆÊàFËOÂ…½WA€…Kpì¿C:À‹#÷©O]ÐÆÜÙAbŒD€µÔ'fÓ�å!k,µæ8Ü[¸´ ÿé<üïS°Ùѧ‘Ùç¡uDAê[°ô4ä{ ‚ˆ�ø'âÆK[3@†1wn�cj¡†¢pvº˜øX+m,Ö“‚³3§éd-€A×:ðz*Ï T`§~ãßDj¬rñN\˜Œ¹sƒÄ³Wî °ÚHëºyç˜qggNñèü¼Ü4n6±>ßHê<<ûí¬‰¡†å�à‚÷¼/¢0öö‹ë’‡lØ2Œ¹³&cŒ X™©OÞwfúÄù™b2<¶pžý«ØÑ¨$>àÅ3*ËýªPåC!‚¸Ô%H�y@0æÎ™HŒ1ŠN;qŸØ_ÆúD3m.í¯s|â0ERCU’ðí«Ta€nu‘*åƒRU‘,¤H€ù;ë‘»Æ&cL<:ט~¼ž§ûy›�ÞõP`" 𛏠ÿ‚ä“ÿ·ò4¨ðAyç©§µPS@î » cLâÀ±~­û5ýZçQ'.,µæi¥ ÞF"L¼ §ÿô^ƒlÿ ¦/óA9q4’‚Ävï g· cÌ(–­VÖüøBsöã©KsõiÖ:K 0ªF¨*¨Àì ðØ?†ÅK4¯ÁÙ{,*|õ¤ ÷™À)ÆÜ¾AbŒI}ÂÿþáH‘ÔN¬´¾Á‹[,®ßå�¬õög¯‡–Ÿ‚bT@ˆü.|à=’fÚ žÔ  ´€ÆÜ¾AbŒV#Žý‘OÖîŸ:þ �¼u>q�Ã�eÖÚKxñxP�W§à�T  …÷%ß…l€FZ0SŸL€Ð—1·q�c¤›·�·³Öãe,ƒϱþaÚiïÂXtpi®ôö« Ï-Ã(}ëÀ¯L�(`æ2ô_U%õIÂ�îí$Æ‚ÄÓŒ?öòΫ˪ʡÎÇûëˆámÔÁÓkð¯> ÿýþƒùÏã[ÈGÏ{¦^ž†«@pâH}�= , ÆÜ–AbŒ`=õé…"äõ~­Ë…ùs,·H| x�ÀÛ�xq®µ꿜ƒÏƒ½ïËøól7P”Jc�k@ cnË 1ÆÌ_Ÿùä>?ߘápw'Žà=‰On¤ Ñq@ù0xç™*ú>ó)@¸ýn'o,HŒ1(€“À�¾¼—wgêIÁ¡öݬMÔHY•DÕ[r=ËÑÞ!×É[[€ÝÛ'HŒ‰1¦|øià·ß;Që›oÎÇû‡¯Ÿò›‡ 'ÕÈ­ÐÎ[I+mn ZsË‚Ä#@X>|;ð)àD=)š½µ|½³ÌÅÅóÌ·fHCBâiHñÎs+lwÒ"É#ppÀ$ÆÜ’ 1Æ4�€¯×EçdÙ;·ZOk³ëÝåæ¡Î²;7uŠcÝuj!' )a|ÝÈ­ ™O³¹Æt¸ ¼ dóyŒ1ï–3ÀcÀãÀ#ÞÉÙ,Iɳä¼wîxp~rI7z벿ÁÙéS¤.Q‚ó·,DT•*FJ-å¿¿òv߬q�üàÆÜA‰190¬Œ«@è“À̸×Ç�h= ùÿõ0*à[€_~AD¾«–%_Ûn_Ñnäó4Yñ^j½¼#ÓÅû!Â'W.0ݜĻñóEª’[!jdT�@ ŠÎŠH¼\cnó 1F€˜¾ønàG€Ÿ~b|ü­ÀW�ëë�o¾8 ´€x úÀIàÀWg€I åý ô€Î¸/€~7ð�_¾!O3Ò�Þ’iDQªXáœ'q�J#ªÚôâ—€CÀ40æ6cè«Àçä3Ýfí«':õolÙ×d‰ÿ¦àÝ·‹ðíÀ7_=“O�Cçû€‡Ï×�_óµÀ'�ÏŒþ)à³À] „/­¬�¿ï»Çß÷K¼¿1þ¾ãÞ¹©ZžÌv›ÅÂÜd»±<Ûcn¢�wŽªRzYŸnÖ¹¾œurrÆÓ€wž,II|¸%û"Á‚ó(JêiÝg>í�CúÛ€¯N}ÀcÌmö„Dc¨€)à|§Q{xy¦s2OB”;{ÃXVq4–Ãý>Üÿg¸½;,Gû]•JÑ‘*à�0îƒñq¤€^ž×ÿ^om ãÞV€.01>î}ÆAäDBš„¤È“¤Ý(ÒV=OjY"E-àÊæƒAÅ\>ÇD:y}cýÜÌij!?Ø“¨J’�àDøÐHu¼["‚ªP ³�)i$…Ûít�ó@2ß×€ÿ <¼Œ0æ6 c¶¦:Í|æÄêt84×gw0boXºÁ¨Ì®m²+[»lî ØÚVû½ÚŽT•RUwö_3Ü”µ*FQʼnÈU½âœËD˜ˆª0íDÖ¼w×bÔ]U}ɉ\ Á_N“pÕ SeÛ{ÃѨ,c%BKD&ªd±ŠM…Â9Iqiâ}³È}·Yø¢–J-KH‚@U¹¶½ÇÞNd¥¶F/ô˜kÌð‰¥ ô²£ª$ñ�*¨}ð0Q�|V¿­«ðÄxuDy'ªJTÅ;Gp�nÖ¦•7ÒWö^«bÔ ` È€à4ð<ðw�ÿŠ1·I�S¯($Û»£øÚÕ6–&™,¨* TUdTVì;{#¿ÿ¿;á‚¢¾8zm{�­�¡^ÝÞU-ª¨•¼s) ã3“¨*�•¥GÕ‰*FFe¥Á9ySð¾ ΗIâ½NU¥ª"Î µ,¥×.h×k¤ÉÛ϶R`{oÈövÉLXàPãà‘¹äÈÄN!ÆŸ0,‡ì×õcïÜû QXz NýgHFFpíQ¦ï*Lœ1Feº>A¿ÖõÏl=W}ï\,Ëj>¨¾PÅØUe xxcÞ�`Ìÿ{)ððƒyšœ__èyðø¢;²0�sܨŠJYÅýª(ËH<CP”—^ßâÙ—¯pes�Ýa‰÷ž,MH¼Cœ°u}ª°½³_{CbŒˆÞ;‚s( "YBŽ$b<2! žV=§YÏo˜"Táêö.››C¦ýóµyNLlðÀäq–Û 4³:ªŠsÆÇÃjDŒ‘àÃ{Û+Q�ÖUXÿ,^‚bF |î8|á( rÞI+¢*Áyžßz‘?ôþû™DÔH‘'ªŠ–UŒÁ˵*ꥭ�ÁeYý-…¿\""ïSÝ:—_Û¼UÐ÷KÀ.°ëHŒQ æcÔÙ< �¨š7j)­z~ÓÿAïH¼Ç{Aœ ×çé6jxïØŸLØ” Ë €z-#ã÷;'„àPÕñÒNð4ŠŒÉnƒ^»N§y0uµŒú›•§×{–‚w7„ÈæÎ/¿¶E:j0•O±Üšç⃬w–©bDà†‹EïN"rPpê¿À‘ÿéÔ.ÂäËP¦ðÊ ïD¼ó8qçyæÚeþ÷Ÿ§ÔE–J‘'’&ÁïkIð3NÜ¢wn‘¼Š±¶DØãæ�SÀp¸œŽŒ7ô€¾ªN”•®÷ZÅ™é^ãB§Q{Ä;YÙŒV€û€sÀ5à̘M$ƤÀEà;µôìÊlïèâT;=l‘f-Eù’Æ×JUED`TV!KR>aþ)Ì=* zPO†w�÷âÿ²÷Pv\ÇÚhíÃL0g˜HD–-GŽ�Bt™á1-zÌL°è133Áe¢äÚ±cñh˜3õ«¯V­Ö:ó�¤Ñ�FI—¼Ý‡§{ww}»ê+€òÿ‚þµ÷þ]zPyHÌɼµ;bµ�Õ·nEf’˜6²–«õæ»<¿ÆÇö}"úT­H_æ5{‚ǤFè¥ôœýœ``°Øe®©3œŠ†Ñà0/*R Ú!ÃÂw‹-”÷¿x¸½Êç ¿ûÿ �‘#*ŽEâˆÃ“`Ûéõ2Ì;dá´�ͫҧæX¸tUëÄÒçѰ[‰XíŽX'¬Ä=äã�-, Î:+Ãçq‹»¬ÕîÀ%f»X`yÆYÁowx.;‘€ÏðÁÇÖN„­�P& rþP0Â+‡D$àbWeâab«”F21JDh8Ÿ7w+¡ÝrVOXAĶ‚q¢¶qÄÒœŽ/ê­ö s)VüW å†;ð("6!îRÞd,#HGÉùbµE[… ¥¢”?., ¼‹V¢Ä¶ Uôàk„?áÚYd‘¸Ñ"8qWŠF™XÏRô�é·PÉ}P´ò7ÉÏ …m—Š·Žê?†åÑõåÓDÕÑæ0Ñô=¢‘U~‚@ú )?ƒ [6×G6’H:IÐ'Á{ HZ Tƒs„s «%ðeøœ,[Dc éÏNM0G²ÝqczíZU�x‹¸>¾Ç£ùès2ŒnZN§®ŸéŒfžÅ©a$ëyX1¸wŠÕþÆn©rgy«Ž¼�ô�$X%³ÉXŒWîaæZ¬ˆ¶ø}øÙš•Ðõ«s#W_ZõŒfb‡¾ A¨Ã­µºÍ`¶¶+®(qY±uÁAäWe¶ÿ)×<šP¨¢L%Ÿ…o88Lßœ|S,�Ñà-¤fì6¹LEËgž€Fd©¢We‰xý¬8ä³ôØ‚êX]zwó#úÁÆûôý�¿¤ž¿Î®¥øç@Íç+: ¢®)6?÷´ööYnÉïò�>¹¿Ù^ß-ße¾™öÿ�¿$GN[‹Ä»HV�à1àÐ׺<* &[úZ†ßeÐdNÁ°²œ·ˆâÈü ±F‡ü.^uF˜´å˵02Á0C©ØÚP2ºcŒé±«¤R(×úìç•‚ F™²5Pöù<6vJ›«[„ð>à‘d÷ÑL?%.•Cç%À€›\¬$>¶ÚmÚ.öh$‰¼/‰î+cX*pñÀÍ•/·¨ßC�? ‡³„.‡1ODt!Ô­¾„Ñò†¬ò<îÕ#D÷ça%<>Ä× öd?MÑ`‚¢Ç~º(êŽÓF»L=uoíç�l²‚çsMåjS4cle.L¤in,ƒÇòÚ1ò|`á€ûrc‚º° “#§&�88ÃZ#jÌEc×¢Š)iÁc›�]c̃Cœ}Ý!V 9Vªq’`Àïc=ëöócwß²àô�œ BiLÔ­#«ð¬À�P¤p‡`eêåß8[#° °¯óê·Ç˜Tä蜼»A¨áUæE û…l6�‹ sŠr^ {Cõ²Â4p�µA¬‹KKžç¶Èsþ™ÑU`ð7(>á¿kÈìäþÊè«ô¿ÿ™f§¢`�¨˜"*¤xNXô¼Hn�p;Q„âþ(Eøøz5‹êÍ–ðY:78‡È�7b9Ó± €ÈÕùa¸ õ7éXÅ€#‘H<‚{”tqåáqŠH¯8á¿p`HµÆôÿŠvì;§î«ií3×Ï@:Êq¸X™Ç“±ð0“ʓܘi‚{jL°RO™@ß²àÁ2à¸d:‹¸‹ p�KÀÊävOFß¶@ø‹°$]WøP ¸MBxñ=CIoà( ÿ¶ñº ±kìXÜ$ŠߖdÄN`ÖÀ(²/hAáâ8�å¡€+HÓ±qšŒŽ çÑ^ÄE¾€!Ï�’™Z qYd�ïák‘µ2A†\$©2ÄL"ÑŠœ:žIXo%Ž2'§Ò›„E‹I2 Z=ÚjìR¾Y µÆ·…ó­�=Ûšc'“Å|ˆÂ7áz:QÒ\×çK[Õ�R½¥ ŸOutÉ‘#‰$Žå˜xTê\Àb„Ç ‘ËÊ{ucõö@¤Å "Äl2z‰­‹±4×a ÃÃ!Ÿn(~�˜Dg×P–'s4=’Þ�ÁÉ~pe æ\X„ÇvÂ_2¶CsœHË�Ø.-bdÂR"%HÌ™à÷¡ÀáoÇj÷Xš@�‚eÖG%¶HŒK¬&9ynlh?�+§ß5v‡… ¹�š—ÄÃòC0±"ÏØ&Ge l bêQ¢•I‰ª2ðÎ:ðÒ‡DÁ†|ÐæF\}©£%à²9*a¾'(v8¯¥‰‰°HšÝ&=¬¬ÑRý! !bŠ …JCj•±Åà0Ãé(_Y<&sÂñá¨þüÙƒ­MÞ—ɇŽE⸶9ü-­®«s ! ÊMåDºê¶ÚÔçn5 VÜ»®¬Ø3 ãÈ.æÇn(øP9n‰hòz0ܰ*ìzP°"­¶(¸©4[ Ï‚U‚Çx À‚ωËËÛ¯¡°è;WX¿ `ÂßÒB‹nùG{Q¡\¥¥�¥ã!“ã�d,(•†%â¨TÇV±Í£¤î.ñÉ›¿ÒO_iõäõ•êm7v)â›ÐÕ|Ÿ:M"ïgçÉ�(Š«ÊêxÈ›$ ±›!Óóªá�ÂôuÙÚåMö–9‰Tˆ¼m¢FðTâ({ý. ˜»}”ĨÁ`"Ü�‘ÌjÖ;’°z) ‰;p2§d$@æ2��?ÂVI§Ï~Õ‘C‰$€x «Õ‘Ñ’½¹Î󸢯5™Ì†?ªúìšèpy‹>÷Ïh#k<›ˆæ8k|Îãç†�J ž‡¬PëO‡[ ÜBX,(3‚Ý{œÁð{ì[ßzD€#£ü±Ùê°LêÍ„|ɥݭcŠMwIÂbàÀäûmk ýqKá, e5f0c¾\©P»ß¡F§!`2¢Ý¬'¢nÕOîµ1êçÔíy_ù� 8�»çPlðxȘŸÙëCž»ú6�ˆèg�.ƒ µŸh©}¢M¦7»-yͲ\ˆXà�†|ÄÙè2C äÝœR«` É�-¶"ݪŸÊ<Š˜<›8@âHH]W³u•RP1ZYõ’á-›Õ©ñ¬Î&†ƒ_—•u,“Šžg…�¨Vê�©ñ!ÓïvyôÜ¥RÝ@Yj ‹gB3X.�'É¡ÈW|ûÄn6$5JÙ÷­b¥ÍÅ-uÀIGYA”“,¬ŽZ&l]!bÉž #ö’6�.Ý)>÷Ö°7KA_�úV�¬†›úŸ\!Wu“:î#o=E¦TäÛCD£ëÄÕãa=�bÖÈÑÁÄXRX~;·_x±rZ.­f¡¤ B™P¸Ú€,"<÷©hP,‘\*J¹d¯ŸVéôœa#µk—•w@äÙŧ·ù%‹ .>^©Ï°{ib|8�>??ê[˜™fë"nYbtŵÄÄ9�%iiiƒðZ§Ý¡‡Ë[Gȱ>••(ƒ!J›°²ß—×ôH–ıñXYÃç¿´Q¤•­"•ëmê ÿb¹.€ÒR-^åpšµ•ZezëJ’ÜñuŠdÈëšLÇE›[ê0ÏÑ1¨þÛœ3(ôR., –zY3·‰|¢Z˜èÖEÔÓ::�LÝ'ºü>IQÇï¿0Ùç7-‰Ø²t±µUç—£Ëc¬€×m±«ÓËuœç+›‹UwJ@G0G�y,Xß µJzäÈSÅG†Ô�•SŽ£Ã#Æå)¦&F3ç/Ì��ŽæR TReððB °+‹†Ò1bp!þž‹Éôût÷þ:õº’·A/‚@YÁu¶¾]¢|¹Nk;eâ>°Ž‘l&QŒF3×›�"•jm€À³ÝÀ «¨Ónóg[´Vݤ�v>§WrW„Wxý2·}i'Bƒ…�÷7ÙÅþ`–Ä2éz$BKjgÅŠ¼ÍàñÑ@ü @äÜgˆà‚¿ã€í±Îìn�²¯•vÍæH"^?€ÖÎkjøôADšáÜÔ@jñEé•ãc«ÔÂelª° ŽU™A‰r©ršM á^®ï‚Ük„ëH$—rƒCÁÜòk()Ò!OÏOÕN�š½æTÀÒ9bÉ*_ëY¹÷ŸsÔÅ*%¹ V'Ù…ÇGsgÍß"ºô#"OWþDú—`Ú¾ÿ:QÇ·¯u¹ÓÈÓç»wLªRØã2,„\¸³7ïöñáÖ>Ç�r0æHµ—0ßXHxôižÇ-÷ö‰#�8¢~ߨŽë<Þâ1 .bœ�ù&$Æ'FÒþ¸cü>bò\Ü-™d”ñ0"¥4›ü¯ kÁj:ÒÊê½@8"#™8su*Uj´Á@8™K�Ëã>Œ‹ÖÅcÿ¬ä—lë´ÉÇ�ô%AGÌ-²àí(¶B¯LIWFî‡;ŸÑlb’|–X"RáWÚèòs!â÷ÏMÙK€Ûr,âî>ú-£Wý`�$›¾cäµ½`°Û(Ðfm‡²†>/ˆÞ“ÒîãÙŽ}ðóš}|À ~Iùþ£–Ș7<—9“(gíJ‰÷±}ÊzŸG«\kõé‘E²}öAÄ9ršÒ0Þïñøyü<~�¹�«óÓÃWo\�ûö7Îë{ï\Ÿû鯽ìÿÎÛ/ÑW¿t�n^_ «ç§èâÂ8å² ;;ܲö_q×YAÖ-<~¡,òVré8Å"-ù¡2¸;=éÁ>ðšŠ6ƘDÑ–¬:;– ²¼ñ™Ü…^C•^‰vXñ~ýºW^Bt™~Þ+î¢n®2)¡B§*p›Ý[@IyX#ƒx�Ú_=Ͼ\u§ß¥¥ò*mÖw¨Þ­àF„5‚s`Ï—–Œ‡—ˆp(|ÔHÃs¸÷X-‰d£&Š]¶¬„×ðžTb>ˆQ  ª4Ú]þþ®6²Âvsðq€Äq_}—ǯi7¹/sÅWgƇ^º|nò•—.NŸÿÆW®Æõ§¾ì~çË—éò¹ J'¢ÈsÈÀ>0à£Üä/š@A'¢;•¾Ä Žà7êJVKzø´ypÞ ¶ö{Í6²ò[âÎ"K> ŧÙûRì %Š&OªÛœŽG¨çjS­W¥j»N¾úCZ¯n) !Q½H �{i ¨œžX$DýÖ0IÁÈO¯•ã$‘`·Îó{¾}°®Þßü˜–+«V‡Zø Xˆž“Ðæ¾eÏÂ¥1_õ…¹Õ…@ „Ü€œV»0Àu*@²U¬ó{ $ôtk„«ý¢7̇ÿnh=¸9üˆˆãÚr$Æã%¯i4V•sÙ…5ÄüÇäÔX6:5–1 $tiqÂ.ˆhY‡ÏÃòûáb‘bEô"JPößEUVX»UQî¨ÃµŸXZÞœ@àƒZMX1w¥@¤Ë%á@òÒ=±‹°V[´tÅçð‚Iy²Ä:ñ»\¨1…y”dÌ2¢·Ê%Ê·óôáöçõEèÛ3oR6”Ægì>$(ˆˆÑW×שä^€À¿}î-X ½@(û¹ÏòûÞÆGôñö-*¶Ë]¯×åÎ&"õÉ ¡¼ ÈwòŽºµð˜ëkb‹9ÆüÚV#zï«M‚¤£AŠ„s§c�§^É£­BÍâq›y’žZ#y9‰c‘8Vˆ”0ù�)cŒ{šÐßzóÚ¥o}õÚ¥ï½órì—¿÷šùõŸ}ƒØ­%äîqH8¤@î z!Ê @ îñ®,±8°JÆ*Ønš¥ÖQ+…Ÿâ5”>A ¬Q„=åR4ü¾¸¶Ä g�$"!òù]´Ñ\—ö´°üçô»KFùfÑ­6Rú™þb¯›Ëz @º^|ÖïÛr÷ƒÍOé½ý[ô�­‘¦Uw±UæšN ŒÅ-ˆÒ2˜CÌ«‚Š�DÂ…¥î-KºR®lWØ‚lP�_‡‹ c´êðS¬$B"�¥ÉuQªDäS‚ý.�U|„ù G¢…õkÒ›`–ÿW~êËó?û�7Þºy1Ç<ˆù¹oÞ ël‰Ä´¶Ôqé}�¦“U@ÖÂ*@NÀ„[ñŠ‚3ûé×¾e+5ä…ÔZm|€á‚eÖë‰ï¯A!ÊãB¥I•z e‡GƒR À·¸(wéu �”¡÷XTèè~ù!ýÉÊè÷—þn¢�}“¢ŽÊ� €x;h­+µº ûù t· ÷éºõÿÐçù»Tj—Èçs¹s)áE ]ÞŠó`@üæïØÑ2s‰ÇLmP¾ÒP ±`Á9ÁÜɼ?IîË[E¶>+->×9å7y¼ÿ“–Õî‰ã"ŒkM¬/ñøU3�¯p’à¥o¿yíí_ýé×õk¯_þ [!‘ŸþÚ+týÒ …C~›4?.�ÒDÔV*"Ü:/ €«�€ËW!<‰çÆÈ¶ÖììµHX=CÙÁ²Àêî+Þbu-[ñýW¢8ÊØÑnP¨v¹”NW‚íšaàJ�ëÏN{‡>-Ü¢ÿïþÒo=øcIì3dŽ?‘!½ÃkD7þ‚èÂGˆÐ:tåß»…%úo>ûßé#vÍ ˆŒNÅ@°0¯Pþ Ì5À KÏž_ p!¨�È«ƒ-§¡hõä–œK¹«'å� ghe»,@Òéɇïðø\,G~‚€Ä™Ã) áý«4ëW#áÀëóSÃ7�$ľ«C\‡ =f ÌenªJ’Dš±6`í (@·º8M�ÔD“È,u'OÌ/@'àþßyÜŒ Öv”©éó¹ªY–$ ~Âã�9òRFÞ‘ –5y‡ÇMÎ._àü�Å+ç'Ç9 +úËß}ÍýÊåYJ'£â®9IAUÛF£%M¤È"Z[ߥZ½ùB†ÃØ)U)_ªÙdúHù .¸NàÆÂkJ 1OâÏ×R÷¢ä¤oŠòÎâƒG(ªòÚEéSn�‡‚ð~­y½Jî×%ÃMÔ7\f%ðø)¨}Ü/êÒB¶z2o÷;‘fXk“�õ$¼˜�Ë@Öúƒò ý·l‰ tÒ2 †]l]EäXàÝ{ùf ˆX–X·˜……7žk£î/ùœÌ�«ÂÎÛÁKûꕹÄ÷Þ¾îfR�8*KÝI @¤\©£5ªŒR¹NEVÆ*>öâ‰pFìî(‹òç§’Dò{±†{J”:Í] iAùAÉñèÙÖG«#[ùÒê·P~„ÿi1!¹Æm¨Ú«R�¶ÇòÒÃò}²sKHøt0‰‹‡·P@ ã»ñ"I¾ˆ·K‰µ<Åïy ˆÊB²!úŒüéê»ôÞý=úb÷®‹ñXäw$Ì7¤.<ÁŽNO€Ø¶˜m|UB|t0ð%3XçskóNá€×>¨&°7A–ʽµ]Ú*ÖÚ;¥Ú:ŸWë¿Íã÷yÔÉ‘3 q@#H�Ú}öµŒû7y|•oŽïÅ"¡Wa…,ÎŒ _¿4íý™oÜ ׿$?á” ;q{·$7òÖN‰vx0 à=ËÖ3/�€'A‹\¬‚1—£ÙÅ#åYh^% RR?>^E×ÇÏÈîæ(ÿH'É8â|#¨ä<d¡Y b›çžAl»–§[Lj/•V,ŠÏí……òl b’ÐÞjTª �¾4+E!*¶K e[Ök[œò †¸Üþtå]ZfpÃßîšµ<ʤ¨”0`mY 2/ø»–ÍoØó©‚Çøü^÷¬ 2 / Ùa‰ÀzÄVÀ¢"Ü×òf‘Š•f‘+>¯ò9‚;ëÿá±DŽü˜‰ã¶šÐ¬KZ‘÷¢f¦¿Áî�7r™ÄÅ™ñì‡ï™ 1ßüÊUâüqÍœ¦�°E²¹]¤&ƒÇÊÊU«RÕUÞÖñ¹· ˜`]ä2Qž×¬iº´SnØjn+¸±àR±‘Ë{”ÜÉí#:<ÂÕÕèÕ)ß.PµÕ7ãjuS¢£¾ÈßC)d“#š Àr0@ÁGÚ~)¼(cmB¶Æ É?9Îf·M o!ȧ»wèÓ�Ût·¸ÄW0ëR›ê®2¥RAD¢=î¯+�(èâ1=zMDÁb@Ìà›GÏ)ÀÊ�UŽ<¼9àÖZÙ.Yl‘l²[ ÖÈ{<~çÇÍq€Ä™›,�Is‘¯Dxµ|ix(9ö¥ksA¶@Ì/~ç&]˜“ ¼ÏKjµ&Wý]Ù-”©«„ò *v @,ê§±á8eSQ9¦¦†ïª«Ö ˆyyN¤.«SÞOXFàÐ÷¼Òaò¸UaàèI®íz�±X±? r«J!o�"¾°½ØX0<ôß^1ú¾Ër“TÇíµ$äx¥²7šp?ä,õû¥eZ«mŠ%´Û,"’L"ÌÚ®&u½ ʤC 耱 {^;Ð|ÀJ„D’BÉ @û× ¸µn±ÕÝ,T󼸫 òÉ�SÞˆ“ÙîHOWFSš•žæˆ¡æXiĸ:¯÷�ç ç… #mcí*¨§)}Í£hµ$â†J¥šp%ívWB2_dÁ±Ý¸2Nã#qZÞ(`rû÷wÊnp$\I­nWCLñ‘çkA¡1úÑ«uªÖk´Ó©õ=Ô°êTëÖÐûC¢»Î§æh(œ¡€ÛG�T Áà"¯ËkL_-Én£HË•5„ð ÿÒêµÁƒHÉ–T kG«Þmðã5­5M�,W—�ØGñhD¬¹Ó¸ûz= ·–¿¬4„ç² áuX’8‡‘¦–Bù‚G—ù1G¼Zëç�k.crliŒqÞÇ,¯:c‹³£ÄdºTäòî¶©~Ú"ž�N‡*Õ†Ö�‚‚ÒKAÄÐ *íN—ü~7-ÎdéülЍ×lu;;•ÈæNÅ b àQïw´M,aœ w2”ŒQ*ÖC¨¬T3.Ô·YñïP¾·Íc‡ÔPȦ˜7*à’d0ˆxÃh˜>ƒÁ¢„ו»èÓv#/@QmWqª…s!’2,l}äÙú¨S£ß$`B{ûÞ…#nІ£àqNµ¯ˆÔêÒ°lC�ž³ì¾/ˆ¼?²[ªQ“_àkµ©9#äÈ��8âÖH¬—µ¼ÉUö'ÇcÑÐ<ç~LrUZÏÕ SôÝ·^¢‹‹ãð‹?Wåe)! W– á²ù*•Ëu«Óé½Ð Ò“ä¶.ÍϤh|8�"Š3œM†¯œ›pŸc9Ç|È«l�p»Ûç ² ï[°BPGK¢´ ["ëÌ“ÔéEæ@˜�­ /òÊÅ šIR½Ñ!+� ï-ïˆ5¢ég´N˜î'øþí°Ùý* +? ô-áÄjÙ›Ûair¦¾®\�®‡¾_~_>s–D“Þ .K$�v:”ìÇ›KäŒü Š5¿h†Ž8@Ô!4ÄwŒWƒ¯qÞÅûR^¾<Ãà1/ 2;™Cqų⃑.ÊÇíq¡ü‰¬ì¤, �ÙÁq Ò) ÐkצèÚ¹1)= €S"€Šå†Še��š`} µ(À‰èðˆâ¬Éy±£’íÜÜOGùû.÷™=çêÒÂ0v^Š‚lhÕeCkŒ€ ¿÷qŠ6¾¨@âÕl'K]æìô ̇„¿òêyqe-LgªS$ì?sy(ʸ³S–ÆU;»eÔÖzáÝËy¶Dúüï•Ë $3øìD†L*‚jÆâÿWu&@|€<•ô4ñûðî'$þÁ%%ï{y Å«ã'I„í☠¬ÍîúNù]&Ü?â(·<æÆ‘M ©hÜ+ƒkÃÙĶ<"”wçæS´À â—ÞÏ¥ÞÑ2ÝívG¬�F³E[ÛE ’Jå…áFö«;¦îª>]˜ËY¯_ŸàÀÄëçme % åm=G W�µÄC‰ˆDf §òûx�ä¿d,L xŽýÇw~Ò8e±À0ƒ,® \Yß©|Ây%àE–øü÷ºŽ8@ò¢ó,�×ù¦~�kd½qíÂtîÍ›èÚ…IX!ÈPGËÛç�Œ7– –E’/²±Y b± ÿ9‰ÔÓ¢N§Kg]P¢ÒƒÞåX±6:½Ú…ùáÖÍ«Sf~2KÆ ö«@N À´P®“K;>7ÖÞ| X~-�¶­€êYD$§dl(E¹LüD˵h„׳X·§éæR×V¿Ã\_²]?ï«G’~µœ¼#/Š8@æñt.䋸ç¸&ÖWæ&s €Â.­ñ[§¸EyºŸ9úG‰uÔÎ’°ÑR¹&õ´È,Dþ (ãY$žU©¹l%’@S¨tA°ô¤>U(@Vº•M‡ºo\Ÿò0��¸¯¬A^ª4%S?òá—¡ÀOÇ�¸§1”„Õú}<‚k–@"¤¹p%ùå×â‘�{*¥‘l Ÿ9±f`Nk\O;È@™üSÄ„ýú˜‰“1£dQJÛêJ®�#Ù~–�/Åï ò¦’ì©t"znl85?9–u�ùÒµy ï•›cV¦¶BµúûEðhd�KÞ‡¨@á  Äè¿Ý|™ÆG3ìÒ*iÒWÿ̺°°*‡5pH|E/æW¬,7+¿z«a¾úÊTü•K“‘}óJ°êG"ÚÝ‚Œ¸žÄÖ¥ÑQdÉcÍS±£§�ï!û¢Ÿxà±Vv=RÎx-à—DÁt*!¯‹…ÙjKR­)%l4Y¯‹ß?2ˆ`h^‘]tû‚¿ë•óÐh,R4ÜŸ‡ õ4¬¸1qM‰Cìò ˆ›YÛí:âÉYÌ ™SðXà1¤î¬ ]Ι㈬Åk¦‹%eLN…/Qœ_/u ˆŸ+B¢¸D‘ -íP>_FË\áF>¿½‚ܸµPíÖþþYI<† K€„«%“¸€‚~N,\'´§xùŸ‚ÈãDWÖHV¬7ÚÒ™ð˜Ävג3bY�A™¬ôáŽxØu´, [/rAô7s= ¸³™ çÒ´07©‰4Ój­oìxrC[n˘Þ?ºíYZÞ84W�¿ƒ:\ pØ'o×mï7ƒÝÿ`ï£ xÜ„—0Úf-àcö8•<¬ 7Ìï¶éðc/9rÆÄ’°Öɺ¢VȈ7¯æ{œ�~ñâüx–;w2¤‹ 㸡ŽS†û¹ÌÀèXäÆm6›$®¿Ÿ­Œ†ýyMb¿ð!k]ÜBÛL¬Ë�Ø·›7�é¾ëáÍ0Á<”Iðc/…ÃAÞú™ó¨ÓƒåMº2›bÞ 6XR£'­sam‰$·o7óU€ŽŸ9ö@)CâõÊy b…¯û�½2ª¨ù¼â3Ñdd‰küNˆ­«‰±MO�R:¥.çÜe¹¡@¦Ãa¯ÖŸýå'G®çàý‘ÚZQtb0@`þ±�6ìÍ}�õðIP¾‡}´˜7íŽDÐ ä�0ê3:Ò5'×õ£ �6‹K�#gL²=«I†“¸>ìçn·Çþ¼~ÇVNÉD”b±0mí‰ GBy©KË:³.-Q¼~/ˆi±H2ég%¡Ý´´ºM­fƒæÆÄÕ5°b­Ö[¢èšÍ=XÙ¥��²ïÆv™*µ²ßñþqºZ \5Djba¿±ÕvºB¦ƒT‡eˆkf€“pëÑs‹E(‘ˆâ˜Á•àÛ9'þ�ùÙ ·—Or¾X9r(2]H™Ì¹�þØoä±dð¶›ËÁ~â}仄Òñ¨Ið6à8íÈ3 X[>�ü¾{Ä®’|CÝ�Éi�79âÉ*¾˜ÔÎˆŽ°1&Åa—®žŸÌ sáe䉈•pü¢ÖˆKDÀ‚�¬} À! Á¯) áu<ÆÀÈ÷DIéÍÏAöMíõdÎfx/ö n¬T2 A£/€Š¼�UñŽUJ„½”IFô´Ù@ŒðPT1–PßÍÝ }zwƒV7‹´�¯ æ–”K9QÅjt…ÞWâÜEaVüuUº$Þ÷à=[© Øï §µ iúì`ëªÖ.(7sâšPKJ8$X(xMÚ­çÀ¢5¸lPÀë8.ÙOO�0㑌†åq$è¨bh¾Œ€À‘‚"AÀ E8Y ÌÑ1Ar䌈$5•!n}œäVŽ“טPˆŒ§O*~^]S»Áž¸¸tk[x  ‰D" 6€ Fú9x=¬œx.$üYeYÍFX��eivj˜Æx›NÇr/Ö· ´¹µKÒ=zØý+@¢çK5ZZËÓn±F[ $ï²BúÞ=€ ¬€Í‘³ÑeNÕªÐ2C¡b«çG† jµØà²ÇÅd7 Áê”s ‡d»Ÿ4àÖ$‹ÏgîÕCY~ V€„ºÜ°ß6øÁºø}¼•¤Nù.®»�sƒÏtðy$P"Á2€ ¡üdd~”¨?,€#iS÷9Ì#ÇÃGŽ8ɱFâÝNòˆñÅàð‰×®/d¬cu|‚ä´r>ÖÈ3å›è°£Ó�+‡h~ôñ]¬v÷ukƒaNÝåeŒp2·�¡l‚ÆFÓP�pÉA¹ déo2�X½¸¬’íyk¨¥±S¨ÑÊFQާPnн•|%È{lvø­E„Õ¸ò†LO¸yþ(ÄeÏ%Ä%€ =±9@ß�BI§SqÊc¯“[\Ùt‚-·½W(?óu&J±(h©u´WiÛÇåÝÕ* BÎ Ð(Êï(¨iYzÌ,6pr.MŸöÆÇs7~ó™î)µŠ°oØF:D™³² uıHܺ²¹Îã´Ä sÆÔ¯œóÿêO}«ßS‰p‚ò‡{ Š¿ÝncBñ‰…BÁ`�Âá0ExôÉMuDkuºÍSÁÖ¶nNSü겂R€5‹†Æ‹­ò9ƒŠ­PfkceK”X:!zDP£ ’+±^ä£[k‘Tª-ð#_=4Ði¿Bê²÷�Ö¸Pü~�ÖÂÐϪõ�!Ïûû·[,Gá´†Ùê�Ç#xí±eoêl‘\:?MFŸôXìcòØÜÜX�µð>Ž/Ê–nL, ?¬ ÛÛ >8N €"€P9ÛÍ•ŽGìv°\™÷‚ßÇÖÏ×Hú¬¸¶q€dœÇ¯ðx›Ç°1ewVôëo\ñþ5¿ø&MŒ¦OµfÀŠÞ«‘>‡0ñÙ‡AÜ&hd8-n“¡¡”" F E.ÖÀp.%Ï-²N­ï#É€�‡)7”¤l&Îû™¢¿†ýÚ+…b•V7ò’¡žN„i¯('·ÜX±h„ç#Fs3ãü8L�ä¥�EBÄoçK=U¾ƒ ‘š3òDkIócp¬ ÕO;FcßWü%Ì•Õv@±‹6jd¢‹Ç°p›Ž¹³.®®£ˆ&æaµ/€Ð:YPâ^%›J°û}Ðj,‡„*$ˆf3Id�#Üá½Ò'|à š Å7ÊlÔ*ò+?&I­PâD¢Ð ÇVV \Zè¹÷–dÆóØ×‚ÂÔº°µ“m¢”O]ÒŽœ¾8‰’¡¸¿ÉãxŒòðÊ«ñ•¼?ÿíW¹ŸÈȱ—ÄFNƒúê±Ý×ÚÀkpA!?.¯C‹”R�ÓWßx‰¦èò¥9ºtqV��ç˜Ô‚5÷Ï€¢0æd-ð!S“9€,¸³ ä» /”jTk4¹nBS æ~w±D>¾}tÑC;b ó«.-X!°H$ Ü­|äÐó pH!± •ІŸá69Îs–MR½Ù:ØBÇ"›\‡k) Û!¹§Å's¦I˜м}œÑB�Ø ÀK™’öJ´gÉ‘ÓH²É¨¬^î�—¡¬¯ñEü7iù/�MΩÿÆÏ¾á»z~êØC|¡0Š¥*Š'ÚÏ¡÷­_Ô¥f³A•J™?×ÂkGN¸¯Pr$—M± )#7óØè�ÜÈÕZݶ‚4_>îãŽ� ³¡`@ÜZÙ,gÊJïï«xr°H0PŽÆc“ºJ>Wî»[¨K‘E(+XX x0‰HDŽî nÖÜZ�©‰9Oƒ%¼ŸNÆäZªÕ�ˆzÀ%Cr.Uàú˜ž“àØAÈk5×Þƒ´ Ijµ}nä¹÷œ ˜n‹§¹åˆ$禆éŸý»~AÄÿü;ï¦Ù×úëdÑ+Z”qã×~úõ‡ÿÀßþsS/]šq�D!—*«r¹.E«ÕÊ•<É2Ñ2ðm“ ,“#¯´Pˆá x(Hq�ìæK´²²¥u½ >IÉdŸ=NW¸(OøöÖ å‰}x:`[R¬�-’*æ >ú�Î~PŠ[ù*€DÜ%Ï"FÁHÃvm…EdÉÊ]ªóòV€EÀÄo“ÌGÍvGnÜzqž�,3dÃ04´ãI o‡�w»ví,;÷y¶FĶ8~€ƒº!Ê�Èû~Ì“£×Î_AÙ}½� $irä4Ä’¡TŒþ³ÿãOé�—¢éxøWŒ¡Ÿ¶H.Ä-&ÖüCÇÏßœIG5Zˆ¬(#‚ä:øÂ‘y½¹UDùéPØjuÔ Ù?<ÖB½:ZÙw�Ø�bâ=ÊJ¹Û‘I„òð—cµ<1ž“׎óè^pa ç’41–�Ò<бàù"ÅN‘R±�ʼnh0,¸ÌI5¡¬ƒ÷Ò� mMÔ<$ÂdW½ êÊ],&-…rL¡ÞÊ …ؽ7,†¸´¬ƒôœ�ˆß�ï/Ì¢ÞØ“£ ª„- £`øüZ´Â³‚´p7òš2™$e‡RR$ÐÁ}ƒ$74Ã:¶ãV0@)tÞJÒ¥®<Í�ˆYXkw–6„'¹87LÙT”l†Ù%i[�OŠ@2…�·ê^‘¯m� ù%>ÔÝe÷9Ѳ'r-s�†Á“á館„Ñè*ÃöÜô¨ÝBùqß7ríÈ÷téy‹fÆË¹“ëÃãÑRüY H®Ž}NÂ<àZo•Œ!rO2Ü} 0pM‡Nå�q€äÞê¶+_yµÓé½Íggj4k^»º0Î+œ ”øÚú.+øV †òGܤç§Ø??lW×!�>îtÌ¡ÀP˜ø]`þÖÁ”™$ÒúVA€t~2#Êxï ?òaûD‹Äý¨ìyµè ”ôåä“ß�†…±£‰”K8¶™JÝ4’Q²!¿RÚæàbì27Íf‹nÝ]~’Õ å<`Uá˜Î‚¸Øpmc °xcnP¯  !�"5iÖ"Ü�†ŒI.Ãù3.9 JI8�#',�y\ç•Ï×y“âgh$OGÞÀÚFAdiy D¸X n!S– ü²Çþ‹ ‘[PªXùÕ2Á߀-¯ncÀ"@döÑ9:ÜÇo(˜Tù»%�ðÍ”ô1oÆñ A–Éìô¥R1„¢â>öL~ÀVàA $†òêÃɽêá¸â"éîË/ ŽfTCc ÅʯËP°QÅuì«wp`XQÃ"A°æ}l$û,‹;ª. £š±Ì§µ†MÌK8sàùEjí @Ãn;,¼”ö°€x4ªK? î.$G‚ˆ7Ö@'SG 9Ù‚Œ_BÒ¡eYó|CñµêÅÂVE;¬¼‹¥ƒHMúÆFž-… aõ' Ϫ¼*|±epΤ$¡œM·¸U]Ì`µ  ŸËç+à À¥0Ä*ÙÝÝ…»Ë®£p¾Jš*lž„pÅ�ŸŸ@‰r Ž]”Þq :3b®(p!>MàÎÑ2’�¢T x¦}æµ÷äìlÝJ†¶ ëÒðR;iÏØ�¿9\8Xq#äVrG¼>ï¡\dèÓ‚` Xa{›Ié1ÛÅ=Z@Û3Æ[ „uh‡UK_{¸½„#kp „½X%¼Õ ÷^�'UsIBäÈ Š$“ZGk„mÚ²(Ȧ±8“¯ÙjÁ�å KA=®MܬhO ­Öɱt@l4$#+hýí&$<,Ü4âæ*ªèÕ-¼ÊÞÊÚ?—·PÈòÝ&�ÝÝ­­­1J])¦­­<=Ëâ+åkW à�ý~b…)Ë•Õê-±ÈÌSJsl3°ß_ÙWÓüdVJÂߺ¿¹eeNOʼÖ|¿W,-iÎ`¡„®lÕQЉU:Æ<#°at8#QW‡)� <‹p,£C°"‘T:@¢k¿»Ô»1t§<ˆÖ.{ÔŽx/0ª«ÑþI/·a¼q´Ð9rBâ”Hñòˆ©U’°È {ù �M{±*…@©ÃuÔ·Iîˆ=\àá ¶¶‹”NE5î(½½�…•T»#.(Ã, 7‡òj þ.¬,Ô¤j°ÞvEiܹ»ª‰mŠÅBBÖg¤Ek–u› ‰{æÙ8V²RŠÞ磓’B¡‚ºZ°ÊpÌOt��=Æs‘ŒGiy£ˆð_;Ï@Eûg»q×WĶ:¤wˆ¸­[¼fWì=a?¥"¯íf2jÝ‚l/”�ˆYÆþ£:22·üT£Ïq=g•F�ý2†ÜcO> —·ø”›Œ‚È��xôÉ‘cÇ"1g~‰G„À\LÒù˜1{j9Áíî�Ê^£d¹�î ½a%hov:|¤”!‘> ]ü;_Å1Ù"Ç„IÔuqµUë qo­¯ç0xOÜ^«ë»x_Húååmºso�VW·ùs[tçÎC‚€Üç?¨ÅFä ø›_�‚¡©xŒäB#,>u¡õ÷åj<ï[»%ñŸO�Q­MèrwÇÞÏŠµ2”Š>&ãÜhYs†öÛàá±G= ˆ©™’ý¨, $ÿQ$+„­æ]gKržAe~f”9 Hж͖^·‘æ�¼bÌÞ7¶{ ‹D ßIé$G ™åñuYV®¹‰ÜèÅÙQ7\û¬p”·°Ä}´»[ˆ?�{ 7סÄ"ÃVŽjký=*p­¬lR¾Î¦À€Q�û Ü�ñ·î¬L:,`riC"½VVw`vó’ø¸¹¹Ëಥu»šN°ae è$¤£sk ó «û·7ïDÜ_l)Ž %èÚÅ)Xð.3èzBQÊT<„Õ7ËÞ²éö¹ akމÕ궨P/ÊÖæ%ýÓJ²{—ï)¿¢ÜÕ±X@’i ˆ›ëââ”$6¾zý¼æ¡4½�Ç6aýã!6�k&`ŒÁ �á�­#Ç*� iUßy‘ùÉ‘é—ǹÜÖÓ99(8 ¹…ÒS·–Kˆù^·w¨Òâ‘LF\(õðŒíwaÁh´S›jÕ\mPÄPÂ<êhtdGE}öÅCkk«h=x¸IŸßzˆÈ/ð-(… E Ãz²Μ\/dÓ¯­ç‘Ù�ˆ¹}ç uµ #Ù$� §a‰àüÚ‹ÅCé( gc¶o{ìè+ÒDC¯–L?<‰×ãj~“–vWh­Äcu— �"íÔvi­¸I律êÞ4ý¿ñý?úýî_~FŸ?X§ªFVÁ:h·ðùc±ö`Õ,Î�ÓÏÑ8s%™Tœ [p9I,e±sGd>q®ìq¶,ð\ éáÖ4ê;Ü%åq8’ˆZ"oñȱ=zu~t: ¹tóhô8ú‡c…�Õ?¸ŠÃ)K2ÆS©㛇^Zâ £�•|§c‰[®_ïìÞ÷ð‘p&3S#ˆø%–J¾1ôØM^ωf?Ã’Âïß�SØ·}s\Fs)I H¢¦e‰ÙkÎÅ"ºvn”>½³ŽÎˆUDdÁp ”R's”•“‡-A‹ú®u#Òs¨ á§o}ë§Ùš›§¿xÀóÿ‚|rw�r©(ݸ4Ió£°"Ô…Ú;rËc€ÄðP à$eVòÅ Ü]à÷¤zÀ·R§ÝÖ¬q×�Á²ÞlS[­sM�µ‹(‚�¿ø¼Ýda)¢ Ý’— ®V€÷{QÓý_úä@°ßOŽÊÒ•³ÏïÃÓ"�Áûøz¼/¨"éìÜÌ(¢“0ïè¹.¡¢uø½ÜÃÄ[.¹„]^Ä�ÍȱÑ¥Ôhu¨^ë1˜ 4˲-Ÿv«…ú]-gÁ mËbÉ|±ü�>ùü}ðÑmº}wù bJEÝ,MŽ ÑÍW.ÐÛo\§‹ “”å×qÝÄò-ÕZt›ù¶9ý“{«ôÙýuú˜Ý¨wÖÖÅâZÚ]£õÂ5:MzÞ‚üNCþ‰‹>õ@|UHwG 9+ée‘qAö�'†SÑ82dŸÕ|†Ÿ«X‹!¸ˆ¤‚O«6¸‹ð©g&[³it-ŒžJë^(‡<ƒ!ÜG»ð)E¸Â°•ãxœH$XÜO‹þ�÷ߢ¿óïú{è‡?|°<Ø¿ˆ‡êÝ2}pç}ÂËÇŸÞKñ8ãr‘÷„>%‹sôÒåyz‹Án¯ÀSˆ} m÷³ÕôÎÏÐÅ/}�n­äé½O—èÝÏôn-ÓÃ�2Þù=꺴Q^§Íʵ»m›':mþVj.C5h[\½s¸ÿ�Z{ËH2Ú2w�GŒW+ñt<”ކüϤ?$¼”Œ(¯ÏÂÖ‰½ÒE<Èn—‹žHFÙUsåÒ,ùqsŸ‚ô°¢ÇêÿÁÃuŽòÚ„kÅŽpR%®�mמt+)“OrVØ:A€Ç~×Z¡V¢•Â:[Jzïýè‹/¾ ÿï·~‹+À œxÿ(¶�Ò6}¾²Dw7V\ŠòÚiˆ`~T*¥k má²ÒrH¹Ãê'G 1¤¥x•âb"î"+_"Â÷ÌJï[ �! rn�‹Ö6¤¤9nÒC»~@_¹8‰&S§äâ²Ä¥µ±µ+@!¡´ZRE«üb î< \b’àèr¹é„Éd €5’-.û̇EWo¾F‡\EžÿZ³C{�<›+‹£ÄU º´”E2#Beµ5î‘V@ÉëñQ½ÞgKi�«I% î½Ö, ‡±ÿ.ûu·ÛP­[¢ÏÞ¥FSÜ['&(Ãò&»¸x!¢RiVh§¶MÆÓpûïÿ‡ÿžþš¿ö¯§ñ_úW¨X,,"pÝ?>±ËîÆ†œ·L4Eñ@ìT£·BR3�áÝåÚµIv;¯ÄHÇ�D]ƬpõÐI6Å3(…Åy‹ €àI@¸£§8rK²ŠÒÁÀáÉh¹é’éÏqšÒÑlòí�‚�§Â£D”UAˆ0ʯl Á 蟨px½^»þx¨}t!B°áV‘s ‡…Þ-Õ ²O—ëË×gúLÌ÷àöÐpßcv„åƒëÊ깩TnŒíý <4’ Ù0å46–2é{ݜ߾Å^·NHôï¼zãýê/~ÜœD¾•D6+ÛÔ³z¶ 7tÑææ&¨D"øbQßãpŠJ à7SÔ!ðÞ§(8>€ {±=}"»kbP†#‡HÜ<–Ø A³›K|�A© úçÐÅêzÓäa4‹öîýuQÄCÙÄ1ÜRüÔÜ[–†-�à]]Û€ðvK^ÛaÀ¼…lx‹Pr®&T§=é}ÓÚ[u»j±×3P_É"¸¨Ý•â�ǨÙ1´¾]²�iB!§Ä›Œ‡Ìq[{àÈ`áÀšj5ûT,¶Ûà83£Ùé8EØ I§tù|šFHöÔyûáÇŸ1(õN¼ÉÌÔ(7ŒÐêÎ6­7¨ÓëA7_ÎÑ�kC4>~bÎÅýåUªÖêtU;V�Vw·©³O¸ß`} ^Ó@ñú¾—qÜpåqx=½¼8‹ÄÐÓŠÞB‰„=DpmrXx”Iü°Õ€q€$ ÿNOzÅ/MŠ‚¸éÁ�Kù÷†ÑHLÜÕü™Cwˆ GÄ "+]C'.ØX}¸¸¾¸µ„ãB5^»áQ  R©"Ó?¥ÚL(˜¹¼*U�µJï£*è<:ŽÌVFBëFõE‰´ú^juÝ à‰Œ›‡‹‡ÁV¸�«Féæµ© ;�ËÐ(ï�Ïã¥N›xÿ«´¹]Îļ� y„{Ø{yìÍ?)–ËTªVOœL(ñy~÷“/¨ÚhÀ¢Ø< ÀrŠ„}ZB®DaÖóžë¨O;y´9èÒüw¿üÅÃ!÷‰•ªwkyiS¬.KXô �åÍÂ-¾aîòÀÖ/ÃHžý:37yú]¾Ñ(G�ˆ±’=Æd? %F´ÆU �ŸµŒø@�(¸ÞpæRq$±DYªÕ:,�.[Tx‘»÷VÀ•€dG~ ¤;Ü^ân: _ÓîömKOÃ�<Üû•Ç ¹ÓwS­ëãBD.ÞºØ*°¸ÈíñÑë¯ÌÒH6vì hÂ´È tq*GéXŒú=¢ÝB“¹§:óLM! ;�¾ù´á%|Jž?³¹]ãù­Ëg X˜À}z’ *!¼÷Ñz÷Ã;<¿}Þ�Aû` àüB’“stír†^º’¡/±Uµ×-+pkÝC›47ž£ßüÎë4šIQµÞFÂ& '3 Á->Ÿ�Ò©8:zâ9jŠTÖê­öšOÔÒI‡�Ò¾¿´F~|‡îpX0<3ï}ð9w–‡Ë›ê®9 K Ò  5©º¬aº.’½@Ê£RCÏúY_ŒÈ Pqû‰Œ€‚Úfô¥ë3˜«½YèØÊ8¬€È_˜ÈÑÕ… 𵆤I”�­mï6Ø h�C�ÒåÑb\YݨÒÒJ™Á¥-sì9'™ÿÎýÙ{ŸÑÿôÿ1UJMŠãä!_»vËU±¯à9°Xñ«e¡1x¼7Bk}³†ïHØíϽu�¦G3´¶Y’ÊÌ­v÷ÁIT( pÍÒÜÌ8�Žd¥]ñù¹‘ìµ S9’ž.>b‡�ØrÄ©µu‘#7nc¢D·HÑ £Eó+D�­­÷Q²+xõ%û¹ì\ yŽ(%É�^]/R¯Û¡h4ŒÞÝb¡4-„iÊï<|¸. ›Üx]Crµ¢+AP· |Ç!£·Üšè'*»X¬ào¢Ýj»ŠíiJ¡Xæò!ËtýêŒ}œ{W”xÉ£À[à}~¸²A™Tœç4LÔk)îøˆŒGO˜á ïYëÓ;›½Ë;ü&ÖJ¯Láæ9JÄP‘wÖ�ìS¹Q£Z®# Fm.X�ü?ÕQ>ÅåæsÞ&«ßãsÝ…<9šƒëwìÚ¡ûOÞ§»Öûïýè"ؽ‰ÜWZßÉÓf¾HÛõ&ƒ]G‚Â!åCàêÔœ=BÇZ½#¼ãw¿r…Þ|e�òŬ\«¸®O4ï…(QÝx(“äý ʽ ¸Ã×K�èÒÚ®—«E·y_;Ê•ôÈ‘gHBƘ×8yd†}» ‚Õ’9A‚X’ç ‰"G 2àQ~]Vvj�hïôžZ*(unÑ{?ºMÝvÖ”9ÀDd}c$.V[P˜òw*•püÈpôF�Y�Ï Ú¯�l¦‘OÏJC8Ú†ž£Àý‚¤Iãš{b1¸¶àZ*ò¼üÅûŸÉ|¾ruž‚~?QOÁ!¨*áXÀ¼ùÚ%êYŸY ú& "˜sŽ27Çâ»Ï$¢Òá¯ÙîHB^»�jÍò{|´03JLFûüŠF¢Ì¯Uèÿù½Ðý•M� DçÌ“™Ïåµmº»´N?üð6BÙÝà o\[¤7^»*‰©ÿöcyKrŠZ # ›B¾-²­p=]žãb©ÅÜgK?N¿òíWè7/²ÅÕD‘Lxˆœˆ5A!Jœ/Xêè.9”Má>“pæ`Èß›Ì×S‹3c÷è)ue'¨ÈÃ…qP@qÄ’)ŽÖºdYVœXƲI¬Oœ´0 ÿš4ÍNËÊ­XªÓ…sÓÔëuÅòÐU+\+p)iÎD�ªå*årI R1Ùg¦ÇpƒH߉eŒE¤A‘g<Ç¥áH½¤$䇟ܥI~�¨På5IÒÛ<_¢]°ê|ðp‡�/K.óx+`ˆ•ÙN¡"$ð‡Ÿß“ʾ¯^] æ„)ñ®–ÉÔÔ¨g~½D�vä> lÞK[ 9‰qOŒÆÇrl©6e�ñÖW_æëcŠÅ:ê+–ìù?üŽX ©hBÀ踹9÷–7$*nemË…j¹ã¹[|‹ôÎ×è�ׯÑ4s ÿçÿ÷gôp}G‰¡ŠL½]§f§¥ ȺÔnÙ?.WGß{c–¾ýÆZ˜ÊIïü�nm°UÐàϵìræV¯óùÙq,²¤½°D÷’û°† ˜ž5”ˆ…Ýø‰›H$NŽ8@ò ¹#W8&ØŽG‚pi�ZrߦvPœžÊñ o“¯ònX#êŽÂŠN²°ñXj ÍÎŒäý…ù„¸[2é$ÿÎ5¤wû.n(9�‹øØ =ÃJé<ƒÕû|AÉDÄ£¸yv™(ñÅ’•òå “(n¹Ÿ‚Çœ ƒååÅ üìÎýÑ÷?’×_ãÆN.á§,»`‚ºzi†­€º+U àËŽº�.,¿cKŽ``‹s9ÎÜÉ¥ó³rΕLæ[å´“/Ó§Ü/fnzXÇ-»¸&lÚ  ƒ5‚�b¹Š¹’¶Êß|û�ä’Ö_¾÷™7†@Œ¿_mÂ]ÕoÆß‹P6¡—'éÍs¨kÅ×}•¶òUþuÊk $íc±ª”¿€‡ƒÚÈL,6\ë˜[¹W<è/c¤‘šÑ¹Ø¥` ©KÙR×Vè d»#�$yŒ³«#É7‹'%uw<§–à×A5X¿¿Â|G­Zu]»þäžå1>Þ¤V»�RÜÁ`î.Q4©T �|<¬ß.+%ê¡pd�á×@ï䋸áà2Áª7”‚]Üpo„xðxï¬ƒŠ…pÙjÍ^�îåš ±+C×s³¬üŠUÚ`òø÷ÿìäЇ_ƒ’Ö&$–X‰“cYVâúüÎ æ õÇ$ÐÀ.ÙÒêÀ5òLŠÑ©�9ˆÀ—/½A&ÆsXU?VqαÅ2:”B¹{qÙ³`厤RÞ�!Ç—†kÏbeŒ–ÏײÞi#* Ö‘ÁšG²Í£"`âÈSÄ ÿ ò˜dwVÞ(GX#†N_º¼‚òÛ`7B£ÕEBœ*w¯r#€ ” VŸ¨ yªß]ùlå&“~(ü¿1?7N€0Á È€“ÖÖ²/¿FwEq<ð÷‘™¦{wC‘£HæÞ¨1A;¼�qœÓ̧Lñ¼Ì¯¬Ñ§·ÐÚæ.ýèÓûÈ "`‚*ô ޲Ï$ç%�ŽÃªcЉ£ˆ¦†n?{ËÁ(Arñü \“O±ܰ6Á{áŸHã'tO‚n‰±xÔš�ígÓñýÏ¿äáø‰,£ žð¹2DÑ\¬ÆûúV¼È¡r�07¸ž´¸~¤èÅ?4”‚‹WI®\š£i¶.Ï/N±›p}V´O½Ÿ6¶ ÷ñf¡ô£I>o!¾×šDTÖÑ&G 9 [k…£´ZðHÿÏK PpSÀà÷(6$.°Þcb2�J@é`l¥†(±¡¡ä™µH˜P.;«?àí±çRZAňUr‰s9Ò‰¨2<äÕ6»A‡hw€ä a¿ç™y…•á˜eI7+)7nž›Eâa²}7€(D%#1ŽW”oÁ�ƒõ!E8¢±±!øê¥ŒK\¬—añi[dAÁ@ñ�¢fžƒØ‰›¸©TA ¡ ª+ÑFáV²&ØjådQì2ßc 3C8F±J®]œ“÷~ÀÉŸW·‘s€�«(àö‰e76’®"Áƒ-"¸ÎØ"QŠ àŒ2önð8“ïÀš�…ƒPTù;gIFùz¸y}‘Þ|í2]akn¸ýëȧ€†l]«Ä&ã S×òh0jiÄåüLŒå´Ä�¨Ì!"¬0‡˜â<ÇYXÉ ‹tnaŠ9ªad¨ËBg”îÙ«gùqó�kÓ®‰ƒ-ø™õ­¼\÷±Dš<þ­kWŒFø<ÍóêBæÍ×ofD/iÑϧ�‰#NÔÖ �¯’¡¯·»Ý”EV ÏÕ"i·Úèå.uëõ†dÝZ–A˜®úŠ¡¬O²7ƒ7€KÜYU°ø›Ø™z�„䌄m†„´(­nü|¸%Ÿg•#Ác€�º²À�(xúývTR»m�óY‹Áà1C¥ Êx´Ù’H ý¯¿õƒ~±úsíü˜¹Îï£Ï Yƒ€âö€›qãøQ˜ €- ÊíœÅU„°T¸Á°Xh6Û~�w.›‚’”ãШ8lÏ ï4ÎJ< ãz쟰 +„Œ¥eÎŒX"ýi[e‡GèÕWÑn°®¨�s†HBÙFxôº}Xs8‡ÈÑ�Å‚ˆ1�<¶,9x<�?¢áb2Å ¿J@{·Ò¡­²E/°%35AnIVœÂ5ã"¢:Až±åˆc‘„y\æ±`Ytžýµ^Þ‚‹€B}nNQ„×Þ{°Šª¹HD(.ú Ä #ÓùT¸ Ì z´IÔä䨸<^7öî-�ÊHî‚‹Foò8õyŸ— Ñ �}‚ý9<£ (C»Ø¥äçH5X~È[?‹ÂüêÍ«|Œ(‰Î:?;"­y?½·Kòîç(7³WO)È `‰k%“| XðãÃÝÅÏÓ ÔöÂÀ*Ïyu=‰ ,dÿ �=p¤“QÞï˜\�—bl¢Ý`¸yH>χdÆsrç<ÜUR€Õ ËBÀ4�Ku‚ÔB·Ý­#¹ ¢ÇŽ-Ÿ‰ien\«z^<\²Ý»Ðà”Á+©Ô0 ÚiæVΑÏïÃP1£·¾±UvD?9âX$óc ®Ø!¸1‚~/Z ¢ês%Š¡—–7 x  Ѷ+}ñ£ooçMuª+U(Føÿá†)•+‡Qܼð‘Kì‹�-¬îŽýê\� øÁ*ˆDÄ«ŽÈÒ0߽䶽âç÷¥z@'f[éÜœä ·DŽë+_ºHÍ?ùÈ4úúb¹DíÎ'ôÝ·¯C¡ís PLÞzí�i&à-&~Q•Øt�FDÁmƒ•9\pš„Š÷l ;‹røý26Àtiè2H˜c\÷X'pz}H~w+¥a h¶ÿ£ü”G©²O 'r?ŽÑòJžb‰ 'å^‚µ©çÏþ¥öÆÖÎŽHŸ‡×&Ýq€dŸÜ‘ Ÿ¬î ) žNI"!æT¶û‰æ”Ø[ªëB_]àÜZ£)üÑäØƒË}vo‡,W„îmÔéOß»C_{í‚R$ëñ Ɔǰ€HÇÀÁ…ˆÌq¸r 8áÎ�ËËœú9>} JˆG¬Ø€º9j9:óH>"ÒPbÛKfìŒÙ§•À·…ò@³ó‹´–ïÒÈX”-ÄID)îEì‹‹ÏGX;kzìòŽì#�$xÀ x=®\æ’Q>”¤L"BÏ[šÍ¦”¶¾ÿ` …a�ˆ/}·PBѹç²ÜW�ËðÊÝâÒéÇñ»Âá”·ä•üå{ŸBaôdUyÚ«­ÓîPr4«< \”¶…´¯RÛ»Å?X7ÈK°WÀçxîÑd·á%>ôѽ<%kô%®å²½)ûŸM|4¾„w¯ËE†m�xé'Eì2%Ä× ,Bˆ�w3 å÷öÕß@¥J %.×»Kkôpm—F'f¨åNÒätŒ&, k_w»µ:Û;»‘Ž‚É�}WDŽ8@’Öªž6±£†% �xÐûã Ü`¨.{÷þ*VlàJð,¸Cà†±Kt? >5b¼Áj‘Iã>Çñ»à–èv@0÷ÐEÈ̕խç2o»…2¥3 Î㘰{`𻪩ö¨(ËV2úº0�£àn›"Ñ8+�mz÷³ Ê—4– P8à•¨­¨&Ñ=K}¨ŸpQ£yÒ'èIAráêú­o(Æç  ÑÒZžÚ–Ÿ¼¢\ÏØ|È~+qm}³½›/à «:q€d_r?Å#À¦´5¶ø4”Ž�¸;36,.j”t‡¯‰6ȯÕ#~÷¾egM#Q ‚UÝi‰‡—÷¥wéâ\¯^«KéíI­ ^çy æ¤\®‰¿]*ÓÓõÿÏÞW@7v4Yß~OÌ,[f†L&É$ùò1Ã~›efü™™™™w÷,ó~Da¢É0›‰$YdYðúïªÓç¼ßÈÇ«Î鑬ñ€¥÷úvUݺ÷~7rž3éË XF]ñJ­¦Êyã¸vs ½™RQ7d£Æ�ö¾®4ú»Û‰„°�Ùc+¤Þ¹xÓsYfwÕÕ"ºzCt¢£ï0üA’¯IÓ@ã=AäúÍQ¼üúÛ+ªï§³ŒÈ¢- ¹Ë¿hJxÈ 1 ¢;#VÊ.»I$—k° ¢Œs‹š�Äl¡Œ…Ÿ7’ÊH<·ÃA™‰©¦‡%1ÌŠ…2”´O‘OLÎQÉ‚›œ;’€—27Î X9ùþ©Ü÷”itµÇ˜…äp€”àŸsq¹ �/·ÃÄÕ‘YLL/²¼ÊÉÃý°u©vyhÕbì‰ÿ/õ?®ÝšÂ[箓ä Æ–++¬\pàð :z~PëÍ™ïëasáÒ5¼þæ»KµZ­ `Àe´âÑ¢ÿz$Rj0ã.‡é"·¶pлKO\’O×ÓÓ ä�N³%DadË"5Ur>ÌÓ�ò $]h¨Ž‡Ç"‘�ϼøüžÒÀ¥; §¹xÈÒ½S÷º—b¡'åÆÇ>ðˆ¢²F4�ét#Ó=ˆþáãpø“™)㛯^Ä…«c;e9LÆRÄ “Ĩ»oš¸”¨U«ÈÏ/`abª‘S{ :Öïâ FÞg¯á«Ï¿ƒ[㳨TVyö'–hÃSÏ|'=ƒH4FYú}ÝåJ3³sõ¹…¥y��ŒXº›3b+Z‰©ON%϶ºzÃÙ­`BÍwÚ´¹obII= š)a[Y­ìû þswe oœA0Ú†£§»^Ίƒðy½bÂtÈå–UYë ®]¿9®X’yÝh/ß DZÑ’»!º�JF*êëi�S­OôC¥”Ô€ç¿Õ”˜žY¤™ ꪔW '¸XÓÓÅ Â!µŠöŒß©Z¹_¿©[çØîL–&›,R˜ŒøðèÑnqÉÙ@±RB¹°ˆ�ž‘ðÚÙXȯàÜÍ%ˆzCâ¢1Ž®L’$Úé‘È›²®¯,ã�X›¯Yo ²\ä ö9QÀe:Y”_«WWQ^. °°Äß÷Þ¨–WÍz�è��,ýT`l¶ˆ›³«Hw ãÀð õ³�î…­ÿ=”ÀÜÂ"æçfPÈÍK@j/äÑŠ=&v.|> àc~�ûø‘�L -Þs,••Õ ;ŠTP <蔫mwÅÎ-êÉÈè 90’¾ƒÜÎ…¤�Hf�µµÅé9�ôwT-aý—ˆý§ç|ˆ„|h#rDµ�Ê*`:hðÈç ÜóšžÏBɲÃír€ÕiM��ý—.Þ¨æ–<]騡Kµ° ÔWkà¥À}¥TF)¯dq ÅÅ,}MŸÝm= F­§Ë$„AKûв,d—˨¬ZðE;á´!ÝÖ�á¡ôte4sQlªxöìyùâ‹/Zsó 婹ܻ^pénÓì­he$i…)5-4öàô°@±PB¹TafÒTrž´¯(Ká> i2ø�è0Ñ I._ÀììÙ„ÙÓéétŒ3¡¥Rv4¬U@˜ìMÞ�I@BЋ H$âøØS¸rk._G6Û€/’†Û¨¡Y«bd| KËèïiCOGŠgQÖDy=eJêÞŸ  §i®ø•b +å •ù½‘M2okÞMM»ãZ(fóˆwevæP¢Á€þ]jœãí¦z_–òøC1tt÷#ÖF$æF$*·ì߬Vʘ�¼.¨+ì5‹š©u¹5„ØbmÝ °ô™†Hª {µVç²È^ Ú ÈWüüÅ›¤ÆËôàѱÎ7$·8$­{ÉÑ=܇î®v4¤´v²ùª­ˆÙ¬ ÷LŒ�OeS-‹š”,èqGöcx°�3áðÁJãø±C$]ÏÃt¯¾}ßzåm¢ëæœW@4:µ`�³¦!n,l6¨UWQ[!Y›Úm "ôc­Ñdõ¹lA�[ÞRJrÛ€ƒ@™ý[x÷â-|E±°~ó˯àw¾þÞ¹2�RÓËBº­}KúIRZkÞÛɉ knfJ^²�˜p@­he$÷˜f? ÃašjðÕc²…%¡ödH)9hZM.‹ %%Ï}˜ÓuÙlBÓ$Ýî ¤ÿ˜³† 2?ZÖÎMÚ³¾——©¾Z*^ÐÿáÁÔ…e{”Ëøâ²j€$ óàä‘AÄÂ~\¾>ÆV¾KeŽ  5ÎLƧ8ÃxìÄ0z:S÷Å&¤™�pÐÏŒµ…|™±ÅmN‡÷ú+«uL.ä‘/­p锲šÓY²,é„€w+ÛÎt€+–˜ÆŽùÅ<&fi„•¯M‰@(Œh<�îŽNÕ@"Îì[r=Y�¤E÷† Д—.¼ÝÈæò.õµ¥ Ê^ÑI­¸C´€ÄÐ’ñ'D\N‡Cà o�OöJV�½|u„æ&x3iÔäÂ'sS- É¿'°½±Z­q�ŸËKÂØ1Ò ýœlÇôAÚ,®×Äö×ÐXL3¼\†“|/ø„=6>…1¢ÝZ~œ8v”$=X¶~f.‹×Ï^c½°¡¾Ìûn †!ØÙq¨·]Î�O‰Ù¥ƒ‰÷w:(UkxëêrÅ  ä’‘ ÒÑÚ:’'¡”ÜÒþ�‡Ê¾®âòÍ –ÿÉ-—°�- òû2tð¸rå|íé$]·[:#e5©gTR�5¹´8¿º47áfÅjÈJg[ì�¿þ'¾{éoþ‹_A+î­Ò–_«ýÆ…^—ÓôªG4µaÓ^*S�¨#g(¹�ÍqP¹‡gLJ¥Êöû„è,`n.KΊ´ìh™�~N-éãA§˜ÒZËê²jºñÐ$0áçÁ@‡ôƒiÂñ$Œ@;zzz”ʲ´¥²%Ë~Ü›…ž6¿g¤âaîiÔ,¹òîÍ)k|.{_ R­7p}bµFíñ:8Еb]±¶®ŒËTH²™’'‡&€�Öµ›jýü å8y ×U²”/£!MDb)ô ãÑGOá£~šÀ–®£-´m6ªX-gQÉMËéñëM’U©78CÉFBþÑ{@ZÑÊH↠P !Ü$þiâ¡Ì£µÄB�3ÈdR4ùκWÅb…2Ö’¶Ú¬-«ºÿÊDÈðŠh¨ìëÝh8xþe§BÚs%»ƒ^'ø  {™Ã€ýv[0…E%ý5`„»Ñãt!àóâ¹×Î+é�<µýÔ£‡”ÍíÖƒ¼{VvòÄAÕëo^ª\™É¹¸:;aÜ-¨2:›¥ÅY�ßãbŸz'Ùt´!œJÐu²ñ’§`ÏêÿpßçÚ­IŽ·XËa9" 8|°VŠ­n ™ˆmù•a5ê°¬š�UÔ«ETJ9cdlƯ€ºiIifRÑQó�{¸ZÑ’”ÎJªBOÓ²ê‘u“¦•´®]EQJ€] =XXÈ‘“¡–+'·EöÂà߇Šõ%uoAl¶ÄDµ~v™d Ú)¡F÷�¤”¶ÿÈ®®ÒN­ÂàY@€K\zö„›ñÚSœ‚~Oø;�ȸñ‘§L>œUï×^<ËÔî' HÞKâÿÌé#Þ®Î6ùöù2›Í5+«•ì=Q'™Yj¨e˜¦aô¤£²«-n);`#Ñž±ö4.ç†ËWt�ÍÎgYsìÊÍ ŒNÎ3�¯=ÎÈBa B,ž Ûc ýèêhg/ý­ ÙÔ²RDmeÍzU½N¤‚2 ¥ƒzR—óBogrùüÕ ´¢$w+�uêyröóD\N¦Câa *óLNLƒ‰t¹h Ž,\ù^]­af–Ýç´¬ˆD£Ù ²˜P"À!6+ÓAËAI±‰�Úˆèçáž�¶ÞE*ˆúyÓ~�Ê[Mê›x!n÷<÷¶!–ñáS| XÊ121Ç„Š£zÈèì^¢’¤<,‚~¯P®€Ò*š®F�æBt*J€oŠ:DÓðû«ü`Ÿ ûÝNèû>W Þ``ã -âÂÕQ “Ô!ý/ÊL9Kõù|èéíÇÀÐ:8„Žö6ú¼øóÛŽ ÆúJq�W+y¬––ÔsÊN,eI])¬âÊ­ißÿøÕoù••p­hÉ]Dû„LȆðÒÍÉ.ˆ’Ù,¥·ݸ“Ssðú¼˜_ȱðãÂbž‡à 4ÒT< òÅÝq)7 &$›ÞדÁÒRù\M)·½ÑNÃô3P_˜Jú».¤¥K]šÁÕ€ÎF‚wy«à £­ÿž�&^xéUf8]¸2ÊTÙÓ'†øg½G�¯:Í£˜�ZͬK¨VVÀsF†Áê¦Ëiv5e€¬~ !`Ü�RaløpE“ë£SPÙ¹êƒJ“?“6:ÌÀ‰ÎžAœV}�ÎÎvRØ>ЗR›š j¬ceyõÕ2$½ïšéÖß�Fäü-1¿¸Ü˜]ȇš–5 ®¥³hÅ¢$!�‘¸MÓˆ)0qøµg¶„|Ƚ$Ê¥2n�L³Äûìì"ݸ0Ô;açÅ¥¥eôõf;{Û˜PÓ{ ¯3³‹d¼#�fiÑD½¹k¹ÜÒî—póÝÉÙÇ=Ãp£sà(ž¶,¼ôÊë˜Wà|N� 籃}ï[£÷Â$/}²"$ÊBm˜$âºß"¡Clæý§©zú·¨òÂk±�]æ\2‚×í‚:r©L’É1vâÖk(àhÔ*hÖVP¯1ˆ¬yoz:’âô±~\™)LL/Õ-KºG&æÑŠ;E‹µÕ #]Û"D§#2²’<�íØÎsää÷�çÞÂ(y¾Câæ­)ÊNh`<ÍvÚôçÁ…e9z RîXÒ\e&͵³+»73¡GÓÃ�÷É`Ò=t§{ ñh˜Ö¯¾p—oŒ¯Oö^ä O™ Ñm·Dߌãò�Ifa½ñîuŠT¥!tµ'Y?,�àè‘Ãøâ³ß…S§Ï ««÷€ˆ$=±:I97…ji‰û$°ƒ™�ªªH3HóêëZ­hÉËZÃô(„p™ þ’ä:{E3 ì�Ÿ˜áþÈÄä<›ÿ¸Ý¬EEÒ*Ü× ²¹ªÎñhb–eäw(iS7›±ëCÖ@ø±ÉƒŠôâû€‰ ýNâè± ÈJxþÕ P=�u ’è6˜Eµ¼ioüë*Ë}þµ xéÍK¸zk’®še!ð`Ý0‹DGŒÁv" jNrGš�UC­Z †5×o{/È×ýÊÍ)UŠ›•êz�P׫k¢$†ÎF†H~•�8IP�ÒZ·Ó„×ã„Äþ °\ÂÔô",É% b1 T««T⢠bóS,)Hÿ‹¾ÞNC-ÊBè@À@)ùsÝõ¡%TxH±Ik°\úѺ¸HVîíì;ˆH4ÁZ\Ë¥ g³ ¹5’#÷úl(ODCؘŠ”’ÊWÄ"#áç�@4 Hf˜½®(Ëcs%¬H?:zXâ]`çBè{]�H­²Ll­Ûäz¤ä )PÕP“õYEU´â=Ñê‘8´iêoÃa.uRâù*H0eÔåp`…D±X¢ÌC*Â�TÔ`ƒX>T†¢ ¢»« ’A¦I�´Y¯{£!“ oìíÉhpªAˆ-¯\0Q@Z’Á„½Ù…Ø;Ä ‹˜, p™ B÷L,]úòÞöCÇãq<úøSxç�—P*.ó|ƯýþK8<ÔÍð´¡³y–¼sÏ„~Ÿh¸õ7Áßo$GÀþl¹�þM¥våÆ˨еCå2¢ÑÎ.Y*?Ž¡££C}þ]<ø±ÓAàÁYI½J r·ƒ±ßèúlKUënþ#­hI @E§«‡i©”¥yóq:èQ`¿7G«UŒMΕÔÀ"™AQ, bqQf²ºJ7¡äx¡ã¾Ü¥)·²‚k[* lÓ,‰$ð£MŒA$ ±µ¯±gDÓ$³¶8Rg p¨CÌ}˜wÎ*Á›úÍËX\˜Ãåë×TŸbÊê“=M92€ž9¿Ï{Ǭ#ôq& Èû2¬œÀs ¯½s7Çf09³HýîIufÚÐÖž�3D{W;‹Rê¡B8ÄAM_pÍú*¤¦_ßEè“2Zê©e¶@¤$÷º¢¼B B@ÂLCp6"ö©‚”Rä²ÿ­±ù×Ô‰, )‡úû:ˆÔYûHJ°9àåçÐ ­zhñžšODÃ��[Äøä�Ó�µ|Ë–öEôÔì ÚÛ4Ã${ L¤~4Ýà ¥QÜÜ-Ü.>ŒT:�üÒ"b©Åèz£S³(Wª¸zss‹95ÐxœúDs_ÓŸ Sx{:Ó0ïS?­N@E’&"Ô_cŸ’Á�~tvuahh7)$‘iO#�0+ëA†a8¨ÙNÉûÉïS?§ÆC’€G¯V´€ä6Úo7€ © Ä­‡žt½ØÄþiÎÎÌ=º¸˜] ø=«ñxØMÄ•â�2 vv$I9˜J€,±R]Y½Ÿjšš'C"„$–Ͱl‹½o8` …ü4“°8%-=o™@­`%¾�&“I^}ƒ8rì$^ýu\¹rW®ÝÄ×_:«²†=ÐËì©L:Æ}>ÁYC`£-›¥vR¤©ôo¾ü.p¹=^ ut¡½£@{[š?k˲à8€ÀBT«qOVl2¢žQ@½7©|¡2À�V´€äN–ºÜ†!"ÄØ"V’ÃëÖ§ãVHi¹ëµÕŽåFݺtyÄReƒ|!N&¹*•ð©vv‰†íMF7ÔïN�õQ£»+ õ÷Úe±- Cð`‰î3ïÉМi=[bˆØaa¼ÏŸ—�zûú�J¥qâä)¼söœÊP^ÃÅëÓ˜[, �%—�¤·3MÞ'ÌncÛæ| ¥Ê e›Ô+ áA-dÀ’œ'ò¥*Nž<‰'ƒä UøýAšHg�0�é쪹©F­Lƒ‡Ôh¿ç�23L†€K ^vªù‘+š c2º·BG H<º´E¾#!µÌrµF_ Æ'X‡Jl…eYÆÜ|ÖRóV³Q7&&çxs‰ÆBTkçFüáX˜A„²)%—°(4(CN tR/ŠÙ[6ý[ÕZ�³Ÿ�eÆ!M°wCê¹P‰«ÀKMøû#ŸÏËö³Ý]�èéíÅ׿ù"®]¿�üØz-²Å:ƦsìàèÐ22•¶k¦Ì“_3M¤HÄ¢p8ÝðBèèè™3�‘¬ ¦ffÙ$ Ø`¶«‚¼èK¨U—Ñಖ¼g6ò�†“•`cÐçq¹+Õšü ‚Vèh‰C÷GL¯ÛRÀ!Ô…ÖØ1ßãWÑ ’T1ÔâîÌÜ"j«u\¾2B mª‹s9„jÉ*©D:p0x#Ò�x9S¸yk’e9´i}mI„²¦/—*Ô4e]10kËÀÞ ©™[&¸Ob¸6ôÞP©é‰Ó'©_¡†P_Æ•«7à …‘JÆgËåL—‹¾¸¼‡×‹d —ÓŽ—Ë�˜’@ €h$Œxà ÌÎ/Àë!pOAg‘Úd…{^¯—2Lý9ƒ�ßíqߦ…µ+ADû¼7µó!QIå~²%rÕ<4؉óWÇ]±HÀ§&Ý=-iÉ{£ê4ͰÊH�Øl~¯ë.3$­ÐróÄÿgšèµëc8|°‹K96&šŸÏr= êMK{ºØ=^�ê*›3é™ ÝËÝüÿ�²Ÿ×CÿMùµwsºq_ßFƒŠ†ƒ�d3}&Á`ÇŽ`J®%%õøíž-)†^-v>lÔVx†ä>ƒ–Ôt7•rD÷TÛŒ :Z“í+æ#É x ºqô†C'Ú{Î�´z&ì´(Õ£¤ÒT._ÂäÔ1ËÙàfƒ2Zôwe5ô3ãæ¡ ©÷°æ T¬*P/è× b¿+2°Ž—´ªEÒÖâ××äÞxô@7Ò‰¨©˜ˆO~þã�¬M '¸à,€…} $- ù¶�Ä•‘¹ ”2SÕÍ`‡i°töj­Ñº26Þ3a¯÷Šî�(áGöƒ¿v}c3(ʨjõd¤Ø$CŽþ<5׉©EÍuÊ€TbÑMffÙ�õ†ö-øõVM‹æFØù°©é¾RÊuoºÜn´§x扣 L"H%"žzôÐÅ ˜°‚VìßÒÆ\¶Ð¡©´©k¢¤%d˜Æ&7¶Öœ eÄt!.[õ}Tò¢¡72Ç"×Ef£ †\Ô¤ßhhßÒú"a0‘Rqâa¯ÝèÌ£©û"pvb¸Pöu¡³’•�d":Ün.ÉâÀ`—˜]\Æ¥kã†RL~\i“}D))�˜@+ö/�|÷'ÃßúO¿}@éçÄ!†à�,=Œ¸R­¨*€¨­Ö¸Ôň~‹í|5£‹}Û©7�GØ~£A€DÓõä9BFÒ+>/Oaãá©ÇGô 5 $ʈH-‡ÒX¥¬dÃ2;ºWJ’0 (:169O}¿ø£Ç?¢Ì¿ÞTÄ’Å}œ•´J[ßþ§þµ«iY�K)ý@£i¡^o²t4Í”ì֊Ƚå(øµ]g’ukd gÏ]Sгgñ¢’¿teϽø6.\ºÉïs½VßThI‹üF˜¥•LD´ÚÚâû„0!õÒ�wô�eŸ‚ˆ%›Ô\g±šµ f#€S˿ТèhK  R¶k$ã‘3Çöý€á}Ú�jIˆ]ýˆåëèæ–¥Ö2üÜNçžáˆ ûô¤/úݪ͵‚Å¥eÊNX‘WYøÒð"--f—7ýŽÄ£!¤Ó1f�qYË¥û#û)¬x5k\êÚ�A½8lÔ˜ò»Q;=œl¨e:”N"ï*׺;3‰Oöv¥? „Vì? éLE‘Ž…"ª®ÞKh”�P„|ý8v> nÍàÅ›¢vlË+˜ÏhñóBy¤ZL¿§AÅZ»&s‘RrϤXZÁ²”ññYÝ#‘›zߨÁ®N‰ä�B‹û#û.¬š.q9Pö]h¹š`×l­ gýÄÞ°Œ{ÛqâP? øû¢éø'<¶%SZ=’ÁÎr…JZ5Ú;yó`›¶x\¥ylп¥µ|@A3äg®/`ZÿˆŒ…<*«5.Ãñ©‰þ­´ZohÓ!2 2™¾è$C�“¿G@ÀÔd«)õ�ô΃‰ÎRˆáµaP£?Dzàìµáæf{Äa²2­s߉°{&† û/$¸©I“ìDý¥Çu‰}p£Ð¶Ñ¼8´—ÎpÝ)¼øúEQ«×O©~Ü·)–¢À«æ÷I]±$�I\› )qÁ�äiÖ ð"ò‘â/mÀ;Òë †0éKU–Â4Lf I Úms�UXú9�ƒ!�àï“Ë¥ªåp9Ìš%QXY•ôw–*«–¶ &AòøŠX€ M|‡ü€;j8¢>?@%a¹¡,„Ô}I RÁTß`п�Ùvz~D-èèÜ_ÍuÐ�¬ŽF­ªû#«TæÚhIK7ØÝ”éÞöou´+:ðc‡qáÊ(}kOÇ¿9:ÓÔ‚Ž_0…V샌¤;ÿÛîˆÊH¼jQ³„´µD Ïžyô¦ç²xåí+HÄB‡Õ=725»tEƒÉÌ>�LiÉþÿWèiKIÀ€6nµLÞœ¥Ü¹Í�2·ÛÉ¢‚m阎,_¤ííI«½=&‚a¿ Í’X'‡ÓRÞÚêk¿frsÂÂ'ÔsNÇ¥]6â }‹™hôoiÏí:®�Îà•·®b|j‘nªZÝÖ¢ÌL¿»5tâ¤Gê‡�A—¥eñkû5ì¹I�ûkfÄj² ãj%GÙ» ®7´3´o ¯»eñŸûØc¬!÷ö…›A¥Éuâë/ž}ûÖøìŠÇí²H?®ÿ‰7_¬ ;LÃA �-!Ķo€ä�AÒ!ZÚœ3’`ЃRÙ‹d2†GN ð5L`AYˆ.×R®��ï%"èf»C¿i&Ó™u= ¡ D<À±ƒÝŸ^ÄëgoâÊ�)bRñ�àÈÌ"‚Ü+ ñ�–Ã[Cî2J1ÝÀ$�Ò–ŽÓp)ÙýÚfLû5„¡–IOö¥®V­Zä²–ÕØ0åW»hštp{ß9$¢?Ô‹Å\á ï ë#§¿ë—÷…+j¾d@ Iö�ÄÕæs˜Nì´‘[ZB^ä¶uþÃãu1€Kz¤ �²®ñŸzä ’ñ zͽ¦o°FÓðhà’{AA'â‘ º;â¸rs £óX.®`©XÁR¡Œˆß«›ô.„ü–Ö7Mñ 3•ÛèÍN.m@Ñ´ä~¯jÙJÀ†c_ •´äÆkÔ¡Þˆåt:5éñÞ×cOG =m8wiD¨žç£'�ôÿÐWŸgÀ…Ý×to…ØJŠ­”xZmŽÿÇ0Œ^j2+O u¥Ð“Il«X#ýÛaêGt$01µÅCG<âòSggédb‡¯>ê-d—K™˜g@›ZÄÔló yÝŒw0ˆ$"~$£!zÏ„;þ%�þ|ØB·³#�T*ŠÁþN.ßéaDìÛ0Ý€+ªV„3“‡=tÊ ­ji¥ì$ê+Ëž¡L$¨\—*õ2¿üÜ[T6¦âüï|õõ521û?L¢ç‰eIS”Q’ç0 A,(mÉ*¶tD@×\m¡õDP¢‘º»SêD*~ &uÔk «'Ná;?sßöñÓøèÓGñø#CH¥"XÑ@sKÌ¥[Ó™ZÀÌâ2rE”VV¹³�a׫Ù–'Ø3™$ý¿yYM‹zMô{-éaÚÔßýp–Ô`_¡æ:‰3ê’–\W¦n7Øé:£ûº¥uHÈÇõQy‹®Áäéãƒu:G+ÚÒVP@U â¥M*àõ “ #òAˆ�—\lš®ÐM; º$ŦÞôà0Ü÷80ÔÉ âPÀ÷À)«<Õo|#?ØC©:+óÞŸS}”xóݺY_üe)aouÎL‚zˆ“z*®mè3BBšM¶¼AzÏ„@0ä§ç $Z[ ­�Z"Å ñÐg$Ô©–iø�åPî7Xœ•ïUC³'Ýj9©¬µ¡ƒãÑándóETߪ Õ¿{DMÀŸ|å­Ëy/È£»"Ll]P9ë§…!ü7ú:înÛÔ$47¸õtµÞ”¡³Úùy<‚b\!™#ÓG&“ ÷µ]wЦÿ+N%¸«#A�yÊXtIPBIîÓŒ @?§!Ô× òráaH …zÓÀ(„°çiHE‹EBLñí̤x¡ü!g:t„íu!°�CS~ 7(øQ<¼ .)-Ê@Ø´j¥¸€†6®º?eê¦f8ZúÐgÀë%æSÉ7jñLž%tí“=¯×é0cSóKêðå°dÆŠ|(šx- ¡“31|žt:Í6„E‚> u§‘Œ±ÙÐì/@òÍKMt:%SC�/®ƒz˜âÛÙ‘¤º>Í«ì¦á9 †ÆÚçRrɯ³-†Žö8‰"2øY–ÅÃ’B€³+)Aï+±Ï¨§B ¼é÷’(½Í&³±(8›ëîng`ŽECôZC¹ R¯DÐ{ �VYKhC+Ã@o¬ôúà ºlGe-ÎHj•Ü}g#t�kÒ’’¯ch#»P(HÙȦ2jºîÛR1~”�Iå_Ò©\Îåb™Lß ŠhÅÞ/mËU§aˆãHPY&ä÷ÒÚŠžˆn ¸=Nг`ÀË�ßX,Èioƒ‡>íÊ2�[ÛK?§Ÿc°7ƒd<„¶T„ûÊn33K´Ùs3ÞítP˜n ½—ƒJ ”uÐI‘@�ÀC %_öù½!´¨Z€Ð‹‚<Û^@j_wÓû¶F�Ü©2€4ïsfD&—ðp®€ ¬–¿6„�p¸ °é�VÊæŸ8u€LÖ ¥ºp@õK¢ H‚ùB9 à+º¿r[«µ¿{ôgËWïæIDÑçp˜¦aZ‹Ê¹ì#]Ê‚ZÌÌB_;_¤™7­ »�²›ýÖpé $´*ý|ÜÏ9v ›³¯×Å7ÝÍ‘½ä^Iˆ‡Í-5�°#±°¨È_¯ÏÃý‘X4¨Ù�W„ª} ´Â~ÓЄ¡U€ëàG‡xÈf 4š54V+$ÌHMöu  ”ËU\¿1Ň”x,¨ í $S)hÝASÊH†ú2(¯TéžO©ìcßzåܲüõð€›ûlú]ph €¹ìР@@IƒlQk–-é¯KºÏdí i3„H°p¡!èç´ÜDƒ�ÁCŸ éäN�œ©"Û¿î:ÿw6Ô“¼ f¯üóh’‡` 1M­�n·À‘á.t´ÅÐžŠ²ÖÕèø<¤\á>‰ßKTá¨ÿ´ÑÒ�M“{%¹Ò¿MˆB UÎj !Bh…Ý¡G©3¶à­Ù¥®‡ÌÈ‹2j°×*ËÔd_×uE"¡S3KÔ›$ h~ŒE#Ü7Ÿ˜C8 }�¯¹Í•µU6‚X8@„�Auð™ý½¯½^PMþ€´“³¼I>üáÐ LÀ ŸðÝǪ·ì ‡ÃÞp(è®ÕV]RRõqu4›Ë¤e•5ˆ¼àmå $€A)eP>¤ fS§=ñ a0ÐÍtž q¨çä_@ß³ÛBÈó]Þ"Â-­eÅ` UŠÁ5äjuN§Éþ�Ÿæìä+ßz×nNѦO‹›ñéx±�ãb�ÔHˆ‘Z%‘–º©ñn˜¦_{M´²}¢Ñ`µð0ª fd•ß:ËÄp¿AB×�)l8;aIµ!Ͼ{U�zt_o†{p›—Å&çÎÁ>* ‡ÍÁžö'Tõ£ôK¿ýüe]Æ Hxé!V 4†Ðtè„ÓéJƒ�nÅnk�Å¢þ¶TÂÝÛÓp˜¦³X*ÑáU†qz)›Ë^¸xenjfv~uµ–Ñ%¯·UEbµººúÀ€$ ß0 K ÕÉ€Ï .k‰Í�ì›|²7�¨¤Å'™ö¶8ü>÷® müD¶´ZªÅKMLn: άX@>Ÿ�~Fí!BÍunLê줇ƒg8púÄ Ñ¨ÉÏš€ƒ2‹„”HD‚ÊLœNž¡þ�îJ ¥àõz‚¦i8ï2WÔ ©A¦Ý€×ñp°·$/Ëâ ‡‹¿f;])ïþ',ÉœùÅeê�ð«RÚm5D(Î]¼�ÇNÒêî-ÿt˜|èÂc'‡=ªÔõ‘Bi¥úûßxã¼6Ã:¤?¬WÂáE€£Nè¡¥îé´ŽLWg&­ñ¡€ßçìïíÂÓgNãÐ�AÞsjì–j—Û˜™]üöï}¥óüÅ«%µÏ -årrdtbB•º'« Hb¼¦Áˆ‚NÒ´ alnðP¤ûÄÙG[:J,"©2AÊn*eélƒ?,ÝÑÏÙ¿CƒD“¾&`ÔzCŽ5™‹þ³$ š–àáÊ™…¼ ‡ƒˆ‡¢¢3AÃŒ$Iz_÷-dI@Å“¬§EÔß Zú‚�¸×_Ô*o ½$õH¤ÎHÐõ ‡@‡K@¸¼AøÂmtø¡Ò 5²©•a2%˜}I$rˆ�E½J¾v§g–P¾Ýÿ†oee•5çôìÒvô‰�èûàG?£€+ðÂë/jé°  ‚‡#<@ž�0 c ™H fÚÓ CBím)ôõt2xœ:yméÔ)$²¹e,/ õÙ(Ë�Z¨³³= Þx÷Âå_TN>qú$þýù?;Jÿ5ôÈ(VÒçr˜FO{œt¤Ös1èÅω;N›&SU»:S4áJ¥-áp»É×]3¯\š‘e°(e&´´Ú)�‡óN�&úûÀ@<·Tl\½5S,ŠªÆÙ$ÉŸˆÙûÞí¼š32X7«Þ´Ô…ÖƒÁ�NÊòLƒv‰VÜ3lú¯a?7Ý঻l>%B0˜ÀéöÃå Áé Âáò¡¾ö0.p¦ÂR£Å~?ù| #£³äÌIs§þ ½N¬@ª(l‹<ÒR®H@æTŒÎŒ�O-\° ü‰ÀÓN«jGæø±#ÇN=xäàp¿ïä±CøàÓ�ã ŸýN¨ç‘hw>tÓ!•çI”+eä—Ù6Ù—ˆE?uæôõßùƒßº‰zÿúŸþMü£þvL"% ¦?0±Z¯�¦¥5£Ö¯íC›§©)²Z‚žú"Ü1Í"vƒœû:£ çN  =wj`zñsè×p§Ð ÂËëõa ·]™”SQ) Ëp¬�{c¡\åÌ"à[Ÿl‰`ºoÃâÁG‚Í«Ö3$Öê•HK-}Ë4«ô¢mÁû1· ÓI‹ÅHÁáKÎ ožh¯n?$#é>4-…å2fæ²´‘3°Ü)D¤”dÿÌ*[Ô�=4Ø 5[BûŽW‰;~@õNâHn=$3&1g P{B íÉ3§Ÿ~ôÄá£Ï<õ˜óãyÏ~áSøè‡žD"£ûú}Kˆ‰DLÏ80ÜÏ{X½Ñ8dYÖ¿ûïÿáß÷ç¾óÇ…åÆ.¿¼#¥-@‡N·¢–”áFâ¬aCƒsÚÞݨ7L�p$@s#rÀPƒ€]Ê,øsÒû°Î"ì>‡ÝO´Ë_:¹OÀ2áõšªé~€nÄ ò;™Ÿ_Ì+"W5zydý�I¤¢¡û~_$Àˆ«M`f!ww/aXÙ?„Z­ lÖ«€Á¤¹J·5ÆXÊåqåÚ-,e³,I¤KS†¢ŽËhÀe-äëÖ;çn:òË%¨lùýtøh£¢aÛmÿ$çá¡.ÌÌgé~ë9v°÷“ßxéÝØÛ‘ð¤¦òº;”tÇç>ýÑ�;<<|ìÈ0—±Tƒ�ÊÕë&4 ô⃕‹e¼ôʘ�[0Mã߯?sýØÏþéÿû»¿úßwHL�nÕuÈ«]Î6,…�™Ñß“ˆ‡(+y@~öÿK´';ôëúD/ôÍA¯YÐÀý}kØ\ë�x4ˆ<~D�³é_Ê.Ž�Nl4ay,¤VÐÝç>ɽAÉÍŒ¢¬JÀðß…=#ÀRézÊyÍ �~¢_´…©¿ŸB>ÄÆVЈÜ#©Á�ÂñЙY Eœ=w ‹K9¨:(¨Y;?¿€…Ù±²¼‡° R34ï}ÑFåfÇÒ&³¼<šÂ¾•÷iwgDA×3ɳ/ «Ç’qÒ³kG"G(A­)W¯]¿Uo4›Î÷ëÙIÉ z c1Ðð¶Ý¿d*ð±Et¨§+Õ¦Hyåùné2|—âËîâìÄC{*€Oè'±�Ïöã‡ÿèÏýèg>ùѤˆÅ¹Õ PiŒõÐÈÑ•]¥‰©Ù«ª§u @q»›í–ÇI‰€Ã0=N§©}.6´‰h¥fk¡½-ÆÃˆ;Ô8× ¡›œ¹O£ÁÁ„ÎD Aä¶LÆfq le=ÐÝó§îÙÌ'>ü9?rã|q¹„ëãó¸|k‹¹"1ÞL´ÜŠ›hÂ,ÎØÕ‘F_O›jLvñðØï^ÓÒ:�N­rkrs•kÿxÃ4=°ö×0`ç½7ƒaÃ]ê>‰Å ªÖ‡˜`°v“ü8ªJ!Ÿùćñ“?ñ#ÖgΔé03»T ^Þ½2}*Ÿ“Só\"Ëæ d§ ÿ™­ºÎŸ>}ú;3§Ž~¢+“< «'i½IÓ_ ìž0uöñQß àxJ±~èûž}ü¿ç‹Ÿÿð3gbôþCnÇ µ�ƒÃøÐž@ow'"J�F#¨‡�Û $nýÁP˜MËòðL…–zß�Ñéi…ý´ ’ 1�ˆ1°ìRg¼l£›aE¯ídÏf¸¯CüñŸú"~ìû?Ãå†Z£‰b¥Š¹l³ÙeÌ)0©Y“zºÚБIRæA ÂÿßGú¸lø�—ϱ@$^ ÂiÁtÙ4VÃA™ ¿î èçnPèkú>ÓK� BvÏÀ;|xTrõÌ$-ý3;ö%!áð¡!ïOýäG;2ír)_ÄB¾tOÍ;ËbæÎ�¿®»ïbnn‰gLjõ:¶: !ØÇ'Â&w�ø©£Tåჺ$õŒIç.ÀMø€/ø¸aÕH?xèg~ü>õÙO}ôŠ]åQ2'Ø–Ð%iÞ/ˆ¨—2ÐR£Þ¨éÉù¶í.muéÁ˜ŒèW5úU–r“êoG*JfLëEE}êñÂÅžá>~¾“¡{$:3±ËXïYÆì‰7k'|w£-�ÀèÄœT§;HМI:ƒ>)³´þÁá´¥âœå¬Qægª¦jD20ê7LBHà a:Á›£Á v ËM›¨ÎD ð`ž3øžæ¼Åà³6#±ìr˜|oïD`O†þùøç7lÐÜ�”èP(lŒNÌŠ‹/Á²ˆ–~o•j`,JjUØ<Íth4,>0jväV…΂ÊP=D•³œ:Üg”2„G™dÕ$kæcF‹Öð”úÝ 9Nà¦ØWƒg{äàÿw }âc >râ°P™É_¿v¬T«(—+Äšå篽qöÊùKW¯èù›y‹Û$À#z8&jFR5Ù»ÕFæ"/òj§FïzB˪Ó#Í�p“9äÆÙN�öõ ˆ ,H‹^³ í¾Çƒ­ÓkÐîïÄ�¡^ÌÍgÅr¾)A xj”ò�‹GØ«%•ˆÂM7«fq‘´ÄÄôFÆg(™èèì…鉭鋨} ý¨³–ÆŠiÿ¾lÚ¿OaöÔ·á°�DSŒõk{Ó�ÚtèÒ–¹Ç›í›/‰XÂÀÅË×°œËÑõEĺ÷ïé["Ίëµ&7wÁb¦Ž-¥&O¼Ã©MñTÞ©X�1µG5&¦K–”~í"°Ë7€æ•±º<®÷Ñv&V†‚�ûÐSÇ?òÌS}Ï~Û§Ìá�^øý[³’l�'¬T¹§ÊïùØøžéu(©Ke<ÍYîÅW_º92Þ0M€«�Dný× ¥ßx§!„�äÕ¢&ûºA„‚~H°Ìyƒ´ Dv0lïwÝÿà©ô]8Ó]br@X5åPü?ˆÿþ ¿ ŤÁb¾ˆk£ÓìnCoÊ[^[Ÿ �à†äSª†ü�ßÅWŸ†Ó‡c�<·—¥äìº�°ý7¤Î<èQÿžÔ}‚^Ó¯»ìþg%�{m¾»¼QoknA—ñömÐ,B_7>ðÁgðͯ~Kù%V­Î¤¢¬¾p· 9•l–û*ÜÃ[XÈáÌÇhâzë>wI†{^xøà`ô±GŽ‹îÎŽ-±Ž rÅôdF–äóË )¢`¿øÊ›˜™™c Íåòt�V[±µ ß `€µ]s$�.èæ¡t'�F(“0„X·l<-ú¡™Îêu#ÜQÏu]ÒrÓã¢ÙRú¦“RÕ«ðgÿð÷àͳ—ðªbì]™Æ�‰yLÎ.áÑãCäg¿f „.Їz1¿�Çù«£øús¯àüå[Hv à‰ÇN¡»³}mùX¼G{ÊЯ ¢Áj“þ=Z†°ûV <ͪ��°»¸ë,‹Ž] 4̉³iÎû9¤d©ø'NŸ@.—ÃË/¼€©ùE”«5ô¶Ç¹GEabjÁeòÙajLNÎÓ÷2SLˆ5½Ê|6ågD^;Ñ0³?áW”š‚ÿPw[èÅ7.]ºzs²¨÷Á€]e™�ׯ74séŠ^ÕM*�<®�„�võsŸùTß§>þÁO†ƒÞ>§Ã‡áàðà¦A„Þ·ù…EI4íÂòrY�ÈÂRn9£æ‚\¹ü2ïsËù<—›Ô«" ¡Ò·Ùl4üzo_PØ®‰ÐÍ¡¸FŒº�ŽŒ¢ýöD‚~Ñ!LÐ:3=ƒA™ƒQ:¥×vÄ—@LK“P†žï"�']ÎÒý ‡vü}~Ÿ íÉ(ÏŒ¨:0”¯5.]Ÿ@›zm §Ý~/õhN‡2Á©Ù,ÞXn6„ö0êlOb°·Ýy` ³ëôñ¡¾¾î¶ˆÚ<-Ur3Õ½P+#¥<­Ù]úAŸÒ@Ñ@RY§”©åMNê¿Ó£Âú›í/ýSäÇ¿ó©ÇŽg:3mbh°ÝÝ�<(¸™ Px÷ü¥ìó_ùòJqú–7?v Þ•%ér^W0f¬Tʨæ–œè #èêM‰º(–+¥o½øêY•ÁÔ”4xÖ·«GbúMõ«ò”i™x$€¶x˜ÒÚ ô'´¹˜p]?™Œ@lŒ’L!î÷ßÕʽZ–Ä MßÅΆxŸ!87ºyó4m:.eºÏßãò'�Hw`e¥Lr¸5>‹É™Eôv¥ÑžŠÝ&:Ô?©5,\[�“3 ÖÄäŒhZM•Zg¸>Kqg1í×aÁn56&[K#�ŒÒ}á=q¸¯]´†’ñȰºœN3£†©½êß[Qe·eÝ#Ð=¸ÔîÓYPéá§5Êèèì,ÿ—÷O?ø}Ï~êÙL{2¨@…ËJ~m/±‰�éÕ¯¿yvæ¿ù+Ù„(µ÷F½f:ì3SÑ€–eÌd‹˜½GµˆÃ�1´…Ü9-8êdK«¸tsbê¾ú­«–e­êÌìúvɪN}œR@rÆãr¨9£ûdø=®uÞ“¦nŽQô÷¶moÃd¶ûÏ„ ]]‘ÔX§-½¶Ãe-aõÝÞ1´›×N?8L§> ;è÷×:ý¬Úê D˜«¿°°øßÿå_}ö©ÇŽ}RÝ{Ъ’ÞzõÕÆ+_ýýRÆ/:†»Ò.bršš$¤\1>>C6p°+�¾ö8´Ôg~WÆç‘+­Ì½}îÒe)± m‹GXÛ$R×;æÁHÀwR•¶¢ä/ûá[�h[^*+ñ${,B$¼ýŒ-Í΂ž`׃‡H„“7${C–°i¸.ð)ޥ͓ËD|xÏÉ›ËIüJ(D8ÅØÄ&§fè¦äŸ) )€â½A@’ŒH*\�ôÊ•k#˜›[=]èVë¾ñ[西¶›ëRÿŒ {Ó•KŒppE¥”ü=6¸Jýó¹4mp!0%@’”(è}qx4p'ú}œ- ,agˆ œ†û4toÑ å°' aíím"ŽT•�N!›Ë{Ê•AÙ‡”RolbÍÕ£2ÊH¤r ¥ùA4ang ìÑ>=³Àv½ÑHˆK7Њ 6]A ÞhØÏeà#zðÌãGð�ÇŽ8Ÿ-ASw˜Þ+ì͆=—Á=µ@nXg!&¯µÙ€€mkOØÓÓb®€[#ã$y Pï„=õdYlškëÇ.��\Ì/ÄÄôÛÿ*& ‚†ÒSò:å³%X]Þjè¡G-]¦´)ÛV²¡�Âc3¿ —áÖ`Óàß³ÁEÿa€�¤¹Bßso \o؃;….åíëŒ$à÷!•Lðµg±p©éŒ'“.úœfgŒB±‚²Î<„ÁÄZæ¦P%1K1¿$ùï¨ëO�  eÔn7)L° —–ÙY´X^A8à*‚”,Y¿¡Ò—a°­½!“ŠQ–e\RjEBC9´ºU),®º½+«µÕk·¦´ "BšÁš…-%Óóu'´ÆWóäÉÍÿñŸþ凞ýÜǾð¹ÛÁž«UÊõÉ·Ÿ‡ £-Î÷÷5-)Y-òCh0¿61�7®Œ7G¦.¼}éæÿR5½ßpU—ïäv‰“Çç9#q ©läˆWýx‘Š×Í8`™jœË´'ÈqÛ�©ô) ö¬ˆsǧÕuùÇn>›^} wÚ2Î=¿K`ܱ¤B“ªT›D¥R‚Ë4 Qqþʺ3I´§cw8U 6 :’”8¿'Ã} ¸ë�Ûÿ�†ô4¼ÝÿÑ›³ž¦_«BìðÚÚ^VÝ—HLHЬØÓùX�Ò]ï)%ÓBµÎÛz@Òþ™t‰Í.q ìçðx=hO'A‚ªÏF bÃaøA”ŠTÊePÖ[®¬2 P–¢ïE„?–%…¥@e¥ZCue•ƒ¸P&C3SXU¯Ó)š>3Ê^,¶ý­m¦1Çû^¿Äó(:!KÄÓk6·\2tóÝ §KýOÑt:Œ:˜.üôOüh×?ø[þ{NŸ8xÆé4�ÛDʑٙÉ+7ϾîWârj•ÀަDs©Òh”+# µ_C̰{ùÂ.ŒÌ\yíÒèo*«Ï !nê>�Ünõ߀FÜGMCœVÍ´6·Ó‰hÈÇ=�†m °+b’.ˆmchÐg#Ú¨Šå�˜=iÉͲé0œv)kì#L¤²Ö¤Ê.V«e(ç8LÎ.òMyd¸‡@{ÍûÃl¹DÑX”)Ã7G§�͈âÉ*°ú½Ù$ ˜úѸýt¯•–nªÛ€i÷CèkÓe7õ…æ†6�Ò ä|n!Ç€™Œ…áZ7�èÞ–éÕh ˆ­ k‰±XéTþ@¡HÊ/ªÔ…JµFëÅ•Vj (M¶�Fc=À=¥Ló&ry¹HN�4=õSJå &&ç™ùåtðþ�í Êò}ˆE‚nõ­½òÖåy)eˆ³›Åz@÷C\G�)üÝ¿ñŸþ±xö»ú{2Ê”¶ó _^Z¸õæË/ù'æs’MнT} ÷µ°´"¯ÎYëF¾á(eŒ�ö¸é0 ’W"Á;7&ç^»4öµ|iåUoêþÏŽXíöxÀquv¹�ŽõGÚs‹Ðp#bkĦ aD²äôùÜØêÐý舮¯:èqÍôúΆ€îƒØÚU�ü5/a7Ô7"ù’ˆG±¸”ÇÜü‚537/³d—K¬�z` N—ã6ÿ’ ß_ DƒcBüï+z"m [%1rû#$ìÌöô»^ 4Ö �.‰ÙÒ-šb\Öv¸{xRëcÝiöõ³×x“ìi_W©Ë·]ÎðZñž˜�_à�8Óá@{¦�DÙÛ¤ À Pª"W(+0aÃ0¨2ùmžFÂfx E ‹9õ÷å�Ë17—%ÅZ.q)ô=ÎôbÛhó¡mÀ�¨ RYɤ:˜55€ôj@‰+Vü{¾ëÛÓï¯ÿ¹�}ðÉSg”`ª{û÷ §7`ݺvmfzz:=Ÿ+¾ä¢=SuLÈŽÃή�áN áVì†×å^ŸWÇg+¿öÍw.¿pîæ«J+í€×ŒodH˱A’!=Út˜†Û4 ÜÜ´57ñ¦Q-”äQHJ^n“|† "ö¿A;®Ÿ¥–ùÿ)³æZÓ¤MÜ 4Åú™O~¥Rɘž™—ªOÂ`ò›_~™t Ÿúð©µ7›$Í. rª¡Å.äsYŒ��É—_{[P†“iOoaoH?BÞpL ¬Nð½ªødm �Ðï£îµ4VèÑ΄tÅÎFò¸xeŒ&ü×FÒƒ¸_½"i?N´âÎ�Ã}<¡î t’ë ý¨×^}åR‰dRøð™/VøÞOÅBï{% Pn#Èåôô™S^ºÏvCøq‡�¡Pà…·Î^ø%%ï³,+ »±ûæ›íí0Öaƒ¬uÛÔ‡å ’X8ÀÈ›:5k‹N[tÁÚFU8ø9….kmå—Aáö­¦¼Ú*¾†×¶1ô\ ]\ôóR¿²Yç÷£i5‘ÔÖ§¤¼LwôÞk0!°¡›�o²B¡€Õ†…L{‰XtçèÑÐï'£Æšým:`°eWLÝŒo®ÂôÆJô`>W–o½sNÌ/æxN ¯«�N¦˜1‘º—eŽB¸¬– ã:‚®/¥€ ÃáäÙ�l6Ǿ“Kæ)AŠÑåU Ã+ D\Š쨔«‚K†i�±½HÖ…2U­ûUE._´Ëݦa_ƒsäР;à÷ó}¶«Cˆ¹¯|ýùÿû³ì/ÕÏÖW¯k=-¹Ó@Ò¡…ÇÒ†!R HR>õ �b!ÿf€Dû¢;‘LF·ÌÔJO¬Ó£._INõ4C ºÔµ}å,S7�y@? Û(Š–Ão—l¶ËÏ:Á@oœ.'æççP.—ÁþÖ^7Ï–4-I®rl~EA72™y½..Jež–·` Oý=¤´3ñ°€÷VÚ·ô{iØe/gnŸÑpXL��âÚ�QÎHŽì¹û¡EÜÈ™öç& €Ü@VÙ Êâñœ.7*Õ:Ï…ÌÍ/¡ÞlÀ4t z}µÛ’2D3)¤Ó"“€¤ºRcJ|>_â’—€D©´ŠÅÅ<1É4^ûÐ ¶ Æï~ý *s›N¥f?õÙoó}ñ³}$�Œ;T`„TjÀðíßÿ3cÙlþ §fi½®; $½zü?l¢]q¿ÛT6bÆB>$cA¸�ŽM)‰í—¸à‰xh£¿Æ7�mx¶)þÿ’–TÏÅv•µl�ð®•<15‹Ù\ $2$|H8IÚÓµ¯ÊYïà¹WÏóaª³£­öø“Ï4ž:óX�¢Ø#1=3wëgþè_zU :hªòÛþ`Mƒ}‡{$«z¹x!!¦AÃl› \f¨7X>z3ÃEk4´ìÒ•€N“¡�ÅR¯›[ÚÑàa×è%8œÁµŠ´†ÛÞ€híTH Ó–ÆÀÀ ÆFG1:6Ž|¡Ä§­ƒ�Ü�œ!†ƒ\#z% ’±×û¯üúoó â‰ã‡w�Ó¡Ðï>¢‰ÿ`˜æ”¼TFá!̓ƒ�t}l€½eQÙLƒFÀfàÁD+6Ƹ¡F±òç X² äÒÓÄÈ-4´8°Ñ }E-À0êÜ,–*Ü�Î/—x¯Bp™kjz�@†K\ßxùžý"rËe¤ÓiÙÞ=l?v<¡®±>¥”*û™üÇÿâ?~ówþàkmzîå€çm Ý�¡5ê+Ü+‘ðJ}«¹Èï|“%":1PšIig6W">ø††õ\¥ÇzbÝ¥¥X¬5@³u <¤®�Ã/† &·Ï4˜Øé •ãcGcaaùåe©ÜÑhÐ 7F§ÈÄÀò)ÐA7e-4õNY Ÿ ß< ÿç—~�NhT’Ø3¶¹´q,dK˜],¢XXf·HåIAYØÆ‘YÒ�ÊŠ6ýhÅÆƒú Ää"[‡!ðû¹j•"g[–%Õj@‡�/6ÙÓûÇ\¶PÿÒËäèô’“”±©‡óøãOˆ'̤ÞÊ^¸Þ)«ºzíæìßüûÿò÷Kk@F7Õ¿à"¶0Œ 0¶*z–¤NKhjßVL‘ÒFO@Bé¤2õâTR¿¾Nj±ÔMvj°3# º©Nkûô´lÝ+;3¡ç†Çžq°K/¼T� âÐÐ0ܾˆÌ*äE‚¥|‘̰˜‚ ñ^Q;7©£RcžíK^Þxûy>k€Þ;FLáp‰d ¹B ×G§édº±Í_Zú±i³¸´%ñæz—­ ‘Æ�þnô÷÷âÔc§Ñ�fI¬±ÞØ6›mboÍ©ãìÕ1üúWÞh¾qa̲àB(¤®ùGŽãÉ'Ncq÷7Õ5Xž;yìoü½þ[¿ò¿7À£ZÏél¤ñ �Dha²0€Ã+d_©™[r#Íjt-“lB‰k™ÐoëÕÕÒÖ¹Žd1PØl,»¡¾ëÖL‡‰CðÈÉãÂíõËñéLÏ.�„ònŒÎ QoÞöóeÚâ¬K•ŠGÐß�F©TįýÖàæ­Qúý=C;èEo_?KÁhý±Í•Õdl%ÌaÙƒŠ­ØdÞ îëïïGÏà0æóLÌeù¤½U!´\ÎÒr‰í©oª5½° áp{¢Ñ¨GÙ(ˆ/|æcø¾ïü‹—:÷Fc�îÉçýë_ø•ßøýëRÊ€V'þ5¿ªeMìt³Ý«©¿]‚ª'’Vƒkí!ŸW�3¢z¾eR¦ (*¹”K+Ü„wÝ¿<=5ص,¼±C`¢Zö\{TpãÕéß•ÍWrdK&âbd| “RÑ…² Ó}› ìéÓ—mÏKåGê-«NÉiÓ| ŸOŸ<Êà´Âé¢�4¦&Ç05=Ë¥»Ãƒ]t­lÀ>Þž‚ìC‹¹µ™� Iª�¸#]w£cð{ÜøÜ[Ò? õÛ±Ù,f—QªTÑ�Þ@œ~ä>ûÉ�à}mm)Æ^ÈD,º¦'þÆßÿ¿ôK¿ú;sº‚äÒì¬/X†Ž™‘ø í"i9f�írËÞd–öÎùIj~!�R¹Ê›””Qü5±#!›ºÁî³Mªt9k73xzº;ð±?-ÚÚ3–r�“WnL@ù/°ç»úú¶L£§3Å=…£{ñÄ#à½÷Õ×ßÁøäôÞé H‰LG»" åëìÜåQÊL6XÞ’´ôìŠ n¾7Ö”Êl9üVl¨ÄÕÛÓ‰#‡†qðÐ$ÛÚåÌÒ2”/Ä&ûWTö=5Ÿ#‰ „£8xä(>ðô|îÓÃ?ÿI<óÔãdƒ»7z"Í&ù­þëÿø?_ø_¿ðksº>®3‘¯Û4ß$nMó0-‰pÓ² ÊDP¶*8È[ RYE±¸Búzù;ÿ¹ ö‰5]¶&”ö2ßŵNÙ{zzD*™lŽŒÏr91ðáÖØ êµ:ìà,‘fNx˜ïÔÑ~8 J£ÇpöÜ%X�æžòïééeB鎽qîÑ>7`x%í’–p�¨¬U5� ´bÃAY3”ð 7à�=*"‰6L/Po6±‘ ìfb6‹ñÙ%òF¡žiõÌ“gÊ_üâäç>óqüÔ�}žýÂ'qüÈA²½�ºqÖöÖ;ðåwÎþ‡ÿú¯êÈY/¸¼ŽÉõë‘8ôs�iˆ�” GDµœ[õžp‰A7Ú©œÅ²†i¬³yf7Ñv, ·­kºtãUìúÓy{[ý}ݦËã�¦%ßÝï™0ˆ'èäiröuñÚ8Jå•MP�%`i*p³Ê¶¿¼$Z`²ù C,€åCOGG;âéLMCýúø�x>DJ¦—*«äoÂH…˜Ÿ5K:±ÆÀ¡£Ö#�?ÞøÌç?Wû©Ÿþ Ïç?ûɘ‚4 ðû]{$h{ùµ·ðù·Ê/¼ôú;sssãZþ»²;�¤¦§!—5 ¬ÇT«³&ÓVE£iq$›+{ˆÅ—².wÝGhYèÆÎP}ÛƒÄÖÏÚ;!‘iO‘Ç69Ö òƒ¡òÖÄôú»Úî˜m C -Ã@O;÷Mj øÏ_¼Œ¥lvOe%œÔpg™a·½6 &šúÛ(«UÑ%®­Ø Z!¤“ |ð�+FÕqqòÔ#ΡÃÇê†'X�\,Ô'çsrdjׯfqm|–鼓 ËVÍðÔ#í}Öá“§ÌS§Oÿè‡�Ÿøø‡}O>~ÊŒ„C{µÞ‡ë7Gñ+¿ñµ_}ë­©ééKÔP׫ Š] $ &³’3F‰b¥ åÌEÔÐ-AY%ÒÃ©è ¤H–œ…2� ­ûÎjhmKHË–7ÑN}ô¨{"¼öTHž&¦›“UHh(‘À„Âé0ï ÖV]™3¹ê�ºT‰¼5:iY{¦,`8\lüµZcY}¶2ØXHØOµt MºË}Ýj¸oEèž'õ/žyúq|úã4¿ý‹Ÿõ}ø£u >n’Má É:L ÓÕHut5N�yZö6‚á°© )öÖ ü^'fff­›#äTWÝXÚh4Øàje•‡`¡½)Ö`ÆCË×s™Ë"P©2 ´bkÜNÉáØ‘ø ¢ç?zС¼àÝÃ:>üÑ�àø‡ð¹/ü?öÞ8Ž,[>·Å̲ÀLÃÌË �èc ø™™™è1,ïÌÛ¡´ÇÌÌ’m13Q·š¹êþY'nÈŸÇš±-Û²us#£šÔ㕺+ëPž8¾ÿýï:¾ú•×ßþæŽ?üÝïÓ7¾ú*†p7²E�a<¼ßÓH$JgÎ_¡‹-í-W¯Ÿ±,«…ˆŽÝÅ á}÷ÚÊ€ L Õ”@*ƒ‰“#…•×I"Qöð¢d&Í)´/ß½%¸7u!X@”˜¨”–ç¡ïÌÁJTþrvtus]$±M ç�N(¾ÉóL±eŠ”Äyøt‰Å€ŸÆÆ'%¶³‰ÆÜ:zÀ]?Ѥe "Xp—Q¬Ì2zKÑ ï�'©ÒžnÒXp�.??—¶lÚ`.*ÇÞB�Õ²\³…WžòÖC!ˆ!åC�Û›Ÿ÷ÑÉ3å‘“:?ÞµïX8îR–'s<$B˜S)®Y–L¢èLÚÉ ÿ‘°z—#Ø¥ðí:{sÙ—ˆDT�„�÷,µe¥‰\…YWëÆC:›ã…§Ðc|µ�¹n0>½@OloæÎ¦%d™96Ö9IZ’ Õþ®!cvfÖôù�¨·°€¯òû±¥3ÒNßq-HÞ‹ �VŠ(­V*»Š—vôk¬L7W]mfMJ—Ög†‘-�€€NONÎÐɳ—SGNœ»¸{ï¡ãØ¿ÞGDçÁa6!‘*2qHNu‰Œ%¥jÙ•+Þ•�& ÂA{5&˜£&8­åL"•…<�òO²ƒŽ\™ˆG"]_[�+¼fºpñ ÍÌ/Úm±q`ÿõç}ùD\_SNOlk¶-SD(è—ØmÁ¦ÛXí9hÞUãñp§O0’ ŽxÕÞ’•h2 •êJ93JHŒ•‹`uºë¦ÁÁG©¹!‹SGwA@‚ÇN^8yæÜ…£‰D¢GíZŸY -�Æ]\ryHJò¬�Oˆ{Sd†h°Ž¼E�-æÑ |Ë4¶jûå+“{ µ6WÙ¡<*'î£ßˆöÊòòrH”fæü4Lê-Âl™RSYB¡À¢1;;“I$“…pÚ³ ÕU•œâBw ãÝAf×Bn´“ 2ÓÄi.3¹|½DCC>÷Ù5Ç·ÞýXþŸúÓ©¿þé['Ž;q"rQE"S EÀÃ*$($ˆIš7âž £Ê[óãäô·óîI”Neø¨õ¹R¸§&kjØLÕJœôH@J.:¿üÂ3lT—›_@Ýýã¶1#ÖIÜúа¤0\„›Æôôt;±%@«–iò¿]EN<ØÆgÙÑ¥¼S11ùÈPGîÜJˆÅÄJÝü: %"‰xk®ÓûïK`b½ÏþÃW&'§.¨AÃ0LYx˜#’h¦QŒP“¥Ü6*îÁÐ Ä‚OdƒCÓ47äH˒ܪUÑJÖI�íã ƒ_sÏ<Ál ñH0a—‚^ýéÅçž”6FÇç(ŽÒrÈÏÏáIxì-#Ã#ÎT2™B¬êa.Ÿ‘W§’)¶wIàÈs$bkiÒTGE3u#Í¥ìS²£ -"Á@�ö:ž~ÿ£}½o¾ýÑ©‹—ZÎ&“É£Dô‰‘�‚�dÀPX¦e¢e’}k©ÝpÁ}fvÝDÃ<ÝžH¤•ð�bvWÖÒѲLÞŒ¸â'²ì¼¶Ã�U0ôˆ�_}ûëod6oh°fæý\'Ynj=ÇãæèH Aà#žÑÑñ`¼Ô ùuO$$š:ø³Fò êH >JîäŠ�Qb1‘¦ŽL´€ð¹l~~�ö>%?Øuèüßýâïw÷õ$¢·À·Á^0ÊGEH,p´Û�±DÒ²¿€ÑxŠî•CïÁˆÄiÁ¢ñ‰yNsዯ¦®eV«¯¡Ò^–:JZ1¨z“Õ«I"“-[6;``ÇɾÅ0-á0¸Fâv:(•J.^6Á‘«¶@›Ÿ—K†!¸ö&¥dWXìd¹w•ª~"2a¢t„©¦à×®±£§îgfæèì…+„"éý‡O´8tìj!ˆèÕÞë£U 㮾Ü¡k:ák#qdk“{��Fã4·äáD!zñ�`¡¹�8—Ú€W’˜†û†Ç–ÓK�$¤$¯7GTU×Zhóåö^49,+ uUe¼ËDJ¢��A1<¼Š7'ªýÜè.#ÔsÒ¤|¡23àôÖ=…štg±E%º©¦¢±6 ¥d«“ÝûÓß¿ûI Æ‹Gwí9ôA hUžY3ËÔ…ˆ„á²,)�X|2‡˜XRÞóf)\!ó°\EyŸ´Á(wܨÖ_Vxµn—VKKª,Nm±A£á¢GHS‰ 7Іú*‰)w^þ´œð”–ж�ëøw�ÇÅØØ˜­ì«V(=n7o‰Ä„;‡(¨·q—Zhi‡û½=¯³˜¤‚ÄÆŽVZ¥¹¬µ�æÒ‚/~{úéØÉ³ö�¡y¹åúŧ/ì †Bg‰è88OËàQ©‘Œ�!HBGl1¡LÆ"ûx¯��Œ¡ ð¦é§ºT˜xo¢‘¥Ô–tM ç#=XÆs%õõŽæ†z™—ã¡Þ¡‰e�3�.'íØÜÈ+—q•OÝÝ]”HÄWkʆ[Ä«*Ë8= 1á�œ“³~¶Ð¿¯sÒ"JÌ‘L²Šóò¦.0�/KÊU?¡žL$Ù¹ч½�ÊÂfÃk•¿’R~š½GäQ Ž€ ÃH:�¥2Jƒ¨—Üã|¢d{ù‘ÑY¤ZRìÅ…”„2”|BHqÑ_(1‘+›Ú²’, ‰Hë‘Îks;pnž¨_×`{o±s Y6]µ¡©†¶n¬g‹•þ�!'2Vmz BRNBš¼$ÿƒ% G¸–%ïïj+M"é·Ó]JD,EºÍ�&bµG"i“E�YÔÚÖ™èèê;ÕÝ7ø ác/M‚iÖ€�0æÀq¨Ìå°w·;È’Äu’{å:kÛÌ'hÁán.˲øj8{wq:�"Éÿ´RXZ›k&TšËE�<„AMë×Sey‰´£>»XJ¹Ì’,/=¹c=×JìbûððÐêMo{²Ý2A‹ ç|A6rDÑý¾9sGà3jÛ¢ŠV¬”.ÄßViÛ¹rêˆÒÕ¶ºÔÒ–ºÚÖ=xñòõ÷Z®µÿOÁ`è³{cÿ¾úçHÒàœeZ�ŒeÅ Á�R ¼o�Ã#ÓÜ\�ŸË‰Ú¬ÈÏ)®ð•œqÓ’GRR~a©°»·ÝG!y‹¨„» ¶oj€#ðÊñºifz¢Ï5‡UÚPà¥Úê*ÞI‚œ5·9/,†8¢zf‚$¸dô(Ó ù|ÖtQ}l|Šö:�zÈå <³öœ:sáÏÆÆ'þdQNQò¦šîImeÀ�$ ¢.²€Ô–´íäMSÒýö”ðpbqqþ’Õ<‘T¦�Ö=(”Z7,ä�9ܽµ&àp‹õ6É†Ú io¼Þ9$¤iÝòw[^ZD›××Q®×K~ß<Ò[£«53í¿O?±�êjÊmäôÝ$jn¾Àj]¶R$R!“` d³Ç‡<"Ñ"22:A‡�Ÿ¡ —¯?ûáþƒGv£½w�š 1ÕÅñZ†¥¢»V†ãÉE{wH2E’îЭÅþ[33~ˆÉ�U¼~â$ÍÍήƨ„×íVW× %WHB.¸·@(Õçê�7ÃLwq‘dj<\ðûiÏþcæO~ùNÇ/ß|ç†t»ˆè¤bÌZj ÔB"Á!pL£õ7A7n’æ‘g–Ü71á}î<™RÎÀ¶€†MŽLV¦cK8z–zý×\¢¬¢ ‘F5-BÀÏ\îâ¶ÙÏÑS�-ë뙋�µ\ë k×®[ÉÕg/Ï…öŠª:*+-&´²óLRkÇ õN<¸½Ò¼ñ53ÓÙ~ ‰ÌÎ-ЯßþÐü³¿ùUçgûµ¡2DDý _h!¹�ypLJIf(G€ËLÌ-²‰ãýܽ=1å£Ñ±9Jóþx“·Þe¥ºî Nm­EåUÓz¤�JŠ ¨¥}€Úº‡oéìQZœÏé-»®²àÒÕk×åðÈøªÜ²çÍ- —'�ÐÆÎ�Ãã³téZ¼>HëÆJg+µôØê�N´ˆø|~:xô´µ{ß±Ž —ZN[–u‰ˆN‚�Ÿow¢…$Ž«c)-‰'þ`”‹î·<‡ ±âSÿbˆú‡¦©£wŠ»N—Z{ù$€çVÎ\¸TÁÝË©­µiG%ÔÐØLOí\ÏEõ“;¸žð9).Þ¯ßX_Aù9¼iq`pH´µwJL½¯ª¬ 0 *++¥ºúz*,È'üžàòõ>™œ{°é¸¥½%0½zmç5xzph„ví;B'Î^éºÖÖ¹WJyŒˆ@‘=¢pÜ}?,£lK-ÖÞâ‚\ËŽîçp¯å�šš§d"Á.´ÍMu¼‚sE\ Ðp°ˆ¨Éö5ÚÛo°G•ÓŠóŽP$¢õëªol¥K ONÃàÑØÔ<;x=^ª©©•å«ÌÂÛ¢±‰)²ýÁ‚¡0 !ئ®ºŒvni$ñ k%œNMÛ7T;°{uÕš4xøöÌùËèκJ�=ƒÝŸ~vàvª·«HdÌÐ-¡# Žƒ`tÙ“í~为‡§ia1|ßí˜MÓäT…á0¸õW­Þ½{áIÇÌR­dÍ‚£’\*)« šŠ®…\ºÚK£Ó7ÒøKGIeEôü“[¨¬¤€‚¡ˆ˜Ÿ›•#cls³š ‰4Ô£‘ ²’gK !l»ÁpäÝ¥uÖ'“ ’r5-RÓ‚Ï/Ó3stêìERÝYWÞÿp÷î¹¹¹ËDtœM´�,� Sê¶). ÷ÌÈ´/ÛÄ‘ àBˆ{=ÀEw¯Ç³²ûH„êÖ²)k[L„ƒ Ëj©¦ªœÊK És­äsN¸¥ln®¥Úª2;ÕÈ ±2ÔÂvÂUuBt#ÊÚ¶uíØ¾…rs½vôÄÿÞî�q™˜{°©-‹»ìù¸ä¬ ñ@ar“Oˆ®µuÑG»Ð…+í‹—Z;öïÝ装…[@€ô¥¡…$úÁ˜R^—”R`ž$<>ëÏ,"¤D„éñ¸–+™¬ˆÐ†à“ö’ðû­Œí›qc+¢Ú”¸–axŠhãFœx‘ö©Gúg|zžǦÉ̘7ÕJlÙÐPÍû@0(æf¦-ÿb`ÕÙ¥4Ô×Ò¯½LOìØLy9^v‘GJîz×7t<H+ËbGÃù€‹íñx‚ZÑ…øÛ�öÐ{í1��¾Ø±{íúlßGðÒ:©Q-ÒmA ‰ ö€ÝJLè@$’ „b‹ ¡¨tºTRœ�Bfaì·$Lâ# ,½ïº¦!-É3%xŸ;ëÜ4nÐpªh„´ˆdÍÖ”×SC] U–óÅÖöAš™_¼éד—ç¥�͵œ2šž[‘pÐBaÒ$)WÛþÚ¹cýø{ß -ê¨ /b©ôVüÁEP,$)0>¨Ös )‰ëgÇN�£�w Ã'Î&ŽŸ¾ØrèȉO;»ºöH)÷Ñe0F€’ÛÇØ F²ŠJŽh<™™YZ–$þ’ÖV—òÐW}]9y=.¦Ûí$ Ÿô‹‹l‘ñ�×뾓H‚Ó( »¸’ ²LëÎ"e¯~ù¨j#Š‚Hƒ{mؼ�šjX$&g¨«Œwê3ä�”fS}•-î¼–Ù®‰±Aa;4¯6Ð+/¿HÏ=¹�/x╸�Ý>´ö�ˆ~^ãt×ÐBb�íànð(8 –%ݨ•P(– üüþRÂ:ƒ#��jibëjË©±±ŠÖUREE1žË£2ˆMa~. ¶‡B|é®- ³àç‹ø¿…ûwa‰âä/.]…Ù)L ÃIµ�›h˦&ûoÆ-Á=HÍ/(¸Pàí‰e%…œ&êš Žök”N&VáŽ/­kÚH•¥ä0 n"è˜àÿ_fÆ¢©„Äá¾aÓceîÝæD nÿž™™£°~ÿèÓ}ÔÞÕKRR|vnnŸÏçû%íûV, ÑBƒûÁ}`�è¦ÅP u'ŸLqØW®,åe…¨™pD²®®ÂÞùÍKuU åBp0ͯCzAÕSh9ï,~NðI+Ÿw”†ÞEjËáá%VœÖr哯çÃé-¤u ëyÅ®½»trŽúG¦²­üùïPW]Êk!¿æz{-ú—üºVÕ€bEuÕÖ7ItsI»�|i»«ƒl óÀ`™J<@3Î˯ø1�•7[ŒÇ±|êýé_ý‚~ò«w[ I­ðnA�ä-": ή|H¨…D‚³àeÅ0�%3½ÃSAÞÓˆ6¸&‚\9G(Þ7¯p-Âã%%ù\3©€¸@Pxº¦¦”Êpß~­Ä)«\p¹"{^^¿à®­»)p29ÜÃÏpx?7ÑTUßL›64²WvŸó"ï>ω °–ߺ�]v¹VÒ74N ¾UYU---¡-[·¡™ ziÒ}d|–k@ñ;]'-¿˜ËC²€pz+Å1qÚеˆ„ì‚ú‰sôæÛÑ¡c§x!›m/¶Ö±ñÉÿ'��BI㞉šªàtQ I´ú†§ã'.vùpµ*q’ç=ØžáqÌaz¸«« ^ÃÝ]MÈ»WVCHrY@êk+¸…TJyË¥ÿÈc³ýwAû&ÝÁy_¨Ôq.zÉ_KšË ƒi8=ù´mÇã´©¹Ž{ìç‚ìW•Igˆ!‰/¶m\‡c.ÏiLÏúidl’íèW0½�:É&Q[].ìô–!ÏË �ÎÐÜB�äm‹Çm¼~9X�^\NÒX±TìØ{üÔyú©¬³¨‹,ø9ÓašfçàÐØ5<:¾ßårÅîÝ<œ’lAé÷€m`)c߉«©Žþ‰˜›Û{�ì™î´Bº‹EÅír𱺪5“"ŽXrìûÕˆNJ àºÉrŽ#£Sü߀`Ý~à)Ä�Ž-Ê^«ëø‚AD�âŠ:Ú¹�‹Ô<ŽiwŽLøoÒ´®’‡‘.b—ݱÉ.f«.½UR\L뛸®@ígVµ6Ûÿn)å� ‚øb.ÿžREËb‰+cüÚÕÓOï~°›>ƒWV{GaÖɾ 0!—°áð¿Ænõc†!R÷e›¡FRu2|¢Ža{£Þ‡û/„1o`}žš«ú‹ Ä„¯^UÄÂÑIC}%G*ÜÕ…ç𸠇�.»ÉÍ|>Üi±�a8T‚_8‚4>Âá¡Æõ[0ƒ±Ž •£³lǺ1ñÎÉ5•¥|á�Âk`›ÂÌjìÞ***¢_|‘šëyЈ\÷Á>ŽPnDļ«Ñ ~&N CÜýGG€2«8;‚©qgÖJmŽ=ŽR{g/ÚÓ�Ùˆ�ÛåZˆ'Ÿö ÿ�°õ9ŒŽÐ´eIJ§Ó¤¡ à {‡èƒ`9X5:1oâKæØ¾y�Ç.ÌÊ[Xœ¨…T8òmN…,EBpÊ Q Z…ùád–m}YY‰ùüs;l±w&¯.õEuƒ.�;¶¾D*AÃåñ’G$i‘êc‹4G •hÿ&5O4>½ÀC~Å…ùò›_{Ýܺy“a Ëjƒá“b”uùêuÛ^^Ø7/<µ…S§7ƒ?‡j~Ø®©°ˆ†£1¤æœŽ[¦UÔTmï‚@‘Ŭ•®"wa™ネD¢dY¿¿j|)°7ßÀÐ(>vš.·´Qÿà0ƒa²,k¿×ß"*y¾pq® ?8-$ �c`\ošVéÈļÀÀ`Îú†J“ÃËyf-E(.·Ó>rDÂ��€=¼È­Ä©û"ñsvꬼ¼4óÜS[Éa㎗X±Š�£—�_ÚñW‡%ˆs(�ÑÔÌ%l�DÍõÕˆBXüíT­ëé'ËlÚ¸Ñér»i5ÂátR"-­×ÄÜB€O<;·6qýîóRY�`”ì×Í‚X3ÌbÒ74ImÝ#ö _Cp:—Óeã“ó¨%ÍñsCc3\WšœñA|âr ŽÉ|;†ŸG:�÷àïܶ�ªjÖ‘;Ï!Ó¦_„#Qî|Ÿd'æ�¡ꇠlÜÐDÛ6o¤ªÊrDæžlS;5¡¯E¤·†‹ûèÒ•k\TW&¯ñŸ¿;»ˆ¨ku,¢ÒB’=ù¾ôš–õOðåÙyáj%6Š~ã|I×±¯Ñr6 ÁµLGs4™Ì ¤�qâó‡í™nÓô/Ìç„BQŠ”Ê�A-"‡JkÝ^Û¯†A•5�´mS3MN/ð‰°«œO†8©ñ<ц¦»/¦§¦SÈa8V«k~Ii ϺNµR‘Bÿð¤ý9ãy%~� †¹QÕõª+Kèä…vŦE_ Dþ@„™H¦xRi>~->û,JH�‘Êká íâ¾DÔ’@o¥Á@^n®cÇöé·ÇëŽD£ÂŽŽ Ãà…JþÅ ëö½°x 8Œ4Ímß²‘ÊËJí:_€ys² M¥\{Öï¦É®½ïüvut÷„ƒìú‡µF°W¤"ò+®íjy€©­åÅdtZ–¬5 Q‚°Ýƒï ÁvžJ‹óØÛèKzjñ—�Ý„q´Å%†||ii·�JË(΋ÒÒ¢;‹H 5ÁîÌåÔó¶ÞGÃp¸Èë”4>>f·úrú17ÇˉÃà9¤sìú�¹óñ'DQQ�ƒV'8E ÎcïJ7Oå£U‘tÕÕ”‰›ënö/†Ù2s5vŠ/ƒç‘p w ®IÆ’2…(;D’Hˆ9|¶'paÔ�TV+R\g'§}çÑ�™žó_GºlBf¾¼`bjÚ=19-�Ç·…‰kQ˜oàD…#”é™Y§kH{u÷€ýˆŒÆ)Žðžz‡jF1ÔQ7|ä2¦ÉQpÓsÀCû}ðÙí½'ÏÓ;èκÞÑ̓‡Àäü¼ïðàðè)ˆò§DthuZžèˆ${úýS|� 0 häåx¶uL”¢“‹[+ßxq†Öª9}õE9Iwu¹U÷ÏÖÍõdI¢‰‰y*,Ì£ˆúRa�èÚbÁ)-Õ±%o;"Ñ0¨ ´ššê î~µ}†w¸£Öeר¸­gÌÙÖÖ‘YW[å^µ½ù†i¹zNý˜?«#èH{æñ�d‚Era1ÄŸ·©9?OÀ# Æõ IÒ „¢þ@ø8#Gý¤Ý_õÃ($"~Þ5ŠB®$ê±,kõÃEt¼Í©&•èMP EuCÿÀPSqqÑft=‹è¾?_–KI”²CüBáÌs9]©´åAçQn47lÇZ)%G!ùy¹TQ^J�ëê©®¶ÊŽTx½°Û¸+±-Š `1J$,¢Ô[Ê••–SA~¿ŠûìkwSmKÊU…¤S)Õ+­mtñÊUšA-Ä2­D4ïÇŽœ_ÎÍû®Q?¸¦Iã¶ èÁ ü§2üzUyÑŽ\¯»Wyb}c}í¥ôü9}E$oú\~ÞYÝ4y"ÛsLLÎó êjÊ©!~q±=³àüâvßì/‚¡üµ‹t�ý.ü¡»¯Pow�q’¤§vl€çVŠÒ:p²íÁVͺͩõ/þ‘'´Uû-‰ø§èg¿x“Þýô?ðûß{…þàû¯ØÅuÞGß34a§ñXh 2„¾_\èø  }=ƒã‡1(û®25- ÐkÀ\0ŽÚG¬öåbû­†$“©”C‰K™b-¢‹§HP9:"-œø¡1N7s– !šQ)¢Ž<�¢n|'œ Üu:9MWZRÂÑ>^Ë-®x?[,˜†|ܺyÕTURßà0‹JEY R–ü>98 ÃXU²Q<~ú<¯ÂEMÉŽÈ"ˆÐF'§fº�ÊÚgšÖ^"ò=t©,-$ŒðG˜ùNiQþS%Eyõ†0äÖëéõç·Ñ“Û›b“�0äçýÛñ„ù,(°J±?èl îõæ|a»/CZ7ÞÚSBäñœÆ�_ úç¦éô‰C„ýÕdY’×|õåÇm_.ž19{¹‹qŠýÃøs6®oÀªX;xà€ü»Ÿ¿E±Å÷מßÎ-Î�PDÍŒ$ìÏ_ÝW½HouâD<!™TÛòún1¦hÜ©�¯*»ˆ¨X‰JXzÁ$˜RÂU�i2 ±�$ÕåååVàä_‚½ùîÂü¼Œ$2…�êÊr žxù ‹E˜ŸpCT<$Ãëõr*BÆÂAQNy„èˆ Bµtì±ÐÔÕV„Œ<œ‚v<°ÖÞÑñ)teÇ*ÜKöÔzÊçŒÍÎ/ÅzÜ)e�:�õ £|Ö0Œçj+K¾Z^ZðÒ^ņT�ÎS;×ÓW^ÜN;6­[ÖE!»Ÿ?¼¶ÝJñp¢Ë »ý¦‰8ñVÜ]§–ç^¦+Ï.¥ÕŸyl=÷Ä&Ú;zö�ÏS/¼üºãÅžsØ'§UP;sî"ýíO~![®÷ %b“âzRÖ)QIu�>Ô2®""™Q)’~ðÂ}¼Ú5²êž&(³†ŽsÁzpØì„J E8€º•QNj&¾3ß•R)­M†0ê¹l(-)®)A�}uU…7 »ì”–i™\cq»9†È¿Œ¿c©·6Û Û­oj úºÂϧР–fi„÷°˜n" îÆ:xôuv÷YSðã™™�ï ‚‡™&¢nÃ0‚‚4r!a`=ø2 _­()|Íá46#ïìEˆŽIèzå™-ôÆ Û©©¾’OF¦%y–dù‚¼‹Š‹Ùi–s¼Ë~pyïH‘•⨄…ÅSÆC_º6²&xÁ>°›f¦&ì+T¶½ùÎÏpmëÀ‰+4ŠtdYE½úú×ië–�œ"Ym­¢0îãYƒOví1/]¹j†Õ•¥C8yö¢Ë�Ö^ÒW“OL¯�—Vá¶<ô*aK+ÁŠ.У¢›íàN4´¬+€•¢—"|k‰¨ÑY!þFùHcåXRº„� baAQ­ùv”ÂQ ×WJŠ *X�¶äÇvláù†”+2™n‚¶€C!¤gQ i§£'ÎÐÀàht|rjí½ãxÝ"z_1-åJnÓpЃ‡Ä/(¥ÅÝRãpN¡nRàF˜�Ô��!F uMRÀl=ïPöð_|UÓñ|å´|}DfùkDN¯ÚAâ$�»‡ËåfË”ÉñQ"’ü÷üR ¥ Ç(Ž ž\7ihhàôˆ�Õ’æª>`ŸgæüVk[÷èÔôüâØäÜAL±¿�Bú ;¥%¥ÌQi’6°ŒÒêƒT’Q·³7Á$TõœQ)å,ú�GÐí´0¿àÃÈŠ¿Wû×Á9ñ¢±˜ƒÅSÚÝʼnߓ-*†�@‰&&¦yÈolbÊî$ã}çH­Rgü9P5™Ûýƒ0!by\ÁDúe°åj;�>™ÓX(ª[ݽƒ>Lª÷aR½KJ¹ßp†4Vb•]¼ )9ÇÛT[Uº±¬8ÿ›Œç†±ž„€Š›ž~l½öÜVÚÔTMÅ…yË�l8½U\\‚SîÛ¡CÕCÔ4»·b‹ì�`�N?lMŽ vDÙP[N¯¿ð§·._ïãý$ ‹úö·¿+¿úÕ7DÞê–¦qËèÿöÿþíð®ÏŒ©en{Á^p#èTi¬n0ü�#ܪÖR¨nK°XñIA3¢Re¹¨�„jkª**+ÊG‘»Á¸âñ$IÉ+ìèD"ºùÔÔXOõ¨¥lÞØÌi°|;­)Pó-êvv3¥´,îÈœFÔ1>9ÍѶÒÌÌ­Å‘ˆÅ¢VêrD¢±¢ˆ40Ðg�=yX†C!róܽµ±©–Î\î¤Î¾Qêê£'ŸØ™ùGÿø••†òZ#`U‰É{ïŸø7ÿƒÿ²%‹Eˆèˆ&äãÚ€½`‰·ŠhªŠ Ö76Ôm**,|l1hÀà°Ý5–I¸ÀË-Aç%§º²Â‹�I×ã;·:Û±ÕâÈÏãöP:“¶-`øwŽ&Š rí¢á‘1ŽDÐÌÀõ©™9»>#åDÆ'¦&‘†ìDaÝ'‰¦U£Ã50AkKHjJø©Ít®¥×»ˆ�èìú<ö#ˆÇ“…ÈÐÚmÂ_ù1ZW[vK1ÉÏ/@‘¯ð–�#,"Ï�™G.‘§ø¤µ4p¥HgÏœŠO�õ9-Ëráïhïqçî­®ÞQꧪŠùû¿ÿ{r覂 —Z9 „XUõ’îþÑù?ùçÿΞöö/u‚‡Á«Kó�¹À͆a<+¥lËU�¦ÌÃwÒƒ(Æ‚èXÕÕñ�Û¶ä¯Ãr´�¢HŸ ɇ뮢a %&’É$[Î@hl¦Ðç<ƒ÷ÅçªÞXÝHŸù®·wÇPɨÖ0¸¸Æ·j!Q`Ëñ­ëk©½wÌ‹”È6´ÿ! µ¿_\˜ÛðÍWwÿî·Ÿ7� £Ïs|@}„\ÑÂxë_�3OùØ{‰…Ä•§‹ì÷È“g.�;OD &ôôÎ�ü÷mm ÐÞªøÆ+ÏÉï|ÿG¢¼¼bÉÊc5a1¢w>Ü×ò_ü·ÿKK$ ª�Ó`·“›æ{D”V©"¾¡"˜Mêè%˜óA�ÁŽßyi¤¿Š �˜Ç©DôáEôaw•EÀy"ê°,ó¨Ý‰…�W[4"Y5_¡÷¨k!Y6B±£�“»*Q7ù·1ƒòýæu•e¿ó­çK¾õÚãÚõ:ŠÔ",)å’ýCii·ßZHrU�ÄMä)×Vñ÷¸·ÿÜùóé®kDÒ¨«*£ŸÞBWÚXHl·\öâúÁw¿IO=÷2Ò’ùÄ»ø…XUÿÚ:ûÒÿß_ýòÔoÞý �ˆBY]Z÷¼ÖJH\`žb“ª½4(ÁÉUâ"À˜V÷k‰Á`8΂I¬m˜†AØf"çØÆ9Ó²~…ûõ L^ë£Ý?ïèÄÁS×Òg¯ô�½D Ï/¥"À›n߀¼Ñò+MÒ¸÷¶ì›6nr•”– {xtfa‘]²xxÜN�Y<ÖÝÕI¾Ùq\�Þ[5Ue®¯¾þÂó[·n.ViœpX@kÿé?ýN6o§cÌÌÚU4žUÅð�Á}jÞ.u·:¾ þø€¿�«ŸM®Žz ñ�ÿÛ7�?ŸFø»¡y]E ¼ºòëªJ+^{~›û™�ͼøª  � î*BaÞÔ¹Åí¾žf×É}°ðîéD׋FÅë/>FH�Ó‘ÓW¹^Ò7<ɾVßüÊKôÜË_£¢âR˜U6éÎîºo°§ÿ¯òæñ@`q@åã{ÀË`òQ’lüßoXù¤˜†’ûQmŸ_†ácѺÚò\D-ͯ<»Õm3®GgPqQ a|~ÎÝps§×J\÷¾Ð®Á®«'Oœ°Ú®^~ñão½h[� ƒ«‹Úº‡¹#ç)ÔO¾ùíïÑÆ-ÛÙ S±ê¦ÝOŸ»,?;pràÀáãŸŽŽŽ†”€| ök!ÑЩ­‡Ø î?�¤1…�séó­}£{޶Dw¾,/^í£p$FBæMÊgË2ï“%ŠF.jVÏ>÷¼Q]×(a'�t¤�ë* Ýy`!Áu�†Æ¦©õZÏ ¬Æ5§ö¬KSC½xbǦMßýÖ×^u»=yª¨ÜºHCC ÉÂ¥-ŒÝàà›Rʾ™ù@¤¥chb߉« î;Ÿ:§Yø ݺþg¸îóŽ�Šò2zþ…�XJÊÓ—:Èát`¶¤‘·ÂÈ“;¸†‡†ibr’Âj'ùê€$’’k%U•eT_W Küú-M� ªX\–Ñ£ Ž@²©¡…äQ€à�û5xKˆÂ½c‘«Áww�нùáQÞŸ-¥¼)µÅtæÞçlŸFccmÞºCvôM˜½ƒT©¶_Ú‘‰�©éêëí¶'ãÙÚüÁA’´Lu´È²2$¥E05ÄJÛõØïQSòÂsOUƒ…¤‰44´�<”È€Êq•7�Y–œ™]ºÐí•Ü}èRâOþ)?×FX_šµÃšSZ÷¿6¢Áö5/½ô‚!ÜyéË×ú,¤&y‹bQAž]á´ŽŽNæP’efˆ$©£E õ5ôÚKÏŠü'¿W‡¦Žy%$[A/ihhÓÆ‡¶n’ýà,¸ȱWÌûƒN˜î™öúSì�çÝ'˜;Qz"—"�û!x#æÜü¢èé@DR`lÛ°Žw{ =É ±¡0‹JsS3ååç²Ù©Ýæ K÷oÒ2oÃÏK°pX–ÉG6�å8)r8]ìU__›ŸÊÑ3gϯSÃv—1ë4M‹44´�<œb’*BéC–e@@ ìZI§mÉÑ?îˆÆ“¢®²˜òr\'Ñé­ûµù/m ŽLZd&%öаš·÷œó {Á¦D<&srs%\ ¦ž9µde’¼°ïƒ&ÂÈÚ).ñ˜¥„T‚#Iòcf:ɯ1 '‹CÖf¿›JE$éD˜¤•¦L*N†ÃI™tBâ&A,HžÉ陿sZràŠëÁ`lW"áì»v†�b% �Úzø Á8¶ªA¦?—RîÃö:vn'Q€�ý_û¡õßÿoÓé ×) ‘Æý‡ápÐ6ì!yö™§EÆÈ !½enjªÃÎ’§ÙÔ¦�„­vâjË%qõzÁ¾œ×'§@šˆ!øäžJFø„â~‚Å&k±&‹�™I© Ž¥zY2àŸ'¾/ùç-¼Žk!öÏ@0Òxoï—JDñú bd?Ïÿ"‹¼.Ã[VZ\HD°òùêæõ[Kì®Asäc>û¦††’‡i0ª"“Ãàß�ï"ÝÐE#£sÑ=G.™ÿÝÿóúÿæ7(ìöž»Ï\Ú[øì}ào~©§ot!f[‚ÛûË·¬¯£šÊR2MIssób ·‹ÆÆ'‰„àx"ºH�(AIDü%àIJÅê¦aòkIZ,¯'’QðãtCLø½ÔëÓ© „B¦xx�ÛïäÿF"âÃí3‰Ç’Ñ• ÛlǶÍZƒ$iÖî6� 27�††’‡?Õ•Qu“ëà{௤”g�ÌÎù¡ŽÞÑÄ[²þ§ÿë/éý�?£‘ÑqJg2÷GP48­TWSeO°çž¾:*N]êIØ5’¦ú*nÆ’3š÷‡hrbœ®·µQ(à¨!…¨€#‘D"ÅíÇíß',uÌ,¥«ÌtŠE?ǯ�Hðó"ˆGˆpp ðkL¼¯ãÿ.‹W*ÎBƒaXúƒ}=±¡©nÊÞfëtO”ÖËó²ZHzHÅØ­<}~¾ƒµ¾gƒ‘aÔN|gη†ÿê'o&ÿ¿¿ü¹õÉî,(¦iÞAÑQIqIa?ùÑÜ‹cæ¹–n [3‘Þª¡FµfyzÖG'Nœ¢ööv[,ø¤žû(“Œª“<×/ˆÓNf ÷”É:é[Ž^X ¤™áŸO§¢üX&�äèQ Èé+ûÆø½!6,.V†_Ë?k™,N”IڥʒÚÙ”çzîñõ."²à^»^bùBBÆŽ44t±ýÑ�¥eìG¤”D D(Ñp8šš˜š¦öÎ^'v8€�Ó<�}ïEÃàt•Eر-悎X,f9äØÐXC¾@"²H¶°`Ý+¹ÂÚØXNN‡™L’SP .œ£ŽÁâ!¹Ã*Í`±`°»¥Q)M% AœE…s~/<�‘Éï�àŸqyòlÑà:‹”–J‘ ¬¥B¾eG+aŒCg¯v!C—ö ‘àÍýÅ/÷P`¼…þG -$�N„b*A™P‚ÒkYÖvTGá£”Æ Ë��k™îÞGGW¯'�B*åx½œŠ!A+ŒââBÞ!ƒ=F(š�~¿ßÄ.wêY(¸/R4ž ?D]Qæ–æJY”ç6 >,ŠTÒÐX3B¢Å$­% ΀=RÊAì�Ž$I3‹™¹y×Ð𘳻o@ �ÐÂÂ"åÑöÉ©™» 6p,EK0BB1:>mú}>;"‰DŠ£�H4ÎmÚÕÅ„´‘!„HIi™xÎT¦$T¤ÀÖ&BZ7Ú€-[”ʼnPQ^ÃG€5L¨·L‘“ß’1Ô2•J?'lу�ص÷èäü””4Ljh¬9!Ñ‚’“`œMËêI¥Óóð} ºÜn ŽŠFÆ&œ-W;èJk �ŒQ$£B¤¾r!*Âá �;ƒËÞ·_X@0K3ý2‰v}D‚ÏÄŒo”ˆ:À$ih|IèˆDC€…8Ô74¬{ È‹EE�%‰Íáp8ßNqa>…Š‹ ¹;©¾¶š6mh¢­°TG4þS¨ðA­'7RŠcúä³Cò£O?“‹h F�,.ÌüÃWÄ7^}‚¼nIú²àÅT¢à\î;‘ɤÔë½_t1fo|Üw>tær÷A¤¹þ/"ºB·-$ºX¬Ò…`MnnÞvج|­¬¬t¦ã›S©T†çs�öâùt(Qú:jnZ‡ÔW=ß~ú‰�T^VÂJ€Eô¶÷ÀQúð“=²·¯_&âq¹skƒã»_yšßÚH奅$nO}%(¾¤ð$Uäâ¦/ÚéíOO¦öŸ¸znÞü-½FICã6 S[R¥½|étj6 OÌÌÎ ÍÎÍO!" aÄ!"BáBñ؆ht|’FÆ&)‰fUØ0u�îR°Ewãú&*).¡pLbz< qIIŒ7…!åçyÙÂf…/ª–Š÷_ög¼a0Ñèœ /£5厣†Æ�CG$Xn7�µ‘ â› +�æ*BÔ Z) )]••eôõ7^¡7^}�jª+�”RaA>ç ÃXÓŸÞD"I—[®Óácg$œšÓE¹†‘㲌ü\·=_blÝPÏ‚ò �áUÚ}ø2í:tiìÂÕ¾ÃRÊwˆèÄJÝ54´�èˆ0,óÔí*bæTž@kq£Ã0=^w%‡sÑáeØWáMHy5ƒšéé'wòvÁµœæBc]¼|Õ¶ýç¶j"+ ÂF&âÚ¶¡*×”ü¼žë š“Rrzëï?>üøÀ…½¨“ØBrÌ�†Æ B ‰†%èëÁ`%¸QÇKèæªw¹\^¯;ÝK.â…]Gyê‰ôò ÏÐÎí[¸`¿fÅÒÕÛO]=ý„9*(È·Ú;{Òñ°Ÿ¼Î”³´0ÇÑXWÁ ½÷ýÈf×\ØdòÝ]§ã?}÷ÐßÏûB»‰è' �{]#ѰÀ8 öƒ×¥”¨�¤ºüHådÐí%0ؘñùöÞlq>ÿ"Ï¢­É ¢7BÄ¡¨±×õÚ›@äæ F’Î_”F¦äb DáhT 2aw—Óqß.Ô,))Ž Ø¥   x‘ˆFÁihÜch!Ñ‚’ƒ`Ÿ•€Ý¢jšaùV b’„½zzrj6342aÍÌ-E†½ÏÃ¥lX×L‡\Nn *:âøØˆ®·²²‘6I,,F%deü‹<‹»zŽN ,÷<Ú$hÎ40SížZú{’†Æ}„-*>pZ‰‹ úA:º¢èìJ£,Ö70¾ÚÖ-†GÇœ–iD’ëx çé].—µ (KM¹y¹TWSÅ.ÞœަE"MQø£¥fçüîx<É‚âñ¸îiã‚Dþ@„æýagÿðÔÖïöÑ,ih<h!ÑK·&GTzdLI)�X,NcãÖ•«ítüÔyyáÒ5«»wÐ33ó"m ŠeQNŽ— Në¬�H%¿ Ÿ645ÐæMëEUUµËá΋‹Qx¡åÀ™ònÂP#B¬ÄêÇçýP¡‰i_Ò[iDFÃDÔ JÒиÏÐB¢‘gÁ1•™T¢r L€eˆ@òqÅ-à:œjëì‰]oï^lïê Ùk‚Û:z ì£7<.—Àl o#$!ÖŒùc>R^uµUsÃáóûÓóó>cn!``… ‡A¹^7×YîÐIŸƒp$AHk˜)‰Í-Lj¨}µÙ¥hhè®-}AP~| \§˜ &ÔD¶Ç‹Nii‰±eóç Ï>éFÇ—kǶMžªŠ2§½ûƒEEÊ5ñÉO&R415cž9{6uüØ1W(rVUÓÛ›é)°´$Ÿ ò+õëXX ÓÁ“WmN�½Òóž”òoˆh�44V´�hxÀr°ü¸t�Å`%˜`Ja••–FÛ¹=ý­o¼QðÝo~¥´¡®ª¼ ?7ákCT„ 8 È=û™û²æçfQ{FUy1íØÒÀv+Mõ•‹·†ÆˆH442ªNÒvªâ}¶Bäùý‹Î+­×ým½¡H4î…K±·¤¸PÀ÷þ‘°Y�}¿¨®ªrLMÏËEÿ¡ëÊ6ä|yi‘B:�¡LƘ=Éý×Óǰc¡®þqÙ;4uy|zá4 ¬Õ½$:"ÑÐp‚%`-¸¬QËSÀ…øùgŸªþÑ÷¾~#·©¡ŽŒŽOÐîÝ»£/^tûC®žÜL¿÷Ý© /‡nžó…0ÈXA·‹Ha"rær7�¾Ô?y±óS´¿IDÇ×rjKCÛ5t„2^?þZÕT¢˜IIcÈqæÿù‹ŸuüäWï�wv÷›–eÑ£€Æuõôãÿ®U[S›DäA�}ãÔÖ=B¦i‘ â)xD+, <ˆØÚ9Dí½£+y ó«!Ñ4‘††NmihȬš@�˜°þU^ìTŸ�ÔÕÖ”À0åÇCS?wÛ(�(¼G{zúríA‚6¯¯¥¯‡‰ÂQn8()̧©ÙEºÒ>H½c46µ�î™n‹D'‰è,54tD¢¡GÁÝ*B¹F1ÜèÙ»ÿðÂ÷¿þiËÞÇCÑhì¡pÆ "uõ Ø^\7ÿ{ ‡hZ¿Iææx$¶˜ �ÏR<‘´X„‚¥k`Åõkt¥m€F¦ íýˆNNQËòEv ]#ÑÐ`3ø•q%˜Ú²e“øþ­µíß~£¸®¶šÄ*Ó4éô¹Ë”† |åµ—¸�,Iòøñcé÷ß{×…#Â08"�ÉcùaÂc,&X«K~¤µØb •EÛï;�ýDÔ FIC〓44î $8~€;ÁªÞÞ~ãú?þl²³«Çû�þøÇÞçŸybÕ¶ !x Øõö.ª­©¢Û6)¤S)919rFY(£tÆ$ž>Œ%y§‰_‰‰Ý¥…:Љ!Ä ÈOÁ÷ˆ(ðåRZ:µ¥¡a©Vá_�«úIzzzÆñöûŸÌÿé_ý"}êì%^LB¬ÊEX…ùäõz©§o�|þE®™o; ÎtttÆ "cZfŽ×�(-ÊOzpDÑ=…”WÐëuObX±¦�G‘îú àß$¢!ÐZ¤¡¡‹í·µo~œ#`U"‘tNÏΧFÇ&óÑådÔ#Í•““³êv•æ|\'ñ‚Ü™%…sô—oôÉ;ïœ÷�™¹@&û÷¡‰à"ú-ÌŒNÎÔÝ?ñ&àFãÉRÊãn—sÚ¼«Î5 ]#ÑÐp€…j:þ�ÀÆšêêš§ßVýýï|Müî¿M5Õ•´ŠÀÑRëµNºÞÑ��˜^ †¢Ãã'<V~Y¥à$¸lS)+!�ßÿîK¼{äjç�XYO- -$àm:�ÎõÍM ë·nZ_þÜ3�ÓýÞhãúFZÉaD?„2ÁþþŽŠû >?\ñÏ��ON�ŒMFÚ:zè£O÷SËÕvŠ'+6 219MíxïÙ¹º”—•vÜ'ý~¿[Ùì×1b`ØvÿEDCC ‰†F<žÆœÉäðèX/j&Áö®^:pä$�¿ØJAÔ%îÂܶ;‡:Ç•«mÔ‹¢y2•º½â¾(ºlöªNǸ*š_[œÅ‰†† �9ð x,‰v·ut·÷ ‡zziïÁcôégiQÄ]A·înݼ� å._¥Óè C·•Ü•’ét*¡šF”€œ¤¡ñÀ …DC# ^!µ@+Ž õŽôöBLúú‡éø©óôÉžƒ\£¸¸\.®»<ýΝ«á6Þ+­m49=KÑHŒ¾ÉdŠ®¶÷ ƒÁ´ê@ëW‡O–†† �”J�ÏÂì±e`häÚÈø”µ ˜=Ò�Ã'YLîbÖ„‹÷ëêk饞¦çžy‚ ðݽÔÙÓÏBq+X–Eí�½±¿ûù›!Õq–�Õ5¢¡¡…DCûÁãà Ìlô÷ôöô Œ,tv÷I;uìä9òALî¹¹¹´¾©�¶nÙ@nD*SˆJ‡G—DÃÖ( !ht|ŠÞùpÏØ¹ó—r•�Ì‚£`˜444V�EІNs 1\èæŠMMÏ$ÉdS8­F Å57ïCDñ íܾ™ƒßV±šHÇÀÒÐÐX•s$,ŸßŸÆfÂÍØR‰:GáÆ �⫯¿L¯½ô,ÒÝmÇÔr­ƒÛƒËÊJ30Y ‚ÜÕÕÕ‹Û·mîzûý]óÿß_ümN (R‹WÀƒ*zŠ�††Æª �"%&/‚O¹ÝîâÒ’âíu5U5O<¾ÝõúËÏÑË/>C ëêÈÞ /Ä�¹û�ÑðÈ�¹ÐšüàÓg"áPÜp¸¦ü~ßH4Í!¢&Ð>ž€CÏ̈†† �|ðiðp#¸5Çë­mjª¯Ü¶ycá·¾þÛ»ßÍ$|*•æÉ[ï~lýüÍ÷Û|}Œ(Áˆ€çÁ‹àâÃeu¢¡¡‹í𬲢¿Îbâ=Ð×?4ÚÑÝ¿Ðr•ÓRwµÓÄívQIq‘íò+„`ë/è…ª‰úÿÛ»Iº0ŒÂcۢ߶mÛ¶m¬mÛ¶mÛ6c wßštŒµÏ“œô�:Bñ««æ»ÖFà@‚‹L�ڡƨ j› ‹tÙvà°í¨õ ¯õþtYó%&&*ÊæºuµLMTÃ]ë ¸„ª‡UsÕ+%%¹ïç¿s¨O—–fߖŦ¾xŸ1%ûO®Ò¦øèfÓ¹m㚬¬Ìñúß¶êiå¯\bW$@‘𝆍ÕrjXqÁ²UOœ2[û«ï²v#<éÁEë›^6»³º°°È¸®>œªZ.a êMõ[lLLÏçž~bK‹F¿W/ž5ƔضXWÇ»QJëåóÆ›7_{a�—·wOýß×*S¸Ä�£jšºÎát–O™>»Üæp:jw^ewäDß}ÇMÞš iØ.×hb½^éu_ý7c”ªÐûá£6}‚eaÉüEË÷ÕÖÔXoiU^^3"€u‡úJu qËM7Ìþé»Ï·NÝß±néôŠó'ÔOÛߌÒÝôêÜÂ4ý÷gÓ䟟Ìç¿]÷À}w内…Y{Š,R�Õë*P.#aê)õ«ê¨†ªÉ‘‘‘ão¸þÚO=ñèŒG¼o•~—Ýxõ۲³Òí™i@èŠe�ëãêkuÅåùì` ˆRwªl•¬bU°²«ÝÊ_y+k`$ÒÓ°V>jŸš¥6\Þ“ë€pu¯úPýªZ«ÿÔóêiõ²zV=§ÞP_«Ô3*‘+€%@e¨»Ôóêv®”¿òs寂T òVî  î*\¥©å«8iÊS8ŸŽ,^ö pÇIEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/rory.png0000644000175100017510000020532515224505336023610 0ustar runnerrunner‰PNG  IHDR’š%ÑÅ— œIDATxÚì×C²žA†Ñî߈m{Ûöbg”Ê<•-Ç|cÛ¹çT=Zón€ÿL3µ_¾à“ZiLšž&§ùiUÚ“¶¦Õ/Çk€h¤Aš�¶§séFº™®§‹éjºÖ¨õD:UkÝ›ÿYit›Æ§qiü˺oÝ`º©•j�¿H³?Ä´&M‡Óþ´*Íi4êäA¯3!Íö:K‡ýîŠA¿³eô ·¶Ûn­l·škZÍÆ¶¬;^KÙö¤”Í¥”-/Ï›Ÿ¦¤yiI–ˆ;ËçO{Üí´Ê­Û÷Ê“N«ß«¦Aš�6¦irê5u\»ÙÝn7{y?eç,x#ùí0üv3ÜðåÏÌefaŶ_£ß¦øŠ¢23Ó1ChiÐvß8Ö)e¢¿ÒÏx¼�%ís?{&‹ZË9ç¾ÓuV:k¡¤ôhÇ@cŒ-;k§¢´p7•§•’{jÞ÷Šb_+uó`\îNfuíŽïY‘1™s¢Š1�’´áØ!&æ?�˜˜˜%ò8y’,“g(ƒG(ŽuVG"� @Zë`�ƒ’@<õá)߯•„” %­lJxnxpg9®å}gÓ²®ê¦kZcù¹klÏþkA(i�KG.�Kä€×»Î˜Ñ‘HÚÎ\çñ>û¯œ¨V.“o’ß‘11w©Hbbr2Oúá|LJ¢ˆ(bÈ,\³äd>ßâ¿›²Cž%¯[!ÄJžê¢—¥«‰– Š& $¼0(Xo¥t�+/Ž,Õ�âh,ü9?çÍ!ðç¡0«߆ܖдl@Y€ ¿ÓúqÖ<¦ÇìAÕtך¶«(™7Èùùz¨Rbbî‘ÄÄ’�•ðEüH8NÉa�Á„ÔD“œ€Ù'§Éo‚TIHAž ['–m.†{5øç#HNzÄIúäAò yy” IO+™S +ý<)¸�Ž¢Ÿ!O!—ù±H¤~yJ+_uxi�üS±.T!ŒÂái�†ó÷ —}ß”â) å‚ñ´ruÛ±¨1cžïqü-×ÈoÉWÈOÉÍ;y¹+&Š$&F�%²Jž—R¼ca.Z)¹Y7�hÚ®4ÖUÆÚ‰s^(–d¤>!˜³d—ŒHØ";¤GÚ0ö×ä—äÙ òiˆÃ_OAÖCµñ0d-Häq„Ísb•”HSUy6·¼0§ŠzYòg…ßHwŠý’�‚×þ—á\¢nŽ„Rc÷`âÅbŽ«¤Ž¢Ù£X(Ü"çÉo¿‚9 11w˜HbbÈ äƒËóýw?¸±ø\žh]/ÛØÎØ–RiØ6üÑpɦkÙ:ãàZ¶å‰'‘thk¢CúÆä ¹ø9¹Ž'd�ä¤íòðöò{_ÿ܃�n­ ¬[TM‡ºí0šÖ8˜”ÏjLÊÆ°5UC‡8tü1㘦ª»ž±V°O !Ø ¥TB`h�ËX^°JÉ‘µ®äõë’ã´VW)ƒC)°Ö»Àû¶¬Œ˜B !P8‡Ì;p@Ÿ‚H„L¥ý\- úªßKâ—§î¦Ð~¹�•ßñÜ’Öc(™3³²)9w%€«dFê –/’Ÿ &&Š$æÊSäcë˃·½ü‰­­×?ÿ ßWpÎÁ0Æo{©Ìª»‡3”M %�÷(7šV˜Ìw8­?çÿsm4ǤŽúü²Ža'MášÖøs!ˆÓRò‚¿§•P¼tÔ)­sÂ{{3›{ X^èca® ÅßãnçÇÏ/+>_¡P.%�÷ʺ)9¿Æût…s6åÐoø ÙCLLIÌ�”¼Ž|”òxÕc§Vžxõ3;ò‰Sÿºç÷›Î¹ï„§âJÆ’‚€]ò ò-2!1ˆK[11mh‡Æ¸yîA¬i%Ó!×ÊóDãïE@ú/9Îæz©o�u0Äo$×-²ääÒ¼¨ò,ñ›èÖ9Ô3žU)ø9/Åñ÷k”’A¾a®Òü²‡VŠ’n ¥”Kø†sî‘籄-ïÓy*¸l•ò‰·‚ÕG¸Ðï- z=Š#Y,rIy`H‘³*Åæpk‹6†@u}wÜßÍJsäÆŸTA1ˆILÌ€¼G¼÷ÔÚâ+Ù\~þùG7ÔÎÚ"Ø÷OÅ„·È¹d…Ë7G¸rk>ÓJj¤ÉÚòÀ åOS·Æ%FËcˆð¿aî×øý“,IÀ®û2áïyyY‡?÷âç‰sLWÖÍ´mÍaÓuµ1Nèq˜£xFEVôÒù<Õý…¿o£Øþ�½·€ŽãJº€ëu3�˜ÈL±Ã»Yú˜™ñgffffffú˜h1»á8qÌ ‹yßûžwú(¶dË"ËvWN¥{@㙆º¯ê… „ϤçˆCgpLÈU…±UB�ÇX“?}÷zëÃë³7ñ^<þúo<\‰$žx×€–� W®ýù´Œ#$…"=Ù+ëe™[Þg±ÈpV± !Ahœÿóâ·åÿ¡ÿH™ö#=Ðß„ÇðÚñáü«Ç‡ …Óc=4,»`ñÉ{ ü"wç×dzq]ÖËur&¡`€F  ²mæÂ]¬o4Ù‘Ù_4|z4ž¸éÙ&ÃËa�cFÜq’ ]E¸ë#ŸŒðX:@øJ›Ž=½@e"”¸Âs÷­Ëwåýk3Ë«Õ{ZXqÿ¯Cß9:}Á<ñ€Ä›‚f…Â8vR(¬f®›ø¶ß¨2Û”€ªyO«/ŸòM ÷8Åz>­µ½´VîÎ-¯—®ß[¨Ú–Õe¦�PR0Èé|:‘@Ì=Š4Û Ñ^¿àTºÃ]8;Þ{öüTŸ¯!µ‡¶ kÑ3¹1³,KkaÈÊï'g4Æl‹8Ad£RƒÖMG^Ëdp¹ZvùñHyœg��§Mˆ¸¡)€/û Ú¶¥Üí Ã’ß�\¾5ßœ]Þ¸�óSù— ßO[< ñÄm’7`‘…›ý‚ivXÚÔbdªMk�ˆR¢a êà”¥Ô„I¬,r4´°êtFðž¨S Žp‡ÓavF´^RJuœö«•.B"¥ÑpÙU°Ë¾�@Àw{ni}þþªUÖiÉ…—¦úG's4>{7›ëã›s,j%bY¶ôfLýÝJ´�lvšÂDCxSôÀ^t�$<Ðn…@›Àq™ÌÊxŽû(ÚÜ'Ò¿#ŸÜž—÷®Ít�‘w�?‘ úçâÉ¡ˆ7!Ñ›5@Ðgæ€÷C' ¨ø ˆÔ6�LÚpT‰-ÛŠ`mã‡C�0›8j;£j(l%š 1!¥F³ÅU¼(†,&-å6*”D,Lcì‡Ö:êÒs˜\èÉ%;øü5¤å® ’½¶Q­·4>ÄØeB£?Ú—¥á‘Y�÷Ãï¸RªJ.Û²ÀP‰�KÁo7a·òE&à]� Ë�Ú© ŒÇs<1\�žLÌ­ÓÙ'a8 QlTàzÍa¨:¸Ð–'žGâ‰eãÓòÃÌôàª|Š,oÔd~¥$‘ÞÌ~xþ†‰�œl8=·j+"ŠžIø¶ø·©žlfh€Ñ«Œ:ÂY–S¼épXÈÊ:`>†êpl«æº ¸‰žìZ< ñB�YÃwÛ4¹/�0-GrB¡×± �ð¨;jYªÂÜBšn ’wêÁ`<íX˜!&zzfì+Jƒ‚ºzH‡eXŠ`ÀÑnº,ž#�à}4ÔJ Ù,Ë­�ð "XÒ,4�mþ ²½&ez%ÙddßV½éDXŽ å…GëUnÛÐ&àÈïɘ¼O ˆ {�›Ñ°_r‰Ã�CŤ¤c¡ƒ·æÚ{v%^¥‡í>ë‰$žìvôkô’“¨9_}DN/¥¥”8qÿšãÀãhÅ¢¡8�jO³Ét4èRJÅ Ì9 ' —3˜Ô¶†7ô … … 0Û®Z�žCµžâáû° ¼ÞŸ%(½"„¸�¨M�Å5†·ø;”Rô†´IeU/8štº{<T£óÜ8Å¢…tì�Ž�vŠð"ms½o@מ L<ñ€Ä“ˆ Y�O¤×h~ÓxÙ”m³ÖA%ã‘h:k÷äRݾb:�JDbµjÃ?2PŒ´ öÆFE‘ûy"c LG]� �‘'Îàњ߉}¯PÔÈ~O keö_‚÷$û ÌÞlžQ� ! kLŽg¹%ôLè]¡¨î…&D8žH«%á€-™xØñDÈY¡'Ú¡qIŠ™3À²¶ÛVþÉAÄH¼Ùæ§ ÇMÍGï¦mÂ)á@A†¿˜OYÅ\Ò�,$;�ŒªÞB:¾#’D¢¯�–;wæ$ J«Ù’»÷Ü•ù.åPŒ+ÂlÔ…ÕŒýC^#½é}«.WJqe�Ôf„ÏP¿°î¤)­Y»€T<çsR› Èò¹”ܾ;/++%rψ0ƒªˆßVªÖÈe,¬–£ßGcFÉ€€G½ ÷�ê2‹%mK1³ cjð •êôY’Œ'À)%"î5Å®ËÂÇTKY"jOjlsƒ…„Ï<\�^…Þ|H<ñ€Ä“ 4nôôkÐ Â0ÂU©±ábt¤?žé±G �. Ï™Y•ê=•Œ2SJ”¸œ„~ %x $ùôý%y†p„ ½ð²ÖËUY/Ud@8TL‰å³w"±,µÝ¿CoUõ¬*SŽ•XôLÀðØæŒçòdi±V$ÑûëÉÆ�&Œ‘‡›cvŶ�ü ùÈ8n|Ìp¤â¢Ædíu¹UêñUôhÂÙü�î&�dñañÄ�H4蓆@?ŽÕï ¸�\6ÏLŽö_;?ia+cCI'cC”S¹Ü:)’¯Wa «µ†ûÞgIЗ‹á:­Ù"žÓŸÐ;àonÁxY]áßšç\`Ð ¥Y$’Óñ Ç0RµIK†gB²ÿùæK"œŒr�›³‰8šX´¸×�b*8Ã�݆ËVsð[b) “u•:Þcµ?‡ƒÆl‹áªhÈ’Ç� 3´Úhܸ,"M¨³�‡jO< ñÄ6rрȴÏgÛ½Éx8—ˆG²ÃýùÐK§Gå /—“ƒ éÈÃ@°#`PJ&äGž¹X½Å6,%8§�01|÷¨fަ/W¾Zó1ëV~«2Æ®Cc‡ôdùæ½ ø™2-J5ø:{nùmG¥3ÖžGÑZÓ#Ãp¯F1�qÂ}N‡¦^wí®t•:ôî8•ÒÍº&A¡ÁZÌ)¡÷§EKǹÍî> pAÑ€(@=ÆA·_Y\-/!ýwÎÜ/5ñøŠ$ž$ ç ¯™l¬~ËR9¤íæ�²›wx�êõ—¦äìñav›u��]zÁ ŸF½ÜåY”0¿¿%e€ÁÜr†ˆ«åíÒEÍjØ&ç�Ð [¬`Åì4\Š%¢NOt = ¼‡Uø¬!ÁËÂ6ò¢É9-‹ùfèÕóÚG«£”,öfâixpA(�¿³Ó^€@�¢Å°–â>úkr»Î0 M¯ÂÙûkå:÷³ñ°ã]0�;›=Î¥ -¬V4ôx’ŽñFV„[žGâ‰ç…|Õ„±zE¨jxt ß7ÔŸ��?9¬ÎŸ„±¸*Àì §? ‡ÄD%ž5¡q‡ˆ´DwËðPB’|Ä )­…VÉ4xa¤0j««ÜŽÄèbKN«oÂŽáRØH²Ã™& m¥bz#)·âÿ9êèK¯µ^Cÿ¬~wÔOà®ã.� Šé™ÁKãûá%óu5³ùZ£EP(5NšÇ<dˆ‹£„]û}ÊN8¶Ñ¬Ï¯–ʦ-ÊM3#ç>´+ž¼€@âIܤñ~ÅT¡§³©XñØxÿÀ@©¼�s'‡åÄx?Sv�1¤î‡ÀHLžQ¡Ñ†a#GQ®Ödu£FçÇó"3«½K ©Ô[4r±v€ïEx‹Æ,äÂV›ÏÁ ÒS�gBãÐrÓ�1)–�›4‚M‰GÙÿy i±<æ‹tÐo øéc— P ã1��aÿ3L9$¨hÍÁS<ðh82^÷ÂâóJ)~Æó½±Ï…B ¨7©kYZÛ¶•M€mQaü PçXl‘EÅ�—9¾ÔFŠ×Wòôb¸ p>‰æß´M«Í£êFX34½¸A iuøæëÐ+ôHlå ‘§¢§¢\"ÿ9rÆÛó³i¦©$Åß©½n¥Fr� `²Þxü0ôZ@Ìóã3¶¥Ì{:�P€ü ½p)[ài@°³»t窢5 /CoC+âÉ â‘x2½±Þ€~) ]BíǹWÏOŽýâ�½‘þ›í|_{㌠+KBL­ÔÔýN,¤Vªu—t‡0I]Ú25š—ãcEÇuPDW[Z*Åæ—JÊÅIµÛ"�š0–'GÄ›i›´ì®&Wâvh8¡.Ö-cÑSǸVë&¤5'ž<'‰Î: ýEèß ý»Ð0ðBX'z‹éÓ“#½‰ýêEù;~ó‡å'¾ÿ’²‰§MH3 §\®‘™™[“��ªnµ:Ï4ˆtXÜÖ–Á¾¤ ô¤$C#¾ÑþlèÄx¯†�¢A2õ nüÞ“##nBƒ©t7£§Õ °,­WÅdÔu´f8köY®ñ<O”�ô´!ÒOA0ÿ<‰V¤¿˜ 9©¼_E&ÆÚJ<2i�òÔá+ò1ÈÔbXíê�Y¹~sÞHS=ëEsÆ|."S#yg.ˆéweÉÂrÙëþ2› ’Ø5z4!Ä-ìÌ­ÅpV:î´[au½°NÇRUœËiÖÒâÉ3ì‘x3BΘ̬Ÿ�¾d €Ñw cô^>;úÙ~MP\(ÇÑ ©¾G¢À¯Å6-z"ÄšgçV¤Z©Ê³.¨p–V· ^$%“#§Êœ¿o+Ø™Åuùìö"Wµ7èÈq$“=Ñ¢»n望BŒ–*,B옌0,MÝȬxây$ÏhEú iirŒ#nE¾€jðQ¤ôFÏ´ÑdQ&0qkBž¦t5oNz!>¿Íô^%Od–YZϺ€![!/rñä  õ¦”-‡�åä›÷–è�D´>¢}Â|äû7FÔ-â{áB[J'›C–ìHÐjµXC"À„¿îˆÈו’5/:éɳXrÞ�éy3#äµD,‹­P°²c˜«n&">ËÓúÖ†ÉxH^;7,çŽõ‹m۱В@k5ŠÖG§'˜©öæ¾e)‰mÔGÃYáyq³’Î1a¹"´¥¨J´É¨c#Ț麬d Þ‘›ˆx¡­gHüf›¶mët"9>$êL(|ý¥c«ô % ¥º jÆtiiƒƒ«––7°’o>óáåx"]é¢+ò €d”í]Dkj.c7cN�¤è#ê� ä©Ð�“=¬°ÏY&HÔ`Q%‹ò°Ø09âEóL(�j‚ æ•,Ï.m|„ûGÁ€oåY zâIÉ Ì ÁœëAV<�˜ N� f†pØT�MŸþJ½Õ6mº›-z!µzCר¶ByV¼ûáÙ&\ÕÅ|–¢~ãÂ(�ÃxÓ<‘êaoýô€|Óï°¡  "� 9�H0ÀAe±pˆ•)(óûão^8N¹ëæf;*\¥Ù¥ÒeÔ•ÌŠÈœÿŽºžx¡­g <Ç ¯Ø¶õÆ`oöMŒº-N�õÊØ`�^È(¶0O»�ó0,¥XS X/2ç41ät; Y—Õµ²´ðúQ€ÖnëSçÂýZ«S91Ñã{ùô@pb(oBAî¼ Ó+«#«UwÒ¡ˆ>ì0< £iáâ‚ f˜\‚z .ñøLØË/P•eœ/­Òcåµ³SP<¬›�Ö—´p [¸¶ƒ–¥|ø®U1íä=yVÄóH¢Ð/@ѲÔOõå¾0>T<1ѯ9wbDP+"¡ý˲Ÿ4ûÇ%Ö,6Älfi‰z&>Ÿ%5*GM”1žbŒH*“|6É$xz4°‘HÈ©J×ùl¤ýæ…aß«g‡…á+ýy¾^ª³R?Ák¢hÀ%Œø9CH$ ûéiò ¸Vèy0´…×|6SZÊ à¹d,âüvv�îÍgøžfÚÞ[�ý-›³ËUM�ö/’¥úDKÆŒÕe­�'žGr”_4 ò–!Ù3ÙTüXOf³Ó-‡yåÜ„dÓqa†Í>S× êîV<&³Çâë›—^ÈV±Jø{i ôåÒZwWöG4„ÅPNÓxKI`Í/Z„a:ÛgKµQS_¼8œ¼xjˆ ²U] Vù,@LÄBOp=ˆ¬e3‰æ¾Û’[62Äwqßðà~8èl-q�3gÀÙâ>›Iñy¶Xo4Y‡T©³… ÿ+s~þA„jêŠÌ÷å@.þ»~ž‡ÖçZ›Yõ·OÛA‹6aL\6DàÙ…f6ãv=ñ€ä(Ö†Œð˜„L8kÚF[“qddM�;1ì{ë•“rþä‡(i}!opSÀÈUj§Ã›] pŸF¥õ9 aÈŠ$­mÉõ[3ŒµT˜¥…¹#ÌË?Â@B ŒÅÂÿÁ†‹½½ü[”eñ¹Åµ’¼ñÒ¨¼yaœ™q[�†‡ü6�²¥,foTö�87çˆ\‡9¦4ÊxžÞ ÉÌry¿m»ƒÊl›Þ€vÃ9f(¯§�‘Ñ‘>~Ò³.K;Ï+ËVëx C*w§ç÷úÝÝ0¬!ö d¶9Æ&œÈl¿ÏɦE³u‹ sÐâf¹Y*h.õúQ›àé‰$4=gªÔýÐ^S'Ç ×€ ‚L„âûòë§3DÜøýe[o`­ßïç­�-·Íj48+B(  ™bjiKVà�`d.¹‘+צY;‚°þ¶ëþýIà‡�¤«q†€ÂAÎJÀ'òÒ‰~ü>"Û‰YY¯³L“ÓüöIÜð ¼i9CÏBÛ´`¥Ï*l“$@�Dko|Û|†¸cf!4Üù\JzŠY™2I×á{³sK¾baÁÖJuÞýèšïν¹Ý&Jðßéê® pøNÛv¿7Àn3ÿ°é;Z†Ï±OQ›L³&ø�B/È7/K)Õ¾_<ñ€är /AÏ/¤×¨�Õ|çµ “'ON ä{ )Ánæ†ìkÛ÷Í@@%?bB[~€``á�[¯×…¡›`^FÍ}¿)bƒ—”J(àT­3,´¸´.¼»îlˆ£Éf“4ê<ç– á¥dZÎøÚo~÷òž'†ü~ ;.Á€a™qÁ¬}¡÷ ø»”0<¦A‚+„�xŒ7×�Í#ÙÓ5ÇëZÜ$ Ÿ—OŽ˜xd{ÞAGÍV…a�À�œú�/žËŸèÌST®ÒtÿD»)«4î‚­ŸžG0`èÃ<¦Ú¶Ï}?Ô�¢¦SÎ@§¨,,­I<vŒ— ié#Ò¢á úbšI.›pgCBÛrçþ¢4ê5ïÏð±d!;ί·äöô²Ì-mð÷Î-"͹Òp³¦ö3ÔB£kq…îV~onñ8è'™Ž}?=CÃG¸œ„mtsbE"“T*Îß ®„çØÔœH Ÿ116hûq’WÖJ{ME&áïóY† �9I~o¤Ôdð·J‰Ëåà{òuÔ»„²É¸Ja‹†ø;Ã!Gém|ü þí.Åå•LæžO‰*š&©¶xrDÄM›N¯Ñ¨R*ƒ�S'ÎÊ!KKN@Úâ ub¼ËÁBB¡�ãu˜çPχ%ŽðyìS0ü;)só# À½©ýz3GN|ưÂÀ2Œ•IÇ h¿ï€ _‡Ð«øìæ}IEý’KÇDDmbvŠ]Z)3Õw~¹$ŸÜ˜“ûók²¸R’¥Õ2Û¥ìƒÃªÌ ½kˆsK¢0ü Ã{ÐS �áë>‚ˆkT]ƒ¾É+qiÜ¿S‚­U®Ô,$¸ÜÌ.@„ È�=)rHôPðÚ6ç#L`äïuAÏówñ{wr„ 2Г=¨â=šò¹– |¼9ĵyëó¹ž�&‹!,&Ølz0œçà÷Á8§øØ�ý£"”¹š�Á ôçel¸Gú±ÍL6W-Ï.®Êü²ô!¤•Œ‡É�˜ù$ÑWÖ+rgfE–×*² yïò´|ã�›x+›½V£ó˜¯Â¬�¹Ol^·mª Æk¡> xÉýÛx,B¯S)…ã±}ƒÏšÖ�ó™dxu7žŸ+Ÿ³” ¹ñ{»àï‚à`Ë¢Nþ­ðZTjógtÂ!T²À2ìL4Lã�áo4u1»pmšï…¡ñÄãHŽˆ7’„ÆÍvšÀÅB øÔk¦*Ö¹:>@rÚpBN>Þȓԛ¸j>ƒ î€!š>¾ÁÕîa-C˜ªCy)EN€ÇÖ‘B>%ý}YH„ähÜ6WéÏHt§En«d÷¸ÕŒ§±´Z‘é¹5þžÕ�šÜœ^âß¡ùn››~«E¸·ìJT‡Ü»5 ôÄ‹Tä‘]}òü{)�t6“$ lw�¬ÃãÊgSðÜòÎêÆ_g–R ·E hïè!£m~�wÛtI 9O@�Ï�"?ǶÜ}€m|W3Q€ç2Tðµ8�m~æ“ÜSÆ+âwÃ6ÖÉ•­'žGb›•Íè%¨37ŠŒ™7/ þÂ�¾ÎÕï!d8Ñø“”…áo6›Ô'7|ôP$ƒ ŽJ Ú[ªN¶V«M0À¸u*غÞÍaJЄ¬`è�$â¦ñbkø%›Àocz�F,›Š‰l"¨1ƒ„ŕؒùèê 9’R¹A~„é«»:ÿ¦0–#²q¸�x„Å„4ÙZP÷½ô>¨|¼Ýçê·)éíÉ2lR(dh¬�Å4Zrz=Å kч5wƒ€‘ $“Q)Ò’Ï%ñ=3ø¾1~¯„í\îÏ­°B=›ŠÊƒrûþŠ”áyÌ.¬ËÍ»KrmãW>¨„ÞõùSð _àœzB†™†ÓRŠFÜO²™`ÂׂZµ]2gŠ¢ù\šÕãH÷e �tÛ¿PÌfCóͧ2+ã ?Æ¢Va:- úôâÚ³DB “à[ o±2º¥¥5¿»h·Ñ !€ 0$í‰ç‘<­4Sh@D¾úÓÐ>h+¯Ú.�ðÿÔ¾Œy"½ûÝ›5 &VÏíVÞž£"Åú(C^»¶ROÊßd@ �£G‚p�Üv«ðUé•Z�™;l§ÎC§xLw§'òñµ½ƒˆ)0t3–DkziÑ ø2©Ê…ìú8%B¯ÐpCñèc3܆pÌòi„¸;[èhC®3´”ŠGÝ”ÜÃÎâSŠÊÐ%Ûí<(eøf¼™L8f‘ÒÞíyñä0Å’|:ÎÕëO~å%ÇXŸ³,õךö'~è?Q’NÇùÞ} e‘k€ñdli½4žø�l-¬R_…áÄñbŒÞ²•¡Q\X)HêOHª+F&m×5X"š+wvçÅ–ÀB0 º$óÄT»;µAfhe³)zf¬†<(PMj7õ ï¦�·Ûnï,7÷)Žfƶ<l©oBŠn#Á„u.âoô‹©_aÛ}s;… �dÅ“ÃH ˆÃÿ×ÿï7äÍó“ñl2úóJÉ�iá…¸býö?ô7ÿÔ«C½Ù¸É½ÿmDX\‡X8+¯çÖœö%œPØh´Œ²ez.½…jÕ =µ}77'S„I¼Ça”Û-öH’"[ŠEs28Päsûùë^ aõÓ2ØŸ£ÑÜÉoÑЕ5ªó3‰ˆô’"òù´à x$e€Œè�OŠd…¶)4u ,(DÈíz6+wzLµ_©Þ†Š ¼×Cƒ!-½3‚‰Îgðï'Çœ~c�Î* #¬ @Ê€áS@`0ž H“»ásàÉ£,Žt{“ù x/~oÈyMÙ–Ššâá~ñÄ’Ã"×ÿ�¿ó—Ôõ»ógPýKx\ôYÖÊë¦îü£ëOŸÍ¤b}ˆÅ»3±Ñà�mº÷«E¶iÝÁ4Üù…U’¬0¦‘b�-ÓËØoa«ÔC†‡½µÖÖVñþ �e�¡&fGõõæØv£Ÿ†1 …ƒ’J²=»’}Ó¹8•ŒÊøX åeönšž]XTYÍ^ÌÆÅEƒƒêõu€lgGYgfåK5rfVˆß5l0RîëCîî?gÓ‹H¥ãp¤ï¸H”ž“ñFΜ×5>DéÑ…|,,äBÁ6ÿÅ€™åVÇîƒÏ»ÅœP“¦lxò;T>Á¯‡ð71�p?4ñ€äîܲüú?ùŸ¦7ªõŸGd†£q|¬·ó7þò×Îc¾È°3»ceµ,­fÛñØä°\ªíÛAxðß@7^¦³bÔ-t]Që°2¹\®;†“+âÎmÞµîš0×À¨Â}¾¾‡6¹\Zò… ›*®\,rÃ0¬ýúÝ. :¶,º4+Oµb–ÞÚõ;säINŽ÷H>—aÖ4’®÷ø¨ $õ9Ž‚ä­ ¯pÅëz#(¾c‹îrg„샘¶'L¹ö‹GÉ“1W©�aºÊÁÃésG(o÷÷Š×ÿΨuÚä˜Êxž;^>ŸiÅo2Ë"!Öê¸ç$ �„ á GG±0A‰(ñäÄ’›÷ƒ +¥—[­Î—qq¶†ûòêµ³“Xáä#>3» ¿"s « �ý~…3ð9\å“©8 Uâ ©é™eYÂöÞô"Ÿ‡áä¿‹i†�³·L°˜öñôJ $ä÷ ¼I�M #>ßãv×!Ï9î"jŸëcÚü\Ø þ[;3f,6DFÖ*�tb(Gcüà ? pû(�ÄÞÔöÃoš²¥�S@˜Ñ%/âf.aߎD˜}ÓT²e R~ÙÚfç¢Ü67õzC®Þ¸÷(¯™Æy³WeA�‚ØØpms«5¹*ôë"hpR$ zdR‰"�¤8Êâ’ Ó <9`ñ€$ ½€•Ï×°ŠÉ`ÅéÍ&³±�?43·J¹soÁ!Âé-ܽ»ÀJix&ŒËîSú/oxTfnÁ¨rå¿f<üû°{÷©ð˜™ƒïÃìü=|†“2þv�$üæaJfÛ�q<(P¤g26Ò/™L‚©¨¸�÷½’¿‡à ¯Œp§Ÿ�%’pÚ«÷¤2LÇ´ ¿ô¨�’©¨¦!Æ–@ÁÏS ØÅÚïÕ;90¬¨é‘ ـǽ¿7ÿ$‹7«.‹²›1Ž'ÏÕ6\�KÌ3�9tø™Z�N ÇGÐ0c‡<7¦í @ÄïwsC^w±8D¼Ò›'™zâÉ7d|úƒZë ÜP¸VíH"²lKÉŒ÷Úz R¡AŸ›[�§P¢á ñƳ\Rw_Våeò!ÏH´r&ëLå¬S¯bUÕìÌèµ Ã‹ï[Á÷W@.€A¯dyy™á.ÓGË'~W¬im@ì‘ÇT܉‰A§E9¿�Fo¿“y¬( !>FÎÑNÒ›�³U x¦-Ž+{@=6¬Å m*Óz B&½Ô-ÚSnƒÅýŸGÞaû›S~ᕱvÄðï*D†9-L–Т&ÅýMÍ}¦�$¶G�·¤ïÐM«FÈ�£EŽ„ÞàçZØÓ+ÁÖT¸ã2åÊ)�ˆ'(� ™>Z½0´Y­% ×8Ø“KðÆ«7 gႤ§CÏ7+ÇÓÂ@»ÞÉ~L@¬ÕX‘쬠Íg7h0$á×N³æZ]-sV÷ ¶ ¼é™%<æäCäZ�«{€ÉªÌÌÌ ÙWŠÀ´€ ö'X|r¥|îÌ$ ªßª1%€°‚˜~ƒ™zLkŽEû­é†š&†òl õÖüƒ8È•¹<ªòÚÔ›�¸ 1KˆFÕ6„.·6·nè :ã83±¡¯'Ǭ«Ý´N0< 9–Á¾½H•~ŽD7óNÜVïJÉ‘œáALï²MãˆF=A_I–­€7VÈÌOH¼)~hÂx%)-:êÇ:Ò—õ;«RˆcÔ:êº$w‡Ä.pTÈ,,®I67p{™í­4VRðH‚RPÑZáyÃÐ ¢Â¿Kïâx ìIUƒ^´×h4®ß¸o Û|’HDHÖç8¢5CÝ% ÏÈ“HF–­è9 8–ØW ^ó£Â@àGCOàX¤“q¹7·Æô_Sg FÌü åîÚ,æm®×ÁÙ! [ ·xÎíØ{ÀB~ FŸ!D¤_ï:̤„Þ-ÉöÕu§sƒßÝ‘¹°hšÌ?fŸmâzŽ*�ÀïDz£¼Ç|&]^㔫œ‘aèGЮx²Ïây$Ê䙟‚ÆPf�¤ €QôrbØÜ´K�d~‘¤;Iox f6»ì>SJ @¤Ûh²o› âÖ«¸ Æ-kL®Þ˜e¨­\­1¼5;»B€Ák {ÝŸ]v^'IïÞ¢\¿9#÷ï/â} rýú]��ÜÇ?hš?îÈe`*ð÷í5 G¢ã1‹ ŒðøL­»%PÁq_X^gü|d  •¦pÊ!¾—ÞJ!ߦâ\‰2­Þ¡fÞÔçŽzÙK?2—ìGg’ü{‘h”^¼æ˜\€'9P™íç„3¶Y›ëV‹˜’gA”ÚÁëÊ o…ÌB1pP6ÉHÆ _ƒæa8¬ñÁbßɱ>¡�­V8†·Ð -ÃHDÈO@0̵+Ñ¢àå¶ Àh-›A…á¯ééy„´Îf€Qbø Ü ‰ø«×ïL�:L0¹ugŽ™^Ó÷—0KÁ>ÎÏ/\Lß®úŽ ,{aŒÂáÐ5¤ä1¤·ÅÌ´23Óª;aø žbo!%çNÓ“ÀÓ‹ø\Èò$“Œpõ Ãò`Ût÷Ü�¸U{ÈÄj´²Z]ãÖå%ÄýÏt"h‰™]þPûÃ]í‹ÄJûpˆa®“SÃ,l|ùÂqS{SZ´½©Ñáó!ô>Åx[!¥”_DrPl=Ù_ñ€¤`ºúN@cC½#ç§Ðnëñœ œ›r £gÂZ‰ùN»³«RCÓ阅VO<~ŒÉvjJ†¡6bah•ƒŽLV”|úÙ]½°°¦oß�—+Wï2ó | [¡ÀC1€EÐztNØ/VÓÏÌ®°²s[+öÕ"¨å‘ÙÔ“¥'À�õ=´d-dãÒ“O¸1tlÄçf_‰)4ô›–é»&Ñz¼¿2/w–§efcyYVkk²TY–™µyÙèZRõgå÷¿ý©üÞ7>’?ùî§råö¬”™YEï€@3?¿²Þ½š©ñ)à €+ÉeRäLFŠ,,…¸µ#<»8W®1�‹A’ôk*;îµ”ßoñ8’˜ñD¾-"¾?;Ñ7’MD¬Ý<]f?™•|ƒóñÂçê\Å.�€fÅx&“1>¿ëÕ¡v”¡0¦ûnZék†åºÕ®ÒZä;oDÎdt¸×Éø¢+²!�¯”lÛ£Éï;ÐêgzRø|ÆóÑcŠßm«—¾bFz3jj-y“x. ɹc}òÉõYNF¨2#‹mà•ÚÜJ}O4�%>x‚ZºVEÚ±–˜sh@"(?ð?onB>üì6ŽÿÛ|íò�)fâréÔ�L ö9^„ ¡vö:ò˜ Ñ®àÄ6++k%x’ä÷Ø=à³kw¥ÕlšªqkÇ`Yŵß4Þ¹)�u›(Âp3ëíi‡É¢l¢’åu�žV#„ï=eÓý´#žìQ< @/ é±,+:6�/ôæAÙ™0Db cÑ¢…YF0P4p&LñDñu­I~š™ Ëœ­±ß+}¨»bk™ÐHk¬ü—˜%a˜YYì¼¥˜•s 0¤çÁ�Îà}XˆÔc7£m“°è,ŸI0ñ^¡3s�©¢Uð›RYù›{á¹S²V"h E‰Ëðã÷Rª5ZR­t¤cµñ]Ø8ðs³:š�ûwm9KNhamƒžLGÚN(Š^dÜÆÉã£ûÔ3@H±åM2!e‹`Ñ²àŽ ~œç[ªµdÜàòZ‰¢D1L93Püòh0(ý¹<¶ayŠÂú„ÉìÒº……�@R‘/B?†ÞO¼ÐÖÁí%"Ð0b㩞L<‰ Ù'uŸ§·°£E˜‚‹L*\´ ‰¨'&[óYgjaü0F÷Ò8¬,¯3|´¼êð)k4bØòwl'ÌK¦Dä@gÔs †[my,’ ŽÞu�qµÞeÙ²„ó°ŠPž¨ÏW¾7 º#zÛÆ1#eÅ8#|n/‰Í½Á-¸Ÿ†ü‡ÿÑ¿/Ëßú·Ë»ï¾·°zјOªí yÿúU¹ �åãOnÒSÜO¤FÝç”L�ÊùÓò¥7/0ìz ±ÏÔö ¼¦¯ü¸œ|å«ruzEÞùäŽ|ïÓÛòþÕ{r åoP…vMæ6fe¾´ ÍvÓ剙¡—ZÌ&œnÐ<®�¹‹Ð—¼Þ[{Hrfdî$4�jÙd6ÉÆ#Á'²L/EàøH¸Ã;1+]¶‚'ÙmYòÄ@Ò‡PÍ™Sc å„ OŠa¯Ûwg‘å5ÏÐ ¾‹û[)ÜwC{œVx°¢åþÌ�+‰éÇ:d¼‰Xvƒ«ôÌÂ:¿ãfN'“ŠÊÔHQ&†ó’JD¬˜°eËò=mf u<™ðx>fZ[]–+W®<8kßõ‚A[ºV]î,ÌÊÕ›Óèf0ǃ¾ÏãTxFls饓<[�*Y¯TäÖœœzõKòƒ?öSòs?÷sÒ××/&¤Å¿óûɱð72õ¸Ó’•ʪL¯ÍÈ2ø¡õÚ†,•Ve~cA*Íêa¥cº�ƒÃ!¿O)Õ4 ÉQÖŽy²ñ€¤×x"6 œZ™d´)·«3Vˬ&¿vã>Ón4"Yº°úPãÁ')ü;1Õ/££ƒ‡6Æ·Ûí°@ñÆ�iŽ×E­³�›ˆwp ­­—¤Ûiä*“ÇhL[FW†P¶“ŽI|˯DYúÞìªTkÍÏþ$€ïkoLéŸú¾³ÝñÁœ³R%‹¶H峬½Žvû>©® �´%J<þh.B)¡Qn«ª|ðÙyÿ£«4Öäò±feÞ <¶¼ÖV+(r]�…§T’wÞ{_>ûì3ùƒ?üC,6¦7€Ø'–µuÛÜú¢\™¾#7æ¦.k|î0D‹ln•ÒVJD¤iÚ!wkŸ<ñ€D‰i•€UŠ"î$ I ‹ð‚ÓúÉ/Ô®V (¹ÛgÉÌ[šó&Ýeè‡Dð™“C2u8!.Í�ÖÜÂ2�‚©´¦¥ŠéòË-¸ò(‰±ÀѲl9(étX7CX³Øà²ÅñÐ4„e`‹YÈ-±Öpü+õÖC@žNFÕ™©>Aׂ6€„#eQÌÈTY3wO/� ä÷–êÂSêˆ+ôŠB ?x­iðlÎ÷·Üçm[I¥½.WîÞÿÃðÖA� Û°¼õæ&^l–R½„l³EQ¾6Áíù_ÿùõßø-ùÿ¥EÖÖÖ FxÝoŸáØF¸‘óQ$ÏH2”8Ôì­{¦Þ-kÙ%ÙݺO< Ù?·”šF÷Ð!¸â9´ çʆs7 Á“03ÅY[‚”U¶¶s0ÓîùÌÖà|ŽÃ”–©&_\ZeòêT ë| mU˜"Œö+%]vÜ=@#@àðûýnÿ1ðP[ÙB’í–¥xÂÑ(Â1 dëT…òp—õú…Ñ.ˆù»øª}ë™ÅÆŽð|Ø~^wl¤]7\�Bcù¤·1¨ÌrêMúƒaÎ�¯]E†×Õƒóï¼|é”üÂÏ|ÍáæH˜oDæK‹ÒÑ7„ [HΘç5"n_"اd½ÄÞn 3Ń1ï-‡(ü}D±}]wjb˜êÉ.Äz^HȲÔ)Û¶˜ŠìŸ]7«ë0M“!ÆÇoÜš¥!.äSûp°øa…·ÌJ_‘à½?³è¶ |n €yÕ©†×–5±;­Öþ�èÝ™®ÅL1ÝdÄ´Á9evWšL&¤ÞR2»¸îþ¦ 5%þt2¢öÙÛ#G‡ÞT£ÞÅʽ l~G‡h|™”¼�l&$§�g¥@²…°ÏÛ» Pêx ’Ñá>$7ôÊý¥EÔ¼ÌI«ÓÚÄï¿«é|ü9 …œ¾^[_§õz×S ç%ŒL®6$Zô¡ Ç¢*%R$l…4ÚËð–'»HºUÐ!ö ¸¹ád4ì¤r(ÎÄ%QäÇ6*(ã !Ó¾{O+rÛö²GÒb�"€„À1‡ô`ÂuKróö {v�üF±à"½.ò@&ä>˜QV1]�AŒo‘2´ eÚïøP‘‹‚ø�T:+kå xHŠÙ:@Ÿ�©¯¼:a�„Ý÷Øiß¼ÉûvÓ uVem“g€äžHÉ«/åÒ¹ £�¬¹¸uï>ø–*ÇAKK£ ryQZϱaø ÞÇ×´f(žß²ã2~7CyH¯——¦ÆXzXÙ[>Ÿ�)Ϭ1êtç‘^¿’¤× x·âI> ÿ¶ß¶/9¤(Ì›üȾ´¯"ºÙrÚ§°— 2”Ýtp�1 •®’~x]'ÄõÙÕ;ü]èÆë< …Bq•0ÝÃéÍĆ™÷±éÒëÚlÚ2éÈð2R¦oT—F¤ÑõƒØµE,¿ˆ²¡TA-r;gOôÉ«ç†$ít´?'}ø>Ÿ_ZMÁ÷/£-M•œ‰ÖšÇ2ñ‰ßåE\Ùü˜Ç}mcCÖËå'Öqž¿wù3)×jô¨6}îè9Å¢ÓBß %„1zÏ›„kK+Θƒ¶Làøõó’ŒFø»¨U½¬Ì˜b†,éÑ@j÷æW¯â^lŠÈ²!܃TO< yòëL½ŠèãF9í¨Sñ0W²û‚!1Ýn›W É`ÿ‰Ûˆ‹e1ôÆ6“I²ˆmö`ÏRéO£ �J;¼È�›ÓäJ@²³¾IÒa/†›AÈ×4Û]×Ó3éÀ!¨½EÛq®�[]„uODD,lí�pK`±Äöä�‹cÒ›OìwB‡0MÁ€N %›HH·#¨Ó©ƒ{ª‚gª“€nµº¦É§f†øç:§‚ã[å{ \˜ |z� ;!¼óÑuùÞ‡×q|»ø A�ßÁÀñÉ´¼z±(çNçäü™œ¼¯ê�°½À…%gzh]ÆŠò+?ô†ôå2R®6Y°ÙÕú r˜Ü‚Kr¢'³§@e¦ÚhÎ\16„JI=iZ¥'�¤qs¿#3@A$"½¹½‘ý^Ñcb!IaÔ@LŒ±0•îu‹b?†ÏçN{5ä&1š™\]áïX@–HDj>È�(³¥ÿÝÅ”> À¡`´ÑìqF>üøº\GZpW‹¼óþîx]îÞ›çï’Cñ”iZVW+ìºlÒt-¨ý�BKgf}C´�„ˆ/L !¨ØAå'  ·™¼ra”ÇêóuÜRw)¬_˜,ÊÙÉAí-ðZCÑ$en¡"‹Ë5x r(0ºÐ p• r®,w¦7.Mcß…8Èøwä›ï|*ÿûïþ•”Öë’ 'Å'\»-„åÊü®à9ø=,KÏ$&ýPì?”¡5;_qþ†i·?ù¥ 2Ò—“™ùuvfn4;¼"1Å—þ¾¼Œ� +ÏqÅÇÇ{óçN $³øHx[»¯EÊIdn\RJÅE„a�¢’F‹áWÌ›™í²e7Vð&– P´75u´,Æœ�ò±“¥Ä éû³k ó[�G9»[áµ:> išüœ»wg9°Évž7)¹¦£«@Ø· |Çn³·lSèG“½¶VrþM �!AmºØÊaÊêÚ†\¾rO.œÝü;Õƒ+SŸÞU|ç»Ós’Ë$qL£"�†Á�€ˆò ¥«Pá=¦?¹>ß¹}oɇÏ$`Ãûâ`(€)Ã<{ÉÊ‚/]àwÚ¨U¤RwBGL K|ô:³É T�ö)–�sÞÝíà\wh�‡úŠ Ýí{fƒRìýG_OnÜží¾óÁuœ^ñ§bNø*$³K+2¿²&‹Õ:À®Åä€hÄð! u²æÅ=B‚c¥Ú"ïøÃ_8#o]œ”•µ½\«¼®²î�(Ùݸ�K³ îE€‹½°^‹ß™Yö£[tSkÝÿ“öÛòÄ’ˆRê5�ŒÚ–R®–ÔÄ,žSBC�…,VÀ£ý:WvÆ1³Ó;ÆSqZ�kyçƒk j›ô>`Ì &öĉËÕ &ÿ�R©BŸC2³QèÖã=ìökŠÍÜ̧'%M%OM~aѤ²Æ9@ ^'CKk8.ß~ïSÏ‹g'$ Št ømJA�&Bê­×NIGªú* 3™Çœmnö#vŸKÅ9á¯Þl± ¯Ùuº5·$è ÈähŸ ‚Œ}àâà×Jò{ú6&?ΛÌ@%ZÌñ¼‡¤Šwfåݯ9©ì68C$LÉ›¯�eaêï8ßãÞkŠ5Å…MGw™ê`¼r=í¶ÆµÞwáé'åçð¢|ß«'áqÕÙ$  "ôF@Ј’ç ž:§Kò<b:s8ì ­T3S£ý3zÖÛü²µ¨;O< F¶Ö)­uR ýù4W‡LZ0ÿƒÍñÆFz¸r[[¯Ê‰c#Òé´éy˜U+C+\²f¢"å�²‹i ‡2Ðeéç ‚¹!!4õã€"ß@­áo³_Rùáå2„ç„@åtyM g›#$õŒ W�·ï.á÷åÅRÛ{³¥ÕIà¯Üdgß—ÏNJÇ—„…pUíz&ÃÃ}¾‰Ùu©5Û$÷‘Pàò^¦aäžSQáS}¾„ ôá©Ö¹ÀøÒ_ÂõÑGÃgû|·�žì÷¿þ1=†LUàÆ˜ˆ±Š×Ëõ'sgñw1ɃkºtzHÞº4.HhÁu_–…•2þ�*B[Is_¼*Ã_à£Ñ°dF��×:Ž-ïŸ3_F)Þ?Fg!¤�ELHY›ÐVd§d»'�¤¡u¤-Kù2ì»ã;´¿–Ó 6ØíNƒï¨”ËÖ¹Ócnf¶† H«Õ4u#œé°`Š¿,ɤã0C— ¬ÞŽDúX$/.}„^ÞÏç^:wÌ1Ìö(² €!Æ]Rê¦xº«î#.üžõZÝYEHŒ<ôò™$Wû0 ŒéÏ.®Èïÿù÷8vvb¤—ïÃë*¤PȲ}½Ö$!ÝØgf¨1ü¨»nß±'²1iÜÉD”Ÿ‚I“r*!xÇh˜IÝ(×Á—TP¼Zex Ço?MÒ3�Å"P#¸æ“1ü;k’IÅùz>›r¶¦ %Bwð¤Úˆ#a«1»°ºˆ{B‰PÚÐîN�ÄHFá�¤ŸŽ[J±ÊáÜÑR.U¬�/ßÔ00ì¸q›]¶ÊPŠÀÀ"Ž8á.šL&A€ÀÅÏÇÛÅݲr‰z® s¹4I⥕5Þp™pU­”¢Q0Í Êð/@>€¯qPÑ] `¬¸«ÑÄpMJÊ ázl Æ!¢9�ÇöÍ÷YÓÕÏ÷ÓHÓ–(M/q¨?#“+×§y¼ÐŒ‰3O¤ÅnÉOb•b‡hc€cùü»ƒy¬ª·5œãðXú ¶»ggâý®ÜQTŠïQ`sLpiε§aŒ ñæk�Uï2�i—¡mˆÛ"GãX5ø¾ e­•j|n@„[|?~gdeÑûQ<Žœ³!GB~Äǘ°çÝ„–1´®£|!+Oúo:iB¡%‚‰'�/ý7 B8«íD;´ÖôF”¾´±rŒßµF[,Ë6ÆÝo¸ÁF�«Ot&ˆ<&îîò+Øò&ã<ŸÍϘ §@"˜ààd…£e ^A“ÝçM¨Y`h±2ÿÏî¦!G“̇²Æ  áñåïŸ2 �„üàWfä“k·‘Žº,|r‹‰ ‚‰ˆe;)Áqñ‡gâyÉf“ôê²é$›hšÔí'9@"-H8¬ ÊÐä£=›Þ&x/žãüÄé‰àA8-1‘Œë±á¾n>›Üúü³'(¢•)ðô‹ˆµ¹B”ÃÅ*P‡`Ÿ]Ø /²‹Z'\ÏZ\¿RKYð3 ñ"‘£ÆeÞåq´ÂÐaΊ™S”¹ÅU'I€áãùÕZ8ާqÞ"¸×ê"²a´)žx@²Ã°Ö4²´N´€ó OK˜Zê÷1 y‘Û¶Í­RÖ^êøX]² 7�aä¼ ÇÕôK8¯�˜ÌfRŽÑ%À£Æ,±B!}T=&Œ ÁNw?íq�¥h*Š^É)ÔrdSq>^_߀—±ŽZ�e¬ZW6Y6K„çÁbxg°?+Ã8ný½Ô%ô3Ü8ͤ“4rÐÕ›H"•Ðãƒí©‰!z�;$-%<Ž#ðFø­3+ ÞûöÖÞ‘®r‹:…Ùov€Ç.„×ÈÍéyï“ûôN¶^qáC�™5‚ŽN&pCx8º�×rÞI×ç ³C½^·x�ûD.ˆü~ VÁ`°;H¶ØGS‘õªV¶?ä‹Çã-­eFDæLQb÷1GØHÜ´¾{¨´=¡µö9.v½Ù~JWãã¼yƒ¬'ñLps—â¶Q ¤üX0«k+`ü{4~¥�ˆ?q|„D/VÚä°Jæê«_ÄÃ'rT�„À�•=p^Y¨ÈЉi{b¼:O¨é·âju 7‹}?=šeÉí{3È躥�ì)×MU–Q†Mdj +]kÒ©OèËóß� hÃËã¿…­k7�Ö5bR•9ûC ,? æ†{ó ñ³I‡{S¨õPÈíQ­ÓeÙ&ÛÍwZz(–²Hrߟßà,˜G-‚Æs@(Ö9–\ÔØp-â:䵜ͦx½ž>9&§NŒ|ó¼®q^"Üçè`Œ„æ9B"‹v< 0µq‹„µ ð35-¹B/¼Ì4þ¦'€·ë²z@âɳ—á¾ga8NsЇ=µ¯Dƒo�úàõ»ŸvÛð!4šyÇÃÈgȧ`ÌUÈSÞ¬“ðJ^}ùÃÈ0ÃMÜÇŽÖ·@IüËQfá4Û$ÆmÛÏl N×ù½A±l{�¤¡›�$H€Éî¬z™ŽôûÉ£\»5£®ßšyàt(PÐÆlja†‡$c#ôNhôpL¹öûJ£Òkhøìü$ùaÄ,ÔüXN(óˆ7NN!ôWd‚~¹…ÎV©ºÝ–"CÊò =Ëf¨‹ b‡¤«üðœÂÃ6ÛùlÒ°ªeH–à�cCï"�J 3qÐãiè=��ös{æÔSßÏŸ™$` Ë3A×7ïixÓôPŒ Uú¬�)áw­�ÄSyd0NáßIá󆓽=¿³öˆvHvšö{œÈEÃ~­9ÍŠíÆÕSóH| Û Î @ƒhÀÃ�‘Ðý—oÁ�Fƒ÷ÁpB`�F¤7)bõlã’¤÷ÒØ¶M÷i¥­¨OAÜÂMÃTÆ@ a ˜ª‹l#ï^#˜pËÎÉBÃÎãÝ ,8¿‘^ɹ“ã|ímÞ½¿ÈšQbÄðv€ž]o–\E �ˆ!B€3·(¥!8s…Œïè¸Â>Ž+ÿ†Þ <'•ÿÎQ’>\¯^˜’·^;-gà­*KmÓK9ǃ Á­å¨Oè•ø£¢Qik?�ÇÀxIžŸÁþ"Ï™M€¡WÉ +Câ8ÆyÇK`LɱÉapT=N…::}=9†gÏžÃ~–Ǜצ鉃-ù™Ù…^÷‰TV|!|Ö¹³£^œ§ , NäÞzãÕœ±K j?L<ñ²¶F¡_%_k¶Û-š+Чé‘4MÎro6� 5VÝj­˜¦ËX1³¨r6ƒ�7€‹á,Ÿ1°J‰ó]Hd~üÉM!É #€l3±m¶_¡a0Ý�Ÿ·0XŠ€h8îìL(Ë'Jð ݬ¤fSãy‹d-à1ŠØ½ÓÆ£ O"K ú¿þðíîZùmuîx¿º€×�9+¢?(¶Ïáflþ~4æ$(`KˆvûgK)¦¥" ÆÅB½Þt’¸â.ô`$ù;LV·G…w€OÄ¢¼[ìÒ ¥Eñ�¢'¢ÄµÅM­%ßÓ+/_f»Ás$¸¢S4Ï2 ¹�A;í.½9œCÖèÀca¶"€‡ûZóüqsýˆI£>r¨¯"h/—Z²°¡åä x2Ãb³Xqعfì¾ÞBADªBytÆ–'žG…ž†Nj-ǯõkM2–UËS¦×ÞD1ºæ²@©¸œÿ�p÷«?n‚ Â†‚>3$jh¨�aŸßæ÷Ax‹¤2Š»œ�¹É“ÒÅw|JÂBK€¿ÂpœÏáó©0†n³K£x>Àn°ØÅ6 Ð`~ñÕ³ø�9Çð0;ëøX/Gó~rsY¾þ½+l7ó��"ȰZA/ yxŒã#Ü…ÇY‚z{Q±Šæãc“CLbÀb�ß_É‘G6Ç÷NðÚØV,Ÿå¨!Ú &6”õP8¬ŒGqçÃUHR × Ï‚` �B¨pPŒ‡î†[{‹9f�!›ïI˜ÎÜ–¥Üó*¢\gâÁ…jDØ_¬xÐ΂[9&�`Ày;@;³s %z";°Ožx@2�TJ b[pŸͨŸïiÅ4ˆwîÍÔ` ÑBeAWÙpsØ$7 #ãÿãCŽ¡ÕH£¤ÑCøÀéœ �óf˜¸áާ±�fª³Ïâ œ^�ˆv›Bo›· oY–M ‚øû¸Ão=ÏžW½_xå¤ôçãª#>ùìÞºüé·.³}ÈÖ?”Åz€-W× àõ0Ï�QãÀ�Í„�#ÁhS˜ðʾ۔,áupáB —z|j„`‚P >b1†[et¨€×�ôc¶6Áñ¥÷hâ½ê±©à·Ñ“l 3Ë‘{KM„µrréÂYz››ÉG¥¤9·°´$"]£~‚Š'^hk›Ú‘Ð>Û²òZ“X#97Ô“Ý´Úz*Æ�­×ëõ3Nà��ÃÀÍ„ççÜ:ŽÃ7 m³îœI™ãvšpÒ.翎™$&óÈ ïÐ+8L \Ý+Q¸l†…„<¦Û·¦ÄlMq� 9�ÜJ­NÃ5Ô_¸ôʧ7—D[1¹9W•o¼s]¾úÚ 6R½}Bƒc…ÏÑ0𬠂àT¬ذ ᆼԡŸãá0ë@|ôbC&üˆ¬Azpð8œ /‡!ŒÄB¸ÃŸmÙ Kµ“aV‚ñFkV‘ß±‰pIÎÃMà…‡D¯€�“mk?=czƒ½f!ùÇDkáï\Z—‰KÐgËg·æPð¶Äö*çQÓàö¥:ê¢�w¦äYø¾ä?®Þ¼/ï`àZž0$]©ÖعàØÉsrâô9‰Fã¦ßœý¸6ð¢¯ÊÛßû`ÃÙ:"2 ½­ˆ'[ˆ$!hêpd‘©À´6¸Áá£;æêz•¸­6kK˜Âˆ)€Ìz)•�ɇk¬î}J-]XTgèùøò Ö¼D¢!z‡!›;ƒ`gqZ€E–A—ƒ!Œ»2\Ê×¾p�Åt7oßÛD,¾O2¹^YZZ”[³¸Ërr¢ßi1ÂïtÀÂÁR sé„ˆî ´–f£!UðqM”œûüþ�D>“ÀÖ'GSf|÷ãëòöûW9‰œ{R(öÈ…—.ɉ“§$ ïØ¬T«Uηæ—Œ7rº¼ÝdDO¼ô_Û¬2ü� e<ÇêÒàY÷�3Þyóܸu_®¡¸myy�5%·³ïSúräo0/žß Þ[X ÓìÐÒ”¦‘2XX†É‚nýË�J·)}[¾ˆÔàH[µ,Õ-—JúØÔ¸œ;{F’™¼8 ï 9áÇWï14éÊþ7¦^£ðqõò{Ÿ¬Íß¾«KË«NGéǶ“)!‰ãþg7äÞ§×dæÚ-ëÞ'W»‹+%9¢‚z*‚È_}÷²TmŸ”Bÿ¨L�º(_ýþ–³çÎ? ˆpáóÉ•ë˜I¯<3;W5@r º¶]Æ–'žGÒÖVY¹x$Ô;6�çü‘£.1Ž‹aML¿M¥b‘…¥U¶‰¥È›(õtªž-(<ìÛ,»‡æC.,¥È¡{.k DËÁ‡*תJÞì+g†ä�nCÍÍÏ©{·•¼zéœd"]ùÆ»uYY¯ÉG·VÙ 4ä· +:}ÍN ìÛ0*Îþo¿ÿ½�K½a_nÉ/ªŒ*ñtoAÒżø�ÃX™ÊÚº,Ü™–2ÀĈ@Ê•W×Úø[‚ñ£Œ&šwä»Ý’xºGŽ?‰ C,âL$â ‡E)Ùù1U"««ëk]‘«×nÜE–¤5håQ â‰$NC úÑF*îͲ ˆÖÏFŠ$x®ø»-3³K¬Ùˆ«•š[Áý´HÏ;'jU33þ°båQs ŽÎ%ØŠ—¦;lR˜OEäâé!õ‰¿-¥jY*K¨ˆ/8I ò�÷¯ËâZM>¼±,ªUŸ¥å²uW�Tå�·H¦Ø3 `¼p«¶^ÊÆ2=‘N«í„ªX£Á9'.ÛÏF |®î­²¾!‹Ë|߃R¯Ô|�V‹ôT… ¤®R¼3W’s )¢×ÖÔ$:Œ±�ážüj‘ùÅ%Y˜Ÿ•�Õ<ÒœEBoÄ“gJl9<‰@úµh(x5#±žlò™ËR©5š,Dvk8\åÚ&&~øE‹ ‘À�Ír#¸‚Üá‰v "™5ÖÓ“å¾ wÖ¯gk¤ÿ²²ÿLCDR‰ˆô8Éuö†ˆí °ædmmCV�m4¯mÙ%ð »ÓÚ»_~Æ'—¯××—CƒÅ´eBµÒŹház¡ÜkàÔÊk�¥e)-­ð1ÎÝCP»Ù¿Þ¡‹rT1û ^<º Ž]W"éñÅz¤ØÓ�ïqì3™‹jOáÀ÷ßÿHýë_ïÎ/,Vîϯ~`êF>ٮϖ'žGR„WJÃ�ÓØ>[B·½c])W™™t?¿ÀÞWðRÈ£ˆhV?…>LäIVa¤ææ d‡4 Ì­J/3ôL”i•r¨Òmˆ(›³É‡úr¢E¹v(—ËÊ×Þ*È•›sò1xˆ••¶DRE ZMé4ërëîœ,‚_î‘áþkQž@˜òúÑ•[‚V÷ÑB2fùmûs_+•¥†E‡mÛ<6ºÓÅó�mû¢)%ôJ�eYÜy8¶ðßÅ"©ÁŒ·8.ËkU‰&2Ò?4&™¡¤¤2AI¥õ¾ý›õjE榯)Ÿ´€½vÉdj} m‹'žG² `]„~Ùg[“éDy)Q+›Š1¾þ V3^Œ|y†‘P¼È~\Øgs;m ÷Qô£Òd•â�'+ºsw–Ùe‡è¥1ć¬ª·ÙÏÉÏ߸¢Ý­Rf_,_”á¬lÞiOßeL^+¿ÄâI9>Þ'À zZ挌¥•uÓÅ£aÓ†ö$·åý¯©ñbRÒø{½…Ljù!ôPºð^Eo�ûÜlwØj}½\“®RÝB�Ó8QpP!ð<Ø @ˆ�S·H¤¿{ù¦L/VÄg$Ï8ûb©=_+n˜Lzëæ�î÷Þþ¦³S«ë•0z÷ÿ‘›Û‰'žG’…¾í÷Ù6îÕ�m³EºlžÕ¶ô:ÝÃ"e´ç>Iw]ÃX(åc¦{7)uà”¥H cË㪔:¬ãÀ>i¶Û*^ñ;<•¸£ê<¸¯Û‹«Û$—â÷‡ä<:Þf’QùôÚŽò]®øäÔÔ1¶6‡g‚v‹ô0^>7%Ã…�d²f'�@×*Ò—s²ÕC“u«� ÷š¬•k �:^Ͱí/ã<úEIx?içF³Å†—Lc_Z“{ƒ¢„�¯Û-±À0[”¡þA<]Î`ß—ë©ÛnŠîvøYMýÉÇï¶WV×xÜÅ€²o�¤%žl!�X¦-Êyh*àçK”Ðà=ë=ŠØßêÓÏn ë&|¶´[mα,‹†H‹ækJVõ&cü /±Áž>´Ö(ÇçJÜÍâ¢<0�áÐøvMÄ1Ã+`ù9÷¢ØÓ Ïí>º<Àv£rîÌi€A„mëgçWœúö*›í{¬µ,ÅÉŽ“#½zþî}5·#˜(µ³ÕAÿæ;ŸÝ“Ux·9€\Þm1“žþ|È”Ú×Ó¨„àñíw?“OoÜ㨄Uð ‹+N×e—Éãg1•ó%y^·,&Ü'évΨŒmS//-4–çïÙ±Ztu 'óÝòïúùåúßú_Å“­Ä«#‰šn¿Y¥T8à·ÃØ2$€Í3/ˆus@=”Õ ‚ÈÀ„ûñX ùÆ„iÂÙ¼X±^Æ”Si»Qª°íÇ�;ìøüX°�M¢–e¸ÝìêÚ7îwïίìDê­¶\»· ì&½Ù„Œ÷çäØ`�#u{ûöãs¨5õQb@Ð ídP‰þ ?¸*×à…,ÃjkÜGõ SrñâKòÕ/¿I°ÅuDÙOé´ëÒ¨¬HuuFÏܽÖqÚª´ÚôPVR‰èíG-@<ñ<’,t µ” j-A{Ó|Šç¤{*C7nÍJ__Afæ–Ø÷ªTª: û ií¾×üvרýX‰Ã!G�4TÎõn·}Ðöa3G£¸Ô5þšÀÂYæbm:Ü]±U—!>†XÉ!ö$ Ë_|ç#´þ¸.7îzãâ Œ¹=ÆÞa¢·÷ÊΟ;æs êíï}R½2»¸úûsIÙFȇܞ[¡Z–’h(À9õ~gl¸‘d!Çëd×!O¥È˰™âÜ2ÂWÓÇ›äõ|¶“‘ñE$Æ^XŽº=ŽPV>—Ù÷+£ÛnI·ÛÆ÷iH«^’jyÕºug6  îÀ#±û éÛ–¥æÚ�XpyâIÁx%u¥Tžˆ[öMzŽV i]½v[Jp@,ÒZ\\åp%€Ô™¶ÈY|M(J�hÔ†[P{ 11ÖÏARªÃjÔè#¤µvç��ÆUfR«²XkB›k‡ÜÚ�aw¦¸ð5í—\_P¾ò†Í…Áû0¼üõ÷™Úý:åQ W¯]:èÑï~t]¯¬¬¶cð’N*ÙÒr@dv¹ µðÙÖp1­{²]Œ¶r½•é-Šo·-]”ð:›[Xaϱ+aÝž^p2ÓxíÑ#21uÞXŽc��O�É`/géï£ØRðJ"P¦CÊs$ óLß›¿ßÏÌ)ŸÏr&ðñn4ÇÊ0gzQ9üP»ÓvÂb ƒ³ºûÑÖ¿«5Õçx$¥Ž†Àñ÷�2­QŽPD³ßqŸcx«S±Ã4ªÍ<÷H¦/"?øÅÁòjInÝ›gBÅécÃl?󨦒è<¬�õ¥�¦»¥�N Ýd]‘–3ëmÕÕ±¢Ñú[o�Zéd4è·¬fðb‰˜ Çc‘ÝÊòJI>þì6dš|H`Ï”^j$‘á‘1Ÿ<† ˆ“ÒßÛÃóu@}ÈH¬×J‹Ü6ªkÒ(/c¿Åûø©€®n4�š=ù¯þ·?�b”pI<ñ€d›&�cЄmYiIع99Q3›åyœBÇwúþ<{!-˜�—ÖX‹EXЈ”Y§€�Šì¸Ö{¶Mî“åedÁv´>p¢ÅÐ ³—Ò?j¢»&Ôe2¸Úb¼‘ø6‡Z‰î;'¯j[þêßf†ÓÇWn3UöÒ¹IþÖG窣Ån7›v½T–:¬3²,¦iÛXE vtÌ÷k)¥EIP´VJY»]\qar aMxCÎTB³Péðœô`1ƒ=ž�KàAz™¢}` ¯µhF±.µõy�ìÑݶ›é66T”ÔG7ÕÂÒz{nq-�HÅ8®¥÷Å“-Ä’„ñH‚¶me&¾h8hßéç~ê\¥\‘›·fØâ}¼‰RŠî„uh´ÈaYF,è^À„¤÷øh?<Ÿ%Ž >Œ«Y(+êí#›Ë­©’ï~z�+(ã§åÍ.zv}ëmY8xå6�óÌñÑÇ…ðx,ìpHÙ˜“�°QQ0˜Ëe ߢňÚÃñgU=þ-ò õ�ËÈÀZg.ŸKH8­|râÔY)ô K>Ÿc"ÆaÜzmG»Y•N³&­¦ "î±îÏ«KgÆäÚ­Ù�{3Ë-_u$âÉVâÉ´O)±-K%œÕ‘3ÈJ‹prÞó.J)NòûÓ¿xHÓ{ä²eð<E�n¶“‘]çíò#´ÐËé�´Ô‡–¨K gÒÙCíÊ¡z&‚­/ÄÇ�M0š<'�¶–ï|çm¤Ê–ä�þê}ö©©á�^P‹Ó+×oÞgºmo(³×þf,"Äjž[¥¿BCÂ.̶Åä‹áá;vVb‰Œ(Û6õ5Ÿè€1È’ÊÚŒC®“'Ù$Ìl¨²ð5H xܨ'^úïÖa­)g«” IB¶\Wâ³ (¢ä…x³äGîM/pøO0È^Tl­bÛ6W–•½­P��‡Z¶“×úP€’…˜†l–#/ºEá¶ÃBÅG.Á$c|^NŸ9'ÎßøËoÌJø'ÈÎ&è¶™Eµ.{[»/÷/¿ó±|ã{ŸÈgÈÆÂõÃZ–B6ža]å“–•_¼Wb©œ(Ëч²°0ÙYMiÖ7" ×:˜ëç>Bqs×묈´¨ž< �XÆ™„jGÞˆ õ¸Z úm ‡ü¢_ ÁøX‰ÞGMW3$!å d{ƒ!.ˆ=uaÃHÅþ_||pµè…à¼Ekž×#/‘ްH±ãhC˜¬ÛfÛÝ\4;÷Œ—T:Ç^\ëåª|÷ƒkXU¯nn9ò¨sC¯D ÏÓnŒ4ÃWÈ"#ˆ`Ÿ_3�ˆ9S ™aö6R–ïÌ—¥¦£Ò?<ÎïJO”!Ø[‘fu�ÙZ.õçþr$ì °°¼ÞFeýŠˆ” uñäñ€Äí1©¿mŸm°Rb}•haÊhÀç“K4¸€ÉýE�!K]¤“tGu1*¬ç8xIkίæÊuWý¿Z-6oîc‘Ö×[+êh4ÌМRêÙIœ X´…)Àí²ÙÖ ôTºÍ‡�B­%›ÍÊÅWÞ�žžLXŸñ¿ÿî7äϾù¡LÃ;�2Ì'j[΄ßRŠé¿OPdÈs aµ÷äßýD._½C�hvq…Íÿô›—åëïÞ’Ž/ ™”S'O°.$‹Ê! Áƒ^I«NÙf!Âì7x#íR¹ÞÝnþˆ'GÒ„V�» $°åJÖÏ­’MHŽÖërgz¾L#�Æ¥TQz&�†sjj,#nãã <:·²ƒkO!ûò`l»&ƒ˜?Iãt*ÁѾÖ3Ó4M3k‹ÒÖÆQ"þ7È‹ˆ½µW1>1!4ê7>•¥Åyôìº*Ÿ^¿'õÉ™&N�Ë[¯�’è6ñ=Q½³"CÅÎ ¬ùÎ{Ÿ¡Ò~V¦g—B�œÔ@_�ôôö‰?—ÞÁ^™šu‹ }Oc¡f.¸N«!Ú¤_o3e‘ »]ÛHu3¤¡1hX)I9@ÂLKÑ¡a|á„  VW6¢7ï,|+²<ž˜í§Gï…„©H˜û¦C«Ûñ=Ÿ˜†;7�Œ­éy‚Ò�Ýö-ûÈ‹ôPû"½=9§.†3H”Pž-0Ñfk�gR nËõp6ùÉ“'¥P,ÊÚò’d ýÈèú®Ü¾?î«.Ÿ¡vc~i�gÉW Í}3?ÁUx¯Ó!Ù²wØ?­E BK‚ø5Î)™“�ÁA™œœÄ¿át4ȃo+²C/³²ž¢X–�d;<’ǶßG”¢É"I‘ÕH¶Hû‚Æ�Â#x A=¹ñb[^lÑöÜìüÅ¥¥•¥X4ÔÈf“A'q¥t½ÂˆýyvF�-VêµÆN木Q5Ï�D(„d¨5,2Þ7™ŒH‰(kž½œÚ1)Á¶HsÃ-J|�æóyêèø„œ:s^Þ~ûm¹rå3¹rõ†üÉ7އװˆâÅN_ì+f ãâ@)zq²Uw­Ý‘ÍÚLRDU:BgpW0–ÉþAéí’cÇŽáØy®±` À@žº�#�¶•G€Å ‡3ŠáØÖ6ªóì~á‰$[�Ô…-K¥œŒ-'+ÉšÕ±'Zwƒ­f£½Ýê~òé­.ÂÖâòšœ?3Åv+•pÕ?7·Ì"F×ÈB}+ñ¯Ïá0†‹‚ÏuÃbû)–ba�ÄðÌϤ¸9Ó¬-±"|NwùøÑ' �ŒŽJ¡P”sç_’÷ÞÿÊwäòµx&RÌ%�”\ÉÈ@Qú{²ä–8¶y­,åj�Þ&Ä ŸßÇâÀÉñrQÈÞ2¯ï¯Í©7›ôzPÃsªˆ&Ï2ª›ºqB\U†ÅíŒ"‘0‡?  ɈüÉŸ}}×®ËÚ�eéúd¥Ô’;3«œàè3mdª5Žk¦çécZ¼O4žÏeÒâó%KH?<�×^{Y,Ë–û³sœ ’ˆÇÜ÷h‰3‹¾,Íúº´ÖÒ� Ã&qŠ“Ñ°1 ‚Õz3"<ROŒx@â3üˆú… …n¸m?8¯Â´T± \á΂ãh6¿r‹„6ââ ‡ –LP) ôe„€À !2ŸÃ/<>N³-‡éƒÅ^_ûÀ�ÐëÁyd{|0¥ì+†¸’~vÅ„™,[È“X�݆š^½tÞá+P„úM¹òÙu‰$’3f‰³•JYì@€_Z_†‡1–€?@î#J@‹Å$�‚7ϽÌxþF‡�(¯¨�p¯ ÝiI£²*Zw{¬ rl¬aÀ•¼ñ0ìCÖx$îÓH44ãT³‡�ˆÏ¶Ä š´_¿‹y=ž,*•:¹‘[·gÀ?ÄhD”(<_“¥¥5Iá9„® Wñ¹ì7z- ×™–»²ZŒZNVV6öHÜÆ„A[>—â¿§LÏ-Û¶žõX#´c¶Ý=õ蓟û©Á9¼'s ‹ð p/¸^$I�ÜF`Óœg!ðñø�^XÜ?Š¢9F˜“±m°%ʼ%NÕ<11 }v7�IÅ"¨t‰HÛH”šß¶“ðH$¦€ îl`›OL»yæÿ#Má‘;r}�––W9˜haa…$:fÄ»!¥ÔfŽ„Ú®78œÉÔ”€ —»×ïGï'ñ;X¶µ¹@ñˆ»�ëI,�dÇs�ÇäÌ©cLÉíjM^ÀïrV&ÞøÃ©¾�ˆ'F< ‰Ù–•…7’RŠƒ|6tG!�3Q0Ø€¡—Q|6ƒ*f€ ãÐ+«ëÐ ¹üéMÊçÿ®©;Ü=™t:!3³K|~¯â÷Ùäj´)¬+²ätðGUž,æ®Mú¯ñ„á.qÁ£Ûàã'-Ï£°ð°ÕàÀ*è†tšuÙ�¸YkWoÍrbc4c‹ŽAOš}OÄ’ ÜÖ�ßç :ÓÚÌ0+‰„‚Úñ�€wøŽõõ’¾w�ä9€„-è‘ÙÅö*z‹ú¬òXˆ88Xt<†µöClŸmªÙƒb1Å8È'ø\�Oˆ™ húpµ*xPù¼I*ðD±M|³º&-€H»Q&G²S™]X?rG@²K»Ý-‰” :µÅ“š#QPŸe©$<¿í€�$Äö‰V¯ù޶ð]'óTÇ€K•û\{yÂÌ,ÔŒ0¤aˆKï,ìÇ…e*e#ËN§ÙÿW1³xMhë”9gwù¼'/nÓFGªÓåœr±G·ÅòB[O*  a!“‹$úÚZ‰C«°å(_´4gxËÄ›]0Áð%x2ef~5qü÷*JÑCâgôd`€UÕÏ幤Ò©CL&ˆ\JÆñ’Š˜¡Õª3¤…Úîã¹'Šôó)g¸•”«Íîð@O~d P0ÅˆËæ-Ýû`{@âWJù‘™�t:48Jc÷O\Câ‰P¨µµ2{q­®–L1[�Uï öéjµ(LóÙN½{oŽ ¾qJ*>›)«‰x”7Ï£¸Ã¯:5lZкHkÃïÅ´X7R//KǤûj­Ÿ¸x3 Jo1'_|õ4À$%…\êØO¼†lÀ„ˆ,@kâÉ Í‘ø”R©v§›‰&¡€˜ÊêIÚ=6¯Î„ž2]˜†ËQ½ñC](zãp,L]d1<fp9^ánÅÌ�`¯/ÁDkM¾ë¹†‚’]|BïÄ zd»RÆ+©íÆ1¢$ 1\{lbPÍ-­Ë'WïZè˜ü z“}�”o‰È=ñä…öHÚ¶m…aÀJ‰øMþ<¼#†'uÄú:C]ôBØç{ù“›róÖ}¹yû¾´àÁd³)†w)$T×s戥[¯D¬Â~Þ!Ûm1ï6pÔÚmÚ¡´ôJvÙfÇåJц�Ž�˜Ai¡ìÅ3_Çw– Ï·Ygõ:Iv­wžµ, j»Ïa&�\:;ÉÙ-×úr?02XüAÎ5òäÅxʶ­a­eØ ©(SˆˆÐ�ø|ÖÓXb ~΢*¥Ü‰m+à@bC¹¿#�‚)¾f@Ū’#ã¹h­É™”Ê5†½îÞ�3‰ÞËq#ÁŽU"g£@y>_8é6MÕ»�€ò‰i÷a»ÉÖÚ­×ï†[7ƒÑÄH¯œ;1&¢ø¾t1ûý"òò‹Ù2Å#Ûmè¸R’%ÌÖJÆÃÒ“I†FHË� ÿ­Íµˆ½ržùçùíö?B¡Á£Úh²î…«&ç3L/ F«m†9€ÓaM ÒéiEB~¾G ã¾4¾ÝŽ6EÒ‡&ÆKa†×.A�Ç¶àœµ$ÙžòÙìLëá€DÎDØ^þÅ-$ÕE³’©¿Ü>)�˜…÷ÍØh*ÅÌҙ듡þ‚|ýí˪Ùj½>î'�¥‘oC^œ¸¢$è«0ØQ§1Ock@¨EØñø0¸Âè/ÅTY‹e3Ik¡aÜ<Ç\)ò8nn»¥Ú¢…ïÓëåz×4lP6j í|f¹ÚèšqÁÎ å·-€I@âNÅw"J»Ã ‡f Þö´<ên¼t÷eƒH¥S}ãñè œmgêG”2 ý/¹.΂¬ð¨~¤Á0×îBZ$Ø9cžîCÿV/Ò�_>)_¹í<Ìô3¯Ü¸=Û1 ÿz_½àxôj –�BZÐ&ûY` �GeñØ¡ =6“¶ÍýE†µw±^À*ö¦tà…´©œ7²«{Ò¶9!r;ž�ų¯]4cHo$ô³©`O1àXáEÚÛ›ïööfT<U0–Ì:ñùü]ÌÖÆcŸ³I®‹²”Š(ìÓ×n؈7€ÛúŶÌÌí–\»=+ßzç3¹{‰7€Ê×+HÆÂîq8¢b<fÔ�Á€,†u·Ëç^XqëJ4·/TÍH·Ã†Œ�ê*½ÝiÉŠ;ŽÙÌ­¡n#ôâôk/³‡Ü»߈£'×¹?ùúûïÞ¼;W ]§œ'Ï7�D …®–¸ å÷‘h'�(¥Ü"�­C1­Íé‘Äã!)WÂ’Ïgä¹q^à z!&\cãîÆÂõƒ‰J Ùî3Íf:3`•ˆkÞ˜œ9>$wg–äí÷oÈ•ë÷�L* Þš]bè«'›à�‘ÃG‹>Z)ż�Ñœeûtü%¾Ð@¢,¨ý"f£HšõÃZÝö®S~ÍMÛY¸qÿÂTà³'Fd ^~29øÊ¥Ÿû_þ¿¿º²¸¼~OD<$yÎ�¤­Ù–Êh1†G3æÎ­¨ƒ­ÿ…�R¹Ê-  ã-0ÆÿÒ…ã’ÏÆñ\p3o°§Œ¦ÍàcÒœÉÅ£’MÅAfåÊ�ûrûÞ‚¬—j²\ªÊ2ÒrSѰ!éà�Bl­oÛêiz*¥7ûÚŠq¿ÓÕ/|TËílù^H €0¤¥wŸ±FNÜH5$Lx|Üõ8 Ò}|¸G>üä–çyñü©±_û£¿|ﶈ||ôHwOì} )µEôŠÁ¶¬„e*Ù Ù~ +ZË¢÷#ý}YòEð…|Šm=úó2Л–à!͉ç˜ÕXXR,H/¾ *u9Ï£ÞlËìâVZ%Y°¬`­“Œ‡"ÊQ9Ta�ÏæwpºúrDo×ðP¿{Ätú}¡ù_”ú"p$Æe†V£º"õÊ ö«|~—ÞgÓ‡Ba…}µÓö<ÑpHf–^/U­D,’_XZ_CÇë›"²!ž<Ÿu$#ÙJ©h¬ÃÐ�"ÑΑ¬¢öµD)qc®ØRE 9‘$Hât*&CC™š�B.ùT†Ô�k`�Õ«ç'ågø5ù‰ï»$_}ó´¼raR …”Ô@b¯¬—å&8•OnÎÈ­û‹2»´.‹™r­AæeS¼šã`YÁÞ×—w¾7µÛé’kÂk^ëe»©¿/ÄbX;{�ä:š3š�–~2OÝ%ØmOÞZ�å̉Q'¼å\ƒùKg'¾ŠEÏ—D$+ž<·¡­¸u=žÂޑЅCÒ—OJ†ÞÈ®W÷›Òt•!íº I™•Žkô|¶�¼Ç±ÉÄXÁÐH>í•�m[¼Îv\uvæ½yw<ÊuùÞ×~cK· álu¦ ƒSag ÛÀþóLÌ`Ó$„˜ê̱¼ñX„^Q<å>€Äí­å‰6-Rü"J=ï 9‘zy ÛÛ¡>·G0 �ùõ‹'¸x|/9�k7ø{ö½~J™†ß2…ŠÏ:šx@RªÔý–¥Î*‘Œ>+Ú¡ûÁ‰ÐMæü‚�Ÿ÷0ˆl’ÙL\‰¸Œ�ô°ØQ ð1qÁâöö2ûü#}’‡WÕSH‘�¸ @™�]fÍJ�ƒìUb¦×>¤÷òfD¨� ¯ƒ7y”I Q"ˆT"ÑpB¼T-eÂ™í¾°¸£wí° ì]¶9|¯„Þ•Ï'fj;`B>ç ÆŽ¾r~BN€ãéíÉ0´…¦‘ y™�Ô>€šÐó@Kx‚R|á�äÈ�dÒqˆ¬!a¡+Z+O܃Ǝ¿Útn¹Éó$JY'¤U‚n�dßyñ¡’J¥.×àYßÿ7;·,w§eo:mú¶íQèEO"ñæË'‘´r¬ðÊùc/ã¹ã"òЃN@­°œÿ�z¾cЯBú7@ úKП„¾a^2-­¬£D¶÷XJ娏Ðr Eï�`'x˜U!Œ-y¬œÉƒhÅñ¯Gnþ;À�«~SÉËñ¸xŽ¿ÇÌ``{xlaø8TrjjPú{2Lƪ 7å‚h]#O %—Š ø§Ýr%<¦Nep_ÛÃ\ø·Y€¨”ÕN¥â¥TBÆ¢ÕŸL&ÃÉD<Øl6Z;ÑÇÆí•ÕÕ €{Å$+| ú.´ò´�Ä‚Nh­ãJ‰°·–éî¹Kq+¾•¥XÛ`Ètr >ìc~ßsÔÄÈæá=nx '•j×¹Sâð3ZêõþÆæü�WÎO1íÿü=¹zã>�>”d|1 ï!Ý}3Hjq€‡©éâftˆw‡É~þšð¼—a 0TžÇ.ÀÊiÌHn¤Å6ñ²3!€ð:ˆ°Ã¶e ½&�ÔÛúý>SN÷èÑ‘>K²ޣgâ—‰Q'$œ´'†{_Eô£ü?ý?ù© ãÄ¡è7žãŽÁʀƔñ.²Ðœß(Æã±!d·õf2éhO!ˆá÷—Êegñªa.-¯¬®||ùÊüýÙ¹…F£ÙgB^ÁF½Ñxj@ƒŽá vA²ëX$( k©½­ìY‹b[»ÄX‚• B/Yo‘#(fðçªCœ,’•œÍBÏŠ $ñ["®§Òl6ùÞc¼“6‡5réÜÓ¨1ÏšÀÏ„Í"Ekx'ñ]y&~?ëDÈ�`u×F+” !Æq¬ýÛ¸¹žh*b»u%Fž“ì-Mív;ô¶,_€�9NWëíÿ¢«¹ÀA¡ ùm¢¢ü§Ë„úðòuyù¥¦ûD�Ÿ·OCô¸èzùüT¨R«e£\«ÿîŸ~÷#3 ëÔ6«íiy¾$= ½v÷tÀÑ78ÐWÄ">‹Füc#ƒòæk—äı Úœ&§¥Z …Æ±Ù¹ÅØÿó;8ðÑåÏʰ3“Ë««úÖí{÷êž®/>= É@æQ9+i(‰¶=*K€¬ô>zŠiAØEÃ#QðPŽT(Ëx¶m¾Î÷oò\Ü¿Ñ@T±¸Uð:™ŒK6SUã±(Q,fDCÈgÅáß%P±ñ$ûi9©¿qhLÇ⑸hyÔyá-eT;‰6‰OL<è9èÃ¥DY> „ãIöpñƒÐ5â^³™ÁŹ$š‹f‚Pçµ;o¤òðü¾V«5ØsÎÔ.ÿˆvH‘·^=ýîØ_½}ù²‘aètZ•çCB@^…æ,ËÏçr}½Åq`H¢·§ £Ã�—Ο†Ý,l1?HËÊ꺬¯oX87‰F³™èÍ$b±ï~ðñ§ÿ#þN¿zé¼ü‡ÿÙw¨d»eܪ V íÕ-6JŒ„ODFSE¨–b8 ™E¬¸†KìT¨+ŸÏ:JsÝÝ‚+`aƒ�Ù Îì›×]y°ú—ä7<lCôºÆ‡ûœüûöýÅõõZ³¥ÒÒoÛL«WòDY\AxI¼ Aˆ6¯&�ø=Ùa©¶îB["Bžäó#xE=d»íK$Õ+Ñt¿Äs£’È�I43€ý>ŠçňH§+ÎL‚ÇÊJIæçWùx»LÁéû ²QªD�0³¹b‘°Ór>ò…WN½õÅWNLHë>T=G£Ð/ ‰#ÚQ¸pþìÔßxùôë¯\H¼õæ+òS?öò×ýÖ/É�ýð×dp°›!tŠÜÕ÷õ‹òæë%�r"¡Àçû~ûW¾ÿ�÷¾-?úC_•å{ï*�¤�GR�*ô�²& Ã(õä½},e‘1‡P›¼ø‘§]`èÎS0[‚éSEÀØqØ z"&Õ×(÷Ýç¶¹‘]%ŽÈøÈ€ öüH¥´º–¯æLoܨÔéY |øDÇC)!(·»,|KqxÕ“ä÷{`B ˆÅš>  èç*s˲ýT0*ÁXA|‘‚ˆ?!V8/¡ôH7Ù{B§Š£K6Ö+2;¿"Ë«ð^¶iïï…÷7Æ?³‹Ã> ùØRÌ¥»óÿ³wàm\ËzÎY�A’-³Œ‰;®CM1Tfº½¥÷.3>fffffºÌXæ¤"'N 3J2HÚóþ�o¾5¾¹�mɉÛÌ÷Mµ^Û�µÚ=ÿ™™þ)„¸ãÔNʉh~ >E+ßÊà[„•†5!T³uËUÛ¯ÜÔ±@â¿õ¦íôÀ}wÐÍ7lEq?×o”B¬¨(£ë·_KkÛšy ƒ"Éeý¿úç¿ùëGîy胾I|^=Ÿ[–Ô–†×I¸u�)I§]ŽÓÍ.ãmÉ'…zí(*) qßÈ”:°©," þœd–"º­sØz¢MI!þ<˘8z£÷ †1ïdppxD®™èÁ㧨¹¾’ª¢‘ó¾.†ˆÕ�g3„tÙØ¹u¼”`eßüR�$‹µÅê˜ÀqìÑ›Ðãt¨ó�ŒŽrc¬¤¦4¨ã& ¸Cã)÷Õ=]¾ñ‰8òïß ø÷±PqÔ’‡QÍÌàìhm`�G­uÓ†öU·?öìîi"êãBòʶ*øVx=þãÿùåOÿó²‰#áVJ @…2ålÑR($‘ €ˆÿ?åŽJ.Ì< ûw œ™É.çeG¯äáðιg€C~n›k!V Âu€³S<26¹¿§ûd;(Å%žÈãdÕ45Ö”s�仃’&÷Œ„Ò΢âPñ9Ø3ŠXéÖ¸²xZ±|Ò¾*‡þÍ,h(ïO"?q�$5Eüª|oºaVS“SôÚž( �rèãbíàà õŸVÓÃäS®vÈCó»>C¼Pybi†Y^BaÏåsÚX_M×oÙ@�?·[ml_µ¯î}‡{|D´¾O"”Ù&¨Û߯…;›6mlþþ�½ï¦«.¿¬µeU#ˆÒ¼ù[4ûmÝem41'¸hß�ÃðΊx<ùë§>õ¹¯ô-GjKIJkB>L?çí ·ñ·�ˆ®3A`\lF¨*ßSËZ± ƒìüµ¤�$…ŲìYµNy Ààu^:kQ- Ñ�[7†~ò¶´µ®Úé’„—>ìA�sÏÑžZ°t¬;œ®²Æ Î͆`½±~V59ñ»•¼þæS< QZÇÚÉî›°ßãcî“sÊž3öY0˜À,9q‚o>G¡‡¯mk¡-×l¦[¯¢v\K×^}9/: MM”RÕÕ7 O�Lp¿ÓCP'1#£(Ìç#ÃÀkÅš¦º`rÕ¦ÖðÍÛ7ÝÜX[ÙJD7Áß¿Þ/X! Ò!wÜÙrÍ•kîÇ?yßÃo»½uóÆu)‰�Ê•ŒMD&MRœ¯äϦ³ëxË ;_¹î_ÿóÓþåm,†_¡µ`öH[Ðï/ |T_UÆz[ Lmñξ@F½¢iŽ‹ì¥Ho-W‘=[Ê“�DD€.,f–OÀ‚�„AÄŽUÙ@”‹HŠA¢®¦¼]½nÏÉ‘¹‘‘ñ£øô,ÍrYƒ,À‹GJFÒ çkªÊ¸{½­µ‰ª«ÊéhÏiLq"„ÿgšÃ,À}Ev"àÙl$¥ìy?ÜF$¼C7>'Ë›ON^Šîü^}Åö½¾yŒïgÈ1³ÅX¼¢N‰Ü{}] ¹õr,b¥4—1³�GŽ¥°iô¿QÍÎ0ÃkÎ�"UÆb %ùz~y¨\¨ˆGDGšªj¦âÓã˜ùîJ¾AšøF/âè¤~üx³'rïÝ·vüà'Þ×í7ï¨òXœ¹¶p(Ä Y¥Ot�@ˆÒw²ÿ0jZ�\cÊsDâ #"b …|Ú)=Uæ\,já›±²¢”µzê«X*ì­e~ˆüg@&L*®Ó88g(¼"ó"™ìˆ$—¶~mcÓ�â�ÚÛn¼š™pá|9ê#è€?xì4ÌBTÇ`‚TGuØ ²ŠoC]5¨�5(L6póØ®Ý�,]q&‡¥ü¢rëpq•sÿ¾âÓ)Ȳ_“ÎjÎ�á÷Ìþ&,¸©“Àµß¾×7-Áàõ‹t)ÑõˆJîºíFúð‡Þç^»eKÂÛÌô�LrQý»EúX›4¢N‘�ŽMò8ùgr &|Ÿo¿ªƒÖ6××^±~Ím µ•k¥û»Zé òµºÈ”ØëEÚäøÆ*°ÞóŽ®y÷÷Üï�×m)ÃõÏÇÞŒ×Ýö¶Ž‘¡ñ¸¤¡ÏYpgæÖž½GPØÝ Êð÷˜Ì¥R”kÓJ±Rv)¹ •_±¾åúúXE»èKUJ�IýEÔ€[ ¿M´°nÕZ¡�Þ~è¸wÜ}ÇÍ;À®*€Ì åÅD©™× €þ]/�§Sé9I«Õä;µÕ �1µJQ3Šn-HK=Õߺª(Ëž/e×SHž^ÄÇËiRûð€D"›Æ:Ë/̰'^¬ýHú¨£­9Í êî0ØÝ)"–žG¤Œd§Ìc†ÛÛš�Þ*ç(O³FY1S5Qˆd`º¬µY;ÅÊh?)Ç/uö¬V/‰D4qcž?|Vqî+:+"qm:̘³EÑŠ4yüþµÍ·"%:)ÑÝ}ýjÿþ\§ }W•jn`œœŒÃ“<<ÍñiÓ“7ŒÂŽÌ•I”ðÆôR“å°¹¯…2D†dÍÃ7ìiø|îw©¯ƒß-Yõ`_­Ùrõæö÷¾óÁÖÛn¹>¼yS‡Bd’ãû×ÚôÌ %IntÖŽæãw½vhï�Ǥ‘s>œ/ QðÍðËáQ­ue¨0èÍk”‚ŠW[YÊ 1‘UçWô�p£æ–kÇ/&Ür, �Åç$É®{\Ø<½€^[s=�WÑÀਚŸäµz"1�¤�ì²òR€Mš9£ÄõKKô¡Nr¼· „úKý*`DÙëê"¶NÂ`!¯µ¤§HŒý>×F²£m»¾µÏ œ�åÜŠœNíø$µåðë[tòßK®Ò´ÿ`'MŒ�ñýy$yöÏ=·DqTœšóf˜L)n@Î)5X;ÜñN~Š�G?X�eX£Ò}§†ã®1ÅDƒ—f¥o\xf™ÒX ðkd�Á#H)Õßrö�7]·mõo»ÃikY…ë”›5²MÜž0=Ã5U¾æ=½'é©gw¤Rh*ž Ïz{O¦ž|æ…g»Ž÷fˆè”î0Üäƒþ[¯’ ï×Jyòp.²/D`ü&‰eÎÓ¬¹ Rï8’úGà"1kˆ�SL>Rîä´ÛU¨øÝôÏÿõe¨¬ž¤a¤:»OÑšÆÌj©%Ì¡Âl}2C\�܆òcÏì¦o=õ i?dì7_KÁÂ0A¸�±ÁÐý8&;ÃpäÁ¯ ü=þšWS|Ž]l}�£’7迨 õÙš[‘¼UM)ˆ'6Ҏ믣ǿõ-aW-2ØXÒ9L:â¹®Â5¼!°·\»�DD&gS÷þ•Ö°ôüøD‚v<æCÊ|½!|òù½ã®1Õ¢[åYRt¹ÀŠz°f€Éq²EŽƒ¥¥¥Åë;Ön^ßÑÖÞѾ&zõæ�ª±¾.£#X�|r*ŽÍæ0h||‚ ¥Âìgž ³�hÇÐ7 ‡¼�4–bwH®‡�ÀÝ|E$Uð˜J-¤C˜JÐ… OŠå…ÊŽhÍ©ª««`ªê2v³3x ¨.r%Ìκ(G¾²;AYØ]¸¦J¤®.ïhòddHkÍ2('![ÑÜ#̸f‰”¬õ�&ŠZT<1ƒý$ëî£îînüÎRÔ©"D¤mGyžõ5§¿´­HÊËÒ�™ÁeÏ_ÌQŒ¤æ„õädÓi˜òRtñä¿Ý X†Û[Ø‚ÁkŠ£ÙðäÉÓ��¥$Ž >8×Ï&¬x-MŠLATÂ$’³k+Z«%ßYÝ:RÌu×⧪¢´.VUæONÏN¢?Ë%iª–ÚI»™;àWKÊi�lºÇ—Øà…ï�H$wï¹ëŽ–�~ð]wo»öò ÍMõEWmÞ P§àôþŒ¯ÀÃêìRÝÝ=‰×vï?ÕÝs¢hpxÄI$§ñ9�Ðú‚Ê¢Èþ)%r¿yúÙ÷w÷žœÙ ÏäH”‡Ê¥1Š)~µ ý6•†‹U š‘’$µ HÀ6ù!á´VuU”Ïå9 ±Â‹"M‚��/ Ëî×8S¿ð3íÔÖ1øçð�(Våy ÈÆ�Ò�#}Tƒs-M1{-å„x8G=ŒÝaWO¦ïÔˆW?QŸ< hغ»Êv-.)_ù;åï7Ä —Nò1/¾îœ­›0˜, 0¯”L*dzc�QNÞ¦' �HÚî-›ÞâŨ²¼÷Ñ”9pøŠè3Êeâ� ¸›m¸9 9G%>¿Ã ß4À¥)¯Ù™÷¢�~ê Ð` Xª)™aT«ÄTÒ˜mK}6Y«W7Ö”bóê"åæ`+‚×c®vW›è«àe’›‘ݺ»ÀTÖ)´Á `î¯þÒÏ®ÿ±øàCÛ®ÞX[_[£Z׬¦ÆÆzn\‚1�a÷Þ£O}óÓS§ŽŽ÷tRáôˆ øua \¦§“ 𢓤M %ö¥2†RFÑT"â™^ÍITvžÊWD¢áJ.j1BÈ+G×–—†¨¦¼„ÃÚ…×'|¼¸i­8¯_‰ˆD-Š’Ì¦Î÷ßå^‘%) »å tÄk%ì©€âÆ€0áæY ]c@9Õ?Ì n1×TPßàÔ·+£%k2i”#“êÊ(ž‹êÂM«cØhµV–—¶áyhA C-²+…ø÷¦‘v› ¢I©HM…R ?0QðˆD7Û…¥ëêëÿðWxý;¸ã�ÚXe Âi¥b/±hSŠéÕ;_zíôcŸÿÔh…ŠÇVE �ê’"§*Š‹èºú´'²Ù}„|Pwî¨/£šH�"~—|©$�Ægé@Wßɯ}ë‰Ã¸Ö³"|y$Ÿ@2+í~EÔ Ù‚P¶)-ž“Q\Xà3©È‘â ¹ý˜GÛ[4™íü#!-J!† ëÈÑò¹åMk)ÛÔ7¿bh‹×H±9~Ù Û®s)’óÏø ÂT*e®þððDõF¸›½ }9e%á³q‹£¿otqc£.«¨Fçñ� ™íͦèho=¨¾>åeûG´M�¹i[ˆ'#ïqŽ_¦ ¸¢Ye˜f!Í9ÀÅp¯ ~÷¬h@e‡P”ý¬�•àWî’Û—Èí­jÉä4_°Àd\£Æ'¦˜ §²"¦4ÏÀ¦b&!oNu �~—lqž¤à"’ñV�Y“¹�Í+ƒÖ/ €ñ#�Àf¶ ¯ðæõ-%/[•Z¿¶)QSY6ŒÙð³hÖ+’þ“(ƒš!§�×­m"HÑÓŽ«×ù·^Ñ^}û W¶¾ýŽ-›p® u·«çtÚuM³ÔNRçè’÷KA}»Ô]œââ�þƒßùÕæ_øÉïûÞºê²ÍJ‘C9¶¾»é…o~Yׄý‘æX¹/[¤J+øÞsY¸Ôñ—WVH;ˆ(†ô$z:y(Í©×37Rb.˜_ìMPQž  XD\„ÓˆS[Ã#¼9B>ÿVˆ³ưdý¢R_ZóXoÐge\ŸEz…ªàØ|ihÕ‘ +ÇFwÕôìÜl籓Z¢�ˆŽF³¤Dʤ¿n“ˆ-f.¿|Sæ_þîOoxàž[LE> sÉDêÄ+OQ!¥uM¹<ßó5ùs) ø©�ÄOÂlO»ÑȺBü¬-,DáÓÖX¦Zñ΀çjc<1߃©d•Õ+â_önuIÿØâ³S(z¿•áð‡øxþ x›Êwª–„ÃÌ/ô+òfX>v’^Ù×E³£êPóÌpš¯ÃþÎê ‡ûUÖ¬fÀ]¨Íÿµ€@ÀvÃKýGgÛMŸ­Bì+´Ú^nJ~.€W' H2IÛ�ïV:~ÎgÁôPÑy[H²«3)6›âRôV¶,ȱêJÈ”4"ZT´Ò>] S|j’’‰Mħ)‘œe@A”ræYTü™�v]£\€Ê4Ò\¨×±b0À…#ôLÑ,ÎcÍŸÀ…›ñ;\cYJa^)uÎr˜ŸY�*;Üu¢›4-Å÷€ìÚ'àðmð[áEؘ}ôCïoø�_ûéï¹jSûÔ_üy"å˜ÑÓ'u½¶³ð‹JH¶e eF’ét"™ÔUѰÂzM¬×‡kùܾã´ïøéC/èþÂdröI¥T—Ô�L¾EC‚¸W:Z]…bZ ))≠ábD9ÚP_É7D¾Z0‰@8‘AU (dØ“HŽ(«�®ýY©,g;Äb޲°‹"‡æÌÞƒÇÔÁ£}üP®kkbÐþN× )>®«ì?t °Iž¨ÖP³×fI€âÈ«ž·»@e—¢º2¶Âôç€-ê+Ÿ¦´ �’ˆ†�‡Ìwð�¡1O€*ËJ(°@ ±4àBb ш—Œ”R¼ñ(++Å»¦‹C!Š”–æ…R]LFÁzqñé9”!ÛÀ”¬ žs e Ñ úMÌj'ø=iz®§ÄIê;1ÈÌ/¿�×�¼eJ°¾••†ƒø[çžùà 1&ÂQ‰e±®•zH`ýúu“¿ù+?»ýïzàáæ¦Z¯E"¯|bdèØKÏ=[Ü78òd“¢áBNYão�™6‡Ü�{t<í‹�é–X¹ãÓšå• ôêÑ/èùöx|ú"z žX®™í5$Ô8­u˜7�ÜŒäzÇbL¦"òÍÅ͉95;…Q\ÅHqÝž_~S¶û›àº€S®¶K\-MŒmm‹ÐæÐ¾W\­µãQ}Ÿxa/57Æè÷_�.øàY묡Hq€6oXKèf)•—^ÙƒúIÂÉ¥YŸ%/âÊ91C,Ø4‘;~–¾ÜM[Šqj ¯Ò@I†k(ç.Lfø½hÑ[@d‘êR~Û½Oš.ÙüÅ~ŸŸ…DQ4gÕàëo¼�z{zèå—_¡‘‰¸hCfw!ay¥³×%¯ˆ8œS[ØÔxi-f†ÂE »»ÕA>^‚T9G‡ÑÒRUXV}}}.šµ—[ÂÃXU^j{L^÷;$ú Š�cQ>£”£ ÉÃtr òÊ6¿c>íØg)ÅÂÈbãWeåÝ¿°ÆHz5oUèGÃæ®\í [Æ*V7‘ÏÃFK®±¼dóžëâÂB®q”––ÐºŽµ´aÃ:š)Ôï|šIÞ}*F5ƒƒRê�6†,y>GÝ%>ÍÀ•kžÄHŠdÄïëg ÉfrÉêe%žºûø—Ý9l U‰€mûÅ|ï;:>ùáw]¹q]k9¤Šò"6•í�D«kg{{zÈ"&RÊ¡h•¯^¹bÓºÊ5Í ÑŸÛ?§¡Y¥û“”þÌ·^xùkϾ¶ ÓOD=ð¤G†–#" J“N…âa†ŽΩ¼.MyW{BkwÜ•mÎk# Ù"‘RÉsD¢l>_Lê¶QϦtr�d‡ýUU•ª½c5î>˜éî;åô¢añß>ó(7%‚êxVýˆ¨¡:Bí-5´ç`¯Ù·ÿ€[Zqn�–r^:ç4Z¥í±1Dp,nknʦÈ©Ÿ¤–�ÀZ^i>/¬0«raˆì�ŒÀS(çÙùÌR“Ñ»6µv®ô÷¥4R\ "¢®ÍúZÅhþݾ}+��ŽÐÎgŸ¡=ûŠHê>*̳ øsÙ ­åJm•ÂyéEøèóÄÄ_J$œJeÌŽœÑo³Á!`VϱS冒 ‡yïR7 (––͇ÐF])g1³rf†[i©­µÅi]ÓâN'“4†%І“Çø“\5á@�ÓBìH50»ëòŽfïÁÑGz†Ý®cÇéXsÏ.ÐZç«)ó¬HEË!ƒ‰•ˆá“Ê‚¯¯Ø.@îÜëkO&ß}21ËŸ7¢h÷òݼû]¨‰@¥Ô`æˆüç“—¿™)i„-Ľ¸šZ׬âûhÓ†úÓ?ý+zu÷^þ‚ÔÔ¬”búðÑc'xC‹¹'©ät›ª­©ð!BáÆFo T9îgcˆA @\‡0’”Ø… ©‚Y>G Ñ;‡¨ç¥/ß´~õ{¾÷�ÆG¸«ƒ *¢)øÂŠò2dF9}[]]yFn×@]{Õæ"zöÿ>ûåȤxÇÓ2Џ�Ä–+µå—ÔVµÒ*P\¬Å‡çqIˆ{Hkxع�¤mM]Ît¶¤/„E�1"�ƒ«æIÄçµc]<+B–�Äi- _0/)-óåAéU3ÓS<œÉåÝÜ, ¯Ÿ¹è,×-Í̇‹õ'O ª$˜6…Åè-©åbjþLY·¤Ý¥J4bçÆ+9g�š#=' Ì,sè÷uö�µv„RÞüñ‘ îp† S?M æT¤e¡]²‘_ø5‰P t¡<ñ@žü�i© 5P…"3>?49•H�ŒL„»ŽŸä˜ëîüv3vœ·VŠ`ˆf˜õE’VóÓ¡|Ï|ë©×èÈñSÔyìÄÁƒGOõ~èïY�TÖæûï¹µ;þ½ }]x6£%/d-´,3XÿžøÔç¾úßÿþߟqa¢îû(|t¹�¤@&ŒÕi¥½Ñº5¸ð>oIÙÒ€„ »fmñ‚– ±ƒªdd.Ã,í7/µN×ÌgúåÕªøjÉûòÎüÁ¬fdõ�8*æ89Ú¡Œ›!tòŒ•—Ë¢&²;›Œ'hð€rOÊlÚ¥ÚX-¢•èòÑ£Õ™ëi$¶ 2OŒ¬ìŠ#sT2ËÂ2ŠT4ÑàX¼üê58<Æ}«b¼ ,H¤[_g5Ž*mg³\•ó5¾¿ €KÚççÞ�ÑÑ1ž‹ás8e¾˜”£« ÃÓ˜@ø’‰…‚?ÏÜ€³½ ë›WÖý¹±ñ)›îÆÏ){r·ý¾ñ<Ҽǽ†È¹ÞS㯽ÿ}ïîøÁ�½w˺ËÖ=†¤RêbGï�o>úÔ~ü‡~6Œ÷\"W;EOË,7�Ô c«ZkU ©*¼fIJHñR€Dæ¢û©²2š³¡VÒ±î½JúŠ£’3³9$Õ•¿t§«”Me)~µ5¥Ù95#)›|Τ¯ŸãôéSxXÄó­ ƒÜ[’q�§’Ê Ì²3ãaD……NLÆÔ©z×hZ½º‰ÓeËcʺâWõÝ•ö]¹–Ú¦½üØ�c§V¢NövSçÑn‚ ,­oo:Ǧåüê$EÚ�CfÚÒœ/Ù‚ºËË‘’ñ‚ §D½v„R™49ZRÐ [¨ƒ®1¯'Åã"Ç’™é9–_�sÊ �ˆÒ§É³a€¢â¹*ÒüHŠÇÓ—Ý奂¹\]UÕÇÝo+ºÿî›7WW–û`´Ì  ííïüXÏèèx»d–^ ™�ÓrÉ*iÿ/ÑZÅÀý®A4â”EÐÕ^&(/‰® :*sÁ+Ê#‹Eøì¹éì" Ï®IdÂ_+X~eß}…¯—<ñIÑXæŠð«¤aòmœÖãÆ®!èïgæ Î1séª�kXQ ;¥€E–Æy¡=ë) #ÿZ-熳eÞ…)ös}ih~¿ŠÚ.öú�ÚJÒÈÐ KÆ`®÷â�D¤ö-sÌ•¢á""’K©.Œ/੟J;œêB‚úNžæâ¶1Äâ�ŽÖ ¼_¸ÁÑÁFˆSµ�j û9¤Ñ¦ðÿà¯R $ 2Hq^Zæ{ÿÛO¿JO¾°—7Sõu5s×l½.½mËÕM kZ!vêôÀ±�ýàϽAÇé}yþµyöe,¶ÏŠà…dHùÍâ"Kž�J³|ô’š‹²5´lꊥê³k'.Î;¹[ãì@QêµÔ38[‘´ |9­6VM--k¨3Hº{zi\ ïí-õÔXWIYÆbI¸˜¯÷øašJÌÐó/ìU»�j/‚I‡J^ÞpÄÿá’r5“Ó(½Ðj_SoqAì-×K› h„,oÑ}¾—zO¼Bñ·^Ï °È©§¾ãÇ8)ñÖƒ%,1XWàDZ§¸¯u.H£žÂk�RŠÓ\'O y Ã)®Ç@jç~0üT�VŒXc›³qÃÆ �Ú»&|c}?ñûò·�ékß®‘ÆÉcð§ìÝå%õI®•*4ò¨|>Þ-,ÁxÇ€0“ÃÎѱ8ö‚…†³V÷ÏáÒ±8SÌÏšÜ�Yð0’;'Mì¾ ^-˜Èü {nùS ¬r¼a}:…ñM [xRy;´£˜¬X[SÆò)YS~ȵp*(žœa¦Ó®}�ôøSÏÑ;~›e{]üÆ ÇÐhœú‡§ÐÇ0ÁÓ"1“‚£°Å™¦›KœÖò…–8›ä’¡ÞÀL.Œuà>“¯bŒï\rŠ#‡\˜ëxšÄ“ìÜk"ëÇÀèdêëÏí3ݧFü³)£¼Î5×\«®½æjÇ«­¬á¨êpgWÿ¯þöŸ~,­YÑþJHq}?åÐô"T“ÒK’ò\ µ¾Ô4/ô''&¼û•ó ¤)²û9]& ŽLÄó‘’9kŠ fçc]`{lê…ýBYc}ŒZ[Û¨05˜»`Æ‘žÄµ?ÐÙÇlRg‹Ú©¦*ŠCˆ.[Ó@¡Bs^eþ?™•5ˆ©¤¤„**«˜}à‰�éâãÊ«ÌZ!eG/å¢\2n`lin¤ææUtÅÕWQÚhêGj÷j¾Æl3{kÿÆkP<øì7wevíëq] 2*¡+7o¤­×^"Pìâ/ª XîÙ{°çW~ë�¿ð©Ï}¥GHR£ð'%I_H Q"LVŸÖZMû|ú ³"×Ú0\蚘LT8—I"ð¶`j!´´8'¾f©§Ê²±lAý¢kXs|˧\uåfUU]ã‚�Åá>)ˆLNS:•™÷þêjÊ=½!fv]{y%â“ô�GŸB—øàJÙ…ó}Ñ ©—U«›)\\Ì©­ýÃKJ«q?IfƦ»¤Qñ’-µ$4÷Lš©iM Ž'©o`”wÚ¹LŠbma¹–£PÁ킟š å D£ÑŒQP÷Ýu ½ã¡ûX¼Ô¿2 ëÔu¬ûèoüÞŸÿ¨¾GŒ1!Q'þ üÓ(t!�¤Þ(Ç.j#i\X¥HQ 7»|ýŠŽÜ%èxýƒcHÁŒst¢Ô‚tµDÝw™dOŒ±j½¤mƒœÈ™_tª°†�ª*§;n¹^]±y³Â”Ä j%܈ç銯‹qö5c•4€ßze;µ®ªB�¥�žyþ%°çh¥XQqmÞ´‘V5Õsñš[Ü[²8‰y#ãÛ èή€ÔÇÊ}Ü£ üíTß´@2FÃcñœÕâÓ³<‡£ëăIÊ(*)« U«éÚ«6Ñ#ÜM½ýnÂL^Nï^ü‘ˆ‹†ÌÓ}¿ñûñ©Ï~ñëRŠ˜–(äë2‡�.4�‘Œ‹ôT¸À„ §’T®>\ψÄ3¼ƒ6f1MOËôᛌØ‹ì�*Ig]Ì žºÚºáºmzݺ ©9„ƒÒñÍ3ß1îìHƒ'-b’¯Ÿ›;Vyton$;=0´rjÆP-й›6®çûlÏÁnŽL—Þ2žKïŠC\|OŸ•*9üK¶¨þ:ÌÅi¿l-UÖÄÌé‘ n’UK¬œž Cˆ¾Ob³ ½)*‰Rûºõ´cûºçÎ[èþ{o§ë¶]ÃcpWDM›ïcÇ{gÿüoÿõéû¯Ï H ¼W"‘G-Í÷ÂIP¨cÅpÇ5T‚DcË꿹2w™z�ÔSSÓü¡/°�¿üŸ»ÒgÆÄÚŽê”f¼HMiM±šJÚ°q£³cë5–Ú‚8ú8ÖsÚîÔ-˜{N¸'%\Há"ŸÑ”· âQŸ+iÎxSÓ*&tôžÂ`Ÿ=�Lû\øÀ+“¥^ìc ¨XŸŒ¾d‹·JD%—o\Çøuë׫Ҋ¤Ÿ&)•YTú�¥ëûúG©·„g£ f:³eë–Äý÷ßgî¹ëVúÈÞAÜw;m\×Î@vÑKĤèåW÷Ñ~êK¯ýÍ?þça©�¼~pÒðy¯‘øä¸ÀÑ*ÂÚ5ÜŸ;¹fGŸ)´>–²ÐŽ^`ñŒ–_^í, ' …WuÑïÎá�×õî×þb�Þ Ó}bÐS8¥Ññ8FoŽ}Ç‚5Äy.|@¥)95J¨“ÄQgYI«­K­ŠŒkèèÏ3ºÈ¨JxÂ餀†D(0~•Bü%[‚ðc´„Ö¢FQ]UI±úò‡JQLríŒåWzO�ÒÉ¡1D5)l$ŠÆï¾÷îñO~âÃÞ—ºóÖ¸‚I�¼ÙXÆiå§žÛEÿüŸŸéÿ‹¿ýד“SéV>Ib�8ò{rL>©Eäh]cÆ�ˆ«1-3.äg;ư·ÔòöÈׂi¸�l¸Ò¬7ÕÕ;àOŸÛ?8ÊEw°´&S–6m�;|Ñ�Ì‘c�ß�£\îÑX1f ÷)\¾¡�i�ÕgE`¼×Å“®Ü”�Ïõ–g¹”ÖÊ‘BE¬¦ )®VªÁk¸´Ü �'Ü~¤byÊâ}4Æ€¥—à 8™`° ÝÖÖšøØÇ?ì~òc¨\ÛÚì‹Åª¹)2kó¹"j"O<ý"ýÿþßøç¾ô�׆½®J¢g$‰Ó2™o¡i8iFtá…®k´ÖÊJ2çðâ€ÌÒi­IÁ!.È@-õŸW�E(Ô·®uÛWvÀ‘¯ØÊ�¯Œîf¾¾Ñh‰×h¨‡µv=P@mjóºfVÉ�™M�­’ËêšÊR‘Ÿ¡ò’ŽW”iµ€^Z[]Æ‘×þÎ^t÷·Rd1}\Ó2L+%Åw)Â;E6j¹d‹¶H$ŒºVÅ™a8K“ããs}}ÝÎdbÚ_[)³�dÉ\*Ãã—áVå¨,ÓRݤIëLm¬&ÆbÁæMëŠKK"Ë�ÏY=ù¹_¦ÿü¿/$ž~v竽"ÿ´­‡\œ@2'Ý�e(ÑK°&ëlå‚ìr}à`m(™�<ߦ1‘…Nç—êKÒ€˜Ý­îYº/©—‡†nuuU×®�yrÙð)LR¬ùŽé¥•× È”à—öŒB»«ÿŒLÿJ²ªê:Û<å÷Œ)} $‹2ãÚME:Á÷Ÿsì˜àK¶´í[5RN×ï¸Æ›‰£UøCáðìð`æÄð¤.˜ödãyÖ;Š}œÙ€ä�[]S“)¯©×uõuŽ'¥^VZªëêjüí­-<[gEšRtäèqúÔç¾6÷Ì /¿|òÔ©ÂÌztõ�eOm%á&ý†#FCSI<„“ Þ äeJ¬‡0áÝÄ”×,7™Z°{¾Q {>LÄN6á>[Q+N𢏍�:Ú[)žœSñdÊ”E™¹E¤ÔwXf\Ã$4(2bßÁNwhhÈ]i‹%&GR]¬šiÏdˆ#áÅ™É:déø4§¸ðu. î—Ljž^ýâºí×Ð�·^ï¼ýþ»‹n¼ùf_kÇF7TY—Q“ò�Û ¤«êÒWlÙnVµuèpI‰†¢ºbÓzºù†m\H_É ’L$é¥W÷º{öwî9~¼{�¤³¾mµ³.n IÁ§$÷¦°�¸^øˆi\Üqjr—Úb0hðÐì0TpnAô¼™íN—Ѯҹ.�ˆ+õæ„ …Ãa:pô”ªˆ–0Årdtò;ï¤y`ÏTá5«k)�Ó¡ƒ‡S.d“Éi_�šêªH[’ÇÒ›ÓE¾žë&º\ºd9™vÊ*ÖÖ­¥ëAÏݸ¾Ý«�ÛÚÛ}7Þ|½û½ï¡{î»×¹çž»œ›n¼Î¹ã¶œG¸‡n½i]¶v Ki½rŸSÌâÞ­_Þ·÷åWw?ƒõòe"zlñ�†ËŸÚJÃ�J‘}Ý ÒPiÇÑ>Žrk¼;Œ'XËfÓ)N¡iG/¤ØžŸ:‰R &’Ò ®øx$ P@ó×óÏ¿ˆnïQr3.w}#…5¯¦à5ÈKC ô0}¬ë0Æùvdª««œ‘o6†`sšV5T³fXÆ]zd•B”�NÒÓv€™ Ð%ˉq�.*¢µ^Š*Åވ€)¾_¡ÕÅ3x¸‘P)[YÁ¹½¡¡zò™Í·Ÿ|aÿg¿ðÕÇ@½—” Âi… Û ¤¸ÞæºfFÍÁ}³^D’ã £w9ÒA§5פÎ#‘ ¿æ+µÅ;M$k·®Wºò*‡úëÑøÕÛw<ûI*)R\t€Ï¯‰˜cS��?“áÑ OšÝ9yâDš�d%˜RLñœI+R³î�ÉùdL2Ä,®”ŒTö—Úý—,'l®ºÚjÔøÊxÃ(cæÇÊE yòd?=ù쮹o?ñÜ‹_üÊ7ÇüõN"z~œ.°éE&ƒÓpÇpªK¥]c„²krÎJH$f€Âœæ ‡ùAçc|ï $äù÷óÛÉ·‘‰¢nÌÞª¨ˆBбºFÓ Ãösú às¾?crÁ½>V ПuºŽ0`ÔdVÒ@%¥}4<>Ãr)ŽÖ¹\tl‘Ýʨ°ç”|)ÝÅvÜ‹6ee û›Á¦“Ó^=„þý¿0ñ/ÿùÙ¯ÿϧ>ÿi€È³Dô8¼nV �°)x�ŒÑÈÏÍpû„ÊO‘ ÁÒåJñCÏó Ÿ3�-´ßüëâ(e¥ãE¬ñÍ`hNdšeq¨RëÌÜ‚OršëÆý>««ñ»Å44pZ öÏ’R+$ +àô¦?ò}ÅÌÀ%™9«òÿì½tÛø¶5~~2†™™ÊÃwæ133-ü|ÌÌLt™ †Ë<¥)¥f‡ÍÌ’~ßÖYZµ§¯Íí´Iê6Úïí%;q<½mÚGš-Á‚H˧¹´ìÆõ „àsß켇¾ôõ·ä¿þ�ŸZýïŸúÒÙ“§ÏžÌd2WÌHdÔAzV…D…Q#É+<ë!¶LÍî+N±¬¬LGÞ+C0ˆâ#pϨ±Øþ|‹P6ãˆÄþÜ, ¬(öÂ2û5r¸+éöøœáCÅ‘ ‰ ¶ÛU•Sþ6ŠØg¦§SR—ú³0Ì•J§yøË�Ø>_Þû|²WL4>2Ì#G"ù±˜è¹ûgÁBAD2é ]»q›¾ùÖá &Ö§9q}eeõ²9h8Æ©ÏrD’µ<Šæd)åU�Ä Ý@,øD6;·F>b¡p*-�ÎòÏ…øÈI�íã…³ùÕ®bè9sQ6æ‡Ã†Ú—ô0W0¦‘ÑIš™[ åÕu Fâl¡AbkiR3�&µ\!ÍeÚ§G',‰F¢tøø™ü7ß<<ùů¾yîÊÕá‹HŸ"¢·M‰�ô<‰ @¡kº–W5ö­Él�•8Ü×½øÒ�Íón’L&ÏQ ZS‹»²îu]ã͈›Þ­UA_þúÛ9/žz÷àñoG"‘¦gÖúõç!"a8t] D#zÞ4IÓ¥Üòf)\!ó~ñ¦ÆNOD¢Iöã2[ùDf®Û¥ÍDaI•Ω-6hTô¼í”b`pP ½%}�Ý )yï@OûV©ù¬ˆGƒ¥\$!—ÓIååÆmár9ø3 ǹt_Á}KÀb’‹i)sžíTvHšËJe©ªÆKáNpÑ2Ô® ß¾röüåƒÑXÌlí¥ ¾pÏO�dŒA:bˆ O Ç­FB²¸ä#€¯×Öƒœê2ÃÄ-‰F ©-;èà¢)G$Âö\Ø;;;m}Ý�Ò(¤OÎ-s á!`ùÝ�ü £¦291N¹L¦„ lÔÜÜÀ‚BRòFH�ÓxÛ:ƒ u¢Œ�d.RTœ—è³ð(Ð¥,ù õl&Ëνˆ>Œ…T:6Þ‚¨ü7)å;Å{Džw!‘à°)JÖnW(§ª”Q/Ùâ|¢d;ù�©–{q¡vb JRU•r\ô¦˜ÈÍMméYŽD¤þ|çµ¥¤²ò ÑÙÕMÕUå|µŽ½î¦·ºÛŒE^“¼°°@kk«¥ë?&[µPu…ƒÊ\»|ÞK‚ˆ„?gÛºÚFϓȆ8ÝÅ"BºI²vš|LˆR�Dò‹:³èÆ�ÑÌÈØÔ¹ñ©Ù•ÎdÑ ˜i Ã.AýS›±»ÝFºd ø-Ï)jºa3ŸÁÉ*ÁÝ\ºn.U*Ú]œÏçHòÿ Ú$Öæj3Íå çB¡Þþ~ømÕI#êó ”R>ÜE¸ÂM{»82õBäñÌ—rC¯Ú$IŽf¹b³%ÛmäÌ]\€WÍm‹f´¢ç¬Büc;–L§Ž$ݼ3BW‡ïänÞŸ½ríö7†oÝý'Ñhìým°/É9’<èÓ5=¢êzJÜ)Êmët˜_Xã`´�rDbnVäß™Üä+9å£SìBÙ Õ@ª¬®ÆúQEw�1¼'•ðèÞ¡.c� G‹ËK‹üå!!Jt³ ÷Zxzߌ�Q G¼Ìëi@h’à=£G™µçà³fÕ—VéÈñ³¨‡\‹Â3ë๠—ÿ|íþ²(çˆ({Mw§¤¶T0&‰¢¸ú µ% ;yM“´]ÀžN¬­­¼g5O$MÓF} ¥zÁBÞ^ÆÝ[;6§è’ÝíMÒØã~{tNHMèk‹}ï\tWu�|Þu“RÍÌ(6Œ*‡�âjdñXZóÓª7ÄÂòTÄOÏ‘ÈÅpÌ‚9�ÍŸñˆÄ‘Ï2�8s�._»9qæâwŽ;ùÚ{š³!šyq¼Ó„„¡ƒyШUÄ£ñt6Oñ IÛtÚ°ÿÖúzˆmçͰ–£#A‚ôÍŒ’¤4ÅÎÚœ*UT7ˆý»û„±r÷Òð8Ÿlï½÷Áá°Ñ`o;·{–Öhläe3©Ò|Á$Q%}ß«{y2?K¡;ÍG±Dê)™þI"=²/1Yxv 6>�.]½A£“³kç>þÎñ“gNÑè0õÐ}ØÉB"5]W2¹|ÔˆHR™Üv)+[¤BqP\lj ¡! ‰ÙÌ·¥.7±|'‰i+Ûa©Idw‰¶¶6¹g “›®Ý™&©?4êãÔ¢.º�ŽŽÑÚÊB‰ ¯$§»{XzŒ�$|za•wÕólÒÓ€” F÷ů��™™3"!Ä¢~¯-Ð Î�ÃÁj:’„š�?'�€mÞçΑ9ÓØE1È‘Éætl GW¡×§ÁQ%šZi´rAúµ1Êdlr(Á3%»ú;x6cn~A&ã‘Ýé.¨¼Ë‘êÙ&E×%ݸ;K“³ËOm§EanD²?[»5¬HÄë оúí?ý�Ï�¾øøÔCæˆh I€KH ðƒ+`V©±d:Š&Õ©E/­ù#ܦ» àÙ‘åÕ y}”çýñåUYHum„,(œÚÚ‰6*«i¥þÞNœp«høî ÝŸ¨³G}m¥‘ÞbQŸ_\+K •è—È˜4Smunç—½tõÖ¼>H½°ÒYÏ~Vòщ%"Á`ˆŽ�:¯¿wøôÈå«Ãçu]¿jD"àèƒíN,!‰€Kæ1‹Âw.žÊ$½Á(ͯ¸^"òbovßx(£é¹5™\¥c§oÓÕ“|Àï6Ï\8Ì‚»›Ó!;ÒˆJ¨»§�^9ÐÏEõ®Œp=á).Þ¯ßÓÙd8ó¶Å‰‰ EW³²4Çel´{ï~Ú¿§Ÿ몹 ðÚí)ZXñ=ÕÖåÂÞÌ—®í¼„ž�[ wŸ¤³¯�ݺ3zHJyšˆ@‘=§°=y?,£ ìktÉ1y%(pEZ�œó6�×ò®®ú)›É�Ón£>äó±‚ss\ >3²ˆ˜“í;´·_aK»žæ pP$¢þ®ÖÂVJQ¸`°+ �Ï,q'tE¼úòé*¯V¨Q†™’|*B+«ë4tµ7>]¯ A€$.¾ e»„Í‚ ž—º32Nï:A'NŸ§ÉOôîèĵñ‰É³¢;FDÃ`ô±Û{­ˆ„‘çÁp L€vH#,Á| UW—óN‹úújêén¦6¤C::1“ÐÊW´5è߯¯«ä”¦‹q4€@ñ±v•”•¹AG%O¶àÆ\bew:gìe÷-²²P^ÛFûöîE×V G“Óó«4gÞïk<êlk¤x}Þãj´¯)Ã×.+‹sS%9V"ð>ïÙ=D¯½0Dø:ª‹-R˜ ÿí=C´{¨×xϸ%x©  BÅÀ…·Ñ6£‹+™JÓÂâ:ݽs“ü~I6TÖµÓ®�.D´NŽJ&f–ù—¦êôT M!±9 6=ººu›-ð6Ðõu…õû›ï¦»c“$%¥½>ßá`0ø9"zœÚ´Ö^KHqðxÐTèuPÅPb^(B‘F DGãʕޱ¡Ú¨�pDÒ…–Kìü戅S`8Íòã\N.šƒ´‘wÿNðI«’w”@¸@ñ˜m—‚Èæ"Ò2fJ«’,té?òØ|RªÂsëIEˆ NJïjAôÂbÉPÄ“tD‘˜Hý£ÑˆL �g­tÂqðè)º;:‰¡Ù¾¸q:�t&óÎÔÌü?^X\>�ŽÐ¼®KB»/Yø(l´uÈ�q0¶‚íóK>ÊoÛ·«ËefåC,NÌ…T÷ Q8/DBpÊ Q Ÿ€Ð*Ì?Îfódu•:í�×÷b#sÑ‘ùEu‚�;¶!•`Áár“Kdiye�ë ¹|žmT07B$‰ëcˆR8í¥(6Ù×? ©šTŒ/gSc56ÖS)ÁæpÓìŠ~ãÖ]£Ð.ŒÏÞ÷¾²›S§>‡æ4ü¼QSa�'S$Ì9'üýCÓ*æ4ÛÞ�¢ˆE+ UDÎB/ ï¾H$’¤ë:?¿Zx$°7ßÌœ‡gC® ßAd£Õõe �D}�­ˆYü9ò�ôGÙÖÖ®¹ËÊmÈEs9ÐÛÍïc‰€OÊÙTBLŒ� „0>göôrýîA©¬H4ÉÎÁ^Ðв˜LÍ­À‹l�fVYhÁé\ŠÄ“´´bˆª�7·¸Î»²d_2UÕ¹õØ“qd¶ÃŸMW džBÔ˜nÐ�»ã„+gòCTær‘üîÞVMdbjŽŽ?�­Ûhú˜¥5¯Ï¸ø‰C@¾ŠnÂÏÑ0¶q*Ó‚}|°ãàaÐþÑXjèÐéa'Ûïüâ÷Ó®¾vîô‘ò¡¾Y �¿xR—ä°óí{‘¯æÙ“X,eL̳9Ë*ò$Èm®Ý}¼Ž-ÅeÎ�(\d‡ˆ<â0¢{y= ìÚ‹ÔÖ:�£ËÉë� ¾¢¾î62°»¿“ö v¿h룗åêºWLNÏÑ+/î£îîÎ’ÚØ×Õ,^Þ?@£S‹Ü‘6¿è¥ø„ÊŸÃâ°Zšv0í¨!Rs,š00å¿ @XÐþÌ5¿îö&þìÆi"’fªOå¨GN¡5 ˆÛlgßÖ\Ï{ðì –¶.rVØd^ ‰8"‘@ Hž¥nb˜™[ iÊà@/íÝ5H-Í�ìîÀ¢àõA–ˆàóÃÅÃtõú-.ª›&¯é@04Œ!Äw‰hìéOª[I14Ðf¥”í€êH,UáÅ�vPNylaм’Ãcyò…0Rq½W‚ˆ’Ñ‹ Gˆ@MD)‚ç�ã~÷Á¨œ[ZÏàuŠOͯ®�N-Ç–ýéò+·flGNž§Î^¤¯Þàt Ò3Æ>!Ⲷ惀#"ÌdÌï��Ò©4ç÷ó" ?ß±ÖïpíåöÞa˜/úüA£C/3Æ9XÂ_‡ˆ|ŠˆÎ€y²PJBÂÈ�sÄÖ)†˜ˆ:|�\Æ—&›Sñ¥«`3ÇGä!&ì&Œ#„â‘Å0c·�J](΋úúšÇû²(滽œS[ %";%„÷Å.iii‘òªÊéÇò27Ÿ`ñ¾ñwØ©5­º¶AH©Ûb8:l6ìï!7[*°5;-�í›óœvÂg‘4\ÚˆÄ_®s$S¶ÖA4ð¿‘Å)>¿�d²X"Š;ˆ2"U—2ñ‰‘¤ áÃg{éÙq¤²nà¿uqe-ø!"º k¾Ðm¤Ëü -ÓUË«kÎå•5aÔGðœ\‹Â|§ÀBá(G(kë^ }.Ñ-¤½Æ'gÀiDFKdD�‹ÍkÅ< ¥àߥjGIÀ}¿c<³ß‡ ÑÞûÁ‡ô5tgÝçÁC`Å�÷œÃ–Ãwˆèx©ZžXBR“e„ínM—ue.Ge"•)C[%G¸’ã�Ù��#7k(Ü6L6»ÑååæŽ.¿?ÂÓñµµ•èªaÑyd·úrMÄfÚ£|ün- ¿7°Œ7jU)¢å—S:œˆ… žÇXõ¥ª+šÛív¨ªÊ'µ––&¤€êJé*–VVÖèîÄÏÉ`Ç §ç„ Iœäyslf‰Žž¦ãÜ`.ÔI4ˆæ¢�/A þ¢‹kxì„ä.†uG�ŒA(®-­ÞÆÉoÁ‡ì;°cùv ¼‰èåDâ"¾/—ˆèŠ®ës˜s‡ÅÌ®šã©t&ˆˆ#Š×Y‡€`ô7µ;ì)dKÕ;¦°ÅÔÌ&³'Á)nq�Ú§ÁÐÖJžÅZ€ðD @ˆ ¹íun~‰æ=‹ˆ(½xŸB”Ëæø½Á &ĈïÛìÜ„R`‰F!ˆÄ8j;wñ*x…£·^ÀD"5ŽÛÿ¢üž®ëÇL«§Y(�ÉÆX¿‰/’kÅFÝ]ß�¹�뾈˜ZX§ÿ¾ýôÆKƒ¼Y�H>(�+AQT?aQiÄãaÏϧN~€Ûi£D"!©†àØ­Ý·¸ÿŸWê:»åׂ$»ËØïÞ‹”JŠO>ÑxŠ ÊûwuSMe9Û´·4ÔØSÉ„¬© œ ùŠ_jLÉ÷ñû[2 µ-ø7usº çNK-,{ù"AsË8éhÍ6Ší‘VíëAì~Ÿš˜]:�AÙ¯›¦¦U ¢çûÚÀr0zŒ#VûrÔó •{b|3‰:ˆ¨Ád{8}…5ªø2áâÊ aAPcwÖÔT7!zQ½¢(@÷J^u"б¯{¨-VîÚíl+T_WwÏA'^ncFË+S‚�{v Pfk¦�N«ª¬À÷íÜõµ\‡Áóð`±Ùv_2õ˜p(BgÎÈ«p‘ö3D0�v^d +rC¨Qðñë!==´�¿Œ�Ÿ­¯©|¥®¦¢SŠ­m¢{!?üÆ^zy_/O´ß×1!ôoÇc¸@‰+8X¥ð½ºº ò²ïÚîË�zá©]uDN¿³ðøW‚!ß�?{œ°¿šS%�ˆìû_$´óŒÉÅkc´H¥^zåõ2¯ß/Œ.¤]Cýôk¿øÓT…÷ޤ,™6Ñ ç?ÐÿóÿŒ¸;1/öíê¡zc·8Gb sf$c|þbËI¤·Fq"öCHVÌmySSTׯ×,;ˆ¨Ö• °tƒY0g W¦¼�$uTT”7áä_×ÜÔ謮¬P%‘&„ˆ´67êˆh*�p æ'œIA´È‘„Ì’Ä߯ÚÚC�XH�âŽ= ÌX[ BÆi@ü·ŸÚ{æYZEWÖŽDÍå‚¡È"jG§P –RÞ!"ÐJc=ÃB¨?¡(ÊëíÍu?ÖX_õ:òƵŠÔ„Î+úéG¿wíêÚÐå>Ay(zÃn¥Œx8Qˆ�…„Ý~ótŽ"wÓ“ujYàÜýí×èú•‹÷2°gÇ÷!Ú;uñyVù¶î!Nº6>ùdô ?ócôÒ û¨DÀj.]¡ÿùÉÏÊáÛÂJ|i?_©£"•¤eO!}uÉ:Àiðò6^í*Eéj ”ECÇå`'8öÙ¡hˆ nD”S…Z�†ïŒÀw¥^J}HJGmmõ@}]m[zì[[šÜ‘hÜaÌ«hºÆ5'„¥¡®C¥ üCDɯ‡P¶;êG+w'Ÿáo‘1¨ç‹:s–†„[WL×tB¤ÁÝXÇN�ƒíû”¾ºæ ­{ý‘Hô"³D4®(JTçB�…g\H Ø ~? °©®ú‡lveòÎn„蘄®£xm7ýÈ÷ìúÖf>iºäY’� ò\-ÕòÖåÚp¹‹ïÑs•°°¸xèk^"Ë_êGߣõÕeã •mo~öG^ãÖÖ£g¯³[peu=Õ4t 0á“À«Xwû‹?ûã|²*…VQ÷±ìÛïÒ®^¿©+Š­Íõs8yN†"‰º¯‚H_E4>1Q¼^-Ámy è6…-o Ž0é]ft³<€zeW¬$½ÔàûØNDu¨eUCˆ*‘Æ*Ó¥t!8:AÄ‚b¶æs”‚¨„ë]¨WBTÚ‘ì¥öïæÚ CÊM™L×@C@ÐùÇu�ë7îÒ©³hfÖ“\ZY]C7ÛwÐH« !Ö¤ÜÌ5“lôô!ñÁ‹J)=¸¢[ÄUé\€sång•a6R †1ÔµJH³õ¼Í´‡ÿîW5Â,:¿K}Dùká¾ÝmÙídáÉáp8Ù2eeÉCD’ß?Ì[ RÏu“h Òὧʪî*/+ã«Ù œ ž6„Y0R<ë¾�~ãθguÍFqü†¿ŠÄÏ)-)e™™&¹ƒI*=HS@TPÞ÷s Ì‚QpôH)½™lvµ«€?ÄÈJhBu ù�d*ecñ”¤©š*ñ:¢¢ º!¤‘hyy�‡ü—W¹“ „Ô!uÆŸ³&óíùÜÀÁ×1‘~ ¾y—Σ‚4„äŽ>>9Dkô&ÕǤ”G ×ÉÂs($…¶Éœ´Ž«Ô\á�áƒ8ŒmÜl®E“ÎÉÙòb¡á©v&&üœº®ÝKmmÜê[(ª2'G#VËïæ�ˆÝ˜+ ‡Cz,élŽó×ðÝbÑ@g“!&l«Kd¤¢Ø„E6Ôס˜[_óv¶ã©0jÊôÜbttlrY×¥ÏL_-ƒ•`ؼŒÓ³�<çM΀cฮËÛ¨GÞAJì:Ÿ–c±Äj(AÚ(6«ëúe|w+++œ¹,§Ìlø=ï:O$“äó$RMâ&æ8à¤KH9±ab>|ÁgÚÂ…ÛJaPGJÉv0üím´5_‡xœ½p™†�À#‹§û—–×Rxî9tiù1‘áûàAÐOž[!¹é¶æ:ÌS…'}Áè9ôØŸUlÊ< è6˜ü5¡eÔ5‡ŽI’ð¸ ÅàBûÆéÞ†X0g´¹9ÍÅG ›hÉî6ÒrmuIÏf2 ÞOΧww4s×S Œ”Äz€45/këê ©M!¥ÃÄNNM–põ̹~M§$rïój>o#"8 ®ƒKà<˜¡ç ˜2…%͈e4ŠÖW5M»‰í¤—ý�ÐE,,E*pßÛID4 ·ËT$Kø—‘wÂlK3. tMå±è(|K4R �°X(Š�‹÷>_€�‹ÐêÊ:]ÄÊ�Óļvã6ÍÎ/òï½þ qA"Q�Kà¹ægç<—VW×'t)oÑ·Ás[+ì•.ØJâ•ý}tixÒ»ˆAtvý*~ö˘5y¹Z£Mø'¾ÿêjox¨˜TVV¡ÈWýðÎa#Þ„(5SHʉ\µ›ŸÖ²À3 /œK¯-N!ÊÔxéߨÏÝ[c“žÁ(w»d]c G™0ì=~å~+š9—^*Ñóø´Çÿ»ú¼{wÄmŠÈ ðæ½Ih 6Ð:À]‡OH){ÁF³FSVà;éBÁ_¯©®Ò[[›Òöî®ìÂrÿêªÊj¤5Ë!L•hÓE‘?¦ %&²Ù,[Πöb0‡>çu<·Ÿ«i̾Œ#}D”’B„“0…}Þ?Iv®�˜àÁ¶=ýítwrÑ ›�½h þ-¤À~£¶º¼û§~ðEç¯ýÌ ¢“‰ G#55µœß~èK`¯ÀApÂBâ¨Ø¢—ÆòäêÕKg0 ®‚˜Ð«ùý½qw†®ß™fË›Ý%˪êE[[+ýüOÿ½òÒ®‰• ‘}í;‡‡ÿÖßÿgØUŠš3 çÁqKLî·½gQ)[ÀSHêÀ!óè%¨‚• KaÇïŠ|UUe £ó8ͪª¹õ]e ÐOD#º®�2:±¢‘(ʰk�º%$E(<¬øÁ•±fÁÿ'fP~†z ’º_ù©×+›ª�à˜$ à~}}×J.$å\tçˆÄÕhYÅoqoÿ¥?Ì�ݺb#’Jê$ßûênˆÈ CLà–Ë8*Ù©¡©�^Ü¿—~óWžª½�,�ÿ wF§òÿá¿}îÜW¾þí)"ŠuiÍ?ô Ø‚0…ÄV˜ì«ÁnSpÊMq`Ì›÷Û‰Áà8zÁ,HB<Í Õ‚Bϸ«"B/íëñiºþy܇�ÄÊí/|çìä§¾vr娹ÛùeoˆŠ]q›yÿíd¡åWjdaKÁmŸCƒCŽºúFa �®ÂÜ\Sm´�Ú¹u”7ªYÒr)îøA~¼”®y8MмŽûáïycÏž]µf§ «hà¯ÿñÏóãtŒi¦@ÁEð"x| < ¾ ¾kÞÏ<~üoà¿?ž1ÿ6[ôÄ3þoüÌ' v¶ÖÛQS©üéz©bã€Í »±VUU£å°â^„Þ?�h¶ûÚxšÝŠH¶ÁÂ{b”Î�9)Sɤøáï}��§“çor½dz~…j«+IØìÔÚÑK¿ýë¿Äs%%¶š•×õÛ§ÿû'¿x& Ϙùø ð˜}Þ…¤ÿöKG7?)fÁŠH¶œ�q»:»èµ½uìJö_ÿ¯wÿë«'rWoMó.ˆ\ŽÛMéáûI‘TY@¶GZ­vàîÞêêÛ-³yMŽM-R™ËÉFˆðš2ü¬x"Ý>¤å³„‚+{$•R;6òØäû^iðw~óW~¬§§§ÉÜú}`7YxlX"b ÉvC‚+à›à¿¢ëríÁùC§oD‘êJ.ë}5•4x°�ÓgK×¶ÇÅ~âõ7”ÖŽ‰í€† 0õ Ýy`5»(Bк×Ç ˆ––WK.QQ^F½Ý�â¥ýCC?÷Ó?þƒN§«Â,*÷€²`Á’gª™œ3s©ÿEJ:�”ð;ǯzQ?Y¼92—GãÄ"Â|@ýOqXˆÛ ÞÑþÆ÷|¯’ÊIyþê¯Ø¿«‡­æaäÉQI(¤ÅÅe·'«OKä=’DRr­¤¥¹ø­4Ø×¹»·§»É,w€ ôü‚SYÅ´`õ{?Û(ðÒ`Â$›æa�>1·Z92¹”!�åG´rí„QX`ÅÇmuûµÀµ+ìv—Ø—¡·6Õ*íÍõlæˆB<Û§dqTUŽ©·§“Ÿ$I]'!¥ÔXÔÊÜnö}Ã^7ì|VîÜ�1Âà2Y°` É3;�—Á9]—v,Λ^ÎÁÉvwÒãFº‹»ƒêpÕkwØH(ŽÂ4û¶¦¶,8œNr¹Ë ËŒ\*SûÚ6–>ñEîâÊfyÉXOW'ØñÔ,S¤®™î²* $¥N‚ˆ ››DKKkòí÷ŽÜÂ.�VÓÂ}šg°` ɳ Ì™¢’gÁ8NL]X<¤ã¨­zCvìͶCdÓ±Tîv°ý)N¦…m„úüa1�u°Í UÊÞ�.Þí� ‚‘'%ÎJk+RHý½ÄÃ¥R§¹ ÷?¦0�(L, ì߆#‹ñœÙ쪄˜tv¶WæT%yáâ‡]æ°Ý5Ì:E5M' ¬ɳi8½`<~\³ËKèò`’:ö…oŸ”ŸÿÖ ZYÅÃÔ˜Úî† R,2hÿ¾Ý6L³ËYϺVUYF/c×Ç‹{ûŒÕ¼KÍ¥ivvNkbÕ¼Jº–ÇÏREVâ*ijŽŸ�É`ãN>ÿ÷X<žŸ`qx ¤üèÊÖL‚´|šréÿ7ñï2ž�EÉ®PÕPÇÏ556Ñ ÐîËfCâ÷~å‡È‚KHžÝÈ$ úAŸ9ÄôäÛ�bùÐê�ñïíѹð±†Õÿþ¥ƒ(æÎ˜'&¹Í] ŠÍF{wÒ'^{U¨JE,¯jÚPov–¼JHuñL‰Ëa£t©ç² >±ƒ¸Ÿ!]Íw”²°°àB±ÝPͦ"ü÷Ä÷YlHÇã ,Z>Cy<·†çËe’x|”‰ödþ=þ-ü‘s;wC}m55ÁÊçÇvõï©»rsŠ´…·hþâ'™,XBòlA‚1ÐkŠÉ-ð[à'1wr|Å[\ñûN]¸•ý�Ÿ~‡¾õÎ1š››csA`ÅŠJ°…ÏØnsWÖ»¦<�,Êy¦dwï-‘’(™ˆ‹Hp�àK$ˆOà™d˜Oö¦ P&BÄ’1~‰¨!ÎB¡k?–¤Îb ñxÜ�ÙÍôVALø¹ÌÇçs) „"! àçÆóFù¿‘Iq;ÆÌâgÙd„ënû÷îRМ!ICX»ÛM‚Hé ,!y¶#“1£“yðøßt]ÿ¡püôÊzhîêí©ð'¿ô¾ú�ÿå¡÷Ÿ?Io¿ŒwH¹Š¡(ÔÑÖb×ËÏßôˆsW'2¨‘ µÕÂíÀXrFþPŒ¼ëkˆJF(�pÔ�CTÀ‘H&‘HrT‰û¸�âûD‚E@×8¹wÛLeñshù‹ þŽ‘àßCˆXL R} ÂÁ1Â�ÑðX<Žÿ»,^¸¡�u#ýæ/ÿDf ·cÕØf‹Ž®—aJë&z‘,X°„äù”4c¦¨\Ðtý?Fb‰ÿ2¿è=72éY=õÁ•ľòõ_ü»ÿ.ÿÍú$�=™"áè6D(VTR[WC­ÍMŠ$˯Œ,j—†Çu£Ø>ØÛF=æšåµõ �={ŽîÞ½kˆŸÔ3Hy©Ù¤y’ÏóI�ÓNZ÷3¤�ôu•£©q]·“ü35ŸåèQ Èé+ã¦ø¹!6,.ºÊ�5k,,N¤â1*ž§¹®ŒôV8^±ßAD:ÜkwÁK¬RÁK¼,X°º¶žÈ"'Ò„”ÒƒÚÉ:ƒ*‘ÖªÇV7%Ž*žÅeåöÈ„À2î쩬(繡([ *sF;¶˜ÙR©”n·‘m §�‚‘8­yÃaá‰Ó&ôÁžF²Û„PÕ¬‘‚b±P;×1¸ÎVy�#E‚ æI)5S2. “ÍîäçÂÏ!1Òø¹3ü7WqD£kf�]%"Á‚u¯�¯ÑJÜ…Î@ïÍÑ9dèò®,oé#ßùÜAŠ, ÓÆÑ‚KHžh (z ‚àm]ʈŠ;Æ�‰]Õæ|Ê­»c›Ý„±æÓÈß—›;¼7WP,ÔÖVóì™PbÉŒ …BZ$š°3%^˜’é … *Ø®íîk–5NâÂá`¡È¥$Íû† Ønî¶"žFwp”¡©,”7„!ÏQŽ4–™3…Aãû:(H·ù¹IéR7íË¥i"­š�“³$q¬vÏcÓ§Äçiµ¿»eòÿû+]þñK–�X°„ä9‹Jò ZÄ4¸ .€a]—ÆúÎL*�Ib¨K™\׆o‹‹—‡±zŽò¹<¹œNcß4Û{XØœ®†ºZ„P8†áD5�ß¶uÌ™@Ü nŽ&WúJKc�ÒÝ^�¿QT!lºÔU…;²¤FÒ)YdX<Ì�BNeñã€\†;±øq|4j)x¼ ¡ðÑœaJP—š4ëóâžQ-ß—÷j/¨�Ô-­Ò7Gæãº”éH,u“ˆ²–ˆXx>…Ä‚V†Ãà¸z5M aÕgE.›s Jqz}Aerj–Î]ºJŠÅÔ ß¨ŠŠ2>Zxò�˜XµJ¨/l¿3RŒºG¡ŽÉ §¸„P¨µ“Úê²D$az`ÙøÏ7u¤Y\φ¹˜ž#Š9’ç”IÐôѦL@4"™&©Ùð˜œé�¢ ˜óÈ=Çøã¾06¬¡9À=>³Š%ÒIs!S�,X(À’çx">&À$h®Q¥X†èD…%}G�ô– i.ÛÒò]…˜À+Š­Îa—Áv– <8«¯­1^SáYZÓ`ÞhD$"“Éq´‘H¦IÓt‚?!m¤!rRêšd,2(÷“yŽ—Bê…6`ÝÐMåÛÂŒ*ð>¬ `Æ|Ê Ùù)‰¡Ü·74UŠøwÂ=‰Q+qzVü«RÒ<hÁÂŽ Ò•|‘À¬ƒ#à˜®ËqD(>0…ÝѺÃn¯Jg2¶Ï2ݸ=BHÃ�.%§g*Pœ– <‰‹òº7 „Ã!™L$£>"AXdÆ)®êÊ28w L‘ëRJ›ær)U�*‰oë8jăq“sSÒ �¿D¤0Ï! ÙMq°™E,†(¦�£'A Ë~ç²o)¹"šf‘³`aG ‰%(Y“:×ÀIðª”ò‚ªª£’¤¦[# Q–N¥•Å¥Uºy{”†o�:¿)Þ[át:£ÛËB9^»2¤¸‚‘˜ ‡B:|·¤¶¸Ã …w¶rßÝߎÔb�ƒˆ ÚAaÀ û¤èfd¡›d¡¹/2&•¢ï‰R$bã�ä ;Ôvlže¿ÑÉM�²`aÇ ‰Ì€90o KôJ)g�nYDºK“Á‰Í®8Q}O&Óö¥¥UŽNÆaD87ï! ̧²Š27‹ Á´ðÝ·)65Ö#2©VÐö«…CA�HDÄ“i.¼çrªÚÞR/{[o ÐnÒi…Å-ß²H`Š…A+º/ï»mããÃED7(¥,Ó^>'VÖƒ�éùµq³æ& v´�XŠfR‰Ì 8ŠT×*ìκ¦gÊ`�QWWãD®p$BF”29=Ë¢�që*¡xOv›�lvPQÈÂá(ŠaÓN±DF�8k‘HT�~sÈO"êXÓ+ëªï�Š…ÅfF)&IRJágþ]±ÝG•�f4T,‚H¹Q:““w'ï¢AÀ+„µ£Ä‚%$îç5Ž Ý5�L¦Va<¸RUU™omi®‚�”CáH”f�ÌÌyxu¬Ïäá:D2äv¹8M#**V[0Šïöò¯�–ÁÐ(º»„"®G´·Ô Ý9åõ@êJQ„¢Ü÷{>n�ÊÒAa5PšTŒú̃¾O:š˜Y)Ÿ^XKÃñQ³P’ �%$VqD1+¥¼ƒ�ych]½®êúBGG{²­­¹bQáóÄêº�¦gç‘òZäãºÏÏÄ*YÞ�‚H…¯Â?RS±ÀÍ ]mœÚZZY×¥¦ —ÓNB]vÃ)ذ /`Ã: Ê}é+„‚ˆXôØb!<Þy¯  ‰Ns‹^ei5èZ^z̦�,hÁ†°„ÄB € Ñhì6]ó# B(¡ššBQ…Égg0¦ùÅe2:¾Æ'fhzfž<¸ŽÆŒÙÞ»�®0Bw˜¥'€‚Û«÷íeeåbyuMæs9 BÍK¥èîh"Ç£uʱ ‹‹ÈÝXE‘Œj>Þµñwˆÿˆ'ògÖlhΨª6ÁC¯,ç�ÅâËë^ßœ×ç_…ýJL%  rêö"رÐÂâ Þ^¨»PyYÕÖÖXé.x½xín]m­ˆÅSÓãyˆKžHÚ’¨£(l²éæÊ›{QU(Þ?ê߸]B4¢LήÄÃÑdÄœrÇÑ‚…Ç�%$ ~^+ˆBbp¸   i9ŽÌÇâñrêAE±az^µ£Æb‡‹í.¦æc±»s Û GitzñŒŠØÁ¢bwØØJíÁ€q¯[fšèž£@8&u]Õ•å;m2ó¡ë\'ËkÁ,v߬Ñ2èÙ¬¢» VjËòJ°¬0o·�¨�¼d·Û{ =.·³öõå­-MŠqÞ‹”W8Ð×C¯¾|€· îä4èʵ›twt’…–HW£ÑH\Q޽-åXT*+ÊÈþ”„WJÉé­/¿u6úÖÑˇÐü5":ªdÁÂ&ÁŠH¬éù(WÀypM×õRZ³ðõ f³9'HáHL_Z^±ÁyX™™]óž%Z[÷ÀV"è Û¡‘‰ƒb¹Ë\”A4R_W§  ¸Â‰<­û£ºgyM à­hšNØ£Î]qBl×ΛÂ< "Ì“x®cžd’ˆ·JH,X°„Ä‚FLA™oK)—Q;(̇2™¬Š�F�ÁF5 ÛŒ½ ž%�½)ÔÒÜH5µ5;²†‚è�±aÕm§ü± Dnöh"k_&Ñ5µ*!ÄO&"v€Á¶Eüº”�§ìRfÐ6S[ ²`a a ‰½(Z™2E%"¥´kšn\y§ &YØ«çWV½êܲ¾î (ˆLÃ0ÒQ°aaî”9¸ ° ¢#Ž�=èzkh¨y�D œ”X�¥BažEÁ@#¢8'§¼¶:m,H�/UP+INͯÞ{?É‚…m„%$–¨Á5S\40‘þJ¢³+�°ÔÔìBtzÖ“óúAÒNVÃyz‡ƒÅeGŠb:”cˆ±£­JR_e"žÌ‹Lž’˜ïÁër¦ÓY—ËÁ³…¥ EäÅíÓó« ˜}™$"/Y°°�°„Ä‚Sà²ÉÐ.�9)¥bì8‡Adòêð�칋×ô›wÆäèÄ4v¥L‰ù…Ec‡ ‘$Þ;o³Û‰AD;CX*«*i ·›v õ‹––V‡ÍY‘ŠFÂIx¡•Á™W›.k«ËIy¡- +Úôoøßì�pdkÇÿßíšá KkÛ¶�2Ö¶mÛ|ÆpçelFê`’Ž:N[º·ûî¿nUVššÉ«�LÏùUýÞK§Îw´ŠƒuŸ·tfF£úoDç– H òtŽNÐ!:EÇh'Í1 ¬dû«ka!¨÷ é�Ý}iÞ›O]jíN_nïÆ…KíšoàŠâ"IÅ‹�`�ÀºHÈÀrK,t»]hj\¥Ø6ìÈš-ë !m>Õ8‹ͦÁYYnÕY®ƒÚé¿‘HfÁg-�3%éù`l@�¬K$�ËaqŒNS? 9š§IZiF%õ ‘hÔ˜L®øó—Z;ó<\8±ÝèêWµh5U^åñ¸n•ËŽV‡×ºæ&{ÓšõZ:gäÛ:zU§Ï¯±†�t6�Ëa=w]c@Qÿ-ˆ,Ügæ#`V¢fB£|4AXH LPš >z™vR;­¢)ê¤Z‘躑gpÉOÏÌEùô•líì ²XÕ4{¡¦Úks8*ìš­ä'è­©÷ÚÚjmã†õö‚©™ã“ÓÆÔ̼b C ø§À)t+cãPã’ë'ŠF�™ XtçâÎð$‡NýÆ!Ë $B�&hˆFhŒ.PƒFAi’êÔÆéz“+[Š=¾þÔáãgÆ;º†SéܤÍf74¥*9^¾¸‰¸T©¨(Wk×4k«V7 È}ÿéT\ãÓ“êžÄ\0f=ýy]«Ãk XÙ WÉc40�‰é…Y&Nè£Â2E‰”:@éí¢—éqz–.Ð4uP�âî¡a¿çÀÁÃúmwí™=u®u4‘ÊÎbÑ4UÁ“Áe|ú*Ù§®æÆ­¶¾Þ¦@Š:rÙ¬b§˜™Ð$8oe'J©k]úhRÅ€l­•g�ÄdÁ}"‘ÌŒè¥yÂ2F‰�£:FgA©�öÒËt€&é�§Ut=‡&“U‡ŽÏíÞh¢³gpÐVV1åõx5—Ëá°Ù4{©=}ñçd­›i^³FÕÖÕ+À4 FÞÌëºâA-¤hM•›ÁÄq-ë†LªS�*fxV÷ÖH`¶ŸÙN€�›%�‚¡H³4I ¢Óyꣃ4@gè<-ÒZZÍLÅ;쩸o÷þÈ}ûZ†2Y££~媬MSÕ3®)©º‰×ëAss3Ö®[¯LØ‹ÉDÜÔPÔX[3++q9+¡i ¸:ÕiùbX EÊ7˜aѽ@75 %²´Qʨ”n¡�£ÛèfÚHí4M'xWeâOrÅË_üÜ]O}â£w¬mn¨âæÝÒÚF¬’É$ZÛ»ÌöÖË……¹¸¢ÆÎ½YxÄÎ xèöµW«›˜4NÝÔF¡ë´œêÀm»Oõ\è¸òKYB‰e$‚¬eIÑ ÚFÏÒ¡꡵º®oa=Å{°åXàR[Ï8Ÿ} >‡ymšVæõºK¦8_Î@ÑÔРªkêUÁ´gñH�ëþ˦fCP XQ[õ¿vvÅ(ˆó7Ï,D12> Ì]äG»o•§-A2ApЕt;]OwÒíÔC5(Uظa�óå/~~í«_öüƇ=d«½¼¬ÜêxÒJ ¨0 Žš-‡Žè]íçí‘Hœ{ÍœxÒ£·ãa;Öá?<ï4Jk©F­À‹§qøtŽœéjm9Õù wÒ8á $‚PIkè6Pº‹>šÖs¥{îÛ¥½æå/j|è®­Õ�}ÔCÕŠõ€i–ÄS×ü|wßso±¯·CqO—âI_<û)ož`ñ§ÈZ Šœ>ä@ã¿ýc]‡NušÇÎõì9×6ðGh‚p£A®�,�¡'éíô—ôô¼¡ë¶Ö¶Î—¾ñƒáïüè7£·ß³?=6>‰’À4±rE^ò’iÛw>4�7 :ï‹à|Û ¸^‹°Ã cS Š‹È"Áp­ÝÃð]™Èø'3ò·ú ‰ �DL§½ôúº—αH�=züÔì·~ðËÞŸÿöŽ€¯¨À¡z”ëÖ4ãå/e±±¡1džºûÇÀõþP€ÕùÅ+p,ÂAD´ûFÀÀ®a Gãé0€Yª‚ �DLš£ýôgôW´›ê�À¤í»?üÅÈG?û�n8fuÔºiê"ÿ-ø±KÍù„Ç=*çvV"žL3�Œ#‘ÊJÁº©oSÖή"?Ì;í`‡ºúÆ0=Ñg"~~»ýÔa $‚�¡ãt/ý½HSÜF\Áiùàg¾üݶ�sÏ×ML8ˆˆ¾�ap…Ì¿ÿx5›Z¿q‹ÉÓ½æâs–|ÉTÜ¥eý?Ë@Ò?`q½ÓzÒ›'Ú‡˜�œÐV*EvA°C|²´‹Æi‚>‘®<}æ|~~!è›�íxÉóŸ^ÝÔ¸z9Ï�X�XÁP3³sxÆSŸhu -R,T$ör0ÁHƒþ)ëùjmc½•‰¤ÓYkÛ/wvYCŒVÊ–×y>>¸‡O`Gô\}QdŽDb4JWQ­ …Âeí]½ép$æåæ]{cêeLæƒ!ttùPUå± í‹pfÆ<áBt20áìžTìÆïŒÀ?1‹™¹³’8ÓÖ¦_¤3¹B8šðÿ`¦òš¼ú³– H “†èMQ­f!Þ1äÕçBNþãlknZ½ï X.‘HbjfŽÁ cÝ€çuIkØrv.<³wßÞÏWóÉÊÌç�"Ÿ¹Lf(GE¹ÉÚIÊ„b�d6N´‡cÉÛ²9ýÏ&hæÚƒˆ H äÞü$�¥Iº*ËU#3s ùñ‰)7»œ´f>s9Že77¢,CV�$�Y�Y¦²�ÿæÏ÷ÞÿÇÛîÎ𩪚OZóºnáBÆ;¹2å~>�b×ÖÝÓÁÛxȪ%•É�`áþxy™}¦°¤Î5A��DA°Q/}>}]×°zuÃ#¶cõ‹_ð,õÊ—> «Wb9aÚ;}èêí7XàˆÄâ©ááÑÀ‰––C‡]6Ò>ú+ÚF³&yÁ3‰Ù…(Ø­¥Ü D$�‚‡¾Ôn·ó@áÚ�Û·l¬ì£†×½ê%ؼqlâS³…xOåzê1VÁ½åè™ÑOé»-£££U´Hƒô>z‘æ!ÿ¤ýWôý‹aó¼%?=61•ìîÀ½»¢­£™löA›™œšA¿mÖ3p=Ô×ÕbÛÖM¹p8\`m¦y:NÞ˜ "H!F�ÑÓœ3™ŸdÍ$ÖÓ7ˆŽœÄù‹íˆÅX*JS`ÍÂê¾jíèÆà?rùüµÍ±(¯Ç£ÊÊÊ+Øi†úéIzéÆA�@"óô =–L¦ú»{û{® �Æý8Ðr »÷µXK—„ °ÅÛ·n² å.wàôÙKˆÅâ×ô¸kšÈêz~ñ0Øm§'h‚pÃ�@":m¥{èE¶ÚŽ ùÇL® �âø©ó¸‹U£X eeeVÝåQ߅榄´¶w[m½©dW#—Ë££g`4‹éB´�ú©A$�7œ<¡§èÙD"Ñ6<2Ö9˜XÛà²GGáØÉsàT<–ŠÓéÄÆõk±}Û&”3S™fVâ_ V€) °P ã�iÜvÏþ‰sç/9xé§ ‚°¬¡Hc4K þ£Žd*e�'SZ:�qF£q[8…ÍnG}mõÒÚƒ•²n­ÏÌ̃ cˆDcàÞ/,,„02¿?ÔñûH¦2ÆïnÛøù/ÿ0’à4>€8m£ýttyf%‚ s$‚ h}8}:}”ËåÜÊ +ׯmò®]Ó¨žÿœ§ã™O}¢µÿj)°ím�½V{p]]­Á=XqM¡|ÕêÕÑíÛ¶ôß~÷ž¹ïþàgŽh4Z E[i í§I‚°l3AHÓ0MÓ‚®ë&[�ƒÁPÄ19=ëšžžÕ £�Ç ·Û ›f»®ò‰Ûí"aÓ4´wõé¿þÃ]­-GNŒ±cl˜dâðÑf6›õÈÓ“t ‚pSd$‚à¦�¢�§›év`m¨­©®Ûµc‹ã5¯xžýô'aÓ&áóyݪ‘üáöû ¿úÝÁP( Bâ4Mè~zœF¨ AnšŒDòt’©ƒ®`íÄņ�'í åÛ¶l´Éõ®¢g²ŠëýƒCŠEý¿í4Š ÐÝô(�C„›:�²Š~œêÔK= "Àˎʲ‡îÜ�׳„éw œ)1;»ûú¹éw@ÝGÒVšƒ ‚PTÑçÑïÒ#¾];¶¦>÷‰÷™#½§Íb|Ô4c×frÜŒO÷š?úΗôÍ›7íðú2ê€ %—‘BŽè$µóIje<‘,ç2ÆŠL&[ÆÃS`ýäÚŽd)eµýž8}1{ö|«?�NÏè �Ò®‡‚ ëé'è!àýݵ#ôé�½W?{ä^31ã³2�«e"Ôúð…ã»Í·¼ñÕ~{YÙ/¼‡n‚ %Ÿ‘B”NÒàë¸BŘ� Æ£±¤�/[Y_§9ÖY\k•Å—°Pt]¤´†#‚^0ôD½Žœ>ýâŸ>ü¡>~¼à}ý0�†Åò†‹Åâв+²›!q߀ç]Ά1Ŧ‘bóˆ$¥ E€³r,´ZøÜüüé_;tè£3==ÍæÓðIúŸôÁbyÃ-mY,– ½ÔWênÖïÞ�N_ÉËz'wärÁ% .]�óðsˆÎO)ðw›Òéq6žÆ“óóÕ:òÆé±X‹å $‹e”ÞëˆÜ7àûWpþ±“s�!6½3›ÿàƒÃsœO\pHï¬ Ã†‰æþx%ŠòÊI�œ Ë$‹e’¾Ý¹·ßó¶oÏf×]Q,æÂÁ86Ó‚ëBpþšsaû)3LüÏÌÍZlµYú�‡ÅrÁ‰Åbº•~¹+r ÷‚Lp kàÖþþ7bg._yå+…•&¥5¸÷¤8èy��>ýt-І¢MX,\�X,– ½R€¯µæ@=,_Z( ><<ÞÜß�ñ ˆ7!*tÁPÁyÄSJÆ‚` Tê4¯ê:b€À=ÞË—Å¢`±XΉt: �‰‰ 8Ž¥²Ù,\×Å2†è¼-ç8›·g2¥‡¾bl,¸¶XD:¹œ×&ÅÇó —·œ;ößQ.¯�§{é,‹Å²:|×w}´Öžˆdh‘a’¥é¶»GŒÐ¯×"¿ÏÖñïl /ýúEÕ¿xÕUæä 7˜S”ÇØ]<Ùæ©ðÐþý†ÿœoL§ÀOÓ‡h‹Åb±¬ �=öˆ‘Iú ý:úÝ~Š~ýJz7½œ>æˆüú–tú_pýú¾ì²æ‘ë®[ �ôxâ1z”N'M<–|~"veBå©+¯4?ºaÃsE×ýc¿E¯¦ ‹�‘X,çÏóÐjµÀ£|êSŸ*¸Öón¥Ô�¾ïïá’ÖŽ0 7»®»Þqœ�Zëíü|»›¸©pä«&&¶Ü;88ÊÙˆÒ"0ËÖ«Z"ˆè²cbòõ+¸»8ã8(8Na¶Ù”§æçç O8HOÁb±Ab±œ; áLD5� QÝǰ¸)nÿÈMäóù‘~2::šç¼$Ës} –~~>ˆ máNuÎEÊÜl(¡ÖKáÁãRh4�ØÖ2£¶¯3 æ<ŠÐPk‰€âÁJeáp­Ö èa{I°Å‰År°aÀ# ©T*Û oMñ9 �‹3™Ì�…uëÖ×_½ÿÀøW_}uzãÆ�)~}† %›‚¬Š¢ô±…I‰`$ áò%-¤ ™ å±CC“09cˆÈ9î~Ok­›ÆT«‡N7›sIž½l/ ¶Ø ±Xζ ÊéÓ§·û¾ÿ.†Ç~Ä:H$õðÃë¯üʯ”»îº _|1"عs'öíÛ'k×®U##ºnŒ,ÎÍazv0žRq(�Ã#±`Éö¥­eíd)Lx¤çµ™dµFÎu=¤ž^\<ÀÍŠÑÒ%Á‹ ‹åO˜››4Ƽ…¯oaˆL äo½õVÿë¿þëåÎ;ï—´À��ˆÄ*¥âKƒ§¦¦°eëV”GF0W«áè±cøüÑ£x¹ZEÅŽŸÆ­£=,’ð0‰IA´,L–9oÍDDPŒŸq¢óóÍf•ó’:€<=h—¸,6H,–³€ DªÕê¥ ‡ûùzS¹\.ÜtÓM[ˆlß¾Žãœñ�²ïyÇäúõxú…ðüsÏažAR‹" 0¤J4�öÐ@ �tùgퟷÈy -‚‚ëJÓ˜â3‹‹3Ç�q†~�¶`±Ø ±XzCDÀ ÜŹ‚!2ºk×®ÔW|ÅW`Û¶mñç½ ´T( †xö©§0êZ ’A¾M¥:B„Æt¼N„̲6" K¯Ï=L<¥@ƒ¹fS>5;»9/ÒiX, ‹¥—0™ð<ïr.U­å=sÛm·Å³�¥éÓjaÓæÍØ²k*ZãX½ŽS�šÆt\ÚkÎ"QÛ ¥  A›ÉkÚ¶,vîp%øL”5›Óé€>z+ƒÅÒ[�X,¶�c4ÙÄ ‚ Í%*°‘,-gõŒ10Ôw]äûûáðXa°œ`˜Ò5<º%Bã0i,Ÿ³P{ö„JaW.çÝÔß?™Ò:p½”º°X^;H,$¤À6²™A’c#ÑCCC` à,ˆƒD+íy¨ˆèt­f^©V['�V�_йG¤{S‰[ËIë<¶CG||ÀÖÀŽlv@�^FGa±ô$‹¥Àö1†a@/çE__þ×´]Í%”—ÔÖNÔj'>~êÔô¯¾øâ³¿sàÀ³ï=~|ú2]ç#�áÒ¹ÔÕÖJÌ’ç†#‚m™ŒÚ›Ë­Ë9NÀ½Èþ ±Ø ±XzhD3H2l#a¡Pn^?ÛF²4 ¯6¯‚:øôüüÓŸ›�}ê_�ù»¿8xðßõ¹ç>ü|áY>W]0]lk&±QÇÕ_g�¡ž‡=ù|Ž�2&€×Â-6H,‹R $Œ¢¨�;ÚýV«eÀãY/•5ø½GOœ0 µZ="c-6›OÌ6Ï?37÷ä_:ôáß~ñÅ©ÕÐém¥ÕÞNzž‘ôþˆ^6p^2>äûk`936H,»£�´(sš\u2Cål“ õf •JË#jôsôô%zŒ¡rüß�yñ¯_yåóÆ|Îó‚º ä—íŠ?�01t4pU±èmç^O©"€�tð̃w‹ ‹År¬Ùl>[¯×¹õã”™žž�g½´õ47#~þé§›µZ- ðÙ$D>Nç¨4¢H=~äH•CøM‘Cˆ–ÇÃ$jŸ“ˆœ·V¢è®lW•Jý¼÷(Ó;èÅTÃbƒÄb±tìh‡ˆÌ²�š™™9Á;«7Ž?ŽÙÙÙ³j7l3øçû7<ÿÒK5†SED>€ÂÐ#ô0êZ\”ççæf[Àœ*è‚Yö:Zò¼î'‰1´ìyØ”N+ÎJ.*ºî;ènt^Åe±Ab±X0ÆÔœ_ðõ•R'$u>‹¼÷Vüüµ$öÅ—^ŸþÙŸáƒúP“A4ÓŒ¢ü#}„ÔéÇè´UmµN·€FTÑ#��b–æòöò”J%äóy‹ÅxŸÉ±cÇðòË/ƒ³�x8Ï×8zôè"ƒèh³Õz_+Šþ¤Ë“OÐCôb¶’ƒ¤$J…Ýö�Èk],âùDèD‚K\Sa8ùäÜÜA#t }Kq$‹¥B?Â0Q †#ö2PF¹ÜU~þùçS —ûKÛK< œx /"q¸„ahæ ÛÉþ•_ók^ìg[Y†¢®‰ú|?ÈhÝ—œëz °àÕ‰–…IÇמãåÀãA ¶f2#_ZXx¹ECI�|øslÏðõK‡š>pàÀ›GÕS÷}¿šJ¥Ž°¡¼Ïó¼ßgÀü<Ãä7÷öõ=ûʃÊ_íß�ß~;–Q¤¹”㘵ét¿'’íaÆÑeçûŠÌIb|¥Àa;†}¿ŸûJF¤hš–`éŠm$‹ “Yú$}��R0ÂÁù¦ˆ¯9)pÙjˆfDÄáœÄ0H^æ¬äsüüYÉSÕZmf]±ˆÛ¾]žž›sÔM¡ M×ÐÒd:í¯Ïd”ˆ2èàŒ­ÄtéùÅA?ƒd,ôdŽóA]‡›Æ¤Úö•4`yUl�X,–&=Å�8%"Ïs�þ1†É0B±yv]÷›Ê"¿¦þÀ=÷DG§§ñ™{ïÕ3Õª òÖ÷¿yYhí_].L²ÎtÁ²g‘HÇü£K¨ˆçyVRb�Œú>Ö…añ‹óó¹£õz€î!·,6H, g%H¨Ó—Dä0€OcZàÇl"HõÈ3žü™ŸÁûn¹EŒˆ$!�ñüAÎSº…®»²\.Þ<44•r霉ôH´£¡œ/<¥â{pqàîLÁŸ¯ò¤è,,=cƒÄb±©µ_Å×xôÑGQ.•°é}ïÃÔ×}]Ü H‹O)¬ÿ«¿B‚C/§w …¡÷ÖÉÉõ£aØ1‰�"Ëc©•VGkÂó ̓Õjé¹JeÔUð™ÿ]�Xì°Ýb±¼êæÄ_ÿõ_Ç-O<�©| Ô×WüÓ?¡�AzC õà—MLìÜ�Ínëy®±ìwJ›dÅ¦àºØ�Ëag.—𠂉¤Uí£ –•Á‰Åb¹üÿmd蕬½¤¯oã {|¥¼žÚH·°è&tE”BŸëJŸçõ P°�f`9wl�X,–ýÿò/¶áÓ«èþÑTjäÆÁÁÍAt„HïáÑ "�UZâñý\ZëÀ…eE±Ab±X<$Ë@×]íÀÀ¦Kûú2Z¤s?H·6b:#F–›| �+ÚJŠÿs)°×ïy9›Vî×Yl�X,¡›èÝ�Ö—ì,6qIk°èyˆº\îûZ´‡„ty¿RšÑÇJ) ’4ËŠ`ƒÄb± Ы|¥vl/¶rIkt}6+½ÏEÚC¢Ócº|¶2xJÅ÷ßâ¬$Ð"€õt–óŽ ‹Å2DïT"{·æóëo¿¢¿_ûZw.i‰ôtµ–€¬z霓Œ}ß ”Ò ç7H,6H,‹Ð1z?½~(6^Ùß¿vw©äæ=¯ã®½ä¬—³¤K¡+ÊØÿìr×YÇ.½(>ZÎ 6H,Ë8}�Þ�qœ5¬O^U.g(0=ÏE:o‹"mª3… V†}ßIkí0´LCXÎ$‹e’¾�ÞÁ™bˆŒÞ4Ä5­tZÈYì9cé<·Š;ݧþç2`À"uÎ}?‰Å‰ÅbYOß!À̓A0Â{h�><99vQ¡àêŽé�.mDzXòZ1:xÖ")�‘–Þ±Ab±X„’�ô«´ÈíkÒéñ[XBnÜ–Ï+O©ŽémI«ûr?õ@áùÕÀ$wÞ˜N§B­}ýtd±Ù„¥wl�X,Ï×ø6W©»&S©ÍÜp8Æ%­ìÆlª­‰ÐŽ1=†=sˆ$çdõoäè—=Ïhs €Ôéz–Þ°Ab±XÊô.¾%ÐúÖ‰Tj|o©”¿¼¿ß½¸X„§õ²Ázgœé"^Ça»IÔ"`ˆøE×ÍpéF:Kwl�X,†Hžî‘·†Z?Vô¼}ë2™ÁËûú‚ë…{Fàö²œu6m¬vˆ,!tÀóôÖLFX }tS÷¿7û<‹ÅX\œ`ÛÆ59ǹr$ 7æ]7Ë¢ö•JX›ÉÀQ Ñy ÕÃ-QTìÊbÐù:纲!•J˜¦Yº™¾—ž†e ÛH,‹C'è�Z䦜çÝ^ò¼{¸/d÷Îb1ÿÐÄ„ºapk’9›«³b{�WCuÌNV'D"êŠ`{6[ ‚&€Ý–hI°Ab±X2t7½Ï¹3Ôú-¾R¥¹Ñps.§¯g€pÇ:†Â°[9s1¦³etØ•¥QÀj†ÈÒQ‰`È÷vårcš´˜I–ÿçAb±X4]Gïàm�Öw<ï¦~ßßËå¬üþ�|Ùä$6ærP"0‰èrës†!KGu–³µ*ó‘ΣPÛÕÖtz�» E·ÓqXþ?‰ÅbQ" iz _¿ÓSꑬëÞ^f€Œ¥R“¼*Ë{çš5x Cd<ÙxgÚ/ñ9ãü<&vÂC¤íxæï;¿˜×“(ÙO²¿¯¯´-“Iƒ�ôJš‚åÿ6H,6 ®„yì1<~Ë-XN¨u¼#;2fD‰¼‡òíǹ�!²«Ï÷'¸;½xçȈfˆ€—÷"å80gZÆ¢½ïTïTõ°¤…×éj-³L¥¶f³é·ŒŒ¬Ï;Îqn$k`ybƒÄbêÒ¦ƒÔwDä}ÓÓ�_û5LW*øí+¯„17�Œ`]6‹J«å�¨Õ.ãìãÛ _•÷¼kyäYnaO±è>06†[‡‡11‰éþGV]é1D¤×ï7f5ÂäUoo’ ¾¾¿ôÖr9 à8� ×Ñ–76H,–azýjúÝô»èWлëQ´Ó׺߼ûÝò¦÷½ïúàQô}lÈf?‚·ä\÷Gofx¬í÷¼ÂŽBÁã–¼{íZl/à.¦£ãu'F¤÷é"Ý‚eí‚et†è’EÏ ¯ëïß5†@“^O/¡ËŽ ‹Å¥;é{èWÑ·*¥®qg‹ˆìð0ýšz«õîðw÷rnø®íÛ3ï^¿¾ï?§§o�Œù�@©ïH+u9Çcaèór^y—±¸œ…Žô4;èDz�.·…?ãl«ôP+ƒ.tÞ„¾RØW( =<22œÖº ŸÞN‡`¹€±-E÷Ò·‰ÈÖ †iX,²Ùlãäɓщ'¼J¥ÒgŒÙÜ0æ’+•§ý™g¥§ÏÙŸ×z—ɳ‘èËJ%ì¡S™ ­õþªî!"ÒËŽuÚA�Á´ê,‘%Ëž§nØü©™™ú¿?>c€)Ûèj`¹±�ÄbYOïÑZïfp ¯Y³¦xË-·¤¾ñ¿ï~÷»ýýû÷g/¹ä’ÔÔÔT&ŸÏgÓ™Ì6? ¬ßU‹¢oæpøÚß/íÍçõ£““xëø8väóHk}6¡Ñ‘3´“Þ–éuؾúƒ÷“<{C¹¼m< CUº‰fa¹±Ab±é- ‘}étzd```øÖ[oÍ~Ë·|Kæ­o}ëÚGydÇþàîüžïùž­ßôMß´éMozÓÔÆuëÆòéô”ã8ãM­‹­TÊYW.ã¶¡!\^,"¥uO�¯•žŸÒùšv§×Ô1WY] :�C­qI±X¸¡¿}‰-@žNQ�åBÀ‰Å""ñ�nUJÝÁÏóúÖ­[—ºãŽ;B>�0`Pø™LÆ_»vmxýõ×g*LîÊnÚºÕËd³0ZcÆœH¥p<—Âã@´×!vïÏé §NLo»Ê;ƒË¬ ½‡^´ì5Û®éëa3Ù�qœ€]´Ë…€ ‹Å#dHk½?Íváû~¶¿¿ß¿é¦›œ 6€!’„ ! ¤ü ï~Ï{p×=÷`ã¦Mð‚sÕ*>vèþˆþu­†Ã¾Q €J„vß ØþÇœv„Gâ¢ÃVy#b'ÒãN÷(Q‹`S6«÷ Ó=Jd4YâÒ°¼žØ ±X`�ˆ ³qìt]·Ì 8Ñ[¶l‰?ã¹8H8XÎÄÄxàÜ{ß}ؾcrÅ"jÍ&žžžÆñ‹øå—_ÆçDâºphczÞm.áÑûUU¦ËÄô|�­ÕÇœ¡•=CAà¯K§wN¦RDdÇë¾IÑbƒÄb©×ë¨V«šá‘g�¤Úó<Å!» Æ!¢µŽƒd9<;Èyg(x×;߉];w"W(@'n'�¿ð~îóŸÇßV*8Æs—j*çd÷Ëz;C„vù¬cßÉ*¶é5PD`¨£T|[}†HŽídg¨õ­î )X^/l�X,IËphŽRåÒVÄ«±„Ä!€‰}­K©¯÷Ý?8„ç*cSqÂ-|éøqüòSOáØNþsa/..¢Õj!n(=Ü«ó}ï˜nôH:–ÜV‡ÎgÛ加½•”=Ûr95•J�e]wD€+A{Ù’`±ûH,–€æh „ÌÑ ÕÔIÔ´E“Ï"ÚŽÐ,uèm€Õl6ÝÅÅÅ ‘ˆ�îÁÂÂÂÒL$ �84ºá.¿â °Íàâ‹/Æ_ýõ_㓟üdüsNòçýÛÉSé4Ög2Ø�Ëa;_1¬B­¡E–þX 5=„† gzŸ‘ƒU¡ã_go›E#A€AÏËó˜ámhúšQt€—èÓ°,Ã6‹E¨G‡émôíôËé×Ò¯N^¿‰Þ™x}�ÞE÷Ð\[À´�^D¯§wÐÝ´I°ÔjµW¸Ô5Ûh4pìØ1=z4nÉ|$ö5ÁÐÈÞò¶·áG~äGðN.wMNNÂõ}´”Â+óóø(ʰ�üÂáÃøë™<Á YàïQ"p:ÚAÏ®‰t¾?ûAûê߇+YæbuÄÎBÁÝ”Éä|¥côJÂò†BÃbYù)ÒQºW)¹©� ¯Ì¤üK´R›Ø("[ Ì&ëèFº•NÒ‰äõ:º�®¡;’¹˜îNÞ綾Ñqš�¢ÈXÇÀv]W¶nÝŠõë׃ %­ul/ðëâÙÉöíÛ‘/Њ"Ì3Dš ŒHsl(Çè‹ô‹ ’9c hN�ÖP]—¸zo'ÒÃvEê³4»Q ¯ËU[�·ÐOŽK’8p=¥p¬VSü÷ï“Ö æ=K6H,�®¥×³©k§†‹{ ™r>öeB¯œKåtà ÑQßs†�1eÚ/�QH›’и†^E/§ë“àèKŽëéf:Êï-A°†!Rb‘!Ü›,-i9ŽôNü½Û¶mÃæÍ›‘âëÈ4êu@�fU¶ŸSµU«x��2ÍùIƒa“Õ!U"��ÐC;é �Σj’ä¨W{F²¼q´GÒ(mÄA’ÒÚ=U¯{/W«�J«µ`†¾@›°$Ø ±Xšt\ÃýÙ�{6�æömÇÄ@“CE™,8ƒ¥¬_Ȇ)†J–fR�— }'Ÿ ýÃ&í¹:-�¥¤ •*8š(©¹ŽãºŽ.ó Ï©¢çèÑÀsøy>2&Å+¹„a‚;v`xx8nI+IB¥w>ñÏØ¹sg<; Ã06 X¶”ØÌÑV+n)¯0PêÍf*Z¾Rñ=ßžô$º=HVùò_9Ãíò# »Ü`²ÀÖ˜uœô‘Z­u´V;Õˆ¢€—éXl�X,†¦èužëL2Avr¸¦riÅ\ åB#ý9Œ ä1ZΫþ|Zõe�©á’Ç“æ¹l¹˜KÙ”n�Ïél:ȧS~™?'ŸK‡a6¤@iÏsІ!ÒjÅ¥(0HâM‡4^Þ‘.Wqõ¼g%”�7¢\.#•JÅ�…‘†FÄ@¡¢5Já$÷³l(_˜�Åù<¥b]úZ-½,k' �8H^§Û£HÇþ‘¶"�€Ç�ŸEaˆÓl&'¹ä5Ë 9Ía;Yš§¤8¡RpE ´‡H|ì}>²ú­¤‡F‚ä˜uxL÷ÏÍÍÕ¹Ô'ž§¢Xl�X,5Zh²•°1ø™ ÞG¢(Dzÿ�,wQ…Às⩱‘Ä&ÕzÂÇÍD‹AXºw¤\¸%xwøžû®áþü[ŠÙ‡:õ²7OŽô]Æö1ÄF¢9Ϩ2aŠ{CŠl%qË8”H<\/dh_ŒÅj­¨Å–ÒD&ôáj‰w˜GFáä©Sxî¹çâ} œ™¬b+é\¦c Ä eÓæÍØ´iS,#ccñž>o!Ãe¾ZÃBM²ï„Ë?øg( ¾w•‚Oª. 6.Ѷ1±Ëc�—šI$ͦ0HZ+•Ó>I?ËŠcƒÄ¢h†%Á°—^Io¦{’ÐØHó´L'“÷}Ô‘IÏuv¤C#CbW1›ÚÇÍ}Wsé«ûòé ‚�ùL¸)úëx~mà»cƘA.!•�Vží¢/Š¢)@¶ŠÈ60¨\Wïa`lð}oœ_7ÀÏKJÉ0Æ–0ðv±�L1hr0¦VÈÅ"[ ›Î�8Dr ßÕ8=¿´ÏL¼Äæ*¢1_©bzz'OžŒ—¸’yIìë‚$—çrñ3QÆ'&°žûRÖQîGÁäºuq{.—በɆr¬^�ïíõ©™|”¾P© E(¡íC¬.Ò¹¼Õõk•¶£¦-cðÙ¹¹æççOx‚~–ʼn �ñ¤IÜE¯¡—ÒÝt'¦%Z¦-Z¡)šV")×uR Ž!6�Íl[Ø>Ö)¥†9´æ@ܤ¹äÿ7{ï-ɶl Çʲ°‹ÛR[8¾÷\¿ûÝmÛ¶mÛ~µqØÇmme®æëå¨ÑÝ»»·ÐݧãdiWeefÅ\3 ”òxAFT¤;“àêwYü‡CIš½¦4¹‰: QÇû‰R‚ûlphßçßT,pa j¶uºRmXð&Àd·ÆœY]‰ÀŸ³YªÙûDïD)‹ÓnÛ!/-E„‡æX 0aû|÷Ç”÷Ø„:<‘d&#=•èvñ•WäÔÅ‹Ò @Y´Óƒà¤ïd¥Ñ`¸ëÍí=¹—M#™íu¨²µ7Bµ¸Ä1€7ÒþÅúz r‹^Éž&ºâ‰+�c.¼Ð Ðß…~zÌxÆ÷H_CiÄ‚ç‘(t£ÇÕ`"€§1Ï¡KDáYxÊ¡§WñðÄp0ÌChkiQÛb‘K5•è^«ÑYËáµù’îÒjµ;SxùwJðùŒ¦Â{+;ôχ|Æ.…]¥è å‹«Øoˆ $¼mï�™YÂÐ’ q6˜P•­Ø�*»ºßçc–WÉ0B`ã¨IéBØËòû u¨€¨ ›¸}ÏN.—É«ôá1€‰SÐÙ¡²ç!ã¾CÝ&�¨G¨å(‹1 ’ß\ZªVÛí%yº)®ìX\ qÅkøŽŒ‰!ãi\6 rší ÕzpКí� •îD¶+v*•ˆô!dB˜Ê �DáqÁãnCFz³29˜—áž [˜´MÏ*¤Î<ôHD o#ÌÅö$]1¶içc'GÂÇáÍSuŤí*E�‰°EJHÐd‘ﯵ&aÎdO 9‰ ¸ Ù(VDY"Ø}X™œƒÐXW«5™ŸŸ—4BJh9O é¨/9 €Â­Qz+Yìç)x)§Q\™²Åç£ €¥ ÝÀkÖ°]Æön»M‚þn©ÄpØ*ý¿œnÄ"6›¬B×ñø*À oo@1¬ËÞòu5h÷Íslñ‚ǸÅy¥ªÇ5™tž,�Pá‘ÈVWËøœ$ âʶÅW”�qèËÐ!£ýÐס¿mxŽ0´�ëð>ªÐeîu„¢4Vú™�îÔD>ïX°•\*A ;‡&�Ù`B™Å�ÂN GåRq¾„9ï+édÔ<%�”ðXDR ÜOD$�m Ûd,ÂÇá æ�ð±LW çmx-ô"Ãý0½‰½ds1u%Ìà‚gDi¶q†{ÒÚ,&èêK‚{‹Vó‡ ,Žš¶ö¿ðN.½ô¹”@Ç7•b¡c۾ˌÖòŒÿø¾×pû:¶w&7¦Wð½�”â�*û·ßÁퟭ¯ËG¸ý.n¿ýϽæšÝÅ·í¿ùzÏM×jrÿ:€ [„Ú"är|�¥�G„¸nãó~²¶VGØnÃ�í7Å•#)J\9ªç%hˆóËL ºL'Ý3æ±*xŽ&ÂJ+‘pp¡(Øø€é¹u<æ÷‘GÕø˜R*Žû Þy ôñÅxZ®Ôde£D� C£Ì5½zBôBóßÓØG†É¦ÖdviMbA�œí&˜(Ù¡µ° ÃwcV–ÖK°œJ7|�ˆB0¼ ÜÑ!˜cu±¥¡FCFÊ3 ¿ Û¸OMÉÌ•Ÿ™ž–å¥%Þ^Á¶€çvÄ÷j6@Vö Ó0þ>ÜVÐÀ@‰lm ›*^(Š! Æíezr´y�°£q Ú Ê8žÂjdŸ� >¼gܲø:_GQ;Beèóû++òoܹ³0»#"ÿ+ô?†–Ä•'Š $®„�×1ÚQï4ç*=¥°íJD‹CýY?Xò2 š'•3 ø“ÅB98ÔŸSífÚòll”•í¬wb  xì›”X7î/f­ËhoR†»»–ÚK#;µˆUõý%Y˜ÔX°Hž¡¯:€ÄvW ÞI@Þxã ù›þ¦¿‰Þ ¼Î!˜<#‚D¶Î___'Hܽ{WÞ}ï=™º_Ö×ÖØÂ} FϱïW žI½V#(à9Þjx®`ƒÌŸÏPÁ1À°VÔ§5; Æza¨÷Y«PJÂ�ÛÀ„ÄuûqDA­%‡÷ÁûŒã¹1N÷CPûy3£„á´ÿ ³ðÿ×¹¹€ä¶ˆüOÐÿº&®€¤oXµZ ç¥l2%™L€\y‘=—H0-L&ÄÔ…€,Ï8ºÁoôŒ uG_»8éˆHO¾KPßÁÌ*C”SõÐáÞýEùø³»25½Ä‚¿gHhtîͯÊüòšÄC>¹8ÙËTãmëLõµë\œÇ:3„D ûq½õٌ̭l2=XÓèiöãjaëõ�‰Ä$›ËÁ3¹$_ýêWÅÌ}§Á„gBƒëʶ…žJa­™™&¼½zõ*A†ç^Ož ÂdÅ�rùûðvþ“���?ÅómxD<—®¼h‰+ — ™>M£Ï” éL:Þ=Ô—‰�=>$/�“ñáÖ?ˆ<¼ÿ4\@¹\cç‚&Š$¥PªÀ¨7ee³Ìð�ÇR�#êþj²F„÷ÙÅïcá$Ÿ«Ö[¬UÁq%±l^kª÷ƒ¢[©ÐÓC�0|~•ãÑM)PKñÁ$ݳÙ,ëL¸º®Õ|“íÍ4q…õ;]ƒuuuÉ$U¾Š÷ï¾óŽ|û;ß!¨¬nl0 &Z‡.Â1n!ÔÖ†È\yÑ ]‰C_6-L^…Žc•œêËœš?{b¨çò™ÑÀ×>wV¾ôÚ)ò»ÍTªÖêrÞH¡P–gP„ ÜÙXQ·5CNá-R�5@ ©58ƒ„¯-Tj¼dPøÂµbUjõÁE‹HUØØ‘MM+Üt*õÑ^…¡˜V«I0*—+’N¥Ùò Â¿3¡ÞÞAÓGWÃa,¬„ÇA€0Ë Ü �-¸˜Doo¯…ÎÈ?�–¿ùÍo2$æÊ‹$þŠ’¼R*0ÜŸÍ#tuò©‘‘—ÏO¿ðÊIõæ+'d¸?ÇÕðÞˆ’é¹Udä´kìéU©Öeq½h‡æèE ™ã–œªùwNLD¨Š i- s•kMÓÄ‘¡+z;Hû%±‘¤Öx#?Èb_ÛÄ>4ê5ÊÔô4ÇõÌÌw„ZH>ÃØu‰ (Ûo§ÏcØ××'§OŸÉ~d‘)œ…ç’“““ï~ç;ß¹õ­o}Kþõý_Wž÷Ж+1Ó¶ýK&#K�ôçü/�G÷Üó Ô£ÇF{Ê•¯!Í÷*·‘s9" ÛŸUñšÎÂv›–"¼€µÍ [ÁûèIPѦq$ÂP…¶nK´éçk˸�“ôJJ�&+ P±MP)”ëäD ô80`˾oº×% òž ¼»¥…y’îJ´Ü¼yC‚¡0ŽsTz{{äË_þ2Û¼#³‹¡.¬²©. l{àÁäWõWå6RŠö³ŸÙ$¼B1 Nåsßýîw¿ÿ×ýu]ý¿þ¯ÿkùëÿú¿^\yÞ<�cЬi_òeè¯AOÙÝu¿øêÉ“_yãìkO�L¼tvÜ„¶Ø¬PËÞJ«ÅTV†µfæ–ihŸ5a*n­)ÁØWéM äðLè!4ü‰.LßÝ4E…Zkr À Y-Tø^hb½FïlÛz£M`Bª/=“ÎÏeNGˆ �’Yaï5ŸW«V¤X,H©X’Ù¹9 Ǻ‰Ð{$�‘âJo«ç‡HxP¶'±XŒá-(½„¸<8®ê³Ï>û.²¶Öÿ©êŸWž' qÅ2^ÇKB¡~. žB ȱW/LŒýÚ×^ê~ýÒ±À™cƒÒ‹L,�ì²_R(–ƒ¦hgfWªih Pª4Ìk mC¸?b$é�ÇÓÍyc·àr¹Äd€Z­ÁTT€ áM‘q~du9Õð8—CÙ^ú19S4¼ ÇÓ‹”áÿ÷ÿ<¿6ÉxWž q%=eÂX¯ º|©»“gŽöõsgc¿ó�W=—N�Jº+ÆUíþ‘Ù øj$ÛE ŒÛŠ”ÊÕŽ”ág«¦dy£ˆì�’í…ÐéIÇaà-ã�ÔùnÓà« r"uôZÚÌM <†³À¡H|Ý#Ž‹!ß¡~|A CÏÃT‚�š|O WÐ0|ìo5==ÍP V×ÜR¶ã¥¸Âcð°» (Ô®X”%ç-Ë*ØçÀ•g�#q½�$tЀÈ9Äõ/ç2‰¡‘�|ÏñÑÞÐë—&åRyÓÉØAq%¬Ž™ö /„  ú;?[?KuCà38e]�«ø> kEYW‘KFl�ƒdz©Ò€�V7Ù†¼MPÑš "À!ÃqMÙ)Òô¼–Á‰E1lï€à¢…BÈè´1ûaoÔ2!CAÎy±o³j›Žlžî|6™@1¡µ 2]à•°Âù0VIøL‚ÆÊê& Y‹¤; ð³^[À‰ŠÁ G2©°$âai8s×µÉÒ€H“á,m Äü둃H ‚ë‚u,ÞB™\UµÔ”º7@âu' 6�ÌaScccrâÄ ÎŠïyq%îŠ�‡ZXX`+€­FȆ½Å± ˆˆb×bWŽ8�¸Ò‚–¡C&Œ•ÆìñÂ'�£;¯ï�ËÇÔ¯}õ² œÅŒ­5õ …Óþ8ȨÎÏÞØ(‘p¯×ÉÈ3,ün—ÏôKOB¦æ×ìƒÛ¾³¼é)L„u"í–6•Ú"ôB=ãÌx&äP8°«Ú¨!ì葆YZ\Â9Úd¸ „<½»Øe!/†r`@ &&äõ¢‡¶x"×sž] –òxœÍ#]9ª¡-W|Є)*ü"ŒÃHí:wrØó—ýÚêwþâ«‚&‹Ìæ9 QPëäEà…й{oÑÎÔÒàKžå¦œä|~K^½0$g&{m`l¡ž£²¾Qö/¬¼fh@¨äÎõÑ Ë± ÂBÇVœ &™ÂŒðB7%¶T¿zõ3Ê]z-L5iÎμG¡xoiyƒÆtai«ÜyÍŽ¿Ï0ˆÐÂÑNË¥S’á¾Q¨ZÙ®(¾®¥>»µ  lJÉp–´é ÍžaL#6óñ ¢[uÕªìÛ.…ेríÚ5ùÉOJ^� É`Åíôó‚aåíÄS¡g†D†q,ìôê&ŽK)×k"² ½TIÜЖ+ÊHzúWb½ÞG!¬p_>™._~ý´ôäº$aŸ¥C5^_Ñ@Áó`Xíú­9L©›µG›*yv…†r³X‘l&,“ÃYÉ$#v ëGWо;3v‘eÓ€‡Ñ£ß—�á.p(œÌX®Öò²ù$F” –‡  1Ý•@‚V ÒÓÓ�y #rüøq±Ã;ì>Œ†œ‹ò¼wFX‹óP°äJ”R%6°­h­=G D\ qg„LBÍ€©×Àƒtc"ar°78>Ö'¯]<&¨PgQ!ä(÷±z]T‹L%¬ÎçæW¥\*?ë+Uöµj´›àE’21œ“h8 ËkvabYf—6äÚÝ%)VŒ7rDÛ»ˆieÿˆçØn%MDCp(%m)m®âï‘§TÜD¶R‡—–1\ëɤÓ ‡Ù%wbbž‹BN%�` Ï¨Ðó€’Czç�wè�<ë"Ò4iø‡Nºâ†¶‚ÐèYèEèe%ò&~ä£ölô '‡}ÏŒÉET¥¿r~\0îöðÉg†84;â*KÁ+©Ó�úø³û¬Áí™’*@qi½ #ý)ùÒËrb¬›„úF±*Sskrõö@¥$…ÒÑŒ9åP�™óèNÀÊ€ *óé©DC*@†Þ‹²ÿ¶Ý8a¼_©VP�²`YÇÊœY_mkFá’´7+v‚ ôy˜Þè¤CüñÇ6�h„·jÐ9#~ µþž[W\�ä�ëCÎ>$ í³,õ*8�ÉZ+´b©J¯¤ ƒ³¼²Éz§Ê3*«…’´¥-—NHFØÞE´¦fRQx„a¶&Š> îeÏ-2CË p°RÝrZ± (Ê£x¯cË—­„žŠåÁëB¬š¯T (Òl×¥°V"¨XxÞòxe½RfÖ—²ð¹� ]°çtF‡\p,=öln .�Xðš£Î•‘lGŸ-¶™�'¢QÕ.’›u}ˆÄƒ¶ñÂÄ•ÃH ÐèŒÁ9ž€'Ű)¹pj˜•é‘@àðA†ŠÉcÑÑ ©Tk‚äþÐVÀ Ï7#ËïÔ) ÏilOŒåõëF€ƒb^�ïN5ÆÚ#ú�\;ûåá¾y±e«zÓÌ‘á)Q|Î4‡d_/|gvÀãO „è߯ÄxÄL8(š>jJ ¼–:+ùÛ¶ Ži³Á,0/GûÙLNœ$ˆý&AŸÉdH`c !àÂQ·xŒsT !q]3UÞù!„ð4¾� $kØÇï¢)æâßð7ü .ˆ¸@rè½³F¡/ãGý::õ¾qb¼??9Ú#£9z!#ØÂ0z1ÚkÐÕ9%Ð/ÑóvÃ*Ó@eäóš™"xÔ… 5 /6N±$+Ó­Ò‰ñnïK§ûãƒY'!©ŽïOCº¶Yæle^pÀa,’äàq"=às ¯D‚~‚ ÚÍôï÷-Ã]l–ʲ‚ó¥õ¶?›ŸH0\Õ,8…§BÂE¯N ¸Ð)8ª°3ñÚ÷Axü,VÒ£¢Þƒ‘ÀÇ\tÞÈ0<�†-\Ês1)΀à6§O¾õÖ[v¿2†µ&d~ª”Ò¹ë´æwå Å’ô‹ -užÇ©±ÁüÀ‰ñ>56”—S‚ǸÔ{›}bŒgk;Ù?JÕÔm8hˆ76K\Õâ9Û3a(¥b@娥0<–ÉD”ǵ`ÏÁÖTo#«¨³épó� CÁ 'ú¾âtQÔ�ÐpFÂxNhT¥ÜªïûþKGj±‚ ÀDà&äA¼Æ+¡GÂt\þ­ã±Dì­×Ã×`ÄÎ_žEu7…ŽP!Q�cÅŽÇ(Du<#-bFרÜÒãóã9»??“Þ ö‡À‚pWüRŠ 0ãããä[ £'ƒ�¹x/�c<´Éx†øÊÕ}IÝ5ÂÛ¦NEœZ„¨¸/æu“ÒËsìÌ0± Øâ>�JòqxvÖ!•ªlaÿ(ít!`¸ Ãj¬+jµé¡r�EñXµ±-”ñ¶m�ðšöcÛ’zµÁ–fCÓ Ãxó=жûW#‘�&‰6¸ÐK9{ö,¡0ò,ðfœî¼`x�=؈’Úº2 ÁÛæµÜ£ÌB�´Q¬ÙÀç\ ü�?gÒþwþ�Gþ¾¿ïïW\ 9ˆÚ�1М g @›¨CFÖä¹CÞ7_>)çOK8äß>ÄüÀ €ØÕÇ�Y‘9Æ’F¥Ñ 4Bƒàóy¹‚¿ygV’ñAYZœ;£q”�„@�†ŽAÃE�¾)|Ã0xŒ©¾¯_‘7.Œ‘0'àQ i”-e{2š ²—¢:Z‘ ¥‰sLa„�qn9™YG~7½ŸSiOÁ »áÓƒìÎ¥dd¸—ï�&š6—¥ñ¸m¼ÕF±L¯æþô®÷€æ„Ú<–} ¶<ÆÜ_zM/«7ª¼ß¤§Õ’*¾ŸÏãQŠ×Z³Éë“­IŒ�kA .½ò)øÎ¹|ž×g&“%°ìç 0=ðd’‰Ãcx=ÆñP;ªóñ[ Bœßš4j€H  QÁ{.�#ù1²¶¾…¿]ýkÿÚ¿ÖH„ÿÈ@Ï™*u´Ç̉á×€ `nȼï_;-§'œøý.e+oƒ`‚­»ªì-ï›ötÓAÆBi¸œZ[7d2ÊÈ�\½1-¥R•a­f³íüýIà $Ý9ކQ  °pá‘‹'úðý ˆl%4@šÅŠ ±ÇÖ^r J/BÃKÏB>§É•>  z$ZÓx;sã-ngÕM#›Í$¥;�âÁ±Aã‘�Û¨ÍÍ/{ó¹E�VªõÞG7¼÷¦æw¢áç°w—8ì�Â×ô8û °ãœs�Tcü-�’Àgï²¶ÐK´¼A„>½&<æ5£o토e<Þ¢'uóÖM<ï!ˆàfèÖãñÚ =„¡èÁ€ah \ µÓK1óX4Þ¿�Ïj£§A!·Z¢ÜF½Ì/$?‘;x�ü·ÿí+®¸@rÈEèã…ôõ`5ßBuúÉ“ãýÙî\R@°Ëɉ~®ÖöÒw�‰•ó1Ÿv¸<Å̆n†Ìë1 ƒ[ úYµŽ°{jÁP�;1ʼnGyî:Sx3™„ä2I¦QG"!l0Re¹;µ gFSÒ“�w³bJ³Çà ³¹Ð’�a …Õ"Aߟ¯ÙëDf^q>ˆHYÌþ‹h'½畯éÌ&ã…@* ïj //ÃC½0œ nžsKóðR2 åú§o²ëÖ+AŸ� �ˆ…w ²Cððãõô`í Ž+8ð½øw í5Ò’•͹òÄ�Óö PE<4ö^á±×~ ^ ®×f³ÁëµT,òØX¦&óx«!Hd*‘TŠ•êþ`€ç° A•~Õù8>ëð>‚}ôQ-QZ·oßžB«˜wA¸ÿ\D¾�÷* ‘ïéÎjßoq�$kŠ  €ä M„+¼à@Žara& ²Ðp¨/C¯÷6óä!ðÀ„b<³jó˜0ן»ù\ñÑÃ0¦F]É[ LÏ,I a¢¥%KªG÷‡ä¤½úÙ ÄO�$“Ž‹1Ö44÷ð]jÕŠŒõ1Ôå�ëH¦¸Æ¢A€lCîÍ®"œ"¨Ì/m²¢ÝdMíi·]Ë¢Æþs¿MÊ5Ï“×â¾ÛÏñµsÛe€¤3±"�J2c‚¸‡?±% 0ð\½q_V× »K€�ðgý �ŠÆœû¨é]Œñ‹X[mz¼xŽ£kY§�3 ©U©R¥C ñY ñ8ð=à,€#ÒÂãï¿â·³ ëxN CB§’¯ï¬iZ²ß ¥¹Ù°xpßzû-“þ–¤z5.›¸Ö—A ·àÕh4¯,àö]loc¡õ©Rê[—-ÂSÜvAÄ’©ë‚&;<‘ Rʃ:‘�³ÇSvFVow—  ã~e|¼^Ÿñ:]~Ĉ`ÐÚc¶�‡£ÀÛ¢Äx5üÁ’I‹‹k4´>¿—¡­�\z/†‰+ù®®™Jc€éU\»=#ɈO2]Qó�µÄöŠxyµHã…vñì¯Æû-­Øs« €Ùá*ž…‚" …ý4ø Ò¸ò»(EÃȬ'lù¼—ņ¬b„a§8�4ý˜ð܃`kK;á #CoD¸OãI‘C"ˆr�™Ú?“ªõ6ÁÑ«¼|À‰-o›c ^[ëñ:k™sjP xm~¨.Ø>Кà­ÍL|¨iío~ ÜDz�AV)‰×x5( %ÿçHô Š “¸Î=Øç*®û9™†~ý3¥Ô¼‘ ãºâI3·=ævH¨;›´gˆÈéc 퓘,-�3` #½ ã¥<1EØx,fHÍ ä- ›×&6ÓI3´JóuG@cˆcÍ}íÎw ¦GJÚ•Œ AÄÈÜÒš,,®È«§ûŽ qkH`®šW7J}Uh¼$÷g×Bp�·²+c¢ ~l›mz"4’feÎ-‰wE Ëœi“À¦>"Äd °ÆF¨’©©8an%;¬)ç3�Š ð²âV(Äái:„@�œž�á.M £ç¬Tç{t´ˆøÛ¬‡¡·"÷BP’Ü7ÇŠÙjÉXÐdеV¸M~6�…jŠ1!–çM”"'ÀckKàÑ×›¦�ŒÇ#4n�Uú Ýj�!ÙkŽ[…ž†°)ãôü:¿ÏÚfEnO/óï6wÞ¨ÑI¿¥�ôþ9 DµÈ}ð> (W䑎p•Å�…× _·•xMv€„F:�JP¶ºN66Ëདྷ’êŠË»k›Û½ÎœÑ¾aZ&%ù!¯Ë|vQn�ÁRlÉÇ;=+e²¶Ìm…ˆŸ‰ç2Tõ›Ùøš¡*«E0fø¬­Âí,ª¥g þÅñ(ª¸ï-ZL�.¶jö¹-ˆÈmè,ô*ô{Ð� 5<Üˆ#Ð/B?Í#ÔíÉvu}ñÕSþ¿ê7ßá?€pÝyBZ Û4νCÊó=îM¯ Ë댰HÑdx‰¨N.†¯Ãó@³²”Rä’‰CZÈÎ"˜˜DéÊ{Ý’:Vã¯�…!�9F a+ž»3+àFÖ¸]¨,¯–P ^¢1Ú‰(c4 Â@�«j€ Ãf&¤åd5™T_'%ØèVïOðÀk ¹l—ä@¨�Žôñs¶j{óÁ§7É—”âšš]ÚNH [‚÷9 G²Õ°+S<è´˜1ç‹ü•sÿQ…¤ä„:3¼l ä}^c&sÍÛy^Ìqs”Ÿ‰×óó¨~NŒ´ßÇ„À؇ìºÖòŸ‰ÈïC¿½m‰+‡ ®GÒý]èëÐn¥$†pVô+oœñýU¿õ¦ `…¬õÁŽ…8…Y;må@‰bů¥§; :Œû!Ö‘`€Ц 9 ŒÜåãJÔ�Ì݈Ǻa:o©½l"É(�ÅÂv.3È@KÇü’NFäx¬JžÇÜâÛÆWëœ×ÁTç�Š2€gVÔ<¬K¡‘óB-5C¤}{ ¼³ÑBЮd\ÆFúql"²…Ѐо~iuã©¿�™E…m§ RY4Ø�ó–L} ½�ãq=AÔƒ&B‚žµ¥ªi>©Ë_˜ÄAŠž|Ìì·Õý1½WžY±ä¯ŽAúuSpˆø¿üÚéÀ_ú«o° ü!xÈ IPvKê›pƒGò2 uñü1ë—ÇFääñfr öh²ôV”¨ i¥^C9Ì‘ê¦7ò¨>e¬е[-†YXÅ.Ú w`uK ¹ù俉vp%‘�^¬2€Œ±S‰ÃÏÛðF°õc‚ÚgVÜÛ?]ŠÀ�§�Gº/kH¤[þ…b6C‘OéA2#K)ÃKDLd‚ßÓHgl7&húÍq&Ïò8q*þÍcÜ÷oÏ~£¸¸âÉa¥™Býf¾úoB{¡A\à•Ï]>áû�¯¿„4ßž=æ@Ä!º!Ü>b%ÆÇ ô@&Rw57�­Ôòù7ÎËäÄ�œ>5&§NŽòví+rô LgXG”Ú_� Ëäà‘Ô`�·\…¯�H·SL£!†UL²–â1EÜ�žÈÇ7æÈ—ìFL�¡“±$\‰û™h�âNz$¬gF•@!;>„C]vçÉ ÑyR†Û`?ŽY¶‹YOOµÐÑâd‚ÅÂA‚‰Ÿ½¾~Ì‹Qéáç­<( öÑd¼Ñä9ñ{ò3mß•ƒH²]1®^ýKmc}ñß`ÚŸø  ¨)ÿe¿ö†ÿìñ¡½æCƒ±¾QdóDsŸFpëy M©V+R(l²wÛpvçSl9’Ϧà�døcîëÍñ‡\,•Ť C¹ 'Ù»—Àѹª ‡‚ÌÈÊfmâq„‘ãó�0<9x$ÔpˆÄ¶yŽÇŠÞ¼ò";n)ŽÐX‘Œf­„×¾O0‰H"»NwF ³Ô��Eèáùx,ñùtWœ×8’§^;Jø=,cÀÍm9$áw‡‡ÄãùÐo�»æys[r3ôŒ¨3êÚ/®„¸@r ¡“áïøMˆÿûÛï¤ÛþKE³›o:ÿ—üÊë÷ÿá¿õׇΟ±¸âÝc±Œ±ÚÜ,³ X¬°]Éã<ÄÐâBÿ z&»‡ØGB†0ö24²‚8ûôô" wQäLðÚ½ e‘k€ñdl?�ŽÓxbž Øš3Gà‘y¼’àV,�#4Š‹«E ãîÛÕÑúÃêHåÑ\¹mC�-�JrÚãÙ‹�#Çê¢ØŽZét’žž L5cècAߤ�3ÌgZñ;ɇ$<ïÈã–aCŸ×á=:‹sF´�¿£OLý ÛSÐIZ\9q�$—ŠË÷?‘7ÎOÄÒ‰Èï*%¿¢…â"ˆõ»ÿèßö¯ ö¤c&åTôÞ·aqªrYy½°¸n·/a�U­Ö0^È#Ósé- “)Aež‰ùq2E˜ÙA1åf£Eâ=�û–"7 ýy>¶‡ßžà…ù��¾ �æÓ| ]]P,¯K*–Þ\BF˜Þ[¨°½úV;üøÌ"¯SÛ€L&†€Xij˜Úk?Ý£¼)( �즇Á�–~:‚‰ö{ðï'Fûr{ܱ #¬ @ê0BZ”ά1SËÂãËýëðôÙì–ÌŇéMÆó€×r`žSø­FLñpŸ¸âÉA‘ëÿöß󗪛÷Πú/Åý¼×²V_»0yïŸø;~ól*íE,Þ™‰�‡l.·W)±x_H¹\ˆ¬‘d…A0�+,È2cpú‘kþmÅgkx}‰À²ËP³£z{2L#ÍÁÀwÁ˜ÊI&Øžƒß]ɉf(ƒi¾c£½$”ŸÒ(³0mznÅÎ*c5{>ët)hpf6²¬O�'‰YùRé‰�À¥�r Œ”ó<Ç�»{ÇÏyèE$»bp¤Ó�>e6žã�œ9®kl�Ò¦%’à¶¿÷aÏ`7`ƶ2 Ã}8­d |žˆ9'~/ù*Ÿ`„çƒø›(“f\9q�äþüŠüUÿÌѵY®þ.B c0µã£=­¿å/ûÊy̲gw¬¢¡QoÚ^› •=› ¨ ~ºñ2�3Ó¡è�µ) “‹Åªm8¹"n=Ðæ]ë?sm²“*nóùÝ´ùÈdº$›K±I âÊÕÏ"7 ÃÚ«ïí€Z¡cË¢K³òTOCÌÒ[»yož<Éɱnɦb$À)šFÒñ—�¤¤“£ ykÂ+\ñŠñF˜–€a7á.gFÈ^Ûž˜!Uì%‹�'c*®ROm„Sɘdàa� ÷2LŠ�ß–oúXñï¨E9l1•ñ–Ðgk ÓŠßOT4tÎIŠÐ"y«®x„©Ä"?& "áùJ®¸@r{f)°¸Zx í¾ˆ‹³1Ô›U¯ž�èÇ 'cñÙ¹øU™_\#ˆXhì÷*œ�÷á*ŸüHÉ­MMÏ®È2¶SÓK|†“Ÿ[è˜ÂÊþCÙ\Mz%Pò»þH�M!>ßít×!Ï9î"j�ëcš|_Ø ~–RO¿›8s‹kÒñÁ �ñƒ+üpØÏ­ýøÊ“:ê3Mí•nˆÆÉÏ÷�EÈ‹8ÙDNa¡ì�„‚AÛ¡&Q¶¬AÊ/ö?´­%‰ßÛjµ&×oM=Îk¦qîôª,èQ�6\ÛN/.€7�M Áïèï(j´Ã�J�$ ÅùS¿�¤˜NàÊ>‹ $!è¬|¾‚UL +ÎpO:‘Ž}ÁÙù5Ƚ©E›§·pÿþ"ÇÐÂ3a\v�Òùƒ‡AeæŒ*WþëÆ3ÁçÀ¦f–¨ð˜™ƒýavþŽÞÀI»A¾s˜’¹�íÓq<èLÏdt¸OR©8SQ}¦÷Ó^ ¾Á^ àÓ¾;@–HÜn¯ÞÝõ�¹`:.@ iø¥Çö—ò²:š†[§�S ØÅÚëÕ;90¬¨é‘ ـǽ¯'»�ÅŠ“U‹FØMÇ“çj .È!æ™ÎÌJ|uTÆ);…œaª‚pà¹1mg"&«Ë¼^ÃcéD[ŸÒ�“L]q�dŸ2¾ ýºÖz?(\«žp<´ìUÑ2Œ÷úF R¢AŸŸ_…§P á š†ÔÝ›Uy‘|Õi-äL6˜ÊY¥^Ǫ²¹ 0£×‚ /¾nû®€\ ƒ^ÉÊÊ Ã]¦�Á ûŠ¿­9-Ó¹Ùᘊ;>>`·(' à»Óèíµ`2#�5…!Ä'ÃY Úi@z²16jÏôˆãÚz|u¶Ph "T¦õ„Lz©S´GƒEEí)ˆ˜WÜÑ» °vÄç÷í(D†9-L–ÐBOõ¡.ÂJžK3žÛ£Æ[ ¤wè÷9møé1ZäH ‧SªèÝs"".S®œ²Ð°¸²�âÉ ô´†6­µ„àº3qÓQ´Æp.Hz 0ôüÁãÇÊñ´0ÐŽw²¹‚l³Ëi˼w�@~�á4‹a®µµ"»ô®b»À›ž]Æ}N>¤A®T¹º˜¬ F‹ 7í÷"0-.®Ê6Ÿ\)Ÿ;3A·€¿Ý§Æ”Â’=#…™zBkŽ%û�éE†šÆ³ Õ×åú�…q�+sQ�1ü¦Þ„Äm�YB4ªCèrëáÖ íW§cg&6ôvg˜uµ“Ö †g!Ç2Л³½H•v’èÁ�@búbÉQyC¤:Æ?Œz‚¾pÀÀFo¬ é•7 ®ì“¸@âƒÆ�W’Ô¢#>\¡Ã½iŸ½*…Ш#tDNž¶U“oP¡±_\Z7¹í»�í­4VRÕ×Ú�éfìm ŸMÒTð<@cÏizkë%YÂ} =ÜæknÞš‘7¡ØÎ-¬ÈÇŸÞ–Ûw§Xs²°´JÀQj{§(ô3^ïóûeŸàX w…cÍcú¸0øÆÐãàmº1™Bgßjýá¿ó±ÿ•Ú²M;Äñ:¸BgÌ�d/oÙoþ€üŒ>CˆH¿Þq˜I ½[’íÎNH@ €Â Ø“:?Ù�v[Oæä`à˜Ù£µ¦ÑK§b -/¯q’ßNFwjhWWÔB«‡íL'¤q‚:�•!­Mñ�°Ö¬� ‰½Y,k%–ܽ¿ À¹`ßã’HÄð7†Sü^Ü£ÇÖXjßx±š~vŽá7�€äï}ðX±¯–-˜ ƒÐMZÖ×�’�ï x¹ít±ré˜tgãrÍæO,~½„ÅøR3‘Ï´ß1‰N^kfu�¡´Ha$oÀŒ€m¹5+y,D.Ÿ”ñ�^Û‹0!ÔÓ¬w[›Ô�ÅÀ‰mVV× wÁóe÷€k7îK£^7UãÖÓ‚%GëÖ�wn d�&ŠÑ`àÐÃd6Ñ ÊÊ.«Ä~OŠÈeè¹Ã­öB\ ñC/ éÆ(2ÚŸÍõdâO”fˆÄ<Æ¢E ³Œ` hàL˜b;ñug*\_o†sÇÛ{œ^«ug¨ , m‘´ž›_f–„^ ²²Ø x«eo©€Ÿû¬Ô¾�Îä}PÈÔãGxx,:Ë›BE3½BÌ\gªhÙ|G*+¿Ó@<—|RÖ M¡(q¸¾ý.CJxPåRKZVûâóV¹]¯ÕØ¿«cä,9¡ÅõMz2-áÔKVùÇãQ΄ٛ†˜~*@Š„{"&åÁ¢eÑü$Ï·PiÈ,¸Á•õD‰¢§åÚ3P|òH }™,=²Czx(“¹å /€$g&œ~Ìñ»®¸É.Áí¢‘~h±ñdw*–@…ì¶ÝgÄ鹊Õ"LÁEÕ7 .Z†DÔ¶ÉÖl:*ÉdŒ†ó F÷®®l°àreÍæSÖiİå÷ØB¸RŽ'’"²¯3ê9�“y,0v— bŒ1¤)Êòp¬îÚf™‡¾ã¸òµ±HÀ™óÞ4ÞHÀçÌÝpRHw“(PoÐ|÷S“ÿä?ý�äoÿ;þ.yï½÷;‹ ‰z¥ÜÜ”+7¯Ë'ðX�eGOq/‘uOœS296 çO�ËÞ¸Àñ½Á'ûLmÀkúÒ¯ÊÉ—¿,×§WåÝOïÉ;ŸÝ•+×§ä>"Ê×  =™ßÄð°Â¢Ô›u‡':`þ…^j‘tƒ¶àqÍ�1½èöÞÚ­¸@’1³×' qTË&Ò‰p:lË~0ÕW”°qœßKÂÞ‰Yé²<ÉnËÚ~ÖN/B5gN�J?î‚^ƒ™bmð&sÈòZ0óà­‡âà¸é„öbí¯h™™]æq%1ýC‡Œ7ËS³S§g7¸��œN*‘ÉἌe%hâx�€GÚVUî-ÎÉõÛÓèf0σ¾ÇãTxFlsùâI‹G�*Ù(•äÎ⼜zå òõ_ù ù�ßùéííÒâßù|äXø™zÜB-SiM¦×ge¥´"ôŒ+¬ÉÂæ¢”êåƒJÇt:‡‚>¯Rªn’#¬seâI�ñD<(pj¤‘>¨¤ÜŽVÌX-³šµL»1,û¡ÆƒÛ)ü;1Ù'##6Æ·Ýn±@ñÖ­i¹sw†µ"ÊbV�›³`Øh$m»ÕÜÏU&�#2И¶Œ® ¡l%liÏ΃>%ÊÒöLör¥.�‡?àûÊë“ú7¾z¶=6�±Wª$cÑvƒ©¼–µÛQÂNß'ÕöÀ#mȃ‹=ž‹PJh”›ª,\»*W>ºNc½O.kV&à� Áã‘×ÚZ E®ksð” òîûWäÚµkògßü&Ó�À ¶Ó‰�Å6¿±$W§ïÉ­ùi€Ë:;ÑÐŽV)M¥dQDê¦R~§öÉH”˜V X¥X âNÂ�ø“Ñ0/8­·¡¶µ"h@É5x¼–Ìγ¥9¤; ý�>sr�C¦&Ä¥Òš_\!P0•Ö´T1]~¹wB!1v#¶,�ì— ˜ Ú¦¬™ipyÄñÐ4„e`‹YÈ ±ÖqüKÕÆC@Þ•ˆ¨3“½‚®M GÊ¢˜‘©²f4î®^AÉçõ#,Õ†§ÔGè±PxðZÓàÙìý·œÇ=%¥æ†\½ üÃ[û&lÃòæ˜xÑ)…jA–KK¢¼M‚ÛÿþüïòWýÕ­ü+ÿ꿎º©õÎE¯û­3›7r>Šdb)I㙽efçÞ-k¥ƒdºS] Ù ?³”šF÷ÐA¸â´ çʆs' Á“03ÅY[‚aTlmâ`¦�ó˜­Áù) SM¾´¼ÆäÔ©@7øÚªpZ"Ú¯l”´Ùqw��Ãçó9ýÇÀC=Ê’l·,ÅsŠDŽ©![§,”‡3¸¬×.Œ´AÌ·ØÅWíYÏ,6v„çÃöóºå‘�ÍÁØ|4® ¯ôäÃ&;™Ê,§¾îÓ³ s~|ã:2¼®ï�˜Ïyéò)ù½ßúŠÍÍ‘0߈,–¤¥[N7²�œ±Àj‘Ž ¾xÌ¿N!M›½ÝfŠ¢Þ[Pøý&ˆb{Û"ÎÔÄÕ•ˆ $è=x!ö°›S¸À˜ŠìŸ7«k1M“!ÆÇoÝ™£!Îe“{ð`;ðƒ o™•¾"Á;3»d¶‹|l€yýæ}:Xkk!ÔÄî´Zïû>Ñ»3]‹™bÚaÄ´Á9evW šHÄ¥ÚP2·´á|§ 5%¾®DXí±·GŽ ½©Zµ�•{ X—NpŒ£ñeB¢ðBÒ© œ>ž–^É#„}ÞÞûø3€Rkß[�Œ õ"¹¡Gf–—dv}^­F¿#ÜWÓù¸(À1Ù}½}�V«M\O œ—2¹2Ø hÑ6‹ª”øI‘°RÀhÃ[®ì@\ i#dTB‡Ø×áæ†‘Ó1g×a!z'‹ë,D{b9í»w&fÀ“÷€=’[ÓHóH† \·Œ>]³�‡I~ÏÎ-Ñë"´oBîƒe%ÓÄx'ˆ¡m(Ó~Çó\DÁï$»P Xl�€ç�¤˜­ ôé¨úÒ+ãHØ=��¦°àÝHÞ7ëv¨³,랉ߒ{<)¯\ÌËås9F[sqgj|K™ßc¿¥¡QP¹²$�‡çØ0üïã�kZ³�?²ã2¾7CyH¯—‹“£, =¨ì-tª`Ê3kŒZí¤…Le{šØi7`W\ ÉÆÂ¡¿åI—Râ�üÈž´Gk §¹âÊ {p9 CÙI×(à \é*ÙwáþÃhÛ!®k×ïñ{¡¯3ð( ÚÕИöÁôfbÃÌ©v56]z›í�6L:2¼Œ¤éÕ¦©µ} v=6/¢ðsÅX*%¹Ý´JWÜÊö.ü¸_‡‘  –žL’ÞÈ^¯è1±�«"¿YBf4­©*çÊÙ¬4 …Ǹ’†ÑàkK ŒïÝ_tfb`uE½±X7ž¢sŽ ¶¼‡-W“fuÎ̧íNé³ †f�èŒ0F¾òsÞ½r•žgÁ%Ã5â)ÙÒ´ üç¡�Hm‹è–H«Á£·™¼|aDþð;9ÇIñMne§ Ãú…‰�<çwfý²°¾jMÒ8oë$ÞC!/?e“„´Æym°[œ]Ú1Ÿ‹H2®Ù¯k?€Ì6Ó]þ_ü#)lT%NˆH“ !€ù�Èô S|•í™@}"”NÏ…Z å• <²„üú.ÈpoF>¹>ÏÎ̵z‹Çy?3PLpéëÍJž=ãüü~ÇÇz²wVóW>½7‹N×AÞ*ˆ+;×#9‰Ì�ËJ©˜@v )ºi´~Ec‹¦ƒ\=—©UÓoÉë´-7HG?&Þg–’ |d~Cb±ˆ™Ý�4co“2<Ô˾\¨Hv:¼Š(§‹+¶|žï½³ì-<…”`dim`¶ÉêKfµ]g2‡îÁœ+�çRÖÖ7å“«SÎ÷¤<$P¼vîþ4Hã&ôED,æò„ð"¨ñPΟÕ£C¹&@žàk@1l—¡;îg|ÉH¸�LV’‘(÷� À¨UDüž ^Ãê9`' 4p¥ZÁ¢d±$ºíádÏ3”bGèoýø}ùÃo¿Õ~÷ƒ› {m“ŒF¤? ¾ €Ò¨)xQU™ž+a_,¸¶í¹8ößCXléMMÍl!ïø�Ï�‘7/M�p‡7Âáb¨üßçY.!v7îɧåøä_Ž vG.œ�!ç”Ò&êÛn¿-W\�$¬”zÅ##ˆí*€‹”Ô>Ä,žSBâ: 2½µ ¬€Gûu(ã�˜Ùé-^¾w?¸¢¶Îöð>&öĉ‹K–Ÿƒ°O‰8Þ‡d8f£Ð­ÇkØí×›9™OÛ%Óþ¼®äЄáM*k̤Ïn9Q�¡¥u—Ÿ¿ÿ�祳ã è‰P:RP#ñ zóÕSÒÒŸi€¾Š„CL&À1§×µ±ûL2Æ Õzƒyõv“†8€ðÚÄH¯ €Œö¼¯øµ‚üÉwßÂäÇ“¨Dëý9žSHª¸uoNÞûð†�Êîgˆ€IyãÕ³,Lý#{?¦±6è).lZºM‡ÍñN¶­q­×àMµáé'äw¿~I¾úÊIð.U6ÉDƒG‚½‘}€?ÏW<átÉ\6…Ç‚Lg…­ÁÕrjr¤OcFÏF“;!ëP‹ú”€âŠ $CÈÖ:¥µN¤/ÛÅUá>“ÖÌÿ`s¼Ñán®N×QÛpâØ0~PMz>îå*µej&JRÜ,J>ßW=eƒ]ö‘á>þ@07„ „¦~PäEú³kwé�t ?üä– â1!PÙ]^».==‹g\¸ê¼{ß/+–ÚÚ ÈÁ˜-¯Hxõ6;û¾„I�~_Bâê^ C½Þñ¹ ©Ô›ô&áÍ8¼— î:1Õë�K_ÞöR¹ÀøÂç/âúè¥áóx½vª/‡Oý�ÿÇ·é1¤bI‚Ñ ksnOÍ3+nzvÑŠØ-íó)LTœ”/½qN¾ôú9×ð‡öS¹?·ÌAbØ=)×ËRmÔ̲¦ÔkŠû‡vuòß•¯¿qB&†òœ�ÿBZ›¼®Æôø½€Ë\çã£ý\d%(ÌîSÂßaÉN‚iéÆbãð²žG„’W\ ÙFíÈYÇ⥑D4Ä�ÖA+ÁÍÅá¡ƒÛ ¶#0JÇVï_¹ÆPˆG¤‚¬€(öEs¥|úÄ ›[>ÂÀó˜ ‚Ýn/O/ð³›³òÃ_|ÄÇ_Å`'‹a@í4Lc k(*gO�À X–[ìJ‚¤g'ÍF“žßÇ0°å¹ìGÈëÔñQžsü3Éâ ÈrZFáç§×ïc\n7{,ìà»°Ej7�Þk<Öñ½•R«üµ/^F˜¨K¿ýîg Ũ g®áùbµÂÉœÀYü]T²àš.Ÿ”7/� ZpÝeqµˆÏ@ht½ ©‹Ö{Õ'Lð‘HÈ 2£ÇÆkÇ–¿¯=_F)þ~0ŒÎ´ €,lB½Ú„¶ÂOK¶»âI´¡Ž.ËRÞûîx¬À¯awƒ ´ÛÓà;JÅ¢uîô¨“™…­áDü¸Øë¦n„3@æLñ—%©®˜øÍÐ%c÷áp/‹á¥Ã¥ÓËÁëùØÅsÇl#ÀlŸþþ<[`À8ĸ!墴“°?âÂý¬b%�U4�ÄÈCß!›Jpµ ®¹¥UùÓï¿Ã±³ãÃ=|ž'ˆP!¹\š-è«•:éÆ683C�áGÝvúŽmÈR@Ĥq'â¾ÿi&Mò¼=�kËno¼r†#w1G„ße�ã[ �»å÷„§F&��¨&7d’CΞžPIp8›kK¢zÈÂ6þ!\{„ ½æ„wŒ†™ÔÍ"2ÔVJ(^-3¼…ã·�&é™G£a¨\³‰(>g]RÉŸÏ2)D™&”ÝÁ“j"nŒ„­ÚÜâÚ~J„Ò„¶ŸH\q�dÞHñi»5 Û¡ìÀ-ÅBÉúø“ÛÆ†½Š�@ÛÎØ!H@øƒ…Âv¸‹†&•Š ü†üÝ*îŽ�•“ ƒÃt�EÂA¬f×ùƒCÈ„«j¥�Þ÷� /¡€ ÀçŽ8¨è¶0–œÕèb¸&%epöƱQ?„ˆæ—Wå{?½Âº˜¶Öx¼�Fš¶Diz‰ƒ}Yñ¨\½9Íã…dpN4œfžHƒÝ’·c•b‡hc€cù)|î@«ê- ç<–^u´»gÈf�…+w•b?rlŽ .;ö4Œ±‚!î¼ÖXõ.ý)‘fZ"öò€8-r4ŽU� �ýe(k½Pác{"Übÿ¸ÏÈÊ¢÷�gx9g%LŽ„üˆ— 0aÏ»{-ch]KyƒV$–°ÉvËÔ‘,A Wž nÖV:ˆpVÓŽvh­é�(9xib%d¿y„*µ& â�»Ïp#^‚ Œ WŸèLyBÜÝáW°å�ŒóP¼¾ÇøX?9Á?@NÚŒ–õ‘ ´«Mþ5 -¦,áÙÝ4äh’ÉïÖ) �ðøò{ƒO‚†ƒ>ð+³òé�»2‹‚Ê>½ÃD¡(þ³<>£Œx·Í3ñ¼¤Ó zué®›hš9-Û9@"-H8¬ ÊÐäãÆœÞ&x/žã}üÄé‰àA8-1žˆ!{­·�M'}þ™åÑÊxútVˆr¸X jìs‹›äEvPëÄcƒë™@‹ë—@j)‹£ âe"É™Sc2 ïÙYÌbÄœ3§> óKkv’ÃÇ k•�?ëÂy ã·V‘M£uqÅ’§ kM#K«fG 8?zHBƒ‚C�@€9.nn•b0u§í °ºd~L†1�óZ(WÛß—c,n?b2�JÚF—cŒ³Är¹®#ë‘0i\v\ wìfDZ$@PQôJNM H:ãý��Mxè#6¿‚Uëj‡e³p›áD†wúÒ¬Wé�16ÒÇpc?4@�‘£*%O™ê�ðºÅó¼ @ä‚ 0b% »‡d‹Mp4%Ù(kåñ½±X¬¡µÌŠÈ¼)Jl?á»â‰“Ö7…JÛZk¯íbWëÍCºrç�®8nû:Á„?bÜÞUAÇߤ|6X0«k+`|�B)$âO&Ñ‹•69¬’¹ºÇêñðq�U !p`eÏœW*2tbÚž¯ŽÇjú-¹ZíïIã¶�Í €äîÔ,2ºîh;{ÊqS•e”a™ÅJÀšHÀxõfù¹cýmxyü,l; M;Ã1“ªÌÙeù`0�€0,8Гeˆ/�kÜ›B­‡@n�j­:§P ³Ý,O?=x $¹g69 æq‹�€ñе�%56\‹¸y-£nŠ×ëé“£rêÄ(À7Ëëç ÂÛ¬¡ÂHhž#$²hÛúS§±HXß?SÑ’ÉõÀËìÂßtûñ¹mc<.�¸@ò´Ù�+pßÓ0§YUΡC‡·K0$ô ]¿{i· B£™µ=ŒlŠ| VÁ\µ�<å�u^É+/�bXfø÷2„£„õ-Pÿr”„Y8õ&‰qT®3¨Õ¶¿o@,�—½ÀÒÐN `A²sÕ‹tl{Ðy”wfÕÍ;³œå Úøã81l°à€dt˜Þ �Ž)WÃX ;ž ½&€†—ÀÎý É#fÀ—±£ÁÑ&nœœDè/Ï|r ­G¥ê¶"ˆ )x!ôHXà°A…a¯¶òÁ¨³:Ÿí|:€4ä„j’%xàØÐ»èJÆ‘™8`‰ñ4rôžGáb‹Ö8SßÏŸ™ ` °� „뛿ixÓôPŒêÞô"kc 8Çm+(±dŒ“øœ$Þo(ÑÓ�ó™1»!—hw�äiÓ~�ƒ¹cØÇ^44E“ZÒb(Ëçá<„·X[pïþœ(ÅÂJvžBôaI…(Ã,8Ãþp>‡×›Ãwi;½Åä&PŸÓDû æç_9+W>½KÃ3 ~àøh�ukf]z{…GŸ»|¼LŒ(2T ­0=V‰íqP’ë1î#@Ú¤¤bõÍì® |X,pÿ•9!p¤»bO8ËË'>ÉÍã�Í’ôfk ¤Yr<ÎcƒÎ ì°0>Ò�9üÓ¼æ&ìM‡×°?½‚˜&ð;‡#ê‘ �ù%{Ri…^ÒFE´SàVމ?à·_ÀŽÐÖÜüb�žÈSDL\q�d:¡”À6g‡1BG bêaÅüAÝ›šgœÆ�ck±Òg}ii•ÙT¸RåJñ†d66 i” @ÃùúF�ûbª…�pÇ}®öüèD£ôFèÕ‰h“æëÈÝ�Ù)€` AÛ*ÇÆXƒ`×–ð{}îå“RýñGªÒöʵ© ú'ò�/^ p=üE fW—ÏI™¯ã¸½°´¦B �2#Ša¬Ì‚³,taÂ#(»Ø/¶)qæ±X&u™õP~¯{½„YW�2Ü*©dD‚¦Ú_tGvE©'¥‚ƒ!¹ŠÄe cñdE¹§èmvž?¥¤>¿¸¼l¤ õ9¤»+.�<¢vä´ÜHVkk\év§¹?$¡1¼?µ€�G�'ðNÈaàÇ„Çç�:Žƒ7‚ý±ë#Xw�•d‘ãvš`Ú%VÝ\AšÌ#;¼C¯à -Á"R¢:в•¾Ó{¹}„85%fkŠëÀš (Þ +ÇÊ¡áD8‡žŠ:ðs|ð b@@4áGd ÚÜ5:ÀÉO{îx7| åTÌ>©>¸-¶Ÿ”ÙÕ¦ôôÅ@Ì2KñAPľX83Y“±äg#kË%/=п:/ä2€ã|$T§�ysj¬÷г�´ÖŒ¡£ˆŠ�á�Ð@®¬m°éÜ ¸ ¼è‹ýÚ`§u»mýôçØ4¬+ùƒ?úW×{qU 09p#Õß›EZ/�Òiå½½†˜žPÃA…„œ×*ª³ž™[ÆP§Û²Rñ±­G»Õ�Ï_‘—Ñ?Êr¦žlÏ3Ÿíx /¨8Í/![ÔÝè§1Œ�B™ …�ºÏÊýÙ鑾ácð9 ŒÞç£äíw?(þ5Ë?ð'Ÿ]»Ù‘ûÐ?‚þX\y„¸@rú—BGQÑþV=ã ‘/Ÿæ\}$ÈMÉm¬Ø˜BŠø0C!È™g‹Ó¢»#-ø ‰YŽImÛ¤ñ»ïfÁsâŠú÷1ô[r äÁ›³è=L‚o˜`lÇ8ú€¤ÓRmõZ†Ênb&ýôJ]Ö+´ë_¢ç2Ú—�¾ þ �Y[1SDçÊ� Å…û9T¬Çq›Ý›]•ºHÏÀ(Çáí¤·zz‰ø'ßYý›ÿîìÛ‹K+¹b€äŽ¸â†¶‘nœ‚axv�-üOré8'ëi9‹Ä#cý÷dž+(6„‘fܽ­MÕ4Û¦TF9°4e˾´N�k•Ke¶Þ4}¡l^ç°Ç„S!o'¨cObþ¬3éEÜ~C*&?«×λ/×oÝÄ¿”亢›uí#HCìaFѾy�®ðЮ@ÞÇX„Ù…Ufw!¤Êtõ¦ê—¾‘“‚þYð’ó,h|ˆÜ¸uW~úÖ{ð~ Úº ]W\ ÙâóÒPÔC© R*–A&Ê`>Ŭ”#æî“d— aÊ(2·HB"M”ó0p›Ã‚Fb!ÜA‹dª‡µ=—½ã�Ö¬"Ÿš^ÀsœépÐGŒÀ ‰^;'{¬½ôŒéi ô¤˜îëõ­…ßsy\J8&\C×î̃Ü]f{•ó¨ipúRuÑÆ;Sò,ì/ù�ë·gä] ÜBˆ¤‘•ÅÎÇNž“§Ïaa3ýæ¾v�†ê„ƒ¥�¦‘Q÷ô-lðºzµ*ë‹K²45Ó\CÅ^“™GVf|ûÊuùÖß—Û÷çñ»¨±ö'•é–×?ÿe9éUIv¥è¥?Ío£T.#T¹ÐXXZY4ÞÈ}èÊV“]q=�Yeøl AËxŽÕ5çÈ‚ òè™:yëÎ Sl›�§ça¬¬ÓÙ÷�vŽüM Ä&æµÛ@ ØTAÓŽÛmz”(& :cŠ÷UÚuéMEåóH øÁ™,¬´‹…‚:ubB¡g“ܾsW—W¤òé {~¡)$3²ö)?š× ר{éMF“ Nİbò=¶�Li}S–§f¤ˆd�¥ððÀÉÉBª7ß%GO˜.üÞÇ7åGo"Ͷ’‘± l-‰'RrñÂYd~õ‰÷éç×sáóéÕ›rëîTqvn¾l€ät}«Œ-W\ i k«¬L,ìíÏrþÈQ„ŒÇŰ&¦ßbõOY\^c›Qм 6‡RõlAÁMඇó;¦�Â|P‚E+Æ9Ã#‡A‰ˆhÙÿP%ãZeÉÁ›}ùÌ ¼Û®©ù…y5uWÉ+—ÏI*Ü–Ÿ¼W•Õ�Š|tg�Í@ƒ>‹¡°¼Ý× áTÿ£B�_óÊ;zBÞ̲OTUâ]=9éÊg†ó?Æ‚×Ù�Å{Ó#)S\[oâo�Zˆ‹žÝ‡WïÉÛÝ‘XW·;~PÌI¯8&áPˆ¿�§>¦Jdmma­«rýÆ­ûÈ’´Á£-=D\q�$íGÊ/ÚHu…‡zÒl¢õ³‘" ž+þvKË,2TP³Á¢®r©B7zh¤§Ÿ„ãµ²™P±òˆ9¹GçlÄKÓ-6)Ì&Ãréô úÔ×”B¹(¥ÍeÌ+ÉÙI ò‹+7ei½"ÞZÕ(ˆ×Òò‰u_ìTåþ·H¦Ø5 `¼p£²QHGSÝá"Så�k48çÀåa¿æc po¥�MÙ\Záë”j©âm5 C.�ÚNHñÞ|AnÍ×$?€^[“è0 0ö;\ض�¡„—eqaN6×qOs ½Wž)9Èà~úKЯD‚�³¨‰v§Ï\–J¥Vg1 ²£ì‚G“n,¬;„¢E†H`Èæ8�í@r'Ú.ˆdÖXwwš·±Ò?@Pm³WÒÙÙ¦Ž!,ÉxXºíäˆ* {Mzó³æd}}“œ×,¼J´eGhÌ+ìNëÙøò=>ýäfu}i%8�ï²L¨VÚ8 \/T€{œZq‚�[ay•÷qî‚Xùûüøðð\w‚ÙWð²àÑ•pìÚîêo´[òÝ}HñC—å^“¹¨v¼rå#ýãÿ¸½°¸TšYXûÀÔ�|ºU5»+®G’‡WJåP-£±}¶„n{ƺT,33i&»ÈÞWðRÈ£ˆhVF¦L:)k0RóóK²æT¥çó)z&Ê´J9Pi%”‡³ÉQ©E9v(“IËWÞÌÉÕÛóòñg7àU6%œÌKÀªK«^•; ‹—V6dt¨[†úr¬EÙ†0åõ£«w­î#¹DÔBÓ8éøJ¡(,:vä±Ñ­6oÑ3Ùâ£P@h,=Àâ΃¹°…ŸkçÌx»…ã²²^–H<%}ƒ£’LH2‰®÷ì3«å’ÌOßP^i{=“©õ´)®¸@²ÅçŒCG<–Êb@s8†E<Ï Ð@°áÝGÈŸÏ/§)+¬u@ß&’{ÚÑ�I“5•ä#l¸7�ô_­ÛI¾šQÄÖÅ}±”uð®"é·ŽÆ�m¨ò²I`Ð×’SÇG…Fÿã*®;-¡h—Œö„R¡Ñ”Å•u‚ÊËç�Io>µ­•ö"€èîÌ’5x †_±Å{gH”ž Â]�ª%üõf‹�*«8�uËÓî«7T Pûi§U¼â>JÜQµ &¼­›Â‹«]Çmè‚r˜R‰ˆ|vãGù®”¼rjò[›DÐÎc‰`óÒ¹IêÏ=Í"‡5;‰X„kKë%éÍØYˆê¡I‡�;ƒåKë²^¬0tj{5C_çÑ'JB{H;s±Q(2�É"ë2…0(êAØùºÙÒ�'¤+�G³~ð cðt9ƒ}O®§v³.ºÝâ{4õ§¿×\][÷ã~Ê~f<’†¸âɵ*§¡ç¡I•y†¼g½Gû[}víŽ]7AcÒl49„­Ó¡Z4ŸS²¿R«Öãgx‰�>°Ö(ÇJ²]wdqQL F84¾Yñ™áå·|œ{�6ZðÏÈ=;í¶‘sgN Âôæ�™÷Ö•ëìU61ÒûDjYŠ“'†{ôÂý5¿%˜(õt«ƒ">óÝkS²V(K —MÆ$ß•î¾lДÚÓÓ¨„àñó÷®Ég·ð™ëY²„×ÑhŒÇeâøYLå¼€EI–×í^ÖHµ[6gTĶ®W–k+ Szo¢ËýÝ©·ÿ™¿÷wWþ¹÷ÿW܂ĭf�œ€¦•R!¿Ï–a-lžyA¬›’è¡€Àˆ°ŽáÖ˜‹e¾f_Åx «œ¬p a>|O§¥»u¸.&Á£3«‹‰R¼ 0áíX4Љ|£Â4átV¬h� aÊ)Ž´EØ’m?nÝcÇç'úƒ¹tBNL5ëm]ùàÖLûþÂêS�H‹ŽS‹‚ÁnÒ“ŽËX_FŽ ä8R·{ ×ïyrµ¦>NLúa¡� *Ñ?º‰‰“×å¼�xPMí÷‘C=Ȥ\ºtQ¾üÅ7¶¸Žö¼Ð¶Õ¬J­´*åµY={ÿFËn«ÒhÒCYMÆ#w·qÅõHÒÐIhÄR* µàú?WWµÖ ܺ3'½½9»ò�}¯ XeÂcaŸ!^îè6ÛÙVu÷+qx"ìü‹4TÎõn6½ÐæænQŽçåM`á,s±:w[<ªÍ� ïC¬æ¢ûü ‡ä¿ø­?n²jûõK'0æö{‡‰ÞÚ+;î˜×†ª·Þù´|un-põõe²…�¹ ^J¯&ôsN½Ï[Ð×-‰\†×ÉŽCžŠ3kØLä9ÂWÓÇÛÉ2Sîóð8Ä–({aå8êö8BYÙLjϯŒv³!ívûS“Fµ åâšuçÞ\@ ºJ{zs]wq æ›�Yp¹âIÎx%U¥Tžˆ[öMzŽV i]¿qW p@,ÒZyÙß›3íÊÛœO…Ï E)d}ir �]†˜ëç”IT L0Äïhæ�‘ÆUBÀà�VÕÌ3:µ'Ã&ÎLqás*Ò'™Þ€|éuW`x¿ýã+Lœx €ò8��«W/Ÿ ôwë÷>º©WWךQx ;•ìiØ 2·Ò„Zxok(ߥºÓmŒ¶2=9•êÉ‹€²#QÂël~q•=Ç®"„uwzÑÎLãµG� <Èøä xcŽ=>>9*}=œ¥¿‡BðÐ- •‚Ô+؇F�·Hðo+–ÍIý¾�‡û³!ÄçŠ $[…ÎúÅ ñ‡‘�WB�2Rž#a˜gzj–LèËÅ‚¸\¶‹?àZ ñ÷yNŸ3mE4ÈͦSœÕÞ‹¶þm­©^Û#)´ä †ˆß‡œ�×st¼L‚ ”Òrcx«eó&!Õ‡fž‡º%Õ–¯IqA°²V�;S L¨8}lˆƒÎ×T�‡U,R˜ ¨Û…Í–¿Yg]‘–3ë=ª!ªeE"Õ7ß±º‘€Ï²˜Áï�Æ£*‹Dv*+«ô» ™&R˜Á3¥—‡ehxTÆ&ŽÉ‰ãÒ×ÓÍóÅŒ¬½ë•·µòºÔŠ+¸m{'ÌÖÔ6@—7kHÍž ÿ·ÿç÷#%\W\ Ù¢Iã(4Ž § @²œœ‚¨™ÍòAÖCpÙL\B¿hå•§ÎJ®wH²Ù 1â§×p4ëeiÕ+Ò¨; ⛡¾¬º|fTnܙۜš]iøw�xàÊ£ÄÍÚ†ö*e_Û*n¯ŽìAVZ„“óžwpp’ßwð®Ü�š'xÞº=CïF€MðL¶Ó®Ïß /‡E‚¼s`‰ºz&­‡jWަg­'ÈíãEL'ÎÉå—^’tW‚ßõ[?º"ŸÝ¼¿�kjqz%<¦ÛîE3öcšYXopƒE‘ É@O–ýÃr餜>uR~ý7G.^~Uú 8ÑÚƒL )­ÍHµ¸Bž¤C˜Ùhƒ*B€v Ò"î×EêŠ $�kMÚ[¥”Hô³åº²][Š’BàÌ‘™š^äðŸ@€½¨ØZä¸*»[¡ÒÇc-Ä<ÛÈP²Ó�ÍräE7 ܶX¨øÀÕ¬Œ=v^NŸ9ÇGÖñ}øó�Èv²³ ºMfQmÈ.„ÃÖnÀËýá/>–Ÿ¼ó©\C6®Ö²<Ø7¬m7±ââ�õH4™ } “�U—zu“Z ×:˜ëg¡¸y�ëuŽEˆTW\ yø}‡¡P àˆÀñ¡¡Ýڀσ�Oô 4+ÑdÑ´5CR,P@¶×â‚�Øý€)6ŒTìÿÅûû7P‹^Î+�P´æy=òBi ‹[¶Ö„©Áºi¶íG€‹fçÞþ‘ã’ìʰ×F±L/-EëHÝúÜÐÏ€QÂó´#Íð²È"¸ÍÝìŠGí)�Ì0{ )Ë÷ŠRÑéc‹wu°×9‰õ@¤^Þ`¶Öƒíz´&GÂ𢚨¬_‘´*®< .Gâƒv›Ôß&òÖýX)±¾J´0eÔÏÐÖ‹$ÜHÑö<44Rƒ-dùØa(zƒÝ¢ 2-li¬·mh0$ˆÍ‡I¸oÐ;QjÏ#LÐmM0álv¥ž�Ä ‚ES˜¬,†¹pÃp&mú =ô¥Óé´\zùuyÿíŸH±°ÁúŒÿë�"''Y¡Açð,ýhÎÏ3 ·Ñ$ þÔ}Õ´Ö$п÷³åêÍ)¶QÁµc‡Ë˜F;¿\`«üh"%}}}8ÿ¬ ‰F#rÀBð WÒ¨D¶Xˆ0û ×g³P¬¶·š?⊠$uhÙ¸«A¬@bv( [W´J^4!9Z­Ê½é…" íaP1)”²¸è™Ôjö�PS£Qǘ=Õ´C¼„�[5>£;—â¯rl»&øÁˆDº’qŽöeEû3!šY[”¦6ˆñEq¿F^D<�ö*ÆÆÇ…FýÖg²¼´€ž]×ÁSL F}²qç…Scòæ«§æ =ÒëˆÇÂôDñäÓ*vN`È/Þ¿†Jû9™ž[¶ùrRý½ÝÒÝÓ+¾XLzzdr|Ä)*ôÆBÍ\p­FM´I¿Þ¢Ñ'=(n¶=.ˆ¸@ò¸C4 )%IH˜Éb)z#4Œ/œÔÚêfäö½Å_`E–Å£#}‘{i-%âmÓNMÑâãz>1 w~[Ó -¤;í[ö�!è¡öEzº3v] g�(¡<[`¢ÍÖz(Í’H`k®'à÷ËÉ“'%—ÏËúʲ¤r}Èèz�çÁ}Uåj7–×PÐx–|ÒÜ;ù ®Â{òiñXž§ìŸÖ P¡¥ Aü甌��JÿÀ€LLLà3ìŽYðmy¤'™•u˜‚ÄL’íðHžØ~QŠ:‹$E‚TW\ yDÚï 4fÁ °èɉ{äÅ홟[¸´¼¼º�két"`'®n–ر¿/ËÎÁ²ÅJµR{š9'jTÍs  !™e„–}ï›HD $ñx„5 Ï^N¶ž7)Á‘ú¦S”ø$ Íf³Ô‘±q9uæ¼¼õÖ[rõê5¹zý–|ç'Wà5,¡xq˜ÙShIO^…‹¥èÅÉ£ºãhíŒlÖÂIЬJÿÞO?à®@0$}ÒÓ7(ÇŽñÏó\cÁ@€;dé,B„6—G€Í¦â6gűɭo–ØýÂH5R°,•´3¶À ˆ70«cW´nõZßF³Ñþô³;m„=¬%Ô*œ?3Év+•pÕ??¿Â"FÇÈBýQâ3^8 p.yÁû:a±½K±°Ž@bxægRœœiÖ–X“"Ìû�ÿ;aøhxdDr¹¼œ;QÞ¿ò!<”_È'7fá™lJ>“°Sr $ÃýyéëN3»�c›×‹R,WèmÂ3eñ xÛ6>ßçõÉz±*çÏŸ—×bIYÛ¬J$cE:ÛºO稳Òê%‚hÌ®Ñ3c×QâGÃË~Ô�\5 :¢»+F\ šÐ–=w$õ”ªufø¤¸‚õš¢DW°¢´WÛ¨/@—í†55½@ãÒ•ŠÛ±vñ'S ‚H­Î0CXÞî”äõyY81ÖO. Ù[æù½µ9Õz�^O8Ì6㢈&Ï2ª›º±C\elC$៌ÂáÇÏô£ɰ|ç{?Fßµ›²~oE†Û^Y-4äÞì'8zM™r…ãšéyz™ï�Ç3©.œÇ€„£qéƒòê«/ÙmUdfnž³Aⱨó¹GKìYôE©W7¤É°–~l6‹]œŒ†�±pÐ(Wëažr«®¸@â¼/ùO(à‹8.ºá•2|\AK Êî8Žz ±ñ«wHh#.ÎpbÉ•B_F8 "ó>\ñÂS@áã4Ûr˜>Xìõµü½œG¶ÇSʾbx‚+égWL˜ ›<‰åßɱa¨é•Ëçm¾E¨?•«×nJ8ž@˜1Mœ-•Šâñû ð…�xè!ã!,¬üä>üþ€¤$ÑhI ðfày —ÏßÈÐÀåµÎâ5¡Á�ÔJk¢uë‰Ç "ÇF{\ ÃÁ>¤�Gâ¬1]q�DCSv5{Ðï{>hÀ¤ýú|�˜×ã Á¢Tª’¹swüC”FD‰ÂãYFE|�!te¸ŠÎì7¡×r�-ìW× �QËÈêêæ^‰Ó˜0`Ëf’üùì¶àùþ®©ÚÜ=™.dVÍÎ-óñÝ Î'¹m ëò¹49<ÆÂQ•íÅܵIÿ5Þ‚0ÜEð ´k¼¿mÑò< 5¬‚n"¬U•§'kíú�9NlŒ„blÑ1èIsÛqC[+½Ùä2´D¸j cu|ê!:.¯!]@õ;@„)½hAO„^Ç#ê;°Êc!â^[.UÖÚáÐ*d‘ñ3-¦ø9�çê|@Lû•á.oH |œð®@ÛÄ×Ëëœ~جÉ‘<­Ì-®�¹‡ë¼mkÁôÛ BÏ@— Ó/x*° $ÿð_ó�Òß»&JIXká|åS¾DüÛZ½ºä;Ú·±â·Qv½R…m a�b [¦wÄæ#æçÛä¾ër Qvñ!Ã4]ɳµPëò !ÄÓˆzM%¢¼æ¡–—2os±ì 3³P3Â�„!.ýta?fm&ã6²lµêmð%i CO™svŸ�»òb†¶Rˆ¡£àh\APhÅ•²Ÿ„¬ÚÚ¦(€†…L.’èëë­Â–£|×7ÙÒ‰70áð¥��"3¿ê�Ö¤ýÒCâ{÷÷夿ŸUÕÏç¹l7lÏZc*0A„àR0EŠnR3´U†´P;ÂÛxl[‘¾|6i·’b¹ÞêïÎ÷çrÆY1/i¿¸ÛúüJa:[FkÍ™ÑÆÀ1v¿íW”ÖZ­¯Ù‹km­`ŠÙ*¬zX°OW£A@Ám†½¨÷§æé îVàyPñÞLY�Ç"̸y.EÓ†±Í#Ÿå4À$)¹LòØë—N¼ŠlÀ¸ˆ,2óÓ•–#!`,¬nöa;nÏ“Ö&&ŠIÚ]6·Î„ž2]˜†ËQ½±0C](zãp,L]d1<fpÙ$ýNÅÌ�`¯/ÁûÀĉçZÊ4u„ŠWè�XÊ -J¯¤²OĈÂb$Èpí±ñ5¿¼!Ÿ^¿o¡còËèMö%tR¾#"SâÊ‹ $¿ûµ—äŸÿÏÿúç¤E)ñZŠY¦qZi¸R@Ôku†ºX€isœ¯ÉèâÜö<˜t:Éùð;ªë9sÆÖ+á«°ŸwÈ6å#¦Ðš µ "Ú´CiÖè•ì°ÍŽÃ•¢% åøX?fô,Ú‰"éKgÆ¿„á_ï ±dùöJÜÐÖoüýÿ�Ãj^ÖZG”<‘6ŒP‹­£QSrd ×ߎ‚�¹!Y·ïÌÈ•¯£ãìù1Ú�Šþ\?øñ{òñ§·xœõÆn<@v¡Å¼fie3]­îîô ’0¡©Ëõ8Àò¢‚H[·H®Dà�Ôé�ìD|¦ý ”÷û°@IáÓZ[ÙtòÕ³ÇGþJ™|A )HPÅ.äÑ|ÞAR–¦¯�ÇkI$ÂÖç{frDUÇê [èÑLf®• Û;aG^Œðeñ"²¼º±ë#’FQc>ŸÌL†µü†y‘¤Ý¤‚`¨ëEŽÏ­WY½N’]ë‡gáÑB=Îcè€!—ÏNpv µ�þÞÌ/ ä¿Î¹F®¼x@ÒŸë’|*žD\}Ðè�`_�ábñP”¸ ~Ì¢Ò(š‰m« -‚Ćòö&Œ4º’ò9*ÈP•ÏEkMΤP¬0õ÷þýyÑèÝ7ìX%r6 ”üÈ 'íº qy (/œ˜v/V°›l­{ý-c:Áh|¸GÎ�Q|]W_>ý5yél™âr$ãý9YÛ,çA´÷Óø*á›ît‚ᡃ ð³:k+{åõò­»s-ÓÐñ[ÐqåðHsy/�„G‚²% zkD‚¯h}pÄ9VÏô@ÐTÐ̵ö±%à ¼¦ùÅ5r¬ÜëM>']ÛdÒ †ß`0cHo$ð±©`w>àXåEÚÓ“m÷ô¤T,Q0–Ì:ñz}mÌÖÆ}/ó�\e)V¸Mw\‹6b…~»M°Àg™™Û ¹qwN~öî5¹?³Ì€ŠÜŠñ è™™ãpTÅx ̨!‚Y êv›�½°âÔ•hn_¨š‘v‹ kå5z#a®mŠ3ŽÙÌ­¡n!ôâù+/±‡Ü{ߊ¡'×¹ïüøÊ{·ïÏW‚ÛîçÊó_GZ/”'Îñâb!ˆ@÷ݱYa8ÈÖ!¶˜ÖæôHb± K!ÉfSráܯa€½®±hÜ)Úü{ A‰!Û½æ y˜ÎLXÅc†ƒŒŽÊýÙeyëÊ-¹zsÆÎ¤b‘à´r�‘+Šó‡Â‘ÃG‹>Z)Åü£ 8ÍâRŒû%¾Ð@¢,(§À¾�}µêÕÃZíæŽS~ÍM�½pãíÇS�Ïž–exù‰Xø¸ÿK—çÿÃ]E}É”ˆ¸HòIÆç$Vì4ämÓB~rŽ� †ü�B±Ì-  í-0ÆñÂqɦcx,ÐÉì*£©|L¹ X¤_ÒÉHô\½5#w§e£P‘•BYV�–›Œ„ Iï—x$ÈÖú�:LOå¡ôfC[QÞnµõ Õr:[ÞH ié�g¬‘7ÒF Ÿt=�tê–?½£Ày^:jô¯üÖß¿+"=Òݵ—)¶ZË0Žÿ#VÃðJ3Idb 'C½™ýlÖÈÏNØ|D_F¦ZB:<„8ÃOýHÈg¢öê#·°jwà�Z$ ÜÃ~Í̯¢?Öº!㽑L2"Ù®8�Åëa:òAï+³hÌùáÝþ¾¼äÀíŒ�ö3|gŠå…O@ÄßMÒ3yÞÅx ÌЪ—¤¸:-�ÊÆNkGè‰D£Qhl;]ÈeþÙÞeØ ÄÅ?øÖ[ÿþ�©ùÿ޳J\yëH¸º÷(%ǵ–.´ç qG”ɪö´Dacb®ÜB ‰àD(]ɨ ‚øŸœè—\&q(CêÀ5°Àê•óòÛßxU~í«—åËoœ–—/LÀH'¥b€æ6æÓÛ³rgfIæÐshi­ ÅJ�<Ì>JG¼šã`YÁÞÛ›µ÷›ÚnµÉ5á9·õ¿ò8©¿/ÄbXÛ{…ä:š3š�–Þž§îìúýÛo­ƒFŽræÄˆÞ²¯Áìå³ã_Æ‚æ "’WžÛÐVL‰:  ÙF* Â#IH á¥vréHÓU†´k3$e⮎Ñóz¼ä=ŽMô“ ŽGÇž²Êª~�ÅÂÙãC¶«Îμ·ï/€G¹)ï|p“á7Ö±´ÛÎV§g3EœQlý{Ï31B“bª3ÇòÆ¢az&±x„·$No-W´i‘âãJæ9÷HȉT‹ËØØåi…ÍYkªLödêcÊïNާ'e\cõ݆wðçöîgë"ò(¶®ÙK?ÝgýMÊRc¬bGfšìÞE%4Áé®6FÙñB`y; W’…çÑÛ�ƪ:ÃékGlÍ}ÅÀ)¦ôeHÌÃc1!A-h¹oרpIç±î79Ë¥Ö`] S¨w Œï³J$¯Dâ‘u8C+ }´i±hˆÄo*ãE::ÜMð0«¢#&0@ÒÑÛ‹·ù=Ƈ{% ¯ª;—$qE�ss+4öaÜGL5Át÷é½ „Äð:¸R$¨õåX”�Vñ¥p$7UKD…pf»7$Îè]OHž7Ñœ»Î1º�ÖSÖŒ(Åäç*QôjqŸ##‰ºD¢ÑÝ´Ò›åâ1{Èš…® ÇÀ—tHb뛥¼ˆ|Óð•‡H,×¾MgÊöÕG™#I*¥F6ñÀ@š^T¾½È>2¡,�23KFGzx‘öõfHZ[†G9ÊÒ 0Ü ܆²u ùœ3Çé…„B~þènÝ™cÓKp%쪌×íÉY4vdÂ�Lñ …ƒäGR]1r` vìK‰+ìqKè}° pC¸õ>W‡ˆa¨&À£Y+³1#Höm*)•ªrãæ )i,ò´�$›Ë± ušÒ#™é•<­u Ø—¾ÿ³7Pø‘ûÐ÷¡·^°êwE;N  „¡Ð>h?4-�-˜že+æ~ÑðLí£$Ý0è%Šñ}œp® õ.v‚‡YbåNÇ•3y­8þõˆÍ'PpÕo*y9�ñû˜ l a»Üw€%Prjr@úîêÉu±×ÕÝû‹¢u…”d|> ï!Ùy3HjhÄH€†ÙÆ�Ñ&Þ-ìOú�kÂõF”‚ŠI†ÊóØXÙ�Ùå·Á6ñ›òtBáua‡mËz'L ©6õ•®)»{ôÈp/98Q»n‹ÍÉ�ã#vH8áêyÑ�âÿúû?üÌ„qbÐô'ÏqÇ`e@cz̤Cg|>>‹"»­'•êŠtç2�á¡þ(~ã¾B±h/^5ìÏå•ÕµÕ�?¹º037¿X«Õ{MÈë=D$jÕZíЀ$ ŶQT§£á€0¬¥v·²oqeo!ìµCZ\ÉôÀ‰„‘#(Îà'Œ¥5­ZBx¬MÒQѳbI|—°ã©Ôëu>‡×ï¤ Àa ‡\>7Î4j̳&pÀ3a³HÑÞIlGž‰ÏÇ:ò#XÝ5Ñ e3 Æp¬}[Ô¹¢ ¨ˆÇ!à�<'Ù[šÚn3„'–×Ïû§«õÖÑÖ\à,.o�Á#ŒŠòYŠÕ‡ŸÜ”—.ž0Ý'{ÖüÆ‘‹®—ÎOêúÒf±Rýãï¾ý‘†uœ¬Ÿ=‡Å‹~èièeè�­øMç½ý½y,âãÑHØ7:< o¼zYN§Í©sZª¥ü_tn~)úûôÍþ�>¹V„�™XY[ÓwîNM!Ô=]]:< IAC‹ˆÊ^ICw5›�d¥÷Ñ�ï„]4<åH…²Œ·Á“e8s›ó; H´xßãáó|}‡çâü­ˆ€x¢ŠÅ•sKë:‘ˆI:Ueã±(Q,fD\˜ý¾ž²‘%�Š�'ÙOËNý�A£: ÇDËãÞÈ o)£ÚæH´ñH¼bâAÏA.%ÊBø8“p¢›‹„¶Ø¨wðœ‡)ÁœK¢¹Èav¸J^»³ðFJÏ¿ás•J�=çLíÒ¾ð�È@ ¿ùÊéo¸¢?zë“O ˆ A—¡«Ð²<4ò 4›1–ÍdÆ{{òcÀ�xOwNF†ú ÏŸ†ÝÌ=¢#…–Õµ ÙØØ´pn0ò£ïïïIţѷ?øø³ÿ§_¹|^þ“ÿò<ÐÊv˸U%¬Èa«eb€Áv.£"TK1œ…Ì"V\Ã%¶+Ժ祹îNÁ€�€°0€ÁûlPgn›çy°ú—[ðX° Òëêµóï›3K•zCÛµ|x].'W²�,®¼$^PŠ „­0U: û\yÊRm݆6DÄð$�#xE=d»Ç’p²G"]}ËŒH<;*‘T?nóñ`,‹ÐrXð·ÓÔ ««YXXãý­2§ge³PÚ�´|fsEÃ!»å|øs/Ÿzóó/ŸÊ™�Ö T=G#ÐÏ ‰!Ú‘»pþìäç_éôk/_ˆ¿ùÆËò¿òKò7þµ©üÊ7¾"}[´5R䮾öåÏ˯]Bé�™f>ÿäßü×ýå_{÷ýŸË/ÿ…/ËÊÔ•’.ã‘” ªÖhXÓ3jÛ½}èÅxLЬiAO^üÈa:óÌ–€`úT0ÒbfƒžˆIõ5ÊÛÎc[ü�% ËØp¿ ôæ|H¥´Ú–·bOoÜ,UéY |¸­ã¡””›m>24ˆ‹g;Eb.˜HÌO¦UÞ! èç*sËòø¨¾@DÑœxÃ9_\¬PV‚]ÃíDÏ �Ì�L,ÙÜ(Éܪ¬¬Á{Ù¢½;¼þ¾1þ™]öXÈÇžïÔ–Øvçÿgï*ÀIžoõÀ@’ ˆëºÙ¹ÛþÌöïîîîîîîînvî{~k–Õ¸ Qæÿæ}ýu¸ËYÈj_-0°I˜™î×UõêUâŽw!w‘4ìÂuRcƒÝk‡aMÕÞ~Ûþ;÷íÚº@b?tÿ�rèãå�{o—D<Æyý~!ÄD"&÷Üy«lêlåE’-ýßúãßùíÏüè§©w׫ûÔ Ú²`uÚÝŠÂÕ­X^ÎÑkXGáœioëÕ‰zË£¤¢2ĺ‘+Y`¨”E~"Xð:éuX'ÑMžÃäóÂ_ú¸ú€€å˜x�tßäNÄ0ú�ŒŒŒN‚È5=uq@ZëQ|�|àóâÀàþËŠ \6ñî:^*¯�•ù°› ’<ÖÕ\0�á¹’ëp&åt×7’D6&7¨ãN4äË¥'—ro=ï�œÊ þþžÁÿ�…Š^K Ä?ÉàÜÚÑ€Ê÷qw¾5íØÜüÈÏÉŠH/É×ö¨†Ý®©¼þ:HwÀc¸ÇÖÎÎÛ:ÆB‚�áêB MímÍrÏ\Vfà->ÿâ+24œnÃõ·,ë—|_òÕßþ—ÿñ¼!@âÑîÖ’Nt—³uK¡ˆöLDü9‰x„^É•é‡aþ.º'»9Nä¥'‡{,g€Cn ›«��†§›˜>Ñ}©3(Å®ÈãtuVkãÌ“¼7(ñz°f$—vÁ †‚ïžQB©t]åœ_džèƒæQyøùëYÐÐ|?z ¶0G²4#|TÞë®™ÕÌôŒ¼yô$肺`0Y;2’–ôРÊN�ŠWå,ÌLÍÐ|ï9„…ŠySä Éò*3öâÍÓÆú¹ç¶òä GÔÎÍÍ{°ñºtüL·{qŽÂŽkeáÔm€Ý KÁ<»vílýú¯ú¢û÷ïÞÒÑÖÜHQZ)c½ì·m[:ej:# pÉñ“g`]‰Lfî›VÏüý?ÿgïF„¶”iMé‹i3nïP¸�oà�]'ÇçL6ÃUÕï© Í�è0”1¼6a'„°øp ™Ü�y Àèÿg×;¢àËßwûÎÐw~ͧµuv4Ÿ@•Y7|Ø�çÑs}®Z°®X÷0\•?Æ,6ë�úY5�õÇÿ¥WòÖ›O �(K›—”×üº óž‘ÑŸWÊ¿.ë,&9–ôF<þëMÀÑÐÃ7u¶Ém·ì‘»nß/÷Þu«Üz`7�†¦&YR>u¾wØ›b½Óû C�'qÆÆ§�˜-E„�kE;DQï˜ìßÕ~àÎ]4¦ª:Dä~ا`÷ÀÚ`e׈lÕw'ÌsÛ-û6}ß·íÇ?ã�tìÙ¹M"QE Rƒ± ϤI'ç«xmºÎ_l;üòëwÿé_þƒ½QZ[AØ^¥¤-ðuúm;æjBÕWÇØ_£ÀÐwöeºÕ+Šæ˜d¯Dxkƒ’ìùR&œLÚ1t~™ffy5XHL}ˆk"*ˆŠáI$êjãå¨êÍu÷�-Ž�MFˆâÙ6ÍBX â£À"Ú‘Š£Ã øû©Q”L& ©ß$5Õq9×=ˆ.Ži�ûoŠÃVAæ-7×°‘”Z=Nбay‰å#‹ ÇŒÇr�én™¤;¿«7h¾ëu4x?C~ˆÌ>$cñˆòÐÖoüš/þð#ÜUí²8‹=¡²J)vt�‚‹ÒÛ?t9­.æ˜Jì‘ä4#"â8òZž2ÐSuŸ‹u-"º‰R%µzê«Q7#{kc'‘m@C3©˜§1²&šÂk@äížL¾GṞ}ScÓ·Í¡ÔÃ÷x&Lœ�"F}ð§. ¢5é ½:€‰–[ñs7!F(×€X‹Äd‹Ç^9ÒEé ÃR6„ £�…(PÈÓƒG b^ãͼâ< 2üδë0ácžfÙæ»^¿ƒ·.Òa„D·Ã+ùðÃ÷É—Ùån½í¶Yw3346ͤú{yúX›,x% ‘�OL³�‚þ5ÅÞçwîß*›ZëS{··?Ü�ªÚ¤«¿kô"½C¿VW™€n=ìØgÂvVƒ5ðŸsè–Ïÿ¬O~ì¾»o‹áü—boÆuwsg=ÏæÆzw‘A=¬‹íR‰V£Ÿ{�À-cM…–z_W£%¥ ¥ä"92�®@ÿ†¯4H&,¯Q�aXñØFæl:[êÔ7Å'åK>÷Ãݘ,bÏ î<<>-C Ls9’šjáÙUÑó¨ðïݵ­…ýZžxá("ÅÈDrâñqq4-e½å ·Cú¹ß _[^Ššp˜É±ÃëE%×ÔŒðÑ„a7�a•­[:_ñå_­K%�1Ô‰¤'3œ?ï‘p'sëè±³HìexŒ5&‹KKÅ÷L”bŸJ6¹ Å÷no»§>™Ø,"�°*]cRàÖÀ†}ö�e)$Ò7o÷àG>pØUe�9)]�¼RÂõ ‚ßëz tq]ÔaµÚRIÖ«QJ¿ŽËóB:¿”âÍFf>jBñÆÜx‰ãm ¦ÇŒ¬ ߇™PÖ†.Ö¶Ë™—/øôû廾þ³eKG“àe^ n<ÿ¶¥°drnR½\:ÛÙgD)"¥´\î½u»¸l›ç^>!‹ Ëpaâ–ã†il˜²W½ ËOp0�Þ†…yvØôæÀ£ùÛL|4¹Ã?oË�¨k4éîäU·çxNo@:4½²––fÏö=ûAû_a·OÖ(½ûà=wö\�œ>Ó-}¯�ŒÉÄÄŒ¦½}4�!aÙ·³#þ¡ûöݽmSÓnl›4�6 \Uê;µrl+ØW›P‘¾å+¿äs¶}êã ô*ä.ŠJ¦ÈY`NN"×¥u©dµ{-Æ.×JÊ�©RåHll7,ŠEµ*ð7–ùlz‘°W»· #«ÎGÔ�°Ð(ŒŸ¥Ô†%Úu ˆÏ4°xL{ ùy�+§7qìÎÖzt„l–á‘q559ÍÉ85›˜€�Å+Ù«¥:?ΧfqQZ¢ùbÏrYÈ¿Ô7ÃÁˆ½5/bò$dréGíµ,g $†K¦×Šy__ÐÕªo¾ï`ÁssìšìNíñj°ôðñmÚÂ{)§,9qªK¦&&xA‰sÿ½ú–(zÅK‹n“¬ˆbrQ©Á–G‘ònë¦xx´Á‚Œa�ZîÍ`¡ êÍp%ÌΠׯlP«v‹^G“°BJõÞ{ÇÎûï¾£åÐ'z:AÏ ‹³B¶‰å ÙyæTyλ{úå™ç_H¥`:ËÏõôô/=ýÜáçÏ_ìqÏÃl6 sJAÿ-‡Uëo[J•»ò0&Ù  ~I¡Ìù2µ "Mõ¥á™öH|W]‹Ys-bòŠÊ-¢üf ~¾üñ_ýTVûea†®KÒŽî”­Ê+Çd äë“9„äˆ!?ñÜyô™×Ų!c¿çVñ(%/ü¨I Ûx.¦ÿ�Ó+Éá{|mç­©ŠÇ`“_ WòžÕàWy¢Þhn™0Þ ;”‚xb£ÜuÏÝòä£�BnŒ,®T°±”wº"žyæðÒ`Þvë2ˆDœ¢]¦0îý};Ú)=?‰âÉ#§.x2ߎ·Fž~ñØ$Ô­[åŽ9­Ëuv –�Y´âŒ¶ v›~¬ nߺiÏö­�›·nn�سS5Ö×£uȧg2ØlŽ À�Þ¤THÁ~îÅWÑûh˜@;�º¡H8än¤±çÒú|8°aX®TI5,©%I  SCª°!áájC}`Íÿœe)Jš×Õ%HUÅÏÝP¦ ’êZ®„쬫³å+Œ@Â…=s{>Äe÷Ö&WFÆý»bè‡lEkSRÐãš)yë;?E.*ƒJùQ$>/\ê• .JÿȤ„#ä©"ü¹&™¬\Ëí¥ÇarL¾[yt`!x˜ÿgéŘðYOC§Õ£€�—º¢‹'ÿv�Ï0Ünä�{‹ó4ƒÐHÿ $äÇeÏË|^óŠï &P¼6EŠ ˆÐ+‰dMnëÂe%ß©n 2ï ”yª•uÉê˜=—]˜F}VNtQµÎ�lÖIæ­°:ôÔóÂ&/³À1 »K{"!Xî£>Øö•_úy¹ãÖÝ;Z›êË÷ïÙ¡�§ :Æe ž7€‡sºë¼ºt©{öÍ#'.u÷•�ŒŽyf粸Nc’F]P,ŠèDdE‰V"·�gŸéÄ¥žþi "G`+¥¥“Cq]ŒE¿h¿M•á ª�c!rCR…y¦ÈžÃZ5ÕQÛˆ6¸@d#M‚ ÈçWÅȯ×ð0ï óAz4ÅÏa’ø$Ye¿†qx%èk-'ÏöJ-޵P̹ÔÈ«¸ÀCO°h\^=z6×Ý7â $¦’{ …Ã4ÄM囥M‡|4øš¿SY ‚Þ²ÞàxËuOÊ8h0) 1¯ó0&_ñ~ £<%ëžh€„¡‚6¼ÅŨ*—Ññç䙳nÜ]åH¼Ñ îÖž'b!§Wâµ=\ø²—ò`âõKÌù�~Êö½‚bÌuVÀ×'«Ð•4ioj«oÀ&«¥¥±¶›×BnÌ…,…õ`¿fwuê}5,¦Ã`ó°¹›Ay4íÖ?³ #÷£?ô½Û¿í¾ôÓï8°3UŸªUí-h{]ÏBÁË$09vrü™ÿûßìÌÀ…Àdw—²cŽÏ¶¾pÌÊÎÍÊüDZ*œ9ÙÕP!a[„yUG¡Ûí\æ©ç¿ ZÔ^ÙiØR©< ¦ôI Â…Ü‹*¯ Im¼‚nmáù /7€ ãúUðHÔ:€¸�-«N¬›fT�@ÀÐw7v¨wÅ…™»~.žCÇÕ»~Ë$·}Á„$jê0‘g]¹¹€H Àšj;°5`Ðaþdq9'g»ÓNß`:×Û7¨Vr+p­SŒÏb¼ €xV� Ã\ú5Ô:gb¯æWr+°%×›â÷0ŸË-Æ×/ωCÑÖØâì,¨`íÝÜ©PV]}ýìüÖ/Þó9‡J%«Â†•‚º½Äe|AÒ«_~õÍÁ'þåïÇ*“lŽ<5åžê(Nb.g º"›—ΊêÎ[ëcRñKÄΉwiNÆ3 rò|oÿ?úÔœë-|y¶”@² +Úm%Ò ¹ ®l!-öÉ–ù œ“J<:9†�Ø~Ò¥í­›ÌöÁ=! FÚ/ëˆÑòØÆ†µ”)ê[3pÜ$¯í ¾b6wÂùUç&IŽÏØe�U’«ŸN§à@m­fÔåÄ*Âkp kö„_Àvä50”vzû” ¦ˆ×¾ ¨¾-äeêG,ýZi€P`ý¹øTH1j0ÈiÍ*‡Œ0Râ¼ ¸à¸áÿ}«7 ò�CS”mêc•ø•wÆö�çv£Ž¹¹,Ï‚í/sVrŽšœšÁÔʉÏëQ«"¦²f ÏE±x3Ô•�ü_Øâ8dà¢%ãW™ùÄ„Ç׳y%Haý²06Âé>lfËP¾Þ³½­bç–æ¥í›šfk«b£è ¿€b½r]…Íj�YyŸ„ú]:D€­|Ƨ ýùüÚGî8°ãÌ=1S�+ȇ¼vøðò‹�þW&Tu� 5>„ Ò ]‘Lï sl36—-ɸQâp=¿Ó¨G›Èd‡_?zòNmZ·-¾Tʉ£c†[Qu½¹2T¾¡­hHGì½¼ Q¦-/ÂJ¬d�Å"à2—œ±eØYº‚]^ Q6¤ÕÙÑ@ášO¸‹÷E¹x"LÃ1%oÛy3œÄ#„¥**£Ò�ä{âÖ˜”üN‰h½†�N ©Š†Äãõ©©Lb}ÁóO«&HQ7Â>0~+m¢Í$×ý—Í¢Ë÷|íG.T8�s1àêèïççœ5àÂ^;”DtÞ¢L‡"8á}NQ‡É‘ÀIк1‡‘ B‡=e6’ÉZª¨œG�éñ‰É2ÄåÑf‚JÀzaSùw=x$ò&n}‰šžÎð³ãÓìÑ>0˜f»Þhe„¡ъ€Ë� 0?ƒœ!ÃÀ  ¤èå®ÛìÛ÷n®yäÞ}Ÿ:xÛ.kB^'w¾{p^S«Î�,½K•¼­êw꼋' Y¿ð3?ÚúßùuŸ]WÛ£”=ìÑ{òˆþ¿ÿ°jÃv¤5÷æ‹ç*åz8ŸÐ=«ÂÛáW¿í1Lº—OõÈùþ´+|ùÊÙKýÏhé‚M”Z"¥¶lú‰»í.Ë@ýÅ‚a¢­°y©x1±ó@n¤Râȳ„CBý¥'¢wK†ú‹çŸDf­Š&2'<÷Uh/ÄC[“7m`•g·€‰‰t05 yÀŸŽÜ {ê£Å0ëLü8ÄРsjdlZõ 3 &‰„ /†2UòÚå3,†uæ,ë¢GÍ$ç±(`,�вUæ—Ðtm‹‡Ÿá{\øøû$+Y~¦¨yû|Ì&šPÞ�ì‘ 34ݼ÷r.õØñª*®<Š´5=3'³ÚóP=Qoen*„Är`~9`o*Ü ñô¨‘„w™F m�ŽM±³èÌl¿+Ä(‚ãP²~]¡/€uê*T)„€‘Ÿ¥ŒK5¬2² UçG(,Ž�nsvaq¡ëB¿¥½�¬\ƒÉLžäû­°]°le÷î]+ò{¿zï¡�>øIÈÉ$JAYœ›]ê{ý ȲU×ó{m¡&¯KÀgKä¥Á¼«wD^9ݳr±?}üõ“çÿ 1½ÿÂá3:|ç”Hl2è‘x;à�l à°¶¤:.˜¶F™jÅ�åQRÉ;#–º1•ÞEåÕŠØ^­®Ã?&ùL�C Ì62\×ìv-ŸM•?P©Ê0ÀÅî>„2âÃ"ª;Ý-³“$òPkw•J‚ /*¹Ô;L)qœ5¶p kÿFKƒ€ÏTÃëü�YœM5}¾ ±7`´½è½(¥½2 $ž< Y™3Õù¬å ÿï»)€jtÞ Iš2!¶Õ—’y”aANÖTA¦¤A�gsAŠ×*…%33-s³³¯¬­ ¼3uͬ…õ@å*Y„¹æ³ T ¸Ð“A͋簋v¯ÁÞ ×x4—•˜WJ½k:Ì&«ÓçGbgÎ÷ MLe,�|÷Á&t¨?»ö¬ÓôW~Ù7üÌ�}÷gíßµù6ä_ì‘rœñÁ¾Óçß|9ñÙZ%$ÀY›[^ž�›³ª£a…õZ¨×‡sùÂñ‹rüâàé—N^ú×é¹…§•RçuÈ)µhcH#î>�¥ö#™V ÁF‰FÊ™#1hX¸h#¿†ú*Þ¥bha˜î†8¦UP®H³'#9B&�I¤ÃlÊZû…‰ kõ còÍJ\×¾¡QNÊm�MíüóC¶´Î¢±¨xqNÎ_ê‡3ÍŽj uIsn.P<|„­ÝÝk@…™¤ºqL>„¯ 6Lêós˜–ÙtÊx4qÞÀ‡ÓÌ* ú C�ÄÒ€xsˆRŠ�X¬»ûÛCC!‰TV ú… B]¤à^\&»H@)Ód ¥×טC™�7ƒzgjŠ•ð Òô̧df礷o„Ì/ÛËõ£d‘ƒ ¬o±Ê°ë⋯�Áߘa±nÒùßöíÛ¦òG¾÷Î/ù¼CŸÑÚ”rK$Jz§ÆÒ^}áù`ïÈDÈ•MІŒ>ào�Ë:g†s¡Ü¹Éeofù�¿<ŠY­lC�Ø~é~ÎÖ¯ýòÏÛ·s[GµQë‰×¶#ÑšÔBOwÏ,¢¾©%å‘h�Ä[6EöîÚVÕÞÚ`ÁûË Md$ >ÄМ,ÿ㣇_ûïçß<ïpHDºa‡asëúýëTým„%›zlCx¼<å]Ë•›v¹ã9“£-rnÄx¸ €äLi†2ñ|=L>Àêå‡tŒf•*Æwg=HL®X¢J&¦¦s‹ VÏÀˆüÙ?>΢DPß–?¸Æ>i¨‰Èæ¶Z9zªÇ9~âd€äy$ZɸtÑi´†ÞË ¯�ñ¹1“ƒÈ-OÍxpðF !�ÞÌ2�kV˜Q¹À!Ø7‚87»P®¤—šRÆã1ws¼ãýO– ¢Õµ©¯Dñï�wÞ.“ãcòòóÏÉÑã§ø¬�OþLRW»º©&‰�†úZå¶¡ðã>…Š-s¯Èo˜ðv~·Óu ††v1ôðèƒ×>‡C¾T*ú’ÏÿÌÎ/úœO†;;Z7^�ÏqTª¡¡å£ŸóóGŽiaYì²2•JÕØ�£ç:í.k||\�ê:Ÿ;væÜø›g.ŽkæÙìÃÐÚ ᨄqéõ"˜îˆG#þåŠ.¡�ÌŽ¢…—òÁÁç#+ ?Ûô Ñ¡-Ët8,y•º£Œj,=ŽewÐ&+fÒ–,šºT-rO)51>Ž�à$ÙpÏ&OðJnïlŸß6 - ÉÈîÚ½µÕ�8ÖÙîÑÜyȨ\hmbï‚â»ëê­OÍkK?%˜‰<¡ðõÍÄœE~îÉYᵟ›]àõ†-à  ÞÍÝo¡ÃT2ƒße€¸üMÏÄ\Órx(�-ÒÑÞÌûh׎­ò«¿ú[òÆ‘cü Á5+¥H>w¡�Zô=YšËvªTm ……�nC¨x,Â[ ÂFN:«Ca9î~¯Iôò9 ÕˆžEx=8´¼{×ö–/øìC�Ÿyèà ®¨ˆ¤à‰x ‡qY�WVSSeäVpÔ­û÷Ìãéó÷OÿÑ ™÷yV·"î…™±¡-[‡¶j”¥|Á2 ϱ†$Ä’õLvÖ‘t¶×MgK×…P„7Œ–@aã*ÓéP[i+Ö�·a›Ý·a#1,Â<ˆÉ•q0¬€bK†¦3³Êïìy>(S¡yý,Xĵ4çm™Ì�€ò(ý#jL›²@µ%)&SK7”13,“?1ވɭ˜Î�¨éé˜éÁ,�C¼«W^?~V–ÜþãcSÌÉAÐ�ÔÏuSƒáå³ÐnŽ‚È/| E"⃇](W<��?}º[j¡TaÅ�dýiä`–ÇÆ¦Âç/öóÞ³‹•ß¹ÓΛù\0ÀèÍ€õ¥C»kKp€÷̣ϼ)g/H×…¾S§Î ô|Ù—|A BY{>ùчØùóÿ]éóêÃÜŒVD\O�ZhyÃÁú÷ÔßÿóýõŸÿõ?�å0´ºïã°ñ�’2Ýa¬ÎR–ÛZ·'Þ["vy@Â�]Y[¸‘Š$¦Q•n™Ëç&¬UšÜÃ5k´*/ì1}<`¾R1L] ò%ü¾™L›ê%žÈ¡*yƒÊ˱¨î4& €†\þiÄ´–sHj¦à­D7jfÀÌù$jä'è×ê€ÉªìŠG'ãW–�˜DM221ë¼öÆQ52:Á:�–†$”B�ÄTëBåjLo÷› ò�ï/(àâ4Ú¬ Ÿp뙸 ã^\OÊ£+…á,úÄûÞ¹Ùy…*y\2‹=8Àö¢¬ ÖR­û…¤ÿäŒ wãsyB§ŠÕöÿú¿/"Ì{Ñ-ˆ\ì˜|ó‹¿èó·~ãW}ámÛ¶´ûCÁ çÙUŽÞÃÿ÷ø3ùÕßô½a|ç -qõ²ÖÓr6Hê`�°\àjIu9þq‹c‘àå‰î‹nCg IûP hL-­ê«ÃWôJ CI‡ºJÎb¸J­†²”èGeTsiÞ  Ù”¬Ÿu Éõæ&±}6X3ج³do�)ÃÚÈZP%"vfg¶°°„…ÖÇмVËçÄ’–æ&ª$oÌPÆ4p¨÷VÚÏési­†½lìÎʃةU¨þžKÒuî’@V¶on2ÀY°G¢”ñ"ÍÆa%[8Ëîæ`#Ž�Œí󃼤ÕkÇ &¸  Ñ!èÂj?·üåê´¸~?€u)‹¤ÄONfòB¤d�¬wÇ(À^Œ¦Âë>ôŠm‚å?…¡`„ñAi®:ø‘O”ò#ì©©Š{1äÎHzô¿?õ¹_Õ=>>¹YG–^Ò@2“�’f]þ_ I‚û] oÄ‹ ª=P�/KI´_rÁñÈz>¿o:MËÂÓ”ã™àµƒç £´²ïÞÀ[%O¼L›¾"¦.BJ¯v\Æ–»È¸”Ì¡!2_ày�¹´g;òC XdùG„L/H]�ÊŽDÁ°«Ûè]˜¢½ÛKGÖÖ«´M�“½^„¶æd,=Ð ¹}½× $Fjß0Çr:é¸é‘¬ÇkF8¦¶&áÒVêBBzû™ÜÆ¥£ø#¼—ï8z°b¨÷¯–°_$}¸·˜ùB¥$„¿è�WV„yï?öÿì}xçºõûÍÖ¸{ˆ¼PhK]Ž›]ýÝÝÝÝÍ®ËñÓsO]¡(PÌŠ…@â®;²Ýeæû×¼Ï<ó¤y€Ò�À̺w�‰lÍÞ{Ö÷ÚzÏtЩ ]|˜ª¯«Ní}úùÌ3O=ш–c…Ö ffçFþÚßýW`èØj´*_-/°ßËb{Ò Ì!I{Úy˜Í‘»Þ�ΰ}ôÝ -õÐZ’ºf˜l‹&ÄjÍY¦éÔ+%1KiA—ybÞ+H¢Úê*jmÝHãcc46>A�P„O[[Zë©¡®‚–€#Ä¢N‰±mZy×û[ïîçAÅ];·eÁ¦Ca\>ó|ÄÿEe<¬‰4 inÙXOBˆtoiœ6#�|³o…ãY¤$½Püµ/¿À°XÈ©§ÉÑŽFŠôû��V ÜW@"EI³$ê&\�F=…ï5BNsMÏ,è"Ã)®ãh?�•Ôþ`…ë*YӰɶó‘�åzkïzØð)¥Dô=:õ¿óû'>8t¬Ú˜{Oƒ‹ Ý!`Œ�n’”#�·šÓnçÓÂ]€O 39ìôù#¼»}%Ã�R²�èá±1±î4‹ùËö³ÛV·+K�Ü9)Ä´»p5ÅÄxœ\¶ûûž‚ ï�ìØF óx% œú  �Í@dJ‰íS$™À›Œ£L½ëQ Ÿ ¯tõÐ/^W?¡qJb�¬ÍåÇ‚/BžÅ0…CAÞ‰�…­X™%�§µx¯ý]EiPoàN.¬uà9“ƒ~?¥baŽVš&Á €h„˜aCme=¼Þ9ªêöüÇÿþ[¢K+ixE�âú ZE(+pý��.0­S­}«0EÊ7z ‡“Á`”CIã럳µXEv½ÀÎ�!fQ\«ÁÃe[&¬¸¹»gÙ>tæýBC} µµmBÝ CŠ1}‰>x¨/ÃâlËMí\쎊Â<¯/ÍGÝäòÕNÞù̽Ž1QyE%ù‰ BD+eº/OÄ·4Táù Ó;ûÑðÈØz9…ó뢭µ‰šš[t+ÓlåÄó$j‚ˆ¡™ƒŠî¶ï" ÷ALZ¨qã&šÄhrÎÇ'íÕ‚0ìr¼Á MÎÓ08³$aw¹KJJÜX£ ¾ó�/ÑŸøÕï°y©c}Öõ÷äÐù_¿ó*Z}¥”ù†;ñ;àÛf«ï}7Ø@ÄÐPÉà«ë99W锟Éèíx:U Âä™÷#àèDˆ;Î .q÷½G¶'Ršn½øÀ�3íÌIPVAU”—Ñ7¾ò’€Áœ�Rh}ÃS BÄy»â§¢@|Œ@T_SΧú½�nbc¸½ƒ´ÿà1´üº1¾ÍŤÿî];©©±ž‹¯Ýýã<[²2‹yi´'ÈPÔØ7Š,ܽèW–—±ðoÛ¶…ê›!$~ZôGV­~�/ÞÃ1<µÀb’–‚ŠJË©©©�ž||ýú/“~õ—¾IØÉËéÝ,g°ƒhò¿üïßEóðœQŠˆQÈap¤û-$¹d¬‹Ô‰V¸4Ä„$§’Ī=¹V~Ý`p"‰&ø-åJ†žltO U£Àžk.©2ÒYYÝÁӈΫ/½ô¬¨®©Õ°Nö Mö/ðÎw|NËÕ»±¾’k ;¶4Ñ“»7‘M!ºp©ƒ&¦fÖOm@Jª­«A£À~�uöŽqd²²ô–$¦Æ›‰‹ï™e©2©­<Õe¥¸Xð·oÝD[¶n¦Šê9ë ò�¬¸ËúÁìb�ú}OÏûõ&™_TB[¶ï çž}оõõ/Ñ÷¾ýUzþ™½¼w]ÔDTUß?”ü�ï¿|æç¯¾3gÔÀ'ŒHäc³Í7 „Ä‚y M“T„'DѧÚuAY-@8xÊTŸ¤‡ãü¤ÎBþ½Þ…BæšXs¢:m3f-…CöÆÆFQYQ¡ŽNx8�Xˆ6à‘ñÙå'uNcb愇ùöìh!»¢‡Ñãt­³‡´Œº®öŒ766qC‡î;v¹s€Û>?÷Â+S 4nåf!Ñ2fSfÈŠÁ‘ó£;·s~ûŽ¢¸¼é§¥U•V¶®ŸôøhÂãåÝ(¨™&žzú©è÷¾÷ù­o|™þÊ_øôËßù*íܾ……,ëÁ1ijïè¦WÞúàÚþø•~£r <öÞÆþ¾ÕHìD ·M…ì]ã´ƒŽU+5 ubÚ�N;[Y`ÒôsÏøÊ¼gP\æ. .¼Š¬?�× ¸¥¹Áæt犌ªÉ+�ƒìCå DhvÞÓ‚5›J¢â7ƒºp¹ƒü�­'ÔÔÖ¢îSIR“„´ž¾ft…Q•q$¢aD(®f!ÞÂ]?–Ñfxª*+¨¦~²ÅEˆ(Bš^ð#ªIã ‘øæ·¿ø›ã/»å{ß_ÿò‹|°Â¦G>l¬¤S):}þ2ýô•w<¿ûý—{B¡°Ó˜V¿†È@¶ ‰ T@·ñ1Ù�ZÄj•4MšæjhËDh™C$‰OŒwÞ½%î팜ÔÈR@s³a¶ „:‡ÚГLk H×nŒ°9]ue ¯ç½IW?çh f‡MÐÈÈ„$¸žÒ[<§°kÇžªF[3;kš¶ò¦+†¹#žë%lÏb¥µVÉ¡¢¢¿}kUãZP\&Qͳ Þ²H·‡&¥¾÷„k,Xà$].wfÓ¦¶è_ûëYû›í/Tlnk±×ÔT¡£où tö×DNž¹H?zùÍÀ{|tmQŸª$:kD"�²QHT0 j`ަIÔÄiɼš¿}Y8É4 EasAtoÝÑ“,¥4[†ñøµ7c4×ÆÚ ;Ž|ŽFÖ‰Mÿ~[áê[RR,ҪЇAYLj«ÊØÏÁÍ–fAhŠõ9 6vL§z‘�O÷ëŠ�ÊÊõ“§�£¯‰Æï¢X²x° ¨ 0FL¹ íÀx!`ùÐS‡WYUm Â�œ˜ÓÅ�x>DJnŽà^Ç8Aýƒ¢)M:òK3­[wh»÷îÍ|ãÛßJý•¿ú—ÜßþæWK1ic›$)™ë|;±�^ys_ô̹Ksss†ü³’¥B’Á !( û1¥ÒìÉ´Z@z…ë#>Xïàb3A¯/Äé®Ï„i �¹7­¾€¹²Ô?ký@RmM%ïØÆÆ:¡ïƒÑÓ[“h nÙP}ÓhS(s%¥úb,®›¤ ü]7zñ<ùÖUT¢‘C/¸³€Ìò¶½­,&Fëo& ÆŒ×jÜ ,° )§žÛK�ïÞ)ݳÛѶ푴â.HL-†ÒSó~9Ц‰�q Lx¸�wj!¨¥wº¸¦YÛöèÛžÇ÷(_þâKޝ|ù¥Ü§÷î±#Y—‚‡Çè­÷¥Î^hoŸž™éá‚:hÖC²XHb`Ð�äˆQR8– o(ʓ髡²%öɈ¾Ÿ9в¨€wÕ0×R#ÀÜlh÷™5p=Aò4±þæd‰>”Èb°QÝMÅZSiCm9wr¥3i‰ˆDŽŒM’Ô´u“PìN^üó>Ýé˜íÄWI&$[§€q"™5¾Z¸K5O½~ñü³{éë_~ÁöKßûfîK_ü¢½mÛN-¿¢NîB™&›D�2SY·!³ç©geÓ¦mJAQ‘ ;yÄž];è‹/>Ã…t,h[·¿‡XïÏŽ.­óÆ@çèèX§‘Î:fzge¹�¤Á0…ªišžŸÄ6.'åê½ÁYL ¼ô?žà®Ïõ¬ñîus:ÝXíÊ“ëæ âº„¼° ‹q(M–ðÓ<Ò­‹þð­" û²’Btom¤¼a•¯6<:Î6ÝëAK3™ /¸Š'yVï äéfò.ÛÁ¹ƒ+ &�9“UX«ðn.k{dûfzí¹;wl±c¼kÓ–-ö—¾øúÓöÏз¾ómÛ·¾õ Û^zÞöµ¯¼hûõ_þ}ù Ïaw#[)Êú}ŸF"Q:ûɺØÞÝÕÞqý¬¦iíDt|僆÷Þk+�60�TSi¨ j$vŽV\'‰DÙË’™4§Ðî¼{K€´6u!X@L1qä›VâëX„ÃoÎîž^®‹„"º©á<Ò Å´ÜóL±eŠ”Äyøt ÀG“ÓÛÙDcn­p×O4©é‚Üe+—= 2£“LœÌfÂIV\§ËG›úæ¶V} Þpì-ú‚)EìÕ•ãvó ¡y’r]çö¼têìEyìÔ…ïî;x<÷Æó ­!ã1ð»š&‚(Ú“zD²ÊOVïr¤ƒIkþ¸Îå$ñÙ‘ˆY#ÁuÍR[|Òt.9­+ëÞy›á)ôŸvâñ × &gi×¶fîlZîÚj¬³“Ô$ª}=#ÊœgNõzývÔ[XÀ³üFÄ~léŒÔÓw\ ’«ÝŒA’¸‹+m¬Tv›;ú-¬N7W]mfMJÍõÙm,Žõ/ ‚ÓÅÓÓ:uîrêØÉó÷xäö¯Ñ'à(Ýg(+Lg@›äT—ÈhR-»rÕ»¢ÑT8Èi®´ü1¾÷òÊ5ždÍÈD<9èúÚjœðš)Ï�gÁÏm±¾À­Ó[¸³mÊ®­Í”ëv‰PÐ'± ZƒM÷º°àÀI–;}‚‘qÄ+ÄêÞtŒ=òKmTÀUîâ²Ò]ä×¢™²’’ù ‹ëõú£7ööÊ»‡_ëý·!"çˆè8Êu($ ºHJEÓ´�Oˆ5)2³hDb ‚·¨±Åú�h¯,//‡€DÉ3ï£_ˆ´Û¤.Ñ"Ì–)5˜; üÊÜœ'“H&×…pê³ ÕU•¤ò>ï ߌîrY-Äh &A¤¦‰Ó\jòöõ „à{Ÿ^süùkïÊÿù?˜ù½üüä±ã'�%‰‹F$2j ­W!Q@!AÔHÒ¼yPk&ŒFÞšOŒÓ³‹†�#ï•¡t*ÃWÀ4j\j¾V0‡Í8"±?(‹…¸èüÌ“�ÑW¾ø<åæP/låǧ湰NâÖ'Â’ÂTfgg“؉-ÊfhªJB³{'ƒÏ¥¦-óÉZ©˜¨|Ì+G"鱘h©å�³`Á‘D<�5 ×é�w&0±>xàÐÑ+ÓÓ3ŒAÃ>0 šXÏI TÓ(F“¥Ü6*Ö`èbÁ7²á‘Yš_ò¤¦InÕŒr´ò©›ÛÇ+ ?fÍ<Á¬ ñ@0a—‚^ý§è©'•:ÆÑ� GévÈÏÏáIxì-c£cv¸g„Y=Ìåõùyåp*™b{—®®“Ünj=ÇåäèH Aà#®ññ1±0^j…üºŒ'MüZ#¹ƒ:’Eƒ¯’;¹b`”XL¤jE&–€ð½laa‘=-ßÚwä“?üñíúˆˆ~¾öƒ1P>(B¢�ó Þþk�%’šþŒÆS¤ÉµêùWñ¦�Ó¢7D“S œæÂߘº–KZ}#í¥WI«£Â$Á×õ4iCd²yó& ìx8Ñë¿}$-l ×Hœv¥R)åÂÅËJ ""k ´ùDTÁµ7)%»Â¦Ò鵨4ê'"&JG˜ÆüÃkìh�S÷Ï<�»p…^ûƒô¡£'¯>rü$j!ˆèUð4è¥,†B+‡$îÐUí𵑸RÆè¦ZKD£qš_ òp¢‚ ½øG°Ð��ØÍ6àÕ…ÔÉuÓcËî¦R¢v�#ªªk5´ùr{/šn+ uUe¼ËDJ¢¡¡a1:šÅ›�ýÜzwê9iR>¨xœÞZS“î, º¨¤B7©©Xx ¥d«“ý�Ò½ö^ Æ‹ï;pä­@ pÕðÌòܶ ¾þ#†CÓ¤@4¢ñÍb¢I¹æÍR8!ó°\Eyß´Á¨Þqc¶þBáÍu»« sI•Æ©-6hTô i*Ѻq£h¨¯’˜rçåO·žR ômݸ�÷ñx\LLLèÊžµBér:)/7—'ÜI�zw©…Ìîk{_g1IÙØ‘#NsiCšË‚|øí¦ã§ÎéC†êåöëOž¹p  ­½´@+Àz«‘L€!HBGt1á5¹úu­��L  ð¦Y�—S]F˜¸6ш™Ú²ƒ⢩b Ëx®¤¾ÞÖÜP/ór\Ô?2u[ãL»ÃNÛ75�é§|êíí¡D"ž­)n¯ª,ãô(Ä„7rNÏùØBÿžÎ H�(1O2XRœ—7é³p'ФÌú õd"Éνˆ>ô…T6^ƒ¨ü®”ò}s�È:‚í.§â÷Âý·:Çí(r9íJn øXL[ëœ"Ýëë*Hͨ¼Ž·¶ºES»Ù*ltn�«™hDöœOùl=ÈÀr‘ˆ…åÜܬ€ˆ°A#ö—ÜRx°„Œ×ôöOS&“†Òv*«¨ÈÎá0cøõêìîåÀòÒBÚÖÖÀC–f³†¸7b"Ô¤ÑÄaHÆüIrŽÒÖ�bÖF"i•ë!‡��¢žþ¡ÄàÐØ¹¾Áá߈D£�ZˆFë ­óà$Ô?æ°ÙÒÒ$[À¯yNQÕt›ù-ú"ÜÍñàÓðÒÝÅétŠ$ÿŸ Õ‚¹6WMi.=ð 5µ´Pey‰Ô£>´ß:Ú“’…äÑí-\+Ñ‹í££#ºògñNp—^W56lœ÷ÙÈE÷{näÌ]\€ÏÛ�hEK­ÿB¼%"†SG”::»éR{gª£³wøâå믷_ëúOÁ`胕ٿ¯ÿ9’48¯©Z £i1Ep§(ïY§ÃèØ,·äç²W’±Y‘¿gp•sÍÊò=$C5�ò K…Þ½ è>�ˆÈ[Ñò‰~ÏŽVD§NòÌNCô¹æ�¥ nD³U¼“9kns^ô‡Ø´ò~@¨ ’ iô(Ó jE#@Q}br†9‰zÈå <³œ>{á7'&§þÒª§‰(É™“‡KHL¿­�$ ¢.²˜Êd¤n'¯ª’î°§„‡‹‹óM«y"i˜6jkP(ÕL yNo)Nz(`sŠ–Ö6ÙP[!õM‚×oŒ©j·üÝ–—Ѧ–:¤8Ýäó.ÐäÄx¶Ö޹õwÏ®mT‡TD�—©M£ææ ܧÖe-E"Â5 ¦@6{\ç‰%"cãStôÄYºpùzàè‰soúèØ~´÷0fCTãpü° CÓ Åâ©p0OúõÝ!ÉÔ=½g [‹ý·<}»�¹Š—£#A‚´ÕŒ’¤4rØÂ´ ( 喉훛&×é|{/ÍÌùÌßÃ2ðêÚ��µÜš=1é¡Þîk(.fgT"„À–:zì‘6**Èãç :ïS]Gi �}¹ˆ¡¸ÈÂ:… ‚‰)�¿t•nôÏžþ¤ýí#ÇN'¢Sè0§ex˜…D¢f¡`˜+ˆˆ„÷#ß#ee‹”E_˜=¸±±ç—f0>^¥ââÓs$¶œ‡,Õ ‰ì.QSS#·´Öó¼ÅelO”Ú­£¾–†j^ûà Òuä„'Ɔ)[QQYEÍMõ’\®¿a· ×I�êº#÷<¨6WõZX§› cqºz½'sþÒõ®‡>þÙåËWÑ9Ü«ÆUNßXB’ƒ†˜¨°H‰BL2þP”7&Š{T' # ói—\N‡i™b·³¥4§¾îæ+aÛ¥ö𥄃\…‡¦:ª*/ÆéjŒ¦f—ì}X¾«Ä�SþFŠà¶á'OÑüÜ\6F%¼n·ºº†;¶„\po‡P¯«û"Ü 5E<´H’ia}ÁçóÓ�CÇÕïÿäÕüêI éö葈bB3�-!‘à8¢34“‚Ž`3n’üaþ&Ý31á}î<™2œ�! Fû/G&«Ó±%\]f¯ÿCG�(«¨B¤QM~øÙËúœHòfzʲ¹¥žéD¨ýZ7]»v]KfŸ½<Ú+ªê¨¬´˜ìv…g’®v£…yêþµ-›s#’HM¯³ÝV$27¿H?{åmõ7ÿ§7>8x¤õ�"}¦-¶%$&Ài0)%©¡hB,35ïgÇ{¹{{jÆKãó”æýñ*o½3S]«³·_±sjë¡„°QNQ5µ TRT@í]CÔÙ;zKg�Òâ|Noéu•E_�:®]—£c“Y¹eÏ�[@W¡��7F'çèÒµx½/�™+�µ”ùµì�N,ñz}ôÑÇg´ý�w_¸Ô~FÓ´Kz$Þ¸ùŒˆ%$p’¯l[”‘‘x2á F¹è~Ë{¸«^0õùC482KÝý3ôÑñëtéj?ßð½Õ³£àîæÔÖéG%ÔÐØL»w´pQýÔÅn®'Ü$ÅÅûõë+О�Û‡†GDg× ‰©÷¬Ê E¡²²Rª«¯§Â‚|RÁÉ—¯ÐØôüýMÇ™{K2`:‹mç-À¬«.ÆhßÁctòÜ•žk�7>”R'"�E$B7…%$)°¼&A'Ns1D&ªžÞRµ[®­I�ÒãñÒ•+7hnvŽæ= \ŒGÊkõlÀI3‹ì|}(¡PAI <µšhcS-» à=CÉ›�Ü… ¦ú*sJÜ3ïýý¸9�OfÝ¡º¤¸�êëj©¼¬ØœICTråú o‡¼/ÐÒÆ6EI”‰páŸg_mÎßž8ý 8|ÅõÞÞ= {‰è<8Þ&´µ„D‚“à˜úd»JÒ;:K‹þð½¶cæ©v¤*ø4¬IÍ\½{w0öxk³VòЂ£’\*)« šŠ®…\êè§¡ñY2ÍæUREYí}t3••P0 ósrlbŠPP˪ÁÄD" õh$¨¬äÙEÂz®“„#÷©èn¤²x¦$“ ’2›©Y‚ï/³žy:}î"ÝYWÞx{ÿþùùùËDtô€*H–�Ü0 ¦Œ�ÝHqiˆH2c³Þ¥&Ž\B¬õ\ÝÝ.×êî#Ü­Å4]€V–ÕRMU9•—’7æZÉMn¸lŒ¸©¹–j«ÊôT#o$ÄÊP Û ³ê†èt:ië–6Ú¾m3åæº Í#üïí𤱩ùû›ÚҸ˞¯¦³‚…û •›|Bt­³‡ÞÙw˜.\éò_ºÚ}èÃCGÞY\\Ôä08Ew KH ŒÊë�R Ì“„'ç|™Å@Ä�.—ãv%“UE|3À^’þÄP\E´q#n¼›©¾ºŒ&ghxb–M4—ÕJXDZªù¹ƒbÞ3«ùü�¬³Ki¨¯¥Ÿ†vmßDy9nv‘�œY ë=#ÜÐqߊî¦Å<®Šý>Û-Äã ºŠ.Ä7ß9@¯¿s@=~æb÷þ�ýpßß�—Ö)c•Ÿ>,!QÁ>°×Ú‰$¡˜1•v‡ 9è|2óø c?S4˜$Ì–]Xz³Ü ¤&y¦?ge�[B€ŠI~óJ $KD–ÌÖ”×SC] U–óÅ«]ÃäYðÓò_OfJ66×rÊhvÞ/"᠆¤JRfÛþÚ±}+}ï›_¦Í­uT�—ÑŒôVüþEP,$)0Þ¯Ös RCa:~ú<½»ÿ0=y.qâÌÅö#ÇN¾£§ç€”ò ]cô�Á¶Z" Vƒ»ÁRP€M“ÜÑêBSZ’Ï))¬vÅU€Ï—ê‚pQNŽtqTrw n4"a®Òå7¯¹‡Ä‚‰ÜâÚ¶u+µl¨âhrpt†FôÂû§‡´¹së¹½Ûõâ;3%Nœ8¥LŒeÝÍ�­ð ‹iûÖMœ²ÃA‡'Ý/\í£Pø~¶--èj‚2s/fJ,Á"rûC~þê;¼÷Fï�­½ÇÆ'&_'¢_€À8Ý5,!ÑÀ.p?ø18*š&�¨•P(– üü~SÂ:ƒ#��­µ¤ŸJëpÊkl¬¢† •TQQŒïåQĦ0?—ˆ„¡Üi׆Yðç‹øïÂçwa‰bç7._…Ÿ²HaZ0'þkÛhs[?gÁp”ú� ZX ÐRà ÀÛËJ 9M482EÝ]×(�LdáŽ7mhÚH•¥dSn"èšâÿ.5£Ý¿Z‰T¹NgÚôh™µÛœh�·�z<ótÖïï¼�ºzúIJŠÏÍÏôz½?!¢}à˜É’ÕA<»Á(hG'0ùC1ÔIì|3AÄ¡Ÿ\Y,ÊË Q3)àˆdC]…¾ó›#–êªÊ…à Æ�s9¹hÒm½³¸+ŒoZù¼£ÄØ�¸Â¶KAds© #¥•On»»�64´ðŠ]ìîç ‰ƒc3K­üùy¨«.åçZÁ�¹ÞÕG~ŸéוUŠÕuT[ß$ÑÍ%õôÛ”g‘:º‡Ùæ¾AS ñÕ8/¿â¯YX}³Åx˧®Ñoüî�éû?}•º{Œ´¹lG�äçDtœ[ý�Ð Î�— ö�I¬ÄÍô�ÎQxOëц¢€B8G(î' ð¹T„¯— ­åv9¨âAáièššR*Ãçúc… NYå‚·+²çååðãîÚº›'ÖÃ=ü ›û¦ÑˆAUõÍÔÖÚ¨{Uñîó‰™Þ}¾T$Za-÷`vÙ †¢402I‹‹Þ¬¬ª–––Ðæ-[ÑLPmNºëŠ=ƒ¼®`E�ŸÍÛC²€pz+Å5±mè–ˆ„ô‚úÉóôò+ïБã§y!•ËåԽخNLNÿŸh4v^$ k"$¨‚ àECHB 60:?y±Ç‹ÓªÄMž÷T`{„Ä5‡éâ®®bD(x ww5!ï^YQ !Ée©¯­àR)å-—þ#�Íößì›D$VRÔT@â\´é¯%ÕÛ ƒY°»òiëö�ÔÖ\Ç=ö ‹Aö«Ê¤3Ä�ćƒ­7àšËs³s>›˜f;úlƒËéD�¤MÔV— ›Ù Áó2#ãš_ ’\�xÜáãožnO,ñⲓ…Õ[@Ç^žPëýƒ¼W}ÑëçL‡ªª7†G&þÕèøä!‡Ã[³y8KHLh`?xìHq(Ov¤º§bNnïU–Î|p‡Ò],*N‡�¯ÕU¥¨™qÄ’£^�褴€®›ÜÎÀql|Fÿ;X°H®`ð�@.ª/]«kûŒAD Åu´c©y*Óî™àç¤iC%1"]Ä.»Ó.fY—Þ*).¦–æ®ëPû™ãÖfâ·”re‚ >›·ÿ™Òˆ–…I «cüÚÓ7H¯½µŸ>€WVWwaÖI?P¨ŽKØpø¯±[ý¸¢ˆÔ=Ùfh # ^ß3®a}£ÞÛ‡.„1o ÝLÍ�ú‹ Ä„O¯FÄÂÑIC}%G*ÜÕ…ïáë†Ù8]¶ÜÍO>ß VS,›‘à7 œ An�šRcËfÌ`làB%ÚgÙŽ=dN¼'ÖT–òÁ!…ÇÀ6…#˜lìÞ***¢§žzŠšëyЈ\÷Á>ŽPnŽˆ±«…£—H,N EÜýKG,©}:‚iaeÖJ�އ?¦®ýÔ;0D¡p÷!Çb<‘x`hô?ÂÖç(:BÓš& í¾dáÓ°ÑÚ!zÁ XV�O-¨x“Ù¶mÚàÒ ³òæ'æB*c �S!ft!§¬•°` U˜¿œ\b[_VV¢î}b».6beòê0Þ¨NÐrÇÖ¤,8\nr‰$M!Ò@} b‘æ¤íß$yèSð‹<äW\˜/¿òÅÔ-›Ú]X² ŠM@LŠ!3ÚåŽëº½¼Ð7OîÞ̩ӛ¯Cc~T¯©°ˆ†£1Æœ^»·L«WÐh{Š%4W° 39‹Ëäx÷E$%MÓøç+ …;{ó �ŒólÈåöN¥`0Lš¦Mã÷ú&¢’7à w÷šðýñ‡³„D‚!pŒ€-ªª•ŽM-ˆtFÍii¨T09|;Ï,3Bq8íú•#þ¨#/×Å­Ä©û"ñ÷ôÔYyyiæ‰Ý[ȦeÅK¬ØEÁÕm#²¨ÜþGZ6D�9”N„hƳH ]àQs}5¢=•ÅEk@Ûóè#™¶�íÔ$²6»�±�h¿zMÌ/øÆ³cK×ïn–Ê £¤?nÄša“�‘ix‘�é+|YhÁé\N—MN/ –4Ïß™ðp]iÚã…øÄy(7MàçùÙäR�ѯ¿6]E<ëóèüÅ«ÔÙÕK89Ó‚×G9.¹ÁÏö´j"}#tèÈqth]§¾þaÔíæõÃOò ÖüÈHχnŸÊ´`¿;CÃàAÐþ­`(Ööáñö :_lüÛÏè†~Üé#å-}³@¿ñ¤&Éaç��ˆ…·ÙñìI(Ó'æY€œ9yiä6ÖcKqs# Ù!"w8ŒhÁž[J­›¶"µå¡^ÆÜB€< >jn¨!€;·vmmÖÅR¯hN—K’”‚²[‘ªÛ�˜ñ©yˆCˆF'æèÙÇ3ü:$I&¤aS‹"0Å^Ñx’ÿ –{éSó\ók¨­à×n('"ÉôHõqT�+§Ðà˜ÌÇðç‘ä=ø;¶¶RUÍræÙdZõ‰p$Ê�oã“ÓìÄ<42Fƒ”�­M´uÓFªª,gw†KÖ8X"Ò?8ÃŃtéÊ5.ª&¯ñE¯¯‡�}DÔ““ê–�˜H�‚nUÓþ,Þ<;.t Vb“¡øÎ—ÛtûÝB§"¸6‚éh.€&“„ô1ŽB¼¾°>sÂmš¾Å…œP("JK ïrÿˆÍHk}¾¶_ UÖ4ÒÖ¶fšž]äaÏà$ß qSãy¢Ö¦½/fgfSȦزÕ5¿¤´„‡g§¯² Ìàè4¿Î0¯Ä0û1@î@4êzÕ•%têB�cÓ¢7"_ ÂL$S<)�4?¯}%¤ÎÈÈká"õâ¾DÔ’@¿JÃ�¼Ü\Ûöm³5N—Û‰F…)ŠÂ •|þ ë„Ûåfñ@qiš1Ú¶y#7 ”—•êu>€¹s–šJùðY¿«*»ö¾úæ>êî éõ5ª�a¯ÈDä§\ÛµDä>¦¶n/&# ]Ód-"�„í.¼O¶óTZœÇfŽwè©ÅoBvÆU—X,‰Þÿn;•š*Pœ¥¥E+‹Hc‚ݞ˩-Ð*d~îúž»¤ÉÉ ½Õ—Ó�¹9nHD;-Ï‘ �£×Ô;w‰¢¢e'8E .`ïJ/Oå£U‘tÕÕ”™"²´Î‘áVaŸ?Ì–1˜«ÑS||? Çð ÄU "ÉhR¦e‡HÒ" 1�×öF½He]EŠëÜô¬÷Dtggç}ב.[€�©°//˜š™uNMÏ ½>‚ŸÉµ(Ì7p ¢ÂʬgŽFÆ&éÒ^½ýCà "£I ‡#¼§σÙMgtª™¯óŒªr”,ûcݾ¼z{ï©OèUtg]ïîåÁC`zaÁ{txtü4Dù}":’•–'VDb" ¾�7P•¼×ÖÞ¡©Rtrqkå‹OmÇÐZ5§¯>+' !â®.§Ñý³eS=i’hjj� qâ�o* ­ -œÒ2:¶¤‘¬d“bi555ÔAÜ}ÆtïpG­K¯1°#pgß„½³³;³¡¶Ê™µ½ùŠi¹zNý˜_«cèH{lçFÓ�rÑâ×Û̼�'à‘ãú†$©BÑ)_ üœ‘O ~R�î¯zÜÄ ‰È…?ïG!Wõiš6‰ú¡oóF“J t€*(ТÚ:84ÒT\\´ E�#º¯ÀŸ/Ë¥$Jé!~¡°ç9ìŽTZs¡ó(7‹+˜Ä&)%G!ùy¹TQ^J�ê©®¶J�Tx½°S¸+Ñ-Š8Šg1J$,¢.—ndY‚ƒZ1_òøg¡¸Ï¾vËj[ÙáAéTŠEõÊÕNºx¥ƒ<¨…hª–€yì väüd~ÁÛADƒà"˜& Ÿ ‚îÀ?‡!Ã/U•mÏu;ËqÊ-ðÜúâÓÛiï®�œ¾"’7{].¿«s>YÓ4¶ç˜š^àÔÕ”S%Büâb}fÁ~Gí¾æA1üµl.\‹¬û]øC ÷^¡þÞ.ã&I»··Âs«Eé>uíÁCZ͆M©¿üÿ´7´¬}—D|3ôÿL¯½œ¿ð«ß|–~í[ÏêÅuÞGß72¥§ñXh 2„¾_t¼Ð�¾áÉ£”}Í05-PkÀ\0Žë׺ê2.¶ßjH2™JÙ q)3X‹èb7 *GG¤†?4ÆîÄלEE…eBˆ&ATЍ#ÂãÆ€¨ï ;„EƒwívNÓ•–”˜Úhqå6fˆS‚¯[6µRMU% �²¨T”•±À eÉ?'WD/Y%,~ˆâ‰3ŸÐÙO.jJzDA„6>=ãéA*ë î‘wÝ¥²,!aÔ€ßÅ È×K‹òw—åÕ+B±AX�[¯§ön¥G·5Äfyt"oöoÇcXPæ³ À*…_è……ävç|V»¯yã3´«„È â{V~ôÍÏÒ™“GÈçóqª¤u¬/<³“ÐþÍ3&ç.÷P N±?õ§þTÎÆ–FdíÀÚG‡Ë?üÑÏ Â ¶¡øþüÞmÜâEŒ™‘„þú ¡ûªé­¸/@H¦�my·STVjãk‡DTlˆJXºÁ$˜2„«Ó„ÈeIªËËË­ÀÍ¿¤²¢ÜY˜Ÿ—‘Dª"P]Y®Á/qÑ_„ù 'DÅARÜn7§’!dºð° �$~!:bA‚�p­{,4uµÕ!ãÔ3þîûÖÚ;>9ƒ®¬X…{IŸZOy}�‰¹…Å�±·]JÙID •ÆZÇBÂ(Wå‰ÚÊ’/”—<�´W±"Uà†³{G ½ôÔ6ÚÞ¶á¶Ö(Ëä¼6^ôºÝJñp¢·vûM“ =qWÜ]§–ÎÝ_¿z™®\> Û÷m~<ž¹…¾@ x‘ÙQ"êÅ}'¨q!ÈÂ:†ÖƒÏÀ°ñ¹Š’ÂçmveòÎn„蘄.¡gÛL/>¹�šê+ùf¤j’gIn_�wà´ÄN³¡Û.·bá]#ZŠ£W}Yµ‘U0Á †èèáýä™™ÒO¨l{óõãÚÖá“WhéȲŠ*zî…/Ñ–Í9E’m­¢0îãYƒ÷öP/]éÐEˆêÊÒÜ<ûщåCk¯é«€Ê7&Š€×ÀKY¸-OÝ ¦ Á  ,·�;Ðв¡Vˆ^Šð~¬%¢Dg…xŽò‘ÆÊÑ !8:AÄ¢ ŠÙš�(…£®¯”AT°:mÉ�lßÌó9 )We2]u †BH1ΡÒEŸÂé­Ë×x?É¢?B_ûÚ7ä¾ð¢ÈË.1a`i·Œþ·ÿû£û>8½ãvbÂ_/))Å 8ïöi-ŽD4s¥." «ê�HCCÚ¹SGe8²!7ÏÝ[±[ÿìåtc`œz&èÑ];2úÏü¥²¢R1¼ÖÈ*1yýÝCSóüËöx,!¢cÄbÂPÁ _Ð –¢â“`UQaAKcC][Qaá#þ@ ƒÃz×X& à€—[‚Î1JNue…“Ž�;¶ØÙ¾ÅâÈÏåtQ:“Ö-`øwŽ&Š rí¡Ñ± ŽDÐÌÀõϼ^Ÿ‘ˆr"“S3ÓHCÞ@aÝ+‰f�F‡k`‚,<\BÂ0¦„woo¦óíýØElDg×/ákß…x;1`×4Í�çQßãÎÝ[=ýãÔ;4IU%òWõWäæ­;‡±r@‘Uõ’ÞÁñ…?ñþÎ�®®n7Ý�‚æü�hà&<‡�K)›Àr£FSæá=éB£At´êêŠøŽ­›ó7`¹?ÚÀ Q¤Ï…€äÃu× ÑP�Éd’-g 4:Sèsöàg�ãu5o¬^¤Ï¼×»zc¨áDŒÖ(訷ZBb‚-Ç·´ÔRWÿ„)‘­h þujµ¸0·á+Ïítþò×ö*H…ÑÍPaW´0ÞúW`ÏÃEpDÂBâȳŠìkäÉ3—Οˆ'"þˆ íÙ±‘Ÿß«]CÔê[_|ö ùõo}W”—W˜VÙ D¯¾}°ý_üÛÿÒ‰D‚Æ È°×“åó½,*9`X*` Øf\] 3`>èRØñ;/�ôW£ó8•ˆ>܈>ô®²¸@DÝš¦~¬wbaàU�È’š¯°ö¨[Br»…‡O]ì©DÝäocå[Í*Ë~é«{K¾úüN íºmEÆ",)¥iÿPZZÆmÀ·’\£Fâ$r•[VñkÜÛþ“OÒ=×.Úˆ¤RWUFOíÙLW:YHt·\öâúö7¾B»ŸxiÉ|â]üBdÕCç��ôÿûÝŸœþÅko QhI—Öè-OÀ¨€0Ï`X6‚“kˆ‹`Úø¼–vƒCà˜ÍC¤KHî»¶6ÒõÞñ&"ú“ŠO¢E¸ê¥§¶×lÙXW[WUêØÖVÏû/lFZBÂw)åM ¸¼—ÝJ�È]µÆBbµ{f=túøa9;;- þôÍ/<Î-ÀçP+›¥hĤâù½[��íhæÅW…fÁ sYç–ÑîkãivKHî�…wß tq“±hT¼ðÔ#„Ô8;ÓÁõ’�ÑiöµúÊKOÓÏ|‘ŠŠK9*ɲIwv×}å­ƒ¿÷ý—Oþ!#ß^“–�ÜeRÌ‚%$÷ ¸ ÜîŸ�ácцÚò\D-ÍÏ>¾Å©3¶ 3¨¸¨ˆ„PnžsWœ‘p­ÄQ°ö…v ìºzêäI­³ã²€ð‹ï}õ)Ýz\=X5Ê9»Q?ùÊ×¾I7o#wöE%<í~æüeùÁáSC‡�žx|| �� ´iËvÙ=0¥öOQ¥±ýR�LtÌ 0?ÐßË“ñ˜e»¯/1©©ÆU#MË�”ÁÔ+m[°ß£¦äÉ'vWƒ…¤‰,X°„d]"N�@Þt¦iÒ3·t e8¹ÿÈ¥Äoüè}:q¾“°¾tÉkNiÝûÚˆî¦{úé'áÌK_¾6 !5É[‹ òôº uwß ááQ^p?á%ij†H’qÕ¨¡¾†žúqñgþįԡ©cÁ’- ›,X°L×mÝ$úÀ9pT�c¯Xðí0ÝSõõ§ØÏ»O0wbè‰4# ÷BðFÌù¿èëBDR lmÝÀ»=�žä…X�P˜E¥¹©™òòsõ¶ns·¹ óó;"ŒÏáç%X84Må+›ÇòœÙìö‰ª¯¯ÍOe”èÙsŸl0ZY/cÖ)¨ªY°`E$ë32 €=àào‚?ƒmÏäÌbìÌ¥‰ßúñ¾ä¿üï?“?ç8-,,e"Djì>tZ&\°È íÛ6Ûr Jåð¸G…·=Š];·6c%o%/Äèï—íW¯jº9Û†«iʤbd€SMj&Å?�É�¤é(R²üx ?`q¸)¤üôÊÖD„ÔtœRñÿ�™T\ÿy,Jv… ÚZê¾QQ^ÞFD[0»-™ô‰?ù½çÉ‚KHÖ'$gÀ«àOÀß’RÄö:/vn'Q€�ý¯?x[û÷ÿï:sá:Å#!²pïÏqÚŠ=$�?¶Gd”¼Ò[j[Sv–ìaSG]X°ÕNt´_×; öå¼>9•ˆ˜B@$ø¦žJFøÆâói™äÒÅš,,,8€Plf½, ðŸ'þœÅ†4<Žk!úŸI'(�Ÿ­âç¥Q<>ÈTÓIþ>þ- »Š»¬´¸�ˆ*`åó…M-[Jô®Auì]=÷}¦ –�¬?¤Á(8ÿ| é†�@(Ÿš�8vIýwÿçôÿ8ù¾w�;¸¬¨[øô}à6w~©k`|1¦[‚ëûË7·ÔQMe)©ª¤ùù1ÔßC“ÓDBð <õë7{SPeÒ ®]¤âa£¦¡òcñ?,R˰X(#£€nŠ ~–ùøt*ÆBá�P„)^Ä×õŸä¿#ñâã3‰¯%£*A·Ùö­›´'HRÖî6� R[É‚KHÖ5L»x|ü©”ò¢“¹yo ÔÝ?žøù[G´ÿô¿~‡Þx÷Ÿ¤t&soůg­«©Ò'ØsÏtŒ‹Ó—úz�¤©¾ŠÛ�±äŒ|!šžš¤ë�� 8jH!*àH$�HD9Ý…ÏñqŒ?',Sð1˜1 èj:Å¢‚?Ç��Hð÷!D,&)Ž>áààǨx,Ç/‹W*ÎBƒaXúµï~)ÑÚT7£o³Å&ÏGaJëæyY ,!Y÷�c`/øøCðU¬õ=ëFF1gâ=ûÉÕðï~ÿåäÿû�iïí?Ì‚¢ªê=+*).)"ì§ _ š{±{B=ßÞ«ak&Ò[5Ôh¬YžÅàâÉ“§©««K ¾©'Â^Ê$£ÆM>Í7uN;©)|ž Ì’›¾–áè…Bª\WÁÇQþZ&�äèQ Èé+ý ÆøgClX\´ ?Ö¨±°8áï×,J•%9´£)ÏñÄÎip¯Ý§ê|!!cG,X][4CP&Áà˜”2‚$�%GSS3³Ôu£ßŽ6`ç4Oc¯� XP8]¥vl‹ùÅ -‹ivÙZkÈCDü¤ Ö½’Ó&´��åd· ‘É$õ‹…¢Ø¹ŽÁEpî°J³h,1\0�²àH©¢�€p�lv'ÿ,|"•v‚ÿŒƒëÑhªQ`Ï‘`Á2 ùš­„]3s¾¹Ž#ÈÐ¥]ÉTúÍÿöOP`²�~ W ,!yp"Õ”)CPú5M›ÂŽê(|”Ò¸aÙ°q-ÓÛ?dëîé·ã¦ PH¥·›S1$ÈÂ*£¸¸�wÈ`Ï„Š&¤ÏçS±Ë݆z î~vöAT°+\ÝÜ\)‹òœ ćÂÁB‘ŠGHŸë‚‚%°ÜmE<�îà(CͰxPZ†4GfËH� òç(HðÇøÙüÜkR3ìË%‘$~I�áÉ0V»§±éSÂ7l¦¥¡ªÿü£*ÿÁ_þŽ%$`!±%΢â� Œ ]ÎÑ(njö‘±IÇÕëÝΫ׺nbúâÂv7BœEÅÂêup••³ øü!KdÒ7zlÌ™àù D2MHCD]Á�ÒP[Š?£d„°iRË(Ü‘%U’†HÉ"ÃâÁ…u!8•Å�R îÄâÇñUU¹µX…PøjÎ�€Ô¤*IÒ§ŠóçÒ¬½ 6R‚VóxG÷hX“2Å:ˆ(i‰ˆ…^H,˜5”CT†¥”“é 2àñ„. ŽYÏ|εÎ^i/1=ë!Þæ–—Ç;3 (Vêk•6bbÕ*¡¾ °ýNŸ˜ÒæqŠà_o$šà— 5£vR\˜“‚ˆD ,ßàùC îÚbA0‡¹˜ž"Š1’æ”þÇôцL@T"'©Úð˜?PJžˆ\æ Í=ÇøÁ œ15‰æwïДïQ#âõ’ ��Xb’6% zÀ>)å0vHG’‰¤ ‡…g~Á12:aïC#c´¸èç¢<Ú>95sW‚b� KÑŒ�PŒOΪ>¯W�HD"‘âh#�s›vuE1!m¤!Rá¾§‘N)H‘[›©™mÀ<Ô( ‹aDx _Ö0aüÈÙùGCY¶74TŠø{B=‰^+qbi׌”4JZ°ðÐ ‰%(0 †ÀipXÕ´¾T:½ß§ ÃéT±à¨hlbÊÞÞÑMW®vÒÈØE¢1*Dê+¢‚£)YXðûå©w˜¥)~¿OF#E¯�E�ËéàWa~ƒ¦Èõ;¿MV� I™�d$ñÇ®*qÀ ȹ)©“@Ž_LaQ0Õ„X@Hm…ÉOC,¥�£'AcS α©ù¤äÆŒ%*Y°ðÐ ‰Í`ôƒ3`§¦iíˆLÆTU‹á¤\ˆiëü©éY[WO? ÊÕk7hÁë#ÜPŒ(ÅñùS_XŒs�â¨�ôû||·¤¶¸Ã …w¶rßÜR«¯Vv‘N;(t Á¥>)šYhYh–=ß b¾OŒHãv á–ý;ÔvlãS óˆNFˆhL� ¥�X�U2"‡ÝîA*kÅø¡`0´ˆ.Ò4rQÆ6ŠÈDt|ÑÈè8-,úù'äå¸YTH¦…ÛC׃ŠòRD&… ºèT¿Ï+‰˜9rá3@™ZlXÜØT-ôÆ@€vƒNÐ.LWN3ųLÔ%ŸËeÛ@yÑtJ)M±1 î„Tœ˜öxGg{�vs?hÁYBb�sì@ «{g!(½è"ê‰'~\í»C+))vA. @“3Ô?8Ì¢ që*Åã ²Ûld³ƒ·ëú²À •eŠ$tp©�@P�~cÈO¢°-ÚškEyIáòè`©°ØŒ(Å I2aFòæbb~Ï£eÌðÕˆ†–Š RnO¤dWÿDæ„ )²`á6°v¶[(Ÿ {Š‹‹škkªvÖVWnEj« ãщ„›b9oÕknÜ@ÕU´ic UU–“Ùù%%YX!Èã™§WßÚ§½·ïCuffÖ.‰„^ö¨¯)¥?ñÝçÄ×^Ü}'¢¬ !4\Õe‚¢˜Ñ “±<’Q�ïiM¡1¢;-C(£}]ʼ²ïô��‘™ïÑP# n+"±�BŒÉë‰D¢­«W2š6VWW­©©,ÄÜIÞ<\lgpCÄ¢&t}ñ`:¹Hït88RQeYMÅB^^.m¨«áÔÖä´G“jF¸œvÒ…A67³Sð�ž�BZ–¾R¤”f=d)Sˆ–Ö\ðx§Y�_fT�F&æ”ɯkÊã'¢n0I,|,!±�Á1ÔO:±°éò‚70&„â+**Ò ˆPœ^¿ŸF'¦hl|Šzû†hph”Æñ¹?"LÖS&�!ÔbÈq±ô„ïæ<ºmËF‘““+`c#Ó©”€ /À*-Î uä€w*(¦H°ˆÜ¦ 0»ùŒÇ»>ë=$„à‰ü¡±YZ�˜rïㆠ>¬Ô–uó3Ò…`MnnÞ6ج|±¬¬´ 7ÅæT*U‡áù¤½xþJÔ°¡Û7 õUÏïÙµƒÊËJ¸CÉ E½}xøczû½²`P&âq¹cKƒí/í¡�[©¼´�ÄçS_³&r©±¤!:Nº ÐN¯¼*uèdÇùoðM"ú9% >¬ˆÄ‚4Ò^Þt:5 …§9McÓ‰D ³*l‰º Yé.‹îÆ–&*).¡pLbz< qIIŒ7…"åç¹ÙÂf•UfñþNÿŒÛå D#JÿðtØŒŒ)w\-XX9¬ˆÄ‚�[Á6°"R�B|Saaa†ðJ„5ˆV HJGee}éÅgéÅ瞤šêJD'¥TX�ÏÅy”êWo"‘¤Ëí×éèñ³NÍé¢\EÉqhJ~®SŸ/Q¶´Ö³ ÜO`x•ö½LûŽ\š¸Ð1pTJù*�\­¢» –�Xa>X æW‘­¨�ìBkq#º�]ng%‡sÑá¥è§ð&¤¼šÁÖæFÚóèÞ.ø0§¹ÐØ@/wè¶ÿÜVM¤e‚Á@XÉD[[«ruAÉÏËá¹q¢9)%§·þèÝ“Áw_øíÀº�|fÈ‚…Õƒ%$ÌSXn+Ámˆ:žF7W=Šîn·3ÝK,€ze÷®íôÌ“�ÑŽm›ÙNä¡HOÿ õô æx¨  _ëºÑ—އ}䶧쥅9¶Æº Þ`èF$wôdiÍ…M&_Ûw&þƒ×ŽüÑ‚7´ŸˆŽƒq²`a `ÕH,h`ÁëRÊ)ÔNR]>¤r2hlÌx½~›¾·[…×ççY”¢â¢‡²†‚è�±A(jŠzÄ&¹Ùƒ‘¤Ýã�¢kjFú! G£‘ » 8ì¶{vPÓ¤¤`8&`—2„6`?�ƒ²`a�a ‰%(I0¢�RÚUU#,ߊAL’°WOOÏÌeFƦ4Ïü¢RTP èû<¦ ó¡é�ËÉÍaAEG_ÑõVVV"Ò*‰ETbAVfÑççYØÕstaYóh_� yoPÁLIt`tÆ|>É‚…{KH,Qñ‚³†¸¨ ô¢£+ŠÎ®4:ÀbC£áŽÎ^1:>a×TM!’\?Àc8Oïp°¸< ‚b6!äbˆ±®¦Š]Ü99"M‹Dš¢˜ïIÍÍûœñx’Åår¬iã‚D¾@„|aûàèÌf_ú‰hŽ,X¸°„ÄZº5ep 'Á””R‰Åâ419¥]é袧?‘.]Ózû‡µáÑ áñ,ˆ´.(šF99nR8­óðD*ùèrkmj Mm-¢ªªÚasæÅ‚^h9pæÅ„¼“Š sIb5VØnöo@m„¦f½9Ho¥�Q/(É‚…ûKH¬©ù9p§ Q¹&À2D ù8q ¸§:oôÅ®wõú»zBúšàÎî>SóŠËá˜Mám„$ÄCcþ˜�”W]m•ÀÆœDF±y}¾ô‚W™_ (˜EÑ×öR®ÛÉu–"Úé&G„´–‚™’Øübp‚ˆº²Í.Å‚«kË:T�ߟ7ÌÆD¶Ë�Nii‰²yS«ýÉÇu¢ã˱}k›«ª¢Ì®ïþ`Q‘ò¡xå')ššñ¨gÏ�K�8~Ü …ìUÅ´k[3íKKò¹ ¿Z¿ŽE˜>:Õ¡sæÜ•¾×¥”¿ODÃdÁB–Á .°¬¿ n`1X æ€ ˜Bhe¥¥‘GvlKõË/|ã+/•6ÔU•äçæ`"üá!NòÀÁ#ê¡�Žh ós¨½ ¥ª¼˜¶on`»•¦úJž�¿][tì\'�¾x#râ|÷>øƒýˆˆNe[zË‚+µeAÃà"8†Œ«£†Ø( =�ª±ñ úøÄiÿþƒÇú®vöŽbæaJ±ÙU"éÆüŠÓt"~@‡Ñ°¡^©ª®!MSµX4¤ õ$z†¦hn1Èé¿Â¼îðº pºÌ‹¨dtrž&f<Lœ!¢žl^¿kÁ‚%$– xÁ1pŒ�£`Øv/±Dwƒ9áp¸°·o xÿ‡¥^{kßÔù‹±dz6�V% ÊÁ^‡ò€Gºs\T_[£”–—ÛZ𒉄Ð;­à�FHŸŽ/ÌÏ%!Äç5}äïA�ÙVu‰‚ûD81ž‡Y°�¥°„Ä‚\²k~ô� Fa¾ÝàöïûðhoOßpŸÃ™3›Ÿ—o‡ ä`³£�0 b`»™ú DiY9çõÔLJ¦Òi‰%( –åSáÍvžÜ^DÒ  §ÓÎÝ[#“ž^D;í,ì–�XX‡°j$Öó-‰LØÁjp ¸Ü Ö�…í jÎ\cCƒí×~õ»ùüW¾ÓÔÜXÓV�çÎ×[g4`e2aß �>s^íî¼J™Ț/Ñ*/-¢§vo¢šÊ’;íìJB‘Güs3tærýä��O~rµÿDôªe•bÁ  ˜–�5àF°¬ÛÀ|0ªúÃ}ѧ÷>îüµ_ú憗žßÛÖ¸¡¶07ÇMBˆª‰DèÊÕëòê•ËêÂܤî6l×÷°?Š®®G¶4|VÝD‚!0´�„4!}tºƒ^}ÿtœ€HD:dÁÂ&$,Ða0Ü>aD-­ ”`ö÷‹{vïÊù•ï~}Ó“�ílimnÈÙP_óàìžDiDýƒ#²ãÚõØ@ï5%ðçè»áßÙJ{v´’.,·@ÀÜåo‘ôÒ[Î 9síw4MþìAöܲ`ÕH,XÅú4ÇÁ«à(—|¯XJY:33+>>qÖwþÒU/&¶µü¼Ü‚’â…$‘¢ŽRÖ{í¤¢¼TÔÖÕ:¡˜ô.xD(þP”E¤¢¬ðfö*0fˆˆ0ŠDt£‚F&<£CcžKDÔó0 %Z°" XÖ�-àó`˜k|ÝÂŽôî]�#Bizdû¦â'ö<"**Ê�èDÐüü"½õö;ZOw‡Ð}ºô•¾_zn§¾DËüOL§3¤iRbösù{L·H¡#§¯Éãç»ö�oïû {Xk$,!±`Á–€µàF°Üî©.Ä{ß]ýÝo~ ürnSC=(Ÿœ¢ýû÷G/^¼èôúCŽ'ÝD¿ò�§¨íÁ· Ï{Cd¬ —“Å„±è ÑY¤µÎ\ꉟºxã}´¿LD'Ö{×– Y°°2dÀð:ø>ø#ðgà0Š™”4†=ÿç·ØýýŸ¾>y£wPÕ4�4n¨§ï}ï—µÚšÚ¤^ó¸10I�½c¤ª "ž‚ÇŒ ‡D¼zc„ºúÇ Vò>¤È|Æ�hš,X°j$,˜®·°Ï˜°þUnìTŸŸ ÔÕÖ”À0Ñn['C�RJæÍj<ØFi‡üѾ¾�\}ÆD� MHoå¸]”H¤(ŽrÃAIa>ÍÌùéJ×0u£621³˜›íD­ä�ûtZË‚+"±`!Žƒû�å"Åp£ëÃCGÿÝý�öŸE£±uá8ŒAD¬îÒ½¸Ì¯ Å&šZÚdnŽK ‹ÉèäÅI}�A(XPz†&éè™kt¥sˆ†Æf 탈NNà!²`á–°j$,°üSàÓ`%˜Ú¼¹Müƒ¿õ—·~ûk/×ÕVguG—ª¢U÷üeJCP^zþibË}š$yâÄñô¯¿æ@ÁG$0yl"o Lø‹ Öê’Ïæ� ˬÆGÆ=¯Bt]pQ²`á–°" $8~vƒó «¿ÐýŸþÇoNÿÏßø~ââ•k¤iZv¯îÍq#*¤�¡ZŠt*%§¦gBúbpoÃÚ5*ë~——;¹¸ë<|òRGeo‹UIu¼£ÄFÙûð ܰamzpP`^ìyd˜¦åɡҵ³µœÒÒÃÛ(“°•ˆ=/ ›ØŒ?¡<@“±W$Œ¥ƒ \‚pB¢¢cânß´rýŽŒSç®èDEEû&8܈¨"Ê>ɳ—o1X{ùî‹·Ÿ=œ°ïñÈË7d'NgM ‹Š=�‘®ž�C?9ù Àßcq9ð2 ‘£F†"Œ1¦eú` Ýá,¼É‘={`˦ 2·¬]$�n/E⼿+iQîòâÞÙ¹iyÚ¬iãBG�ü´yófKqÚkŽ5ì…EP LA�®ˆètl^]§B©‚_`åÏcÌzÂ1ƒE  kÓ¢±,œ=QœßÞ•ôh�ÿu$…¹H\°£dÆzý§ïó~+‡÷lô,P À6ó8 {aŒ…Ê` Œ1ƾbÖÐ.(SªDž$,KæNÁ à¼$†9ÿCDnïïËÓÛg%ÈýÕþ9¯]q²¶¶Þƒã½¯±æ@C0Æcß \0®ZX˜¿®U£Jt÷ÎmeÁ¬ rçòQ‰ø¤ý0‰÷o‡Çrþø.¹tj�8¿¹#É®ø¸Ï¿ø>âòþ¡³J•åŽõ1\ƒÙÐl€1ÆØ7ÊjÂd8diiñ¨bù21=0L&Ž,û·¯–�×Z“ÔH7唆‰ò3o]<üÇ�ŠÿgƒÄùÝÃ÷šÉuØ-ÁcŒ}㌠Œ†xØãKõ0iݼ‘ôëÙI¶oX¢ìQh9L”}_§§rûÒ9¾“ò·¿ë ‰vü»ïÅ ÆMŽÜyÇ·ŽÀ(Œ1ƾ“ô¡, …mVV–J—*Ѱ^M3¼¿ÚµN&šÓQZIu�Oåò©½r ÃäÅý ’îú§_Ÿã)¯^N¨W·ÖÍkqÀ8(Œ1ƾ£ ¡tƒÝ꫹rçʆa’9jh_9q`³„û¼Ó~ÏD3ŒÜ?>�×Oʹc;Åáխ߇®ðúãª1|­ççÇ2aìp'¼gåŽë2¬„ö�cŒ}géCè ñÖÁ‡*•½o•JåRÕ§¹6­^ ¯]V6˵&ÊÊä!É®ÍË娾�é÷AÙ”Ç E½Ÿ¢\í옶|ñï,*Õ Ï-8C¡"cŒ±ï0]PA#X—ÍÍÍ\K/ݶe“Lõ&ü%œ–Šöÿ¨õêÄÇñ‰œ=ºC6®š/GönLÛ±ieÄ®Í+ã.�>èëáðüú¹3ÙØØœÖ ‘ó0ª€0Æûγ†º0Ž]Ëž-«O¥òeRöí¦\}åúþž²:ÉŒýïð.ïîÉõsdö´qÉÅŠ»�w¦\Ê“'ïssóšAvNÁ(ñcÝxÈcÌêÂØLMLÜJ/ƒwÂ+Wti{'|J„›8¾¾-Ó'�ÌÀi´÷š› ¯ÀX ¡Øý˜�:aŒ1¦Åaœ„Wúúz‹)6¤å2^l’ksŠK¹ã}éü©™Y²Ø{àç?‡[°fCm0Æc?pº�zÁAxdffö°iãúØ€×æÑ'Šø'9¸k]F©Åk.ñ]Í¡ðÏõ:jÆcÖЖî¼yóì9´Ÿßž-+ÅËáñ{(c¼�Ä~–Ík§.\è¼ætVÛŸwÂcÌÊÀ0X^°@þÃû÷òÛ°r¾|~ySÒ¢<þõs´üœŸÉ¬©cT*Õ9üÌ%Pýç_‰0ÆË ½aF¶¬YwvhÛÊaÅ¢™©�o�‘¸ ‡¿&øüï_óOîÝ£“ÞÒ¸SóXøBÀcìÈÃ8##ãõÕ«U¹>nÔà€Ó‡·§†z½‘ŒX/eXdâo\Ù%)®Ê�뉡Β€}‘pŸ÷òþÙ5™?sblîܹnãçl‚¡� cŒý"YA-,,,NT«Réö” #/�Þòîéõ$Ü­žyõì~9sd;îd_¡¾BK–Ì›"#‡öÍhÔ N8n:ü¬ÞÀ‡ÍÐÌ�1ÆØ/” ´�é°ŽÂe{{ûó•*–?ѦUóÍ7x…¿ŸU®TÞ©HáÁ… æÁñÁcXÞákŸÀ  ¥@cŒýb© =L��p®Â^˜ ‹`9lÆûÖ�áïÓpîÂèÎ;×cŒÙB} Óa5,€ŽÐºB{è½`,L‚v�‹+ÆcêÌ  Ô�ŽPlÁ LÁDÃ,À AWÁcŒ!]°…ü�÷¿ž®bŒ1ÆôxcáoíÀ!€ ÿ¯½a£èÎ̼ý¢„IEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/backgrounds.qrc0000644000175100017510000000307515224505336025116 0ustar runnerrunner kitteh.png kitteh-xmas.png kitteh-bday.png kitteh-spooky.png rory.png rory-xmas.png rory-bday.png rory-spooky.png rory-flat.png rory-flat-xmas.png rory-flat-bday.png rory-flat-spooky.png teawie.png teawie-xmas.png teawie-bday.png teawie-spooky.png PrismLauncher-11.0.3/launcher/resources/backgrounds/teawie.png0000644000175100017510000046772115224505336024105 0ustar runnerrunner‰PNG  IHDR›¦ª|)‹o˜IDATxÚì™t+[†§m’1bÕ<ŒQ›?¯mÛ¶mÛ¶mÛ6Ží:ß}'m¯Íƒý¬õ$cì•Ùïþ&ŽÁ`0 ƒÁ`0 ÆJ@ÜéÎ Y5dá×€»Áíà†p¸-\6À$,ĶVŽÁ`0Œß¤ž¶ ïÁÏÃ6q:¾gÁÅp쇽Øf1|ž[:6ŽÁ`ü"¹�±ŠQôœ¨fäd§%õOû-¤U»-)ëþ„¨V$$MMŽnù§ØÂó²®Ym¶|žç=¼ ¸,V«’g±ˆ¹yy6(X,Ë*°Í6¹¹¹WËBy¶QÇ`0Œ¼dÁˆ|\φÇÂóà¥ðx¸œ€Q»#|¹_Iµê4 dÞMJÚ”¸ \µI«‡­¢/"*yËqeã�ØÄÃk�žy1Q!Ó‡›ê=~J¨Ex™¢ÂÈò„¬e×Õ¹tÂ…è„Ë�¯¹p/®éx$,á–3¢ºÝ¬pöÄõÎkäSCQm¼û©tÜKéøç†éÄç3t"¾�xpírÞ“´ÎöÇRS¨Žê½�ì½v•‚Š:KË3¨Ž¥tÇ´õ�ð“­<Ï1þt00²FlRª�v ~Å æúˆU|Ÿ;P™Ü‚õ×þ?žºº)Xt(h-l·gƒÝ{4BèbËK)Ř•�´�¸¨e°ÞáÍ48½Ôàðej 7µú 3�âL‹'Ÿ:ƒ%„ïì3Ðæ-Ât–Mï ”¼Øî-¼+-›!ˆ4ŽÁXEþ“IÁ3&ë ,s$pF+ÅÊ£Ó08j6hRªaV?cŽÌ#t6f WëÎþ´f_œ�ÔiØçýZ‡»·ÚpR½ÛGÿ›¡Í;z¨¹¸ÂŸ Cîøœ6?ì*j­ûo¶:lGEÔš_˜}ÅÖSV•é.«ìOÎ/Q ž…cWqŒ?Ìÿðß *˜"´çVæ«O„ËWì\tsK|6j%ej[J…¿mÛf�gÛ6Û¶m[è_%™7»ííé³�=gνAs÷bw¾™ïÛî%9\N²QU$Ø8Ô¦;k*ÓÔ®tnž¹ÞþÕ"ãj[ú‡¥ÂÙO•rš„r�¨N²q0$XÚAke�LI¬¹ 8�x¿έ˜ñ5?{ ÜdÐ󠲜=ÐÒ;MÜ}õ¹Ÿšd{Ç«ü½y$±lÚr¦àÂÔþ›�vÍ]|#98–pp\&Œ�I¦±¢aô' IA!¹Ã|·$�: A§ä¤ø„ ’,‚Ñ^·q ±"d]XfIöjC’ƒ£žÑ½ØG‚­ ™Q™´ž>DNZ;$ºùÕ“Û}`^G\ûõ aý7‹тץ—¢´Â¬ln&F±{I¦ÆÈ-‘žÐã¾8ˆÒ”­¬C9ñÐ*e_�üYgÑ5ÿ=Äy#ŽJ'ÕÉE@ÉëèI8xœuŒ¸ÅÚ�°_ß[SÓZÚÝû€•È¥¼F›ës‹z>φ8s[}¼•��“=t®Èu÷¡ÍéÀâŽeðÇç4ɎϨ­Ê(žñd;5âù^œ…�°Œ‘l«F¿Oâ¡j­3¤Ò"M±w‚ÖFZ«õÊþAÄQ¥§«]‘ãæ…\�7Zxúóµ ’xìT• šxøV¥Û;ïK±V½˜­vwëý'ä(MÍÔþÆhœ˜ÄÉl'_íw•Œ’ŸáÀ“ʤn�IK ñÌ×$XÙJ‚HU;#…‘¹–ûÂFJ´µG–—:¥f£S�‰õÊRô)zíûB—Ú1nˆ¶V!V VÀTg ²=}‘æê‰X•‡?�’¥Uýá´h7ñÎÞ쇴ê•sƒý{åïÈkð˜òØ_…ÞûÀ¦(s«MõħªWˆ·r?�}½$ˆ7ÁÑ-;ŽÁ¤©Ç“�D ¨”�Û嵘<ýz„ÌôŽÐrbî‡×ÔYÚkÌëÔÐr»@[r;¿;û`Š~A#ñÛQY £ýµŽ¹–«¡wÝ/s�B}SYƒ*EJÒþ"aPáH[¸.°ºW’F®›72�Üùl;#Ç=N�Æ=uÙÞ¹ r|üÀjDB–Ò6N`0¥ŠÎÅ©$“žqIxor)V¼þÖ½ó –½ú.ÞÏ/ÅÐôdS½kmU’tÒ]õ:g÷³)¶ê/Óí\<ÛûG˜ÇÔþ3ÖÙX¾ó„‘hL2’`¨bdN¡}x&´î€g†ŽÂÇå�ãÓ‡Ÿ”Ûg‡�F—ð´ Aç˜8ôÎС}T 'H RH@$‘L¯@4Íí�¡OLAÙ¼s(_xùSŽ`È3³Ñ¾’˜»HQ= E냞…ï¡hî9!—­Ðcò”£hÖz0¢4cê Gô÷*û:>è®{oý ¯Ùí¢p‚×å('˜Zæh„ÊÚDÄ6"p[b&?»‘hÇI„ç4è©Y´ÐôRÑ3vÊm‡x…°„i‡JË+Š„$T¹–Ï^“Ðd´ë0Ý'¼�É�„§‹ËÑ/§© ²¨ð…z‘Û("‘¿C5„þIø¢ü \X¶ú­{�퀷ú-{quõfÌ|êE Ц£©‡/ÉÌ­üCÑ& ìŠN­y¿¥{€Ý¿ù>˜š©)%»ÙÄiÅ6S £>Jþž©™˜þìË80{!*6íÂ�Í»P³}?Œ»ÕaçAÔnÛ‡kë·áȼ¥øâ‘§0$3ý™ké§ES¿¤¨œ©z\�È1ÁÍm:�„÷6à‘åULšðèŠ*”Î=‡Ño®ÁØwÖ£pÉhyµüL™”á¶dÁet÷ â݃Êþ…ÕAô[L¼ÃI8·üù‹Y׬#Þý̱ˆI_¨¼¾·�ˆøŽbLℵ-ÁÖ^Ëh¹INw<¼ø ó5 6šÜ\žç·G¾.¡Ò™H’í6�çL"NŠQå(íI*&­µk,ÆØ)ìO¹ŽpM� ;ædÆšY­fÙñ ‘´g_æR¨rdK´¥=ÒÒ;<3ó.#oæ9´éSŠgZhüŒC+—Õ‚nÈÉî�ŽßĈw6câŒ3˜0í4Úô+GB�–cĆ6› •Ž›´×2ÔnT1N’dziÓðåÃOáÔ•0lÝG‚9H(DÓ|]µa'v}9e]z¡{TÚGJÒ¡¥w-ÍÞùÓîÁq¡}£ÓL ÇÔþµŠ&�8ÏÁiy˜àzˆÄv^מXûá縺n°û0°çˆ÷¿v"ÃÀ�suíV¬xûC”t鉞$œÖ�¡Èòð6›ÌÑÄ«5ÈÒ¶À ‡¿@ù’ë’H”‰÷1B™Œ%Ñeõdó('åÒÅè:æE$z‡IœêBQ8gÄ‚R‘3ù“«ÍºðXû½V³ä[Ÿ¹Q溕ŸE7þ¾˜üùÞSìçe¡nâ9q {a�<—‡‰GWKÈs–×@ª…\‹ j0êƒíÈm7ñ*W©t¸‰v²¬åùŸŒS9.Öù¹·ÿmNÆZuk¼™µ–X̼Ì’‹|®I6¢ L.® W1)ßÿ! ~u5òVÈë=öãÝÈnÖQ6Žˆ4·á–U�ÉmÑ«ô3L¤õY°ð:Š—Õ¢hi�$œìÌ.T9n,æ¨Ë½¤©ió~¤«\¤m6¦y,xñ \ã8 É4&n’pŽÎ^ŒÂÝè„ ‘ÛV¾!�ü#gtô�òü7ÝS35ÅêAœ#¹Å.£ý‹eo}€Ê}ˆ å‡Ar‘Ÿ+Ø©à ÜVnÞ�E¯¾� -Û¢FUN \ÀÈu%rÂL¡uÖ›9œòeU’l”¨^±™¸åd ·rR~bMݶ„„Ó™ùœxW?I8´ÕDßEy¶(‹nõs~(ÿŽX«|Ï}·üÚu4"¯ÅcÖRYH_ŸÇ¥ßo%ï?÷ãElO´WU¥s"ÊÒuAñüË dS&ó6 Ë­DYýëâåüœ(ä¤×÷‰HŠÒ!†v�X<ʰBí“ÕΑf–;‚ïº÷ŰûÍ<‰ÿÍJõD[ÕÍ ˜¼"ï5{‚¥ÉGb´¬‰µ°1&ÙªyÌh‰±2ÒÑ -û–aÌgQ°¤…Ë ÌîB6 8²‚µÈKEó®“1ðÅå˜4û —ÖÊkOPaC�QÏ£Id­L/ð÷å}OásÜÂ+‚#ðp�~ØôÑW’4ŒÛö�L~ éÜX» kßúZ´G»�HM0:‡Æb�6ûj—À˜�:úEºý+nŠ©™'YA4ãi=Ñ—6 ÿ^ååqy8µd5Œ$’Fòã¤Ó@0ßû®i³� �ðN^ ¦êÐ).ATXI+(F$d5�èWþ)'U=‰E lEå(ù‹†×œœå¾DÙ¢+è8èaþ}€ôÈI"ñ{”ç§ý™5D¹Ä$¢Í/M²ó»VÄL^¯¥œ;Úܲ¡Ä›¤SEÂõ=Kç^i»GšYœJwÕµ¬ªHëF’ªr~�H–ûÊë•S´¼aŸÐË"‚¶ƒc…_²XÍ”Ê|Aއ�°+õ©.š‹1¶ yZqÔþ}²„ø¾˜û-º³ºl-×µ\‹6³2°zLÚYé,ÌÐùE£Ý€‡1òƒ(\R-®¥D11ö‹#hÛ=��˳öêJäÍ:'Õ ?“DT@äÍ¿Ž�OÏEGæ[DzHó®Z’Ï¯ÖÆ�eËÞ ¦Úc团‚DñƒDÒ`�ý2œ_²sž|%�{¡[´ƒSr1R× #²[žoãÂN ý?ú¦˜š©‰H¾~mÁ‘ë @/[Z[3Ù_ÍÜK‰|�H¾O2 ß9(Ñhÿû¤Ã×5[ö`Ö3/aD³VÒVc¾C–�F™1:ŒÖaò{å /"%£¨ Ñ(ûE ®¢ÃÀ‡W§p$¨6ô<¿5A@‹*Ð÷¡/¤™¨v£�¦‚–¶Ãyn—R5+âUŽÏñØ6a–Öÿ9¯ŸÖØ-\œEÌâJü3$üªdQžl§f1…\ýѬóx k WKâ($ꔊ ìÓñæzä}ÃÞU@Wq6Ñ߬î‚[ˆ»@ÜÝCÌЦ Np‰Ü©» 5¨+ÅÝêP3¨+5œðæ¿ß<2ç£Û—ºœ·çÌÙ}¶˜»3sçÎS_ÒÔuG…ãï m€ÚEi™ÝÅ•²|B@ÐpSd X–`�Õ–Ñ:0Ëö)�Py§XŒµf¶DEL&xfѵÔXØŸ&dÒøô�šG£Rs?ÏéæuuAO¿?tó§sqRœãa行Ž‘6ÜsÇ´ÙtD€æ] Lô¨¦õ=h~ØvÚ¿w Ä‚W–>Fµe”íîÅiŽJÀôIE¼ñ‰�[Sh0qÄâh´tÓôUßRYõb GaÀÅ"µÃ¸Î¥°ó¿´£4{ �ÙY ^KjÃ}ËXìÅ=³11}ùUÓiè¼G) ÑÞoM©}zÞ…½�´ôÜ�â›Û±óÞôEzô¢I÷ì’ëœgO§ÙÁv�}mgáiM zz­5*Z‡xÛó”“;˜bzúòCŒï"[\ç®»!bú”¥ÝþJä�XHÅ ¹r0€æ}ÖåÐ#šA´ÜÀÛ“r³*iØ•«©aÕ!jbpá‡I�5®:H�Ï~«X9NS¶AÝÑ3ßPÅŒ¥”Mù~Á¬b­jdˆdÈTgäКëo£ï6¿xÒÔ˜ŽlÃt ±90ÞÿÚ«o¥E#4¹tiDUF'ÛʃÃ?„¼ÍäBWÿ?äßÔ¹8�¦3æz°qZŽÕ ]ûT�"þ·;^áâ¿ÁQŽ‹¼6#ý·æwlØ~û‰•4!'ŸóÝ!­27ˆ°ÊªÒ̵¥8g+Oÿ0ˆð™D8å£P$˜n½ì96Õ˜Škæ÷_»“Åv8ì ë("V@8.*šçྐྵÀ^U¿CäÄ,¸(äök–¾MÍ`Í¥Dåê$„Ï8gÚÊ“–ÑÿNX8·Ï•Md§žT>ær¦t óÌþD “ú�ÖZD'÷’ òÝÔ¯z%¡ zkÒX @µ…�wáî4Oïu½Î=¿ïŸ]Ü3î¢NGº¬ ì&ôx}€9Ú‹£Ù³)”ù¬øBªœñÀâkIƒÙSf±pƒÈÆål`“šY€fÌ]¯R&|b{ø°`j¶§¯ª12Ûl údœ·˜önØ!£™€Œ˜DØ8´ƒ;vÒs·?@5¹%¨ õ¦>~”çîÛ’Ò¡û®ŒN®NÏæ\þhê§ÁÙÝ…ˆf€F4Ì&—Ó§ë¶ Ëìä`#€!Š–bÓÞ3ÉŒ~v à¼öðS4,%� ƒ&…öàsÛÑày��D-0»¦È[y[£ki6åxW~Cù}'QX»®,à g{X 3Ãuû¯Ó,ÁõRàÆÌ;€ ç;UO9¹’´4gÞ šGù¾Á‚/ìBCP¤CŒ5ÏŠúO¢@D:­µ¯¬Ð°¯n½òªô“5Îâ|¶‚AvPÕªRRÊi:Ι¯Sž\¯YqÀ<Â\›#¤ý7°5¨¢én*É(§è®î¨'q”Ç–áá}°O`°RÏžFÖŸp|þó¿�Ь¿Í�;RcŽ1I÷>Ú+ŒÖÞJµ�~¤€D"Ä&6l‹•{m�vd ÆÙyõZê“y å¡Ç%@£ê`}¼(ÇÍ›ŠúÑëø7kCt.ÑŠµïY×llò¹c“^œ�7ÞEC¢Aˆ "Ÿ`œ“ï>x>RÐÓÿ<§‡s.¤¨¦?Ý;ªNÓªgVv؇«6 # ¢�…Y—Ñ€e§ÝÈþNü­A,xãÑå402– ‡¦¢~B� Ë š{_…S•t™0·n垉h`šóh�³XC ÁIJI™kèC9ˆë¾]uÛc½WE< Žj‚í:U‡” Àø‹û–€ß}ˆ{f¿oèÓÈ*ª¦æ•ßâœì 8é–-è� àÞ ì“µ± Â"–4�ÿ¯“Ô}‡ž{þ× +sßÌä%¯HäÆÑ _§lKºL�ä°­§œxÖ�š{ÃFÊ�EuóäÆB8e®1d¸y¶€­¶ ç0üÏF�Ëì4_àH½…¤ ¢â.‰(.9±ŒªoÚ†ëoi%R7 ˜ˆIÄ#i3¬kžø‚ÊF̧D4lÆ#‚Ic2 iÞ,W/º$4šîiš…Ê6 ð�÷,Ri†ÙŒˆÇØ—¹ß£/¼N̘OYÝÝ™^]‘p¬`ÄÅî�"ÿ59çâš°•jÎJk‡s^@0½þØòãÀ`F"²m’Œ(ÆL§YR£�ß©ÎÓW\m¨.RàWOª—ÔÓ´U\@gc§kO)Ùm‹<å+“§yõXsE#�‰6<ÙïSMŸÚcÇ |°­"UÛY/é%S+î)DGZ#Á°nÞh:ÝA³µfÌæÇ? ¤°tœ÷9ú¾?G4dÔn°¿*¤1÷¢fêr¥( @‚}iµ¹f=•Æà+àcw°âhát«QøÎÍBÑ® 6H7ñ: J1�º<Ù¾ã,5èO4í ¦¼²ýC;¬%Î6­›ŸøQÙØ+©ö±OÂd4lê‰ÖJàwï[Ü¿”ÐÓ@à`T(_pËø¬|Z~å tè¹]'�:$ª±øŒí‡>×ÖŽ)ÒL˜Ù¿’J|C‚qT¶/­£ËÓù.>NvšsùÝ)LéŒÁü'pº­y|ZyÃmdÓœ¿u˜à`ÖkdÝfÓÿóIDtª·Õ7Sê7,ó¡=;¹ÒèëÖPZ4º³4@jÍ�bò½f¨  ª£¨Ž,×Ïmª°c€ ?ß0 jÆîÏŽlL`è‹÷?}Xý–Se—ÖßĪs´HkÖÚÔý‘.~6øþ•¶û>ˆá½ ìóc¤¸ZÔµâªiJù¹¼d¿zCç4vš¼­§Ò$âö<¡3e×îPѨø*ô›L±ž¡’RStóâ�0–‘ýQÐigMó;}Fàzfj^ÌRD4ß(mêï %†ÔfF_¿‰f¬?"l=í^Iz±Ö¤Œ–:k\s”ÆÜþ2e§ö§>>ATØKÕd°é¥pš5°Š>zv½5˜˜@alËëmÖ¿}ë‘g¨&«ˆ†Å¥Ñ¥Q‰¶BÏ =9Ý<šúù†þ~Kçâ\|þùŸH  7mä4j0œ^Ã}úð=æÞcÜ¿�sAO G 3×à´Ùe8Æ"2U±ñö¬5ßQIEfÑtät¥9½ ó?¾ï3°½ u›#!¨ñ$DdQ´·„!šD(RèÖ ßâl9õÆ+`¬ƒöÇçǬ·ã® ü¥NÜ�Á&¥« ÓzÓݽÞAÎBœ×¿[sê™ÿ¸ÿ³ûÏļž2LÌ\�¯‡0F@ò?Î7ŠF\ ͰU´{!Ôf­ShrßtzsÝS{i@í-ø÷Ð Œ³* ¥|Ÿ¼—žKÛn¿ï¸`¦ÓÌ*�fÍHs”†3Í:Y³,Π�HÊóð¥~!‘Gó{ú½žïâëTw.¿å"òöÏÀ©j-¼_ 毷¼h¤ÍŒ&M 1@Æ2k>²_¨”ÀgYPo8f¦+#½%㥃@aÝ}©úêUh^了8bv,ìp$�&ÑŽDu�¶ø)ªš¾„¦>ö>;ÞÉ·n¥¸ x=ú8 Tœµ{çÛ�ÚQKعˆ²TuËÞbç®3âôTÞ§tžú”òúM¡(t²câckýFQ°×cŸ®ª†£€ ﳈg´wM¸{g«ó°1©Ðr<]M@—¸ám‰Š´5ïk=G8èí¹Œ’|B)l& ã´ú~£gFr_"µ«ßV¡ù¬€ÿœÚk_œCÄ3ßÈåîaBï4qíÈÈ5R…Xk)D�™(&µœZ¨:—U6SqteºyQ¾ªÍ(ue¿ŸS@/ß÷CŠÊè�yÅ|m‚€…YÍ;ÆïŒa¶¿B‹«FQ9zoÊÃ@èõ Òi÷¢Ñó4§t.¿%ØŒQM�ŒÂQ³ØpÛD5(À$5fÖW„æ 3úf¬@Ȉ†ô}îG³Ú”>ŔԹ×9àøy�¨†ÁQìtе²é4P~&Ì3v,646ZÀk=E5qÉ.dÖ jöã0ýy‘¦›v%¢ž}pxPFíh`-Í]@(ÇBI–Î~¶šG?¦”Ôh®ì$ýC8†""¬�yÃv`ßGTm*R�Œ†#åúƒ¤ÒhŒÉžXëOï:ÕWˆÓí×-à3U��Àðë;îjJ„ÌFNSn&êZH5~zî«pn¥Š¢ý[üÅßú¨‰¹ˆ+rãÑÜͤÎÝC{Œuóƒ¨úº�Šm& ‚ë–mé7 1ØgüÝ ÷½E…ê(ÁÕ�Rº¹àš=x]àåO³TÒË×_@ÁA´aF4?«fcFSÖçÂ96e¡Êަwî¹3·»WïßÒ×8çxçáàö3³ 6{ÈH:Ì*Î&Ð�Ù7£›ƒ&M‘Ž™B³á?Ñú›ï¤2°€X”€¨Î³_ø‰5 å¬y�Ò;™ªf܇7G¥§Äî@u¡N! h‚–Òm/@ÑðÈ{Pb›? pâ~=®T ±v�í„#T5¦Oyàm�¤ÌÙL8~_o8çË”’Tªäê…f�kQ=?ËA6x OíÇÔ‡hÈÖŒ½y«DG؇€‡¨04q®òÔ.޾kDB’~RÝõ%ñÌM!8vJ:N÷Íp÷:×¥ÛFDq¿ö¿QL²üÄ3³ ¢yEÿø¤G*“ÒÞ@¤ufËFÔ‘ˆÙü`‰�¿çuVhÆ5ŠB¶HÏðõh`£›ÝL^¶‡²óGRšo(ø�m –�vjù@úfÓ󦳶¤E*͈é66€cèªI=ÔêwLɾq\-Uªº Rj…ÞAŸdwq_Têò›4î:gT“ÇùšÒÿ¢h8òÕ 4Óg¦:ÌâµÁF3MH��ôï›Ñæ}@X0 z�˜¢ UdêÓ+‚²“Š(½+1îÁ åÞÏ*PCMÏì•§|¥Ìúa:Ú4]Ôs*ÒIÕ5WQŸ˜4Ž¢p�Tmƒï¿™�Öˆ§í½L³=û*@ fÖºƒ´€ Xôãà36Ôrø3i¸lxè]JŠ+C­½µÖ(ç€JÏõÂkžÞé‹4Ú­ÛeŸ3¥�S} ¯õÏDù;ú6‰¾ÄùJDÐøô—Ôwä|Jˆe-5N§¡·$ÓÝûKÿŸr“yý«E4§Ÿý¿^§Ÿ3%¹«ËKƒb¾�WØ’íîMiÝí"—ñš,ÍDD#Z$c‘&ÓåhäÞÈÃH-š5K.m¦t¯`UŸaÉþ2КóQ¯i,îjókÀ0j6&HXˆuôc É{feîï͇Ÿ¦IT•ˆÙQñ‡²»{îÈëîæô„Îå·˜‹ÿ”rjPÿeRÀø‚LÑ|Ť7›fE[v.–&dzP#àèæÙ«n¤|ÐNy¨,ÖÕŸ†_¾œé­c®\NŃê)#¹Œ7ÝIõí–~� -EòR°7¦_JšJúëgÜHs'×°öUr÷žª¦ò"î[*ì騹¹tÄ•+…)¶À²Üôâ¾~>µKߢ„è<ê�É�­2AHoªuÜhïMÃæ?F&frîlºS�!à£ëà iµ©ÿè&×S·ì*(GYÁÑ<ºÀ0¢ ³-êâ@a:ÔÀOý¥ÿ}bzæÍܘëáûYÿ𢠰ã:ª‘ãmÄbÆäbjþÅ}mÖhØÌ¨¦~¹RÃ10R±Í¸>SèÄMcI?útõ&‹èÄAšLÌA¤³ÓØð{Ž¢ÇÑÁí;éÚê‰è¹I *ŒŸ.ŽØ“ÕÕ}1j7¿ªJ„sqF5ž°÷UH=µG -µê†Ûu€lÃt@Ñ£iFF$c4{sqÌ^›ýªÐ9|´" ðäÉX°¥Ò’ʨññ�àt[  pŽñmª[ò" “>»_ ëœæÒÁ@:òuº´ýs̃™xÍ ºýê©oT,žæ=�aÜò¸o3Õ¼qÁEÇP0§¤Ø>4uù^qÜ` ±-„-Þ&©³»þ7Hd"çXóÀ›Hã ç¨Rƒgœô¥ÆÒ%õ· Hä")"�öÆD� µ*I­ÙMÎERŠ'FK-4 �Ÿ¹ ƒ'¹ûS4€'*â:‚ôÖNÌíÏÿ…ç(�|ÊÓÃϹà£än.ÇpŠ¥9­»+3ÃR]}) ç2@ÃÑ Ÿ§ÜCËL™fYóg<“¦jêJó £O?ö }³BЛ‹úÒûϬuP+�9®É˜``¦ÏÚ˜vsp äƒçï^†q}mcs iLvÁ¾€Æß°�¾š fÍFŸ{#Ÿ™iy�{eÈûèû–}ÎébÜ-Û©¨`œ²‹]Ò”:ìE¶)Æ fj÷äXj�7ÁEï¥@1"Ù6¿cRÛ:åó m¾ûäJzöš›è±EWÓŠko¡7¡DýìÕ7ÑØÜ|ºÝ}+ê©þ�×5'®§—ÄTt"€�ö,ÑŒ€�…Ì\wj•¬¡”€p»X¥šÈ\ýŸh !…]SOÕéï‰  5["+˜ì˘DªE3X`£ pb[ò;a¡ñþ¼d?zñ½q%ÆL7ÝE)pÖj¼r" p²1k� þÑ@óïSÎüßi·¦vs=׎‡äqê,û΄Y¶;Ô ¢ûÐø%¯Êµk÷OO ÕkaÜééTVÀß+ÑbïÛ› 2y¨ÕÌ«J¯Ø`4æûb&ýX0Ú:ZÞs¤½f0Ö`´tö¢CQ‰_ŽÉ* á‰Y»‹Ü®A„ó‹=œ‹3ªqCºâuÔiŽ(k,úT–_{³�IX±Æä»–Ÿ‹9’§�YÖo^Ū™+©=6Ù†LƒtJªj¶sq£±y4gìxZ8¥–.Ÿw5M¼üqš¹ú;ÇË bÎtÑ£“Ñ%NZ êâì¡k6seÍ0A!ü¢NÔ§ÿn€d€'h6q²Ó·[WžÖ£ Ù‡®T-& ¤ƒ‡6�+ �"Ç"À«÷çè²úøŽ¤°t¶[ýÓ_QÉà™Ñ®+“"Õh‚3Ïýò1·à�¦ÍthôêôDDs|‡.ß(š1HÒ¯c/ê@ùHq¥cªhõ�[d$³ö7âµÔçô‘Îü=�4`¿îi(}W4Q¿P•6Èø¡«7M-@ï=½ÆpâmNo™¯-ÀÀܶޖÄŒï™û¶xmÁœÛýôš–áIYŽÍ,8:.«ðHÿˆ•ež½|'ã\œKÀÙçª1Ó0®xoôEí˜F:0*Žö‚ €5°˜c�Í4ÌQ-FŠ£b'MŸ½�èelze»yRBGûÌ™Ô=e@B§®”Ð) ¡pEåciò;¨£�§=ó9œõQ˜¤•$Ŧ ZÓ'ZŠ£ê0ƒôͰ3o@J+ª«;Ï3‰Áºløš œ8}½1T¶lä};`èì7ƒ&-ç)Q�.´)µ}Hœ^ä7ˆšã•( ¦÷› 3T¢´&/ÛM©qEÞΪn£X‚6¤Àv@†… ë¿ @Ó@sT+öåºûØ;÷`�‰Æ¾yC¹áÖª«˜¹L£q è±é)26¡<Ãx­Ed«Qÿš[(¹‡7ehT�¦›µ}JéÃåë :3̘¾i Ö a€’%`˜¢Ãêܬ#´}Û^±5²{pTò§cÓó1† c×ÿÈA¿¤¿q.ÎÞš hN<¤f¥'Â�_9v2×@ÕOä=kegG¯-…6É4¨¼Nó«†S‘_;ê°x€ €†'k"2cÉ©íª¨¡ô:÷Bî´O‰Ì¦,¤I*j®§ºßæ~{zÊ&ŽWzo`BI6F( ÈèéÆ'?¡È®è\G„ë&Üø«¡ÇÖ¢S‡±mô´ˆiÀ&CÞ$õÆÛfŽ0社$ÇAoì”óÅ{ú÷̦Õ麖šü^ÀXŠïÉýi`ja"C¢G!2aE‡¸v�¾Edò8Æwý¡‹qvì�ÙÕmnìÅ�ö%¢î£¢l0ÏX5ž®™€¿åÀI×S󚃚bµˆr~rþ ÆgÓÕû¼6 %zТ?vú+÷!�6ð®LGh¨¿ÅÇ 07ùY¨ Džsá©Qç^<=³»ûîlW/–ÂH¡¿Å‹›)3À<ËöEš =T“îK¢B™9Ó£3A~OO©‰+÷äĆfQ)¹[Ž+eCÁyÙ¬…Ë@=n@D ¢F‡É©™ÉåT}Ãf5Ê5 ÉAÀääl»i²fãq9}'S$ÒsHsú5ËÅ�îh˜Î]õÖ4eÇQ LݶtÛ.€ Ï|ß<ÌZ‘™JӃɷûæ\2⥦¼�ß.0ºejVÿ-µ)¥Q?××8g íüçO�PÜæ.ð9ƒGÐQˆncœÛ(Mƒm“,`Öm¬ë5¦¸'Ûž§Wc8U’Ryæ:MTwozÙòãÅõªÕxÐÄ«(98)-VýÀˆÌ Ž}Œ�ì1¶¤Ü@YæýÆvìÂ�“Ñè㈃Ã"ÖÝ)A9Àî®Xã‰߃L GW™ž>”‹E Òx¼¯x¿(:{ÍÜÐ"þ…Ü³ÃÆõž…Òä©×H„Š+@£G.öµ:ÈkIyImE¢)Yã3}~‹üNA%’1èΙ`›ì›û›†/|‚’<{¡÷É £æà´S³n‚:ö iMXkÛÉ�E4ƒZϱAéßNî;€†§ü=ý)!$™*§ß‡ôÆj‹¨¨0Í @Á9ɵËû­ß�BÀÀ¦»)Ì6rï #èšÊ)ÎêSYS•ñÏŸãkœ‹3…v¡Xh�X9¢œe,ú¶/YfÕ­o޳úž4ÊŒïŠ ÈX°Òž»÷!ÊuóbçÝ®#O^¬¾i éÔå9ÓXô ô³˜Ò¢s)‘J$"Š¤Ô”µŽ¸î�íÖak¨ñpºÑ�ªÿðq Àµ!¨¨h k6l#•ç °q£Lr¼ý(ÃÇ¢ðôÝÕ�#¤ÿ³wÐQ�Ýöi]qww'îÄC$AÜÝÍ‚(´Ô]©AÛ¿j†©SwWên@˜óö|ë�õï¬ón>èÿ–Ï]묙¹sï�›¾=çœ}öžqÝ~.•q¶¢ýîÄ£ÛÇSíZ² }¢c#ˆÝFΛîµÇz�Àj…É`ô8ËÆããô\½Ï5••œNaé|IjÒÆe):w?S»î~üÝüÓ¾ù¬óñ7øü;ü–åü[…5ýamùµßO1CæB¦(.S&–ßçèÕúóáÑX% è^í}ªYÚÌ;^wx}Ðc+hßIŠ‘9MIˆÞÙ3ºèþ«a²O¦ãÕ?³�8ì9! " pì¿éî7/.™úÝÍÓ–Ë-ÓVü>;¥ÿ}˜¹©ù¯¬5‘-6cáùò €&–Š'_í}&¨·¢Àaæa(+a µ}3ÖdÍfL×Ì^ ɨ­;¥åZõ�KåJ°ËˆV¬ »›Y¼íYv»äf��œÞ©’“&¥ ÉRÜ;Z º ¿Ò¡SX\SY�‚�{‡G÷<ïC‡Ë� @Ç=OkŠì‘Ô¤µ$4Æ7vHã¤uˆ’ÔŽ1’:nß~e8×íÿ“€†Å=‰hÀÖÌL‰ÖŒ‚Øet-„ÉtÌ ]ÏôxX’ÆfDö=—iÒÜÑŸ‡{7ú|ÚuAÎÈ—|x¿ ‹I�~]º}“×­ÇuȪOûßÌú°Ì~À¿k(µ~ãI5ëý0·|ó7 îx%4lÒz]2MÆ-½U–=ñ#e[üÈ@HïQ¯†îôì$=¥XlÚtN›@uÞ�y­�‡ à_9MËeÁÊþ¬†«d àFU÷ˆ¾Í“ûß]U0ú‹õ¥SBKû¯ÈkÚîù‚¦Ÿ›Èö7·nÕj„)ÏËðMò—ð‚›†šõJÌ®{å�€aKOX1Mc-àa¨™ ˆKi«qo°;pYJoôZJç\éÊg*˜¹V§äy“íËðízÞ�¯É„Õ[dÐÀ©R”^$CÒûIÿ¤¾20%GÆå �‰} `�#™Q™Ò T�d”ÂRº$¸²Ü ôþ’Ÿ'Yñù’ i”|>•Γa³.“ñå÷ÈÌ«vËêǎȺ½?Ëêð|Í!w_Ú˜×~ 2“-¨< yÎÐL�»†ïıìŃÐÏ`‘Mµ=ͦ\9�ç|<뇭 ÙïÄùº�÷»~“áðþÏé–à´öƦeÏn×q3fobñ÷7eÎ�ÃÙL¸‰}L]zí'˜QªXôÀ'(E> c\ï4ÏÜ}];¶?à,‡ß'Ú:lCÞ€9 ÆŽ �ƒ2h"JÇw¯Z/¯¼k2†“õŸá°$“åø&û5À�÷ðøC¯H¹Öÿk¼òÎë£S³Þ)í{b hàÙ�Z}’רͨ¡]bþ–Dd‹ôkÎBùâ)°ÐŽ%7@¿�ÒG¯¸^B¶|¦@s6¬5  Z´ß/ð­™‘×Oâ�Õà~¥7z*Ó¯ÙKzefŸæZt�®�ò}Èrx±¬UyΟ„žÂxôV&®½O&]ôz A²‡Lºø!�}ã7>,cÐ'˜Œ}S/yPf^³GVºyUXà×Z‡²OçnŒAšé£éHY  Ó¯Ùz@³ ½zš DAˆ²Ψô8puçr– –”ýpÖB®—zMž¹a0r¬¯­ïÈ`ø ‰M’�=£BiÍZ솂ó’^ç^x0 ΚaBz5GóLþmÕ®Ÿ_ �²¥‘ùw¼ÕkøÒàKƒ —í83[Kƒ±ü3rÖ;ýÚ}Ò«F=×_ƒÀ¦£9Ï*(”Ÿ6àb3Œ€¾OSÃHPèñ�^Qz}Æœ|œ¯÷?ûnñ¨qÏä5o{|`G Ð6iõ=ÀfmaËΑ¾Mdû{”g r~‚¬&îO´í€ôù)OæbÄ7möÂýK‹¶À…àÏüà‘]RЮ£kÀG…§É›w’ùw¿I3"<'ì/]œõ9�ƒ&>lÐÀ¯PÁMw 0žÂ •{ï"—A!({`eæuÖ|�¥eTp¼ÑÓûUÀÐk'M<šù›½Ê|Hx”hÁú\3« ÀYg.Øó†É B”õ¨À匫÷Êà¼áRvÙø¬œß!ëó&J—¿‡sQ>«HlÓåÛ÷½ÿ›»Ž/{ô;'!PçF¿fZžê¡)™AKˆj±çW)BFšÒ¤•+�¦#†ôˆ’Þå)�!‚{+§.ý¯a33üé¥+[ð $µ¡Äô9þ?VÜ{õõwômÞîçÒž±Ò¿mçï³ê5¿§°e—ˆ0gdû{ýP€ ÷"R6s tzcÁ,4;ÈYÙ­Ó€M°¯�03ØÉǼ¹íQéƒ2šú޲œÎU(‰PŸF{FéXAG�2=F÷áQ{&F ¬²¹ÚsÊ*3™ÉÙ(Ð)p(ÀpßÅ6÷¹Á¾¾r¯Eï� •�DÃa©Ñº¿%*hÆg 8“Á#g0Æù’³1$¼ÔJÙcßÉà å2"§HÆgæHv‹6!М%»y›Xj¿ÎYuã×�þ •‡pzn#ËŸü ×®2«í¦÷ÆÇèϦ÷Uá²ÔĦm¹6®ÿwoùÅèÓ¼ï? Øf»Ÿ­¦ ÁÁ³kÁÙë³°úlîµ >oï;¸3£N“ÏûµéT‘ߪÃïYõ[ºJA ª ‹@({è~ÆãJ`0C£•²0²¬æß=žãwt*›% ÚgN!½šQ1Iòê=;<ó2¾¹Ÿf PÑyV¡CÃî³`d�óï×çüÿØÅ®osdÁÐQ�÷kÝéûQ1É¡œÆ­>ÌiØ*¢“ÙNlÒ1kòUXS,Ly.í+Ÿï: c@Â8zN°14isÜcWÝ Qç\àdgzàÆaÎO†•ÚÊ,4› ^ e:4\©À¡Âœš1p™ŒÏeàÑ}zŒf”MiŸˆõ×øztìZÏmÜfö ö=#Œ´ÈvJ`3 ÷÷7‘?¯p�ë×0ãÌOmæÿ¼ß<ÌìüÉÿqV #QªuÕß‚�30�\Üb§‹1÷ˆ­¥ÁŽ˜s–CÙ€�¾¯`¦ß˜©áOÍ{ꛈ(xàx%à|CÃ.Ó²œ~¦±jÖ÷´çÀÉ@jçc40zk 8Ž‘†L`hÄÔ#³  ª žÃ×ç)Ûù³ËXÆ®ÀÔ~8m†� }S +Ö<þ�ðLКý!«ùÒ•ÝVóuI �´äL&·‰2Ö0¹`Иå’Ù²½ŒŒM–ÁÝ£%D˜‰àÉn|YŠ_Ö†5üþ5�ÙˆÍLì1–j÷Ûsmåâ™{·¿ž×¸Í—…º…€$€RÚíy�ÛFi‘í$û5gœýŸ(¡Ý &ÈN¦ÿ¾õ›À…÷yì™=½ BÁ¥:Ý·šhÑçWsþö½.¬%%³®�r²CV¦—.ÚZ"c ÑGfl) €�a½ià3»LwÛ¸Ç>¾ÃV°à9ÎƯFAï�z=ãÄÉ—û8“âÏÇ~ -…qFå^+Ë‹ßg¯„í­Üý«ŒYsŸd匒¡ó®åü;éëèÔ&­œYb“¶ ˜?ÈtlmæÃ�ô¨ö˜È¢�{@ú;P`ÑcÈ]�3¯? Y£¥¸K/M”ôÇðæ3æÊ‰Wß«zrßNúûú9‡íÙƒ�)·QÙ‹Ïq�µðþ€²6}·;÷0ºWò‘!]£CýÚvþ ªÜ{r›´ùï“]k"[d¾æ €ÍÛ Omy~€Íá­ˆTÞ3ÇxÂ~z�æNZÆ lžWC&@Â^³ˆ ¼È[?etUþ¶ÏMp­õ+ȘÙý¶Ì½Íd‚ÀB'÷ÙõÓ”¹ˆ:l¬8sÁk£mF‹‚ˆÉè˜)¦ÇñÏÆó5šÙ�GŒ�¥ÑGo¼¶8°«¾è&éU«‘ô¸ –¤GeÉ4Ì.¥vK–,”±R ¾� àY³û'w.ƒ‹U6Ðoíq鿉aÓ9óº¢�3¥¸{,|jÚJ*´ï†G'È—êºiÿS�QñøÐ’̱FŒS£ÊF>_Ç �c‚÷3ð˜c>Ûuè‡ÕEc¾Z5hLhJŸ¾eÔkz°¨Uׯ'»ÖD¶H -%´ÏaôÊlÚR†CBäÈÞ§­Ñ™Í>8øŽ€?^£›Vµ¥g9ø£Ÿ?h¨t?ó'ªÙíÜê ±îp~üý}èõl9m-��‚gŽ\f›rýÓ×>Zz�_ÝŸåtO€uö°Ðbdx|Šdõê#3o9L ô%à€š±1à߉Þ'ÒêswîÜÛK^�¸zv—|èÖå6o#[ à¨Îv¶…â¤Uø¸¿K™ž‘á×~ò“ 8lÙ̆¾oë³�ÿZ7pÂ× �Zد$TØ¡ÇkÅ­»5<Ùµ&²EÀ¦Rï_„Á&<»2¿h0ŒÒ>ð5òù¹ /5Z›ÿïÌÛTÁV 9°)•Nµù|€M5™pÑ67¹¯ßò©aM‹¦©ç¦™öhÑ­,óÂL6zM £i-Ÿi† B˜ z,S°+oÚŒ…'çÖäMg†š‰¹G.2©Àf ,FóŒ�Œ³*}TÐUÖ›sêÌJ/�êv‰­†¬¦a3™9¸D†$gÊ€®=¤(:E ‡Ì“²ÝRIޤ™˜ÁÔfKqg“;ž;Z»~:ÃæK2ì àâêjÖ/×LZ*/™š™Zðþ€–�RNv­‰l°™ áÃÏÓ5�oH3ôC&™ò•>7`cJaÞ°Êï|"bkœõòÀÜ�%Ò팳¥+¢Ûù5eêÆ0ñïÀ†¦á­®—‚‰˜TÚ0/¨¼èÓ, —å(8 b°â~ Ϻ0[Ëuók÷¨ ¸±¬d„­sŸe…ûŽJáørˆ¥Ö —<Ý¿Wˆ—ŽHNs6ÝY(gÅ·î.cÖmwàÁ½©Ê‚¤Ô‹{ÎíS�5Ÿ>ç/S¯Ú+)­º¸yšlèŸeÃâàáMWÚ©õ_,øg%ÿóY¾ ãm³ß€�$OÉ,À~…MôM3–ÿtíäE¡k&. ­ì7òãa�có½‹Ld‹l]Îvfi·ÀRàg'�Ÿ–G®¸ÎçWãéÇxKdžLÆì‚ÁÜÿ<ª]mÚ•;MŸæœÑ°X%gº˜VVH&)~½7ЭܿU_V‚B¹fXTr¢ŒH³ÍJ(«1s54—àşÏ`CFk•˜i|«Ck° 4— -�©î«)»EþºC’�†|AÛÎ’R·‘£7—DÇËPëKc$:[Á$\¹óGÊR¬>›‚ý&õÿy–{[JqÏi V8�HÜ<ûuêé>  VÚ3ZŽì>t 2ÿA‚• 4Ïñ&³1àÅààœ`_ƒýŒÌ5*@¤¸}Îêß/3;tåøù¡K‡Íüxyî°œ“Yk"[„p6ȯ¦4h|rùîß+÷=èÐØ¡0–®ñ¬§¶tè(5@s™ÍȲۜ¦Y9Ó�+7Ûu†FeKxô‡3 ¾®ª&py‘ÁI?Si^ä¹ß£ç²Â7òY Íe*¥S6HA¯dÉó+ n¦³ozVÆÀ3?ª�¤7k)ém»IáàÙÐ;ûÖdu 6 rü«%(PrÿˆõÜ–mûeºÜC;A™X²Ð�Ÿ*ßx1@EÙÃóÈÖøÃc´fgg‚¾ðùÙeö¸àŠï³aÀt[ù¥² «Xä ’9)ý¿Ÿ•ØwÝÔ„œØD¶ªÉ›ƒñuêÿž=ÈÅÂðÒÖm6ëðë£y¿1�úLŽÍtزà™;ï•x€LTp �:ÉN)ÂÐ^¼£ù\�$Åù#]™ffpÜ÷�“KYàOÙèœ4mï†gdØÐLzK™Öù^Øõ\;ˆ©ýKÖ(AÁ¥3½}>߃‰µ`@b Á#Kíh6FÏÙAÔÎ'a?±Ð,#³øþ¥*Ü};Ã6 q+8�>(ëá`:ÿ–g¥¸t®öÂ@gÿñpA}ÌõÙ¨§dT­õó9»a{îÓ°¥5ŽwÊÎI ;÷GŸ,4d5ÝäºY \ �Á&xVÅ…§Ÿ£�žlI#€öl²&ú˜íØÌ'°ÙoÁ¦Šc,ÊÓ·l‘Ù©²(w°ÌëSøãÌ„¾·ÏHÈ‹€Md«²_Sq(µQÓß²1ïзM{yãþ‡ Œû7v²aÁÅkíÌçûçtì1oíx Þ'Í]ϦËig:tlR¡€�Õ#Iú¹ŒFæÝõhµ?¹¾ O•3Àh&ÀŒ0~¾É•¾X ™šæ6‹àÆyeçNbTé9Z’«,�cõÚÜ#‚dfØÔŒ³(žõQPeàÀ9”² ØV"GWf¦GCKlN,sô¢¤()WŠ{ÅJvŸÁ²ìá/Ýõʶ*Ó<%£ÊdØä‹dÁŸêϦ Íó<„Ö9"½_ÛçY «ï¬´ANps@G€ Êg³2ûÊ·O=',Èø½l< ’7…/͘öKšÝÒÁŸñÔõ›™ŒÌË,–YÉù¿ÌˆËÝ:)*ã?ªZk"[l2@{~!£YË£9�Z/�Š“¯ö>#6ÛðÌÀØR›9ÎJpöÈ„Ó8öÚ{rè¶»%³Uôn.”˜ú-$½›èÕ¬| ÊÁ�ÿs1ãJÊ`t�Ö}zœ–¹ˆ¥Æ}]ôX0SJ3V&f›èM.»qô]Ít q€õÊôõ:S"ãlƒ´ÜèþɆšz, 4íàJ÷£Ÿ­L1«‡¦™ÎãßJ¿Ü‘2£x¨L0\¦_úÀð¸û],Ûõ›Ì¼ùEW~¿L½r·¬|ê(•" {NÕô5©þ,éϰ ·ü˜)êÒÛõkJA¸gÕz̉¼Ë‹9ƒ�G†&h¿�“pdl˜lŸ™jž,�4³:tóÝ2®wŠLIÈ–I}™—sÏ´øH-²U 6¹�{y5£yËyÈj&gæÉñ×Þ 073)8ƒÙ”áØëùGŸŸÀ¨w|B6Lœ&MZ8–hÛÒ¬´8L¡Ÿu™”=ð�¬;tœ”šõ›»±0‹6-žZÂQê­µ\vå5p6‘!õ2ô³t*Ó Y¡Ú]ƒŸëçrÑý‘f‚Á�ËOf@ÁsD\®S@â¹.§‘¥Ã� ™xñ#2¤`´,=Q†�)sÖϪ*à2 }`îùÐqÍ uÚ*#Xi}íî[¯‡ìjÜ‚ë%·s¬Ó@bÀ¤¤t,’wŒ†à’Þó÷füÁD€“ž�á÷½Ù�7ûmÉ,ð³Ìóïº_†w‰•q1˜‘ŠêóÔ˜¬ë#`Ù|`3´ç�Я e4k!+‡�Æ‚\ n6ÚsüBœ\ƒ¶çkÙN_C&C½ü:€Ú{,@ÆÍmÀ¡3 2(}` =ã²Geݾ_ÕVÀÐx±°ó¥B�gÉ*;QÖ L4ÍL”Àpû8»Ù¨Aߨ©!o†BÙäËe"îx3ÍoË~fÖDAƒ³íe0�›glŒ‚}†]Ð×ssõSÉØ¥›eìÐi2jà™téP¨p="°D�f`¬4­³ö\ñÈ’AVzѶϤÚ ”Ïz@šåaè -é?H~8ø¢É@ª´ôô]<^7æXÞo{-6s±ÇØò˜ ±äñ¶1Ziö:OߺEÆE%ËØèTÖ9æÇ1Ý“f.2‘-²u»°z˜ö¼)®Ný/ ý�kf/t<ú€?h V1Àf=~‡N¾–-�…ðÞ'�ï“K'Ïܧ£:C5À=ÆÔ¬’A²òž×äÒg�iö‚à¡Jk9  ¡Ç³'ŠnV»GÎØPÍ ¿fг‚‘žçö_ù‚&S&S6ÂèŸQyOÁ�]@õ\«F­A€Á´ç�ôžDìÓŸU3 ÎдlÆd‰U�}-ÓWÝ-óf­–aS7ʘ¤iÉm5©ok?Fï¿>ý îyqÏ uœ{5.cš¸ò.)JÌså³A𬕠w—­QbEP¿„˜jö}{ Þo®y’%¸@©z�ãÞNVÖÆc+`cßu·É¸èd™˜˜Ýç³ñ=SûWµÖD¶íÌîç]¸=¥Q“œaí¶_r¥„Óu~ Já'•‰dpÒjŽÓÿ¬©× ãf3PòsTçPèq~ éW2Ç•ÍÀ.ÓEš¾Ñkó�eôuå>gƇ†'ÿ\®²21 (ÍtÜg»×/¸À5˜=¥‹?ƒi’Ùzçè½ëÏjtÖx4»±ƒ1ƒ«p™‹KŒ•©â²ð¶äÒM·HÙ²+eÚå;e2$V�Öë(°°âóJrUðcb„+S/Úþ…å 3�ås z532òä•­Û ¼@Âï[33>Þ§á×ôg8 ö=[š¶Á× ´lçãlyÎf}�nºF&ŧËĤ¬Ðˆnñ�è7*x¥‰l°9íÌÿîU­æÃ`¢ý’‰©î,è¢íÖ›Jaþ í„1{Þûg˜sè5ÉÙ|"ÚŸyó�Gd#9S7s™ ÂNÏõdøìËdþ_ÌâZ)ó@h…³^|¹oáα3(ª©†Ð÷8Kâ÷µ\Ç%½'•X¡ixã⩌6åoó ÊÜÒì�¯£Ç›`Y\ƒÅ9ÀT—�sÞ´ý-½×{ÿÂíS2{ã6YvùvÌÏ|PÐdm6Î PÔŽîK5×” Ÿ ÷tp¦]w@ z&ºÎÑIðfJ”²¢°Ðž÷0¿A‡ÃL^¶™± 0 ðF`É9èÿ ÏÍóšÏ3†!<^>y–Œ‹K“™9Bc¢S>.í5ed÷„HÏ&²XAŸvæ%4l¼ä€0íÙÍØg7º_‰•­•%;>—¹·–²'a[¼Ïçó5,9AA’iÐÊ äçjc 6Õn¸tïŸR2~�duŒ’ Úu–I›ÝWßÄ@”qøØf†0Àaß·Ÿ¡ÇØ/iv4@Ï·ï3d› l&T%¸¾yò³†;N2…–4 Bl.ÆýWö¡E™šÔÄ #2À‹.£p0ëMÁ�Á‚egì`)…±S y?÷”´ßÄÇÛß—™f{Œ8sÂqÜOÒ2Ú²äÁ/�ö2 C]¦Ÿï�ጇ%jÖOÁEÿ]•é¶ìÑo¤ÊøâT Α(£ÍÉ.�/wôDZݳ`p±@b2>ß{Ò–öØàLˆ_óy!3€jæÞ‚ÀÊ~αWÞ•Ù} ehïx•(Å{|•Ó°uqUkMd‹€MN|ý†ohN@„SúBqÿM·K(€Ç�ç~²%¸`Ó4ÛËqÈÛ.ºÔ1ã”ÚœP·¡Ä·ë%óï|Åe4ºèÈh†`ÊLT"CXÿ—u$ÜsawM6åZKCƒdFFŠÊV–†›ÿ›”,ÀÙ •øôÛ¾³æ."6šØ$@ ¬J³7²`p4Ü 8U(Pp"ÆÞA”Í–a¦JÚ¬ŒÍà©?seMú ÀÁÙ ^sö¥•{o¾Æü͇eDÿ±Rر[X1ÀùÖl3I~þ5òôM㔸�6 Àhÿ¿±`a~~Ô`Ð þ™\Fc¯aAÌ–êì=êãO‡^’Ù9ýe|R†ŒKÊ·ïþivƒ§ÎÈV%Ø ‰«[ÿud6'’ê5@35A8°1q’zf¼�ÂãÐÉñÁ#»drFNØ>Ø•Îba¸•Ô1FfÝp@ÕÌ7ݵöïWGý¾ž_vïë5‰^L=’†1Y‘e¯€>‡ÁFAˆÙgzuj�Ȫ8sF£€Á´kf­•´¸_Ã’‚¬*,0õ™¯CïQ&Bƒ«Jœà!SªUpáÐlEA„Õ Õ¡™V…LºäéÓ5Ñi¡ é-ãR2äæ…Ëœj@0Øø1ù¼€ý6û©rq·‹¸_Ó/Oã•­ :ÞüìU9}~ôè™–š-%ÈGÄ$…2µ8œQ¯i¯ªÖšÈ›eƒ£ã~Dfã ­Æ&¦Ê� lL&ãÏ´ÙK Ñš± øãð›²YMN‹Ö®O�ÁÍÞ5ȸ•w@×ì-´Ö¬l“£ã,Í‚Ècßuâ5g8FÝ µœfLÒÈ€$gÄð\�¹”@ñ\Á�I.ÜÏ å.Ô$ù K[Ÿ� š7gZšq©o0Þ'†›ö‹˜lAÒ0Ì,cÅu–Ôa`´²2\ SÀ2äÖ¤«¬Z ƒ¢O;e:}“ôiÙQ²š·rÙÍÜÂÁ.S6Ívlì~:Ïš®yì¼Àã¿–-e€àY1€Žó�óÎöÇeˆ£âSdXTÂÑ´:�öä7mW­ªµ&²EÀfù¨´ôßûuî*˜³k'Cv]yC`fœj3àx@ˆÁH¿¾øºl^²Ò)ó&Ö­/q5ëH¼êsóÇÊØ(›Š6m̳³%$­ås¹‹åYt¡GhÉIŸkðûZ¦ãþ•¹ˆ^Íþ0{r?³»Ø³_=:ev¿)“©ø³K®©JÝ9�pc¯k™¤v¸ÚŽˆ§oÛ"Ã0»4:!-ܳù)£^³û³´øÏªÖšÈ›ëûwéþUAÇ.’„ÌfÁ€AòÂí÷JHÿÀüÆf�R3z\0Q€ì¡Ïßy»»l&¶FmÉÀ7Ó$ôiæbH�¾Q+³JÁ…Å'™‹çº0Ú è2gýÖ¯35!<)šJÁ‚KeVšß~Ó÷™®‡¢ˆñÏ×c캩Ù3»ŒÎ‚3›ÁhYª\É \‚ã2i­i–¥÷¤÷¡¿k-o±¶ÜF¾�JJ Ìd:´?í¿¸Xá@Åãö—í×÷´dÆ ²SØÿ—Œ»\úwê)ýàÆYÚ3F6MšAªÁ™H°¢ƒˆ×Ú²0­/L`Ö`³�E‡ÕôyÚœ¢¥4=§ûxý¾‡d8JhCzDƒ^Þíû”š îïS·éUµÖD¶Ø\šß±ów}Ûu”Œ¦ÍeÍÈqðeÌÔl½˜V¾ÆcÚT¹ÌÂã�ËË% QM8m:ÉȅׇÓ gKjtë#5¥Mo‚3žµáAO÷ bê{¡ ††0tk3B™–�Ò yî†z=˜T"« h¶Ÿ¢™öó5H%@–‡*Õ“€‚²=ýY Øù÷ËÄîYŸ h”Ê4K!+h&h)MÁÆZ BúZ³¾w½&Ñé2&±�LÐ,ì?XžÛ¼ä€÷ä‹�äÓÇ÷‘î_àŒ¦`ñMÏÀ%e^�MÇ«ì|êa¯gÏ~öa(>��Iv€žÛªý»‰Õë^–V§qÄË&²ýß[TõZÿÞù¿N¿3®VÝ/ÂýšÌÆÍåÊés�±”öò‰pz˜48ú`Ù»Án‰v›ÝáK“×WÊv|Fƒš´�šAN-1ƒJƒ¦Ý‰µ¦¯uB¯¢Þƒ γÒ0ZÚÒ°Æ^\Š¢r…#PùK¯í‚ök°FšÎð(0ð9Lgæžϰèbo%nÌ1îºìªY†!ƒ³Ì–ËLÃõhÃL3J�&S5'ÍýÇ¡\pPÒ1ÌYо³”öŽØ ’÷Þ%·-Z!3²òeÃÈ òÞ¶Ç%D�mÎkð~ŸsgÐ�¥)§™}z¾8<2:AƒÕÁÚ…¶¬‡°ä ��^rµ³k(éÊlÚê�äõgæ6mQˆl�ºh§!³Ù °Ék×ACGjû†ËåÏ—Þ ¶ °ÚgAß‚Ìù>6ÛXæôà(ωµëK¼i¦^µG_}ÔETĦüƒ×¬w¦À`J=,ª..ô|>N³LjªÌVcuh–ùwÏ)à ²�>ZC4Cq6ý#¢Ó¹,9c˜yü;Ðß ƒ8ÂZ °–™‚†þ\zŽ2Þ˜\Á¬3 ,x®4ç@¢€{ÿ ‚Ÿöp8CªÀÖ«’�)EÝ¢dXl¢l˜·@î¾ì —ÜGƧgÉ´´lyÒø'^{ßГ=[B³Çy–ýÖÍ6Óðd6¶ç£á-‡ùÕlèy·Ì/C‰2[ƦfœH¨UÿÙÄõUµÖD¶ˆâóÝÎ>o[r£&ß䂹“ õ€G.¿VŽ¢™ê·dÖ”�1Zg'ð´âõ÷°XåÙéV]4f¢D�òì.¬%ýKç¡gó'õNØŒ3 jŠSÿ†$\¨—¢ßÒͼJˆU6iSŸ´Ô¬u²¾O4b›%ð½é#€µCРko2¤ëõ¢Ààö1ý›D?53Q&�B�ÚØ5ëçKŽ•eh”ý¦YŒUDP QpÑÞ�fOˉ�¦� ©TȔ˞�ìîI2°Go+cúdJ Çh¦÷í/ÓúdC�â�Y�}ÊÏp8ñ‡ÿX£0í13;YjôÉ�ŸÅ ö±:�ÿ׌Ÿ*£æ#SCÉõ¾šZ«azUkMd‹€ÍyQ5ë<™Ù²õ/é d`bÿ‘Ë®‘c~cà@xÛD@ærô•wäøëï11ýÙÑ­„½ARÝ®”Ö#<ÐÙ¬£Ì¹õ]Ô�3h¡¥E›€Ázì»Å‘§ìCÄÌ"c13ižSÖB= V€VаZf8ÇÒ¸ (¹ÿ �cY×�/öþVµYÏ×,Ĩ–Ó±¤ :q¦‡Ef²#Ð}Ö¾™ÁÆe(<ÀI`£Tgê‘€Z¾ï¨ŒXp£ôCÏfxB²uï%ù:Ë ”fǤ¤CÃ+Gn_¼BŽY1Π ,¯Ù÷5ü‹»¿©ï§+Ûã,8飯¯ä™£³eó?¿%+KGBà´· �Žk´õ‡©µµ©j­‰l°9'ºz­½éM[OiÐH26^|¥G3Õ‡“@Ú³‚pV lñÇ‹o@І³&;àydï32‹C�pß&g_ À.Z�솦ê©ÿ� ò/¶zܺJä²îÇ(C,¤ 8ÍÀ�ë%÷C¸¿Ã~/š)0H©£§éÓè=Úæ»‚õ–(«Ñ~”™¦××t¿¦üG´lʨÈÒ€fth†Ç˜Þ‚5Ó¸±¯€£™ »oêçâ=}Ÿ�aÉÖ`Óc¨œÇ`µçO1çj’š�L&[r ½4¸WŒdµlíÌÓffÂÏfËâg†}d€Çkpf{:ž¿ÉšªtÆ´€h>?t̽xÉ?–>Í÷æˆ ó‹�îÜdŠý;õøä€úU.6‘-RFëy~µýð²ù#¬Ðl��…iZ›ë²ù˳¯È§Oì“ï¼ ç±3§†+³]7gQ8»q ÝÎ e$£ 8«›aOU Ðæ½‹«^t� ˜xΊœ}(�qÉ‹ÁQ�Ô¬™‚m3+fÕ±ŽŸÃþ Nº>�£ž£%3m𗱄Ì!¶�¦sú[K³Kªf~š±ø¦Þ�‚ÎÒ'~–aÓ.‘‰†ÊÔ¼~ÎZ`p�hÌnõ”©š5%£ä›}ÏêÂëË,L&4[Ì. °èðôˆÌqš†ýLûÚÞk ‡�†í|÷Á'e˜~Å]zhºýŠþê!€ÍÙU­5‘-B{>7½Eëg¡¬J¨]Oú·í(ÏÝ~�í=“ÕxT›i¿}üì¶-+×ÉZøÒ‰Â]Ùq”­“­â*ô97ó+ jjðï–ÈäS"šµ°€¨~+J¯Õ×”1qO‰{yËžøAFÌØ$ÓK'¢l–!…P¢À‚è4ÒÆ÷É’µ#ÆÊOO¿dp›A0SŒ^#ª û‡*«p¾´``îË6Áí6‚� O�Wáv:%%†tI’Ú É—1ç׸4©VƒÈŒMd«lz¢/ò¦õO¸!Ê&Íý¸BÁFƒ2 *¶Ô&Gö<-«‡Ž’A]z@†¼«l]µÞ¹üQ©Í°Üþ8ü†\7{�$aæ¦Ëég9Љ‚òsŸÄþ²äž750Rú .Ö_]Øu±¥žùËl$ÇLÎ|¼d˜}FóšÉ0}šïYŸ³Ò´Î7¾;:§C37¬~ÀÀK@Ľ.’ÉQ‘Rº^ÀÙY“¯ieqÔ;‡†5H”af®‰`0Á1!j/@€£¡@§Ôg¦b/{ü{:ó é—�-0tr5) Ë€®=Яɖû×^âšÛf17àcÊe@ ,�Ÿºl÷Û2–Í|4¼’6>ßš`ù}´CØêйûš›a–Ö ÞERê7>waí%ùÍÛGhÏ‘­J°éÚëüjï¡du"¡N=É�ÿÇþ›ï`{ºßJÒXVÚ÷_„�óȼw“Ô†�%½aS¹·|ƒa©àA¼½ý1™”–7à鬠ágÓ¯xª¬~ìKboQFcŒÁØß…õÎô<-Yi)ÊØ?+P)À)CK‰Áïñb¯¯9K"{jº%ˆi(‰�l Ì�'�Œ‚ªu÷t×u÷¥¡‹¼q75ÄrÕkWf’[&šáµÛ°ú4 2x ½–5iÓóñœÁ\ï`óƒŒ.Û,ó†�…–!ƒzDÉðød™QP(³A©ÿø±½~ÑË`—YÙ“yøæjüôç`�°@ÆïûïÇ¢Çþ›†eƒ‡IVãÒ·MI­×øH¯s.˜î[k"[lšÇÕ©ÿvj£&˜Ü¯ç†:w_w«ømÌû¦FŒÞ‹S(íå¾e"srÆlWNŸ#Ž$À} j®”·}Ãe’ߺHç…3:q˜¿é?d¶,}ð3Z¨u1féZ ‰n¬e7]Àõx¢0ëüŽéyð€¦ËfŒ�2뉹R 0a†–LY�*grfØ“èѺH+Påe#_Cý&zÏÜ—ãðšüg¨ÌŃššáÎD”(À38!ý�0ðkf¤±Žú4Êä[µó'™yÅN™;w£,5V†Ç%Ɉ$('¦8J=*Lqf––8,+ÓŸ­XßÞï)“yÊc|=¿ú€=Ζ˂”M ÷’ ÉéøÙA@Q;±VýϢϫžë]l"[„ _·þ‹�©ÉhÚRúÔo";@Ù�L¯Ý¿Ôéƒ7ß)»ôph±5ë8_š„Zõd0Àçó�¬å€8ùù¹Wå†yK$‚œ®\ÇNnÞh'M²þÐq,J–µdíxðÑíc#4^ ÝkΈ¬T¾ðÄ1ì#ÃC§Öφûº 3˜è}®7ž1 b Fʆ8é=)êK�vš-')°XáLÎ$”Àzj\l!à¹?j®›™½ÿJšc®±® e�0± 2i�õåXõ€˜núÈ¿Ÿ€L…€A,µJ¬3uÛTz33Î8kÓ0ó8¼ ˆ¶¾+³.yP®¿üÆpùÌõj&fæÊȘU ðHÌÐÏÌXÐñ÷OÊX�×p f�,ió6g@ÁŒ9V¸hüT÷¥4¹~£pÅâ/¬[{žsÁi‘Õ4²U¹AÎÿß{Ÿsá0Ò¾ ÏÙdáËæ²U 6>!A£�Ö°‰Ëb¢«Õ’„. Ò7w¤¤6i)È œMÞýþ‡Ý9z� oŒ?:,›&L•`Àà:nà³7½/¨!Qu›JNúYõèÊt‘ÖG6\ãŒf ´è²§>² 2³ßxh’5õXbÂ13®²ŽQ"à ƒ‡>ù=¾_Î\h‘æ^�5|SÚµ‚! Îcp±ÌîšúY|¿|?ìÀ Pa’ nâ}.5ÚáÐÕúD8§_¹[6ß±C¶^{ƒÌ)ŒÁÎL�ÒG.�2Kéöföô7¼j¶§az#žæ½ï¾xŸµ°Ù’_=Ú†íO}émYV2Rú4lâ¾8bÝø`3+¶F�ˆÚsd;)’ÀægŸÿàBÚv–˧Í6s0xnÁÀj›É;;žpΈá,© ]gInÜÒùÑLX³U[trý€„ëÝ\7w‘Êâ˜àÚúÏO¿,Ïböç`õkÛÑ�˜c¦�}žëãàÞ1Ö·–¹×ï#·MãyÏÇ•{*ÎY�£PÒÁAž{áÅY­œ](ZË`âtû˜ ¯u¶ÇÏ�ú�V!@6Ø0Y€ V³ÌÊøèõôž6ÿ/z¯’@'7îéʆx.†Aà � õ~XøÓötˆ ಡ=ÈìëÉ›wÊêù«d²šañ‰2r*{®½Å”“@â$)½Údh8l³ß”ôü“_9€Â¯8`Ë~6>…Ûi1Ì aâþbÝø±ëige�ìZÙ"`3}›Ï0øv"·E[¹dât7gà bë¼¶Ïòfå£'„gu\ó0 ™L~ÿ ²æ±#²tË«’м“ôB‰î‰�¸{ÿ¡�Áߨ�²¿ðš‘•'ù�‰Æ¹ÇJS¢@/d6½ÎGfS³ž$vˆFsø dmþÐR–ŠK’W —“ðÈ}VhÖaNËLSÐá Eûõ³ƒôÔóêO\7„×:—ÂYÏq N¯É= ½„þ¬æóh˜ñ¦`ꜙp9�KÝ~R8 sAîµ™§ą́œÌÒHO Ç™ ÆøÜhðéò'~”97='óo;,£&­•Ñ™Ž02.QÞÚö˜É&|3*~é~/0ذ¥éSÑK3ÆnÁ€|Ý“7_³Çï¼ú&ÉhÐÄ•ÇóyÝÎ8çÃÎÿ}F£È*ÙNjÃËèžç^ø)èÏ'‘¯;ɱÉd‚ÿ£¸pÒ{oØ,©õKø)øƒLEùlÑ�‡Ý¸n߯R0p†D×mâ4Ïắ£FWhÉŽëÒˆÏ m3":Á‡ìÅ� RvÑè~ÎÕ¸�¤ÄäHÑðð1Ù/+þŸõ‹.´ (ÆÄ‹|h¨/À224ëAVÒ4SH8K@°í´ÂgÅë :߀€’6±²KæT–“1&gܯâaUìg`46¬tÀý!=W�@iÌœ¡1pÿ†Knj#€× D†Pù:ž‹ZI „Ìöá/Ë’¼X8E†¥f;°™ 倯!QÜèçý�öÌÞ¬�3Û´#ûzœ'|–V4W¹g䓾ÑýNÛpf沚¶p%4ÌçýÕ ^XX?Î<Ùµ&²E2›!˜cù ³0' .›¦Ìd‚€¡9ësŽ/v”©¹(�¹o<W¿™Œ+»U.:tLô[ù¼[ž‘¸Fm¤Ç¹:°‰«^[æå…³Ó°ýóWL›í€F³™®gdšu’�ãWÉÜ͇eý¾_qýãİâoþ4ÓBTfêuPSŸ-�Ý2÷3p¬•¢ÑEY5ÑSŒç{þ©v ÇU�v™–«Œ8¦•ƒ1ÆlÜÈ×P`áƒ)¹ïÃÆsš%)`3xéý(0P_…ú-úósvã@†²=�ÊdZ¢ÓcH„“A A 6÷|÷o¡ùw½.yI2.#[ƃ�vÕŒyäÌé³Wög¶‘î-K6@xúDþaQCoÔ4³÷oöósÛ«E|»ÿy†¹%˜,:°Áÿ៻ü÷™¥]Ï¿0¢ÙNlzœ{þga}´T(?�…ÉÔï`”‰ÕäÙ}t××ÌZ Eº èÓ®aŸ�í6¹Á¼fÏ/’_<]z×làÀáýë0l÷”´ŽþÇó¯ËKWI_P¦Ñ‹Á@§Ë„ÐÄuŒ•‘‹eÎæ—dåžß´Im\Ä:’’a)ýM•Ü2�Ô? Yµh¦2뵸ÃY€¯}Ín*'¸4†ãùsxf†í®™ÆŒÐk©“¹L‹uØ4‹1 |]Ôäôšz Oì[À±†gzÏÌ&ã÷5Ëa€’t¿‚gGVw���rù“’Þ-36Q2ýÃ;ÊÖPéË«!fŽó ;rƒÞιx3� ÁJ–‘ÆÄo™Ð‚•?kâcžAß´˜ ™˜yKkØ4„Ú—á¡ðSZl"[DE Ëig½‰o,'Â*C£â\ÙÁôiÔœŸÝ¼Õý§Nª×@`/-Ñ�ÛÊÄ�£äQ�…†}òAMÝüœ$¶ëå²ü±ºŒ%µyéJ0]Þrßúa´$Q H#xEÃÓfôÊ;dùö�ä¢ý¿kOƒ´ÅÔꙨ¾¤+Fh¢!ºeâ=ŽqK¥ÖŒÆ}ß›»žë�؈`’f*@2ÍY&&h¶¡Üe/ ,Ï…‚³ò¸ÂÙÛ7[Å×—¡¦¼f^ &VtSçoÖS9Œ”83bàq™ŠªN¯ao"¸`àq ~\Æ®¼[rz÷‘’è8Œ¾à�o§ÞF€­¹Y”ƒ@Ó™^Œ ›YбÁbŸ¾áO f¨qh¤ù‡Qu´áŠYó% #éÍZHRý†ÇzŸ{ᎨóªGJh‘í”À¦âUÌÃM` †Ãág`�œŒ)Úï/¼�IpøDïÚgѵÊ�iËš½¿ g UÙø¹c2vé�U«¡c¥�Íâ@§��\r•=ü¦ùKµ@ã2Ÿ¤&­uzÙ£_«Œ.‚Ú¨§>IѸlF÷3-™µÉ¨iM zZ´uŸf8�T�粚‚Œ†›i á¼ @¬·¦ûèÞŒ=ö³;fe7NÎØØ‘ÔèÁiŸŠ3 ý¹ P‘,³ÏX&†›YL“†1+ùÓh¦§™ ÷\´D¦ £ÄC 9Ëj[õÔQ2±\úÅe9M´!P°xéýkþ:ü–S4ß}í­òÛ ¯Y °Íu#ŠXbó{ÕØç|®}®Ç{¬ <©ÁÀÅ%8¯þÚOϼ$“@ÔÉnÑZ²ñþW¿v=íìQ§¼ØD¶ˆ�4Çž�¯SïOüI_è—Þú•Ê‚û5ÏÝq¯ŒŠŠw‚|8¯Ob?YõÄ·Xðl/ÂQs_À‚¶÷gé?lŒ5%±¦ãêƒ�v®û#Þ Šê'�î‘Éé9nøŠápn�S/}”èÈf2ŸT˜µÌÄßÔÉùa˜i4ì¨ `šÿ&¼Ï™öÌjÚ …ƒ¬ p,Q¨éX"�®š%2�R‡UôRïCŽi®“ô KççSÓË"wQ<מ�íµpg5yÒèñÄ4#çM.•)0òµÕÙ“²#€Í ‰ë$£c”´ï,%›Þé¨üO\q½î—,¬»D*^}Ïf%Áàâ�‹±YKèXeè`[&S��óf€"‚Ohô-ˆâ&@µ# ,S|©<†ïG§\B‹l‘­ó™çüGÏóª=œÚ°éïi�š"Un&‡P[nŽ_ [V6d¸äAdsêã¹�£dÚ%;\¹Lm]Ôõ›­~Ë^±ã#ÉJ.D‰¬¦ô€W�öp28ÿ(ß›ƒ»d f$4.»é�¹š¤èYòà—¢ek/`fHH¸’›ÕšAhÞÅzþ³"»‚êµ¹o£÷¡¤²4!& ˆ4£Q q€Ì t¹;GÁƒÎ·:fÚ“aðår ;VUšy‚¿\ÃXð¬ ?WàáKQæŒg�QàlˆYk¬ÇÇÓ±D þŽ›S.’¼žÉpèŒv‚‘_í}ÆÙ\Ì�eFA±,9^VÃDí×g_5^ÿ/“�œ4 i0Ø0Ïüø…;ý29Uš¢ÙÙ ûslÁÿÉ�‡ú4i&pöý ³5[;ŸvÖg½‰l‘RÚR°¾~‰º°ºD£)Íœ…ÄHsaænoÝ&ƒP€HoÞZÒcsdã¾u1ÔE˜©¼Ø§å¨ YpçË’“-Ñ5êºìŸï" ÈËæ/’;®ºZë7 ¹ ƒœÕdȬËeÍS:GÃßÔÝ#-¬H:›BÇY€¬ô^íP#ö[OÿÊ= %è})°†ô³ØZ7Y]€Yn\£sÉbÀì§ÁP¢~óÂÍ@Azr¦'£ C>4†z¬×ã2˜î3ò2¤²mÁf•Ôø>9RÁ2ÎŽ”t°÷/)<[òºô–>„;'€æÙ¾ñJ™š;P†ä•Y%“¤¼d´| ; \U¯ò¤$al™Ìî·ó2¼Ðû€Æ¥úÏ¡ð¿æG÷¥rrv_g‚ñ‰ªVã®ü»kMd‹€ÍtðåEB¯’ì©ò'ú'6 4Ž�vÅô9n¢?Uñ�[ÉŒk÷aaªÀ‚© Â²(D»¥Ìcá–7$3©¿ô®Q`â$hŸïÆda~&*F‰N9:¦^™¸a”ž�iÏE¿ ó7a6Cèç)}ì3Rû$ÚÉ‚‘¦/Dækl=À€êJgšmè> 6Mã>‹ž£¿G=_�ŠÕ˜�dz7Æ‚�€ÆØ4+Ú5kÆéqºŸc\šcð^¥ûÝ{l)Àçº÷4KrÀR†À£‚Žƒ™Ël†Î¸T²;öØô�áýäɷȰØÉê�.ý³‡IiÎP™› o›Ç÷qÃJÇ'Ó ?EéOfb2)¿Ý�íË y[Mûïí½‡wºAÎT0úâÿ;§?`­è÷wךÈ›4Äw�� …µËŠ Øü#š‚¶–ë^;‹€))Y2þîýáØ—“Z$k÷þl<\xê]UŒ×“ pxÁ\†Yá°…’Ø´=”ZÂó¦½`ÞÇeXÈhtÎÆe?½«Õ–ôÄ™{ëó²zïïf±$*41ÕÌ4¾R•YMÄFãÅ›{DV/ ÷â®ïXišá}Í$}_³’Ʊôjîñð€)ë¥) „è>Ì ùÇ([Œ¨ÇœÖó ,)2ðïS?‡�œËvøî )¸ØŒQF%³5 Tl)}à˜Œ^r‹dw‹—’˜xÖ´Œ³Xò“HbÀ¦(k¨ÌÎ+–Wî{ˆ³<lLä�ØöV|ײ–ÕVTÔ€¦'åÍŽî^}‘ômÕÞõTóÛv<_³î³•çÈö¯€M h}„Ì¢¢7z$éèÛ¼tÏvýÃ4e…;–—K~Ëv’dJ“– ›Þ)ž®,•ÊL\² «.FËŸüQÆ®¾G2A0HnÝUbë4ÒL' 6nÆߦÄÉÔœW];Åɸò{eÝÁ£ fX°¸w�x測Ýý£,¼ý°”ýã]Yóä÷²f×O²vßoî=» ‰@ßcfÏÎÐJ¢œôšíl–ÄÈ È ºþî8SdZµ§’ ´·£�ýÜk�<B¥2ÔAKî­XÏ÷š€„€ŠÀÞ vr�³8îéœÎ2ÊbZ:ãëèµùZNùyÊeOJA\¦LÈÉ—ƒ§J^ŸA’_ }Ñ#ìŸ5ÌŤô|9xË yæklxiͺèsx²$ÃŒ� *Çñy>Ö[Õ?ÿw^�¡ Uä·ë$Y-Z…­B~F mãß^h"[dë|Ú™gö<ëüG°˜ gyÍÚ Î}…È›– ðõ�祤W´k&Ãæ9¾eWYñà'º`’LŒYd‰ZlîëP[ºí#� ’A£–HTƒP 8C�„³„{t=gž–3B–lyMËs®„·êÉdñÖ7�åÀÈùWKñˆE’Ú³�d$äKß‚ RX:OŠF,–!Ó/‘ÑKoÁg=(‹·¼.+ûÀuŒî‘µÑtšž&)Ë!°!B5ûmùLp ÎXˆ"­³6¦—sÍõhD23¸67ò0hÖ…ge*‰Zrö û�4tâ sê¿»Â$MÍ^8ô}R#Ð×|�³Õ°¯+dæÍ/H1z3eã&ËàüÑ2¬dŽ .š*9#%€“à)�Ï�·Ü-¡€ì#˜84f¡÷ÈÑøi×öõ)k¹yÔÞ§ŸýÍ•þm;I¬ÁD«Àÿ¿¯ñÅ4þ_]o"[D#m12ˆ_QFsR2 І8UfíÕhyáàæ-’ŠÅ><“S§±”ÌÜ$3�TÊ2s æÛÿsœñTÈd‹îy[†/¾QúâÚõ–^u «V­ŽD¡¿[¿¹Ì;CÖÞõ¬,ßö¡Ì¼ò ŽûÈï?Q 2Ð+<Çsnué°êZµ‹³XçVx�j�ý=ð~¯Ú�%`™™Q*%˜šuÃ!Y½ûÍd†ÙFƒ¥ œíhŒCml óô3ˆǬ9wŒ*J_áÀŒõÓ˜ ¡€ïB®);Qp¬À‚]áJPkÐóX‰X¾ûw<ƒlâ¸Ëp<õSô‘2 N†òŒP !J³£8³z€¾÷ƒ�µû挩2.yè+)Á—ŒÙøûHë'ùø·-ÄߟwHZ�4Ié’(ý;÷–�˜çò—¯Üc ±7ø­:�Í$,8�r?è$D�«n@Ÿ&�öí oưuf3”¶Ñ—…âïâ<€u"RB‹lÿr)-qý‘aUæ,ü‘½±íQaFN”˜/Ÿ1WRÀKmÜÌ kοý0 GÒâIÒõxNB•ªf<÷ÍB^~ð¸”ïÿK–=þ½,ºï=™vÅ“2fý2vͽ²jÅe2o IAæ ͵žÖréîç„åmÎBŸ'¬¥v†¸Ç3Îàœ…Ò@ò7n á¨ñˆÒ@8‹rê&˸®=p”3.‘©n—þÌúžf&ËÓ¹v µ¬6—­PiŒ©ÒÆ1“‡G+{Û¨=— ‘ ‹¸ìþŠ ŸÈBô¿æá÷:}ÿè&™¼ô6™¸ø»øf™¼æ^ù™«å¥w¿.Ëpüò‡¾t�KùVVí?öÏ •¶´ ¦™‰>j‰Ì Íͬ6L4& X6gg ¦ le�~+#ÁZÌOFI_<ú÷'#æ_' ÿñ¾ä!ÃMïZtÏxyòªM?†gYüž6 ÐQPò sê±~�6É€À†Â«üüíSÏʨ+ÀÍDÀ,íwPžËþ?ÖšÈî<`³`s4¼ÇB&æ‘+®.|±û RêvÎî9±~C×;Y½ó‡.ªnÁ3Ù k~i6£çõ˜JfŽQór$eÛ>–Rd!)íz:-6××9+<§s&Ï’ngãù™g"³9GºŸ¿› ÏEVt�ͬ-ñmêJLCXÔ¹P¢kVCYÀs&Ž?ý,°¸uÀ ;„Ò›dÕc_ÉÅnnˆ�†ÁÅg2üHC¨.¸™ÎŒ8�AØh¸Ì� J 8ƹ“Å4­]u¥™™ GªX|ÿG2¹ü>�Iû’ìáÒ¯gªdµï-éø™ Û÷’‚¶=$£yGÉlÑQrÚö”ÜŽÑ’•á˜\òÇÉ€~dР™2zÒ:Y9ÿ•»~qYѪšaèÂO¯‰²\†à!O}ïUV¨B”9°Ò2Ÿ>Òû(¥›sµ$wN�Ôî©Ò÷;wë{²hû§.ÓÉêž,C{'ÊN€MÈNÜûJY6sðyë˜iØý6|�>_?�Ù}ö¼¾Tnu³5�š‰ážM(·Uû#ÝO?§ã¿¼ÐD¶ÈÖ½F­ïô_gÌÃbûsÔÿ2Áf€êOh–)õù‘Ë®‘œf­%£i 4ñÈ@,8<ªÙ KêÌ‹./ÄÌü¢çƾYÏ­�å�‘ÑËo—´¸¾]§±ó³éÎZd4ç8IëÑTòs:ÉÄeÙ2ÿºY|ûpYþÀ|îL¹ô…ÙøV> %º¡ð="æ&IŸ¨æ]«šËx6.R!»U½®d¡3ßúËwÿÄúbêÁ¸0te fjq¤×PMg’÷!ŸšJjD:`NŸ�ø½­Ûû›,¾ï}X0”ÒÉ0Û”  vÒ=±>ÈLÓë7•¤šõð"‹ šILµЫ«)™�šI:(¯ÉøwN¨UÇÑÑ“q|hîýÛu—¾˜Òχ;jé¸U2~å™wÏû²|ï_®äÆæg¶/ãyàS%t¸O£Ïyž‡éÎ6ËQðÚ$�'6ñˆ÷ÅdÕÞ?dÁ?>�Ôö½Q0öëöfq6Ç!4 ¸ #ÛÏ瘬DŸ{Ãf –…f%u¼>:Ál¶4ø?ì}tU÷Óíû¼©àV h”( "w‚»{ÐwqÇÝݵF]©» nI(!ûmfå·ÞÉ;+½_?—{ÖšuüBSòÛwÏìÙÓÀ‘í«"Ç"x°™¨Ðøå²‘Í×µ]z'r·›ÍéÉŽî�z|<¼s÷fJ�1‚µqP)´¥+áÙãYњƽ÷ ÌË?¬UM[¥‘TQZÛ‹¡›¿¯k¼Ô�r‹^Zq¾”®c�…n�Ø_Ÿfâðô“ðr쇘).È88•o¯ÅÖŸ³±ëV!öÝ+Ážûë°µ![s°¹1›2±±!Ûxmç�lú,“€4a!ÖúŒ vƒ;�Æõ™®ÒHêÒ×cé4�tø[éïÑtÕ·n¦• k­iýÔ´©6Ùk2µéF%P÷d¯��ÓÖ‰™¡mÞ”ÔãÑe�ØÌEùˆðoKª ¹pPéÇÿÇðíI éÚ‹=ýhÔ^=8¼ÏÐlÑ»ŸéÞÃ;w¥óvËê / BÜXËs~º‚ú BAǧg?ö·@(CYÏL¦ßVíù©—š¤Þ£Mƒ)ñ€ÖY:S+Bhl+R÷ÚÊ�ÿÿy7L®= ì£Æ¯À²�o‚ÏIÖ›LÜÃÌ–öbþú&í–n]~[¿0ë‡`tœ´¡÷ º2ÿËÕl†¯ãʾch �r ¸Ê¬‡Ç¿ÙBcÜŒ›Íý{J 7³€~�=7²°$OšŽ‡ô‘ú•ó,¢ìÅ¥Ù� “û Ä3wŸ¡äÆšÚ CÛG¢íÖ×Zûs¯ŽµßÈÕžlæä¯ˆ™›…áìÁqæéøä3*]æ1¬/æ„£ö£$ì¹]„½ Å8ØPŽC�ØßXŠÝ�E­ “…í�yØÕXˆÜokÌ•{Êp¨¡ûî—`Ó—ˆ- wó^pc?ÏpÖ€$xìIàÅ\ÿb²œl¦òôn @)Ñ�Ö¡™¡@F+–à5=PiÝže�ÑÎ¥&#iÍØ’Óˆ�!)²ˆ!΋gáDàɽÙ‹?÷õ([å¢bbŠ ‚Mé6Ñ�C1>Ø ¡N[6óÙ˜!ÄÌ” � Ù ™lÉ,ûò�~¦è=1V|ÞÊ�S0cEVm»ñâÃVPÔ^hϲF¥Ñô}6Úc^™ÙsªVbÍzÔZÖhâ:©—åÚJþ}|Èv<)6 ±vÄòÈ(, ÃŽ¤LiRþgõÒȹ�¦Ì+¦g)Ãa¤ô½ M?b�çò³¨Y•~é�Ö†ÞÏ=`ûÁYæ>i\!�Û¿éF°‰qøË3ß³kÿ‘KÇ.2æùõíûp¡n3†±–Ãô\¸hEq:fæ¥ûjÁSìF7†XÃf4Elm‡¿Ö\“!©�&,¥2,(l¶8D;üõa3RàòIÖTºaNbj?NÆî†"ìk(ÁÞÆbìi\‡�e8ÜX‰ƒŠ€ CžÛÓ°N®íḣœ] …rN 6TöÂjL˜éN[z²QÅÆ¢(ìXÇá/Fò›qlÕ%œ6ža/ëûM´i5eÛ£Q(6”£*­§¶¿f� ”Cù§˜"ñÀW7#>V®L� û0�ÌH¦Ýi 4Œ`íÖ¹›Ì% âB2ÊÁ “b\±4/q;Æ#ýÜL¬»‹Š+«‘ûæ¤]˜É‘Þ”o›Ä)˜Q˜ÂŸËÔ7ÖsÀ�,ЩCGx$Ü‘&CàKà cŠ-bäL]R$é;ÖuD” Fc²©ê/ dTð¨g´¡Ø�~Ž�öœÑŒ”sw‘ΔšúìØ-ïÁÇÎ#‡¸ˆÃ@´“;Ʋyyuø\ã)}—~;&´:£Ív&v¶ïmÀsM :EY{¬¦�Ïn×B§Ý”á·g^VãËßû`JžiOs‹k¼�µÆ¸…O’2¿áôLç{žò-¶æø }Ê 8P6LY4ë5}Ù§²^×¢éÈ×tÏë� l´†�òyég¯cN:åÕ\\¸H:þUØ …‘¢~d¨-rŽ,ÄŽ›y. 29ÞÙX {2N¹€C€†!ÀD�‘ØÏãdB‡*å=¦Öä³6]ËÀ²Š(¸öé*6‡Áám”^û9 ~Ï'Z †Öq@1�ƒ´Vò-²å2èmhT M@J#º`h½æ«áDÂÞO3>nLm93ÕèÕ­<)ç1§/ÂZpèU„ƒû ÂÏ R‚�}b.ª¿ŒGýíÔßKÁúÆTlhL“4cMS"ªãQroJï¯DÍ�D”|¼‚_fcu} &-öÀ¨p;D ³AèãîòÁæš~–‚™b éoŽ‚Î8vìϤlíÁo Í L4©3m/Žþ8U�·Q¦iû†$x¬èæë°¦Ä4š£¯0ÔfŽ¢L‹p�Ø ||àD{½1Júÿ»vÃñÒo†A¬Æeu]ˆ;ö¦¤tÖdQ0tÎ~Ä1 s=è÷o¾Ð7ãfסÓßXÿýS 6_�ìÕ·Ù�Åá‘’ÃV¤ÂκÀ“i­¹éÛYsh‘ÆÅReEóŠö·>¥V¨•C·©ÓÈ‚)‹xÌŒ$xóÛ§Gï¢èh µ—¾]0kmjÞIÄÁ»åŠR 2‚K�°“}�%ØÏ`MÒiGªh$|^‡ÇªÇi7@ÅØÝ T*ݶëvR¶Í ƒ°ÿûŸ…3ÿ.´é€=g¦ óÜMMúK±5 Ë“P²i]mJ+. ¸h]ÛºHó½(A+kÎy±‰[ÞÀ$ö–x°ÿȃ f þ^Ý{‹¢ÐµƒEÿP;3̞S Qùiê®&aÃý4ljÌ�a0å˜���é<À©oLf¤€×d_×Àó;)¨¼¶9W`Yå(L�ë‰Hw+¦]ú“5=Ÿ^}ɦž…ëC~}ÂÛÔ^ܘãR:ýüC-`0¨e0ªN£«Åh\ 4}>Z÷i R@¥öËÚ�:ý\C舨�éå…Õacñ­kÚ/¬3doØôRo¦ÙÞó†¬þ OØÔ§Ãt©?Û1Äʾ>yt ˆ²u`½ÆŒé×÷ÙW“kó×§Œ£Ÿ�Û¿»ù3¿ÍÔ²cø†Ù §pB¤ÁÂ0ž”ù2«6½®[•&Ò¨ƒ´*) `ô�’YìÞ_¹ùm’6’Ò[7*ŸX�Í0¦³ü=#qçTlû5WØ Y‹ë-Â^ÈR(¸—º Ÿð!I]†¡I¥Ìä9Fëµ<¨Ôša=·Ë�½w"]¬˜»î-,aØ_;cúÚzä½x_�‰­Ö%ºH3�“¡U¨é�5 ¢u%вý8…fŠ'î"¶ô4"¨ s£šÌ‰,Бͫd ¢(óêÛ›5sÌšëƒô½³QÿC2¶4f pÔ6&�à!`Ãkd5 |ø¯ 1ä9Þ“÷«Pu7¥ß¯BÒþ©˜¿"c]ì8`€(Ø<)@ðïÕ�Ï ÆhOL¤rmåÎ�E1¦©ã´ëP í6u]��:W¡MÇ©{qG~B{¨B©0 ¦-RU�þìÑšâ</­ßöûã‘娍0¬ Ó«Øôrj67”n:03ü÷jf­¦hÁRDZÚb4�ÜG[Ù=Þ¥Ç'T¡™þ{¯7ÆÍØä鯡jðN³Á†££¥nÁkplKµÓ÷ªþ@v£ýÆ­I™i&]24Rh�Ì˰´ò"Bi?ãF®Ùƒ@Ckš.X·1ÃÊÑØ~3�àR©ØLk*Œ ¤50ÅæË®›hƒy›Cq"¶\ËbJ¬\€F ÜKªLØ÷�ÉHm†€"À¢ØŒRŸ‰b�A`‘Ïà3ò™¼/ï¨÷ùŽ|³¯¿‘ŒØº1iÚL§‰JÍ� 2b{x>k; Sg¶©­ç虞úÙ•´uvVŠ7ÝÒ ¿“�ýˆ9kª¹XÒ£Žµ‚�ßlhÇóWxYöÁÌù^(8»[Í�ÙÑ�'@ýb(4ŠÍ0„­D˜>K`$ò8E1µçó tä¹Ê¦µT< ‰Èÿ|1ædøsÜ7ë8ÈÇž&øõî�Qöˆ‚Yt*H>{K ZfÜxÚNìÔ>ÃkÚÔšjt•à±æ9*ã�Ää啘™¶~Þ1¬ÉQ@AÖ•5¿½û)ô`  ]ÁÞp�‹¾€/a¸®c`LA;u€ÓîŸßÄ^ºÕQãáõl_™[Ãiœ�hñô9ÁfðÔzcÜŒé´?ñ\ =Ó~a­…5é=‰ŒœƒLòUC¦¦©P3OE;ðKke6ƒ.¦D××9€²æî¢6szª#�æixXà\oŒ_çŽÉužˆ;;[ogC)ÉT(!À‘ÆjHDd6rL Qé39> Ro Lkj(G‚©4ù|¾/‚>'ÇÛx�€ƒõWS1/-HúM<»ô`t£R.†ª¼Œ32 [3¥Sçr­@�M‹¾qS•ö}¾'=$ñ‡¾Ã¸i ¤Ì˜*ŸY³#÷¶tCA&X�ú/S°ûnA´H€E1Š »°�”C±†€F5‡!× JJ<ÐÊv¤¦#÷´ï1伺1Å×—cÅ‘høxö‡{·î8ìé¡€À„‚K„:xa~ÒF$ýQA…õ]Ö‰t2h “ѦÖ$´ %ç²—�ÞŸ¸ƒßaÅö�ø„E/�˳ƒàÕÏ Ë‚Âqç•wÿyuÃ@ñ{Exå ÐîgG`à~wf�:Öz ‰O\ eí!@4K¸wö³YÿkŽq3ÚØl§�M{oÄâ¼,ä\º«ÀF£ÊÒzué„©aeâ°¼¬â‚)�õx¶¿¤Í¤{ÿÏtèß Ñ¹Ó0±> £‹1¡Ú�žZÓ!̤Q˜‰‰UÃ9ÜX…ã µ("…F“§EžUl‡Ñ*‹Î•oèy†çr]1¾# -ë8òY•Ÿ¬ÅÔÙ#àß»¿ô–øRù5ÒÄs8î ÷òC5ËF'WnÃ|Ú²îõ5,íóÚ÷SO]Åø…ùðd+@ãó¸Oi³7™�g·ždZH90›®¦  ìlȰ!›Q¬D~Ü+P‘}ESJ›V)†" C0áyœ\ã3r�ÏÉ}2 pȈäÙ*>Ëc%¹vg-]¦ctŒ=S3½)�¦’±sø0µaj�Ñv˜½¼ɧo¨~-�ã³V"­B�L†F¹&¡™è©�:šþ8Øï“vþ.’N^CÒ…F6}‡é`ÿAt›á‡/¾©}½F?:@ÂPºª½{Ápú®}ÐÒ¿«FŽÍ¾ŒY^þlø h�Íšœžîô²ë3]úþÇ®4ÆÍ˜Nû§?ÿÁ¦˜“2oxSÚêJË’9)[�Ϙ¦�­ Ûš´�¾¿†!…í•^C˜ÿø ²�&QåàЧF­›‚yïå!¬ÂQå.ˆªp@þ•%PµU¯á^Ι6cTH­†5I‡ñº€…’FïÕ\'�€i4ÖeHx¬>_¥ÚäÏÁ@k�$ Î/Aˆ+ÿÞTå�x¼à³ê3™'Òšcjmi´n DØ(ãN­H@1­ ´ø´ÍZSƒöÐŒ ÒËõé.ÂjX£‘Eƒi4¤_jÙ“<›È‹~#£àÖÏav(Y°Œ¶LŸêi=³1¼ÀëçÃ|=»0D¬1”apæ�6>âø�eÁá2•7p°)(ynæ¿+Lgûÿ'.7ÆÍèýO öyê{nàH¯°e•$�& FïF̽¦ÉQÉ}Ù|8cu%üí=™¿ï'ÍšÔÇ.�á7Õ ß-ÅÒïj±ô‹„P˜i�àlK,؆÷ËØ YÕS£üÍÄ÷ìNÊ>_úΠ,<†™»ü„ EÚbT™=FUØ#<Ïc 0®ÌÓê½0s«/fîôÅÊs1Èû`1Ê¿‰ÃÎ[Zе›R§ ب:�bTÏ/Åh7‘õºSà@À™0aRN]CívfP-â$еô£ß@˜ûrÆÏJÁ/Œ  =̱ï�MÀ°¦eÁ¾0Œ©pDx‘5‚Š,àŸI9k íóSYÀOÿ"øå †oÊ ø&„_*¯¥³šmŽÐ‚!S选unXrzr>[ˆš[‰RÛAA+ÛQk‚Œì…QݧÃÀŽé53À ¦ÊÊÇÒ ±åç˜fÜ�[7RÝ3‚5–ÔƒŸ 0§ý^¬Qx÷ã0*S{ËGÑÐ$?§5[ÞÄHöîxš;bFB=²8Ö`eÍ ÏF×2š@ö¨1˜B£Í?Ì^à‹ò÷Va÷ýBG�J•µ ÖµÖh˜UZKjïÑzæ§•HýjR¿˜…¤/§cÍ—±ð�PÄ}2 é_ÌFÞ‹‘ýÕ|”þ´ õ )¨h “id]¦QJÀ†L†@²R@†à"€Ef#Ákò�K˜’ºWÚ°9ÌÇ´åž4Ä4¥÷žŒ2@8j“Ý1+tS†õ¢RS†v*§!í^;[ëÊ­Rl*å¦@IÞY²á ¸™ ¥}�ÆÚ»àõmûd‘þáüË(š½ÙSfá`v¼ýñï[È0Ô5Ãÿí?ÓþDÍvÐï­SÝý9E /\‹$›˜l5²ÕáÏ­þ³×ãfÆ÷T£=t`�e,Õh�ѦÔg4hU·Iã7ø0¿ñA¿,§g:ŠúlX2›�½0¶r&–~]�eßÕ#öÛ�˜rj ‹­RÊ”QÑ俹„)´J¥“Ø~=ɦ#ªxÓmfZ¢¬íO�ß46¦%Ó&ÿq<>Ïã½ÎA¹æÈa½#0¼û3èj[h‚€RS†B+­0v£3 Cѧ±Øq/LÛÉŸ«Ü¤•8�ç² —°cý‡"ˆu‡_ú0ÂÙ3lå$&†üe†{?sÄm}KÀwqÉ)øô(ª_3{°û_œš 9 m~Î^Y�…Y’-Y8`ÆÊrŒ™Š€æ¢<!Á¹3h5aúpT~¦ÈD}'ʳÖô™R¡ñ\êTÒOs?%×W ùÓéX~9 óŽaæ>_LØ3㎸bÜ1WDŸrBÔI'ÄœtÁ„ão¦ñÆÌ½¾˜zÀ ³Nûaá¥0Ä_™£Ÿ£æ~¢65',† F äAF�ˆh õò ŒŸO[¢çzÒ³�`ÝŸì†éÕÑ–vˆ�ù©;�tÓèÆ?+`ј‘ª‘Ѻº—*å­,q‡�ó|ÜÜéˆêåñø‘@S¿*ã솉uË$g|sê’Œö¥Îº”Ùï6sʱ�y:|Æ0‹‘0Ø0*‚€sU0ÅÅ�3PÍ›¡�Ü»õú”f› ¶~Ò8¯Æ¸ý§ËŸŸ&Ø\d£×G¦¾F:"�Ekí¬{½­u`Ñ|Fâ&xš9€’J1¶¯³îÝ2/ ¯” ö»�Xñí©×ŒÝ…Ð|[¦“yd[¡ú³Da4jÑßðcí‹@DŽ-|ׂwit|s Œ ÌVj�ñ†#éò4Ôüœ¤\¡%ÈÚÔBvÜÉC\9UuLiyS†,Ëd9!ƒ-Áé†�‘º͹pnE機1nò*Lu÷ƘÇÞS¦V ŒYReYï`ÌøårmS¡¦œ”i9ŒÒ`^6ŒlÒ‹NÛTÀ‰.ÿä"2½V·"2RÐo`íêjò?[‚•ÏÇ`ê~/ÄlvÆèÚ¡«´FD¥-"6Û"r‡="¶ðÏÜlͰBØ.„í¶æ5[„m±AH YU¹¹ì#ÖÛ"j‹#b¶:cÒ~,¾�œ�¡êj­ªáºuø¯°Ö7£"í±ü9Ïö‰¿ÜséH©2§cÆíx_;ÌKBýÂkAh-Ÿ£ù!œXëaS¨€�#ýμ]mø ;› ³™ŒfƒÔk–}^ƒð e0�žlŠq®¨û1ÊN¦î§dLßâ ÿT¦¬â[Se™ƒ¸¯¸~ðŠïGF#à#`“+@ßìAŒ�¼ÆÈØú A*™ ”ÎÏÉ$Ïä›"¸ÀQõŽˆa¶üš‰óÍ[péáv\|¸ ›Ê¥¶Ã…]úŠâà7|Í.»àxv¢¥ ‰l"{M¹`|älŒwñÆ<Ÿ@ÌõñÃtO/ªËFbfÒfö|äcÄ`+jÍd oÐʃ{‹‘¥w·>Âjih9ÁÉ YæÒñ:¿UiV, §ŽÉú$�V}+iïÏÆ¼cA˜¸ÃaÕ\X*ÈK† `�‚+-^mƒð 6ˆØHÐÙn‹ð�¶ªf�l=.wZ!r³BêùÎއ.#ØTA(#|c+UP*[aƒ‰;ÝÉ€¼°òõÿ¼B”�À£XŽª É=†ÔvJî¬@Ü™ñ ³„+#Ü)‹vïÜ]¬m|˜"šÍÃ+6¼šwꆧ ³ÑÎôiëO§ȵàĽ„z^ê8³Óv"C nø·ðN=¬ 1ÓU¼VŒØ¢±Iÿ8o²ÖTd X´ÍPÌ ðÇGg²•¡´ÛÊÙ÷¬ E„ãÄÖZÌó÷G°)Ù^X,·Ç.lÓFø`ŽW¦ óD”©�¨Î(>�©š‘”¨®-™€í×r¡ÜªÕ~w«`˽l¬ûz9ž Ç¸í®ˆØ`‹àjw™ �³j¦ Ë ¨å%˜ò^P%Ád+Ád»B«.52‘Ûí0ªÖ‘~Np=�hƒ•DX½µN`©9?‡NÁÛÙ€{Ä ËÞCY)õ!ŽrPv8ª/GÒl7V#~ÿD2;ø ”ú ›fù3áÏ0ÆqÆ�[AóÎÏdò§È™õ5†õoP…ÞæF½«ê9Ë7¿‹ ³Óê;î&öðw“1¡^c�…±“×bú 6µ.^ƒ�žlg1oí_k¿¶£â�¸H·u�Ö»Hë™ÍÕ—ßDU|róì€`©ÑD°&È: ÆØ9ünç°uèOþõ¿âzcÜŒèDÒîÛœÇν°0ÿò5sV4ö*2geõœÙ¡íø—§Eàľw L?š$é3¦Î58‹¾-GÌ®‰T“qQ¤ -8Ç‹EГ, ó…P0`)àâ›Ï(L0‘ú "~™’�“‘t›oA)E@I@F@¢L Ì6£òÍLÞÌ5ì-þ\Ô“±—"„}¿äãð/ëpòvÞn9( \c\Ç�–Ïp¼©NņoR1&t¨ÌsáiöL�=·¾½‚û¿|Šw.%¸ìÂÏŸ¾‰‡·¾‘øíÆWøøµsØ�ŸŽÒ„U8³s=Žo¬ÇÿD›Ùh,A•›€”/�¬äÌ �ÅÀ�É(6#i³òŸÖ`Ñ9²™�®®")³�´Yd�Â*¬YÏÏ«# 0Â7Û ¨Þ‚@C°¨" %t#Óc¬ÏŒÚF©ø{„m´&àHMG"d=Y*tÝI¿Ö˜1ÌådJ•› SlBvZñ³-%57ñ˜ò~X,@£u‹f(¹Çc‘[ç~±SW{ÂÏ„ì†)Hþ ´ƒIÖ7>hEÆóMJ ‘¦­áH´� ©s•âU"mýFœ²ØÏ”zæ&O]°™0‘j½©óñÙ‘Ó€ 2í=}Ýæwçãh¤ÎíLÚ”hgЍ&n¾únËö‚âß–G�“Z C&prÔóu~¬²û§¿ù¯¼æ7£*í­á]ºÿæA°`*ÌöÑf–¾b7ñ»?„…Žì§aRÒhÎ}{bTÊ,û¢ž³‹¿¯Äâï*!’ç¯ 1zsüSÌáǺEš9bŽFüÙ)¢8ó¡zLdi’:“ô˜W<ƒµ•RcMTi¾Éƒ@�á;L•­¡T8ž×åY©é°ÎÃ{Œ üÇ f $F•Úc›>qQ?ÒHG�kE8rµ'ïWâBã|ŽóhÆ~eÜÀ]‚Ï©ßÖcß½$VMA¨Þ>%�6Ñɯ�ÚGó5ÁEB C¼÷Á '�8i"'\šÊÏ˯g?YlC9¢wiê(ìþUF¨‘­¬*¥ß­bí+1›\¤èTJ¶R6d2’ d›YoC@!xì䢳ƒßl·;J]&¸Ž©±]V`�F€gÔz{¦Þ,"¦2á;lZ3„àe)u›° « üìmŒMŒ Qô…>®íl¤\}ƒ¹°£q‡†cí[“pà^)N=Z�-Mœ£Ó”®3ïT–7ìñAæ[s0i¢+ÆX³.4ÀD¦‹†Ql1Ó;S§¬ÅšíH�E1”¶u=ãQBu�Ï+€R¤�òíï´ÑÑK16b£'ŠÍ�=�~&�$´ cØ¡Y€F…ž%1 ɪåóhP»êѲÑÑ'ŠÃZM´ý°û÷:É™G6ÿÕ×ãfœ Gº xöêÝ2œõ‰èY©ìiT̆@àÊgúšvg[ˆï›Å¤‰Ós8MÏäHúlÙwu2‘=/ø¼c6G" Õ fT¢Y‹`\©+¼V  ˆ¬™,E€‚òf –ž§ ÁHB¥Ö¤N³¦ŸÔg„Í$ˆäY€È?CRnFÖ IÏEU9bËi8ÒT‚ÓÍ58ük1Ž_-ÃÉ»4ù¼WŽK�6á'¼ŽüØšR»…ïZÞ’Â|å‡kéc-‹dS@þ46³"pÙŠÜ«·ÖB°‘s‰V�‰u0(�úŒ‘‘û3JM%–]ƒš_â°t\xTˆƒ-E8Œ\ÆN<À—Aú¾‘Åþ‡%ÊôS˜ ÷b…Sxu¦¦�€[_Ž#öYõLÐÈÅ’u¤€IX±ùéþÏÑ •Ó4nj¥Ðrž«õ Ïå~›ñÒ2ÔmÖc;¦¥i8”U(³^tÀÒnŠ«ÝëZ†bØ@Sÿüï†V4p÷ÍZêS3o.‰ÓDW6[ŠÄyœ‹[KøÛ—þüô4×]�.Æí¿ »ñ²ÿÓ“oû4¹nm�@ï(¬Ýy…½6Íâê¼¼î2¼†z‰0@¤ÎL ¹»Xbák%Jê,ì†ÇŒMÂlæ¾™�Ð w�€ BйgS§ñ“DY$‹L™ìFö" `úŒÇ…éø‘ùÈõl^Ïy´¼«DêÜï±»›:Uðy�*Þ]‰C¿®ÃáŸÉjnUà$JOþV‰³-u8ó°–,§WZŽ¡¿à!~â·÷rl¤“?÷àÌôÄÔ¡n˜lÆà—ÏÞÒ²-ÈH< «9TS† Lq„³éu �j${jBÍͰtM6|/3h@‰³Í–†,¤2ö¹�lDz` –àb‚I‰€‰È•‹È>JD( À¾•õ–*sy‡ŒE$¼Î†a+÷B6É5Õòù-d2L¡ ÐÔ[s!Ky4k6”¤g©NÀ “wŽÀλÙ‘Bì{��ý-…Á”$J /'ñß ™ñ_áW2Â3Í RiJ]Ò°ñ—&"2Ð >ýûŠ /ÒÄ Ñ¬ÝD;zbÚâ"¤�¹¡¤Í*E¦ë§ÉÑÌZÊnÂTèSlB3¾ÆoøGVµÚß$I:­¸i9r¯/Ââ¢0„Ù‘±™‘E=7�¬_�wpÅŒˆˆ¥0%凌 +�Þ.Ý5mºLB€GÈÊQ½7/=Ä�ŸàÇ ¯âþWô@Ѿéæïtð·;ßF[à×¥ÄÔ5½ÄYl?]z¥%oáÒ«ìÓú†’úfÖgXûz¬:sÄ({Ç_žé˜jñ·÷Ä»ÅƸ7‹¿û‡¿'ÃIõê?ðó'×–H'OŒŽZˆ¥eg2�ŽÄ=e|Àp6sº>×S�Æ ¸�ÕH¬úv›�¥n³äƒ2*�¼œÏÎû ¦·Ø´É¾ 49&ÂjxMj9 ©"_†¸¤–T™H£³h riÕЙ. $6RÏ ›€÷Y&¢L‹=< û~ÎljÛd5+q¢¥¯¶ìÅû�NâÔ½j©ßœhªÀ›Íûqá\ó&¨y7ë¿N•~‘pSKŒaÚ'†Ó {·hY�hÈj¾Àù]´èquC�6Ã[¥Î1ŽC‘P9 [oÊ 30}&ì©àû¥˜zØKdËáå6â@AY‡ •eÒh)’dJ𖢓ÚL•µJA•BE;‡Jñ?ŒÄ ïI�¤ÒØc#€#‚M”H·Jœ˜+-ø<ƒ ‰{‚Ž9V½<‡ZŠT HÍ�GÂjÀs »yÛÐH€~H y„oüÄ:Î{Øô[FkÓgœø¨5Æ"éÒTD³ïÅl€¤ÓFrBj„¹5fúD`æôD$þ†LDÕd4ÌDRjºakнhÅZÇJëèèðå ï£é��õÎWtʰ?8GF/cÖ »hŸûúÔ¥–¬Ù ®s&͵ A¦-£[•g‘V¶£œ>víÕ;–. þï¼Þ7c:­“íMÞ£×kc�\nL�Hÿq°vG€©6püF¹aþ»Ê)€Q'�œqßî÷’J[ô~ÂK¼éÐìÿ42—T©¯´„È™!j´äA²JP.›lI‹É;<–P}8~¡ÌÁ|NRirÝ¿D ç4T¢‰2-$ŸÞ\¥CQÿi"N<¨Â©–jœxT!¬æÚ£/ðûmÞåÚÄZăQ§½Þ|°Õšžm¿äaÎö)l÷Q QY¶2‡æ ”1DÀ¼´ e}£‘i\-“ÎÒY¥ô·š³4½ÅA„ŸÖÜYn"pëäÖg ‹ºæ}ù }Û·}ËÁËž;²·»½Z× ªX‘sÞž§öñkW.#A¿õÉfÞrh?¥íç…ÝÈ)ß1^y®™L½ñÚk/ìí�ˆ!œOm] ç÷þ¥ßÆà¶£€à.™#KÝõbÊtó2õþ¹®!Á[GLHž øò_¼q¨AÌt– 2‡(Ð:¸ Q‰1RÀžUE\è�Õá ½~zàÙ™û„»«71‹Söê-v7vùÊ_¦:Úc´2ÇYæ:׿tõÀ‰Óküâ_õK €SL¨5O?ò}†«—.1ì�ÆÄS}Þö×ßÅàÜ�¤™Hdëòž|†é|ŠQAzÏ»9uæ ø³,tBš‡ ÝÁŒKà `°ðÆ13," âˆ*jŠ%Àƒ#�!h¥x2ˆ! ®¨(Þ‡·ÿ’wqçùsà "t— "ŠD¡{¦h@ÀÈœä$Š`dZiy9¿ŒQ.Ue0ì³þÌ:/ìáæT¡êš[fºXpâ�·±rúNÁ|ɃìõfȨ”1ÜAˆ�ÿr¬¬j�<'DýIN}w@–P^·%ùp�[‰q+_nñGnq¸yz:ŸññÏ~:ø£n^zîÂ`wg»J©Å,»;7ö·w>³}ùÊ¿¯ëúûŸ¬g‡ ù…›C»ÞÔܰ¼{*Ä-Ïù—‹êý!Ä=åȯ{3ç߯ „ —Lðê"‚ãÝXôjÊÎ ÏQïmÀÀ@$ˆƒ�è0 Q �@o€T äîå`tʼRLADÀA(þv`R-„Q@$ Û[ÛŒŽŽY¹}•J#ˆ�%s>ÞÇXŽðÔúc<ü凹rñ2CqîÜyT¸sã‰ël?¶N=khê'ÎÜÎ/ÿ ¿Šay½M'S>û‘OñÔ#OÐÌk¨ªŠS#;÷=ÄÕe{Ÿ O<Ãt{‚+å�wçî‡ÞÌïx+O~þa6®_ÆçŽHÂ8 #ÅÌ 0"xc%"Q¥D4*©:è2<: Õ©ƒ3 ®ŽÕ"ÝZ¯@úà‡ŒFcî¹ónhÊó1G lJ¤™(ðÑ2‡ÂXV¨¨Xᑊ1GxÁ_ ñ J`ÅWО<°ûøvíbÎÁä€z1'ôúè‘ÛX½ï�H¨À— p@œe Ë9_‚H(k³•ûå¡0c¨‘Ÿ$Z~»•åaËÁ[çñ›�&,é'¥¹ÃÖö&ßþ½ß•äG~¨Þ¼v­Ÿr‡Ã":ÝÙØüÿö>�Ù¿Îýê+OÌŒCû-rh?ï,¨CˆÇýÁ0åDV8ùÞ‡Ð^@PÔ#‚Þ]‚tð‰^a6«±yK¶Œ¨€:nHŒ”T¢ A±ÖU|VÒ=¦F9ôÀ¯A‚ ƒPRi*øÂ˳-MDµÌã<òø#\xú9Ön[ãÄÉãÿZûÜÉOsÏé¹úêׯ^Ålnn°˜-â8«k+¬=‚ÍØ›Oðlä¶ppw&{¬šyM¡kÉ£ûVˆÃ AhSËÅ—_ã`{ÃÀàìÙûxç/þÅÌ÷fÜxõdÇ Ð÷J:k–±í U Œ#Á�®·ìDX6r0¤ƒm…g£?î#¹ÍØÌ�JçõÈ0“Á Àpçê•K¤&-¨DíöA ÃRË^Øe¬cNq!2c(¾‘Ø�¨‘†PEn{û)VÞ~”ƒõ}ÁÌÈ)‘ö6™¿ôóõËŒÎ=ˆ±L�Uº@XB§WY¦ÍpÀ(÷È’%!›õ§8–;»åÖr8 Kê9 ÈÍ[nF]7<öäãüè~<¿øÜTtˆ ÃþÈGÃñþÆÕ«Ÿ¢[äôƒ�¶‹«Ì9´_¸°94w�)µÇÛÔFs#ž>ʱÏ$‚‹�ƒ à‚IƒãdI4途§Å';.€CèEÜ tB HÒ"ajHÙ*ÖX7ç­�8ˆâ- Ð ©T òÄñèxíèXPQ˜y²Ýf›Ý�¤ŠrǹWøõ¿Ïñq¤Ím¼i=áúúuî=r=‰ôW‡LgrNTU�Ùþ„Ýí=ÖNßîäœ:½Ææ‰AÕÇBÅd>¡ö�38‰–ëW®qåÒ¥.}–=cÁX=vœ7½ûíôª>Ï>÷$‹ù ‰Bè  ´³–œ3Þ:(i1š„ôj0I �$@E�n ÈD hzw#J…«#&Ý~ÁižÈž‰½ˆh�³'07tP�R`ê$À�~¤§Ì˜²Ã[Üà(GÙeƒ…-ø€VZ #“éI�°8õ«Î°õô:Û/l!*JOÛ¹ÎÞ O0çÜÝwóÄÊ*ûÛÄ:²½½ÉY;ÛŽŒ:5 bŒÐ³Éœ×mz0á…gŸãƵë4mKJm÷¼ã®qâ¡Xe´MËå‹™ÔÚA‹¸Ð‹}Þüæwsü¶5¶·6yõµ Äã±Dj®X�¨60Â0àÉ1ŒP¬m»h¡U¤”ñEFúÚALÜË­áên4ŽÄâ$àV / ¡d^zñUÞþ¶·`b˜Ûë?7�€ŽÄ²®%±ÉcÆ\µk¸FF 8ÎÜg„QäöwŸâê›O²Ø\`“T4 ì̶×Ùyñ)Ž¿ç×Ó?º†;(Ðæ%‚”fRzZ¡ôK­¦Üg‡J!lxdÛ*Ìœ €Èrcü§Ñ`¸ÙÏá'¥ÐÒÿf* ä”xá¥yÿÞdzO?ÅÁl†™Éb1ªêÓétv°³ûRNí÷»ûÿxt1=àÐޚ͡½kå¨xJ8Æê7!Œ<ÀèMg¹ý7½©‚vW’J…º‚‚ppé5&×^Éxv À² &ƒ×J�KO�Jðn^;E;|aàŽˆ =…L篕".RÀ%BJ°"|c%ÚÉb˜—Á[¡¿÷íof<^åÚ‹—˜ìíáµ­â¾{ ×ëuâí3?øy–©Bd8òÎ_ñ^î8Dáúåk|êçåç^`Þ" ÂÉ_zš»û½�~týòu®¼v‰ùt†‹C‚³w>ÀÛ~Ñ»IÒòìO°½q_–¼üGWŒ¥[“ö̽ӂD Zt¯ž]§¶ý8h#˜g$Bëö¤Ñ\ÆB˜•Ê4¯ EY,ÎÞqQ#!(* (¯›ªâîˆJ7o}z¬q_¶/3õ)J@¥è4mÛv`Ä!Õ¨ÇÁ•}vžÝ„šÿ¶¥i„#k¬½í—3Íží_-™¹Ì»H/µ™Å¢ît¨o¹�ÑíG˜í´Þ2Œ=òÖ5¦×^aåôy$TnÿÅ!*ÀR¯Àl©åÅ_tÉ„~(ë¯åÈ«iÄ}2#†[!KXpË ·8;¥çfà¸;ÓÙŒÍÍM{úI>þñ�r饗U 'çœÙÚßÞÙ­góCß9Œ>ðù½Í‡ö†�Íá‹�§Üìï‰ê«z½Ûz±§Ù29*ã»O¡±BÝ0 õ>.'à8M3#Ûé.ÒÆÓòd� T��)ŒPÊs+Èdd$ôzwŸ½‡Õ#«Ý�uá«Ïâ­ÑÛAC/щ2¸àn¨k§ ¹CN"ÐPÒ]óDE�•Þ*Þ8‚ÒhÃît—Ûü6áÈè)•”Òh<¾©)5E‹ÙßÙgÐ`9³h ׯœùÅç°ž±þò�.õ&=%V�&ÕÜ}ç›9uê ™Ìîõm滄.j+i.OŽ&AU‰£H– •@‰BOû%’É™ò�¡¦ˆ–4 D�¡¬ Bk†·t>DÁ“a@è‡a ÍZ¾úÂÓ»í8¾é>ªX À1 Ç)—æÆÕÅ.\zžÛÎߎ¢4R-0Y0=˜ÒÎ`5Ž‘»ÎaÇŸ'±M3�C¤mèt�½—Ÿfí¿’æ�¨@¥Ë`"Ëòh…åg”ÒBãVÝÞW¯ñÇõ§´AñŸ¦’ìqK�5em¶Ì|1g}}�W^{•ÉtÂßÿ>®]¹ÜA§m[Ã!¢’›º™ŠùÏþÉžV_lCýÙG“ í�›CÐüzà?ˆÈ»ªªŠî®³Ù”jЇADWNÀ$a$0iÅ1¢÷ÈuÝE5îB {‚J½Hn3ž LA)©°¡âÙ±YÆÕñ ˜!®H_º(å­÷½�￟¨�ébÆæö&››ëxSü¥—‘¨h/ࡤ’H`ÙH)á8nê  D˜×3\Œþ`ˆˆÀZZÖ÷%!5‰ª �sõâ²eB \á¶{N±rv•Öëë×»ôZ^dh�Á`ÈésgHšpw6_¹�@Ѐ¥é5%2-ðY–œ¨5ƒD*iľ\Á«Ù©(:äyÂËK›dK¸8,« ±¡£Q»qoÀë Ð�}þ‘OsåÚU~í¯ûU «ŽáÇGfÍŒW/_d±ÏIN‚55=zTuŸ´klNæ$FŒ#d%2þÅw±÷ÈEš¶¡iê.Ú“Ô°¸q™ùþýã§¡€&*Ë” 8@é²Ý¬å)>m†ð!f.¼�Fœ 5=ei~K¥™,SjËyp3\`2™Ð4 ŸÿÒøäG?ÂÖÖ&UÕc}c�ÁhD4&V}v÷vlow·Î)­§¦ù‚ãÿ5Äøô#³ƒ‡öFúí�ýQüh~ð-ªú‹Uµ—SÒÔ¶Ä^UÚÉ1ÃÓÇ:ÈdI(Eº+ú%Ž‹#Ö&HÖÁ…@ÑŽ°ˆj‰R ³Èxcà€ƒFAú dèiŸµS'vãÇNã�w?Ý~:Rd¨ÈH°6cfX[šgÇÍéÇ~)I®b—Sw§nçÔ^3<9"VUIµ%c>™ƒ ³SšiC;¯»B„¥b]êlg—œ3Óň1°r÷*:lmlPOkÄ1HmêŠ Žœ<^Dóƒ)7®_&Õ‰ú`Á|s†6sŒŒ !ä׋L¬{^^$ÚTªÕD„Ê#AcŠ0 ˜tÞÈPðžw{á€R ÄÑòî*Zúa Œ ô˜ÁkW^âãŸû×·n0›ÏKd¸{ùšçsnl­óáO|”/?öE®o^¥Î ^ D¤û€ÑÁ¨ÉRÊÆ tYyÇYª“+ô‡ýîy³zÆl²CžO ž!€A–ÑLö›ßJé–)·Òn�r¢.!õp³Â£õ1vSÀ~º÷lJy>öööxú™§ù±÷ÿ8ßö]ßÁý/ÿ‰öOþ!ßöÍßȳÏ}•kë×ÙØÙ‚ ¤œiSk»{»ÓÝ­­�Ô4 ü�â_y¼™?|š7^dsÍTƒ�çüg�§ªÇp§ê— ¬lÎpe…Áø(BœàÁÉ 0QŒ–:àƒÈ4† %ziZ� ¼�•(ž TÀ¥‚YÈ9�A=¯ ýÀÙsg8vâ³=$JÑ#jÃÅ 0JB-µ'GªEIp0¢Ï€ÑpL%}êzÑÍ·M€ö»ˆ ƒ„çŒj „{Û»Ô³1Dbir�ö Î�‘¡°ui“fÒà39ÇÎßNµ�‘ÙßÞc>9À¢!*0ú‚8H�Èh½èW1G½ñ¯Œœ `HâÁçNìE/_s†�¯ 0•Ѹ;´ .A<{‰�zÊÕ�Ë\~ÿEŽ9ÎÇÏ�‹Ñ´5{ó]ö¦»¨–ô�eg¾7g|bL  (7&78¨'¤�Ü€YÑx€‰$¡ªˆ!Ò6Ÿ�fŽŠ h(Làõh¤µeD  à€Ü\l&Ü,±Ô(oŽrÍúü&Ù`ʳrNL§³®þµ‹™N&]™™óâ Ïa٨ۆЫX,Ä^Q7š¦ö¦nf‹ù|Ö|ÍRÝ<T¿§×üÐç÷6Ö9´7(lÓfk¸ÿwàˆê�œ¸ë^bš³¿µfdq\!xYjŒ"àé£ieA³Ø'Oæˆ ¸C0ð™aS+QÍX ¬@†‚FÅZŠ9Xc%>�1Ùœ’Ö�Àd}Â`eÀ}çîç± £U@\ŠáŽ—Ras,�ާ\ðì]ÊÃ4£(GÇ'X=r‚ÙÁ1kI;¥D®3MÝ2›N¨›†ýý½.š,çN‡XLçälÔRè +�éî”ù|†Ywpw)´ûï Žqíúe¼¢¤ÿ’ ö¥D¦xã¥LùH„™#A¡rò"“Rb0 +JöŒ¸¢Y ,š–­·8F^”rçÐ ¸˜ˆ€‹—ŸI#%E€Eëq'o'´ØÛÚé¼1E‚¢p5èÓí1?˜c'�Ê+Úܲ3Ùf>[ж ¿‹§úTgŽP]ؤ9XЯú,Sdã*Ó9~ï[P�€/aâA „`ù™F)ãAÀ|5™,‡ (ð|Ûco2äÁæ »¾Ü½Ô›sfcý:ž~š¦ûYGö÷v88ا®k²eb¬XY=BÕëÑ4M÷sE¨×¯_¿RO¦Oôzý—R]Æá3�,&;‡šÌ6‡ �ÿcücåfâîHoÈÙßòG¹óÝ¿š ßû¿ak Q¥ª®A0r! ®*Ä!KÂÉ`·Œ O€ƒçRÎ+ECÑ"ˆåP•¾€€µó¶TIIO:H=ñÈãœ:е;NâN:HÜs÷�\ÚºÈöæ&T êÁkïàeÉpw$!rònÔŽÇ8%õ5Œp1¬êô™áÀ4óºƒÏöö6Ÿýا¹üÚeÜ���mvwöP”~¯âÝÁ<\±½¹M³_wÀ’ p8²r‚ññ…¤Ô;3Ì‹ŽE|jŒ-–XEE³×@Ï‘ 4(±HuêôŽ0ÕôŽcs@¢Ð¤†"UÄSðâß\`æ8A¼ÚÌ Z८eÿžCÌÁ[ðlEóÁÁ…l #ÓÐ�%C›df;s¼ê“{ )6 J÷ßµF]%´BJ-j†æ–鵋$sÔxÝ*¿UÏì–÷(³� ’ƒU€” ·Œä„¥†íWxõ…ÇùÌ+Opl¶ŽÆ _L¨§3jsuCj&äl¨bˆ%e"©nXL§ Gãî¿SÎÓz:û†º­¿þ ™m>ofÚ6‡ 9|Sˆñׇªª4r6ªÕã<ô'ÿ&þ¦?D³³�‡1Fšl˜F*FF–Ç�ŒP�$a’ ^!mÀØA9Ô0§¤°Š¾Bv|Á²îG t~Ò“â7wlîÔ¶àÙ‡¿Ê¯ý�¿Š�•½ù>'�ŸäôÉSÝK˜n†â‚/|)„° ¸—(**ŽAkô#‚–{wè {ˆ9å²^`º1ÅêÙ*æ;SÞ÷?@#fÆ  JÎÊh…êtŸ•ó«L&]©´ª"íÿcï,€%7’uýe•¨é £Ç0†1¬×»kû.Ãef¦ÇÌÌÌÌÌ—™—½ f�=Lt%U½y•Šsý˜èÔD…Újµ¤Óí®¿3óÏÿ—Ù:~;­V ô� 66QH]¥ çSðpšbó ÀÕ 8ã©QB€£)>ñ ൯ð)XgñúÞâ'(`‡Sw¾ÑéžL4 äàñy�4 ³bL � ‹CWRQŦÎápˆ·B=)©ëITœ�Û—XN @.ˆF²ƒ-ÊÑ7ê“OÍ4eQ! 1©TNGjë߬÷ã¡*K�ØFêþå°Ï]{§¯WŒ·6XÝrþÒY6Ö7âÿ7ÕdBUNȲœùÙ…âãrÂìü‰µ!ÊD˜�™™ý½«Ë7šŒÇ?4WUŸøìæÊ5vÆØü 4·ÿÄû¶$MÒ¬ ˜ÛÍ—üñÎìá»(«Ž�4 ‹—‡Í“2ˆ ¬³‚ZÆ <^Eñ‚!ÅRàËÓ5€z®±š:3…Ákþ?tˆÕçňF!@ hí@ÊÁùËg9õÊ^ÙOFÆæÚ&+×W4@ êa€û%b„åÅk„å}HKM힣=ÓU·ÕÞ'ŽÄ$¡FSUU8_j”T�¥)ƒÑ�4ËÂLlê8©MƒÁx4ÁãØ½4KÑnquý ¥+‘D¬SL1»°•�]í 7µÖä¼*5[Qý5¯"›¾öá>$‘XÞ‰hjpì•u&6Dfu­»ó.€´Ç‡çBЬö8ïµÞ$ €Ë$€F¨Oy›d]éq©<"$ÃõH-Ry½2ôss?ÑFÖÚ9¶T~‘D,ë+ë� Q•%®ðîÉ‹FN2•B+ çOÓLy®¤ÜZ§$›Ê�æLç‰êÎn›€H³©³) Hšc“5¦Ý%Ÿ™'›žçµk›ŒV¡\RmNhÏîÅÍ¢{ðN*W“µ;”£!¾¿Aÿü+¸Ñ€V^2šL”²m ""EÞš–Ä~m"ù{üX^ys{ú‡Ç“ÑéìôÓOÞ¸îÙÿ¯ƒÍÎxhfNª­þ}Ƙ¿�æù[�Tû#„î#ïç¡ïøí̺XIšÒ ®c°"TÃ夯”\4q‡F4 ªþ,€i!mÞtC%HKÃ�=" (jâŒ4DÀ¢©™BÀ£Ç€.Ø“Š/>ùZÝ‚ît'PL¯]¿†Ñ¢õ@m3­².ª,­×�4W˜6µd½œÖLGSx€“(Ÿ" ïõþÛ¦ÒkÃác,³Ó³˜4A%QLP-öõ�:ƒá€ÚT¸¼‘Må+Õ$ChO÷(ZEìTYï¯P–jMY@2‰QÊsUãPʲ°Æ¨B@æô‡.¤êNuMPàR@"©‘ A€E2Vß“Zð€Ç± Ÿ›ÖͰ *dJ­/ÊV«À�µ+�HÑN†/¨|þ.ÑhL<˜ ~� ^#Êd6ÇYEœJ?î¨[8çcjÌ€N:"%:* M¶ò¶�’¼@ðx �%�]d²¶Xpå`‹¡³ë@8&ëLa �©è&c7¯R�úL}V_ø ŸüƒK¯ÓMR¦ºÝðyÎÏ-Ø[c¦œLYß\èÆµ«—üFÿ¯=6³øCŸZ»qƒ�ñÿ2ØìŒj«¿[DþNovöï]:M�4§{ô^Þô}�ÙýG£$ˆ÷¤f&«ïȬ—!•Aë4 ÊHË¢�^bßI3ð¡™3ÌX³9pŠ)$¡ åkñáÚ¦� ˜LPi¯M‰õ„O>ñÉ@Ö…ºW)Ðj¸†òˆxDë(hÕÔ[5-Û¡•tŠþ£Ñ<Ôej9¦6\åzXÄÛí6YšØüÞEæö,qöôÎ_>�8èu§¨ª ín'xÛ¬ —Ù¬ƒ‹zn,îßL¢ 0›[!%e¼v-ú–Ç!5…F$1…„H*4ØZ¥Dû~\U5Ò1>Ô{LÇ’Îdˆ— ³cÊDÌðÀ؇ã•ýo'#!ªÀk!•*E›B©¿â%Lj0ˆ‚Ey/ø\Hggøƒ””�ΤE”'°P;�M3¼sÚÄé·Y¢z(ø@ƒi扞†ÔM´¡!¾^¶ECH q‰jÓMsO�‹6S{‘/`ß[ßOÿâk\ú܇¸ö…1¾ô:­4 ®¤à ÞÏ-,-õÖÖV�–ãÉ÷<Ò™ù—£ÉèߤíÖê“뫞�ñÿ@SçÐÌ-Íó¯(Ú­^(~§»ÞþÕ¼åÿ0sGŽã¼ÐÔ´òî46/0IB«•S¯Œé_¿NM‰Sh\$Me†AÚ§NÁb Íb_‹Z€´U·‹–hz̓‘=1ÚJ)}¬@„‚T& áx!r\S µ";•(½Ú{¨õ9=Õ¹3Õcnß €¦žjjl/�–„¢o¹Z²rc…áxÈfƒ¢×bnaŽ<Ï‚f‘gØ¿�]{öb24Ðúã-:‡{$E µà�m2I‚°gMþ B¿¿¢©Á8 î´“Å—÷à�>çÖk|ß«8¦7ØRY}bDA�ìÒ™Ú‚M�F*‰u$A£¦žA¦Œ*@WšF«J5*HŠ„´“†k{<”>Þ—iY|B¸†¯4eg5Ñ{.)Yß\£L4-i=R‡Ï3¦Ðð†Ä´hï]Ðz“Wà›P—cÀ6A£÷«)Ðf[�¦éê‰îÚñœÍ^NØ�y@ˆçÕ ±V4®ˆ…41dí.³·ßÏ=ßöÛxô�üîü sìAÆU”úƒ>u]§‹ »æzÓÓ÷Õ®þ“‰±«£?"É¿ä�¢“±3þoŽlv”|]ÿÙ4Ï¿®hµ¦µ†oYxÛ—óæü#äÓó±àê·Ùq&øŸ»PO2 òû‰ÏÇX¶0¤ÔU—�Oƒ½€K+mÒì˜h €F„!Lb…h!à�Ü„F�öàˆ‚ 8- S„shmÆ£ 1õ¢¢ ¼FLPiD ž@˜›_dan)Zoml²±º~ÝgyJkºÅ¹—ÎݲN»ÎÙY˜áȱ#èðä­‚v»E§Ý¦öŽÕµ›dEN1“SN‚‹Œ#‘",HS˜° OÜ(°FëNx\b½ÖjœAOY–±_§.kÒ: ‹|åj7U·T2É8qâ>f§fùÔ'?Í`Ôh=¨0‰%É->EÖ«Ä¿·ô½TO¢‰C=†Jc>¹S·Sj•ΩÁ”úþ'& Ï�&#ªÐŒ«ÑT2•#’`PæøPÃI»9íÙ)F¥£ò5­"§Õé‘d9µ#ꢙÆÿŸ•‹ ¢*Dpˆ@AÃCs¿èŒi8M•�ÍĈÙJ<_©� {}ŒHøÎ´÷qÛ{¾‘ƒ�%¯ðG9ûóÿœá¹—CÚw<K·ÓË[­v¶±¹Þ‡«rò ®®¿p"ÉÿYšåO<9ر3þo‰lvƃS³âëú›DäkÒ4�®ªJ*/,½ÿÛyðÿ(ÉÔ3zmEíˆÅGy�„h„ÔuigKºˆ1 ^p}ePù‰ê ¹¾’U:káÛ­WÚkb€eçLO*mKÓrn½¦Z©BíÅãµ0^ûðßá:Ϋ0enЈ‰áØ4avj‰NkŠ ýwýÒUÆý~£¬•qóå›´ÓE^„ÅvÏžÝdyèðªIRa™Í˜Ÿ]ÛÉ꘭õ-° y§EÚMÕu“š1c[j mó$€A2±šB³ªèìúuè¥q•#ÍSé4ÀD$U0O$  T‹ÊÖìííãðþÃ,-.ñØ£�AM“˜9™¶ ï¥%Áà-ŸÎ¯Ì´´�aÂy]Tè–T4b4Vk<Öb+ƒÁežª_iÄhŒÑë ŒWÆLÖÆPHLß™4¥–e²Ne·ðÆ‘-M«Á[–‚ólln@«K’·H aZÓaHÜ6ÆÑÁ3ˆ’5² X<Ä9q0(A«â6ÏÍë)é,,PÖ: ÷ô˾‹·þÑÎÝ¿ý¯aöÝΨr¬o¬ã½—Ù™¹tvn®×›ž9Ü�šþênoúŸäiöß½¸ÿ=ïÙu cgüß6;£ ‘?žżƒIèÝÿ%<ð¿“άSMjUPÖQÌ6¦*lÑ¡½´I2õ<©' Î_ÇJŸ"ØåXŸ*�Ðú��æfÞB­hŠ%ú¦€Ä^ Z´Õmj”™e…DcGÕ/5- ÚmoºÊ¢’,FJh“¢Áäz_ƒÉ-i‡ù=‹Qãl<sñôi½O+!]6:?䵟cL`˜y<�¤•fÑ•k´Ù§O´`í\è$7Æ*x é«´�B ­¼C'ï©=4%­´£÷VydõH©ËI–„4–Q™ãp 5Ø*!Ú]×0� vÝã _¡=FcÏ�ÍëlMú\¼|‰N§Cšda!Ï;9ÔP÷kª­7qÌêI�$¨ˆ§×‚¿™z‚6È:©QÐ𠨵·±á^Ãçeô½žéÍ’úŒ•«ËšJŒ�û¦Èô<€ø³n0žîT�IJí!dö\™(¨™)5UbM¦¡�÷;�aTú¸-ÅôœÐìk_Ío¯ëè‹)kÝ—ZafÏAîºÕ(ýö?ù¯¹ÿ÷þ]òÛdu}�Ë7Ãÿ3­VÛÎÌ̶—öµ;ݯýM¡ƒAÿïß"|ÙC­n‡�ñ8ØìÔiþdšç“[ÃØ„âðÝ<ô„î܉¦h~Á2 ©nõ‹ÜéÒÞ}’LSieÉdeƒÉ`ˆGdðAž¦’Ž �gjzGaM¯ùS3‚iiš ¼F ¥‡ºiï,ØÌ`rÁ õyJpk5n¹Öž�þâ.Œ‚L.úZ¦TœÓ !Q°³SV»ñóŒCwÞÎÞéQ5è›W¯²º|_+xÍÍÎ3¼9¤Þ¬Ð¦çg¸óÎcäÖ"U�TŽNQ„¹¹µš7�óLF£[sBíje–Yê?"Bé'Œž’ E§¥Î@¢‚š®®5%9Õ�›(ÍØU5uî §tf&>š¤™Âbº&Lñ&€ù`ØçÊ¥«A¨s}u�ÙbŽzP…û *؃R‰Î�õøBC}‚Ò´EHD£-jÂ6¨]‹ÑèqR+¥»r¸I­nž5á~© !a°1ÌA Á‹Óô^1O’«¬QRwÂÖIͤ?¤¶Á½5cRÆk7C³¥÷„1ÖúH¬µÔ®bxvY"”vd°XŸcÉŒ‚ +ówœÀØB¹¶! ‡­ L¼¨ÇN™J™¦²D$Ò C�ñ1…ŽÔ³Èmïþý=“¿ó¯³ôèW�·º>Q•jWÛ¢(:YQÌu§¦Nˆµ:˲››^ø;ïßsøý·"živÆÿ°Ù´{ï¿ÞÚä^É‚lI{†c·ÀF²‚˜§6Ä_[™��qa_tº{ŽÐÙwΤ¡!°Þ°üì©°Èz©PË. €£¢”A\L¼8fÞÎÜñ7Am¡BS_¹�hÎ)D¯]è¢+ÊqŇ›,�Ž,…èþZ�N$ꨅm8Ÿ©Ô>hša= ‹àÔÒwk é¼±m·ádÀ„qH¥9j ÓFŒhwúÐÅþ£Í“$­„¬“C ÒÖ5HjKR%˜±D6`¨·d–|*gsc“Ógϰ²ºÊµ•ëJ3okýˈQ µXõö±ë- iIÜdNaÛá§ž‰W^~†q5ŸàÈÞ#¼ô/žgåµ›Ñ}ó¾GîçÝxy–‚H˜>1xk9}ê4çNžªãɈ²šÐî´é<Ô£_m…:IC„¢×fq×. &ê– oŽØX_Ö¨-EÙ^µ*/‹W 4ÉÔLÎ`‘05eˆŒ¢RBK°m|VÖ7ÖXí¯PÕÚ"N�Ã×>oTDqàé "P=TÚrj5eæ¼:©bÁ{µ9À¡rAN€ÓÓ3äÝœëkבT�¢®¡µ¸�bnIïQ@áQ£ÏÑ™U6ž8M'k�w8øîo »ç�Ò‰ ð”f„h®f DÑ Í4Zd•EPŠÇn3p“XŸ‰QT<Öèl~—Dˆtm¶�¥m¦ÛÒœöÜ»N<*Ý#÷ˆ-Z 7Ö0Õ˜ñp eUÑ*Z|ÞéMåyü;¬±_í½xo’÷íêèÔÌÆ¹Á–ß›�ñ?+}–?Ø››G«ÝšÃXÉç‘_ûÇhõ¦âÐCóø˜ã–øÅmvIkÍàò“O0¾yðÔ£’d±Çü÷Ǽ»Œ×…A Âa* ¶h1¸q7š¨+gé£VV`šiÔƒ¨œŒý•�ÓeÑ„mÔ]ðꜩû2ûn½çÚh˜ç-n»ó8÷>ü0�¼ƒ%áÊ… <õÄ'Ùì¯áU ’;ÞIÿÕ-Ný»—peÒ\‹ûvó¿î»9pä ±¤9™p�ÁFŸ“ϼÌÍ7Øl�ά�±øØnV+”ÃR5Ê’„V¯Ëž 5­¤�•„ó_cR�µS¡ â%:•ö‰ÖU$*RC"Ô¢$kCF)Îcƒ”457ÖsH-š²}¯´Ñ¨ÕÕ¨A�YU–øql ÑT^F `c©TMZ4õ†w \� »|ù&nàT„µ“3uðÎÀRDñ*Äé¥ìÖ«×Yþ•“ÁÝÕx!+zÌœxŒÖž£kâ⎀oòã¨)1O>³èo“½ ÷< ˜0t¶õ E`Š- ûš â‰ß#Û"*=®1„¸5€I2ºûncáÞ·ÉÒÃï–lñ€”ý k7ô×ñ^°ÖH^6I“Ì;å᮪®¾Üûõ‰±·íËŠÎþ¼eîžßÝ}s­Ü›�ñ?lì2önà×ö¦¦îÊò"+MÁý·ØgKw>€CÐ|4qÕÿiai0r*i«ËÆùWÙº|_Ž1“ºdþÑãä�Nì±@ÐÔšD{h#–¬ÛÅ‹epí"nT¡`ÐH�9À)@˜LAF2£+„oðYñà5âÁ‚2Ï2 ø(@)™ÄÅ8Ë îºïN¼å‘@ýõÞsãâžüÔ'YÙºŽ5ưn?3“i^ý×/1¹1 2#­v‹w|ù{øÀW}›$ÑÛ%††Øy/<ý<7¯ß €•ç9Æ݇¦uÆAºÅ€ p‡FDÔxÐ0•±gíc¯1yþ&“þ(øe­.‹'eúÈquÏtÛfMµXÈ×ái4a6ÖùĄ׉‚@Ÿñ0÷¼éÁÐí�'4o~þ#cùòUdŒµtó÷“ÿâe®äU]çÅ#wÜÆïú¿‹Gá¶ÿ\•IÍÍ+×ù§ïŸñÄ/Œá`ÀÔÔTèÓ9økocrwÅÕ«—•*<ªhu;{è>ŽÜy{Œnê5Çg>ý+Œ}µ5p¡ï%öß@�ŽÑ”žXÄK8®¦ŽjÎÑ€N‘*¯ Ýoµ‰Öx}?±@‚h¿TI8·zUªŸØÌFêrs¢Fn©Š˜ªDŒ&‘@È‹‚;Nœàô™—p•²Ù&›5óÇßFïÀaj™àÅc]"j·>âì_ø0ƒ_9M=&]gñ÷ÿ®¿Áâý�ãE"Ð$±±³!­ÔØ'l_°#áÅ!8+X@üv%h¶Ë×4"”F]¨Ösá·5y¦¶�j�H§Éb‹'nzìØfv¡q]¯À×LêãQŸåWždóõ¸þÙ_aõÕ§év{Xké÷7´î' „&\×nµ+ƒöÚÝ­¢}5Ï‹³³S³OÏÏ.¾Øëö^OÆTUÙ?uæ•kó“¿èv"›�ñŸKµ?ü–v¯{�ˆdŸpû7ýFï¸_# ÝÖG!Œì˜fn9~‘AHzó¬]8ÅÆé—ñÕ$œd´¾ÉÂ#w’Mu¡Q§©eâC¡¸õŸ«ßÉærlT4Ú�®h´QѨ< L¼²Õ<4›T@ûa�Øoë3¥*ïÞs˜7½õqŽÝwœ¤Hƒ ÂùWNóÅ�>ÁÍëW!õPC‘´¸mê(ËŸ]æÊ/_`4ݳn¯Ç7}ÿ·ðÀ[Ò”™÷ˆ®`HåÎN½|Šó¯ž ~,I’†:ì~Û^®_¿®­‹�ÃzËü�]aqÐSظ¹ìBMO’$¼®ªjÑì¼è> «úžMíâ×÷-I’XßaÎx¥/{¢¥€öÈx¼ºšb•Þ£n¥N¯“ >©$) ¢÷ªêáf—pýò Ijqâ±�݃G!· ^{�L‰3“0G×VYýåW¾¾Ìp 6ÅÌ<‡¾ü»I¦h6kŠ4 ò„Ÿ3‰‘,ÐÛ£*G•×�P@Çv2@S7­ñ»&˜Dp ‚ Ù�ÛéÖ; §Á5iž1Û\G•Æ÷%iFoï!Ž?ĞǾœ=�ÃÚ³uéL ÞWÕï{¹o²6;ã�ShÉ¡$Ë~ h»k‡é»Ÿ¾í·¹ˆ_ÀØ�íiH·�‹Ñ}Q�Ê‹æ�ÓΛg^d¼|U£›•-ìÞ:wïÂ[á¬Ì0áêm PO†lœ;œ©‚ ¦Ögº�F«ê•DÛÝ@wNCj, y’  åÐiM§kÁˆ‚RߑهŽÜÁC�?ÆÃo{œ¥Ý»Ã5Æå˜—Ÿyž§?ùI†n ;k0¹¥h;|›Ï®sùÇÏ3Ùš(ˆð¾oür¾ö›¿†$Ïãj"Äð/LoU–ÿÚ¥+œyùuªªÖ÷Æ•�–¼ïݸ±ug�.Î^Y{ÝÙf¦fQÓ¶$D`Ë×®¶`Lf”Ž!œO�YkP&·ˆ\¨u…÷!²ðŒX¥P·•¼ÁEZt˜ xëÃþ,Í4ºªk,êaã@HM µ ƒ€mU ¨qE]¹&piMj¡Í`°‰+5]W kŠ¥ý´öîC¼U2‰Oðâbfraƒþ¯œe|u#¼—�V‡t÷ö>úeÓó¤©dh2϶¥Ã]´›Íš„ ¤â•^+�0ñ;¡3FøÛì`Fø$²zœþ�«$¡™’Ú4D=·ÏGʲ4&IlwjªÕ™šêæE1�eùRÔ_އûV7V÷ö[g®Öå3;`³3ÞpéÎ|mšgïËò|›räk »î}+š½ÛŠžÍ/¤Ñm“õ}wfúl¼ö¼ú¯xÏæÙ«d‡¦ÉuA –T‰âÔ«Ä«ôˆ3eÿW/Qon�ˆ ò)¾iµXyLjˆ¢c¹ §Çƒ¢N—‰õˆ|ª`מƒ¿ÿAîÿ’·rǃ÷°¸k7-Ṵ̂‹gÎóÔ§>Ík/=~ùy„1C!ÿ®»�3yyÌËÿè9ÒQ‚s5íV‡ƒwå~ë2»0êâÀ‹4…º4â@B}㋟ûk7VU>&xÜæ\ ›ÎØÜØ 4%FW¯¤ó{—ðVSŽY+ – £�A�žqµ6¬æ™�1ZÌÑ¿ņÅ=ISÁÖJ„�ž‰–Úu­n�àôºÖ„ bS‹s>xZj>ê�“{Ä^4ÊIm- é½fžG ͳÌ0Õ›a°¶¥ )– Cï¶ÛIZ )‘<".€ŽÔ&èím~è ncL‘·0F˜yËûØuâql«‹‰8�G¶)7o÷¬‰Cˆ@H´Çi6o´Àh|wbtχ4¿3tô°=A"*WKÓøm[MBO¼V“NݼžØ„¬7Í‘ã}÷ײðØWÅiO1\¾ŽóP–�bÈìÌ,€+òÂúÚ%™M�MR;.'rùڥᠿùÑk®~zlvƱÐ:e9ù�¢Ý~Ð&iQ™œ¾ÿRLÍa 4S xHlÌq7#œ04(¡ ½§°¸uw³zåÃ+gtNè_]eæ�#Ó³mXT‰Ƶ†5k¯Ÿ¤oaS¸ý®ãì^ÚÃÚÊ*uÐB�F øâÒ^Ž?ÎÒ�ýô¦¦É³6ÝÞó‡v³ÿð1î¼ó~xø­»÷8Óûf1-¾áÊ€×^y‰/<ñ ^yö)Ö7–©}¥ mbƒïÌC¾ w¾âå¿ý~Õ‘™,8rÎï^ä»ó÷rç=w"â<¦vq )5Ñç¼ ý6—¯\ãìÉÓAz¤�C$Ð:ÚeïÛpíòU”íU‡TÙx<¤huhϵU9Y,y«ÅúÊ ƒÁV¬lWe…H”z`X‹£R#4¼õ¡¶B8ÑhÑ^šR£DŸøpœTÚ¿ô \ú9�]¸7,€¨‡Ž‡ºPšjPáÔâ[ÓeCƒÒSpšíÌN‡H´NB¤Y•Ždvš©Ãw FI#NJ¼åÂÖ«y�­O�c²5d<Q‹aÿ¾ƒ]÷½'&öÐÄQ71·×VšÊĺéq£<”X»‰m[4Yiq4 øõ6 ¶æpDŒÏûFÔÒ챩26lsõ ªu Œi‚aóq3ò%ôfé¼#0úfÿZŠ{#¿ãaòýw`²³�ìÚhmeR—U[Dh-f§g7 ö“ë[õl£¿6;ã�XhǀߙfÙîÒy»û]ßȱw~56Ibä’ÙF.ÙD¾ØïÌ/ÄÇ‘�1ÿ\ô¦˜=rœ›gNR._Á“«k W†ÌÜ}ˆ¬Û&¥@ƒø°¸h·xF]ŽØ¸tšj¸�µ†;ï¿›cweai�ɰ Í“ø\©\[ëTÃ:˜ŒÍ,ÌrôŽÛ9zûíì9°Ÿ={öÓmO1ÜpýÂ.¼z†s/¼Æ«/¼À«¯>ÇÙ³'Y[Yç§š_I'eï¡ýÜóð= Îxå¯SëÇÔÁ{hOïÂK-(R߯ú Üú„å_8ÉæSç)'%*EÔæ¶[õšÖž#8$‚1:Ðé·ÕgŒDêpÍè¶_<â SKrèh²Õü6 I­^·¬#›-‚“^'ºYÄã›}4Ít`툠B#:�³�׎ ª@¿=;ÑH‹Ç:•~ö6/Èfw‘í=Jvø^Ìñ·ñØ}.œÿôØ4™ÊŠB¦ffÕhòÜx<úbxå™+çÙ›�ñ†õùÖ¼UÌ×Ù÷Þof×ñ7ÅŸk“úWw,[C´×m²aš¿šj} àäu"BÞ�Áv¦¸ùòÓØjŠâË×®mѽs/¾§ £% @-ÔŽÍ+¯QÉ:&—v±{q‰ÙÎ`×ü£�q0™òÆéâV:úƒM®_»È•Ëç9}òg^:ÅéçNòÚó/qêÕ8÷ú«\»r‰7/±Ñ_¥c3¤ÏBº¦t±¸~`ïaÞüð#¼ó WŸ¼Ê+óyF§ûtò.³Ó³Ìî_ànÞöη‘% H£�Ã@ÔA¯ÑŽØ�”¶ ^yñ$—Î] ±)Æ“Õ �}]ö<¼�å•e&ý1Rkñ ¦ º°kW x�ÎL_¡‹v]k�Æèb/IHmÅѤHIÒD­›Ñž›ÍŠºªc¿“x{ý²`@æ�µ4ST±¸¾­ãßʨclKQ�î«b*.@.°q!ßVÛŒZh1úh°×â5ã1µkøt4î-¦ 5 ŸÜVGŠ#ž7áÛíšÊ U“ÕŸø›Œ_{ÒdYfŒXŸ`®ˆç¯zïþÍoþ—ÕïôÙìŒ7÷g­Ô;÷—Ò¼ø¶©¹ÙÅ*íÊ»þâ�1}ðv åhŠ êÜNû„&UÔSOFŒ6VY~ýÆë+Üxî3�¤ .Ÿf¸r ê;é#ã>í­Pì—Ì@nIo[béëbñ=÷’‡K‡õ)›Ë9óÁÆáù£ï|;w=]S£éE>Få˜+ˬ\YáÆµܼ57G (¨XÀ¡ b-¡‘Ò'�÷Š ¢Ùžë°kq»æ–X˜žÃ”™/žá…–KŸºH2ÖÅ7KrfçùºïùFÞý¾w�0/AD4×Aó÷¦Ò�%€�oP¢ñpåòþÁ_þ{<ý™'u‘uެ“qè{oãÐãµ—O…t_í]LÞín?Æ]ÝOfsamk™žþ"Ëg¯ó±t*C2QEì¶WÑL§ì³rs H¨ÝDÙïIH4Õ&á=Šâš¶m1Þ„cj_k/�ŸÎð>ˆmÚV‚w޲_ª4‘ÊݨÓhæhmŽ?p/ƒþ€Ó/¾Šƒ5 Uf˜=þÝÅQM"ñ¹Òâ�PÞØâÚßù,×þÝ“ÁÑÔZKžìþ²ïæ¾ïùýØîLk sdV6›uÆ"“4Ø$ è1L ™ÐÖGݘÚÅó7I o8š`#  ±1úÒ-±ÔT³ÞN±ÖÑ|*·}_¬­Æ±�í†ý-®üØßÁ?÷q¼+ɲÜw‹Îfžäÿº•·~÷øÉ<ܱØÿñ!Ê!£=óéô|èQð’Fî¹öMeÿ¸�©ˆªœ°uó:˧_¤�¸úù±væe—Ï„HDܼ õ›Xòv‹¬Ý6WWƒÇ~áÛ$ç6¸ú�?ÅðÕeö|ÕÃt/á²J=åm eH#ÚqˆSL x§Ò÷^€BXÜ·pk.rlr,,v+««¡Are}5¤aÒ$ ËÓ½i’< ç›îLÑnw �œŒ‰+Ù\Þäì/žæÔÏŸ¤q‹¼Êñ8ʪ µŸoûµßÅÛ„4Mˆ}4€ODÁÄ9Ä;š*�FEæ�Øö­ÇîÚ»›7?þξršÍõ ¡”ÜøàUzošæðG‚òÚúM!m*J.^x�b¦ÍmÇî"—‚éî,¼åm\œ:Ç+_|š²“%>¢7uˤ"€‚‘`+àí婪©M”¾©©”(à�ÜØA RK˜Á•Ó¨?�·�8‹¸0„ŽV”צÅ]÷ÜMg¦Ãë'O…ÏQRQó·Î4Åì<‚D*à|EâZˆ·!U79·ÉƳCëñZçq#Ò™EL–£ãW/òž4ͨ'ÊÿG€iÈ0m�",àš‘’4X` `ætfvF|ÆuhÍkñ2Óモ¥/»�²\ šI@¬ ¿š'¶Â«HC¥º_8|”i,Ò‚Öl‹Îl‡=‡v1©&ÁhÌd–¶-T½À¸è§c¼Z¯ßXçÜÇÎpá‰s V‡ŒVG䦩¢ÚÔÜ~ÿÝ|Ë÷|+>t"DZÑÄJ«úé@džy+ø4—ØHSjo~ìÍ<ñÁ�±ñÜFTbîŸÛâÚ]fqß.Þüè#|ñãŸc½¿†”Ú )•ðêŸ!ºë>ó´²6·�¸“¹] ¼òÔs\¿~¥-[,õ¸ÖBjAL ØÜ ¢úgI7EŒ¨÷OmÀW”e™8À tbqFSi£Éê™HHç©~�¡ÓérïÃ÷Ñ]ìqúÔëlõ7 & Kgß$M/aRŒOU¨ÕÆË›¬}ìuÆ—×ȃå¶a4†hfúÈÝ`S|C_U¶³$Ýöïqâ#°4G¤Ä¥šð4SQê¬KsÄÏ&ÀÕDð‹[�J¶×Gâ9‘”ÒècÄ-"ëÌ#*ר!eÄxù õÖybРo1ÃVWÕäÖì—ãÉÕ;~prèèÑîh8ÔÎñ…’=·ÓÞwGÈ »ÉˆÔyêµÕ©®ýⳬ>÷ž ´[mî¼û.màó–¬NÀ{¼èE¹`lfÄÆor"60ÇZ6×ÅRÔ 5�þrŸ¯^祟z�³?sšõ×Vƒ?¾8ÒIÂÖÆSó³|Å·-ßõ뾋cÇïÀJƒÎ¤çTÙzcâJ ªú VÙhB,xQ´Zá½>óêé`ÛŒó�ªÔ=\íú(ÈP9çuUmyï7�'�Uïý§V×ÖÞ¹¹¶þMRV³iž³1˜°ô•_ÇÞûåܧ~‰kŸü9Æ×ÎQÈV Ó¦Ã�Ï_fðù™²T–\=p¤5�n¯Ö“$�Ñô‹W»d#ŽÒTjM\œ×¾�Éæ„rcB=v Öú\üÌ…[Ày!,¼išÑëL‘¥9¦N(lÁ¸ñ¾oøJÞõ�wsûñc�êŒ!€†Öf´C<‰‡>VöYéc®Ä+È5HúºD oïÛ9ÿú9~ñG~†�Í�`=ÐõSœùéS´¶Ùýþ}ù <ÿÇ“?ô7¸ðáeëì‹”ëËPQ Ñb²Zïœ+«²Õ®Þtu}íÖü0ð÷€¿•­êêúCÀûF›[ß�Z»ØAé‘}ws××ýZòÙ%fo»‡ÞỘ8˜”ÖÕ 7W¡Ô¢õd}„¹.\ùày^ùÙyís¯sýõ¬^^c«ßg}e�ÁÍãå1ÃkÃðxpµÏðÊ�›/ßàê3—¹úônÕ`B-敟y� Ÿ9 ë ®F mßfwgù8£S´xäKÞÊW|ë×ðž/{/»÷íLjG#MŸ â‰�$šÆ#ž˜2Ù&†x]¹ÀJø•¾¸¸À•+W¹~k E’3ZÒ_0Û GoÍ} Ôúã­PÀO“TéS±|õ*W^¿ˆ›x²îøvïÙERd€€CAþQ µYÄ'z=U¥ö¡F¾ÙÁ©¶ajdCô¹í»9pø ßòÝßÊx<áÅÏ?ƒx­‹ô_Øàê/_dfÏ4 ósÌ?<Çé—xùä‹¡ƒ>hÄez�­Á:¯¾ò4§Ï¾ÈÔÒ,S‹s,-í¡è´¸ó‘{Ù{ü «7–¹vé+7®…4©Ç+=Ùè{¥Þ@@*xeŽ@)˜Ý°†Œp=< Ê­ŸƒA“pÿ�ƒÜq÷]øÔ áúò5¶FëX1x ôuPxH§§ñÔ8œöÖÄXdýgÏc® ÐÔU¥ÒAy‹…·¾ŸÖÜ"ÎKÄ{\È+Á£’ûİ‘cl£.¥d"hàSg GMÀFTØh#Šz›× `d[+AS9ÝA�~�:(qÄz¼±‡N¼V3JÛ¦mh€ñÊ .þÐ_ýüilèŸJ™™šñƒ­­¾÷üt]Wò§/¾6ܱØÿµR5m¼ÿky«õ­®®§ª²DŒÅAÄçhbýÅûÚyï¼s#ïýÀ{¿Ž¦Èþ ðÒsÕø*Û¯“äoþj«Û=:=;;Ÿ$‰u&¡žÛÏCß÷ûÙ÷àcxLü4CúñæOüÙ_Ï�/|_—!Šš™�FD½f´î"´¸,ÞÕ! Öî´ÃBT»:ÔA¦¦§Ãâ$ÖÐ÷�þ6$6 ¿è§º=î}ð�º5²k×®Jxˆ` �iÚ5nÿéë­Ý Ë¥¦É«ÆjÏ/ �ÆŽ²®øäG>Éýý­=6£zÄìÑyŽ|ó1Ž~Ýí˜i‹Á–ÚK/½ÄÕk1Ö"4ø¼ ��©U mõø,îÝC«Ó&O‹à-³µµÅÊêõà<¢•J‚ @¿¿ÉÖæ“ñ,x]½"Mûb”‹Q‹�NÒeïþýì:¼;¸oŠÔ›§æ…ϼÀÕK—‚5té*|žÐÛ}73‡ïÆ�Qé"d­-ƒW®³ü�>ÍÚ§_gsy- ’$EfwqÇ÷ÿaö>ò>Lšá=š2+GH’* 2Íc›‹}µßÖ©ß8ÆHÜn�(¶×yb:«I°)]Ã�MŸ‹Öé�nþíç�pÍ’`‹X÷ ³é2ZºÆ=Ò xÍZUŒ|F_åüOÿ}õs¤ŽÄk³®/²b}<þ¨ˆü¡_ºtúÿ�clvúlþ°ˆü&1f¯‹ á:ðÞ×Þû‰÷¾ÄûËÖØ§jW_>œAäÂsåhÌv�ÉZ™xÿÍY«õ':½î.km 0Þ$GàM¿ö�2wäx¸Vu&ºeóÊ9>ô‡¾‹Á¹“YÊ{¾îËx×ûÞÉêê'_|…ó¯Ÿ ªÉâgLÙ´ºmw-’e{öïaß¡8¸Ÿ]»—ÈŠŒf›x|ŒN}Áè˜�—Â4Ûª¼¾¹Ê4ÀL¼^ëú•ëü»¿÷¯øÌ-Ð �ù¾€Ó>Òå¾_û »¿|ŸöÈ`0µáÊù+œ>ó›kÚå�B�5�Ž©]…³Ž$Oiå]Úí)¦çhJû.'Z1>akc-(J†›¡Q¶cU”³ë“p¾Zí¥S-kçìZØÍÜⳋ3j# ‹¥¿¾ÅSO|1èÀñ˜Ö4³Ç *Ä%HµŒ©.¸ù¯žfëç_¦mï=eU)¢#ßöÛ9ò�o%›ß ú6/ŸÅyGkvÆ&Ø,êÛÛë)þ ¤h¶ë›Ñ‚°-Ÿß¦©Ö ×.<ŽÀñ¸8¤ù YïÙ¦°Î¶èb„‰Í´[³�¦ù;¨jD[¹Õs¯žzžk¿øÏªÓOü‚¸º¶^ë°LÆ“Ír<þEó[?rýÂ5þ›ÆØì �:îþ‘µÉCÎÕ‰±Ö{ç\@ï}¸|xø´M’•§GýÍÿÌy©35õ¥S33ÝÄZ ‡Œ'‹�}}ÏïazßQô*±PÙôùãúóŸæ“ê×’Œ7éö:ü‘¿øÇyø­S{Ïx<æÆõ\¿z=x™¬/¯rþô9\¥ªÎ{:��n;° ²~’ب>lëºdý_æÆßû,›go`IT™ÂÃâ£_Æ]ßö;˜=vý•eÎþÊ¿#¿qŠAk�ïû6Ú‹{ƒúsˆb‰AhÓS Êb¡�ÈäòÛzSÞÀ²9n= 0i¦º¶™ FM6Ù\Íó5ô϶5_F¹š†7Ofô˜²Þæ¦+o ¢ÍæNï*†'?ë_ùשZ?ýªL&“Ä©z¹wÎ ·¶¶>í]ý‡?±rísü;`³“N;‚÷x':Ο~¸¬Iš^v¸åø/æ�·zü_Ë‹âD»Ói¡µs*Ÿpà~'¾öéLOÇ_eÍ/jTÆ&£!Oý³?Ë��ý8‹Ó=Üqˆ?øÇ~Ós3h}˽ÎãËŠjRâk•¦±F%VÁhVSL²½éBˆ£‘ÇóMmú¸ªhz,‚ŒúÒè>M�)ØèyñfÛÏÖÈ �s"‘ž«Q ñØr4á3ÿÿà¯þ].�=z½)¼õ -~ùQnÿŽ»hïicD! O� F\¹p•K7/†N{/PÕ•”LëC®ÖZJÒVM¶Zœž§7r!=‰ÓÈЃÖbl¢FsyÊü®ÅâÓóÓ­"‚h$ƒ'[J¤s>ûÑO1Øê£° Yk–é÷1}èêöi}}ðü%®ý­�ÓæR`¾�Æ#<0»ÿwüàgéMï nñÒ�ü&¯~Ž™nÁF{G¿î×3wä.œX¢@å¶E¿™Â²Û€£IÈL£³½p/À6Eç°åWë–Á6é˜Æ9ݶZJ3:àß³wPrËö¶hˆ–½»fˆ)9`;L?yxÌÌÌÌÌÌÌÌDÉÃ0š™q™‡@R«§Núœš£lü L9:³£Œv3ºSU·î%læ×ÁúBI‡@$\l¨“_+=w#þ¡Üÿ«¯øCû÷$, / )C¥ŠQþE3!?\vqçj�M+þ§JjžŠ¢ù¢ ªËb•ÿô1iGIùl!ÄÒù\_:­e*39øa€¢¯°â‰/Ãʇ? v" Þ²0Lc›kb\“îýøk€ÉèïíÂs_þ|¬¿à<À2ºb ˜Ö† MÉÆÄy™Š©/F3šoç#ÞÜþІùÙ s £@!LÆB�0´hÖ·Q06üiŽ,è˜fƒz½�~{¾ù鯠2SB„$�“̦`çm̹¢«*ò‹ 0g6H÷ R/eYA¹XFµXÅÔø}R>ãF ®° |&i{Êfˆm¢9«ˆ„;]Ï%™›T!ƒ¾Þ>t÷ô€jŽ 5i£g§�<•@ú_øpáb|x\ËñÜK6×–ãÐœŽ›ï@~ÉéHdzÌõÒq"%žÁä×ïÁävQ¶e[4@‹D[—–¿¿‹¯{6õeöüè »>M¯'×\‚5Ox9ÜL®YÂ…Ë.qi€YrÙþæëwЂ¸Ò³…æ›»Íóìg. c±¹ÁhˤäA 4BÐ:��W´æ½zЏ¶›mŽ]+ªÑ_~6÷f„£Iäòõox%2ù ¥X1­ž%€¥é²óO4ÿô7=P>ÊÍ�Œß¥Ì¹x†bXeæŠè†gų"ŵE¸¼0¸‰Š9|ó�¢V«áÎÛïÂϾõcìÚ´�ØgnÂCgOÙdWå°äšS0÷Òyðreu'€Ò¼+8 Ó9„åƒ'0=1…ZXCX…dáL’Bº^O@&<˳HÝ:ß�‡V€e¬ŸQôǃ¥lÔ­]¿ ýO¹ R� Eil»}+F†i¾‰ )�@¦g!:´Ò¸kgA‚ôÐ á1òå;1úó­H«$BÊVmØé:ιœLþÙ<öþì‹(íÝ ÏKÒ‡ÌuaÍsކ¼%ˆ øÍÞ“r‰÷g>@‰˜Ü ›aj\M™+d¬5Ð#ëM2 µkñƾ?F�f`åYÆŠÀ¼WÚ�KJq`5„z�ì¯ËS‘Úúû‘Û¿ôa©ËÏý”Ѹ$ähªÕÊïmËzí_FOŒãï$Z`Óʆ*Š.ðÛqÎ×v±Ýq”Š„”B8È­Y�sŸÿ´é’‡¤>—W�§ýüƒ:}lnÿðË�ÑC¸ìáWàå/{!’ÉdÌ—•¤Ø§Í¸LF\ ËUs¦q~)âÁ¼î!â:'<”° 8GÖÌÐ4÷~ ³>5{ÛËžXÆÃêŒa`ûÖ�øÉ·ŒƒZ¼R†•ÁüÈ'`èÔ`fMó®_„Ž3»`elD‚² Ê8<¸Æï‹²”0 ‰2.EHL2ÏNPv%½?-´¿ò¨Fò4tÜ!=›4ê à,Ë)( lb¼ÝúÇ›PmÔ`ÌÀÜB; KÏDª«Ÿ¶¡œLI (1þ½{0ý«m¨•é}Z†p´©×ÒG=N2‹:£™ÙuÉ í§lg½äýè[{ �ˆelbŸƒDs©‹Kİ^ wóä (˜’Ñ—%+}٦Ϙòq³¬ ÿÄɼ¦³Í¬gÃ�6'CrùƆ+ïßùÑÇŽÞ{›­BÙŸÍæ5®Xô1*—Jåréw‰DâM7O àï!ZC�­X›-¬ÀÇmË~R2“Y§éÅ9!„m;6UìC'‰eOxÎyê«‘ï[>P†¸4ˆù ›Ÿélqb öÔÊ“(ÌéÂBMK¶Ì(6ß—‰;qÔ`zà ÂAƒJe¬x!~–7Ì5Ù € vz ¤õ´ŒÞ7ñ@Úu|[ê9ÍéëÁâ%‹àªÅ ‘!\ÛEÚM“‘Üäþq Þ3€âX ù¾©>Ëû½`› `IËjXýš}Pâßì,&Ëá—g°åÄ””~ùî6¼ðµ/Á_Gí8æ‘Ë›íFìÊ 5ðþmëØPl �³yè9ÛÜ&Öà†ÒLçùò„!Ì]§lXöc�î›9« ŒŒ¥tlzO¯CøØ·S“*´ÚÀþ{13>Eó,Q¤ˆ¹¦,@Ù¡¢sU·ît¡ÿ’ùÈ,È)A%6Ç"°AID‚4HAÙts×ÿh=(ˆ = =+ $:j�í½w/Ú¸€ð-Èr{:QëÀÌÝÇa�Ix Qè“\=ˆ4ÍA�Ú\påãQXx &4þη< D‰dšÀFæ;qÆK>ˆ9+ÏÀ0œcsLƒŒ)'óf?ã�ð!HĆ�ù±ãÇ7%2¥˜&+˜Z4 n ÀÕž)˜$�™±ñlÀeœ?„!ÐJYš 'wÞqbû—Þ9áOŒ.³]§`È% ¿!-ˆI©›5ð¼æžÊÌ´¢6¯q¦—¾PÒêkµ¶–“ÌäÑûˆaåµÏ a?!bv»¬,À¦¨-£¸Ï™Ëq%[`æø~Üö–§ 6r”¶=ã¢óð¢×¼+׬mEÆTŠ *pÚ*|ž](�“ßVð>ÝI$× CSÆb3¿,Á©Df‹^‡â ¥ØzK˜#arlRƒÎìÚºSS¤·£89ƒH*#Ð�-„´¥^B¨<Ð{z?ò§¶é¥€lWÙ|Q퀲µˆ.ð”KÇ,B. €OkeÑ#Ù)›ÀIÚ $ú÷þõnŒ‚˜QØ£’¥’2Y–0�{_F¨Û)$ú£G3Êæ¬û7h¦Í\mùÊ»ÝER5®—’iœ¦�¦ÿÌ !,‹Mßó¤6¦lÌ“C±ý™l?Ï8h‰Ø˜¸�3Z ™T —ïw˜Ë'—½1ÇÀéÔ†X@aèËì­ó/uªZœªoýÖ‡å‘_|¹îÚ¢M«­;¶eSŸNA®pŽG‘ü’>ãàá"Zñw6­¹š¤>£<�ÎfÚ`9pûOÁ¥ïùÒm]°D\ÌÐ|xÁÄ|h$¬S`a>hz±U€�_?èÆ°çÚä]sÅ£Žç¼ìyèžÓ(f”‚Y—¥*l%–p˜ˆm9û+fˆ“S0j¦ñϤi`y 0ÙmË21˜l ¼Æ=9#‚nÈ3“ÓØ¾q+6ß¹cƒc:1ˆzµFTg!,Ôýå#ßY@ ðæ¥Ð·ºéEYˆ�\ož¬›EÊB"á‘¿ÍÂ@ÁW>"a%•k ²! ¤Ê“Dcʃel¿aü# xÒ%»h+°�Må‘tRDn�aˆF$áô,F纫ÑyÖÅÈΙËK¡>5‚;?òrÌìÙHì¹T:‹H†8í•Ç H6cžq]1>3Ã𙃠mÃñ›Kï›Å'¦ƒ&ž™(S&cç1L|Na¶`Ž/ÿEæ¸G?-B#úØÆ?�lýÙË£o_�p¬„m;B/Ðfƒ* Cíý7sÂâ�m�ßýÕ‘=­øG›Vœ“Î?Ýóo±{‰å:–�Ìáô¿Ë.y8 ý‘ÑrŠƒŸŒÍ•(ÚÏ ¨/w£ûTÀï䉽¤é„ <ò™OÄ3^ðLdòY@q9#ÛŠó:D8¾ºYE™7ŽyíŸ=²¹:L€“oM—ÿ`w<Ëbë„97) LŽ�cÿ®}ؽe'ï=ˆ±áQ”gJ�RÒgEJϹöêa >P.HØ4QH ÝŸ…ÈØ°ÛdÛ³@�%i~ßdC`ë×ëÅ:üQá¸OÊÛ•C%’»‘åõJa#$C6©—„—B2“F"MC¢”¥DóÏ@Ïú‡ kõ¹°“),½výøóøýw` c*£ÍÔ ±êÑ/ ïÞ/1%)+ö7‰³Þ¹šŒâþ3 àý‘ø—¢&}UÖr-6ÛóÁtÎ ÐqVÓRãï›�òaM¡"4¦F«'þö“�ýßù¸íz‰>ß÷Sdµ­ÏK¨HÊ¢-¬;ê~ã}õ(¸uk¥¤ÐаiÅÚd¶WÊð+é\î’¤€ :xÿ2\òŽo ?g.lÑ .QÄÀ¦î·Æ £æ’…`¦SP6݂͟}3ê#G©ÞÑß�¾þeÚÝòîó ÅÄ-…9o¼\¦¸¼æl€.²ÉµÓbxa‚#h,f¾•[1UD¦y?67-�h@A CsüÈ1Êp¶Ý³wì%­2�¡¢T*¢Ö¨ÂqRÂö#Ÿ”lÇF6™ƒYÐY±ÞlÏ‚„Ù9[Ï% e%Ìׂ¦Ö"íe\´3+]O†´O®£*“C¶³ ÎX‹ö5 Þµn[¨Ï#@7Òcw܈�Ÿ{Ž‹T:ƒÀ¯#sΕ8íi¯‡—ÎÐ Y&>ë&ÅÏË^ñ™ÜfgKÎò‚ ^îe=pš4«| @œèÈzKœ4>œ4 XY™;w†Ó£Qcïí“w|õÅ`èx[&›oÂ@”+%P©Ñqí®9ã7ð*mÿ1€Vü#‚M+Îp“W8ŽûÕîÞÞ¹a8 ©°ìoÁé×?�ØN¼\Á?Ì<ãiHÀfª3ÌÚ–íþ%�vèü¶õ=°¥O7®Ek–ãmy-]dNhJWDøñšû5âd@À¶;Yi�³ ø:®öŒf{u*v&‡c¦ #:–ÕXc�• ™*5™›#ld`Ãz9qì8Ž<½Õ+„>)f @I/E¿Sy¿·Q½Q#ÙÛv 'Î)‹‰¢ˆ2×öJIçq\ò¾ãII=#�ä~ùY˜·þ*š™i_² N:R_`ÿ©ƒ;q÷‡^‚pzA£Fëì‚Xûò¡mñ*�.((eà3\ñ²Wñ¦;ÿµq#5Öl�—Ü8 ­ÙJ™+CÇç‡9ðð ‡À%ŒÀû˜q5 ¦NPAéènÛ×ÞSÙt{*<�„ah<…¤€(i‹ö­2оEÑO·øÕq´â˜úܲ•‹¤œ¯,±*ŸoKAã{·bþy—ÁÍw#Œâ:³˜YÔ\v °é5VŸæåÛF®o¦�`æðÈÀÇÌø$‡±úŒS‘Ï瘷L<³l/S°yÁr! ß7€¸ŽZ¼4Á^çë;(Ër ÛÍtœV€]·`ÙíLJ]©ÇQhË£n–._ŠÕkVã4mwÊÊSÐÙÛ�t.K ¸ŽKóL¶cQ¹JÊAèÃrlø~ƒ ù6 -W@ÂK"”×F{Gººº `¡b\@ЦcÕ³ÞŒ~M“Ïô/";hR™fÀÔªØýýOÂ8„jiŠÊ¦2h`ÍóÞ‰®5烳È ‹3›m‘AÁ^g™L…’Í€91¬fÉ-Óq\Ø’;b²ŒÉ¼F½G–ɘà³=æ­òì‡Ó¨£Ê ľÛäþ_~©²ýëï¯Ï ˆ¡ÌI:`î·GÁ!Û²~Òh4ÞÕÙÓsÃíºÌ†Vüãg6-fZêôd:óÃöÎÎSjµŠ)�Žó¯Áº—|€|D‹ÏÄK ü*›_£GÁo¬ì6uì6á-»çÏð\‡œ8O]w&^òÆW`hÖoWñìÄü anÆÌ›>>„É÷ñ^ ø.&”¾£N›ýãYýÈ÷ᯭ "Š˜N ?7;€R0댹\(Í”059E^8ãÃÌÔ4˦9žééIòºÉå D—¦2štšþsú{©$¿ûɯµzÀ€Jn)´ëžÌÚWéÎ>CÐã-ÑŸ�üñÇ8þ«¯BèŸK TšFç•OÄé�{ ìLÞà¹"€®„Ìæ�JXRqLgå*¦ef�Ÿ`ºkˆÛ DMB˜l“”à (ø¡€Çw«c?ÿ|0½ûžúøÈ(üF=#£È@eÏ„ë! Ù ¶”Ků¸®û‹»ÊÓe´âŸlZqn®Ý¶Þæ%/°]§ÛrláG6?ù58íºg’Œ¼m[ÿðñ·¹Çî�qöxÝ '´UôNýíW @!—…e ¬Zw:žÿª¡«§‹ÝœcÖŠÌÐL1aŘ¹Y¬�/”ŠŽâtk¾/³@³Ÿ(l¾r³W¨ø±ÙÕÄ …2õy0‹TO1ÂOèî*}…!õ\ª”™úW©V¨”H&h¨T86ô¼Íô¤ô£Cs›L×vlÙŽÏ}àÓMu|P,Dè<ý"¬xÁ{�ÈwpI})D¶€m¾¬ð/'\•ƒ×F“M2qkf^2ã>8€a©q’¶5»9/-üx€âl4¡BTŽÀÌŽÛäø=7Ô'÷ïr””n£^·j�iÆå²yÚÚàeÒÃwíºEgœ_ô’É¿ÝSš–hÅ?Ø´'Sè–Rþ°ÐÕunA»rêB4”‡%OxV\õX�/¼°É˜‹û±›:;®ãMZŠˆ‘ Àòh'‰©#{pà§_Dxp „ P©–°þ!—âùÚû¦­£�ÝÅÐ,ïíóˆ¿ß³ä V§1èEçáY­�Rü9@³p²hÅ^0>;RÑ:1ž.þž»ÎÖc23=Ͷٳjo»év|î=ŸÀÀ‘¶KàµêéoÀÊÇ¿2R|¸’NÝ(Ï`÷w>‚é�EQƒ��ÉÞ%8íEïC÷ixÆk.I&Ø2 Nšì¬—"kÊóKdÔfV<ŒLÀJÂͳbüWÈ2% ›©šÍù`'sô$ *NbDgìµ›¢ÑÍ Š#C2•Ê$4ÈØR†ä²êû>(×uGºæÏŸÂð§Çvîüæ6éB+þÉÁ¦¥*py¾Ðþ¢ö®®« TŽl–½z´8â’«Ÿá&@ZVžÇäÛãÞì`ßHe—~Wª¹ü€H¢8t Û¿ñ! Ýþ[Dd*võã®Ç³^ü,8à˜¹nš)É@¬Ô Úˆ½ÆÀBÍŽPüEnW�q `"N.ˆ ]?'»FS‡*‚ø)-$FÅ ¦D ÃŒøc¯%’Í·?ÿ ü\ûï‹%HXHdrXõ¼waÁå�”âý ¢büó�±óËïBP™A’¤ƒ’˜ÿ˜—`•¦:“QÐ,øŽ€kp‚õ?Ø[5³*LS q&˜â} ö¼)3aÛšs1¢pXÿ‘Sì¯Ê˜r�z Å�èÂäm¿Œü¡½rìøqTK%+Š"ËK$EÐ?“Á™çxã:ÃÜ£3™­2~¦¢è–�!ZñÏ6­8oN¯hLN¯JårßéŸ;ÿ4×uœR¹„Z °ð1Z]àªÇ{)’!¼L–›€hò[gßÙPSàêд½¯SnªÄ=ŸŠÛoCäס½uðð'?O~ÎSÑÖ^ˆß-ÀkshÎB8@ð+÷™™ ”„ˆ³Ñf§CÇ2¡xˆYÌÆz‹³qÐã×-dD ô¢,³«)Ù ¬f4á%¾Æêÿ|ò�ÅÝ7߉é™2ü0$ÖÙzM�ï\¹–{óÓi†·Þ�=_~;j“£ð, Ep–œ‰ ¯ÿ,¬L e™Ÿ6ŒàÛ\›÷NÀ{€ñ> ÌUÈ�È P‚Ó˜�më2Oê 1eþ¿”ùõÅÝ9yÒkðžNŽÒðqÔG�câö_«Æ±�rrðh¤ÉN£Z³¨h)L„a¨ Pr,g—nþoŽdx£m;»«<5�Vü ‚M‹ý¸D&ó¾B[a¾mÛžá"¿z=VÜ×ÃQn*�¶Ë`%3�Ê¿áð &ˆšÉ6SæåM\S‚(Â&­20ü·Ÿk€i§‘µ—®Ç ^ùBôÏëEL]1öÃlIEœ ÝœIðˆ£Ã,4i#$ªHG%îÑŸ�g;±8é�)µÑÝ8>ÈÊ•±c!f¿ÖÀð†¿ÛïÞ‚6*Õ*òKNÅïø&2Ýs!%9lT±ù«ïÇôÝ7�þ]Ƴi›9�{5úϽ&6Sú�ù²¡9,žM€@ˆ]SLfý¢r‡Õl:¨Me>EYœM¤nœ1_×âe/^ c›/3f�>Ñ/%*#Ç0=pÅÝw£¸ëN$üiLŽŽ¨FµŠÐDàÓP,E�®ã©@jªVŽ@ª½ímˆ¤üÙ‡�Ž£ÿÂ`Órït-ˆÇx©ÔësímË2éLº^¯ 8)D©6t®> O]‹T¡ª½ù¥§–!¸£"KfÓ©bÛrsqt7ìÕ˜¾÷¯p zàç?ôR¼äu/CgW'âÁšCÀ¬Ù_Ïnb¬2¶C|EâIÊÉ ³G¼¬ÇщŸ9~~ð×YÖež (Ä™ü^µý>òöéÌæ.Tj D°Ð©åeÎ}åÇàf Ìû_að®°ï{ŸDeô8 i å óŠ'bõã^Ll+Äÿöƌ̊œ™Nn,f‚Ë&I¿ÔËðë5„µ ”í�H JR®BÂv ÕÕ‡d¡âþL/T‚~64k È””ˆü𾦍M�¡xpꇶ¨Úè±((Ž Mª°Æ¡KeˆÂ �3Q«WaÊe¶ãÖêµêQ©�aþÕ²ìßå mÇþe8W?þz<ýùÏ@OoxÄMèy™i6±Í8ØÄ%qxf2 °4Q¤?§@ Nò©8i¿çdˆgN<¸µ$ˆ0²ãà#oý n¾ño¨Ö|zÿÝz¶fý+>+‘‚ùõh¹lüÔk1½o ²®�TƒêYŒÓ^ñqd»zcÍ|0W0—P€ñ¥âr/àÅÌr h4àO £xt¢H"ªWP< 1ue4+t°z¥H ™í[„€mÃNçáèGA3DÍ}I‘RùÔö;� +°ƒ DäïÌ`zb"œ´e ¥¸¾…—HPã‡~(„¨*… Êz¤à&ýü'É\îø�£ ­hŽØ´bC®ceµ^}‘›H<"WÈ÷·wvÙaÀo4ˆM“Êà,Xƒn=wѾtµ~<‰\;`j`¥³È0†™*Nú¥ˆd€aí³û[Ú–àÀvh·(Újù9§á¥¯{) 2 !„áÅAq@ˆ—ÃbQ¯ÕI¦¡ût ¯Ð^0{7:žñ^ÿ9 Dt`ÛÍqáÇ�±ÐN¼ßÅŽ§5û±w÷^¼óÕoÃÑ�ûi3ÛõÐwåã°Aƒ�p“ uˆpì¦_b÷·? ÿ¾Yš|<Ì}ìKpŠ&—Ðñø”¾hv¤´bò/±¹ÖC‰'dÆú9”íÄäþ-X�l ×kàÈÞ=ÖMú©ñq”Š%„¡¤ŒÅIå%3èÊ&IÞßN¤Ð•O`¦RÇÌô<Ç…ƒõˆêEÌŒ�±è2„°h†‚oY÷+ !‘”2”5è ئ×ÿÌN§lš™< È´¢6­²ZBEÑ+-Û~ÂÜ… ¥Ré¼Ön,˲ॲ;`Þ…ÃÜs/C®w>,/ €Ý\T³:´k2â÷JEe�òØ ŽjgÏñ;~�¹y#úf1=1N¯g2ô.š‹Ç>ëIØpÉùÈf³8ù7û¤%Çq�ßl£·þé|ûs_Cä‡X°r)žüܧbá’…4 3#Óœ ÅÌÖb:i±TŒ) à?±s°cž4ëQ(Íó·©©il¾{3¾üÑÏa`ÿQPIÈMP&Ð}ѵ8[�ÙÉ4E]g5Û>÷fn§’“wÙZ¬{ÕÇ‘.´ÇÊ^\i™SŠÆllžÐ‡K:™°&óWšÒ™ðÌÞ�X,Æ‘KbfàÈÛ¿?Sž)Ú¡ßoÀEg¤@m DCé¾Ùh}Ð0òä:©Ží¢R.L[–Aˆ0’²"ÃpRo»ÀM®ãþMƒÚ‘JV7·@æ?-°iN"m+)/³„õ'á­Me3žN$¥C ôÒˆÒtݧ™µh%:æ-A¡oì B‚š·€°¨Ž®ÂQÐ@UÌ”¾Y Ü~föo�%}$<.Š˜£o�étšáªåö”ÇàñO{¼ÖS[L€7»ç, ¤T3Y€Í­H)ñƒ¯}?ÕKÒI»­§뮸yÄCÑÞÕn+v‚“’xYnveþú,ÏÉáø1bÚnqC ÈbûÖøÊ'¾ˆƒÛv£\*ïבH¤@= ÛAÏå�ÆE¯ø0à$¨Ütôo¿ÀÁ}²Z‚ •Èbõ ߋ޵—‚ÑLq‚‘Hbæ|,bb˜ªÙö‚Ž»ƒz¶eäÖ_cu'ÔèÐà§‚ ü“œó«ÕB‰•lzBx*R–m;ÂoÔ L‚ €�ÉäH3NKùS9NJI@£7&Ê¿ëºzµÕJ¥aU?©p£®¿$É�w'ÊhÅ;Z`ÓòÁiƒRWZÂz¦ã¸s“ÙLw‡Ïru„A JŤ3YTô™ærì\' s— kÉj^°]@A¯oCml�èFUÔ€“´!kPaˆ á£\œ!ƒÓÏ߀ñÁ¡êÔèh9 ÂRÎf±D x>\Ï㬿x=•Ú¸VÙì7ì8(˜u¼ô5=5ƒï廸놛ÐðˆÂˆó´ ÏÑ /@{g�#Þ¬çV‘q„“Qæ8Øœ|Þ'®Tpr R± IXu *÷jßœO¾ë£(ŽLзøŽŽnÌLM‘gd9–�ž‹¯Ã¹/y?”“ ŠóæO¼ ¥Ã» CéTÉEkpŽÎj¼\{lf†÷[ ;Ñ„ÉN M™;Y pÒIüÌq6Þ„É›Š•=^428øÙÛn¸ñ:ûÎyŽ;?ƒ+58<@G"™Ê¶uwõN��‘” G§-:cwu?ð"¥/Ù¯%’IG)À}'“ÍA÷kÆÔ` iËnP‚»`Y¶Ö+­ø�Ø´œ>ç8¶³Ä±í‡Á±/Ñ€0@‡ëØiÝ$õh' ¡ ˆúi;.©HaX? Wè@!›CX+brfQ@úu’îÃ�,�mÛ&åâE+Wbþ’ÅÅ�[·übòøà¸•«Ï]¡™Rd Y\zýÕ¸æÑ×ê2×"X–ˆ h>è=ž«:‚u©!P)—ñ›ÿ¿ÿþ/àW$Á/ pîC.Æs_ú’17{ĵŸÿs½Ì"Ô£vÇ®-¾-—Ã9I)nb|÷Üy/6Þ½ þþ/iûd2a[�…@B?/רԪÈi•çóßø%ä:»1¶åfì Îªå´õôB«†ÕYA€ÿzm‹½U00þ5 ›ùç1À1½>°×ÌL±Ùºœ‘­·á𯿊eŽ:zèç;ïºç‰›êeÖ·uYZ²§ JõX¦”Z㹉Ei=Ú¯�h^d‰9”©GDœ˜Ðh3 á:ÞX·A©±t:;^ª·n u´âÿ4Z`Ó2`KJ)ç+õx¤¾Imp\w¹ãyI©¤c[¶Æ!GØ®«µª#„ ©t½ù|P€-ª‡S=Ý4z+%•Ìd¤íØå¥§®9~bàÄþý÷Üó‘B¾ý iÙÉÄã¼dòLa‰6×r¨ä±xõ2<ä±×་Ö!GÊÑbÖ|üfŸ‡áôß_ÿèW8õJ é½^õÄëñøg<Ùlæ�3§ÙA&îÁâÁËbÖ^ÿìD ô71<8‚›oºþÕ0vh€JEcc£Ô‡sóO_ƒÇ<ë™Ø³i îúÝPªÖiJ¾K‹´®×"œžccÛ7Þ�¡›~�0ð(èìõ‚·N:O`Àí(Œ—¿/‹ÉÒðÁͤ €°�L{6ºÝ<äIǙܻü)¬èËcjøØ[nºåQw–&‡ge]¶uÙ¾ï;÷•×ä˜Bý4,kXj«_ ÑŠ¿¯h�M+6ä;:ƒ0<3 ‚k”Rk5ÖtY–Ý@9 7ÙÑÙíêÌÇÑãX–%¦§§„%„ ÃPãET×MYo?¬WÖ4èT†?hGØAÝê#ºgk­]ÒÕ—ÒÇu‹ÖœúbË—js©þÀ !�Iaõ†³ñ¨'?«O[mT‹g²'mƒ˜ÐšVšþ{nüéo164‚(”ÄJºî�ÅÃs ’©$âª'mS³Ÿ;ž¦ÄArv0Š—óD‘ÂäÄÆFÇqËßnÃúkL �Ó L C8Ž ]6‚“H`Ãõ×àqO:,YŒ_}ÿøÚ»ß‡b¹F=š”ö±ÙðÆ/¢61„{?ò2šž÷I$ ÝXñ´×`îú‡’ئ�ÏÞ·c��2ÚÆµ�EÌ{†eFì5&“ÄE±•e6{ðI,îÉÁ/�oÛø—¿>oS£rZñÏ-°iÅY^º©hŽmÙËÇ-hp9W»-HxÉTçÜþ¬�Lˆ©�Á’FJDºÄv\)5iY¢¡€×qj¡”®4jC»e Áâž>«46¶¡­kγ´¬Í–Àª|®-=:1ŠJy}óæá‰/x:.¼â"´wu˜ÒÚƒßÇf&j"ïß½¿ÕYÎÐáãÅéëÎÖ̸'`á²ÅàY—ž5þS`óà:ã� ¨¤ä<1>‰m[¶ãßüÆ� A8êõǃ纘ž™F¶�Ç…×<×=ùIX¶zÍŒÀ-üÞóüBJää™Éã¢ü#»î!iÛv‘këDfÎ|œúŠ�R�Îd*Žx`â_È´õ\+n0f3#-Ó¤ðÌ€‡zŽm»MS°?„î|²2¹gÇí·?]»[Þ�VüëD lZqN&゚ɤsž_«æk~5‘Jç‚B®°FФnÂo‘J–T‰›Æ‡Bàä±.×¾ZW[Žóosç-X¯÷mŸ™™&S°Þ¾>,]u νl=ÎZ·]49'Vò`ÌW&Ç'1xìeMZ\A[ݘcý•Ù">Äcv¡LÌÊÙVñ'c#c¸÷®�¸]«6ï¾sJS3®‹\.�t:]$ÀÉ·µaźsqÙu×bíúuÈ àñ×ßÿžÀ& -è6:|¿†µïøƵ=Dq㟡KžÈ¶w£ëâëqÊõÏ,J1é}~IÜPŒ?‚KÑÄ%ý‹^�[¨f_›q €»¿ù´g“°ë{¶ßzÛ 6û՛Њÿn´À¦­8¿Ð•¨Ö*çÚ®û×u/ì훷Р‚ ^ÒÅ¢SOÁe½B³ÖÎ'‡Ëö�ž¥ÌuòaþÙk\ñãÅ1•ŠƒœÀÉ#.ÔIÔåý{àÆßÞˆ-wmÒ½˜TúÒ@N,AÇq©áï>©$οî\¯3™¹ è×30II½MñÅö{7â o7††àz)H`õ Þ�Á›ƒòñ}Ä:L¶uaÅS_‹¹ë®jöz×Â3™\9€^76L}9.èÀm¡-Ð~´p‰¤ÁM7cß÷?GùðS{mÝöŽ-AíhÅÿH´À¦­¬©§Wø“S ³™üÅ]ÝWZŽu™€è×ì6K�VUgO'Öœw®¸æj,^¾˜$p„°Ð 2êäl1¨YiVªuüpñcć4ÅI‡(í6=5�cÇpÛ_nÅ_ùŒ Qçº tw÷ÒÄ»6ë¢u‹Öè&¾.—�»a=æ-\„™©IÌÌÌ 8=ƒý;w’›Ù‘ƒ09>ލVGé¾cïÙ‡À*ËñÐ{ÁÃP<¶µñ!xé Ò=ó°æÅDvÎ<&Ùcs3î€JiÆJ<.þÀ�Êd@<Ì~£[nÁ¾}²VRrâØî‘#‡ß·5¨­øŸŒØ´¢—vÏMêy‘ �Ú}™Îå==½ç( “L$Çu„–œÁŠ3VaÕY§båi«ÐÓ?Ét¢p°œgŒñ ã$8ŠpÖŸgå=s=3…z­�Áƒ8qüvm݉�º\v|û^$“i”FàSf",�L:Knœ®}8úæöcî’%Ðl>Ü{Ûí:qƒ‡�`øÐaÔK%ˆH3P éÁõ<½m#£¨Vkt£OåÚ�í™ �Ä}¯§2È®:ËžðrÀM‚hòæ>ïÅHFðlNsn&Xì»WhŽXæÄçv†wê>Ò7?Y/C”GöØÿ�-~íhÅÿt´À¦­83�³†yÛrÖ(¨‡'‰‹Û í§¸©dgG{§595Žd.…ÓÏ[‹Ëv%Vœº‚,‘]×%•pÛă7â`ï§œœ…ÀŠ&N2”8qì8¶mÚ†ñÑqìØ´ÇöÂÄàÍ%©ûïèÙL4õ®"%ÈÎ[p“IÌ_v ÙAìßÏq‰¾mt¾×¥ãè’ýR÷e+Ù,±Ì ]˜žœÀÈÀqD ZIù·õ/‚ž„„•Èà]Bë8ulKÄšý“ó7êÊM¥5Áú1ÌšrrV›Å=C�~×r5;¿ù!4¦†1¿3[Û³uól×ýô­Ã ­øßˆØ´¢—÷/ÒÚ“^r®mY�’ˆ®Ò̵S);eiY$òI\òÐ˱î è_0él†nÐñ¼zk�“GÜ«F�ô‘Œˆ·W3ànÿË-ت›üÃÇP«�|=a€†æ\‰¤1õ']/}}4³¤ ðhÇq‘Ñ ’J¦ IŠ%Dµ^ƒë¸4l›Ðë5HÑ£­�¥MôÐÞ3ý¤‚Z ûî¸ ~¢�)ÜÎ9P¶KêÉg¼ð=p2m€,ž}°O¾QxÀ¢ ds7Ìfœ;¼r�5“QLîÛŠ=š�”¦Ð•”rÏ–ÍŠ¢è-÷§"´â3Z`ÓŠV\³x¥@¤ ¾ß8³î7žì¥’W)¨Î §Ã0õzÝóûpú¹gÑ@è­(íz.õtlÛ~P•ϸt Çœ\¢FÁˆ=†Aˆ‘áÔYË®m;±Kg2ÃGO 4YDJÒ€3ˆ|'¿ÊÁhP3€ôs.›£¬E+vCƒ-¦f&é®ë’JƒH¥”Ét÷ÍEWo hÓÓÑß�D&‹t¾@eÆÑ£G0¸m3ŽîÙƒ†ïÂÞG÷{d¡]\�—^ ‘�Š-èÂŽ™¥i66�±Òh_Ê„g¥IC̶Í6Îwb‹�jeÌɈhï¶m¿ ƒà±+3­ø¿ˆØ´¢öÍA©Ü¡äÚ\&we"›¹Èñœù„„.?A»'ÂM:X¢•¯XŠÞy}”íh5êq�¾°@å5q�‰aÐl6*Šî/s)§‹¤|0:<†Ø¿k/¶iYuº„ îC‹!ƒFmñ%%õS4Ù�Ža ‹deÒ©4ʵ2 � ”¹;´]ú@32Ù¶NÊV’ím”ÁôÎ_„\O’ ÙŽ(!ôôÃý�¶e£¡Ïôž»p`Û§&! е`¬EgaÉc^ŒÂ¼¥›Êf„m‚=gÍ|fÀèÑœÖ æ’©˜´9wÒäªÑSZ£mûçß;l Ãõƒ7þNA=zS¥Ø›“G lZÑŠk­´´ÝaGCƒ”á2KXËôM6H§2n©RJΧ3mù6aYv5áyv(ÃPJYL%ÓåR¥ºŽ;æ8îñ0 ”^ï(¨¥¶e_ÛPÁ“õú¹€¨ ¥÷E*�¢æx¾-�®¾9äc“ʦѿx>ºçt#›Ë‚L²\‡@Èu]}JDR(Y– ‹Êq@i¦ˆá¡hÙ~ê·ìß±GƒHÇv€Î@ÈjyøÄäýÊÙúš¨|fYÍ”ÊEx®@p˜LÀq\:·*ƒi ¢k€R öY­ ÛvBaíWã´ ià, ›+ ÐÙÛq ÆÆGôþ3t\ÜŒ$½ô4èÐã ÍTÛwçm(NŒ�2y´Ÿ}5V>åUäÚ*)31€Ál¿;Öóñ"Fȳ,Æ\Sl;–íHÅBûK¥”ŽîNnÿÂ[áD>rªRݽeË Q>^ë£hÅ¿L8hE+þ ±6‘Éè’ϳu�ë8–ã8 :…Fà'„°r }}s½0ô¾ #)]דޣÊûžÉBÔeIKX"��­”j(O¸¹\¡Ë°…­AaSaˆ„—@.߆J±ŠÑ¡1RwÖçÄ_~ue™4� `�EEŠŒÖT¨(ãÑ™±¸ê~�Êb�ï£83¿îÓŒ‹¹Gj†¦Ý)((È€²š Ã@„m#“/ÐqËFÊÊÐëdƒ‘ü="¸ù¼î±,×d€¸ó¿†¬”4Pæ1ÑR¬Xs/a² ½�\�™ÑÑÏ) ¥'�hׂ8²c+ÜJžmÃ)ôÀËæH•Ûd`YIAIa1»pò/Š÷b(Ìz“ÍXÍÔgÞÓaR8F?�€."õkJÂRI)”R­x°he6­h…ÖAs…m¿©«gÎÓÿ�½k¶%Y¶«ªº{ûHW˜¹ÏÆ·mÛ¶mÛ¶m[z¶îü1®}¸ÙBÕïXÑQ'Î|K½&*NïÞÍ÷fríÌ\™ `X{3Ñîîv¯Èó@Aic WWÖù«¿*KZ·0ˆPV¥«?#ÍR7Žlžg®†Ò*êD*ŠB“f™2A@ƒÖ«ÌK$ñ‚äC�ÅZÊÆñ㨙 ÉbÁª{0Á ôHHƒè˜ÑÆ`Ïá,�5Ò4å5 CM¯¦  —Â$¼u½Õ5lž<…•§èéY عy�çWֲ݌‰"ln�ÀêÖ6êcG+«|�µ�-,-¯â·~ìûqßK_xâóÞïý1ŸX_÷´Ÿ#!¡ìímcoR³“ç©S/Õä“jE_í�=€G_ùr$ó1ôè8†oñ^Ø|þÛ#\ZCi…ç¢6kBi~hÌïþìÀ Ù&ai’�߀P~×7zH~ñ'¯ïò Åìòýѽ?ñuÐñL±Ø¿}ùêßc>ê¥ûw ´x<´žM‹ïrêœreùÎøPgí‰8YtµÖLpïííÐÛPMž¢þ®&œUÌçs¤,^4PZ«¨ÓE0RÖVZ,_è××(ª]pú§ ÎJ²”¹ˆåõMεŸìí"‰çHJ‹2ÍIU½,ÃNɧ(rhmxï:qOÂ˲„VÉ&žÏ`L@¯(І /ןø$ÜU{!+5ÁlÝý’J¿?À`4Âhy•Íþþ.¯µ˜ÍP–%IÕ!‰ª~?¾¯ÛçgÀAë€dwîéÏÆ•:Ç’e1n\x„ÓN·N�ñ&Y;eÁ@D YŒ"{pÉó ÏzÒé7yÚXèÝ+ˆnÜ •žAÕ¢(ðëj˜ÞÒ€’Ö5JHBòJ$4zA€ó<é@x²jBÞ¥yTÓY;5ZE§œ¡×3ý[p½²,ÐâZ²iÑâ¹Ý�veiövîtµÖŸ0\Y9½²¶ÞÝŽ!Ë2†mrgiÀ+¥°T“ŒvŽÞÂheÃ!Bm0Z^Ao´Dï I\|ýëX‹ÂV8ö”§Ò˜Ï“ wíù`0byc“ ¬¨Û¥ºË) +2v}ïî`~°�4^Л©Ê’×oßAº˜#Í2œÚÜÂÙ»ž@UXÉ„~„ªªxýÇο·®¡Ûá-Þïqüì9Ôï…aã(u´}N‘ìófªÝáp@š¥°Öò}�s¼~žg$­îÓZ£;rŒC‘w0�cìݼ÷ìçyÓǘü‡V€‹ƒ£0€B.QäeYÆÿ�žñ–oƒ³O~®?ö–Vz¸|ùÂÎ:Ö«Ö$�p Š1Ÿ0¸¿pߥLS{Szj´JÂc͵,½0‰AÄh>7\¥¢þ�þ8æ³Y^E  z ´ðÑ’M‹FkU)�²*�vú‰ÐèeYŠNwO}ã7ÅJmÐww9w«ÇŽ×ä°I ‡uºô<¶¶N0œäà0«Iâ—/>†ñ�Ûè-­à­ßûý±yê Dqåšœ… ï×¼bþåà`A·‡îhýÄ'ÑøVUÅã{½>úœÅOÃÏ"ÇN·+¿Âéá°7Yã¡×¾’H�k8ûÄ'ó[Yä¾Ì—8г) ñ>ó4J)®ÊZ”Cóû’!D‹ÀðÐípãÆUyÆ0ÝÍ«—QÉp�¼�u$€�ã²°0oC/.ƒÖKˆ}œ8õ|ò½/yl–bl5–Ï< *:WIކKUVÈAH£! ˆ ¯Àój޶¸Q²Óójãy7EY"™ìc#0Z2PÊYk{¦há£%›-^Ï*Õ;nžz^g4?~"HÓU§ƒ7{×÷„ܺuyžÓHúÕþ®1œô,,èÜ·W“Ìd{U–áÆÅG'1˜ÜG“g˜(<¢á÷ÎRv\Ù p`( ÞôK FK aùÆQJáyÉü\¤vvq©C=õ¹oð>ŠÏ¯bâ3;º:BjÍþ Î:~/ΠɧRpÎH‚B¯ªÅyšb<Ÿ‚dªœõó1$\�ÓB:ŽÒkv àûò3@ÁC^¯šÌš3Úb~áL®¿)¶Ö¶`¡Åq‚mþjwxîŒx<ðš$¤FD xÅ¡²ßï?ꋲɲñfù½Nׄa$ñâÉî Åÿh´hñ/íqvæn¨gúý k+mL€ÓO:†ôbçd ˜õŒ!hH­sÌÙˆ%Äy–ÒHö¢.ò4/!¿ðil¹ žŽ„‘(c¦GTUIABY–$š¢(X©@xmñ��„½¬¥Ñ266�!žNT QÊp3r>7’4A.rd¥áÀý�g'¨¬J¤<ÇÆÖqŒÖ6ÐéöR´YÆÐœóŠ)¢�¡>¾ Ÿ¡Ù/d€Dãlçm8b'éùt »gÓ‹Eds+Rgˆæ×ÕTîèØgˆTZÆCû�{D¥V5ÛÒ{ òའèÝÍs“¦I ”ÒhñxhɦE‹l«¢Ìß½ªÊî|6Åd:©gÓ<“y‰ 0õ „hø�WCCÚlË÷JQÒ .æ3ìmßbG‘\4I©^ à¯yªÅ†ê,„´b¨H§Q ÃØB¿”��¨žô‘¶2ô&„Dhä4$i�†Èh´­(ã´x>|Ž’÷tcA«<Çb>gíÍ è5I˜JÊæ=Ðó°¶ñ¼ø¹ñ¼Þ¯yzσ£§EO%4” qâäiÜZ^!¹Ö´�2e �¼³„ I$J‘Täº^Íñ¯�œ~F½.Ö��Àïz9Òxf&ãý»÷Ïÿå_­�~î8¯Ôªá+qG“ûÖë ýO‹êh£Í£³åä»ÃóqÊÅU<ƒ+RÄó™M“$rÎn ÅQ´žM‹L¼Ÿ�z½Q¯ß×& àŒ� Cð$NPVÌ/ˆ'àDZ(ˆt—ƒµL–GËT�‰T˜ääy6~!‡�ƒ×¤gÓ¯Ém1Àõw\ 'ù!~Þïc6›ð5ñÑx÷ˆQUôR¤ŸIEÞÓY8Ëû_ÀаèÒÚ:ÛáENâºsýª¼¯çY8^‹¯¡!@ò7º!¥’Ÿ…ØGõõÓ$F¿?D]+ÔÿÛýÁ­�Êÿ\ûFqI4n‹vBVÅ¿ Éd%ÉõH¸LdÑl‰S�êµJÎ…„ �|>��î i€›9çn£Åã¡%›-ê>gÃ"M«�ÝmÄ‹9–WV¡5“þœy/ÆQÂ<êCl­“öûôêÏ,üÜÞ½z"e ÿg±ó-­WóR²‰¥å5Kü Nþu˜«¨ 4Æ`ï´À˜&¦±±¾…ååUt:d‹9ßE1Æä·Ð÷„–„"¹$¿} �?„(y¶È¤sNÒ£7X6VVÖ˜ïºyùX?䄜ÙG ¼Žÿ7O‹÷çâ±ʪï†û–×Öé)&Àh´ÔQEqvvý‘o…x-! .ŸÏŒ"9jDà“’¦'æåx<~v$ª† œ&ùÔÏ‚¥åœzò“U¯Ó[ Åã¡%›-ÆÓñÙš†ÖVêäɳxÚsŸ14q¼ q—pŒ„ÒààÕ̓ð$ž“¥)“óÝ^Ÿd#¹OÑåm‹s6Zi{)„TP¨$åz¬Ô©p9”†«·p zت¿s2#+dI"�ç€sâáð~òî$"©ô†T™ÍæSŠöîÜbH€ä¦x=A’'¯!„Ãåq0ŸÝ�ƒEÝ[ûÌëë'ñ"4&8÷{ŸòîÓçS·aOCŠ1¹HâÉXð¯_Í'yÏ ²<ŸÛ$BÈà_>S’¦0¨Ò,ÐÆ¬£EK6-Zi¼¹º®Š"› …:ÃN~M4ŒÌ‹49 Ÿ|Ä�rŸVL²¯¯®3ÜÓíö¨N“¤;�‘‚K!*šmrFk=(ÕO2Aƒ_‘ ÷%ÑÀA¼6Ö,ËÆ„(óŒçA)x�<wËäPÛŒ=L$‚Fè J6™ÁÃëpâfé Ò9‹ì`LEžÔÕÀ� ùΞ¢Î—•{j=Pö ïã Pæ¢ó +òåt1Þs·Ô"ÔH¨ZSB6~� ·ËF"]4d"€RxÏ÷>¥s´„�Bíu!Hæˆã˜µ@UU¹¢,”•Xc -Ù´hQÎýn§·´¾±E#wp°„°¶1|P4ÔB0’¨Cíi°j[ÛÜBVäì1vãæ5ÌâÄ@+øò&�X´Ñô*1ô$9./Þ”£kÄ ò  "Œ!4pñz* ‘9Ÿ-O3î“ç“¿FGò)`ÞHÞYȈ�°!„<À @8£7 S¯$YÔ+Æd»·o‰´™ËHØ ¹pÉ=KÌ�—S pöIO‘s� LW+õ87\n©XÌïÝAÏQ•Õá¶5* „p„€dâIý ó?~ªKˆŒ;,L`äªWÇÓâ$¢Å´dÓ¢…s½²*Wªªì†Q„îp„Õµu(‘7S†k¡¼"Lî÷È@kã{lç罤 Øå˜ÆS¼ ùVL�^>ЉãÏŽ“J{#Õîz“dìoV•åÚò«;Ï@IŽ„õIô"(d�¹2aˆÚ�D*ž‡¿ æc ¤@Éø¹§<QÔC„ ½ë:ƒá1ñÞñÞû¼½b§̯?†Ë/ø}ÜóóߎGþä°ñ€B‡#"�#û|•™�¼Éÿø=Ò¬ä…6ì të›ô`˲༴Z²iÑb8\:§”êåe®Â0ÂæÖqô#JšÄ¬O!ñx*-8�H¢›âÏ ñv€0ꀿxΣQEEÏÅÙæ<€Û¢@sôp ûœiŸ„“èÜgHhŽIùšP$OÃkÉ9¯n'ä µˆ õ¥Õ h(�4*ž8'Zs}Ž à}xMOî Ï1¢¾ã; 1Ô‹ã­Ãœ£’Ž"‰ÅlÖxdJH‰¢Ù–Ü“/8Ò �cY5Z¢´zwgqwj’¼û+ßç=ìsV³O{Ú ~Hí\AöàËpå~çøËqùE€"M a³RÇ÷lp¸Æ¦löéâ¥ÇZó66‹Q4»‚/s”ÔÄ«-‚–lZ´0:(Â0ªò4Ãl:Eæ*ôGC`I\‹±ÐK¨û~`‘CQ6=X^¥‡ÔíõÐUšu.4Ê^N ¹¤xL^RÞÉ1 3Iõ>?‹‡~–žbÒc €4À$�DA€Šíex¬ÜÏóŽÄScsP¥åýXËÓtfnžÓ«ö8'Äìïí°Mó9Úp$‚H»iÈE�& H˜NBs„štOàgNS@œ&ë î=ÿò—S<_,--c}m›£âWÿ)îý¾ÏÃ�×½U‘KèÌ/æ$ù¼žh$%ÈDOÂhpÍ®> ]f�w7A Œ19Z-Ù´hÑïõ9`i´âŒ Ð)g•d¸$ì=x†_¤ 1�TKݸu·ïÜb�ÎþîÃ_î‚„¾À’œG�€Ÿý0œd£¹d¦ à䦲-†šç,Æ$·(ÙIJÈrñ¢H:EQòZ~x�ä iaÓ‰ÜYˆ®âñp$‡J¼þUÚàÌŸJ¯ªßëS•—ìîb1�y9uCHbØ¥E�ßcŽgø Nùyee•³M·mSc5 £g½ýÖ)ý’ɶµeõ³‹ƒñ7M&“KEQØ"ÏÑ5áÁe\ûÙ¯ÆÅ_ÿ$Û×€2÷É…‚Qà"¬l‹"ÍË•Ù4ư߃„µ1*â ïðÑ’M‹LʇӃ}W“³Ð�n¯^( Qˆ•�_øRýîמxän––ÀAeÃ`>3aotÆ2†K¼¹+ï¥�‘^�á¸|Õ–Ï|â¡øy À$~‰î`%‰}’OôÂx":àý¼œ‰ßQš÷⢥¬[pH“L·CU_–%˜0¬–¼·Ev •¼»žÐBH�ÝŸ�å~ ½­n£WSä8R[›Pióäú}xÙx'›Ç‹º}åÊ'_¿|éÞÙd’-3h8¬õØ×ÿ1.|÷§àòÿ4âštÉíˆX@dÓG±ù^P4\B–&˜N&¨Ê²ºöÐÃó4K÷ÐBÐ’M‹ž7²F�¨, E­¤@r6Ÿ‰¡%—òƒïMp[á¯mÃh8ÂÆú& ý|:ᵸ¼ÒâÙˆêÌ+þä Ç»IIòCrOñ�š-…áò =cŒßjFŠ4ÙÚF G}”E!ª¸£ä¦”«”��Èš)ûÞÜ:ÉÎ@–Ä4È~­Œiºhr}y?'Ÿ�xqº ýÉð7ñÀò2mUn(À ÁkæcûÚdöTîÓ餋d7Ï2׉:脽r‚í?þq¼î«?7^ùçÈgþBz•ŠúÌy 6!„”Y⪪DM8YM~pζíjŽ¢%›-´1ýã§Ok¾Q½~Ÿ²\INÇñ‚d „r¸£@�5.†&žŠâDÏ$M((‹œíö�ƒKЇ!óqDmÅm[Y.o:Zs HÒÆ(/ÒÆÓhà�1p�ó¤›�ÇU2g!Ç GK E*4ù([aAÒ†?µ!^û—Ò¢7ÓˆäÞw¸²‚n§‹¨ÓÁúÚêíN½¹¬*Æ+¦{×ç~ºªÜWVyyßîÞn'IM:!Nn®¡WÎpùG>ç¿ÿsðÐoÿ¦×. �°Û¶p-ÞèhÉõô6O"ˆ:Ž�²µ®´6. ¢)Z-Ù´háþ´ŽùQd•6¬ ‘Ù0"W&)Xx91¶�Ÿ?CãiX»2™Ðà;/_Iÿ/á/Þ«:¢Æ’æ›Ò‡Í÷†¸¸ÍýÂKr.ÇYËvUlÑ/ýΘ±–K”qô|¥ÍÒ–†D)צhÀYOç$Ä}BÐ�ÁÓý=ÖÙÔž» ìݺQ&�€ðˆûð;k黿5.Õâiñy•6`¨3ì {vM†¡Ñúp¯œíO6Ö6~>�~òÕ �þÒí›7nŒÒ<Ïݱ­ctn܇ƒ?ûiÜó­�ü+ñèïþ(f7. ÌSñb·¶AÃÕÚ ÎJ[U•‹¢(tÎuŠªÈÐâ0Z²iÑ‚k¥“~oàFKËœ$I; ©–‡ß0SÂHþ\ùÚó4–Öב¤ ·;aıÆ~ç‘{ª—Üy ”ú‚x:ýÄ—.ÿþ­ë×nìïïÅ\·ÓÁR¿^*…½ðÌ_ô›xÍw|:^ûã_ƒ{~ñ;pëu/ÄäúE¤‹ŠÊI±'ߥr.(ËR¥q¬¬³«®Z-Ù´h1�gA¥0Œ¢Ž®ÊecÐ¥³²H‰E!懂˜ÐZ”Hðs+ë̛Ԟ�ìnC¾#®ô2Õ`~†pÒO@$Ö<ÇÛì�)HãC‚õ{!¬Ã]€?�RêrøW:H÷j™=#^•ÜÏD€š��.?› À &m~V@ÔéPþ\PiÇXh†�m!"!S/'å¼BWæ¢pæ)OG-mæ5²<Õ®7_ÌWñÏàï‹4ýû2ûk¥Ôg•Yþy{·n¿r¼°m­Í³4sU‘ÃU›a¥˜ }íŸãæŸÿ^ûÝŸƒ××뾟ù<ð›?„�^ƒƒ«�"Ÿì#ìô Èv‘VPç\-Ž¢%›-T•L§Yž¥NHÓTÂD )†ž$\eÅÓ`^¼!gšâ9lµ¿ºNÒràôN¿^Œ'ÿ�0“íÞ/~ñz¸¥H2Ò¼Ò˱Hˆ�—ç>Þ³¬¨†c¿,A‚ƒß �<¢†;ÂÒr]B¾—óEîM0•c9º,‹z•ÜY¦ISCCR’™=yg._^®HÎ䙳)ön^Gœ,è9ÆI¬N>ñÜSÃ¥áæó—Vþ8ŸÇãzÐÛ„aøAóñø®]¼øõ߇Š8»•Äñ4˲2McOWUšfr‹ó‡½¿þ<ø}Ÿ‹×~Û§á�_úäw�& ²<Ëœs©V:@ � %›-¢¨31Ú$³ùÔMgŒ÷w!¿¶¥Á¦¯Â3ˆ ’3ñs€4·ÕD“Uâ$¦!]LÆô.Äo/É/j�Üâò£2ö�ñLj·¤u€­»ÏñÞy‘“|³ rOåu—ö¼..m¸}$�¤½| É„„òH kÇO’l‚f,öl<¦TÙëÇãŽÖ.‰üš^„Žýc–VWqâ‰O¦×!z½öoÞ¹n kzA¤ð/Äï?ü÷î¥Û“Áúê_êS{�Þ×ÄüÉlþâñÞÞ}ñb1®ŸyQUe†‘+óÚXîè"ÃR9Ag|Ê–ôÚ¢N¤•VûÖÚZ-Ù´hál57Æ”uΦ —`”b—ä }Ý�¬?'ØïžŒÆKPÒòß�¹�ͳw3™½uü8lžÃJÛQ­I¾!.,²Ôò@Bm)¶ö×BG�¶ò4¥ºm4ZƉÓwAC)BOÇëüÜ|Ï“ñÞ•ðr8²Ï[šìî0t(ʾ½kWØg$<§b#Ž†ì„¼ändÖO�E£æpŒRè„¡­·ñ¯Å ®^r¯YŒó¿¸~áAmÌ�m>v1�}Ñtÿà»kâùÃÉÞÁën\½úèÞ�;;û{{Óýýý$ £"0Æ)¥\ ÒV]…sW¡0G‹ÿWТſ½nÿb^ä‰Ñ¦ˆ:ÝN¶X ^L±ÜݤWÃ_ò€�„Ô¤E —³¬ñZÛ8þr?vú,®žó'ÛWipÑéõ!,@‡x1ÒÜ^¨IÚÃÀq[6¹-äæ“ˆS€mŠHÙ¯K&îý�—?=S¶µ„«£yü @CŽñ{·-ol êõP{Š" §§ÓÈ÷rŠÞ�×HÔ ¥iæÅÂÃ3ƒ´†2†ÞÚœÍ/KDA&qüd gø`ÿFüÕ�‹ÀÀ ÞnãÄ‹àîX’ÄO­€gÁº·-“l5™ÎFa ¨jèÁ|6›”Aþk� áuiS´ÓÂGK6-Z$Y²_…½}û† £BJw÷Y'Cå¤3±ä6ÐÀAA*îA²XG‚�cÇ •fšx|qœ`°l›ÄºÆáj�àWÊMäf’7’¿ðs.N‹BHþÉt)д”cƒž‚xR¢6óÃrÖ{ÿªbþ‡+Š:’K‘¯»´²Š¥åUì%1=1«”„ìH0ÚvP²¿)ö< vÃæ1R›ToSR„aÀïkBê†&DQä ó1þ#ðâÝ[@ à êõ¼áÒßTEõc•Í{a>OA­O'ã kíj·Ûýã¼(¬‰¦B‹ÇCK6-Z¨~·TÓ²2&(ÙÞ%I°{ç66N�FÕ^­ýÌâHCMI`kHciyòì9VÔ‡�Ápi•‰rãå-œ´�ñš{ £i¤ÁvýÖ5j7ˆ' Þ��ÎánÉÎÑ×RnŠvo’ð„Œäx¿f&ÍH˜�Û>$̦)£vÒï�ÄÚ€$:ZY£ð`Ð Í2ÕhNFj{B �pD¨ î—¼Ž¨IŒÊÁjCAEu0ïc´¾¾çÉr'€ÿ 8?Ÿ’|¸²Å à-6�«"IÔ«÷n[´ø§ÐælZ´(ãTe±0aØé‡8qüŠ,£Q4&�Aeü+ëÐôÊf¢'‰"×s*Õe¨0„(Sþ쓃ƒbá5µ„ޏíK€�•mvhæ}á…Áä8 Å�ïÜfR~2Ó[¡:¬ªdÍ‘:!9—ގל󰗣¼N $@Âq%,Š¢`N¥×bº·+t!Ic(aÿr[ëfFeÒ2F›†Ó4åèë^§‡*ËuY–'\ÿ…xåÎm÷†ùÄ¢Å?‡–lZ´èahtp±×ïÛn¯¥�k�>Œx>§áó»9‹üN2 õH;.ŠÝ£�Ýýù[7¯â‘î¥P¢,“ñÒÜ/„¿ 4Î^O5‹ˆa$© O5ÆëQ’|ëê%ÄÉŒìõc§XÉoc–‹‡!=Ú¤�R<�P©äs„x…h„tV661XYå{ëÆ )’„Ç’\è)BˆR’þB6¼¿ŒÌ®ÊÉbNÑÁî­›xø ¯ÃÅ¿ÒEŒùlŠýƒ=ìïnëÏ‹�Ñh4\ÌgêÊC÷c÷ö[�È«|—$ºægIŒË´K[Iø‰ßBT¤?wÛ—é³w!?c6³àB4¼CR¼~™g¨ÊŠ’ÎÓŽ÷fý ÒÙ HsLoß¡±%AȲŒ‰}ÓŒtŽt€a„8�á qþå/ÁíZV”$s¤³uì(@ϪÌSö‚[¤ Ê2‡jò#ô¢à(36&�^eÜÃóí]¬nl¡È3N«<ó„'Á–ÖNœÂµûkr­ >gá*̧S(€5G5�ð~Žï¸�13ܼð&7®#0†ä�' ûlÿ³²¼ÊZ›0 Užå]gm…d†-þÇ‘M‹µÜÙÖóÒÚØ];~ìØÉîJ·›—ãݬ�>í÷;¢ä’9,2™’¿ì½GtûCNìì÷û˜/Öœ4 @9¨íæ¥ 4 7._À$] œLQ%)²ª¤7Ðë °ˆgÈâ�xš¥½ÌJ|Ç‘$‚ÝÝ;ü®×ë“xöîÜD‘f/éð[ØØ8É aD‚‹“Ê¢¤ŒXò:Q1÷’Ä ²,%H§Ÿèw®¾nUAܹ ™—c¶,¹Là/î§ð7æg¡WYî[̦<ÇYËã k±´¼ÂP&»[+‡nÔA¨”ñÝNU¿��íÛ|În·‹ Š:YžF›wy«µ­_zùþ¶E‹ÿEPhÑâ_ˆ÷:óä3Yž~ÛÊæÆ{׆n5ìt0£WÄ)�:Ã(â¨kQÄ)o<5—e3Oæ]¢NÄ–9ÌeiªóHŒJ±—Ÿ¥ßëóEY4cJ湌1’+5G?Lø¿ÏÚæ&zý²$Ù»qùò‹ë{~î«æ7ТÅÿ4ϦE‹¢,Jë[µá “8Æl:a(ëÑ<Ãéç<�ÁélŽx:eýÈxg›¿Ê»QbPmšÑøí]¹ÄnQ@‰/L�\vk’ÛîÓs 1žOH½£Œ!®Jç0J’BË59Õûiœ“$®×51"ŒB”eÎ�T™ç˜Î¦Væ8Ͳ, ÖôتB=È�su*NM¥e ï)uD5Y‘d:$ªÎšQ)óTþ 7¥Yø*3eø×ÃÄ}–¥ §A€šIbBJi~GñÃršµG"×)¦UtZ) œò,EIòé¡ úk8÷fo…sÏxö¯_ÃõûîA²X¬Öølïà—Ñ¢Åÿ4ϦE‹·ZÞX�ÂècV¶6¿X+uW'Ð�A§ÛÇ 6Ô: Ùñ·Ì2æ ¥iè� PÙŠá(©ÙÜ<Æm84m_ 6ÃQZcóØ Ûƒ¦9%”bîÁZKù±Ô¿L§Œ†ËÈŠ ãƒ=ôGK¬]Y^^ÆíÛ·hØ«²"ñDA„:÷ÄQ ÉTUÕÌ� š¶üƒ­-L·ï`íôY Ö7Pð9�Ý¿s µ„˜^Fš&ô˜à€x6AÔëCl3µt±³ƒ¸~þ ê`e£~ß$Æbo—÷.‹œ9§(šhdۆÉ$M2t¢i–CBŽJ‡"Gu±4Zbx,OHcŽ´~Ò³Ÿ�§¼ù[R|P�òëóPñó?�|:Æt2IÓÅâ奭>¨nº9A‹ÿɦEK8ïnŒùéåÕÕ3p q1DL�|º½>ò,­½æaý!C[a!4½¥ ˆÂË8Šf6N¿?`þ£r`¸¨Óí²µKØí �c¸ÀÀD¨rs Ý¥%de›&èô-/aåø)¬­m@±82ÇJ½mCâëtz4Ô AöòM KUeÉ¿aI»†Ñîܹ‰z¯1�Žù~"PP†y>?=žŠCÏ{Ò“žN/$Kìîà/õçqëÚ5̃5ôÎ=ÎV$\¦|hWÚÔ+ ¹ ™Â*ÕÂ6ÉsÒª;�"œßdÑçÙ§>🇨ßG–gÒ›ÏôW¿ù+¸ðêWÂhåê–2»� ¾ö•Ó½ŸD‹ÿSȦE‹÷<ód]–Åû¨o¨+ߟ¹˜NÃ,ÏPÙŠ9–À†^…ÌUÙÞ¾‰,· ‡èuõq!Bc˜l¯I‡!¬þÒ Çÿ�½·€Ž#M–…ã+¢f�ÔBË2{Æö0/Ã}Ëû˜™™™™™™™™–qÈ03ƱdÉ‚æ.þûÆ©<çÓ™ŸßòVÞ[§AÕUÕíÙ/*3##–Y2³<N¥ÊÅ|6t³t] ØF­ÕÂ�É>  aÀñœÊPÌb”2X>Û:u�z B;F<Daì͈]´„£Ñ{ð\I³|lj ² ÝYdx˜ýHoçÌ™‹ÅkàæÕWð¯ÿêŸçñ¤-øgŸ#8eâS¸Ï—ÀQ0 e½¤ rH÷n!}ãs°’…M?þ³~>ÖΞE†&ùN·æìÁñçÿ4ò(„çú“p:ýªBþ#þóÞým”QÆ·»gSF/µjGýƒ_æ8îOª×ZãÉȲ<Ao ^»�Áý{¨WëØÙ9Àv?„WiÀ@Õ<�Z³‡ 3ãé› .V/âò»Þ�No¹° pbsÌE€Ð§õž)ö÷„õ ÑH“Y—B´z45–œÚ\Ž‚`–®›Æã¾Ó* x/Žc‚M¦‰€Š‚Û1&�Í™^‹„îܽƒÁár&x\ñb¨¦2§©Há0Ë ]9šÀš`ç1³Ë££Cö«â8$Ø!ŸÉ?¿¸¶�å3çп}išzÓÙäT’$ŸðçPFßΡÎ2Êx¶Úê(¿7ͳ_¥Ñæ:C÷Y^øÔÃÆÖi,­¬#sjÈV�÷È{aœ|Î#ï†éý07/Ã:q X=5j� »ŠÚ`�V ~µÂ9»ÂT†N�tÇKÎñh†©ØãF¢ ÀòÕt6å{‚Æ;Ý.µ!Iî[X.š­5èøi1c15`Ê5›fÑfS�sŠç ¯­(gݺö²$Æd<…éx0L³è™ ç€$#³-“Ÿ�ãâ$šÍ&H÷ï Ïè‰�hFÃ�›²:i ’*¢�¥Î“—.KÆcøAаLë'?4(£ŒoØ”QÆ^uÃ0Œ?6†Ÿ0—§i­®l�öêÝ%|ðÇþD,,¯bø`Žib{gfµ=ß:@¥�Øp‘*“ ¼e0f}T¢>š¾ &tÙè²4€°®4¥c’ð®]÷¹×M04•g@ ×t!NYí8å§$Ä{Ç((ʺ:@Æse.’ý@ä:%$#‘�¬hp°�Ã�ûÌpÆã1Œ Î}-Q"÷åñt…k>@@ÀðÜh€f½Óßš†Ÿ¼^Q,`ö´º¹+˜m5[oiyåbàCe|;À¦Œ2Þ¿~òD³Ñú�µZý£¦eÔ”|σëxþ“?‹«kØ~ëMDEW™,¯–Ãæ›HÄ$i†8I1Ù~ 5œ}9ÜßC’Ä`@ÒHV.¨Š >aH2-ó ,Žx¼H ®(�$Hæ"‹¶”Ëdãñ¥7~ŽƒšÇmh'ÑYWP‘ÐÉäK€Çȃ„øÎ9vç@sð`‡„f~§8I¡ŠìI�¿¿—MI4üm7¨É�ÅBÏ&°„Q„áh“”r«0«Ë àÊPi4°yåqf7´¶F^�L'?á1·b£Œ2¾•`SF^Þ¼¸¸Ôû‹++?Ú÷ƒ&@ÞßÝÆÙ÷½kçÎq�¼óÚ«ˆ&#LÆdÊ€åxPJa­Ï2¬,âÊyÛ"`±,V4»Cô,DèȺ‹T« ‘6÷¥ &™ 3 ½Q/�zð57½Ï"=¢Lß“L/ðšLéç¨ãjÖàkä�¾ÑÁÞ.úvP¯5ÈhƒíN³” ‹ã$èH9/M2È¡Už!¾óudiL9)ßYR\ퟀ’>¦i†n†ˆ rîhóÊc˜Îf¼^sþ¡Y~4°üÇQF%Ø”ñÍŠj-·wjó—÷Nnþ¦j­öŒïûÍF£˲(n™ �~èC¨4šÈóœ47¾üEŒGÜ»ws×yÄA\€‹f7€ x'�áZ !e_ ´WÖ8UÏùÍÈL2½¤y¾Q¯L@È4p<¸?ô ÇÛ'ü|–æKKz=²?:éì0fkIª÷Š4 ͺ@›·;Ò¤ó ÷ïÜÂðhGó-œEP~�ƒªìÏÐd-áq�¢/#€'½é™Ñu„ÔŠSPX�ïǃœÙ¬9¼jYöxŽ4Áò‰MtÏ_ %õüœªÓ]èAõÇ¡Œ2¾9Q‚MImnVj½ÞOY^]ûU³éô¢mÚ^oqÍFµZÝõxÏ�ü±XÞÜBŽ ¦iÒàËŒ¬®¬Ãq=Œ —½Bž#ŠYÆ� ²h‚ÅN  Èfoíÿ½Ya‹ñµÞû¬ 4 ‡…Ý•CΣgEÒÏá±$Ë�dIÏA°’¬Fv‘Å]æct‡N 'ÓK|ñìÑéÖ ÉÈ4‹û\ûÚ—Ïh�@€1‚x�3þ¶ÔnÛ¶øÈk™oâ:nc<<Âp<àobØ&ÜÀû@Äåì[1»!Ȉ�xáÌ5ÓX‚›L'°ç%Ñ�¸b{PFßÈ(Á¦Œ,¬UÚK½_¦~Êë_úr0|¸�,ÏX.ÚÙÛA¸xâ£Çò֖ܱSÌòÞ�ëØß{€ë×_ÃׯÁ^8i^Û¶ Ƕ�Kùg6Ãéõ.êµÕJ�ûÙ®CPÒ['|η�£ƒ‡!ÍzHÈÐd¦Ó�5ªtÎϘ†%džWÎAÀ€dGN�E]ja|-—LîK yg è PqŸ,ÃÁîÜŸ÷¸Äç&7]ØÍEù AE®¿¹N¾'½›, 1½ý2âp‚¼˜£Y\^ÅÊòõ&¯Í£2µ*<{l*Eë!×ÛÛ8·»@�·Édbx¾¿ìºþÏBe|£¢›2ž¯µ»Õ¥îï¾ðøc¿ÄqÜçâ(rƒ Â’ÌþÑC ^ósŠóÂÚ:ˆ ¤+êz½ñå/¢0ÜìfR ’>7$“>Þº~‡ûl^Ëð¡QМSqöLu¿}ÉB^´ž‹¡3½}j8>¯cšº=3ÄéR²*nJ PAö“¿ó=­$'�÷b!7y<¡HË<�дåïÌ�æàÞ�‚�C¹˜®€{4¤7gšu´'ôë ‡°-‹K£ÕA½³ˆi8CGôâÉ8´™ðÜ–Eó6‚í£•Ðµ3¸A€Kó>œçúÔ¯k6[v½Ùüa—-·ìÝ”ñ¿%Ø”ñÁ¥�õåÓ§~û‰S§~òµW_m÷RƒÌ4MTëux�^˜M{i‰ß\Û8µ/}CT«u,.® ÚÛ„å׊þ½_)�å9ê¡S·1™Œ0WÀ4Ñèt¸èë¾üúL�’d(zJ£óù;ôÃtpûfQUæ±u‘ :#Ni™_ ð‰bµP‰õëfs^ɾÊЮ-#àåÏþw g&BR„Ù^‡òëI‚ý á>K¯K¾Ž–©)¤ïÀÈb‚%¨ ¨7pñù—�ä<}}¦ƒçh$Ûr]�¬5€ûð:PØ8s†m³œ6� €åJPýé/4»6Ê(ãÿo”`SÆ~­éu¿åÌùó?öí›·êÛÛÅuƦ½éùxáGüH,oœÐ|‚Cÿ!¼ñiÐ2ˆˆ¥ÓH2a\AÖGîoæ Ò0‚ jƒ1j®¬¢Roà n´PÒç–Kæ"ó1¹ôIÒâzø íxôƒ‘²AFÈ:)@B'BC# ÈsQÐ(Ú<†S£  H–#,º½í·ñöµ«@Q‚Ë,fk9¤ —ó¦ÈÖPxÔ€ešrnîC|>z›¯8� `áä)œºx µZÍf›ÿއy�†Éc‘ùW¯5ùMEqŠeD‚PïâEîOÛë41çñ‘p^Feüÿ‰lÊxÜöž·û,ôzŸþâç>Ûxxÿ>ïŽ;íVW7X’yü£ÃêÉSB–L‚”åÛW_Ãä€þ÷¼ ŽÌ ìÆ’€›ÐaC¦íÓÉ£ƒ=Œ‡\Ø Î?ý,Û–¹†q¬É¯¯ü é¹H_ÛÇ�cðQ²�Ѧ‡(%+AP­‘/æf©F¹€\Bö÷°q“àMZ‡×—Ô _ãþÍ·ŽG€8—-^E@’å89¶àpI̸©L=>„�ÌàØ6|ßG–‹+kHc qR-Ûu=f§–e Œ²äæzwÑL£ˆ'­¨m\|îEŒFC•ã8Fœ&½ùóõt­i Œ2þ¿D 6e\±½—–OŸþýÍ¥¥]õ•Öôðˆ +”¢Í8�±ù®Ñ][ãB'ý€wÂÌjÞx+½,/­ Z©ÃZØ`³ß2M¤IÆÏ¸ŽÅÏ�mŽàÙÛD³Ñ¦Ü­Ö�þµr”Öì'Àˆ}€®\LŒùMôRéR +ì›EE™�+ºgnÂ*“ù¥� ßÒ2«8ŽEA€%µù#?¯í_Û€i0X†¼óÍBùHsE 9åÖD¥@>_ôAN=grpÑlÈ÷µ& è.<ù4&Ó1½uÒ4!c.‹cdÅ÷¦›mÙôöÐ…TàõV ßÿAÄqÂ,·Ùj;¶m4‹“K(£Œÿ7Q‚M�7Úê’é|ªÒjþþJ%x|6n)T£ÑbÙÅ0MŒÒ—ÈG°²uJkx'IÂGÛ´ðö�˜  ãfc&ˆëKÝ-é¯'i³(=¥ÑÍš Çvh!E!C) À�E€ÖÊákm憎�ziKoÎ34 °m[2a¹iÊÑšp¦RòAÚ1¡aƒ›LßKƒ]öv4ÀÓ3³áÑ!ïÝEEüÍ^VµÛ÷ù½©Þœç°­¢dv¸ ÒÓíˆÂ)‰ ³ù£³¸€å�M:œ&y†ƒƒ=<|øGû!Zj’=剴”fbfH¹�À¸õÈ%´¡È`³”al˜¦ùŸot=”QÆÿS”`S†§?ñ‘gžù=[[Oîn︠a“p‚j§‹~ø�BweM&î5¡JJãc2ààæM(ÞÝÇÝu•&’$ƒN;–&t…˜p°»M€òü¾_A³Ó ² jj¦a¼4½Ÿ"õ/qõ �óéž2zn¤ûâ0ôN£ðα,CJtÜôÃè}EPHÅc‡%*×u”$#‘’÷@Ú¹sáQŸ¯ Ó@’[$¤©–�iôíŒÙÕ¨¹q€ŽœUĨVkü7Œ¨¶Û… u ˰H-¯×›p-ÓáPj‹|0ŠÞMT¡+jKϨÒj¢µy‚2:Gû0-³’ð÷y”Q†z”`SƧÏ^R/5Þsî‰ÇåroåìÛwîÚÃý.T¾Ðã¿ÞîâÉO|�^²èéþ-2�¸sû†À2MZ#Og!Ô ضͲ™U,ÜøP›<!‹CÎ턳)¼Z õV[@DBzìÒ`×6Y(ùX”Ñ”Ö;ákMÒFXjZ/"ä)s1…'Œ€r> ðA�Ì&ôcê;r<ù¼”ëÂÉ×¾øy^'¸ØÛðZ ìÙä¹ ™ ”Õ,Ëä&ß_ïc2:„e˜¨×êð‚ Î?þ”Ü °WE”R$=| „ ¹FöÚ�6÷•þ•�ãz8ýÄSètùï깞Ùé.l9¶ûßÛ]i£Œ2$$J°)ãQÀôëµ_òÌ?ð{a¨3¯~åËF>‹È< *µ*â<ÃÅ~�å(Hèrø ФaˆëŸûŒ þñ=«¾·%ÄÅ]¸ô`LS!OBûÈ$ K�$  ^Î7h B@º±,îš%@Î…Uö@¢\¿eì2MþFDÉâ¬��,)rcŸ:Ý:/6]!šE7û·oC|ˆý‡»PEy­Ý[æ¾\_%ëÐÍÍtà“»zä�}Ãh†ãdèÙŸëºhÐ7å\Ò£›gÒ—EC ºô�žÑ¢Yó—ÑÏ/ç‘7ùÿ ûØžSÅ‘ƒ´cey˜©fµ%%7\˜|X�Û’õp¿|° ÌFüítyŽå­ÓãˆD�ÙdBÚºã8dû-,ö�§ŽgnÇê“ìzµžý�8sÍÞ2ÿ½]×C­V¯o=zñY¿ÛþØÓÍŽBe|ƒM—m׫¶Û¿ä‰gŸû“¶eŸ½þòË ‡Ì$*•*g0eàÊG>Š•S§‘C&䡗и ~…å¢;×^G´€4Iø7Òv»k0+u.¼–eÀq,.Ž­‚®›Ä1¢éá¨äà4{ ¿R)²@&Øu]2ˆÊ2Ÿ@²-a£ ÍX²Y(%Ò¿ÕŽeUš\ÍñãqÁ—Mls¸�f[ÃE9 ׇCEóÆÐ];³ îÜ]L‹¬¡³¹§µLM4D‘÷t`ã¥Aåˆön²LfYA"ÊR¬œÜâ÷""Çx2b™s<±”6:<@LèÙ¯yÎ,H®Ww“k,, wîHòÈRÊPƒýƒ¥$ ?Ïf—QFß¿`SÆKí%kuóÔ�¾xå±_Ïf ÷oÞRŠ>%&û4~PAgqOÿÀGÑY]ÕF‰Œ¿V:«�µ4p÷å¯Ã.ÓÂL¹Èj‹Ú¬,–©Èâ› ìß„S s¾ ,ÙøóÍ0-~NÀAÎ�Iö¡_“,úZÆ ËÐÈùõÌE—­ åSññ/zB’Á SK B7$q Ã_‹_Žî¥@¿R‘ßøÚWÉã>†…ýQUëÂqma­ñ¸ìuÑ^€JÐ|O‚tïéF<ƒçº¼æélŠfwí¥e’¦Ó1 IA ·‡ÇNâHÈ’Õ‰]6_W«ufA(Xi:®Ÿ»'xþ‡{§ÓÖùK—>¶~þüOx¶ÖvPFß`SÆ%/0Çý°‘ã' Ûo]»†=zܧ¨T*¨T«€iâÌ /`ã‘Gp|Qæ#Dnßu\È"wík_ÁÍW_¦�s–¥L&ˆÝŒ•Šù²´´>ˆPuUžÁ5*Ž�œ4Þ˜†]‹½èC’’]È ¯�½èâ˜E¯„½!è==$Óy}NÊ,t`r™§1Mfd"Wcˆš³ôR8Ð DþiÂkÐÀšç˜Cÿ`{×®"/€#4|äA piÃaÆ™ÅÂO€A.Ù! ¼iv>E’DìÇ– ¿VEµQ‡Ø˜†I¹šJP…çxpm³þqòú4ùo¦)Rw;‹Ð%x„ÙÖ]^Ææ¥ÇÐn/ QkðF!‰bïòåÇ>\­×_z߯I…2Êø~›²GÓ4–67X昿ګï¹ëVextÄ!JÓ±‘)`0�`åʬ]¸H£.Y^ÁUXú%ú, …>öoÞ„íØ4<ކ8 s Ù#p ƒÊvLøžKêþbŸ<ÞGžÎ0�MÀr�aÀ¯VQk6¡ãø ÍYdõ‰èQè±%:YÛt¹é™úñø ›oZ† éé8¦äQAT“EbFϬtÐÛ»{AµÊŒÎ2Mœ»|~£ÃߊÀ&r=,Eš½kxý:ˆˆî"aV•ñüK×Õ5àèÒ©A�¿9�7�H7Ï!ÇÂ;Ê‘Žë²,§ø%ÜAvÛ©Çž@”¦°‡„„Û7ÞÀüøçÚ+½$£‰…2Êøþ›2živTÐj~ú¹óZeÙ“;ó9š4ŽQ«Ô˜Í†Â4ŽÐ{ôQœ}òé­ª±ˆ2Š ½çz° ƒ6Ï×ç% Éèx07¨Â©w KÜŸ‡qŠ4Éi ÀÒYJú3Ÿ›YFKâp6æìX¾SÈ2R%ËµŽ¿ ò‚סk–±”oäË€¦2XúËåûBÀº4�NRf0?¯�£øÎ;¾O ³Ä�“à%¨DXÿî=^Ÿí8Ø'7¡œ÷"º (‚‘]ÌÕˆºµ”³p {¼�4Ž0�R9®_¡j =6f¡‹F€s}f(Aµ%?gñu–Ÿç KK«ÅµH°ÜˆZ·wiaâù[ŠýÝ]÷䙳Ÿ„e¾ˆ2ÊøÞ›2>¼²e®llþÈÍÍ­_98<ê¨4«4j . dœe 2ÃÀÖÓÏâò‹ï†íºE­^¿Uç3Þýû^À…«Ñha6ãúÿ¯p,IQºòM8­8A• º.Z'I!‘ŸIɉwÔi4å$ûl6%Ýzmë4éÓf‘é™t¬‘ÿSiwãÇKVº«'¯‰ -Ë‚P¢PPôhäQgŽA(Áü¬üF|®5íã$ÿ~~@ül”–Š2Áöí[xx÷ ³D^_~å-$v]L!–ÍË€�+³à²ñ!7×vA6 ë¡Z­¢½¼B@�/ÑY\Â,ŒæÛŒ™hGØ{°‹Ýû÷ ‘¦ÒW{§û({7`Ȱ)‹“—¯PûÎõ<–èÞ»ÃPçÏ\ºôS>²vÚAe|oƒM“éèc@þ ‡—wîÞU\@3ša¡Ý]€WoâÑ÷�¼ðïxuŸ}™fØ£€ëz|l6;ü*>ÿŸÿ#úGûØ{ð€ ®íúØí‡È›k� SÆ…QA¦ð ™|§­±d �¥ü|œDØ?ÜGîØ�TEëûósÐ  x'(*@®] Á™ãÈQ”»LSoÜë7@@Eˆ`ÒÈü›dgRFã~ai%4ñû‰ñöÍ·H{ŽÂ³‘ƒQ‚~^ƒ²]mNó´„瑹¤(Nh¨&"¡By6ú»hÖ[¨WjÌXhA`Ù¨4šü{Êl2æ€gðu…¤B3,X„Rº›ÝqP¨Tk<¯ ´J,®®ÃmwàUŒ§cŒ†îï[�vë±ýÇïGe|o‚M�ØŽñ˜ã2Sø…yš\‰&Ó@%\ì9È×hµXbyä¥÷àÔ¥+°,«XeZ>Ó¤VßFö¬7±óö]|þ_ü#f7V¡¹µuéÚ'ÎS<ò8Xäz¤˜Y¡`$\ºxÍf‹¬'?¨�A¥”Q4ÃÄ4LgÞê%ž,Ïôæ~1Ÿcê £e):8åòJö“^’KbÀÒ�£C–„x¿°W„œ¡gš©œ4ÜwïÝÃö׿Š,I0›NÆbÓ‡j.¤]C(Òš]E8�¢_#*Ô¦RˆÆ‡H¢ giHz°m8ì}µˆEæÆs9ŽÍïèyÁq6™‚„7Óz2té$sP ·V­“*.’=ŽãRÂ(æ¬|7_¿Š8ŽÎ­?rþ§]¶½ Ê(ã{ lÊ9¯1’ŸSi4ÑÂRïù4JjÃÁ€ÍÛ V£tIŒ q%ÀÚ… d-i}2$)¤f4Žã‘‘Ô¨7¹x~ý3ÿ�±Ð]¢zaf!«. /-Í7† ' yšàÁÛ·qãêëØ}¸ Û¶ 8ÍV»È=´ÁÊzh r}Ö%dú_“³ÑCXhò}å®_20¹6H�šš«¨¤y¯[P°Ê/)}™‰ÑuÚvîÜ’ïÇL^F£Ç�N™E²´2¢<TevHÊrñ F8ÄÑf™û{Žm¿Zÿm‘!ËS‚a†$…Tk èh<„Ø H¶Í9$Óâÿ;0MK—¸á÷_Xß@¥ÝEoe• –§)¦£±·uúÌ ÍFû½(£Œï°)ã’íÚÕnçW6š�_çWüƒ~¥xÀžJœÆè÷ÑÑ=s—_z7؈G²P²Ð’!V©ÔÐí.’2 (ܹ~»¯½ŠZµ†0 Yÿ÷[=ìLmdA’äljL¿Y‡ =Ó¨UT*$`<³™Þí­@i²ý€RÂRrçÎ�Ùß×B+‹ÉsÙøZ_Ô¹™tÃ4þIŸn¤�Pmº¸v9Iò}ù8�pïë_c¿Š×bÙTwvºëüþ¼ta!èx ²Ñˆ¸K"0–ÐiwÑm/¢Õl£Õ] R€.‘cÙ6AÅàçMt;KèõzHyÝÐíä3ÚwQÜZ­{v?�JîΟÃh<ây¿‚Ñþ<Ïk�ºüÈO}¦Ú¬£Œ2¾ûÁ¦Œ÷t–+½õ�_m)㣳áh9‰b�e.ÓBwq §Ï]ÄâÚV½Œ Ï<Ëq�k²ü|En†i’Úº¸¸L—Î<gHþÛ¿ü§ØÞ¹‡íû÷8Ÿa¹¼j f}iÄQÂ^ƒø¤¦¢`¤!eŒŠd„]nøžÏsö�x]€,à‚*|”²œ RZ“÷x^Í)“Ÿ3£Ñ‡*�b _†E!¡÷nxRÉb$!àIŸE}3Q•–¾‡lT ˜ ‡”µ *U8•:”åÂpýBS.ƒ€k®_�–Ñå$]ç 'p¬ iƒL¸<Åx2æ±DÁYŒä¼jµF ŽëðïQDANô÷vùY}–›þï ýîµj]iB†h.,¢?À°­ùãv¶ßžo÷ƒÅååwûµÚ»PFßí`Sºk�þðèwY–õ3}Ï»”F±‘%¾ƒi[P¦�9º§ÎàÒ‹/qA—†7ôYNAqö<´[]Ž4©¯~勨¿}IzY&"ec§!÷›/¥Y);–Åýí Sö1¸x‡.¸ˆcž§Õî EM4[Àz^²ù› 5Šl5aO1c�l�€!Thpø|­oYN±K)/i׈ՀDÆýQ—ÖKvÈh€$ qcn%�¥dšñ±ÀXØD®,}Zˆ†ÖŸ¡øf"ý3K@ºGGG�ìÙä ÔB³j5#IQ®æëiÂv’ÂÙŒ6Ïû;Û2¸%ÃÒSf¼¶í@®àßÑZXD÷Ô)P+­^g‰uçÖx¾ß:qöì�x廙2Êøî›2ž¯wš�ïÿüZ³ù‰p2YG–[FnÐÏ$¨ÖP©×4š¨,¯àâóÏÓ�€~×\`�âs“†_>ýL*•*ÄÌktt€¯ýçÿ@^øû'9>ŒÖ ”å€T”�lÓ$„Q‚é,âÂí8©i½á �*¦ÓŽú°�î�IØNš¥¢�¦+@äTXÖ+B³xGyˆ›SP¬•¦Ä¬Ïâ@;¡c8uWO^/:„ò9ä™1HìܽƒÃ·ïÈ‘çOfHœ:¬öš r2T]È4P�70ø~"ìÂRYa�àðQYj­ ™V‘eZ¶Í j6Kƒý8^ó<&étqýQ2`Éö,‹ý5"“¨UK_gëÊã0l›Ù4Uö÷1Ïn,ÓuÎî?Ž2Êøn›2£ñQýòz§ýSšÍö •C Žè1ËuÐ\è¢ÖYÀÂÖ\~Ïû`9®3ÐïÌ‹~€M—ÌZµÎlP�Èk_ý ú»;¼#ïäSä^yЀÙZæ�fÅÁF™‘,¡(œ�Ò/á 1›M`Ù\×£´S©À0uÅdžBu¶œè—Ic_ž�LF ¹Qjδ&8ñ&ïñQJEÐú8/�,`pÑ%}¹èŠÐmqáæË_CÇü -ÛAî5a6–`¹>,ö¹ÄäMßôëÓ3ŒÑx7¡Êá\“$ÇóÈB³m„àoęހ8³Mö˜Ò4�QX"Èô«®n-]*CãB† i¿ bwæyˆéécòw¹{ó&ÛZ5mûƒ(£Œï.°)ã…f·Úêti­ÕüÑJ©Ói’¶a³¡o:üZ•e�ö‰8õÔ“¼«C½cŽD†ƒ ÊEdii²À"vï¿�kŸÿ ¸Ê±<²¶u —ž¸‚¬ÖCœóŽŽcñQØ$ဠ�M)€Í˜ouWaksIœpÑ›†3¸Õšˆ<ʵ H†ÅG�d ç’M÷¢yç1tAL@×c‰ŒH!Ç~4ÞqÎL_øãˆÇ€€AŽÀÄOï¾}»7®Ã0øýÌyЂÙY' qþ%Ë¥?$‹>Ÿk :þ¦"js^ÉE'” 2,“ç[\\Ë…èëºl†ã`6™ñwj5ÚœÍ *ä;Ë|äw9î×#ûñ�Ù�öß�XaŸž+QÜß¾˜à¾aK«žzò¥wµ{çQFß`SÆn¥cyÞo>}ñâÏ[\ì�ÎÓTeIÊÿ¡·ÚmÔ;‚�»°„ÓO=Ëv´¹É´ú¿e³d¶´´‚y†ƒà@Ê-û _ž—ÏvïÝÆÃ‡ÐïQli} Ãi TZ<¶YÜÝÊ\JšÉìI鹘J&éSŒCìî<Ôƒ>–W×È¢Ò²QxÖ,„(ÀÁP¡‹2'KVqKv#›®�¦-5¿ ” Õ Äþ@ †¡?ÕöÅÿ1³ÑèÞ"ϯHìì šÎ˜ÕÄQˆÌ©Â]Ü„]©óÜI’ò÷–ß,%-›'…HÖè¦kÌFa©„¿k³ÞD­Þàw'ðp^(+Ø�Ò73 L“¤¿0PôûˆÂX÷Ý–²™æHjðµ�4$š�6tWT¤¥�X}äQ8®˶9wtë­·ÌÅ¥Þc�Íõ=7î³PFßÙ`SÆ“•†•åùÏŸMÆ¿ýÖ› G‡*‰bö1œ1�fˆÒÁò*.<û­„¹mÀW¹ö>@É‚uT…4!%HÌê™s,åÕ›M–Ô¦ýlÛî�` ) Š%€|7îÃEO¿ €Ñ‚Ÿ3 Y�Sñ`º"™Í²bš %|$ È  Ø–)4f>JÍ&<žëPŒ¥!ÏåÌ ?-³HåÈuÀÒ]7…ü@�}†\§î|­gKV’½]Y>dè® ÅyD=Y Ä9Ô¶M¸ŽÃÌ¢ y3[{c®ì|xÿ�†3LÔY�Ôð˜õR•,ÖB²(Hü½É’IñF@LÛFÃ#¾7 �e²/ ^º~€êâ3±( 1pýúUÎè(@¢°ÄgN�*Èï-`$¿#ÿ{2MK£Óç¹ÞÖ)Í6×å5„³©Jâh3ÍÒçó4­£Œ2¾ý`SÆ“~mc6›ü•0 _�ÆÕùbÊmMá5j…!'¶°rr‹ —>¡5dê›ì«ÞÒ õ–ÞŒ–r ¶›¯¿Š»¯¿‚x6™o!?çW+h÷Öqs{ˆQЃD^ UŠj€,ÎdƒiÚdÚ0 Ÿ[Ñ×^vîã`ÿ!È’r]èwî9ô,ChÐ\… &› A‘Þÿ² kY�X}ê=¨býÌ÷ôª‘f±   Ïøh³)PìÛÈÄ?iÎWç³I³á�À3�Í` Ø‹›P ão§oêR<üîjr~±:ˆ{˜M¨¾€Éd$¥+Б`š%ÜDŒ³Öh¡Zoòß~0ìÓz /Hì:(ó} Ü¥g#åF@Áó‚ÂåUéY'É$•f•Z�¥;×õ�$IÃ0ÍÇðPFÇÃBßÒxÜ­œ¬ÕÔqÝ+a8«í=Ü�iX¤’®Ÿ9˜6ÎÏýhšà;NÙ‚”?¤ošœ£Yè.qO3@à‚º}÷6¾øþ-n½þuLÇc$³–cci¡‹£iм½Û¸ð‰š³.i"“þlüç�^‚Ѓ‹Ð \,×—°€4Ç`ï{wïàΛo Ñî@ì70 ³™1Xà È⮫33äNžÖÄÚी¨|È5ƒ3rÝÌR¸Ÿ°Ñ´žß“Ïë™ø< #*P|N9v¶‘E1Ú\ÛŽ&HjKH¬  ó)úoH @��^›“P–‡ìÍÅá2äi™6ˆynTóoŠ m‚@¬¥dlËFµR%@XP>Ü+ÜKMžWŸ"àKïLsF…–mzž‡z­�˜êL~R±—NŸÆµÿñß�Æ<–ïù¶RÆÊðèðãWª�ÿùµQ_YF0QÆ·,.[îšeY ‚÷Ìf³º¡”²-Žïaek–àü»  �„܉ëSçb~VÁÊòG3ï‡ÞÇâ?üý¿�ÛW_Æþƒm û}‚F³ÝÅ,W«ÌÞ�&ÀÈséB•BLÐ ÃÄ Z+5ÜE×É0Ø{ÈE:K„ƒ!nÎ…>ÿç?ùGøÌ¿û—xåóÿ ·o\Ž7nàÍ«¯áî×qï­7q¸ûG»;¸}íuPÌ&cLÆ#ô�0삆eÌx2–饵r^ ÝEÁf�ºË·´~‡jJS�ß‘%,–¥Šàwz}žÕÜ{ý>Ÿ…!R3€yòi(7à¹$ä÷âñ˜E‰vQâK „ Ñ ‹·¡†àX i’ð{.öz¨40k tW× ¿?X.¤ '³[?¨âsó߸ð‡[Q«7qòÒjÔ�¡{ù¼#ãO�RÏ-©ìºaêó Žú¼¡§“bpÖô²4µ]Óº{®Ù½us<øVNefSƇ–OtûG¸µÐyŸJQN�êíº‹‹ðê-¬?öÚKKú]öqgL6•¹‚rXZ\&àpq­¬4ƒ|vÔ?Â+ÿ󿢿s“£#äiF`ª6ðkMìL Ø[—‘À€Jå® s´˜&§Ìõ;]¹öi¤ç�2ÓRèç¾öÊW1ܾ]Ü囨¥ £>¢(ä6<<Àpg‡l)R†§S(“ßÕviÆ�D¸žÏël¥£þpå80l“M{�ƒ›Š‹p­Ûål�_©!𤠛ÖÐKuR&3…$€\\,Å4M€“ŸS9Ä¡S>O€¼ÿæuΖМ-7a¬®Ãmt鉵³¡�èâSCvZ^¹æŠÉÏHéÍ´à>š^ûÙC$IÄ¿û,olÁ²mMᚪÑÌbh|§d‡�ŽØ·ô�0žŒ dsÉ®²\f�2bù›®ÍM)ÅY§½ˆûÛ·íÆ–�µ3gqÿÕWH ¼ ¢8²ƒjõüÃÛ¿éàè sÉvÿþËq˜ Œo~”`SÆ íc8ülÇwŸM¢¸b[¶bÉó°¼¾U¯ãÌ /" ‘Ùqùw�šÊ»N²”V–×)c¢1«t¯.6_øOÿÓ‡ÛÈ£žã�‰ìXØŸðN>åW@à(,›•X†’HÎË}¤ï@Òzò7»ÕC|Žñ?¡öï"|.–£Ñ€€Q jp‡çÙÙÛfɇwËq/Þ+B è?ØÆžåÀÄi‚8Ëà.Æ£1l‡”jÕÚÆåYl¿‚;µ:î­¾ Óuˆ'�…E´––9bZ6í6¸0‹5ϫ뭓ÉXX[t©¼ö¥Ïñ.>M.àfЄ¹r®(uH °�r$7ü¾rPC² Hغ`*Тáðè�¿‡U3: 2ÓövɈãu€ŸEgeËç/`gž¥Â�= Z~µò´9 ]%K/Vêñí QÆ7/Ê2Z?°ºeV½ê�‰’øFq´™$©aš&l×Å©‹�"è,âÑw¿^¥òŽY}ðPÜ-�6µÎªÅ�½À�ôi‚/ý×ÿˆ7¿üyܹqÑx äà€` ´›°6Ÿ€ªw5qÊÒ|7 N¶ë™_ I æbË»_¡ÍRéØí¬Âªµ·ŠIƒ‘¢ÅÅrŸÏf3ê»)(‚ŽmÛ¼swm‡wÓÈÁE6ŠØ;¡%q8™ÀR ÓÑFš!OÐßÝÅt8ÄdÐÇøà³þ!†{‘N'¸7ïm¿yw¯¾Žë_ýn½q iœÌ÷°LGs1βXŠ3ŠæøhÔçBïßÅkŸù_˜õû°›r2I¥ kí~^ÿ7�՞ǡé™~ó Deš›³R¨£{ð³ò4†Q©ÂiðŽ_Awm]³GÈaš´S€ëºp½·ß¸Š{7®ñï†2Pmµqö‰§ òB60ÑŽ¼n�Ø åTÉ„Æã¡6ÃD ZHÜ»ùT’ÒZ"ŽضmzžßIÒøâd<>³dÚo=È’=”ñ��lÊx©½dŽÇ£ŸTª¿ Ùn_P¦iAÀæ|oc~½‰ÓÏ<ƒ Vƒü�Y·oÖ³ƒÖ½]¬­�€ï|[›[‘~3šW¾ôy|ý?þ{$ã1&ƒ!Â�‹:›ô3U…Úx Y­ @óç`È@¥”–p\-@Ö$Y8 Å�OîO 4ËA´D†ÑX„ÑZ%%Ø]> «¹LðÉ�*LÛ…ãUaù5f–åÊ„¡,õÇXb›L§`sÜqøKh 8LI #@¹.Ë¡Ôÿ¸ßG2�QªeïþÛØ¹—å»Ã½ìܾ‰»¯¿†½Û·pmÞKºúƒÛ×¾<“û€R<¾Iõ“®”¼“O¼öùÏâö+_‡Rà{©á¢zá%˜Õ–n£ÌÍ,ÊhzKhäÌ\õ’?ð ùpLQñ\„“){%Ê0nN¥‚ÕÓgXjÓÊ],£U+5^÷�—¿Šƒ»w8äéØÖO`óÑK@Ñì`�Å{RŽ=®À ]®†ÊóG}–¿P¯ð“päÒ¯R†iÙõ$K—Ò$vO×Z_¸;ÿR(ã%ؔь“ãúþ¯w|ï‚3�z½ÎEÅ *ð—z¸øîw³¼£ %êÙŒ4�¹(µ;‹X^^ãB«c7©£s¡üÿúŸã¿ý½¿…Ù �áцã!Â8D£ÓÅ8¶�o\š=ý|B¿ÕuÐDVŸ²*Òˉböt2†”\”ž•AìlÀñaTÛ0Û«@{yw ùâ©ùãId�Md|¾Tºˆüff€4 '`)Ë´,&iŽ &ûJÙ°l“Mz×ó¹� yÀ²l–èLÓ"(%q„ý½ö�M§¤�ú‡Pq‚�9èìÜz Û7ߤ/Í­×^Á׿øYܽýú‡\PGG‡x}®¼pø`› m’�µ3O²4—¦²øj:u�ÇãC•Ð%l(o�a v‘F3Œ†–=ß#@÷æ@ÓY]ÓXa�Ã~WµŽ$ŽIº8ºw‡€†!6ç}Àõ3gþ.2¬40Ôe~tqSù“<·m›= ( 5Ó;ðßÞ¯ÕxÜY!™NÀ>’aÂ÷|åy~5Š£3Qn-æ+;Yr€2ÊžMß0´3¶íü¢Z­q2ÏsÈ •zõÍ8÷̳p}y&÷ºhh l‰j¥N2€ëвRÚÑ÷ìàßýƒ¿�ík¯c<êó®¸ÓìP ¶‰þ8–¶ Kpl 2µ¯—Ix§Íž�Ìd@ÓÕL4oª;¶Ì‰h�fð53 0” Dz4í2Yð:h-ÐY‚•ˬGÊïæÆâD0 È"ŒÂ,ÉþdN†á,Ì”Z` •Lä†Á»ð0 Áé}EÂÁ‡ÊÍYFëä4�Qõ+Hã„`²s÷r׿ð~÷¥Í-8žÇ�mÛ´Nžæ.°|Ž~?HSH“Ý(˜\))—,�¥@‰($éweT}.æm¬*ö>D#0µÁ\,WB@Ü÷¢ÄI£:f6~¥‚Édùï ¦ îÅ ×u\¯Ó•©5 $àèôhMQÛÂêÊÞxó*Ë�à0»é®¯ƒ$‡óçð¥ÿð0Îß8ŠB£R©vgÖìGª Á“3ëw ê¿ü¥‡»9Ê(3›2þ·fiº–iÿáîÂâó†iP`Sß ªX¼pçž}– ¾Çêæ€Ôþµæ¼bÉluuƒZg`éL_¼Á}övñWþàïÂ�9LeØÓÉa†k]L+=˜ë� …ÌšÈ"£ô2�d8zÿ�‹•\§¼+šhJSR–Y–"ä|Oç8DΟcÊùsYÜD…¹2X’3]™a~ Y¥…¬ÚAV_N½³PóÇ©ßÅĬ"òZ˜Ũɔ™U³ÓA«»ŠÜt1�…¨TëX\ìa8qÑ4‹²\­ÖàcµZÃüߎ18[3 y½õF‘S'Øä¦ hN¥’ðiY‹hÌÙ‹f�P€¶”¯$»AÃïÃ3R„Ó) p`“JÎÝ�M,®okLò[H£Ñäë/æàèÞ=¾¶-‡–‹ë'Evß³,›ŸdH‰ô¸z8_‹Ù›d©BƒFž�â­�£ðo>5�^³Ã¾Xž&œ8Ž”išvš¥'£(\F¿º“Æ{(ãÿ_”`SÆcn%0~‹iÛ?6ü?Øû (IŽ$Ëï”\LͨuK-†‘FÃL´ G¤½[fffff¦a&35Q1&¸_�½°çåÚclÍ–Kñ²:32"#ªÒ¿›Ù·ÿ³´Úï÷Au€0ÂøU‡7#š›IÎßW-ö=ihXJ[ÐJ²Vo8oI"×'2wñ<þöwK›…ðœ é�M¥R%GO·°–LAî8¨€í–Ë>’îß,'#9U*` îq6 r (Ò)4ó{ýÔ¨¼æIzÞ Ô¹®wÀ”¬-#€6š€¥ÖA]iPªNÔFaë£ÀØnÄ»ŽCMìGÖœA?…š¦ôWf.ÏÏ"E©C‰ÁJ¬m´)*è’”OŠ4Ï ìûý.ÉûDaŒvj€™£@c|«;ª'�ƒ’>Í– ÆÐ}c懈^n¡!À†jÅêtÞƒ„%V^­ÑÀðØ*Í&vïy¶r3E¯µZƒ>ã=ï{I99¥âà 7ab×.p[˜‚>[û“(-ç÷ty©?¯H€,,–Wa�ácð»éþµ7Ïßš˜‚í¥%´ZCÈÓŒ"+kL ¤œÎó<�Ôá‡æØOb{|Þ�íñtÜÚý2 üT­^ocDš¥€Ö8vç]dzF6ÎÖzj»^;ýoÉ�dfÇnZm ¦ÌBp=¥}}à|àO~�Å)Y0ÃäÂJ£t‹zæH�˜rõd9\ B^ñ—ÎáK§€'@°Í@©ûÅâœÖ¸I7q:«ågƒÿ|42‚—ËæGË ® –ΡÉÓ¥ã@çÏMA*Ê|¿LIõe`p´r“ç€) M‘Pl¬Áä H�AEg"]‡ÍØèt '¡vìù@Yû°†îý̀¢¡¬¸à®Ù¥­´aÁ×KÇúO}Ñò“(Òú�.ê­vî?€dt×¾øeàßÈðð(1×WWñƒ_þfTZM"´{]¼êßÞ��û:i Ñhˆúztüy…“îñÒ¢^ÏÑÅ3”¦d¡R@€ÍØVçæ � Šqé™§pæžÏb°¾Ž´ßã(Úl*œŸ3¦øwB뼿ß1Øÿãcl¶ÇÓ¥”«½'/ò0Ž+ÔD¹ãÆpðÔõÜõî”�!åØ ]QT²sç^œ­© fÿÌ^º€�½çqÏ;ÿyÚG‘fÄF*Ša’ 59�ó€œ> Uâ(ˆÞË+jÐ9MYSà:�ïßoX' ŽÍÍ�ÜY í9pÒ¤UNÂNƒÌZx×À}:Ä|³à9½7Ï ÓSvÑ•ë~§”c"ܽ"°„ ô¯¿¹k_x6СÁ¯] §ÈÛkÀÊE››hŒCÏ\E,:cXKÄ ¦0óuºZ /#Vw&`* X šÁ7=ÿì¹ÏBå=dƒ”îklâ¶×¿™Á†éæ6“3›ä€‡ñówÿ+ H�9i¶ðÅßüí¨Tk`K¥4¥ ›ÍazÎï±qÀÃÇwë"¦Eƒ~Ÿ�NgÎ>E´ôg¿¿È2"UÐÂJô6ÚøÌ»ÿƒå% 0áA8ØT·¸góç¯~ |Ûãxl§Ñ¶ Í4ü„�½ÉÂDä?‚É«¯Æ±[n£º€cš=Ûmü<¥Î6éÍ”A™fâI¼ÛÙÀŸþ$þü—~s›ô]›çÆPôú= ON"Ó,êqÄ{NRj‰&&'íË=^SŸ5–ýT¼¨„™F\‡!ððûAZ•;&=R/íä À[Ió„Mïg@äZƒ¬ë‚õ>¿qî—`Ui ~?[0è9–uFmàã�%€„Õ1TµI4n=¶‡jGÒÓRsàÅÈå>��óþߺ,cÃ>7´/&íA¬Ï!O»ÈXX„qL ûN\)•ûÝÁÒâD+�üƒß&V�Ò¤R�‰ƒqøÚë©Îb õ+ AµÀR/Íó|àáÕïø\t�¨§Š, ˆ–Í ®æG$ Ú¨7Ê‚Tãã˜?wµ YW#ËR-”ÍÒìÆq!šªÖ.̦l�ÿÆØ›íqãШÆþ¥äÛÂ8®äY’õ:nzÅ«‰Þ¼Õ…©«4¬oX566�VspΊTÄ>ûÔøÓ_ý9Üó‹¢Û…”§G¦÷�`-Ù5¾B‡n’4™# Ù{Å'v“"¥Jgpæ<ø�õÓiEiÚÆÃ”)µ,ËÁ`Fµ : Õ <2¿—{K(êñ ,÷B&3®øýA yÿ­ÏyΡ\¨/ +8;†�rõ̲€p�¥à(“AQjJK+hgA@çô£Az$×R€�Ös4Þûp¤Cûrï`î4m¡K6› B’óÙK`#@£æZ­‰µÅE¼÷÷‹_©çFJŒïÙ‡]W]EàÄ EQ]ÜZC:rŽÙ&½”*o®×eT¸5í*ÉQ–L0K®ì¢ Ú¯12‚ñ]»1{æ4YQdT[,C0öæb0x`Îç±=xlƒÍ Õfs\ȱq¡¦'¤‡Ü5©‚Æ´ F&uPÛ|~dóùxR‡é\‘øg0& ŽEaøÃÕj}L)­Œ)ˆâ|ëÞLn›<ùÑ ?rƒŸÂèèY(­@“0ù„tñÞ¿ù üÉ�ÿ–.œ' ’¢ÈËI=C‰,¬ÃÔÆÐ=Fø:À BÀWžßٸ϶´Ÿ+s¶ÏûŒeë�c«ÑÿŽ­Ä`’'0!è˜ôXXŽdxbs ŽT“ñ'7z,'iÊó{lǤs¯YÑò}JÒÎNQnRe …€÷<3Ë̳%fÜg¤kñ#T,z¥Ü Û)€^ƒ…R­è^Y¸ž–,+¥„ˆô"]_„ê­@ˆâ˜&ïá©iì:z”ŽÁ !I}äïÿ}˜>w·×%™š‰}û0½ÿàØ€ ùê•!‡Ïj­|�¯ÅK§qÍ‹MûxÑT‚|@}PƒAßý~h£ýé:-K)D•*ôæ6ûÔ“�\çÒJ�"+Nì®Ö?paÐ[Áöøç 6'‚hxw¥qÓ�ÆðÃZõEQ¼½È³·¨ xÃðôô—šÂ¼atfú-½õö!ð¯±Ö^»#LŽ�AÈ ¬ÎQåõóoÜþ9hUúk�Å]Yž%a[窻^€½G�{äys¾0cc“˜œœ¡‰ ©3?½9yüÑÏþ8þÐûÈ_ž”žã kzßäÑìÈØ©#�aÅ­¦�« ðÊ•j"Þ„@žá × 5²IMŽJyòÂgWå¤Î©1¶?vþ™¯ dìÉŸÈ ê·áãÒû *øL ÈrS¦é <ÁIp*γkΙ–IpÐsð¼\¸þá�2¸þâ´ÈèA¹ëöR¡°­KžàR—Tï‚ ûVF]ü{´Ñ›}ù KçJ¢F ì>~’&}¦£W*ôÛm¼û÷ëË‹¤ !¤ 3µ‰}û±ãàáò³rÁŸÔ )̲”ÀŠì¦·ÚGCx ›ÖË|ؤŽd}lœˆ§˜0G£l�Ž¢:2ŠùÓ§‘¥}{²Èµ”ª…á̈±�\.²%l�^M�'‚¸k_‹/ z;uÖÇ[ãð&‰’D�5FÔëucÒLÕZ�€UZ#¤7dé`­0æncŠGNèèƒþ¸::òôÇg/Y|žŒN{c(ÐáK…IÆU˜8žM QÏ6¬òDÐLÌ”"†8ýøcø‡ßû ¬]8O_\¬ BÊÍSêL(T6ßc«c�ÃSÔG“•ž÷a¨K`ðŠÎås²ô÷g30NCIðdÉ–Ð\Aaeq=O:´oI pÉŒ…TD™f'Q®âƒ‰ ý Íw‰„à‚¿í·U‰…qà, ܵ@! À5-¥K‰�Ò_(LÙ\ ¹ðgd0åî¦.¹£]óµ8Œ¥ýÀ÷œ¶šÏ±¯*¤ru#Ë÷œï+ÌNkMÃê&í¡×ëRA~$ HŽQ„­&4�nÊ™4¥ç´ÖÄD‹“„j5|À@ ºv¾öååÚ/Ð!ÜØª`|,=Á×JR£÷z‹÷2,×ÂŒÿÝì?~5ýÍ¿ó× YJl6ÄáP–ç¯ £hç‰"ÿ�›¤�û±=>ïÙhì½r3€¯VJ_&ñ´Ö*¬Ôê2Žò©Uë°Ijp˜Ö¯¬¨`IÒyfŠÜE‘²ÁàŒ1æ—-ðÇdý¹ç|OM¥.MšÞÅÉÕë�ÐRí"é7¼‡¯9åÿy¸çh²'¯þ;ö í0{þ,>º™2»¸Ñd X"Ï©?�&Ê(L�ªˆRejbÂÑ02„;ž(¥ìé‹ï•}™°…ÆJàäö‚K7±�™, èå„ÈQé|ËTeSZqûÑ _3¸ˆLEiž¤‹2ÍFO0˜>¿ri((¥%ÛNÓyžs¢G+~¿@ϮӅ¡ÍYÑý+Ó®nÅ}>Ž®ÍvÔ`³’iÀôö€AšC+×Oŵ1ÇR“à{ÕyôCÈN%é q£�×þ»¯ÂIÖXŠJÒvþ‹?Å“÷|ù`€œ¶¶0x3ÐêÖÚFqrRk-Ót€ÖÔ ^úÖw >4ÌôâÒÈÈ)ˆMÀ&u€ËgÏàï}'žüÄGIñ¸–ÔÀùð¥ÕE úˆ¸�°9Œî†ž�ˆÙ0 to™ÕÕï§4‘jU±, çàùgf9_^ÙóÏi8qÉ Pàb7ÿže©ƃ'ô@+fÓñ±xõï÷±í2ÀçæIØ£>³ã¥ÖŠ?»ë·q´qÖvãÚ �.7„ÒµÒµp³h^|�<Ð@¹)g@mµXf²†wÿ àjUÖot5€£]Cx},îxý³÷#}ôƒ(@i¶$Æ›ÿÓ×cdjÁ<¾)ú™÷½ó§Ÿ7—ÆÕîúÂ/Ãñ[o#–"è³æ�±ZEÙ³¤E FGÆÁ½7¼.ÝÈüg6Ós®£–ê?gÏ=MFw…)¸Øç÷…;nɆۻç>ôw…wýίÖ,¹˜ÒÄèoëÃYš~Ý=ýöv„óùZ³¹>iÜja¨ÚlœªTª¥”H}úâ…• ÂZIs;¯>…=ûŽ`÷änìÙ}×Ýt'NÜqùæ#/J¾½AA«gЗ8ŒB„›ÿÁXžg/HÓþä¸P‹ÓµúåÙAÿ¹G �êw[kc!…ˆ£ûo¼ {Ž/'!7Ñù"Œ–ØdKKxÿf$ó¾ßû-,ž=�^·ƒõõ5*î.-/`emíÞ6i¡²ã(ÉÍÈ‘CJÉ…«Ç” †ÚMzØÚóaáúQ˜!Æ+\çƒÂ“J^¦�Â@Ó>†Ói%ËŒMÔ¸g$Ï N©ù>÷p„Ÿ(á̸xÅ-à<ü¥WŸ“ùyžäyòtt]¿ð-º€ŽQÖ¦OîtŸ->ÉŸG8¶×sXrŠÍ÷ó·€ü’½8Ðq¤ª�#A‚léõÛð÷ŸºãSÓ\:ý >üWŠ�¹ËÈȵ ÷ÝôÚ7áů *Õ:@š8-ÊŸ‡)å =×juïžÓµÀ¥SÁÇx6•›"ñþ ÇiÀgÿŽY9ÁcY‡�_ƒÊð(Î�3—/â¾G>ƒ³÷|¢,4–Õ¼°ÅæôúglQü´ ‚Ÿ¿§»^à92^uà¨ØXYùj©ôwYa«�HÆÿæ/ú"ŒíØ ¿ÑÍ_Íw×ÖðØ'>Žù'§Âžgȳ,WÒî´‘æ9E3zxªû®A44È-+k o•ιr¦æ `�ÎwNJ.ª�±ü<` x0UØ“Òá¨pÞ, ýrc\ª.G_öçйÂ(à~nvܺJöê7Â)ß`€X�p�„£³5}æp–‰ œj¤” R®·¾Ÿ«8OÞÇ(â¤9-¬t øº¸ÇÇëY៙¹gKk ¿WÈlûá¡8w/­é½‡žÿ¼ø oÁÒìeüͯý"Ö.]„€ (‡¤þGÇð¶oýLíØEÇ\[_¡º Ó“Yy€k,šŽ zlÔ››€€†Þ5Ãn�ÎÝ+½^çΟF·Û¦”�¬«c•ѱp‘*¥Îöï; ø‡?þ]|öþ†Üe9(…Ø(¬½Ðo·¿JýѶÒÀçIds2ªhaí·Õ[­WWkõj–¦¢( ZÓ»qÇ¿¯þw_�SÏ)v>ŽJ£EEÅáõ’Ü �a-B8”Ð|ÅH &ë3Ø{âzyåë0uü:Y†Å§g ¥PRIÍ"/®…5‡w„•‡/eƒçõñ`m(Ò:ør)剢Èk ¢z ûN]‡¨â§#x¬ÍÏá‰O|}øC˜{úItÛëD[­$UZSÊÃ@ ˆšPCS¨¼•½'¡«-oeÍ ¯8¹ŸYR<‰šÂУ+~sa¿œ ¸/–•¤Y‚¦$—q]ñ vôožÈÔ)5„®4�C1{Hm)TÃõ¨ð`Æ OÊ‚µ¹ü, SliS\?àÕv )ît.Ö§{�¤Ær$¡h_øEpÞß�˜øZÛ8€�Ø :G# `ü¹��™O×e ÅfeM9àæk£7rz‘ß7iÓ£SF`J9_ ƒ�«qI Ñùù>0�JºfyâÙ@¡+³Pi‡š mù»žœBRobêàa¼ö­ÿû�/Sg ×ýô¥B¯ßE‘çÌuªÔÆ0Y„AštíP´¡•v þüœ£ëp P”µ#2LË2@8µm_äÓ“ËMŽF½AößI¥‚©ÝûðÈçz‡ò išr4&”Ö5X{¢×í&SQüÑËYj°=ž[`3jÑ0Eq·�r8/2êÏû¢·cßÕׂ‡#àÒZ4VRHk ´D0Y§GD…Î:°ƒi%F¯Qñ¤0$@ôÌýWŸÂj»�Å3OC9›1&ŽâxÊ{[Ö霟3Åã¸ǦšÂ°µö.è‰~–ŠýW]E6Ì�m ežÿìg‘­¬¡'Äô©×´º4B!“1ÌØ>Ä{O¢ºÿdu$¸žÂ` µfÊ0ÿ<‡MˆÂLãåBw9áó¤Ê+o×câŽCûú¬5ÎÕ3ÀѹüÚ G!,­ï dJO× tN:·â"1§hè5V2¦cq³©. è`0(UEÏ;5bzN8Bk�y* .:á(ƒï£S–v*Û rÔ냒ÊNŸ�ßJ¡YãËn5¢+û‰Oþ`;gês‚”p`Dç°lËMÿ:@ÑY!qNUô ˜H`ÏA¼âËÞŽ^ðŒŒOx}@~�«B”FcqLªÃ(Vˆ GG3(ÍE9€~?�ô¤…X…‚•Ò,% þ»õÝ@„€[PiŸµÖ’Sk|Ÿ~‚RÑ ²Œž”0ÖÔŒ1‡¬1—¦›Í‡h1º=ž;`³+®i­ïŽ+IÕÂÊÞú^ù¿�Ö„pR+<ân•µ6tšCX *è±*õÚpd“-÷`  ß¬"­%Ü1,3˜´Æî#WÓ—`ùòò]/rÒˆRJÊ!S˜ý;£Ê3³þi\ac\È!»#Hâ#q½^kóóè./£×mS®¹0y¹úÖèö{�µQ“�¼�}' *-X!<Ý.ÖÑâæD€;¶©î†Üª Ì2õŠÿå Aò±}æ—ä÷ó{]O ƒ˜täÐýˆŠë!>!­ҕôûR꽂{I]æ"½[{*ÍL�¦ûâ¨É|\»)ÁplnÞ‡_·`ö÷ßø¬;cÊ�…4 äEAϱB³€!gT‘u�-_$šr¾týËOb0ùÜ3è]|éæ¿»O~ ½gîÅàòHçO#_8 »xvs¿3H×`Ò.Šî:òõ%¨ ‚È/‡Ý;©vÉœ¬¯jCøöö;Qk4áòš<™óu€uùº¼.–íQž•½‹›céºÃ ¤{DÇ÷…œ.œÞ(êöÚô½æ ÀQ‘¿háúY–È�6Ð!k|z*CÃ8÷Ø£à†é E…I¿×½Ö Ï™â4¶Çsbˆ²9ñdR­} Z¯µŠ"G4<ޝýµ¿ÀØÌ.Îê–ÇÐÜ’õâv2/ «Æ^Ùõ2ô_‚1Àêô(VLJß/ Ö}2Uúà_ü>Þÿ›?�þ Ñ4i˲l#ëþVñŸ>Ó]ŸÃ2ÖÛk/±Ö~UT©Ü.¥Š´”Díõ{èonÆDÏò�‰=X!ÂéC‡gÈ]ÒÙ?;ÇLxeéu“CšÔ{½‚=­êƒ Då-�I,o]¾œô71÷0æÔ½¶5ྛ,Ëyb÷|i”xBâ">GEØ*ŸÃl%·’§þ.è;²¿‡?ÏÖÁ>8®.äÀÒ5vr�×yà«é{�™Dý¦º ¢ ¶ È‘A5ï¬ P›[Ú]Gc6íE )lÉâ£â7LA.MÞRJbj¥ÈèN€ê˜lÉ ¤B�:®"oNBÕF02ÜÄ®J—žzšJ�æÄ nÍqðøI÷{pGÏyTr€"ŽùùË$“e©ç2ÊûzN±aˆñ±I"±ðï»pj Þþ ósKKó˜�½Hç|¶qÜð>ãÄø4™À‘Ê€±ï>ý�÷àÝ›ÒM�õU¤½´(ÝÖév²l08k­ý•ÄŸ¾wcÍb{\ù`sC¥yľ?®UF„�zòš›ðïôWQo €Gy FÏÍB†˜h¨fŒ`ÿ0„2›o#?³hÌÜ…n%v2åîÌ^7ËøÈ_þ!ÞùK?†ÞÊ"˜Ž9è–ºeŒù¦M–ÚüÿWyš0j R¾a½½úbkqȆ•JŠ|]úƒ>HÒC»aÆ÷AŒì€ «Ü)Î4×Ò~YÂÀ¥”Ê´×·¬G—ÍËfD'´éœ3aáMp”a4:wP!Ü7ûè3�šö'çà 'Ìè$÷ù\ÌãæOåë4ùR¤Çþr‘“è˜ì éö\Ç?Õaè\Ìøâ �ï ßK–ƒaó5Ç6ó¨Ê®Ap÷7Ë€´‹ÁÒE$ë§±{ÇΟ=�õÅy›3¸Òt”fs¥“¤T$-“ç=—edß]šš Zœyî(Èåuöº}Tku¨(D­–о٠ÅÐȦöÂþ“§püæÛ<ð´¾ÂwYR°e‘~m}™$¦Ò,¥Ïã!\ÊKQ£çÈÈ8�$¿/b/r¶.Pè{páÂ:War8`b âHQ6 kê»Ù³ç hø:zí |ü=ÿ€O¿óïÐYYÙs,,YšõÚíû�1ïxŽh©m§ÑÆ!ß$•z…‚X¢ÈsÜôª7¢B!ºB¸‘Ò€…¬GÐÍØ­lçÛ°ÝŒTj7&†a´/ îA*��aŒK�<ˆlЃôÅMò,›)ŠBMGɇgóÿ×EA$mLH}ƒµö§Š¢øW¹Éo•ZÍhè8ŽÙf™¤ÝóÂ"¯O¡~ôvèÝWC MAèˆiÀ\\çG\» GîG(¶Ö,<ú³ë˜wý0œX¢·R¥}Àîš¼�1ð.ÕP@XH§2l±•*ìú) à1»¼ÈÆÉyû ¤iv ebÓ­y�ÂD6rã"=à÷r€ÎI×HçfIBž·ÞSx‹WÀçÔÕÂ:k°³O`pö~Ø¥Óˆ�!ë´I$Ó©°òýZ‡D±°HiyŠ^g ›"Ï�e¤{G ½N\R´“g)EQQÑw¡½º†Í&bt66P­70½{/£ã�š¡óó¹bÂÕ«íK»q¢ÃÒù�X+×è|rCA€Ôêu�nÏhéN—‚¤çI<¶ßëѹ|†$éE]B€_pÌŒ“ûŽC}|—ž~ i· nFÂ@AŠ¡,MG'Uð™9“¯a{\Ù‘Í5AòuR«o«Öëuc�‰«¯ÃWýį£92c}ãÊZàòÁ „@x`²CP ­ÿØln�%!.Þ�ÂùZ~Ä ˜^ïûÉÀßÿ鯊$Ô±±¾n:íKEž}ƒø\'ñ [ü?™0 `Ì)ߨup³…¡`sÔj Ò>”R”^Ê NB¼çjèÆ8t;±E�Ò ®àî¤ïÁ6Á,'ÂÒùtž˜¹±ðéÍÜìÉEvX—bƒ0Š:üº{–¸I€Íz(Vga{�Q"®ÀD q�?«£ 8Ú/ h§ríØY„i®Æ /�ãõ¯X.º»hªŒzÀ KÇò�AÉ´ ÜÃÔfº¯¼ê÷T””µ™^]ŸÅxÿ,¥žÚË @Ö‡ÖÏÛí šPuVÀä*õ&Ƈ'03¾ƒ ù™’¨µ†PCcbËó— `ÖlNï/òÄ3†&öÙû?ƒ�Ëç‘Ô”^ÖAm¥ß})x[jalßaÜñª×cçþƒ^ß+uð&s^°¬®­`cc•=ÎÓ­{? �pæèæÆ‘²×Óå±-ˆNÐuž=û$úý>›§¦áÿÌÇ¢èæÀþ#˜5H÷èÁOÿÛ¿†ÅóçÀB«RIÛmw.dƒÁ/nF7ß�íqÅuÝØ¸@š?Ok}G”$!çÃÍ/} *™jº5�3å9+  =ÓÊ�Z\Ŏɽ8qôz|᫾¯~þkq×�wá–knÅóNÜ‚œÀÉ]‡phd×ì:ŒSûŽâº“7â¶koà ·¾×½ðå¸ñy/Á©Íž¶ãw½Ç_üjˆz“hʃv•j£TY7ÈXY¹( ¬Age�NqµŠ¸R�€W€wàÁrE@)òÚ¥š ³yß­ÚoNdÀÒyÃÐc§qÓ'“,Ê÷H8Ú¹$k„Ísy}:¼ð°ezðTÈã()ë@î¸c“3Øuø(é®ÎÏÒç­Õë"ªTtg}}tBêΙb ÛãŠêr·ƒ)^_©ÕîL*•¸( Ìœ¸~“Rùrobã&ï¤Ý#b@Ø@‘‚¯�uÐóÙ  —Aè7ªh·ê°öXÊ€jA� »wîÇ…ÙóX__¦{!… ‚°™²�S:ø»ËyÖÁÿ¥qÇÈ”6ÆÜÁ×Ökõ[ã(ŽV×WÅÚÚ2ˆ¡'ÈE€vnî<Ž¡«ï@84I�ÛBºžNáÀrG¸ï’ɦa�d°¡•y¹'=Sn¹ŸÂ£ö²e9Sé9Cçæº‡ñ}fZyœ�f ,™cáŒåšCÃh�ŽcöÜHÚ'Ï3Õït2kí#s¦¸BÕG¶‡º¦Z—&Ë^#¤¸9©TBZµ¸ ×<ïÅ4ð�¢ì¯™[F�¦P5sB 'P#UЮi�üü*D9±ö5ô›ÀÓIòYi~S ³õÅU4Cصkê\¾|9¥ eÆò,;4.õGæLÞÆÿ…1bí>!ð­•zýù�öF…‹©J)5±ÑîHö�BmÇ!È0f9Ï_ž‹Ížù”[‰sÔÁ"…žŠ0à‰Å$¨·Ä÷o|J°` ž#(Ëž„�bÚœÉx…ê,”…ðD4e#l�cyi1RL�5°~é,ò4EPoAª€�ë§I|/ýÅ)FF¹URÇcÍѳ°¼úfpà4]·òj.ÜäIÃþ¡”û|e�Œ?9õH ->‰õýõl� ÷€"¡¸^C¥ÞÀhm 7Ÿ|^ñÂ×ã–Íèdz|šDtý[ëRÀú‘(dnè{ã?O� ÛG²ÞEe½�úò:Æú;*-œ8x5Ž=…¡Æ ¤d)ç/S ¬ßí ßë�DÿÙ'ÇèŽ�h  änB÷T½Ã "ÁWò¹ÙÜ)Ñ9tRs©“ ¢ï€ÒŠ@ À?ÕÖˆ:=è÷èÖ£eûTy§¦@=>A„Z­å¾ÜìÚEǘ;–„;­1ÒE6ô&uø®mý´+s¨Ù,µRßÚ¾EJYk1zàNÝùlPú¬¶´i tYÔÔSuÈ$`Öú° 0ªôtkUØ­è>•Zx¨¬w�´»ôžáÆƆÆñà#÷P´46·@*µ³È²]3aò®Ëyšýö¨IŒ1_Skµ^®”%�“ñÐ*û¯GràDÓ�LA… ´Ö¥]qÁñRìPzÔ\WÐv_ü‚RWNªÄÝ$8í)CÿrÀ`|7DU¸Nö¼ü,[4ÁIœB‚¢Cj§%'”íw ‚"eZP$D½WåâBåÂ,ǘNptçA4«CØØX‡Š+XYž#i˜<Ë�§šÍæÎ<ƒ®BR­ú”hN;NHZÀNR¶pfN÷Ìý-:¹ z¬Vk||'éߎŒAŸ³(r/uF�ØZ¿�džk6‡üúŽt‹‚æè(ÖVVÐïn ßé@Þët–`í{çLÑÆö¸â†¼~xTˆ³t �)(´ÞšÞ¼•äa-}„-¿ä¡í�—7xÅF_Ã_0 À“'d¹ñ9xÎg9Dnpõ±SxÅËÞ\†ÔRC§&•~Ažg·ãÿð°ÖNx,†‹<§U–Iš¨^ûR’•QÍ1D•*x8ú¬ó göTFN›OšœZ¢ç kÊy’&A*þç9¥!¼ýi+EýÞ Š|òÌùñK¦—¾ö.­eùø´)!<ʪ{tÑ“—F• zl7Ò±ÃØèæØ3=Š ÕAúøÇ`–/Ñ>ÎáÒÀIß0KKrÄÁ,:Jr´CÅï@ƒ®‰&?N�±›g@¿ -Å›#$®óp-†Ó‹yVÐ1Yã�¨×yöÅ!°ï¯!{æ3«— Š>�ÌÚÚ Æ&&1>1�Ý{÷ã­wÿ'|ÙúwØqëaTOM¢zýª×mn7Í vóTË­vÓ =_99�ʉ $ÇÆíB¸³µ« 9Q‡¨GIA=QJ�)´¤ïŽ•´NW ×:‰¯xõWâàÄìڽ͑Xd) Y6ª|öÝÿ€åùY¯ŸŠä¢^µF\-†nš»§NéšÌøhëúÔÔíuÀk¢%b€1Ñsí‡j˜ZPÒ©MüwÉ�ÆËpt{äÃãz²|]Æ Zk`Ï‘£ÈÓœ�¡ÄxF5l�+rè2u³®u`!ú£P D_õÿL‰}FsŽ£ªB&6¤òŒvÊ3ŒHãȧ:p™hø<:MA+¼2Ÿ®C�W½þMèÖ þú7µ¤<†"©UÆz�:¥j ÷ôÛ÷àÿܘ¢(Jh �úÐ ƒ…³ˆfŽÀ KÊXÃE×rRw),GseK (U¾NŒ ÿ`f§Ì¨ÑÏÙ £|¿§Þ4Kúé7ËûKG( "xQ€™ZÞÑnóAn`N÷ ÚI˜8Ù~„c»‘ë/?ˆ‘Fˆñ¤@ûÒÃ(²Äè!Á+`XGU–ô|¹r.l9��Ꭿ Îf)d‘"è­!ï®A›ÈSôÛËÈj#&B O;2€`2ØèË1¤  ´ëaq†`&ë£÷ä§¡ÖÎ#l5¨R­ÖHõad||S{ìm¸ùÎ;Pk4ðOWGðÃsáÓ,]^°ôh‰©i3›°YŽ¢ŸÃl `{9mÈ ˆÅW‚.„¼ü9߈F­‰/xåãÿþ B�åd³Ï“ ô#Ö`÷¾ƒ¸çƒïÃ�›&jJk¯é’ ù%¾Íx0è•‚˜$ôéÕyàì#øyœN{”:TŠ^w94®Oºk�ÂqœÐyP€Ùx |k-ùðLOïrú~ôçäîíÄŽÝ%ËM ˆB†‘Œ£8BwÛãÊ:kw´5æxš¤�ÎTÊü—@ÂXè~J@@ÑG="© Ÿoyá …~þ÷ÏK]: x,Õ‚h…jcišÇ[ÿÅ¿%í±÷þÙSî8ئ’énlüô ½y“öxù:e&1¬�’R†"�MSwÖ†Z#ÍFKÆaD_FC d ¸øÈ‚}§ kC k`ÿ˜r¢“\¨w ãôµP§´Lu”Âp!��ˆ ×mïI´[G((ÏA›'3—ʳ[<î…dÁN';¸Mé]îùq„c:LÙw3<…´HÑYy£Í‚N‹çîƒtì<+Vr.­8}ÄG °(:ë�½ Èþ:°6O)­"íAHZ ú\ýA‹œ&;Ql6@` P-]{Å÷ž3VÆERiVÀ5:J¹Ažö¡æžBÐY@«Õ@³Ñk»í?vozÛÛpðØ1è0€Í XÐÐÝ)�“£(A@‚@‘V  ô¼�^ƈ, Bz¿.S¥(J Js˜^Ó/`û9Šv “:�ígtÿª•Þò²/Äoüůb}yI­†õ•,\¼®§|öÃïÃ�w½ô{ãûmY˜ÕÝ$Å2H¥²7|rÁÖ‰~NÓ5…®·×(2RRxÂû3 K)‰ÅG=Eyί±ê§ÕXöõ'`çþ%®?º&[]Ò¤ûmtý"Ó¥}²R#Sˆ’gŸ|  Q�CbtlŒ>7!=00 ŠÖÉÒAHõè„ T5¿æh+£Ó¢”'2Ž}èÓ×ùœš†]ŠlŒ)àS %x¸–:õZ`¢@©VpöÉ'ðØ¦ª€ �~¿©×íüál‘u±=®¸¡¦ƒ(ì[¥R“yžÔP5>…ã·½Ô‘,@ªÍùJ¥Q¡RIè�-J +HtØÒX™G®”Ï8óÕ矅F nšœ?mùE7ÐÍá05u"[îÑ—qjl ““;ñð¹'‘÷{lo+•–±P²ªƒ Vk4b!E¨´�ö$3K¡ �öÄdI{Tê ¢¶Þþâ—ã·¿×»ûö"¶Ž©Äh^}5jc8ÿÀgiMؽùóЭqèZkë—)¼¼ +Ù<ÿÔgpJÃü¥ó�Á_IZ0+ žI–“Å‘ ˆÅ·ßõÜ.¹ÃÆjü¹¹¦ÀC•”açÃ`ÄÌi'%"ƒÁØn:ooyYÖC%I0Üj@l,#ë¶Ql>Ê‹ ™/#Q´V$S™ª¢?„äÀ�HvG4< &0VÀ”Œ='èé³Ô\ú†^/øú™†ÎªÏ|¿ò 8÷ ÄÚEZ"ŠT“:®>r=Þüê/!¥cX¦Krq^€@Âg¸8 q œ´?S—iÐß3«Î2¹ã1ó¬(Á§›ÃtRJ•ÙAA÷–ê8Ý”5Ôʺ!¦öìÄÂÒæ.\€‚ Ù—A·‹Mà!_ªJ­î&r·yÑNÔ€I$Ëþ4ü:ØHÎI YC´kQ{ Èô³t|”jÄ6´ØÚûEÇs®žÿMgé€6:]jºÔÕåúGwÀÁC×àÖ[_Š·å×àë¿æñ�ßòCøÖú1¼ùK¾ÇŽŸ@³Z‡²Ù'�gpjSFä…ÿúk5‡Ñ¨·HR£¢�ö#E¾¶k­W�æTŽ¢#_>Ë Ç�b°àÁôa:ë}¹ž¯a©4½rV[,�YÍÀ›°Ù(˧¸‹²6ä¹0z §Ö±G øù¸ ±çZàÀmè¤YA6Á˜©#Z?‡êúYì­a¤Õ"Ÿ…åu¬› Š]×#<ùRTÜ@ Æ hºt÷޲lzt&ø®¿) 4ÂP³OO™´Naé,ZÅ*8€ññ)ŠÆ&Ʀñª×~)&wí£:£ÑFJ­IÛ/‹CäqHf€ÝVk“#h�4ѯ$è´hÕÑmÖ0¨ÄèWd´o„< IýÜ…Š. ²<)[ Qf x?*}fŠÕ>ŠÅ.ÒóëÈ/n Xì!ßܲ¥òåLgC†S7Ý„ÑñqŒNLR‘¥)©U?vï§KÓ7x‹NYñŸPÅÔÛ"¥DL½¤¨Àû¸&bç~JÇêö:TÌïu»0[U˜–Î #:~­F®œÒWpÃò?K¡ëk«`« PªðÜÃÖàm¬¯÷„Á…0Œ¶�æ šrà°—‚0<6ÔB§? 1Ì,í#ªÖ¨~Òš]‚J](¯§ôH_†Å(% xl 7¼úøÁ~^×z�ÑùDÙÇc% «x7iA«FYä°a€÷ã?üKxð#ïÃ=ïüK´/žC\­£N’$=‹ ZÃðÌnì=~-†'§Ñ cN5ÂÂ¥‚± âftŽXÃrÃ�;}èŒÎ‡Û^õf Èñç?üíÈz}ú5d†àü(ö^Ä P}¤ÔÝòóÞ®0¯¥BQÉ!àï @—ÖÈœš#–,„³`æB;ý; Ø-wý³F½Î�v: “’XPÖ|l9‘(gBÆ_rÀQ¨UYóícÁ$V(¸´5 uäè.<�îÜ<i!•&í®Õöý^]Õ@pä: >Š\GàˆÃZP¿‘ä";Àì2þ|Ö:??Á”Û¬(EN)ʃàšaI\‘ý5Ø !ªR‘›ŠÛ:Æ�¾µ[n¢ÒPåÄŸ…qøC0£ËõÇÏŸ>F L:'BA™-X¦ž5Ë€c ¬tTp+%=OÿÎ yÓÍ€åD%€¬…°Ÿ{ìdD:8xð*œºå6|ø]ïMÒØÜñÄ=ŸÁuw¾AzöÎÖ‹¦-,êµzÝ2KŒÏ²éÓéQÒS´(("RJc,%�X¸Ô˜d ˆV¾±¾Fi;:+‰¨‚ÓnÌ ¢ç8ÚY[_Áèè8ØŠúâ™g°±²Lû–궃t°Q©V2t±=®À!NI °ß™Ôj_ÑjAH´¦wãm?ò+Û±‹´Ðv<ü d " ONQáÞ¬ö‘=±HàÀ…#.Û‡N­â÷ÐøØ‚gg#âþ“O_¬ë-@¨Q92i¶ÎãË8ÒZQ^Þ3 .n´W–±pé<¥g¢8¡I6®TQ�â/™Ño'ž¹Hn¦ôªzˆÊ�a TE÷ñ%šÀxÌØAõ'kA&Nø£ß�{ÿæ�©)rxhë�ÒæÔŽ>HÖ›|¶ Òs“L*þ"ûzb¾U¸Mý"΢— û.µÆ¹tÀ�Ñäíyä«2šØJCeiî�àh‰S…B€‰ž³!tÈ�.ž…赡LF“Œª6`«C@eVP¡¾|׊„ó–)ùØ®»©0ã�?#3ž¬Ãƒ’§¼PäèoÖijÝ9 � á*ÀÕ/~ ^üö»)�JÃîÞ{÷¼ÿwíÎïÓú½çÝ{¬%ªtºÄôäÆiQ×8ÍFqJ\e@S ª@ÖCˆPbec¿ô ?�‹çNƒ`ÏÃêÿþ#“¼póRh¾iŸ jùÒÒyÇ´; í4!YÏn+«�¯‚Ž™T*õ�ŒŒC8‰#Ð9�ÕÕê¡év; ÆÍò5€›<·–¿(½G™–=»÷SÖàãï{'Þÿ'€�ÅXkl·Û=?èuü�lðSØWäжkW¬µE¿×E½5©ui¸ ]\å‘KÚ£¯üE_ï�€�åc«th�Æë·ñR3®aÔËiÓÿ‘t ©~¢Ô0j”ôjõá4†G� ¯¿Çì(”‚‚®‹#4[R°†–ÄB'�ž±Pƒ&Žè•F oøßD¦P?ñ!�â‚-пðDuÍÃ7�ÎWF<)ÜUOµfyi3N� ãª1«Í”¾ ÷óEù 8x25QQý:늊~æt““qž5¦ F&åË™�ÑBn•ÌÇBDã{©‘°Ða+œœð¼Tx1@÷Å2³L€)ÌÌÎp>9R —R3D÷ö”¥¸¸}pîQØÅg µHò%ˆì=y#îüâw€�ÆO {¥,(Ü£�&>øx€ä ¾Í({Ê6ÂÖ[ ˆ� õŒ¨? ÂL²¹ÑÂHÀQ¬árÅzv¥ )´ n¿é.üÆ£•iEE ˜ÏžÁèÄhÈ­:r>åÈ@c)›GFèhQã7n2CÏ:íº^¯)ÛHCC#¥³,7„ºßw­VÃêêRiLçÔÆ¥ô¡ °ˆk–aqi;’ 9v~úÝïÄÂ&�š<‡RÊ*+–•PŸÅö¸b‡š Bk�Ö[­Û'§fjJk„õ!œzñ«ÑRcgg¹ðIõ“`Ï0D iµežY2ã-ÛVvŒ¡W¯€‡ÕxÃ{®¶Ú&ê³°=ZA8’²Õ±tXY«×¨¢G F§õ†ðI üÈ¥ZĽ>ÂÞ€ö£Fn [1¨ï nnÓËi%I;)�n£ qµ†‘©™M&ÌG±±¼@L*–v6�Îa’Xg’‹ª<ù”î•’" ~r«\ Dùo0 {~í®'†Ÿøu?\d€a`SÊ)8©'æ vµì¨è¬“ T@û–�X¡ƒ€Þ—å)¤$ª8JÜâÀë#vmÆPZ“M€ÓKïK¯YCDŠl8ÍÁQ“ð4ܶ+/0Ôƽý ýÄGÐo·¡„B'E‘çOI©þìbÚߦ=_Ñ`cÌÁ8Iî ‚ ‰RW뺗¾S)@®œ ÔLj¸À,tÈ‘wSñt~ï4M0~ÉnÂw@àžƒ%•[UÔ¨géEA ë!$_èÀt2ˆ‚> Š@£ÓpVË´ðúoš›  ³Q·G@# Q[é<ºŠN¶¹å@ ½²0T6¬j `xb •¡<ú©� ëu©OCÚ �åÈ¡J]0�ÐÆ¾.Jú¢‚Î"׳„fkf—g–-_¾rå šP¹ûßr^›&qzŸñ—׬›ÆQ7ŸZ8•jöÒ‘àó»†OÖƒ‹D¹)RÀ’ C@Å‘‡ð=y8ñÔ±¢ìHÊ›+t;.$÷+,˜,Cpù14u†Á ƒ^¿‡ÿÌÞU@;r\Ù[ÐÝb}¦™ÿgÆ{Æ;¦$v—™™™½ÌÌÌLfÇACì˜hÆc¢Ï_DMU«¼.Wiû|e½L®st$µZ­–ÔÝ·Þ{÷ÝÛh4ð¬/úÜð™_&Dþ‚Ÿ�Ü‚Ü*¹hšål“slLû\�F.%ç¶ÁŒÆ`X ЙªaP-£_¯@{‚¢0Çädv” %¢¿?zâýµVè B\ù¬!¤cõ�Ö™òß“!ä^·@[çQîX�.�¦é7wl2ÅWÊH)mCéèEA’§N'³ÈV©!­pGr”k¸ž4‰Æún~åßaãüDƒŒ¢Þ*Q5¥r©Pêœ�C�§Çÿ4° Sú€_*½°X*M—ÊeøÅ*žuýóq`§oj–ÕjL"+Z&gšä[£…+¦·ç&Ñž´yo3óÝ%òÈÏ”›]"®Ÿ ˜/S½†ˆ×»ÐabÖA½L'ÜöÁs½;ãl „R(fJ–f�‚„¬ì…#n„£Â Ä<¢ÔÜ…mra›çΠ·vžNò˜OC„q oj´†ëÚ‡®ÎW 0`s’Žš~9j3Ðkòe$ÙSÛèi ÝÌÓ²†RãŸcÁ!‹t,8À±Ñ¨PO)=,B »/öpÀ ˜KÍ\ÚÎÕ—è9š‰ô„•×Qˆhý¼÷ ‘²›­3É'A &b³¬9³�ÖôlÊA …Q¡º|Ÿû?‚ÊÄT0rž+î9ÏÛcä¨üæµ±uŸü`hòÀFËò'Jâ{ˆ >±Þ†7:þÙ³³¬îak:©ÖxÿGÞ &éöM\~ÃóˆÎOÃÖòÌâ"Y»œ›ÉB¿çD7ÝþšÈŽ=:„DU. ö?wÿ+§×ã(¢È&‰ãÑã=7Qu¶a¿‡¼å xüž»�FqF‚ˆ#(¥˜¢8¼=[¥éÕPºw°>uìT¿«ðôø3Äb¡�$ÕúÌôçA0)…äQãE¯Æ"+ØÙˆ\¨‚OQÚŠ1êL£6žJ lì[ On¸LǘÁùäd$ƒ"�ðçËyŒÄˆÎw Áì: 4Zl ˆ�[¦¢¶–›:IMÅ›R„þdÁšG¥Ûýìû™>“îd©”°ƒÊ”'&qò¾�£³½ItN_JÄÍ °ú"d©@+×ñofö&z£ vö^aÍÏ5Ý ;Ó3fhΖYŒJ}XgL+ìɸ#PQÞјmÄälhX€£ÏѰQV¶†ëcqD%XÆ�¹H9aQ)F 6ÌÎäÍXH›ô™«SÁIª(p�¸´ àœ73晥m»T‘B|â^¨ö9”JEzÒÇK†„€Ã×ÝHŸµ‹žæ¿qŒM©%÷<dv觸íT D�Oi¶Aµ„b¯þ$»QpDiŒ»ï»ý¸ŸiÓi†ùƒaf~ÉõGÅ]9³5>g]‘ùÞhhg´F©P—âbpöÞÆ²ÚDÏ™z|±T†s uuJ´n»Ý2`ãìÃëÑÝZÛÛ¸í=ïÀ£ù04}‡#&­Ê<ß÷…”{ØJš¦«çâðžÿc÷2±ÌÎú©Óký^¯Ûi5ÑßÙF²± %”‘Ñà³{ ªs­L0С±µr@ã.Dz4ÊÉ¥ ¢<§p¨ÄÀ L˜ys$ L)+ ¢lî®gÀÜtNsMç´Ù”  [“¡§v:É%q}­ô´"prÝöîä?x嵸æs¿Ì/ØôØT­ŠâæàHa›®]Ý]<³) ¥U–¢’ÎJÀ­ï$CÌsZ¦‰6ê¢ uP�ÅÚ¸tšvQ­cµÕ`j8f9uãóQaD¸”Ÿ#§&Ì,0Únýì· Ç™}BêþÃ^JÌ终ÀÂÅËâáz´~”$ˆ¢Øü.Î2:IS’ 25 +wB5ªÎ& á6öïÛOï £K‡®À5/ú4`Ôl-wi��LrÇXîæÀ¹>)KÔtÛ4�õn„„\](¹õÝg*Eœ»h�¥„Õm¸â—019Ký)­VÍÆŽ|ücèõÚN€Up+åÃðd:ÚÖöèð0Tìw—,F�H1º¿`¶_hÔ6›½6—T™mZ»mxË)#¸ÚàH±Ú˜NŸ¸3�¢‹¦JSš|Æ÷þæVÈý—ã‹ø9§ÙOy(Œ®›g¤}òÇâNÏúúh ¡¡— µ·àï^Kc…å¸"(ñyƃ½…’ž/Ìs13ÏåÔ’Wö+Ó{ý¾å 4;ÇÅ¡9Ƨ‡¯orvx« oóÃÛž ‡Ù «…i�ÉYÆõb¡¯Æ‘ÆÿÓÁà2øú‰¹¹­Õj�‘R"ñcßøc¸`i?Õ2¼+À<Hη�žÜ±gˆ5³~Áõ¾äéÎOå—�ØØA}uLkK(]8oª”‰>² ÕŠìF•àØØ·èzyò»yìÎ¥\ZCƒU+[M·g(?SÖ ´¨²‰p­ç•qœ¿h/Ѧó4n@3íoúûÐ>{ʈQ¦ˆ½* ×|:xPvûçŠývÙ¨ÀèÅ=2Ñ–MqNCM“Ã'¥í”Vv}•*gù<êü‰'õ©ìv!° 1“в}Î@ÁIäØˆÊѤ³åf8fÚhÍ(1b�ôˆj œÜ˜÷�~­-+ÏZ0pWÿqŠÄÌ5ÍF›§ ùö-ÎÓgµÛm\ðÂOÅÿÀÏZŸ÷ÙyPØÕwÉ=wQKžù˜Û®‹šØ¸ô²‹~àÞ;fÝ\t46}Üëã­¿÷‹xèCïXJo«Ï-᪗¿‹.@µR'=±4;^,àçI΂œQ„´±¹Š($›t“jU£âž¦#1j…ø>) éÐôÖ™5Á¹s§©çƲÙÑÙùDêì�8}×�ä1äy>—PÜ¿Œ¹C‡Æˆ•¶uä(ÒV ÝfæsUš$ZëOØ�|?®.V¥'d%ŒÂ ¥Ò�¦S‚‹eéû“išLù¥Ò¤ üÙbµVM“˜«A4(T*Å­Õó<¨”ë~¡� Ú�—2è5›Ã×ÊU!½B<4“0jUggŠ­­­FRQ:„Ö]0&ì8`G—1ƶ$¶ ck\ÊÖ=ýNúÆ<Í“þù¨Ûëw0Kõ{³%0Ÿ¨Š™bÀzpŒIŠ.ú“U„å¢]Nw¹nÌ €)¶»–òi ý�„HˆPw£L1—Ƒȼrxî Às29v�Ù?¢MW¶[ôù£RÙõ5Éa­gÒh eG  u]z \q5®xѧà£÷'ˆâˆŠÐB3°æ*ØüA«ÝÅíIhrÍ‚åÙY¶ˆïÉÑÚÃ?ï»aâ4uc¤ÚŽé°·N�–É 1YR$lï‹U¡Ö_R}(&½:d¯)§ 6êqÃJ‚sT„KƒqgbætÌ4iž9ËG¥¶‘’6cíLË}O‚^1Û²ßÅ¥èHÿ,9sE®ˆõÔë÷�zž=dŸ )ód þ…监Y®ïN”3¹óü²\ã—{šó`sŸ¹kXÃ0¾V”>ÒJ�x–DÄú1Ó4›�Æ6üÀ§ ¸Û®˜ãSÃ�z¥R#@¢�Á`0¼õÌ$#£%3æØƒŽ]È')Héà¸c>•j�ö� mG¥ g�?Ž»ßûl‚Öï÷ºeH_´~ðBÌ<f&A~¡€é•eÄ­ÂGú$eÃ?1|" Ãï¾Ê/>poÔo\SŸb&e û´R¥( ˜R3å½sSÏ8tøÒc÷Ý«W\ªÕëI)Ïój…b©.„ôÃ^ODý>‹ÂA*¤äs³3TÎ4õQÕétXÓóTe¢.&&§˜JÓTi­´VRr>ØY_O+Q}jJvÚ­JBJHÏÓBpoЄQ…z§½¹ÕÔJ·Ò8î^éB­u `À)gÍýÀYÆÅ8Ó÷ ºê<ØÄITãžð£ÜÚô§ tÑ›­˜Z Ö;@7r>éŒ#,ЩWv‹dÜ2�gÝ8`(µ{�ƒÄIÄT¯á¾ÈŠëaVÜË.:šQqÔ�Á¹æ9ƒVî³wíqˆÊŠ�D’˜/G`j/`²èÑb æäD ]Ú.÷ýb ¯ºÞò.ìœz";‰U‚Îã÷ :¹æ— ­Ü¤�¨–'Êú»˜"¾c ¥Úª+N�ЈbãÇIb]­<�¡C§È|t8—ö®mó§M›ØZœ²´Kç™õ€Q©w0fê?”®2©,W;ጻõ]ÿ�íµ�cAN†c8I)ŒÝ�µÒ¶€+|nlhËTéy‰~ ó¾†(L¡Ûi¡Ûíà†¯üJ¬ºÜLŽ2ï€a4â°Ñ‰]Ï­oŸ;¸ÁÍûr@“&÷¾\ÛÐnÛal×I\>z"pž˜™ÇìÜz;ëP…"8ã”–š[ÞGõÆÎ6Ùhî 4\¡Õ‰ž¡Ð©©Yl¬ŸGê¥Pª@ÛÉ–e–sù¤;’¦iµ”ÒóýÂh � ëÂA�(éÍ�-ºÓ5bÃäÚhœqUîäÉŸ¤ò¬•Óœb š5žœ}7¸¼’¢Ÿ�ÑÏv@“ß'82ˆAÇ•rÑX˜Ø«“\r’¯a­'“”Þc/¹ÈéðµÏÁá럇c�vv6‘$è0Ä`í {/Fœ +h'\¸FJÁ³:‡Ó5ƒ½�›™¥½0ÇIb?˜":Ém¯Œ³cfÌÒ¥5lØæ]NÆý?._>¢LÀ£Íþ9‚ƒ1X³¬&ã~é\@A€àì°¹³]?¦GG�çš àp‘“p!­Cû¹&}¨ ×Ÿg &ê¤@]¨MáY/ÿ,HÏs€±ËEÜDfãk+»¼î^Ê�Æ÷ÐèÑc2îxÍ�•{�îw!P½…�ÌLÏbqe«Ãˆaùða %ú½·w6Ém“ á¢TS+£{£Å¥ÍÄF’xéÖö&H’Ä&íëlLJÓD; ÆÓ†T¾;[˜¾Ÿ�æ¦æpæÄqÜö–7áìƒP£ôpI´Óí@<”÷­`â‚ À²‹³™xÁzAÕ––Hï^©¢¿ÓÀÖúšHÒxI)uE©X¾Giå ý‰«nxÎE‡_òìA·sM¿Ó•,J ‚„b¨×&ƒÉýQ­× <‚gõÇ‚ï‘Á“�Ap‚ž 4›Müú¯ý e*öìÙ‹ïþ®ï#¥ú�·¶ð‡üØn6P*ñ-ßüm¨Õªu›H/Â("°ìözÃ[—u;]¯ßïyíV«Ôí´Ñj61èõ*Q¿Éy}y¡R.cªVO5G̤TçW£°×SŒñv}v¶ÛÙÞ9¢TÚH{½�k Õ�&iÜÖZo�±ÓÜóº÷öÚê¿V®¦ò”³Z…-)½Y®€ýK099 9Y‚i&¦Ö«#ñ®K’äcIïeŒ—nþ»ÅÊâÈ’SņîÅд"3FQ9¡ÃÝ.n0¦8 †8ðH¶Æ,È´×lǽ¨øHZ‘QÞÕDÏÖøäcÿ¥WoWáLš€èöº˜,xˆçÀ.´@ãf­.Ož:Ê®íg±Â‡Œ Àê«Ù¾šyºí81S£FVîjgŠÁ´¶ƒÔDžkÜ´Ôe®è~´PoÈ)ýwŽÙÆ�’'Љ„ È(©Ø^ «ó¢À™°¦p pÊúI7Q7[øO¸eåY%s\˜ßIÃkœ…'-oµZð*u\ýâO%+ w\8`q½0n°|$ÁÆ×\ò¢›4ù‰ÏnŸ­sà@�Ù>CnhZ×Õ0=SSulžÔÄ ;{ú$Ê3³P™¬ŒéçR”bdÀpÝÛÄ ëÏ4š®¦ç†I(P­Ö3I›84u9K1Öä e Ëx# i·›1Ôêè4xåý6NÜóq¤aˆR±hÚ>ÍöY¥ ¹8�Êò^0§ ሠ9Öœ_*Ñ>é0B©\F†A¢Ò+¸Â·¥L/K)KåjµV¯Õ.~ì�f½ï~€ '�·ÒÜâ"ÎÝu:J¡×ïc0¼pç(JŸõ{=¸cÄõ,™2(=—Bd‘x�Q¸Óí“Ð÷|Ú¿žý %ú-?y·k\¥Z³3s즛~��FkkþÚÚZõܹ³X_[cC°¹8 ñ' _¡¢x •:EáCW3þ J“)­Ïqß�îí¶Ô(ØÐ<9NJžô˜øèuº(/N‚M—öäN–NšeûA­Lj.+C7§;¦8š§mR�V¶fÃ92AL€,z�úEõhF25�ES»{î&Žèòéy‘θ;mkˆ¥b m€… Qö¬·<uj»Íæ ¼Ù�:ÒŸûÅ_ƒ×�xç=B‚èõÁ#…ÒÔ^¤Ò·'2QœÓ”î Ì¡fÎ/Þ`í¤à&RÐPLt ]Óœ‘å7d 01}7vV(é?u’¨J›;LºJBÓ‘C¢lŸNx¡LålÜpöÂÀ“ ;�ÔELŽ @ÛZJ·íÿ!€a.¢£å®°Í ;›œ¸‹¡�öõŠëž‡å‹/ß]Ó �{ ž»9åç\D2F2Éþ ù({<‹�åèÍùhZçö?ŸZ–A€f§€ê1Ô�¯Û-¤Ilgî Yj"Pè­ LLLÃ�6¬½ÃèdÁÉÿWÊ5º jÀXI+ÛûdMú”³ÜàYm�ÞÓÐÛ8æ$>úŽ·â‘�~I»Óm“¦Z¡^GuzÁÔ$еšA“2£Çž'0;YKÂ[O<�‚Òà•2zݪS“òà%‡¯î´šû„à3Bxe¥ô¼ßíÒ¾µCûäñ!ŸÆÛÞôJŸ-,-á97<“õ:¶67õææf§‰È<¨b�¤©6}l<ð ¬ÕiáÞ{îÖëk«Z0Æþè�ÿ�•K%S¯zäèôÃ6‚"~ûw år™QÄC}C…bÑdB<ºù~0¼ej×¥B�W;;ÄÊ‹Â>ü .ëAkÐãµõ5ÈRA©D4õ}ûhšÉ’$fqc†h5›Ÿˆ†ªçVW«C+m}ö왋†ßý¥ý^7~ÏsÍFã¡4Ž� #Ÿ»¼œ7î�ú!þ�ƒ=³Pñ™Ö¿;¿wù˸`Õ¨ÛÇ—}Ó·á+¾æ3¶G;D|dH(ŸO©³Ä“hì™E{¢ê(’:§•£…ê](�þ ÂâgÀ\<(]2 &3gÎÁã 룣8' [=°D�ÇäÍsŸ™{îf›ä)²øøJ�‘]oÙCéÂI;6¼éA‚öC›Ð)ÕŒÈ kcyʺ9vg/u›;ø›ûNÜwóÛÀ�é@q¯ÿÊO…˜\4QŒÕ™¢{§Vì:­˜“ìw^îpí9ö–QhÖ.ue ·n�ì¡KƒñÑ‚[B‚Õo³N—‰!hW'qò8lTÅ9{, ;1³hÖÈÌ“ÂFÝE×ÉÑ0W@vÑ”!W˜>GK7€¥ÒúøÝˆOÝM³ZJøš_ýS\zýs]ú%§Dž'±Œ¦ÔðTY•¹bÍnµ >®&³ uß½îÞ›Û_ù}U.ÊÇÙÇÁ¯ûSÜ=®ì¶Û”‚òª5¼ì+¿Ž\žçcÔ.šsAu†É‰ 8@¾±×ÕAgõ’ÍÍ5t»í ° Íä‰çÏH{¬&qŒÇîþ8¦Î¶ÏŸ'‡©Á�È­˜žÂÜ¡‹á‹dœ,ØŒ>Žº]¬?ø | LOOSjP pñe‡Ði6 Bº †·^»Cû 2ë£ÆÑFÓ¶$H!(í%¥ƒ“TðƒVûµCB…äBìëv:~†¡ï�ç{^…KQû½žÐZŒ±( ©‘xJ’8©%I¢8ãžÒJ )|!„¯�ÐèÀñÚD½ày^iø?Å™"†¤KB'i±T,)­ÙÀ¹`I¡69©«µ+•ʺR­²! Áò|rÈ�·çwáÀÊ>Ôëuq)¥•¦ÒnògjF!:Ý.†ÑŽ=ö¨~Í«_Ÿzüq(•vÊ•JOt:�wª4=®¡?È8ß¹/êGøW™¦‰€Ö+ý°Ç*•*„çÑS™(ê©! Ñ@ê Š,šóST«ay!Ä]…ÁÕoòÒ5AD`éR©)œK*Ø�5.OŒ úµ´‰0Xr]íNöÑ\=I»kΜªšÊfëZd�!9±âÒ~B ¥^?$à³*Îc4ØÊµ \öü—aç‰GÑoíÐŒ%ŠctÏ<^›FSVZÙæI½¸/dåô•cá¹�ÁÛ¡ \:KiÉ­U€°ZfnPDÃáj:®›Û) о˜EN§Œ"X›!À~¥Ù¬�Ëuö’©É8í6X=­QÃ8"!ŒÊÜ»aS‡ènÍ3(VÉ ’ŒÀ.zÙgààU×™ÈkœÿŒ&R·j"IáEÕ(eCeì Ä�gk}\+x½Lƒ–'Rf*Ì®é4\î˜wËÇÓŽ ˆ±jê.‚· t!õÊ“ eôÚmšñÎÎ/P!<@L©. eë8�FfÙ<;³` ðp:|î‚Ï™­“ÍÎ-@¯)D1õߘßTYñV+eÄ:�<|û­85ÉLS/\sxåÀ~T–)*‰fœ3¨ù=Ò”–g©Òõul?|)O¢×icnißö#߉ ¤§×é"Ž“Æë Š"ZþÐÇïÉã'¦!ZÛdô—hc[Á5½¾èv»S›««7Fýþ2Ï»‹™ð®àh·Û¬×ï øZë*€žâÁ _° `–1ÆcŸX§˜$q  lnA¿Û©�±ºVjÀ„9ªÐÚ—M¯Ê—Iy›tÒØÚ‡ÀäqÎÓ¥•}µÁ`� ÿc9V1ìéI~ö§~œ…‚ï�ž››Ç��å}ûØôÔ4fg173‡JµBŽ! Ðm~nžÈ išø¥ák?˜úª¯ÿ†Éã'Oî½ÿ¾{¯zâÈ‘^G‘Vê#èç�œó[½ °yg{'~jæiCd�ÖožZ˜n¹T.úÂ÷þÐ�ãE/~9ÒóM¤Çw Í$ü×�©ag~š–�™¥�m„ËŸ)³§VIUFï Z¡°\C°§•(ôŽnAu"h¸3­53�íáçó1�Õ.²Ê7Âí’æP ÃÈÊ‹˜æIŠlXÉó²ÿÞ DÛ}»Ñó­õ:Âç Éí�-¼å·'oÿ‰?¦J£+øW}*XeRJ3“Ò®ð.J°EÃÑ(ÇE ¶—ÆD1NÀÒD(Æ��ꀫ‰#S7k·l8ß“–@©=†¼fšKã¥ifãl¤iXS4¥U^ùÚîƒ VBÇQ«­ ©�Fò´i£–ÃD é»8y/&&&(Sž˜ÁWþÊâÐ57ä›1°( ڲΘ‘Aoï÷�Dœ�Ï’H ëÎÐ+µ C¨ŽiÜ6MÊUÓ}T.P'¿# ³Ô“Ç��¥…iŸ!L³–ãýpÆ/wǶ[gëü¼ïÏ~gîÿ½6¦ff©)ùðó_„Ù={íŸE7ÙišŒÉ¤l~nÉ^à�¢·�"³ˆÖgÝ^Û¢Åq”×_£ÑÚÜÄ=¼g￞ðèõ(‰©~!&j˜½ä”§§á´Ôì¤ÇFÁ£f€é ÄÎpλ]š°ù¾Oß¡6;�þ¥ŸÀåW^>êù´K‡7 �?Óù³«ØØÜÂýwÝ‹»?rÎ=q’"‚„¾«D·Ó¬ž9sÇŸw<8‹ÿàqíüÓI •$6o«â„é4õ¡uŒ%tP ”€Šç‡ÓŒuW2v6çbÊ+“:Iµ_, M’”3�„\PðJå’733+]v[Ùçæ±²¼Œw¼ç]xëk_CÔrà_õ: ¼çæ÷âï·Ñj·TFQ1ZûóQH©ô¨†¾…�mpßÛ¾·×Q»§ÑüÒ$ãü-³Ó×~T‹UüÀÏÿ ®<|%’#k@˜�(a©ˆÕ – |™;Àи´š»§ù>­±ôøøÆJ›¬tp¢ ­wdÚ�LM‡dqZSµ]\w§¡²ñÅUzaþäy²~fOÅ ' +>˜ÈôÑç:œíØ­n®,�Ǽ¹ãXkG#~ÿ«ÿ ïÿ£_‡NÉà ›Û›ð÷_‹â¡gCQÁÔ¥Ò‰jÜÅÔÔ3œ ¼É™›Ú 5qZº³¥ Ûz™›}R”f#ˆLIÀi�¥©kʃM…0G\pïµ€eÕ9·¯¥–ÑGϳ劶aÒr.‚á‚Ûïïê/ÚÊÝP²iD¸µ©Ý8Éšxë â߇ŠGOêCzÆg ¾ä¦Ÿ¢”‘Õ0ƒ†H5ƒˆÌÉD’d�£QLÀÁ?q3u$†|¨ÌX8ÜŸmzÀÌoÌ”B§fd˨4Ú|$ñ$Âýj™ ã^:šÈË »Ó¡9s‘üú™xÇïü<νQ¿‹B©YªU>3K{LÝÃK�R�ŒÒž€¦&Îù¹EjÆÔÚZW䤓ܱºÓØD»ÕB¯ß�óÊþ§ÕÓ§pç[ߌîÆ:¥ØØ”B”&lÚ ®>dL×èóИ?r°³ƒöÉSð“””’4¦ºÑܾ=øöþ.\}ÝÕfßÜš syLwÞnnl Áç<Þöº·âÁ[ïB¡TE¯ÛÅÖæF«µ¹õcw‡ÝßÇÿ°qÍÌKÚ¦•  u À €}&zÒæ\h–—¤çŠÅ99|­¼âpÀ0'Ò4õØp”«Uüáý9æggñ‹¿úK8öÈÃÄ^¼ðâÃø²/ýrýÀƒ¤÷.�sP­,5 XfV¶¹/»Ðê§’ÃÎ×ròÍ—JaÏѓੲÞëL2T.›/H2JëÙ°M•48pîÐ>ªå{\Ä2~F˜�l4™Ã�¾µà Vjð&Š™'¿/(ªéo@¥Ù;w–¦Ñ­WóÇ®Ûæ¶¿~ö~é _‚~cåJ½^‘,cò¹_”mäâ êîÂo˜=–^;sœg 2ºx÷±»œSõ $iÖ¨û²ïþqÜðY_¦�GРÔê¢ÜhÃëG”£ÈæÉè‘:�€'è ²r+Õä®ðN Î6º©ÊDj{�j+òJËRC>ÉñçI BÃ3#ßTJ”®ëWJ”–‹ J�î0ãë8¤Dþ®?þU¬>ôq ºYÄQ¨Oàâ羋û@Ja'%F¾ÈFÜF{ŽÒ,s³KT°¶ÿs®cÚÕPH|“Ô Zí&ÆÛ[›8ÿè1<|ÛG‡�7Hxs0#¾�ÉKcbqqÔSh´&hYs–�†Ø>~ýSgŒ}h[:UxÙ—}.¾ìk¾Ëû–ÁxÞÇ›ç p9•`�<0àüâ�ü<šç·†¢áޝŸ;ÿË^©ð³]?¯ñ¿p\Y¬p�$h}1€ s»À^1°ŸHÁåj877WRÈ!!PZñÀØÅ—^†›¾÷û±¼w/¥#F—7àf|ìÖ[ÓV§Ýi7›ëq>Q_\8Þ\ßø@ǯÒªS©µ®2F^,|ÐïÁgj'DRN`r¦$EÓš›À TïÙ1ž@#ßÇPè2Kã¿(pÏ#ÿH;¡+´˜|¯ª=#ÿ9y ã7Àrï%­3g? Ûðšn<ÙE9É ï2N]x—T¡]΀úä ®ü´Ïůü+jh£na� i¬CÌîèyšäV²ß uº½K'IŽj¬�ÓÑè£Ñ‘ue¦Ó?Ã/—fÓš$hœ%8—Ò“¦horåT’RØHÉ4_šÇ³ÑˆSˆ¦ÈKk³Î“@™ÝcÔÅ‘1R H“Ô ˆ4…ÑßB§&Õ –„àÛgP-W¨=Š"DRⲡ¢Cu§E°~o@)2'Y-F)ª ²Š^ö³ž*OØh� y®Éñê(w‘µY�Uh£î$޵­"J�ALï§â0µ@Äã~’Ò~–·[Ùñ.³é R$àé—KK�±'wÕZSî ¤úkP,e¶³¹�JµŠöÖÎi=…�K«F$Ñ™}?@¿ÛÅÑûïı;nCgsÜÈÝPA:ŽPšŸÇÔÁ Qšžvî±f»¹t™ù¿SôšM¬?øT§Ï÷!=i&@Ÿñ�_‚¯þƯDµVãÕ=F¬‘Ì¥d³¾£f çήáoÿðÏqö‘' �°h¬T†ý­�uv4þ—Žûú”âê¸n¼é™õI–öú´^P0‡ƒ+OŸ<1�ª´Î{¦Öú²ÚÑ=wÜÑÿ¶oüÚÒõÏaéÒË.Ç++â/y9[[]'Ož¨—‡4—bï×]÷Ü»?vçÒÉ|;€»ºP™ÓZß:13µоT?ÿý¿‰ý{d&i¾‡õ ö _*æÚñw¡‰æSIyR€vëMŸß@u£�ѽ¹2 NÒÉÙ;¶�Y hg{Û›¨`ki–NÄüöÝÏLËë§�µÍ&jë;0o"êÂb…žs_�„M÷‘-¨NLËBëFÊvMÍåœ|ø~¼ö§oBwc•>´Ùé ž¾åËŸo¤cFg’Ž2ª]íÂ�<¦E�ž vCk6Ë4¤`ÐNÏÍÚ€+XÜÒ#¥|2}¥¬•´µŠvÂŒf¹vöÕ`¶î"LŸž;¥KÐZÛÚЈý¯Ý¾³vQŸN•Ú€˜ýD™8wüÔÇQ+—Œ3%pãK?Ÿ÷™_ŽJ¢É”�©¬¶ÁÀ«ˆŠ—ÕæI˘”`¥kIù[‚ˆMa çœfd Ì²Ü +Uõ(Б¢cÁèØu‰&>ˆ�0E‘Y˜Ð�%Šnš!)‘xYôÓ¯—éÜìÕ*HÝb/˜Yš ú7W¦&AP.Ë2~òû {2…ã­•t�MnŒœ±EL÷âÒã§ÍEÀhƒ &àÏ–¡£݇6H±ÀÉØ0ìì�E›ê5» ò ¢suA�»7¡S§W­¶Ž(y(œ¸ÑƒâŒÒhñFÙIþ¢e¤œ�eÂ�>I¢õs?€“x7:½5œñâ$ ×.xPÊÖILÎܦ–b ÉMš# 8]2—Zrâ�L]z 4<)l$CëZ‹g—¡ßÙD)Œ–#罫@M똜¡±‰¶€âŠþpªÑ®ÑÓýþÎnÚý�»˜í±¼©›r¯ªx€Î­¯ÆDÀQ)•Ðh7051‹¸é—qhßE�PàÄ*䥲‚ç")�Ó½3’�k®apËS墥F¹çù”�e2ÒP ã™¹‚KöþXAP¡(I0ŠpÌ�”}^œ½(¡^ •�Ê…‘ØçäÚ©GRÈ@§S¯A›ÿ®ßéà-¿ÿ˸㭯ÂÑ�é0÷dR(eETçàŒc”¢îE…äðVŸ›Çü� 2à©Ô03·€ÙÙy×;cä�Â^ƒáí̉'p×-ïÅúLJ2OzØÞÞAZðQY^Farb”]–»Z£ßjaýØ1¤›´OQ�½´g}ÚKðußú5¸øðAG�Ø­€ �RõÇ︷}à#8ñð1tÛ]tÚ�,Ò £á-¤”¡gHÚ0ÒŒyÜl4Ö{íö¯�óßÿDJxz\Q(q–¤SZëy×0àYÒ®*¥¸011ˆƒmß÷Ïoœ8ùã¤QÄ®+×÷Ï-.Ý Ëa8µò~ô~ ³s{ˆQ³µgʺî…æ^6nÂQ 0wbLkgˆÆЇ§©8Ÿvct�l:Ë0šÕ­]¸‡fp㺼iÙ˜åã‰%);fÄ'EÕGy_ìIv�äœm#\ïe':gy°ùäƒw½ç­xÍ�KŠ% â}×Cî9lÆœ´Ž•å4Íl=ÅêN¹‹»£L�‚µˆVʾÇúÊ*©U2�Òzʸ¢.hXdöfYAôÜ5ý1«ºœóœ1hG‹N’4Wìv)AzÍ)ØÔŸ£àÚ Éy¬>öÄm(zœÔ…UªpÍsž�ïùÞÇTµYöÁK^¦†!yöŸ �O{#å“êJ5�Ž2‚N\ÔeÿÚn6,øÐç Pƒ6b`þ?„ ´Ù¶�TE”ZΖ�*7D&å–êìq7‚nö¡y‰ïS�§;UEw²ŠXzh…=¼ý�G?ú^ôw6ÉSFzžÒxÏ/ì].v{�rGRk-|éSÄÒëu)•ÄM„ )Á|Ó{—±÷àE˜š[ÀÌü"÷¬d©¥8ÂÆÙ3X;uë§O’·ÌÚÉÇÑëv(õ•˜áêPž›¤¤”¶&ãê>$Ô3µuü$¶‡@<)õâTÒø‚oþ*ªÏÔ'j0^CÊôõüõŸþ Þðÿ`Åm9,†db¤aŠ…½Ea:¾ì)­TÔëß…á/Š xÿ=c»êŸ7Î̳~§»�¤IZOšczcHÐÃk‹µO+Õ*XªTöI)±wåüÀwÿŠ•‹ÓY�f— @�-Àïªôì3 ÚhcúÌ:\:!;aËWÌQÍ&Üî£w¢ žºÿ5(�GÑ––§6ÆGÙ‹Së[(mµi_DÀQ:8.MJÅçˆÖº¬ö² #’Â(Øä�.QQ‘ô÷ð›°ýÀÝð|Ÿ$TB¯Šú³?�¢›l}Ë|râ‡v̰ǜ³³ŽvZT¦Öc×�9‰U.ÿÇIFOv¶ÎfXû[çÉ{ÓÀ1ß2 4a=eŒSfjéÒÐNDT™´£hk—íp,7'³³ ë)ÏÔÓi‚ôá÷£–´!IQ�ìó¾è«ñ9_øÅE¼(Á ÐÐj£îÝ‹MÄA7P¦ ÏlÊKeÏ Sžp9†�F–Êæ·“дaÒsɳ羸¹÷¸µwpbmNª€y&zML”CÇŸëAB˜ÐrÕèF‘FÌ�Nâ{8×ÞÆ+_ýG8uüat› øÅ‚çÇN=ö:•¤ç˜àÏÕ ËBÊ9Ïó'“8ªrÆ=)%>g½~*3J£{PJez³K{ÐÞÙA·±MZ`ÂóèLWŒ1"ÄøA€Æ`€òÊ^”¦¦ì±cë�RŽ¿vyÔíbuXWÂæ�^ÆžÌ|›ÊÓ¸éç~Ͼñ:²H€7óuw'ž8�ù¦›°½ºéù0çEzŒ!£6w;jÐï…Q…à'Z“=mWCÛ‚ïèÅÜÔHìÉ'‰I¹‰Ñ}£÷)*"' ”ۿ칋²l­ ÌECÙG0§2 IDÔFo*ç:J fÒ‚èl£÷à-`Ý jR ‚ææ—ðµC¦Ìág\‘í“`6*Ñ�z³½ÝËÀ†Š!&º†wLfËK£)]¦M †B;‡¹ãhC³ñM€íf€0æsJíQ$äI²`g%ó\Á€’Ó]Bš¥ß˜²!¯$=Hè CEØt~ãÞù¡7âÌêI¬ž=M�œóË{·ï¹ýöïýàÙ“û ¯0°šÖjÀ‹…�û•R—H)÷ ï犕Š?3;€1Ç1š,EA¡H“Æ©'ILi¨Á Ÿ2€OMÏ2&Úq„™Ë.�W.�NìýhDà±}æ ¶Ž�"rM’”¶ ­qͧ¾ß÷Ãß�}–Áò)“|ѮѮÛéà�{À]»xË»ÑXݤt‚줇7½±¾~¶»½ó˜âcq?¢¡ïc� gå!žÿ!C~a²X*õE&úF2’Kt¦jP‚›4Îé~fëñù|!Ÿ¥ ÅFÐæ¤6'’({FÒ\e²þJa´q4,|¹þš1‘óøôž“ôp#Úv:uQÍn}§$ ÁôCÄ¥ G„Óëc^0ìb‡…b•Dèœ:‚j�hŸ£QѨÅ(�‹’¹1 HáRj¦xîŒÐ\QݲÐÌÅÝF5‚q+Ñ!\ãhö™JÛz‘ÕDcÊ=Om¿ íGšíƒtþ´¯®9ÓQ™á"mAÇ÷%FuƤMÑéÑèŠÞûۘ*HY�Ò+LÌì]ÂÂÞ=�9L‰Õ¨[¨Í0HèøãöØÒv{ö¿Û…ɤ˜5 UZÒŠ;Џ¢hˆÎ€g`d•7�}h=z¯µ´ of"(ž¥ü¨®$‰á†OÜ=ÀDD¬ZƒPãsBëÈ‚„Hù@ù-Ž�õ ûC ,îÝ‹™ùùz¡TŠÀ\H70¼ Ý*ïOâ¨$…œ­UëW¦izc¯×½¾±½=9?¿T(ÊÓífÓ‹£I{Qêr¹¢„çiÎxª•NÃpÐ×Iz”{r “(òÂÆ6NÝq¥Ð&–WÔª`F¥Ó8ÆêÑ£dÿLZa$§€ó^!ÀW|ï·ás¾àÓ11Qƒûk$´NÍì7¯g Ôÿìç>×<ëZ|î}ny÷-xÛ+_�ÎNÅRQíăÁk9¿£9;s_2Hñôø2ŒÂ÷e­Ä+�BR·{×ç@¹8¾)rŒÔG^ºE�±`0é‡4u©î’G÷:%™{Ó68§úÑ8J³�1>3v¡ó³É[ßzÿœã¯â\IêßÐFõ¹X •ºõórôcð&¦f0sð0ÂN‹j7T£X{ ñžÃæV, 85{ëån"ð–VlîÒ10›–2ûÑ•íic»¤Ç Èšní)J¢S0X»fÛ�§”²ìž€6ÊÑžÜà]“©sÒ8:#pC-厬§¢`ìÀ2Ð2„Џ߂:õüþ6|O’ÿÉìÒÒ°küTxýS-èFuC͆bYÔ.¢QÆ–ÜÉ̘ &�ésû O•'®Fõ�˜)³Q�5dãf�\47üˆèn m^‚`@Ù§” «�‰"yð#0l¢`iÔˆ2R�¬úð*‹3h§Jy¥iŠfc'îtÚ>rãî~;ÐD6±Þ}ì3÷z[’Ä•~§;y¢óè�©J/OÓ¤ø…i!äŠïyÕ$Žãn§£½¥Á"•¦7AâMñ zN«ÙüÉjµvÑð¹Hâb§�Nûèå=ðg¦Àƒæ¿&Jóæ‘#ˆ7·mÓï`0 ècfe/~øWϼúrŠf°0h¨ÝªÈãšïèØÜ3üü/þª/Æ•×\‰Wþå?àþ�Þ¥TÀ—¥*IµÂÏÓïðôø�¤ p¨T¯xè½0«”Bmz¾î×þå…Å\$ãÆx¡Íqbœn”Z]j¤dÇÑö.”.›/zHÚ!úǶ­Ê€6šI«-#ñsÍœ»i[å÷1Yíb-’ �ž¶¹óÒrÞdÁ¦.tœ¢¦�¤‰¢¾‡Íåy7³#Þ¨´S¾Nã¯Ê×|ä5M`å{Z½üå+\ü0!Á]ºÈ=v�óöBojVšp„îê–1&Üúî¿Ì‰rË wÖÊ\X{ÃmC’ˆæhç?ÌçXÏ\­EQS¬NdÓ©8©Cptc7LS©ñEI"¤§îC­ua¿Ci0† .º_÷u7aïôÅØÆMnš]¹ÖvYæü*¡3OæÕDƒÁõ̘HÆ—Œ»u\Ú͆n"I¬ó,}¶Ò.-§µ$%¸¨Q5Î\”e´Ú`&iéx¨¤ÎÎ*�ýQ‰²– g¯{ÇkqÿÑûÉ»frvÒ“Û�Üsÿ¤ÐõÞ“�é§Ü X( OHTJ!…ôã$.Ak6I“~¹Tê{^ЄýôŽöÎàêbEBéoà‚ßäÁŠ?•rÍf‰Rð'ê¨îYB01AŽš«÷ÞÕ�ïFÍâÆÏy¾ä+‡Àpõ•`®Wæ©æîǧ= õÿÜéóøåùE¬Ÿ>‡^¿§Û�ÆZ§Ùü“b¹ü³·7¶žÿ¡ã}63ÅRéÖ¥••‹º½.’Tã»þú­X:ppWÝ1£¸Ÿ{müȃQ}«�ɳ›£ÿ:D=@éÐ ­­v¨>g�)=_¸‡@9y£øŒ‰.rüã�§)–Ž�² ®Œ`OàNp“Àf«$ŠÈ ç/\Îy»¡Çð‘;oÅ÷W!jíP¾›¬“ ð>…Ń,lÏ�ë;pQ”¹ÀKCe6ÄÃØ²ë[5gÃl£‹xœ$¶žÂMºË¸[ZV˜v—VóÌ;Ù|»ý}]ºÖÇ„�¹ý�ærõp��¼Ž‰ZÒLý—ž; Šö¬š�Ú9ïô}(¥}Šh ¨Ôð‚_ŽÏûœ¯"‡Gž¦F�A3˜ô•P‚e¡ÍoÄ™«­hÐH=A�üIà™º%£{Å ”ˆf<¼·rK~#¡îz™&6åæ…1JͶ aR‚"NiE¢œC­ÖT˜3jru$0Ûsf�*R;`Ó%ð©2 ú¿vþ%žÿÁ� 0ˆãè¶V«yqPd±RÁ#wÝŠ‹®¸Újr¹bw^%à©û}ÀyØØ¢)¥(8#áMJó$Š4ÑÜÆè„"‘B@™#� ­ÇÊå÷�7k%Ú=:™“^J t°³LÓscòíTìBZ,@ïB‚Û]´P©brv‹{—Ñm4P)WQ›œÂz?Fgg²>•¤£½-`L:ËÌðMôÂ¥\�C)£Àá9�³¡Lçl~M3'S`ÆsƘ¹Ù4ƒSuö¤´Mo£¶¶Öâú{80º hS¯á°Ennˆ"×£cAŠiçY�¸Û†O=K„qŸ(–+˜\ÙGÿ£‘ö·ƒ™ß.)JžG Ý{Ò²ÉrrGcm3Ü�wŠÝÝ!VS„ÆHj&D@Ë»5`gnÒF\…n…~ˆÊV 2Ža€ÈF7eÁ¥Õ8³Ç::ÐÝ�êÅ`µ€RiE/ a7 ¢ÁJõêT¯×ÙÆѸ7ê7¯*ÉŸ`©úG¿¶±±öB!…'¥ÃÁR"®$¡Rª7³¼·9´i^ÞÙØi”â£o»zû{ñ‚Ï|^ñ¯Àž•½�æÿr(Ìhòô†W½ó[b¯-©IÇZ… “~Q²Mæ€t”¾�ct{]‡ƒ.€£xzü‡©´B’$]¥TÅ"OÜ7zí6ù‚`ôÄr ºãÆ85fZ.“$›]ºi¢uÄ4ùèX™×]ñ66�œvg.ä÷1˜Üg(Oº’p”f$a˜Qöý.´ó£Q15�sðd°&j_w#NßõQræÛØZC«Ó‚_­ÁÛ9U™“ �P&w?f&Zá’Í1m_—OÖfÌ—tV7ΘMJ £M—ƳÕÉÚPšÎ¤ÞRÚOrpñ¤õ€²‘Ý Px™¶˜¥SÍMÚõS8u³“2si@ãÐ nôØÂÈØ`§àiDŸ×ê4Ñë÷HºDÕjð÷ìAX(ÀÿÜG�‡°\@ìyîÏÊ›ïí&Û¯Ç[Eç•Ìó)\¸õò�Îì³?ÝZ…TZSuúT¶[º€’qb"6ž$à®Fäh× @«Ÿ6Î6‘tÔ i~O½qþ|—1¶†ÿÂqo¯­)ø…ƒ~Ï@uœwýã›ðÑw/û¼OÇ _öÌÎÏYm>!8v¶wðêßÿK2Ï“¶ïÅÈ͘¾%жÈm“hÕ ÚDDF�ÓÊÚŒê¡ÁõÝXƒJSGß³ëss¯²´Ÿ% � UL>öi’Pc^Ç�¥2‚RZ¹Hâ{Ô•J�Ôª �i8×»Z2»uw;Öíò1RM»ˆ çÏ�ü¹I‰¸ZA{x€J§‹ÊN ¥F $­¤‘Hxal##¦XvOê€BÖ�ÑÙn¢ÓnCC¡Ûé„Ûë­õiü7Œ{Ã^à57”ªï‘B~m¯ßý”8ŽŽøÅaÚêÔ3BVnu!å×ùÅ¢ïûÕZ¥X.{Û«›xÕü%Þü7¯Áu/º—]}.¼ðj5Ü{ç½$‰S H‰‡R…,Ž^§£¥çI.d; £Îðq±ÛÙJšAÃÖ* g|‡'ã8>ª¡ï�$jãéñ>$ø~ð˜8ìõ+&ãøØ»ßŒ—^ /(ìFv�ÇG7ùè‚fk,w–˪o)\i;²…Rm6þ.Š1'ë¸Ò ó¢c�‘uB Ì¥ ­w`‚)ÍcØb‰ž"Q‚Á�J­Ž‰Å½Ø:þ‚À§†8�$@ÔFÔ=oz/˜�–ÚìdôM͆$Õ�*®�¶+Ÿö-Mm�ßZíšfP«´ MÌ1³ŽKWqÓ±F±%)pÀ© = XÜê¬ Î­¼‰kÕ´nœ¤6‚"³æmN¾F`b– †f�‰?IVHCp�¢Zª¢ÝmXUªu\xÉ•¤�LÝó’|cèù8ÆÃf´ËF"UÜîú3wˆØîlzê–»ŒÁ.©»NµŒîðVšê¡ºÕD±Ó#ßPczæEIVû1$dR8dƒ¼¾¹†ÀȲ ÓS­t08Ç8ïã¿qÜÚ$Þßzf¹ö;÷ÅÔ÷ÓðÏÞuûŸQÁó£NÿҰߟcE!…¯Åºí^v߇ïBG¤ÈÃ$ч4ÑÍfs­½½ó°JÓXCGœñ¦�òhàÍæövZGHÕšÒ*   àa&ÄéûÂ~ЧÇÊ`™¦Í�áwO|O}zj¿'}æ*ø´›~W½àåYÍÀ�Șów7‹]·LÆ1™¥[{¦–OCÖŠhzǶ�¶"[ VœacÿA¾ÀO“\3Sž´çó ×%‰s.eVQ/�?ïó'}¹ÕÅÔéuÀŒâ�:¼Zf˜JªŸ�lM܉�&ƒj ›{fÍ—ÃK3îºùíxó¯ý¤J�„Ù@zh£Œâ¥/€¨LY�a7�Ç¿ëE¥k'˜i-\�Êʶ[‡O­Í²Me9ñK 'Üéºú}Ïy׋'#O§í˜sÙHO�ª”Ò(؆3«:`ëKF·G#ÝYEáÄÇPÒ �MÐÇÜ�‹ðÍ¿ó7˜Y9p6Þz"×~‘?Fµ‹vsÇp.6&åæ¶ÿÙ™n]06ÆÓ+·�^£Üì`bm "6:jf%ž*JU‹(!RÂ݉7}ôí8ÝZ%?îì©áÅö÷˜'óîNSý�×Úššç\ã¯èô:7 !æªõú¤ üºRJpÎ|)}AÀúƒ¾NãƒÁ ÓÞÙy%€›ü ÞÙi$xzü�ÌÊHË`Qùº™Å…k¦&§ƒ~8@if _𣿄•C—CHùúhÜȧÊí.fŽŸuS“¥KfHiY' Ý7¨^dé“-Ø\Y¤‹q¿Ý´&Ùô~»…AØG¯Õ UåúÌ8$k±zôALî]A¡6�Äh¬žC}i™>°\*£:5ƒéÅ=xô�»±ïâKQž˜¢âr5VX8·MÛ‚üYg5@QF˜’òsÚ‹³›àÄHÓœ�¹ÐŒå³�?‚7þúOb㉣ÐQ„$Š&:Cpé áÏî·?³F]®cß k�1»0ZµEú,bp2ÿŒ;[fçéNŸáƒžsËh3щýl ŽuFIf m¶í{*篓¤©�ÒŒ¥Á¨�ZÎïžÖµ$(ìœBiý¢ö�às—>_ÿ�êôl.’Ï Ÿ^ÝUÓ/¯ù7¾•cÌÿ?îÃX>��¾ÌÐ9�ôÃåFÕ­ÕuFQJ¤Š¬­ßòþ7áæ�ß‚VÒE¯×Õ›kkGØÏ}¤±ñJü/Ï›œ ‚ ¸^Òé´^¨¡ç¥ïïÓZW<ÏÓIG*U±RéÇüô}O×]þÇ‚ �gW§¾¥69ùc•Zu/ÈRX õJ¸á‹¿W¿øS11»�›õågñãÁ¦¶ÕÄÄê&˜2ÅL•“•.›£UÚ3è$A«ÛÂý�܇[�Ý�¾/Ð;’<(Â(ÅÄôKØm"û&gŒQ^Èá� Šh´N‘¤Š.–Ò À„GB|iQ¡¼\ðIN½ �ÉÊfýZÍX9ˆ«†Òó‡÷¡X,’tWœ\;ÓA‚t'+Àž9|Àªä@f÷ßÚê¾å‡ŠÞ‰ãÝGB†ƒ~Ÿö/.N¡té )º€¶Òü® Ÿóч³ÈY4gÍ”ŒÛË\›ÁEyN43Š3k) óÉ €s=6¤`|r °9åiGB0 AdÚWC}6$ŽQÕp‘M´bžÞé{àm�€ŽúÃI›L,À7ýÞßazÏ òµŒï¹Ê¿–»8`æÜXóë)�«ÏŒÓï T»³'9œúE~;^œ¡`òÜ&ü~è¶'8ÞôÎWã#»­A ý~/ÞÞØ8Â4~ðÖÖÖ»ñ¿|\[ªIšfÀŒ'½ZœÄE)äyÏ󸵹µ‰§Çÿl°6x.ìO¦æf_ƒr©X¢ü}%X¸üj\óòÏÄ…W\ƒR­NªÐÒóÇ<óEØ…çˆ]¥móš¨ Ô°¶vÇÏaóèi<òøœ‚K»ÓÄÖ ‰4 xÛ;ô{Tf $˜R7»$Pà ßИœœ!!¿N·MÒ,·«à—«´œ¨½¾Oïí4`ÌC©X@¥VÁâÂ2X¤PªWPŸ�E±VE©XB�K_Ž QG1(bgÿô+E`Œ ý¡±±Š7þÆOcýÑÑÚÜ@¨ÐíE¤Á$0¹ 6{(”)Ê0FeÎ&Úuæ[-30X¥W·qŽ¡@&¬r³¹˜›u\ci’¦”ŒÞš=£(mšK�´�Ü€ŸÙä f L9å0ÓA$G¸0û®Ì­Aõ™äÈ-ÀæI”<ßó³ïS,ákÿð)G@>=66½•ïx+fW›Ì“{<^òÉ}ÞSñŠr Æ(­‹$ÅôêE:濪¼½óýHXŠ$NºC&ÚǵVŸÿþõ3O_ŒŸÿ�`ãÒi .~£:1ñìÙù…É(EªRºx¯0µr ]‚Åý±tð0À2®{wgLpJsõ:™È�JJUM®nA—ïÛ{*I 4δÏbµ»�{?úaìœ_£>›~ØC±Z%jo«Ù€¦ú€O©­$¡œ,”Vô~Ó[âêœS¤£µ²4Û8Ž‘$‰õÉR€Ù)=²½¾n;˜‡ß›z_88*u…€è—Q!ì÷ 0W_Äü�ê×_�Úò Š•:f÷Pd•]˜Ÿ‚*µVxàÃ7ãî7ÿVOCccÝN•J�~·~¨Ê Te|z/XP†,V0êšÉLL>3ª…ÆÌ ‰ó q´ã,2Å|'²É· •¦Þc�…– àŽ]nT ì¬l¥0÷c¶7.¥–1椤ÿB)çé\c@ÖÞ¹!6ƒˆû´ ßóhŸ÷‹€g¾èSÆ\›ŒÓ®Ëƒ òËòþ{¹fTèOLÊ �7rµ#½Ë�´k RkÔšmLŸZC§¹ƒ¿yíŸbµqŽ¢¢A¯×Ø\]{3çü›ß|üáO�§Ç3ØÐ¸¾59%¤Ç»ÝŽ>{âD·ßéÄõ™™: 9 ðD½>}þp“à\ÐÅkbr @³¹ß÷éâÇ8'Ò��2»p†*¥ʳsÐ:†–/¿ ²PBub —^ÿ\+U@±\Eu¸Mƒ´ÑkFŸã�oÄ»ÿøWÐßܤÏô‚Ó3³äÕÁ¸D”­N¡À«3ð&ÀKS`¥š‘“á£2í6ÕeÀÆ6µ%Ij)ÅÐ0æU&½å@é=$¨)�µ ojmÓ]Î(ÍY˜þZϹiZJ6Ý“E�VÖAp8ïœ<ÈXwPK :¨®AÒÞ Zžï´‡?ë ñe?øó`&Êe㢄œhê'·Êøä~Eù1FXÀy?��jÆPÞq6¯¸ž�¨94IçÄ÷Þ‡·üý u©¾Ùj4ÿ©½³n#é¶ð¹Ý=<’lÙq^fÈîcfffffffffffZfÊ2ÇA;¶dËlzã±¢Ò++¥÷3Í—jñÈ¡©SwîísÎ÷7ÖSõ]ÿtêE…††7±©¹Yx |±ïùÜ=tðj‡ñˆ1 ‰Yš€sÇqë“ޘݞ�ëù€ÝµÑǸÉkk©DkG,êÏ2ÆQù%¡Ž¦-òú^Ô¯ŒÖÚõ|[Ýk‘Ë¢ÈË"×A»M[+ÌâÂüòþ¼J\™?pàŠ2ÏX²Þ󕒅ﯺqèy­Ö!Çuã¥ýûŸ{üñÓ2ÍÊ#×^}m–$*Œ[m£äPkc滜Ñ|g¾ëæIŠl˜àÂùsu•A£4ÁZíVûbSºDÎ�úDVµKmV[¢ÏµçÁFîÙÆZÄ;UàåW£TàbܧpÉö6^ºû?Qö×ëï̲º´SQé0Œ˜ëûä²®è $iË}ˆhíåƒØ”.lÜ‚cãJcÒ6†M¸ a\ÕLØÊŒ7Q^„Ñø}ØäF£¾Šà˜ðcãœMìŸ1ãã8ccäŒÆU‘ 18Ÿ4½8p0¾ä7Ùßñ’ /€ VG!nÃ4�[ ô—ýòŸbÿÑËÇ•öz7îeÆ~­©¢4C\€ÙŸ³Sš-` ÐA›p°8ýŸÿ†çÿõoQ¨I2Ì«~ÍSÃáögþáS<�††7#±sñåùC‡?gxaý¼*KS貌‰1?ˆB²dZkµSi£µ1†y®o‰ÀµÖLW”eaˆ1KÄdQ¥V’””¹çùBKµsXx  /ËÒV0X;$¢5FìU œÐJe œæŒ÷*QdEÖ7FÖZª­/Œ±A&Z)VÊÒ¬+eAžZÏõ� &Ä;Zc|?è»®V›«H™åNÜ™»~±Û=𠇢Õé&‚5v˜l RƘÆ:º(ëœuÆÙx—r½”‚ãºõ2Æ õ–*Á€ЊÛ�²€µ½~¯@JYIúAP'¨¬Ä®o´>ÏïQTé[;âBˆ(Œv¿»Èáûü ©së�̃ôÚ°Ñ<àÇ`A Â�Æ65ãaŒÅeêå¢É´O¥ ´ Œ«'`<Àv?g&Ò51š&›p)W[�pãé5GðúçL¦ˆ ÎÆ}!¥õèÒ[-`c�de†ðÌ0ë¯Ö}3ct�Ð)\ïý¥_�÷úÄÏ1>ÝÑbzlù¥£Ä§ˆ��-³O¬=bsißµ1t)Wƒ½ÑRâ™?úmô«þ§›ýÞpscã.¥åÇýÌÿ�¢¡áÍPljnudzßkµÞYçÅuJ–€ˆ¨Ë¸p‰7JI/„®/טu€Ö@H¬µ;ÇúƘ¶ë¹ €æ\l8ÂÙÊ‹ìì’èƒ(%ÆŠê=„q¬ïX=cñz䶸ÃLQ´B/h1ί�»·ZæÚv…�!GÄÇ®½æšd0ÐgÏ't°šÚ[–YN°p+']2Zï«©,KUo£’EÅü½Í�zòÌåŽ1Ê©Jæ:ÞŽni¥Nð}I–Z":v9>ýû~ËU/qêÍé웽ÕÌôצõjö Ðtfï[›½¥`Z߈€ìü9¼üÇ¿ƒók§ê†[ƒiûíÚšŸÿÁþc‹††7W±™äxÔf‚ˆÊ<ï€H‘!¬5p�¤’ FRGÚÀÓ*Ëa¥ä`̆~`ÓdÈÇç-çú¢Ï;1²�¥Cƒ7nõB!÷â0by‘s™g¡ãWù~pCQx\½´´¼LD^¿ß‹{gÏÁqÑŽÛÖó¼(ÉÒPk%ˆsf¬Õ{´0¿˜·š8£t{˜2"a¬áŽp{DôDøuKtú?Î�4xäJbƆ­¨ýBˆ[¤’ïï8îÁÃW^qE‘fœ;ÂϲÄÍòlÔ×’uE•ç áÁ ~« /¢ æ`ƒ6ìÿÝ•¿gh`Wt v™Œí½XÁ0ØÑe9ßá`‚�S@}Òȇ[µH¹œ�Æÿ7öu:h †BJ€nwcJ¥‘k…´”ÐÆ"bç`£êÌh…D*Ø­ó({+�yvQ¤ê©CÅÞç ¿òÙ_í½6½³·b˜}ilvÕS ÉÿOthJµ5»§3}ú Záå¿ûK<ö‡g^|­¨mÓÁðôòÂþ÷úÅ{þå44¼eˆMÃíq‡9Œ³<ÏÜÀõÂUZuªç-:ðC0Î÷Yk½À "ιYï]8¢µŠÂ ÚfŒ­uî¹î³Yž¯j­b!Ä–%Z©v;kLáÓo~gF€ßjun®ó•P½S©å¼TålŸ±¶íÁFš!Äî„Rn.\/q†y°LÀ\ÜÂ4s «%¬Šº,À„¹!hÔsFBêÐ7×õjpü Ãtˆ•s+XßXC2Ü„+¸œÕx´,ÐéÌÃóÂúØ…v ‹�È2ƒR»ß+e‰b4htµŒÃÒ¾e´Zm!`¡Q)ÖÖÎáüÙ3ØØ¸P÷ʬ5(¥¬E±uä(>æ›×Üþΰ—P7í÷ˆÅlA+Àì f| ¦ŠÇk�Ød¼6]�â¹gñâ¿þþõŽ¿ÃFï”’eomýWÂ0úê»{« ÿ±iøª÷ø0>ÚE™¿Ûo­}æˆkÚ�β•:f‚{Âqx–¥µÈ„Q4� ˆ -«Ç¸eµÜºÏäCT+/%´,ár1BЊëJ– ¸‚Ãe Vë:Sj‰ÍAIšáüú*Î_8­¬5 »}�VÔÚ½Ô`ßâÒ4ÙýùZ×Âñz0$I‡hÅ-ø®k�0Ñ�ë¢Ýê`±»„ùê1��åimS³vþ<Î�=…Í­>L=íÆêUj…+ßçƒð>Ÿýeˆ» šô!›:J<9ž¼·’™%>3œÏg¼÷ÿX;®OµÕ©Ñ•Ÿ¯yøÎz3§6ƤÃä|ž$Ÿø„*îFCÃkC#6 ŸuÛ»'^éBû]?8n�}¿¼Ì¯ÁÍòlÎõ<Ç-vðÐặ£Lm4Ykë*%Š"(¥jÐJÕc¬7/ʲî3AXŽs-U=Î~êÌ«xü©GÑßÚíŽWkuqƒíx”à�ÖU‹ë¸¨.%¢·Õ³I²M|w_�BX1£Ô8É›ˆÑ|§K•àØ¥ÅýèÄsù\½Ôî.?t…:vè²" ã®2Êéõ6þ«TåÁRï¿5ØŒ†É°Ç0Œ°tåhUNÐW¿ï‡Ö{—,ÆìÙÝOti#YXĄ̀<^ã�€×"N�.áR­�¯¬+¯ÀnõðÈcàßîøGäyVö××ïWeùmUUs'^ß4bÓðûyToÌä­„bÑsý9×u[•ÉËü:€)ËªŠ EDQUm¥,E…!bÚ÷ý¨š¶ó¸ïv×NŸÙdÄ c,ɳt{åìJyâ¹'äÚ…³%ÀR¥$@äÖPަc+r w�Á=ÅhRÑ 9ÙÑCDzÃŽÑ凯ÀáåÃ0ÖÚÁpË”ey?h`Þ»È O©Äx=NÞ9r:×Þ€£ïù~àŽ€™È¡°ñ˜Üå?c(€þŸ¯ÁþÿG–±§zÚcu4U¤LY`ýá`Μå)žzê1ÜõÐ#ËR›¦É+ÃÍ­Ÿ!Æ~éñ2“hhxSЈMÃ7¾ßlj(Œ…븇=Ï‹µÖá…Þš“9º�G-7ðüy­UŸ¨ï±ýÒé—ÙC�Þ»¡�î˜ N` DDT‚ÈÂZªïwKë´bûÐÚy‹7¿ÿu?øÎú¡"ÍÞY–¥¯”Úu¡ðQ­EÿäKØxäA2‡k-*aÁ?ý÷?àÕ•lš¦§ÓÁöøæªª9�††×�Ø44üñWýÐÛ+­„ ö.ZéÀX3òÂsàt°ð®ï‰øŠ+ƦT³ø4Ûsv³Ú ö˜y^ºâ1ƒsg°ñüSXâ!tã(5¢ ÂÙµ³µËs©õÙõÓ§ÿ  Ÿ«„æ%444bÓÐðzœw7Ö~ Þ@X›¸z^½˜ã£óŽï„ðúÎaí¥|Ȧ4ûÒÙì÷ì �±Ó 9µ,±ñêKè¿ú2Š“/b¡Af6έb°µi}æ0_¨RÊû_xøá¯zæa44¼ÁÑÐðVÂ'¿ûŸÉ˼g´^ ¢}‚;~„ä:Æ`V×€$… X °zòŽö:1OÝS3¹þŸì‰ØÃž”Îñˆ³1ȶú¬žÅKÿùOHžy"ÝDì9öú�ÃJ)œ9w ¥)‘è¬òÙ\ìô3Ïþà «î@CÖ¦²ihø²wÿÐÛ‡EþEË‹û?êò×/tw’„=Æh”RÖfª[dABDÜΖ¯¼®Â«„ .€×zâìR33j‘Iû=œ}ä~ _y·ð\ª,`¤”�,ÊÚS¯4‹3ñÙG¯¸ö–£Žz[é6+òŒò²@–'PZ‚ Üõ`•‚·qð¦Û0à:û–qèŠkàG1Ú û‹q *㢎’0ÆÔ�¹ÿÏQçéãÕViä[›H{ëè?ÿ4T¿”CD�‡ Ãy’ÀŒ. zA“/œÈ £ÎÞ}çýæúÊ©Ÿ8¡Š o|±ih¸Ex‡aí—YØOeŒ/q.;¿ ¢ŽëŒÜ±Çq 0Zõ•I î¸`‚ÃkÍ�y^%FGpðÆ[ë*Ä "t*á¹êØÕX:z9ŒàuÅäD1`â ãn dƒÍºçbµÆàÅçà¨N–€T#%ò4G2Ø‚Q ®p�‘!ªð\,Ø'ðñäS�oýñŸüί�|ùç¸ãÝqB �Ø44¼iMU­ÖÇ|€÷"¢[cä…!ã˜;Ž#@à4650ÚÔ~mŒqpG€ƒ”œs ؉ÈjᇸþúÛpüºã`Ž[ÛðXÇA8¿°›«eý�$œZÀòÍ>d:ŒE·ÓA·ÊÀsÜúçæE¥d}¬àà ð8Œ¤”åsÏ?û¿üëßýÞÆ©S¿õ„*ÖÐÐðæE#6 ·8~Ö~0€·p;[ä‚ÇĹ/„¼À÷|?ðç¶,r‘5ÆX£4çŽà®ëbm dQÔâÀ¯Cßî–wB·Ý… †0ŒQ'ÿ•ÒtÂnuâº~5îy^-6 „vÜ®cÏÑ8 œÁ�C�à*)Òd}k£ÿÊ‹Ïß÷ô3O=ÿâ‰ÇþLZóôEjÐÐðæK#6 ·µçHgy Ö¶¼€«ÄsÇ9ÀµÆÌÔ‚5D‚1渞1±ó�1F0Üq]Ö�ßG‡�*e-R–µ�DA¥Um„j�A»Ý© G‰zý�Z°Úµ³µ#!H,ì[ÒàôÂË+/oŸ<õê+§O¯Ç"Z¬–Ï„ñÜ\àzËÓÔ•yA¾ÐÒþå@…çy1³i‘™È�ôÑå#é…Í�òéž2e‘gao¸Bl¾òʃI’>ÖëmÜ18}æŸúi¯u泇¶ú o;bÓÐÐpk3«”€ÁÚhT` ˆQ ËÒ( ä2ί6F+bÌq]/*‹b¹¡—wžÏ’íMmÔ¡‘oÜ€Ö˜Œ�cŽS>ž &¹}®KÇã6;ÞêÐÎÚ¥�á„[ýˆ×Ë �ãnèï¼vEï_ÐÔäÝÝÖæu8© Â@0¶(ÁýÈçäs%â9¹X4{ ž s9³b:mH»Ux|=S!Cû·MH‡¨çc„ó&ù¤•úx€³a˜ &‡6h�&(BbcYDŠ¢¨h4Mj.ó“¦:rŠxü9"˜_©™”E2sI,“N’ÖºžÃžÈCd2:‚qA :@¢ž[•9–tÛFH‡hG„ÓOô%Òù‡:ÏD9ÄérÃ?ð÷}_Ãçð<WÂ)p  ûÃF0ÆN‡›¢(*��IQ똂úÇúDWÍ|@ æ$S¡FcæC2ÔK‚ã8Ðo'�Õ1äòoÓm"?šÌJ™Œ4x³&C…´š4,ôò÷u¢tF\ë4üòùÏð9¼î"žÇà>¸6‡Ù 2Õ(GQj£¢QOø9€å•ÿÑÔ9%’ùŠºÛ¦ÊÌâ¤Ê¶¡0~S“�"ê�ËxÎ�Hð¬bÑß2—“N6ÛÅ&)@áTh&èG8½ü½ÝØ¢ D>=ÐEÔóOÆW‘j¤ó*<ÃUp8¬ ³ADG/EQT4*˜ä`6>Ø�ú-¼ê�tÓOÔdúh�n¯`�l68†(æ&pŠýö0d˜Ct#�×£{Íá¬H„ƒ,}¢2�èÌ›2¯N:éC:ýPácÊ|Ü'Õ¡…Þuu�à ¸.„-` ˆAdhÙ(Š¢¢ÑZL+¬¡(Î+ùG�Ì{HæWé(››(†–å`àÊææàþ�`ÆU3¶#$¯y ­Í¿áдà�th™ööd®‡¶é -3ïS‘Hg’X̧&50DšGBˆ÷ï¨K©Á;N8/³p'ì­u¢QEE£¸ ˜v€�Ì3¬ð*ÿ·Ù‘Ìb¤É6Éå‚S‹Å€ZŒ-º?"Œ'ÑŒeá˜<ÈÒ¿¤Kî.äCA媖ïx×D€t:êÚ¸Í4 ‹¶Åb}YdCtó{Œ&dó1¼í„ó4\ KBJE£( ¢QÁ„$3ìwEÙ°ŒdÞgåjýkf22µ0<)Ô£“A0¶d3ž;ÖL˜Hšíf„zb±h»$Â!¢ f©Îé”N'ÑÞŸ¤Õ~ŽU÷º}¯Â£°7´LôõEQÑ(/¯¶ŽÔâ0D©;Ðaö.¯Þ¤~Ñ·ë`˜ÌîC0u‰ê<ø}°Ôq-3Ô¬� zÞ ÷þŸ5¥ˆXoâwd>‡ºN°©CiçF8­ÒýSÄb]<_!›ß¤~ãnop;¬ ù‰½MQT4Úºœ�™øà(x„Ãò5Y¾�d~ PÞC˲ïÒd5Sß`fíëÕ#++'–k‰Âè6 #"CšÁî@3€ý^éß]ÏPבԡa›Y‚Tâ\Õýk¾t­¥«kÿð<þïÁ nÓÀdŸ¨æmEE£’ùöУӮw189Bç'á'Ô¾Ÿ–šÌšH†ÕÍ+3¾eèhƌˀ±ˆ¨¯çÐn}bÙ”di"—ò æ€EH�Q{ .&J‰Þ ×ó;×8î�€øºí°»‘Ÿ“AÐudí ¡~#›§eÓt%^ΟD†²yà8ÌÝA´0QÈFQT4Š‹dò°\Ä'O³~ÿ#Ò?¿2'ÓÍðeYæMî±’ó4Y=â©qÍ�éÿ›ªÓÿÁ)Å¢�VÖ¢åzA¤‚,ÖÍE¹4@&ák³"›•0ݓι]ù‘b VÄ!_·×í}#�M%^Ææ�= ƒÌ óEÒ™fÜm ʧ3Zm…~Ƶ>¯ùßö¬(*�fZR©,ï–�Ë8ñ^B2Ÿçé bâ¿—ƒÚ;�%•¶myìêM˜zë<®Îb£�3 íÈVDµ’  ‰ŒB&y 2 2ƒÈ"°?‹Œld3ïç‚9a^�k­�l¤&õP�º�ð0ÈrODg6à÷fCb-Õÿ¾A6¢ÄNW·y N�FtÛ³¢¨h”ùšZ¤V0 œ#Ë$cÔ8„;ÛiÙeã2s%5$S³•¹¶dêù�xLWpøs߉"E£(*ÅýkàåôÖ:£�̲r…H ¼’¹„4ÐЭË3®%S™k©0Ób¤~r>©1[À'±r!få‘þKb�É%ãäR„VþÉ�!Lj5N..ŠYŒë®@GZ˜iá6W³íá:jG íÐû#ÃEÝcå1x<¶nw{êacÈŽXÑ(ŠŠF§þy)=/µƒ’Ì~pöQÀöÖÌf}sÓQ‡4ê° Ϫ¸Z R1’Cv2·bkv¨ëe³6ʘId�D¬XGâ¡ô]`�s6z¡P? —¹]zlYÄ"õ˜�E}ÛpSµíÙ”j?vÇЩ4iH؇ÇOd#Rôy\}nYç7p 4AlÄÉFQT4ÚÊŒXšy¥¿7…ñW©et¶ fB mÈÚ3)uKÆÕWIÅG*¾Ì¦ìËVå-\A~9 Æü®vBë°¤¡lt$!qH;Áˆ\fâwfùýù@¢‹åœX¶G^‡Ó�FJj=¶ÅY"¨Ò ¶êZ¢]ûv $“»€’’´µšH5ªùž„Fâm£EE£¢)¬Îa|{˾ã0î§SÊ—"öÃc¡ÕØÝFê;æn EØÜ7»[1##ö¼Ò·3(¤ê Q…A*Fd07Øz‰c˜ÎuŽQë°"I@<$ù<H3ào²©°…]´"ÒB^v'Û‰D,)qøËã²²Cz5۶ǤFÕ%É­¦i´ðy¼ÕtJg9œ í#©)@QT4*™òÔ*®§nð9zϪÔd˜515§ýa¨¡IwŸ;eïR^ætDr4³%¤½ mÃV(ëg³¶•©…»¼D&F˜UÄbv÷ž›ªÙô©'+“$$IHCø›ìÏÒ ` ñÇ;©ð˜ì| Ò Æ`³´ƒÅ�VhÔ–Ì$.ª‰º¨^‡Õ!3"¢EQÑh]†Ãz&"™39¸ßãÿ‡Cß;!ÜSS2C§‰JÀïJ‹±�AáÞ.’ž’èÄFóÛ"»‹R³‡$â0òù ?7? "+$®cv Í]eY(¸2I“­çÚ‘ï"’zJÃè82<*éIÒ•>’ìwµšà\˜t‚�jEE£Û˜9”g$µt$’y�Cùwñ2ó'þMnú½ÑH„À�ÎlA'—†lMÊ0¤Ëœzï„20«b–:½ìÌÊfÈŠHlTÂ}þíµ�ŒHe€<ð5›Nc Ó^�œD16eW·Û£ƒz๒š�áï1®­'R½{çs°$'TÑ(ŠŠF%§x> ô´/?O½ãGø^ Ôi%Sƒ50êrÃ3›Cb³+�®V" '%ÂY†Wò¤Í¬ $…¶%…x™¢—ƒ÷4ÒlR�?ŽâZR¤'�g¯9©«Ë„3Êu’ÍâVȬÉõ¤Þsé?¢+÷‡ŸhJÀó-ò4Ò�&s5’BC6_ÁîÒn>An P�¦ËHÓLÇ¡v8‡Û“¼šþ†¼“È¡L‡™o'ÞëŒfê:vÏ—DHÌŠ 뺾ˆ4D˜]�Tˆ8d‰¥9øÜ¾;ÙODcSoó»ôõ{�<¸H yÙkZ¹ìEÔC-È.ʤþ"� ~ ÆÝ­ÆüzÒÕFg�í@CÊ6…æv¡ÝË�¹&´4EQѨdb9$CýâÄvÒeÓ „ð'X½HÆ#•G}‡,×�ÃÞP�±b˜Ò1ShÊž”™™dÕ‹0Ìæ:É@~&Ü0ÀÌ0µ»â‘HÀî0£$Óú¶6¥,ÃáÍ„) ¶ù)¢?‰lú�Ìnáæ$L­FQT4º,“È¢‰!Ì=8¬ßdÉãæ=H¦›¢teð+ÿz\DcÎB4 R‚6^™ˆ\�…Ap†˜‹NÀ SÊÇ�n¹åÌ!ÁÌ%Br×XÓu��ê"º·l»p©Fjo8#[6ÎåLóÿ–nzð7ÑÍf}þßåô¾5Š¢¢öð/ÁA>'µŒ»‘̯áû©�ô3ÏQ~¨É„¨�Ìý"j#tS D0 DÚôÐJÌÇHú¬qÐìKܵ%gA¾Ç–èA-å†)©ßÔn‚ë�\ÉeB‡çßÞ8�¿ÙGº~  ß"š«ù·(ä‡û9EE£¢É²Ók-¢™·èþê¢ _9©X¬ oÁøuˆÆ~þ€‹hè®Df$ƒtL»ÔmBûÈ„È×Úܾ1 úV.»;¹¹ šyY”Üj6<ËóãÕXoÏ�ÌÕœËBãp–�¢¨hT4ñÝØïõ5~?íà "½‡†Žd,ÿV2ƒ.¢ÑLï¦÷[ar(ºT™�w�ð5ùžmGæ®�ÁNÈ…�ö&cwIÍ%$—‘̃®‰BîÊI�“��j¾q«i.‚U  �hŠ¢¢–H}†úqå¡_!ÂðX–éÑ%6F÷Þ b0ç}©Ç´„Ú›s�uˆlšÝî±…Ò†D/ ‰WÊ`ekkH.%`�tÏÙ¹š†ªl<ø«¼�Wžp²YRÃJ6Š¢¨h䑍D2WRdÿ áT˜cñØõe—WrÈyƒDSO‹³)¢ðhhðh;6"™¸#yhvmÎ ‡Ú ̤ÆìfäÐ}n&Z¤ötpc£mçžœçoÏÓ(fl&ŸtÒþ¾�¡Ð>ÜD£(ŠŠ&J:fzRgÿeï, £º¾5þH!nX0KÐ�á®Ü�º u3*P§fPwwRZ¼BÝ(Ŭ ÄfæÞý~g÷¾¬yYä5Ð Î]ë·îD˜Â$=ßlûö[4ä‘Ò P;qÌŠav¬8òþ¢#æbˆH€”˜ëûÅÜc½"¿)ðO‹ŠÒyŒ5ƒê.Ç$n™¨FírNÕÚV=H@�kí5¨WÏ©U³æ~–oÁ[¯±XŽ0¬Ð¨qfŠípØç݇¨Æ˜]:ö�ÍÀy‘aðÒ…ðkÇC©j­ÅëW�Ç‘´yÇ"@5©ã´lÞ\ú÷ïïòs½²!B§àX,+45áJRX_Ùä#4>†5ýàšp@Sñ ùïOLtð,3QK©¿Y4ðŽ\‹ýÔ´£{^Ž]‘qÊ ŸË¢A³“G]˜9Ò<"N©C4S“Ç5¢¢HŸ%Hm^úÉÉÒ²e ÉÊÊ’®]»±y ¦@ÃÃiWc±X¬ÐÔ…{ȱ¬ g6(Á¾Å‡#€c’èÁIwX€š‚ËT»Nï3Ñ_:åOšN×\…Ȱ—æX˜ÒcŽa�FÔ==Æñ #hÁ[+ó8"Rrˆûµo/zõ’¾½{K·®]¥KçN’��-Ò©S'#6¯Ã…Р©ØX,+4æŠúÛDóDf)­"Þ-Óñå7k“sлËÿÍVÊS0Ç4{öi›ÖZLLPº kjeEÆF3¹ºmT£}ýp?P¡nå L[^Ïn1±2%5Uî™>]–¿ô’üôí·òÍ×_˯¿.çŸwž "PbÒf jÊæ@g>Þø{—ŠZĤy†˜‰Þt¿ùø8ê ԬȔq[XFàÍp&µŘ%onKH¯îf×êúõå¦AƒdÙ�wÉ–/¿”’Ý»ÅñûÅuÙ»w¯|óÍ7r÷Ýw˘1cJÅ&''Çágý2L€Z‡ÒÍb±X¡©†¥Ëc=ÃVÊ=8*—°”Ìy„wÓÚùµÀÛyOºG߉·ôœ•S=û˜qQQºüì ÿî±$4¹àÕb\j1®i-§ãêÚjîmÔìKýå’þdþí·Ë†O?•â¼×uK)))‘Í›7Ë›o¾)“'O–nݺ©Øð³^ÏzbÐ]Ÿ-‹ïŠãóu I®e†&Ÿ”–÷cÓm¦ëšs÷óà÷º£t_ 5M÷¤�§iMæJ0ÝS¤Ö‚œ•�]t—f÷ ö혦‰Ž^ÓJ£˜p7‡×jJÇŽòäÕW˦`Ç ””” ŒÀÿ¾\qøZBôi5ÙtîÜY:òü¼—ÂÐîPlè´X,VhB 1s-ÏÒv¼‰®°ìûÆIÙÅEÜŸE ì‡Ð,„yÞPa¯®Ðžûd/Šá9uk&KË´Ûìõcx 3ˆbtÓ§iïf�Õ¬®F`4Š‘ö¼F5’ë§M“OˆNòˆR>_©¸(å_|Ù‘-[¶ÈC=$C† Ѩ¦]»vÆEà˜16}f±X¡9ØÑL8ô¦åø=57“â*d}²ß¤¶HÝs/º¤ì튷8ëÍË�œ }3Ýy̰¦Üý½•Ëæë'á0ýLú–Ñö­5ª«½Ž²à(¦¯}פ2�(äÙ[o•?üQJ L$0ðÏ—F6k×®•Y³fI�=ŒØ¸^Ts ¤ìô™Åb±BS .‰dPÓ õ”B†4½9è8ø4ÝEšËÔöËÎ~"BÕ΋fÌÝ´7³@ï¼]Ó‰F-âXkm.M-â`-,•“à(&�×=+!QF¤·‘›Ï:[>ÉÍ•=¤É(ô—‰böï*..–Å‹ËØ±cMÛ³š�àM ÑËÍb±X¡ ƒÎðmÍŸ!4›ØAS8=::`†(=é~’ŠNëÏ÷šúyKÍZ‘‹¶1ŸæùÒ‚–�™:Í;ÇŽ€F1¬^Ð×¢/ÿþö&Š�ÖÔa:ökÕJΛ:Ur_|Q6®^-Å……â*2p "SÕlÚ´QfΜ)Ý»w—*Uª|âE5s õ`E5‹Å M œ š/è:[ÏFͽÔRSSiãm¯¼Óë«H7ß§^e]‚ÒfFP(r×l4Z¢1@=Ír�¡(†õ.Ñž‹�»®Ó’Ý2m£cÜ® Ê$ºÉæÍ™#?1 ³—">Åþ2Q ü‹ËGmgÉ’%2~üx©Zµê*/ªyF@ôÁ¨ÕX,+4ua,Fh>§ýxݱ±ùÔ \#ê5º† ÿ`�)·^Ca_Óa‰Š Ï¡F™ ª+sðêfì¨vfÎ^7-ö?ƪ¢:ãõföð8iÕÂ�ô¨h'«N²;,;G®:w†¬xï=Ù¾i³øIs9i=¦/m ¸å–[$11q•Õ,†YPï`D5‹Å Mcx–¡AV_W`æ7¨H; )@£µ¡)Ã?Õg­Ç°~X¨ù¨À´„ >wb¤ŽGo¡ßåµpŒî=gHd¤Ó ÚGDºbâܬäºÒ»M[9yâDyîñÇeí¯¿Ja~>µ˜@iËòÁ¸üÔz–/_.ééé«BCC?æg¿ž�Î^ÙBc±X¬Ð4ò„f)&—«è û�¶æbSŸI÷álá5/šù§aJÄHë {–úqeñæãŽAÑ ïêµÍyþÑ'0.õ&‡Ù uªÆùÀíCt˜]=ÒÍŒ‹—ìú ¤SÓéÞ¦�Œ8P®½ò*YõÑG²;/OÅÊÔb*ýÒçÞ°aƒ\|ñžjÕª}ä Í»0b+[h,‹šd¸ ^�Å~†×oSè8kïu…Ñ ³ãã+\Ÿá]¼vRõô¢—40¢ÕÒ‚„眣Èß,ü½54€“‚ÿê¸x‡Î:¼É"$+2J²’jJõ—œfÍ¥Kûj|yò 'È3O=%ëh9öyuƒxìkÏž=òÜsÏIrrò'¤Ï>ôÒg7@ÝÊNŸY,+4Ñ0n£ãì)Ü~B !=(òxXë3e„Ê›Ÿ¹a žŸ!…VÚ�ƒxÎûL4óÿ¸Ól!¹qqI+:÷&&9çÇÆ9#L«2i¬Ø8ÉJ¬!Ù ˆ`Z¶”nLãÆUù¸)SäNŠý«>ùDvïÚ%�@@…%˜CpiSÀ_|! ø=""¬þ^‚þY™M‹Å MÔ†N£##OgeóϤÏThÚy“ü/jäñÏ"j¢9ƒh%ÃK“5÷hiðæfÎåë¯èsþ¿ŠXLA_KŸÄ–ÇìÖ1†£çÄĺ㣢¥��cb—$ɪ[ϤÇ4‚é‘‘)CI‘�yÊ©òȼyò9Ö1;þúK|Ÿì?hé³­[·ÊUW]µ»Q£F?†……}Õ¤BXe �Åb±›¡NKòàS¢£×™éôtoö…é~�²vöåˆ� È©ÔtÚ{&š´Kôq: þÍh§¼éóés�3ÂÅtŒM‹Šv‡RÌïMZ¬+ä@µ—Ì:ɒݸ±d§6“ÎiiL†Œ8H.š1C^xæùûþ<#0ÅÅžÀ”áÐ_:À¹`Á‡¨fSTTÔç^Tó:L�ØJL¡Y,+4zUAdÐʼ¡� �¶«(¼[q¡Ñº Ï£ÑP cÖÔçNÛ´¶6Ÿó¿;ÍÜR³°,„·¼ˆÅË- M2‰ˆŽTŸÛkþÎáÕ‰È"$32Z2ˆ^:2½o:Ç:§¤JNËVÒ™²¾�»Èø#äº+¯”…ï¼#ëÖ¬•:É�ìwAe.†K87nÜè^wÝuy 4øž¨æSo€ónhU+Mh,‹„¡:"s<­ÈIy9žeŒ<œâª€8¨Ð`o�XI}oŸ=w­Ïðür¿'\ewá&�Ñ&³½'u“ÆA£®náác¸‰Ì \:"0ÙLf õ§z $Ët�½ô¤¸ß''G†÷ï/gœx¢Ì½ûY¹t©l\÷§ LÀï+—2¨À#‡öÒÞG}T8pàÀ?‰j¾ð^…žP½Ò„Æb±Ø5¬H e6›Èc+Bã§3ÌÅa¹³ò…浿Sgnð&Í f†8õë¹åÍãü¾‹È¹¯",fý�‰Ö¨I ]vÂÊjíŽÓn;H«JÓŽ5ɉ^ºÔ©+Ý6–žÔ]†0\9�ºËlµ¼þŠËåѹsåüÈVÿò«ìÚ±C-c>PÊ‹ãá*‡Sp¼Z�ïꫯΣVó‹W«É…ã!®Ò„Æb±Ø5l¾lÂB²—Y°Ã íÉ:høÎ~ ‡¸ÜI  ÔhtXónž�ƒþ` Œ»/u&‡ùçz ø&âî �æü�¸ ,iÜÛRséŸ ¤ÅºµôIK—á]ºÊq#GÉ¥\νãYøöÛòÛ+ÿ\·N¶mÛ&ù{öh‹²³�(¥lô"ú¸|õ•ŸŸï>ûì³8:oò¢š%üN\IPÅ Ke`…&Œ=4�` KÏv1ïÅ&ǧ½4š\ ZwÛG"0ažÐ ^f€{W¸ŽG ¨¹H‹ùïILôÑ çC\ݨµ˜f‡¶¦åqI7âBäÒ–È%›‚~÷-dX÷2eÐ`¹ðÔÓä¬Zž£˜ÿö[o™Â¹,Å'ì÷ß—¢¢"mM.ï ŽJÊ�R\�Žf*C˜ôß‚«sq¿~ýòjÔ¨ñ#n&}v;$Cˆ=,–ÊÀ M$u”á¬p^…ÐìáÖwýt�í×zeÒlº$�ô› LUˆ#²¡¥Yš‰f*} >ðßu @4壻ø©5Š™k)&-V<5*º¤õ&ƒb~h_=’;EýèXÉaÖ¥[ƒF24§“œ:i²ÜtÍLyö‰'äµ—_–'¸Ïºá9묳dKÇ&Mš$ÓI—ÝsÏ=f²¾ùñç=qrœÀ¿ý{èÊço¾ù¦dôèÑù4¬&}ö‘çѪكÁb© ¬ÐD³]s Bó5µ•‚öÍy¶3šùð ÑŒƬªM44&m6©´©`ÿÓ^ Aè>‘ЉŽŒ>w­àÍVxnLì^ı`:Â2.2*ðZ�{!(�±‰ˆ’Ì(L>ã¤Sý†Ò‹±~imdRŸ~röqÇÉM×]'O#*/>ÿ‚ÌcÖåòË/—ãøüðáÃ¥oß¾Ò³gOéͰåþó™ÂÀå“O>);¨Åú«TP*³¦£‘ÙêÕ«Kø·4iÒd®ÎÆÿì H…p{0X,•…šéõBC¿#)4©¥‹YtözÅ#�f.áÏ�vÓ(& ‘Iò¢™ÙD3o—ïÐÜ�Â7xXò Ry,Ns/D/—î0‡:‹Y}ìæ©d@{Cu@ðÚ3HÙ>>^(»4o.ý3³dʨÑrùùçË<¢’—°`y3ËÙ³gkÔ2nÜ84h�ŠJ·nÝJéÕ«—ŠÌI'�$�>ú¨üùçŸz8¾«ÒD¦T´Ø¾8á„ hXãE4OC³J‹Åb…†úÌ4ºÃ¾GhŠ—AM÷�ŠoÔTwgœ\Sø�F` <6ŸÓUÅ™\XˆR€hÊ! æRÀ×aIæoÜ‘<õ�Çi é 5>߯yò=mÍnf‰X6¯¾-ZÊ”¡CåÊK/•›oºI®¹æ9÷Üs5:6l˜F-¬7ý˜AF!�¤ßûÈ#�ÈgŸ}¦‘Ì!)Ø»å7 Tò¥âyâ‰'î©S§ÎO^�æÙÊŒh,‹š(ºÎ&!6ßµBhhóu®Þ�ÕÍ 'f·+EÚÌ�þ»6c†>]O°Ü²ß�˜¸¯s7Ó÷�gp;fP2@§[¯´ŽÑÝk.®Ï*.ž_ZšnìÄÞÆ Ÿoë Kc{ƒ°t"zêÎLÌþ#ê×—Ñ©©2&3SÆ‘Œ5J¬Â†I”.]ºH°¸ôgf̘1ræ™gÊt˜½Ç~ÒJ¦3K»ÊqÄâá(Óxst“'OÞ÷µçæü4©,¡±X,v`3’èc�I�ä 4³âãÝŠ®næṵ̂£éæ $‡†:ÑUB¤BƒðnJHð•H{¹8» HLÝ«1Wã² ¯0—µÆN7þLVµp?Ïå´ñ¤[AkH3Bޏ >™Ô}º".ƒˆ–Æ!(Ç5i"gÐ1vazº\‡ýˬ¬,™Þºµ äs½;t�^ )0­· 2 ¢i²!C†˜CVÎ'¥vÿý÷Ëûï¿/¿²æ/,c°iQ�9ôi1G§”ƒ9g£ÏùË/¿˜ô mòû°†Æ•!4‹Å MhÈ`å]D!Ü,ìÌaO-¤ÂBóêß¶3¢ _ƒÐ0'¡I¤!Ñrºó\ÔQ”ž‰‰z:é9âl:í ̱´¢Õ¸5¤ø>�o1•D.½©¹Œâ¿w¾b3qD¾©_?¹š× ¢”ãŽÉD&“‘qÜ{µo/ý‰Xöé#ˆT¨¿hªlìØ±rüñÇ«°Ü~ûíòÚk¯É矮�d»víÒÅ`‡ërÝÒn2ç  Lp×Ù| ÙÙÙkØOó¿Ëà.¨¡ö`°Xþ-Vh¡;­È iIÞAê«ØD"óˆ>*ÚŽlŒ'Ù éÃ8³„¨È‰ “ÚFÄ¡ ¢’R-ÜM…æÐ²j5·s+Í!…(%qiV5\šé稳°»%½VmÉhØP²6’÷¡˜?«ý³±z¹„ˆälå8îã�¡D$–ž�;KèÙµ«ôæëž1#GÊLê1÷Ý{¯<ðÀÚ®üÆoËùù矵.AÍEçH4j9Ì—{˜9wîÜiZ¶ÝÔÔÔßY§öï»ï>�X¾ÿþ{Ù²e‹XQÙ¿®6mxX¿~½ÜÍ›€V­Z•„‡‡— 4EüÂb¨QYëœ-‹š8Ñ ð)mÅ›hA˜}ømm¾‘5ê3�HZšÃÔŒ S¬ “ø'Lž$‹æÏ—<"Œ€·ô 8ä|Rˆ8P71y-ÌA××òeË EFD”…ð­Æ~ø¡|÷Ýwfz]…enÉ”û_ ·£‚¼wï^ãm&·Ür‹ËLQQãÆ�ý¼9@hL4³ –BTe �Åb±B £á­p„†y•-ìÂè�e­gŒ] ó3�jUª¸¡ kV§yHß>ìhY(»èhBdöe—_6•£K¸˜[Ñ´‡¡Öøø_G+6Mæ¨0#К^|饗ˆ$ÏôgeeýEKsIÒ• ¼Y ãÌÔg~†¥•¹ÆÙb±X¡©Yð Bó ;i¶œígbß­ˆ¥?V1fÑ™S34Ô_�h&Œù™„عmÖ,Ù¶e³ŠK)‡z?¾­Ãh3‹ÍäÓO?U_7R•.Ýyù;vÜéÕ¾tˆ5##ÃMNN6®Ëá}x·²„Æb±X¡ �Fp ÉJæ`6°rÙÇ^™ µ7¿‚ M‹ŠòÕ +Ž >Y=\ZÐ5¶hÁ|) *9ä ½lzÌ‹Kdûömšvœ3çv™:uªôéÓÇD/ÛÛµk·‘1-áÚ±wÁ(ì£YG ͸,�eð¶=,–ÊÂZÐÄÃt†6áâüG…ákãã÷éu¶ˆ®³²Ãš#"#‹“«UóEã¤GÇY—N9òó+¼›>t"c¯Ò"?µ/†R�ÀÌa‡ÎDÓî¶lÙrEÿ]D.%tï©ß¦¢º[‡¦ m `ߎ›––¶+::z-¿?¯0ß Ke`…&2‰h^¨úÎÈElÄt^ÒôY©°¸‹¨Û �ùF˜ÆZÆ—D3@B“˜/£i=þ•¶Y[[9¤­ÊÔ`vË·´�Ï�û Ú¦™ÆE\Š“’’v%&&J½zõ„HF;øh WÛÒj*N—\r‰¹"4Ò´iS?;iL½f;ü¯ÚÃÁbù·X¡©õY½|cbHÈ· ^îe�™Y,æÌÚñ¿‚…Æ|mNBBß²@<Ö3´5»uøüŤa6nØpðë1¶È¯Qã_;þR�Áb‡ ÿSM ÌÍÌÌ,©_¿þDFŒÈÔæçÒ�9%\©5�F÷^iÄÉ¥5Ü„Ú�°*@Xél8�Øü«à¸Î‹š#6Ñk¦Æ†„|Äàe^Äô-¿$4à=Ö¯áÂì`ŠéÄ¡‰Œp›6i,�=ò°»kWÞÁ{!Åj­Ã *sE÷ KËH‡õñ#&»Û·oŸo]é(îº�´˜\vÙejZNk¸¶<_xá…fvI]Ú¶më²ÚywõêÕ÷j6ïÀ­ö °X+4Õ ‡…eï4¢ó ge¿Ù óª×}V*4Abó_;�ï!rLD“ÀæËìÌ wŲ¥.�•ì×e/Òcºo2]eÀŒ ]d.Eþ¼ôôôÝ&’ñöêèþ�Y³fé÷=øàƒêõö—Š»it“(¶>šJ£¾c|жòûñ| 7ÙÃÂb±Bs é³”êUª<ÊLÌ,ÿKÆEE™¥dNn9{ýM Ç,'k…ИMmRmãF�rýéG×ï+©ÄˆÆ^t‘©À¼ð rÑEéª ú&jñS‡ÙaŠü& 8p Y� KÛžcUõªU«t>i?�6ÕÑùâ‹/Öt[ݺuRpyÞœÍgö°°X¬Ð¨ØÔ¢!à&Œ5lñw�Ƹ8;ûhsvs3Mwxd¤ M#Z¢›7n˜2q‚ûÁ¢E.“ÿVh*é 8]Ävã�7ªMŒú¾!ED1»hSÖ];¬b–{Y‰ðÉ'Ÿ¨ß5�P´Á`öìÙB„$t¡™š�ÄÓ‰ÈPçn~O~;ЋÅb…&1 ß3 }–¶“n2ÿ}‰‰Î;ûhs6^hì¬1[1ÝÖMjTTIZjJ`Ôˆá®ÙË¿�n¦Jik¶—:$¼ýöÛ2nÜ8#0.u¿ç\m¹iÿ±Ç“¯1,%©,{MŸ‹��U¡ApŒM�ùeÙÛÃb9¬ÐÄ…°›&ªJ•¥ ÃÂ6™yšÙå¬tFh„ýþÂæLI¯VÍM�Š,iÓ,Õß�–Úçž«føšý›¨Æ_Š[jåÿæ›oú&Nœè§öâP¬×e†,µM™ú‹¾Þ•-ì+V¬Ðè ‘)…N4¡9`«W«¹Á‹šý‚«:tÇŠæíÚ nb”Ylöú¿¶¡1Qæ›BzM2š4ŸuhÞÌ×»gO÷D: ~ùåg­ ØëßMøC€+ŸvdQ��4n½õVí"»öÚkå•W^ÑH¦²¯íÛ·ËСCU`è>“Ö­[ ©:3g³Í³©yÙËþb…& Ò˜§y‰¨f}BHˆ��ÿî3ûX‚¶^0»ûqÀÀíU”™šRÜ«Gw»Ó*«†˜öª¸¸”ŠòEÅÅÚÆ¬îÕï¾û®øo"5iÚ�o¸áyï½÷´Ã÷Œ.7âDXt¾†4š6 p/f ÓoæÚCÃbÙ_¬ÐT�ƤÏGl6"6%4¸w¹,,#4 Ÿ{ º�ôZψˆâÆI‰¾Ìí'-Hн*¼ÂYq¸—3±ÿ9«ˆV´Eù”SNÑCÿ8v÷°¨L£j2êr}/�Ï¡£MÆCÛ©éBûÖ‹jæÙƒÃbÙ¬Ð蕌ÚÜiu.d¦Æ¹)!ASeåí£AˆØÖø’èRkÖ¤‰3‚ýþK—,a!Ù~ÌÒØÆ�À”`ãïã�7䪫®RQ!u¥‡;…Ý,:}útyòÉ'u“¨v”äëšk®1N¥fj—g³V H]¬ñ¦Å²¿X¡áªÁ:Z�÷ 4þkˆXÞ*_hÜ;ýmªU+NÀM ¹f §g�.iž ¼Û¶ bá°'¦cK?Ñ‹ø´tñ)s¼T P‘¡@çcV¯^­"s(®Å‹‡g\ŸÍÊg±qHŸm6ö4öà°Xö +4\±0�ˆæGšv¦bÿu\\@­hÊ+  p064©M›º�‘ÚQ¡ ÚAc¦tË¥Ër·þâ/¾øBÓ`çÒ©7œH�]Ý–Ùã 62dÈ™6mšF8¯¾úªn=”Mš‡2O£Í�XÑÍ€¨`sãÒ$ð¿+ŸÂ3öð°X¬ÐT®(j4Ù•I!!›;©\ïGhœ} �YùüB“�ÐÄÓ¥ÆvMc¬éÞÊšà<:¡ìš7H` iE.427ß|³¦Çð"Ó6b�`‚¢—™3gÊË/¿,_}õ•º,HsEeä.i¡Öf„F½ÓˆpÔ:%%åW­ÓØUËþ`…†+†”ÙÔø��UMXíkfinNHð“: ”ÚÏ”™§¹-!ÁaÀÓR‚Ð5¨ïΞu½»më–Ò͚Ǩ¸­QÞ½›”׿·Þz+Ÿº‡úˆ1£ÂB— £ó1Lä«…?u˜#¢s�ÈK#+þŽjo3zôh­Ùàƒ¶1h#çýö±X*„®DšÎg5ó÷ÌÑìí†ÐÜKÄòv9BC¤#88 Ó\¢ @µ°°@ríZÎå—^ìlÚ¸Aœ€ÿè›àM¡�²3/ àu� 6<î†õëw¯\±âËyóæ}Ïε�!Ðè�1©2-øŸ|òɺ„låÊ•¥ÑËòšé¿åöÛo7"¨BÃÀ¨ Í~_6ÀÏð®=@,– ak4\µŒ›Ø\�ÐaCS‚ÕŒïÝr„æ „æ\,JpÊ­êÔHLpO?égÍo?‹1× xbsˆL0eEFáßêæåí,úú믾]´h‘:'ŸtÒIz8#0*.ˆŒýÜŒ3ä™gžÑ9™]»vé¡~.÷@lp¨#©Ð ŒºF¡ñ™åhßÁ\{ˆX,ûÄ MÐ)ÌÐ<†±æ¦ú¡¡>j4~“:û¿"š«ˆh°¡‘˜�7<$ĉÅÑ9£C;÷Ñïw·lÞ(¿O'ðÿ.ªQñ¨üÛ\œM›6­ZõÉf¦÷7â|¬Þd¤ÃJ;Ǽe-®ãÀ¬Ã—?üðƒÐu¦s/ç@Ćåh:σ@–6`²©B›a©=D,–æX™phÇ£6«"ªTÉ�E4� ›x±fÍ€®uövxsÍa¸˜ôY”ÿ3³Ò9;££Ü5ç6ù|Õ'òûo¿ÊvÙWjÍÁu+UL¼+8ýUŠ«wÝhi„A÷òoܸÑOÝ¥+~?öý:sœ‹÷­i€¦™è(ÓôÖ1:ÍÏΗÃ]Yîþ:М )4Ö¨Ð`MSè™ln‡_áI{�X,¥X¡ Šb¡'\óͰ&QM1bã4Dh&FEù=¡qÊlÚp�“ó4�fs’ÂÂܨðp‰Â-žH§YJŠôíÝK¦N™,×Ó½ôôÓO©Y#ƒ‰Zdf‘—ËF¤!P~ÆH¿Ç�€8ààÎöJ6%&›7of+åzvÁ¬vIoù–,Y\Œ¨¨Yèž}Òb¹`Ó¯âBjLÓJŒ~ŽCYýÈÂÔèÿ0«#äZ»ÿ{q|lô¼O;Ï£Í͞Ь‡OìAb±X¡ ˜h'ÁÓ°¾€­¡ÿõ_EM 1Ýd§DG—°{Æ48žØÜ`ÞâkXÑ8mXP=4Tª™š�œ¨(f%H£† %;+K(RGÆÚ^#Šäêáõå—_ Ñ�®¦®&‘Ôôà/+2ˆ‚¦ãüz%ÅER�¿GöìÚ)Û·m‘uk×Èêß“5«7bÆ}µÞײ4 ±Ð;+«ùžßˆ´~Åô§`ïþWòéª�ÜeK?p_}õ•¢{î¹;ïÊ+¯Ø~Î9gçM™2eƒ”;(æ—F,J)̽è�™ù7jŠéþûï×ýüD>ÂÜÌ‘˜:ta£ìçE$¦�q�;wV¡©U«V‘'4[á[xÁ&KùK"“ càIø~†/á«Èˆˆ<Šú»I�í¥Å¹¸; ÇGGûpÐ�æ :�W‰pÞ #4—ÇŹ-©Õ„#4aL�#6‘*6)˜3v×wû=¥WÏžZ§ÅWsþÌ�hjɳ¾×CúÅ_”åË—Ë�?þh"  �Ñ6î;¶m•¼í[eÛ†?eýï¿ÊWŸ|$�̽_Î?÷™>uŠLš8o°q2Q3z”Œ5RFs>M÷×hóØ»�tG�YÀßc…î�æïâL4JaŒ s$**²æ®xÝcš;ãŒ3„MénçÈo‚ðÃÙÏ‹ô¡FtYYYjºÉ®š]ìª1Bó,·‡‰ÅR>ÇŠÈ4€s`l€¿`ü’ÞºÕžô- ›Ô«»»QBÂÎ&»R«VÍO§-+<Ü׋ehlÕôŸD”sm|¼onRRàYDç1V>OOHp[ÆÇK\d¤DV—pDÇD51fCcR’4KM•!ÿl–u!0ýôÝ?¹Þ{Ù­xu �Ý ‚`sä rÅ—Ë ×_/×_�Ü}×�òØÃÊK/<+K-”�—/‘¸WÆ�…€õ�œì,ÉèØKû4IMIÑhªAýúR¯^=·>ðØ­_¿ž•† ãße&ÝU<îÁ‚¢óXÅaQÿ/„EScg�u–v—½öÚk‚• )·<�ÄÞuø›ˆÎ4õI'šÖkÌkˆSÀž��ãö9¼~Œ‹ïªuà xß™=°· v3 êÕÛuæI'îšu½ï”ɓлet,nÛ¤Ia«ZµòS¢¢òšV­jDgo Dá)Ä  ØláíŒc­sŸºÉÒ¼Nm©M#@�ø8IŒ‹•Dîµj$I½ädê5Me sÍÕWÉM¬%¾àüó…•= ²ñDGÛ½Ç)ð5MG �ÁÔ=ú÷ëÇçú¹ƒ t§Lž$—\t�ïê«.÷>ÔEhÜì¬L· "S«VÍ@llL€ô] q4÷hâ¨ÅAôîIˆ EmõòòR` #(úß9r¤Lš4IN=õT¹ôÒKM�¢4å÷Ǩ•AAA…"ê:/óñÇkJí¹Ü˜¯ÑÓ¤ õç„{€‹Ú~—ŒÐ¼o‹eßí‘LM8Þ�Õ�Å %ºî"ˆÅóßq×üð½ûåÊå�ûn¿Í9aò¤À€îÝý9­[ûÒ’“‹ÓÉÇ·ˆ�/BxJRcbü©±±NÛ:µ¤cÃ’Þ°¾¤Ô­#M ¥A=i�\[j&%J|lŒ$ÄÅr�•ºÑ€~}å‰Ç•ï¾ýF>ût•ÌçÀ~ò‰'ˆ nÖt õ ¢—Þ9rD€ù�’iS§î%ÊG€valãž×½{·µˆÒ&R^K¦OŸöQÛ¶m×#{ywý'› bbbuêÔ1u…ƒ0ý–÷Ò¼ys�¢øïj �(EkHwÝu Ok§¢¢µ&öMZLEÂÙ¿aTý^J÷ÉÐTPÚðÿUlhäÐ:ëˆRõÍ‚Ÿß¥OŒÐÀ«ÇÀ¡a±X¡ º¢a<_Á.(Adü¼Û×ÓlÒR(�ç³Hk/éÖMÝo¾øÜ}å…çÝÙ3¯qO›6ÍÑ·¯Ó“w®ÍRÝö©Mݶ)M¤MjSi iMKkH3Ÿkž*m[6—ÖÍšJ}„(‰È&šÿN4õšˆ Òä•—^�­[6ËÎQÈßJf“6`iRPE¼ë߉KžéRûùçŸÙÚù‹,[¶Lmòi6B ~`=ô�înÉÍÍ•x@Î9ç5¢d‹y¬õ„HgX<»{…CQëDˆ™ü¬KV_1Þ¥ë߃(E…-€H½%¸Ešæ­ó‰ä8˜u9ÜÑ`ÏC$§¯;¯¹F€Ômv’B[lR³ÇÎáa±X¡ …vp,‡uPdD†ÖT‡À¤¢äÚ�Ú‹èâò±´Œ)RÂ=Ÿ‰õmÂïtg­\²Xp®{ñ9g¹G—Á}0ƒì”-�¨‰dµo+YÚI6�»P'éÛ³»L=\&ŽŠà¤hdaGçlºuí,O?ñ˜¬ÿcµv�ÑA¦�d¤– Edgyï 9ø5ª`ò^ƒ!ûLA-èöI„K[‹?ûì3MY!^&BQaZ²d‰ŠÂRéÑÿ�·¡‘AßõÏ™3G�Tœ‰˜´–s4\Ô£tÑ?Õà ñ6‹ÑV{]Œ/ý‡Åb…&jÀ9ð.|;ŒÈ„††ú©Yh4s+NÂþX+%… n€CRñüâ~ñ•# y²aÝZùúóÏdþ›¯Ë#ó�›gÏ’+/¿L.¾ð|¹ä äÊË.•{î¸]ÍK>[¹Tî¸ñzÉhßFâcUl¼ÈFÅæ‘‡�ß~ú^þÚ¶YŠ Œ�ËÏòâ µ"]ml¢ž#äÒè…H#£wÞyGn»í6Á)@S‚4hTE¶¿)7µ¨¡¯íÒ8@Q39»wïVágû§ i‡|¤.äwí½£øÀ°X¬ÐxW5è�Ãrø‘) µá£�ÙeS¢Œ"uôÍ×_é!�°A�€¢5ˆ2ðy�<Š‹ u†%�¨aàfÀMD7¬W¶oÝ,»vl—ü¼²qÝyæ‰G¤w÷.Ú%ÑP“ǽ{õ�‡çÞ'ß}õ9ϵ-P‘vÛ è»gfrt¦ãp " õ›ùóçk;6)8:²è˜;QÝp<Ö"£Ó@PaÑây5-Õ6£�ÔnXG““”šõkIã´¦’Ý#G#$Ò|‡­Vãù¡iZ“™#m¦¨_¿~±6ع‹å¨š8æÃÇð+ÿóïe¾¥„B¹v[Ý~Û­DÛ$,0à¹.ïG¿WEIÅHñ)ýŠ>—÷=k*Q‡yù…çp è)õéP‹#•­ }›{æÜ"ßýy!öÇòtRdúΞT”j ‘Ãá¾HÁi͈F5ž Æ[Ǭ ŒjãR�K£•¹sçÊigž&]þÓ]š¤§Hýf ¤F½šR/µ¾4jÝDšuh!©í›KN¿Î*¸Ô°ôϦ.4MA¹é¿¹I“&¦ÙÂ4œ,…[�ÎCÃb±B�áøÀšÕ!æÑPDg–dddȲ¥Keo~¾'勌[·€'<Á~`å _£f±]œ7—¹™nÒ¸QC‰§-ê×K–Þ=ºÊÝ·Îv×®ùým)sQ—Ñwö/½ô’F <65­ÕþKEAÓdt©©àP‹Ñ(ƒCWŇÆ}üúë¯WhƆZ”>ר)£¥CŸLÉÜEFŸ3^.~øJ™ùÂl¹=÷^™õúmró;wÊ�oÍ‘ M•fœ¤mפ+Khœh `æé �jnU±‰��ýéèž«±X¬Ð4‡9ð.,'šù5""b=Ê~3©s!ëÉ÷“ 3B�R‘ þÙoÌ)g_KÐåÙÇ+˜Ë/»L+±¡#-*’š i´.4\?óê_­C0�®i) Í x5Šüž1µ=hׯ_¯v:DZ¥]lb©¥NE.Œ:eÌñã¤ëðrüÌSä¡ÏŸ’?{Ræ,ºOî]ñ°Ìýäqåá/ž–G¾zFn]p·Œ9w‚F5æk‘ž®§¦¥bCëx‘×î<Ï2ŠbÄÞ÷ø ~…¯ácê[_Àç¼!\åe#Þ€‡á–#ñßb±BS΂yð*B³Ê¼ÃdÞ„VÔ¾òì³ÏJ>uO`”²Lù— e¾·‚ïþ—EMž*.óV=¡Bs÷Òy2vÆD}mèr;Üÿní¨ãçWš2dfé ]ŽfÅe–'�ÃóžØ¼IÍômjZ34ükNNΟ oeVl÷- ÿAæá{¢Ã\ïϽ¯Â}öÐ>°BSªC[˜W 4ïbÍâo‡Ð˜|ÅköثиeD†ë¿Ù;說­ ÿÅçó=ëÃÞA¤IQéÝ.M±wŠbï�*"½Ù¥#H©�Þ¤Cè5´ ¤ÝýÏïü{œqÆ H¹7ì=Æ’\n9gϽ֚k.¾fÉFtH½3ߨþP»v-S°à]xeA6ØÕ`“tÇyã~¬›Œô Åeú_htôd˹u1÷¥F½ZD3‰ÌH\à¡É—ÍÍÝŠ›¥ š»ÊòH§×Üï<ê1ëÓèÃg‰$"ÁþÕ©?|†úŒ ²'÷açy3Ö’E9Ut�:¯¹>ïÁêÙÚ.'óTÂxïüZ$ ªpÿîÙ³gªÈ{Ÿäã íãL‹Î(Ñ!·ÚÎ\(\#4&jOÖfŽeˆ6t¨R‡‚‘Œ‡¬^II‰4ebçBß…|Èn ¢A‰†g^nÓ[…X|Ê4Ñ‘zÓ(ísÌ� ±@ùš÷™ZÏ=`¦ü5ÛÌLYd¦Ÿœozõ1�h –+lî,u—)U½,u/­Öbp[Óä£fžH"Ñ&n+u&MÑ©}Åù*w†"’‰Ê(4•ˆ¥ºÞ#Hâð¦f!Ÿ™Î®Ðák¶M‰÷Îá×çàˆÆ_ � “¯½öš$<¾d(iÞQßóVØ5–lI¯ @@–ÜRÞg¸9#µF —G_õ<�›…ÂìèkÕªEÁÝ{®¹yaÐ‰Š b™tt–G4¿ÆÏ3�¿ÑÀ#š"÷ÝíMñÊ%=‚ùðû¦ÑÛÏâQï ‘'½N ŸŸê xëÍŸ‡$3@h£>£‡q¶2ÆìÍ„ˆñpž¢ DO 4kÑâ3ŠýJŸaÛŸ'ŦÎö Ku3µÌæúeDzlà4c2ã?[ì^ S"7znž|¶‘yè™GÍ£Më{}25ž©cÊ?t¿)\®¨)Z²¨—JüX2qÒ�¸HïÐO„y©dì·2–!Éúì-;"™6|U¥C÷îÝçéÚÉt…ž}9ÞrR«íŽ”¦X7�æï6}ö;Ñ ®Íô°0byôèѤ¥ìæŸýDÓ¦Mk¥WîD@�&QϲIV9C·øì3Oæ|½Òg’9Çë9¦ ÉÂ)tóœ²*}GôB(=(œ™ê‰Z i.};løvÜ4765ÒBYQ‘¡‡ˆ¡bD7|mÒ¤‰7¯gذa�b4(ï Bž7Âj‚ÍZa�0=7ÿu_��Èù|!~ŠóN”Š]¤'ßzë­ÔÛn»m«Èfƒ%›¹ÅF…×ÉÈ2f”ãþvÔû €º²Sªvß}æu]+¿Ë¹AÛBJ�.Pýæ9ý{ ‘ $�7Þ™D»rÀ[ŽTª7zbæÌ™¤å0q%‚ÜA­ÈmìŽhr’l.šÞuW�h.S½æY÷+¥f4¨ÙÙ0s†è†Ú��pQŽñ¢žŒ’ ?OŠN½sUØ®˜¨!lÞhi` &N8&lQ¯Ï~6¹Ì<•³a2#¦`©Â¦rƒjæ‹I=ÌoÉ ÍìÐÙЕï5CV¬[Å‹tÞîó�é5ç[ÓnÌ—žßXéåLÅj•¸©±çϵn|VL'mÑ¢RjÜÎjö ›è3Ï<“ é›ôyï!Zææ–â¿Ò¾=.\¸�‹¯Jûšçt ©§´Y¨gÌ€d|ì� Ä{Ï1�i8) Ï$Ú8Ëè’ÏÏ.¢À“’ýVšô ýœÉ�ÿýåMSŠ7>´îÕ`^P-¦µRZ)Š8‚D³G)È·õýE4¼Ç¸–Ÿ«Q+‘ÎСCq:§Yv¯<çˆ&'Éæ…üB÷bÅî6@ dCd##ÄòˆÖI³öW3>?4/ºI²ÑM’-P[±H†D|Ò±ðSnUlìõ¦LSåMS¢xñ=J£,°žf…EÂJa½=í€h¤VZKÁ>3ë4¤ÞQÿ$B—}óžïÑ�ï“ )4’!˜ü% x–/…î)bj=ÿ ´v$?ƒ‘%ÑDƒ: QÔG5AESóæÍ)âóÚ8“.ãóÌ ç3NòúX}¾`}´v¶kuæÍ›w”Ò±'LØâ}|Iˆæ� ÌQÕƒD³ä›oL½ŠMÍ H³ñþ¤y­’Škݺ5JD¾z3™6mÚ”î=±jÕ*¢IºÇ¦ò<Ýïˆ&»Œjjý‹+ºå–›å’|•‘šÉwG^©dª˜fMÓµ/WàïÍŠåKÍ5v�dõÐÁX͉?*“¿ã¾2Š¿ÐMòÅ{U¡ 4&f³n’ fã†õfѦ[·®Œ'IY¶Ý6dÆË…Y·ÂHK6;ì&´™2ÅRæÒgvçz͆µMÑû‹y•Ÿ jm&™I4Cm·d¢�pø9Òg’W¿ùôÇÖæž½ˆp‚d£ˆZrÁÂæÇäu@,þëâ½ÏĦPj H{SeKsHÑì~{ X¥÷Q?¥I~¾/-^¾|¹i(ùñ£mý’$ó úö5©ê¡>³WÑÌOŠùÞŠv6lØ`ÂEëׯ÷j.�D.�•¢}A=IÍD\$—î(y4B¬~è� Dü8?ã™Ç爲t‰syvD“Ó ´+„ZB¯2eJ'.TˆÆI š»‹ÑЪüæ&)Óòç˧ƻ‡MûvmÌ RÑJfŒBý)j›i¦N™¢Mj�N½½Mw‘IûvmÕ7ÐØ<þøc¦^ÝÇÍS�žTú1S®\ÙãVq´Oð"a¦ÐW¨ckGóí÷c-ál“\3„4³7׋3­äMuy‹µÒÎŒÜ>ÁŒ;0Íô�ÿ½y¬Y}S¬RI¢/zi;º“ç9övßM…Ç+#‰&·ŽºˆéŸÙѰ±Ð§‚c3‚Ò%È]èäÌÉ—c²€ªÉÄMjŒfÔçž{Ž~ " Ÿ`h´d³Êl‚g-X°uÔ�ÿÓT™e÷P7HFïÓŠði-Ùÿ›7Õ?SWïkM5F¿®ôØ®Ÿ6[e8:YïmGIœßhÔÈÌSÊ,xíЋ"Ùô®¯®’37T ²ZYfJ—6ÍD ¿´oo¾ûàÓ¦iS3Nå ë6„�Ô™^­õ9>¦ÔÚL²,g‚i·U˜7¤X«.— ø}^H�¥„; &èÁôø¸MÞM¶ƒ6³æ*¡¼ð†McM¹í¶[S©Ý@4H iò,ªHu(%ù`1©0  §H7f 2¥Æ9f;³y\᡹PÂþ¿ÚçÁúFØnOº»‡™Ÿ³víÚ,I�Ÿ9Á<üÜ£&o‘;Lžë®47滉¹/ˆ| H› ”(hJ•/M4@z$["^;y|Û+��Žsþ âaZ*cˆˆr�º¢ƒ`P‰ñoô]PŒÇè’€phøãñø}ß`5‹Qy.ü¿ëõÙ®´iÔÝÂïQrïô—$²”xǨŸœŽTq¨E¤X¾¼©®÷þ)Õl>ÔÁiŒ>3Þ‹`”JdÊÁ�Ï rzN�˜ DóLP²ÕÓãâd�ò1¸°½ù@d&¥_¢‰£sZtáàˆ&H8ÿ õmÀ’b¢'§ü"Å‹š‚Å ™r•ÕsS·Žù¸Í' `ccÈ–qÏ»víbóÁ¾ÄkÌã}˜;w.J· ËÉy®D]�Žd¸˜2R/!¥Æ‰šŸÉòTŠ5ŠØxÉ9©ˆ~ÐŽ„Øc“£à¾Áöžžÿ>Te­�¢ÜƒØ1%ÝþžSÀ§FÆA¢‚Uq~ Y’�fÂÑM×B~‡Ï3\DÀA·]ÃËUëœå6yG49‚S,àïÂ�Beá5áC‹�„–BÛ—^|þhÓ¦¯™&�_C8�úÆëM©©<#Óû¼! "Ra:¬5¥”ªþNæ©á²hÀ¿}¬Ô)DÃ!„QVX�Âáù€T~ÿÑóïâ6úH$G8AÒ� òW W ×X“Îë…ël ì ûsó;öw3²ÒzN³„]B¨ïâ�nÂdRÙ± ëº{N©$~Ÿh ËšÛåçv™\³/ÑØê«$%¿å†Ø\HAr§’SgŠ#Þ‰ù-Ñ!.ÓÅ‹‡T<‚±F®FüþvG¥@$ò� g­ªgÍq&ìØ±Ãdöb>53ÔbDӤΨç„G4ÔkÆèšyUµ6”h¤SÓ /"WêxÔiRD4kÂlÑ8h ¶â�E‚Dk)zGSW=›.)“‚Úho” â6‰#ÀMªgÝ ºVa�îI•PS9UtÉAmi+겟¥d¢€Œ2�“-õœÝ»ws‚†ÀüÞ¦³MïAº<æñãÇó mº?�Z�ÔÏ]wÝeT0Gl¨ÙŒÒ8Ôôèßú˒̨hH™‰$ÇJî½QŸi(+R�÷Kê C8�tu᥿VËaà¸"�Þ0x°é%¡@ã‡6/(ê~A2)U>ë4ÒÁôÒH¤S˜A‡;DêA¹ƒ#­vV¡–$„ì×%¸â²ÉECo ÅuN§ù4ø«hÞ¼¦–ê=mUtï.ijs¥’PúìNE9÷/NÝ"Ý™ÿ¼^F "£a��r Ž4`#G€ø3'c6.RVôÂðó¥™cóÅ_0+†¼>âDX—ïºè¢‹dl*�5%((llbˆ�5iðÜP ’βªÂëHkAR¤ôüßãqx-äüéÝÉh4‡ˆE§jNéüü?AÒ±&ª»„Ñ|ŽRÚoš ë±�lV©ò äçÔîxÏqfàPñ¢>תëQô{³ ho—ʳ¼>G1¤dQ B~iEÂ\W]©'‹h¶¦ýѸôÙ.á„%šd�Ø�}þùç&›é#TZ°QaÄÀ¦“æ&Û_N½¤Ì¼™?Š2*ë„:DÄ‚±"�áØ‘ŒÖi•Óé•:õ³! 8Ãå«Ê�É2�“Ôi´o¿ý–ǤVâÝâù@"D=_|1›©,6|¾H2 ÝB4D4ƒtĹöÿ ªâÎcsÂæÿçÿNµc! ó"±1“Bº¢Æ­²ÿeñHo¢KzhˆR}Q âÒj´pMq@�Ú³ fÚðÙ§u""AÝÇ皨÷z¯ÛWÑ8¤1{]X/ÄÚ�è¤6§xnD6ÖìXtw#EŽ˜Řœ Ê1N�^Í"¸(ØCFÖÄܪS¨Òftwû "¢Ò!wk¸Z)1"–¬Háq²EØÀiœ�‡”µ#Rqœ iô„¼™3ÃFG¡ÛÚšdös�œ!?HŒ~"(RkD5©Â~aj$]ƒ"ßÉJEnRD—L],«é.Ò•ÞÐ4E6•õÝ«CJ9M‘üùM!¡ ¢Ñbú»ê8~ê,�(ŸÔ(ïwª®CƦñÑ8X‹šX‹ãB¢ÒC)Hg³r‘Æ€`ˆPqzT“Äდ? MEJ)ý°©Ó;ÁF ‘”Qa¼¿êqê½@5´_NÆ_ê±ó«FSH5jémì�*¦–�Ñ—_~‰ 2ÑUTÏÁ¡1‘TÄM„eSiÞ¸gaX¤\J÷­R$˜H‡?‘YV/D!H’k¢Ø¡pm€;¤PÌ#ÕààRV©Èj6š�hˆ Ó¸¸9T$äË—/½Ù?Žh´ú ûýô™ Yìú3²hY�÷F9E=ƒÂ,Z¢¾úÑ 5NæDSxƒ"ã °‚)hÅ+NT!žUXÇéTÓãÑ)ŸqP GÉEQŸÿ“H‰<>5~Xßi€½6ô‘.‰–õý÷ß³QjÂk~?…§¾š£­F‚ÜYu™~z¯wSŸâ ‘Õ e=1ä PˆZ™j2åtÀ© K§jºî�<¤ïqÍTWOR5¾V­ÊuAs&jÄ´!Y$­;ÍkupDã õ— ôÔı�sÂÌäÔuŠé~!Eõ Òuô»P_¶l©(RR§}<àI‘B m¦à1©…0WüH$Q[išò*¨[œszé@„ô„BQ�îržª2þL …çÌI˜T§â-[¶ØºJä.¤×Di¤ÐP¦ÙZµ›%ršl,Öu–Â! ;l†8Lpp)£„ÒR�›óåø«4'µ="a¾"(! ‹oZ­ûî#¢&µ‹[Ex�†ë9Q @g?s:8¢q°ŽÎ)²‰W´‘œ™2S¢‰ �bófGùC3v6mnܳ"/Nª5™ò9ãW”ĉ’)£Ó*9w"&Rt�Wøâÿ¥�NmË�NIéQ¤gS ªÊ( ³i‰)°‰ãŒ ›:Ï%›;QÊáàË eRd³ËÚ ɉkNb…!rˆåóÏ®ñ¤Oùÿè‘!ͯϜz^zX*ÁG ‰¸ø=® Æ=X’ÁP5¤hl§Ò»Î½Ù�CFšå„ý¢IÔÆ´;“Šçlæ¸ S‹ñ;Ù±žÁ:1@f5oþ¦� ŽËŠ’0X¬¬ú ÿÿ7½'ôÓÐãÒO©5þŒ¼•“.ri ÅDQ�Fll,…uˆy3› ÑDƒª‹Ç$Íù�bƒtH£@z‘TÛ!ÝCÆsEþLí eNô}Ðc³Z˜#|ž�×›6ç™*´§`Fš]‹t-õ–O5Þ9F ÈätèÙ¼¹¯Rã3FöŽžçýÖ[o%è}œf ±Žh\OM¢%šT�|×!ã=ÇMï0dÀD0È@iv$Åæž%Å_:ùIqAd6 a“Ev Ùø –¨Ù ~†*°ô"þ²Dzýî»ï’F£_Æ'¤±ÿ'¤ƒª •� å tã=áyâ@?®|=hMU—g×L­Þjp¦Ï%áE6Õ¼ø|_½ÖŠn×+]‡ þtD3XŸ%Ñ Ÿ+ï! Bê5-[¶LÔµì™ifpð™ƒ#­‘@T³S3ì“ÎeF ½ä¶Ùèµ¹p’fc'š8•|šÍ™ïsÇcŠÔ ϨЀ¨O4äÙ!RwÁ'�œqíƒH#PÈ‹×$Ì-!V¤±ô¶@ØD:¨åNA<|�ÇÎ Ñ}?óúìŽLU´7M ‹Ôœ{Râ’ ÉP¯i£çù„¦or­HÇA…šb‚ˆúwK2“2øúÑ8h vˆæˆ¤±ûìu6 y4)'=5|¸¼bÔ¨Q¤§ÒÝÀ)²·oßå 0ê9ll”ª×�ÚÀ�7H&€M–¨ÃªÜxl~Å›Ð9É´0ØfIÔr~óî˜]ªèÍß! ßAX]ð'c£FETqÔˆüZ>Z™¸p;@¡ç�%iÉj˜dŽQÇ,¾¾ºè:¯Ï`«Ü»CÈdzٲˆT š^Tó‚Þ‹§uA€sÊÅ÷D(LÙD °Yòð®ªÓíÅ+÷Ýw_¼äâ+,ÉÌpÓ5Ñ8œáF ¬Íau·ogRã¤ÏH‡a>Iñ�“º‘ §@<¾h¶L³ŽÀFKÔA? ›3àÏD H�GŒqJ¡ÄF:ŽÑ›:¤¡ð½3œú©¥�âÀ_ /¯pÁM•È‘@jlî¤Á‚óXˆ,Øðy<'ž[¶Í›!…Gñ˜×‘ðºxN€:Ñ`ÓG°!Z’�0y¯2[êL4 Éòx� B é¹l‘g×È,¸¦úJt°’z$G}#'ׇ H—÷›ˆ™ à}á:´nA¤<þøãKóäÉóí9ΜqpDã 5UØ'ÄZVÊk¿6üÅðSmbÔ!H•Al¬lhüWd$¡ái ôø>©$6a¢ ~BÀ^†…è'­E?J*Ò 6 à÷Ø 6O6qþÎÏå¨ Œ´$Ëkåù°±E`ãOÇ>Dƒ8²ä´MM*«Ñ&ÄÎ{ÄìRš@ƒ·WHÏ‹!iý2ñzê'’Y® z�>ãu6ú^¼4ûr0á½÷S�2äš²Q×X²®å9npDã�‰ƒ°Eˆ (Ðj3Š,n`"NóÜÜÜôØÀàž|ÚFHždDaÖŽƒ†¤|ÒáßÛ¶mËó‹Äñ(i;Šá9â4@“+ *R‹7ÈzE5úkøš¢ÚêÌh ÉÌTô£ˆ2D3n¤M/eÜï‡#<\;¤É,É }OT¤é¤Ë™ G4vòæþ°95Û4-D* Ï'6yR JÙàI7pƒ¢ö"²¡@žÆÊÆJï õRkÜØÜÔDHpù½ŒÊ�IŸñ{~Ê�H�¨É, /·N_O"¥iSi¤Ñ‘ÍÕS–žã5ô³Bg‹È6j³q­ØfÙH$~œ¸®QHb9D��ëù¤Ôq™�JtŽh´¾6 …dK6 *dÇ}@V&J4ÁFÏi�þ:¥‰XÒº™ùÊ,ˆ…Í�ÚNÇD>áÄÄãpê§îƒ�(&Ý4�M”àÖ“ )=ĤÏ)PãJUZm»>óigyýÌ€d$0تzSˆy>4ÃFêâÚÃ=’á°Äe™z¾¦¸ý ‹àˆÆA«§°RØï‘�M¡é”›ˆbŠš)*`£TfDé_1w„ ˆ~ (ºñ‰Š°Ë }&&i– ÄÅï EF–Lñ6ó¥Ýbæ×P;²ª¿xý}ÕYŒœ˜-µÕ¤ËªV­"�JíY,ÐÞ½{‘z“îc´6µ'ÈØù3uÔìpÛ¦îGã-¿—aÇ ÔŽ~°â9ëZ;¨×ï¼Ë² Žh´:ó„5Â^!Ö"A‰hR)ÜCä¶|JãKn|¢Ý ú8 AW"ìÖQ˜qÓCHËÙüðçB"M:ƒ‡[™_'êPš‹¢ zŸ œ�ôˆœ�…ѯ"™eŠJ¡"…ºë,…¤Y‰¶8Ì ä9Aû®ÚDÇÔ}p� ‹‘ä‘Qï½¾2Ô¤6Èa±‚j};¹g…�ƒ#­ÞÂ/ÂHÆ"Vˆ·M’�‚Å<Ñ›'Ìô6N£ü‰\H“ALˆ È'øsÈ€É�“JCÀf§´ ž� é¢'NÆY¤s‹ú Q }?¼ÿ–pRU¯ÙÀÌÓÍúf(õ6O›õ>GäÙ�’2=µ)-ênÔ߸îypÀ±’cþŒ²¡i–h8£u¤ñ|xlˆ&¤çž™óþÑ8h  ›„íÂba†0S˜&L– »…¸€³sœÒ"ûq8N¯CÞŸ"=‘ Q §QN¦Ø¼£"!Èõ½/4uÚ Ž �¨òad€oÝâVÖI°Q"Ò€Ôí8j†Î¥H9¶GŸû„4®¡áÂa‘äË(â8ÊïcXšYõ2Ò`ÌE"‚áš@Šl‘qØ!¢!�FJõL†¤z}i|íÚµI—9ùrfÀ�ƒV{ágK […B¬ýó¯Âˆth�M¼%šDa½N¬GÙœÂ7ˆ(†â271'PG�O�ŒpI¦FÃ)i-sRèë@X@縟"m6ö\¸´ï!EET ù+]zÂöVÍÙ4Ùþ¬ÏΓ/£Ð"² Þ“ –þù'6=� 2×Ñ1×Õ™^#�õ>‡(ŽktµÛ2Žh\³TØi f—°Bøî4¶4† F; ¿¬lJäÃI»P«Áár±NÆœŽIg�„[ÅP çNñI-㘉|èÜGâìRd9Ø5/#Uê"D–DšÔÊBv*çzaP”3CŸÙŸRhQÁ?•±ÑD¨Ù@†ô¾PÓ;›³RÔe4Îr �T÷º½!“áˆÆAk”0ñL†]iM‚\RçÂïjdÛ ™HÂÊMKã[xhó�P‚›‚±#sþ©ÙPlÆl’ôÄÃæc’Ý¢^ÆIŸô™È„ƒÑæI}æ ý}™{TsK¦�Ú+Z"6œH™‰ ©Ïw{B„À�ƒVa‰•:‡,b„5:õ&‘7§�†°È‡´W0_yÐ@I#'‚ d!!ˆœú‹[ݺuC%èn¹åˆ†z ½5ë”J[­TÓѯ¿þ:Äà/¢˜hY›7o&e†Œ™ë/Yêµï#ï~spDãÜb¶4‡ a�zR‘œâ@a6Xð'mFÇ?æ’¤Æ @íB¢®ãVdI�ÚFê¢ÁáYÊ3|ã¨ÙVôz‚:QL-R¼D͈ˆ¤S•Þ]a÷˜ƒ#­–hNZ¢9nk7£Ô{0ަ7æþ™M3ñ.C1ÄÆeíù‘¤¢6³„äV¤yñyÙ!n¤6©Óðg¢ÖŸ3)Î([Œ%ຣ&“ªž¬­x�98¢qК.l  _wc…oÔǰ]§FšìPahé+„HU°QA6¤Î�+»qõ ¢K‚ˆ`øÌ|`=D£-S£mñºhî|øá‡C²–Y)]ï¼ÇÑ8ØQ¿[‚D�F¯–ɉù$µDú3ŽÊô>x›”=“Z‹À‚¿K—}öêÕ‹Ï�95¤Ì‚�›ˆ=°y‰J¥éSuD¢³ýj*ý)ï#G4ZŸÛÈe‰0<�ïÏŽ©Â1K4YÒIšm’¬&?sýõ×£Xò‡Iê8Œp+²L5éG¡fÉð91ê™òs ¨ ñ‹Â…œ™=é¿cƒ@P§º¿Ñ8h}g f�°ò65K…]Bl˜ -9ðg 8±�A±ÄIÐgYEd·˜D´Bz3Á Ø@¦Ž ˜)ÛEe4óã�?Bš‰RÌu;£Ìœ„#­‰–džn˜•%¤-ÂÙ·¤“$ “EÏSF‹øQy5·"G‰E .ôÌX’!åé ÖA¤Õ¢pñ¼é• I’½Õ¤ïÙæÙpDã Õ]øB²µ™½�Zr8Ñù`Ñ_ã™pºQ 3$éˆ4ü‚?u4ÛãÄP;¤éÑ*n@iFm&^éÜÙº&G×½æàˆÆ¹<�³µœÂaWxêL%ÉX—PX&ýYƒ¯ÜÂâgРAÔ͈bü ¥� ŸÄœ—(]xéѧEJ þ@¡C”ÜcŽh|CÅ@¦Òˆ&A)³C*À&à¾Ìœ·"¯K¹²­Í@6¤Ð \°Ð�j'êÉ“'›·Þz+U×à]�ý¢ì>spDãÆ<§Ó̹^ˆ<5šRfë”ë_΀3·"káÖ@4ƒ‹: DƒÊ ’Ae†�ñÙQº˜ƒC£0Í™  �ðhÆÁ�ƒM; �`Nñ3„MÂ!VØ-Œ“åÇÀÈô0sµæ‘6³"ê4 ‘ u�hN Ò«…q+žz»ýÑ G4Nòýõ×_©=!ÏNÔäÖÜ0ÀÁ�¤"ÄZ$ ´-[¶t2nEìBnNz‰)šÔeá`3­‹i®ÌÉiÙ²eHssˆªGç‚{ÌÁ�ƒÖŽÑ³õ™Rþ8Ms„×2¾úê+"fÏ`¡ïù™Eéb Ñ ’füôâdê:#Ü_Žh´[9s _#>4nEE­†QÌH�,X`G2GåÂu„™*õÜ"]ƒmrÁ=æàˆÆAk²%˜õÂ>K4ku³¿aÜŠª.úh^Œ€d† R*p�”fƒrÉ=æàˆÆAk¶pÀ"Öb¾ú\}Æ­l3Î\¸p¡×ÔºuëxM�éÑÌߊ½ë”p…#+iŽ Ã¥`Ú·ÜÊ«jMȳÒ¶BþzßGêýrM…Ï~´ã på}Ÿ:·‚ŒÀ�ƒÖŠÁþH>ÛÆ¸åV¯cÇŽQü÷$ÚŠd’4 à?¸QDâ½’·VÛñußê¿÷…φ$?ÿéàÄš¯~µîºJ-¿9Íï98¢qÐZÍþ­ïM5n¹•Å‹ÆLFT­Z5tõÕW£v\i÷È¥e?ì]²~ç¹MÚŒ<þî—¿˜—[óðÜ'ƒOT|¾—“_ç6¢ÑúBøNø^hî�é`M4…Ã#Íçô=7«Ù­¬v ˆÆLܦéÝZæÛ%En©ÞzèƒÍ¾‹Á¤výa¦éðí4ój«áÑ4k7*ù�ö?÷ÉEû�#­ÑÂa¹M÷,æ …Þgþ˜Zm„ýBHH¶D³SxTßÿݸåV¦Ì¨Ë<øàƒ^³©ëm¤6I÷G�;ŒmôÁ #­úL6cfüa¾¹Àtì7Ý|Ü}‚ù¨ÛxÓs𜽹d/pD£5J˜.̶X ü.¬ `µ°D˜”ñÇv t–$¶ õ}׬éV–I±™oD]’¹çž{¨ þ"tŒ”{ãÎ;ŒWjì8ÌøY«Íˆ©+L§þ3 ߎœ0럇CvÌRa•àu®_pÁÇ/ºè¢”üãF_�´öæšk®1×^{mŠþ̆#¬œ?àÆ ‰–d„ÍBœÚ�[ne�”™ÆÌ?üÐ<ôÐC¸3§ÜrË-•2ƒd^l1ô¯Î~3“æ¬1£~ýÝ'™ßMKé<ð·9nÿˆr¢Ñê ,ÖÛ �Iš[Ÿò·¿ý-¹\rÉ%æòË/7šco®ºê*à#Ož<©"£#¶ñpY†´øŽhb,Ù¤X¢Y+䥇θåV&¯+V`˜‰ ¨'ÐÀ6®¿BÏH¸' ?ÒqÔ+­†îþã,Ÿd ðï>S޼ßy¬�º¢’h´º £™li$ŠHB��ÅÜtÓMFsÃM�LÑ¢E™(ˆ…8¹]£‘¹á†ˆløy"�HJØrÚÉ|Žh¶'…�•6¯®Ò÷cŒ[neâúã�?P™y¾l*T0·ÝvÛ_ÊNÌeúk„�LÕ_Rô‡dOXf^øtê2óiω®mD£5ÞÖT6 {l�àˆ.º¤+®¸Â�Pn¼ñF“?~BlÏ0ð§Ÿ~ò,Ð�=ŠC­g·gÏBqìÑù9& z¤CzMQè.á×S>'Ñ„„KÎó„‹õý•Æ-·2i­^½šû×kÊ,R¤Y‰D5fÒ·5_èŸÓ÷Â}ÏôԢפD.Ôcü²È¼×y¬©Ó䛎Û/¢„h´†ØMl¿�(„‚PŠÌpá…Í¡`þ¹7È©k×®f×®]ÌO/ïË A1]�Æ/ÿ±”JKµö*3ÏÇAÐê$ì D3±�²ìç èûûŒ[neBMfÍš5Þ½ùä“Oz£¦/»ì²“"ONÑäx$óhÇyõÞ�Üöë©~šìÍŽ£= óK-†ºH&q¦ÚIÂ:áä)&H4Ì?§‘‹áNH!3l›Þ£GæsPË >fÈv½Ï3îà ¯‡mâ…X‹±GŽ©Æ ªp¥pË­3böóÏ?SøGÆì‘Œ¬ÿ�ë~'ìsëgÑl´7ÿ6ûušR–3HA¾ù曦qãÆþÇæÍ›·ÜJk!Ð!UÆ!¯F�sN¨è¿3ÐX½KØ!.Ì3E0Çј|uÚ™üuÚ{dSõÅ>ñnƒ�dDÍ´Ô†ÉGø*5Y¬j'É8µrâA)ÆEÉ©èÆ'�믿ÞËén۶Ͱ¶oßn¾ùæ ‰„à )䑞éÒ¥ˆÒ"ŸÜ0M�ÁÈ HK²ÒÞaç9ÑlöN››%?ž/_>ON~óÍ7#C%ÊÄ�Ê#·Ü _ˆu>øà09@†¤Ý©ûle€dFÊÁîÖê­Uz¾W|õWúzÌ •[™›ªþÛ¨N“ü@“o;¸ >zˆf‚°WHâT˜ß¦h$…)zÁ‡, ›?‘½1x AžtyРAÞ‰ºL™2桪UÍWï¼c‹x–IÅÒêÕWç7jÔhïôéÓCœ¦ÒZÌ ì±Ç ÒrA²áÿK¶®ÓÎc¢Y+ìö‰FÐ)4 1…%g"NÌ 1cÆŒ1n¹<Ð![®_¿¾©X±"ýn4WÕuÃý¿Þ’Ì‚H¹Þo¨Òj¹ê/'+=ß›zŒ—.+ûDWóèß›ç?2ÊmîQB4Z}l¤/„„]¯½öZ"EúðEa™¼?›5.ÖÑ£G{$$ñr½5kÖ4•J•2CZ´0ñ"ޤß~3©³f™8�~íðê«tò«yéI¤ØÒi#õCš.œlØDS­5þFaÔyH4B¶¢9$‚ jxïݳÏ>ë)‰Ür‹EêúüÁÐýÄu“l3›„E‘ÒŒ n«ÑfèýÏöÜ‹l&wêä}¿Aƒ(¢ ¯SºÆBFÌ¿À�3|C ¸ÇÙ×±B˜�›jvSØ$l´Yœüç?ÿ"u¦ï‹çÒÐ¥K[ së|^C† Áò‰ ‡öúÕBª±×u³�ºl¤Ò^Vñ¹^Ç©É@4Šlè› =óÑOë܆¹8Õ¦uL]ˆÉ¨—NµAM™2…Ô�\¦dÉ’žR¬“È’ 6ôˆä‹×^#‚ñˆæ˜~Ù|+kš[åq†HÒ"œ?ÝRŸˆéÝ»·‘ì’è&=ÂINØ(g»Í7/fä&whûzVYb�ÕÆÇ)! ©FdªÜ:¿#™~ýúqøà¢€û%ÅFÃs…¯#ô:§ûe�W¾Jµ2f£&Mˆ&ÖmæQF4„ÊöTœ"„Ë“…œjmذÙ,z{TM ヿ—ÑtiÚÔ'šE. qÆ óÃ'Ÿ˜ÛeWƒÇÙÈ‘#!µ 1©õíÛ×ÔªU‹OL9q…'œà¬–ƒB¬Uiíµµ�Í6òÅD32ÅùZ§ÕT> ˆ3ð¥š;w®qëü^Ü[W\{›ù¯‹®VÊ%A² Ùty¤NšdJfïJ/ôN )Sžf¦üÓÝÍC; =ÖüûÉn3�>¢YˆfŽ©Þ’Z¼xqOž|ŠEç?ªM AÂtÏ'™víÚyæÒ’Ö~$2RÊ €O6¤ÒZ¨@}»Rnô×P×9Õ ΪU«Ä„tP0Ž�RI–pvºéã�ýûa¼Ð'Šˆì’=a†¾zJ³[o½•(ñüppê1¤˜Á�CÇ̼å[Ì×ÃÿcŠ?ð¾ùß;š "ˆæ© =óñO3¢oãuD³XðK¾J•*%ÐU¾wïÞ4 òØü}¬\¹ 3DãcíÚµlnÞ¦�­xõ”«¥nyX‘ÏOr«ˆçI�`.M Éž÷ïßoN³6œé¸Y™KšžRµ¡„ãÿ¡ÊfLZIsŸ€È[#“ÚìØt[‚l²¤36ÂH¦›0OˆíRa ä“ ¤éº]Ä’p"ÉlßsØôµÀ<ñÞÞæªÙ÷F�ðæ_å?1WÜó±QaÜ\\úóÏR˜”zß\Tò}sañ÷Œ#¼#Bo Eß2y#Uß? Dìõõ;Oi¦@šåu£)WæãѱÑ:„Ûÿïl¬k˜3JRäðh�.~õÔ¢ ÎtúÓŒIƒ&$ù„÷°٠WƒH€.uÒ:¤t¨# ¹…Ð2è‰vâ\fœ#2˜¦l¤óx Ô/¨éXÂá+ê9¢ ð:O²kÓm›­:gR“ÌçÂBÕ¥bQûÉØt$ãúe¢hÅ>f毈1ØÜãélÚ´ 7cÒKÔvx ¤øˆ|ÖFÄžnK‚‚Ý6Ò™'|ŸÍ$Ó^¤ºØ1úŠˆÒ GlgPã!w+²W|B¢Yµq�7 ëƒ.ã¨9˜Ú�¿1xwÝX¥•É[«-äˆÆ�jÑøäß¿¤Ì‡þß/85Ñ€Â:¡k$mRŠb~� @‚mÎ$m¦Þ™~IQ¶Ù:¢Ñêo;Ë� ©ÂV©Ä¶Ž1"H2^AYé4SLäò¢ÒPó¥ôZ¥†¯>-[šbª¯@>D,øŸ�¢¦Ã÷8i“z‹ˆÚH]ˆçBêúðjƒ!D�M©vì4©·cVµ¶ß b„Ù¡\ÓêÎÿ£^¢dˆ²WTC/MxQ0sÆ¥Ç6m�5­ûN1Oø£yä�~ ›(½!Á(†t™O¤È @ÄÉÉð}@”CÄsmÅ&HJ;ÉøH¶ s"e“*V÷ËéåŸêžL½‰Ú."ל…D3YXg‰&NX&WåÐÁƒý�˜ézȗˈPž×Æ«b¾¯;>uªù·Ri·*%ƦGñŸ�.Ê7ÒþjL¯¿þ:^P† ôüxöéŠ$ö)e£té´ ¶Ö5G˜.´ÍìMi±JYÆáh�÷_!Ô~cÇŽõž»[‘»ÇÅ›oGÎ÷È6ЂnPTÝù@{¢ÈI3R $ÂïqÒüîõ•!—p´0WÝû±¹¸ä[æïÅÞ4+ÚœÚLzD’…ÃBŒ0?§#œ2Ot]§4"äK}ÆŠ¾Z}®#š™–hö ›E±ÔS‚M^¤·j)š)§hfŒÔ\�Œ™3x�ý_¿û®'O.~÷ÝÔ?rÝfÇë�t¨?!ãÆáVQ›;½?©¶Tã�ØÈf��f–O”Ö`E-[D4©� ©<œˆ$qÅf~�[‘»P�ýgÅìR£¢"äÔ} "K4œî9åó˜¨ËÒ!ÐB‘Îgæö­Ì5÷I@PüÿØ; ('î ŒSWêîîîîîJ�U(-uZJ½Ô©S§Ô êJ©;în=Î]ÿ�ß¾ýÞ»Ý&áŽKè%÷ÿ¿÷½„Ífw.óíÌ|3ÓB)4T¥ œòP(ð³áÉùm –Ýõº·?ñ¾*ˆÔ”gä­¨¡i8¨Óg�¡õPóÌoÕÜ”Jã‘3‘7C·fîèw±ŽÌ›š×òÖm·Eˆ†:˜+Íè®mÆo7KìÓE ——HÇfò»x€š!òV(êð0È• ©®0QÁÔ°ÉçÐ0¤ö–áöfþõ =£Q–) �Ý Ôetɦ!"“ýj½kúì"’ü„ǹ±æ Âa‹mŸ�XØž’t�A,6ô ò ò;Ö?ð�D6Ö€BcV -ôi¥/ˆýØÞ°ÊÞ=êLÉVkçƒ\’¡Ò0Ãð·áñùe VؽûtëPÇõò�¯Ùèð;ü€,‚Ôf&XÝÌ8“ ×£4“G"�†Šâ�La¶¦ÑΖÄ/°úH†Ð•þ÷˜êl S:nsi¹µ•Ex�¾k}ûö¥Ÿ <_„³ ÀU…J¾jCI8c}d^û˜d~H&·†��’{ mãÃýó œÉÐ��0%ù%¿Zç"„üß]Çî¯àqà��sIC=� A@F$þy ¬&Ò œÄl|侎îü4Äû؇ã(<Ç6žó>)¸È{p¾„×#›ú0‡Sj5? ”}\?×Ê#ŸÁ¤Î“²ËÐz°Þ USSMf\ÅW¨–%bLúé'wÄž{ºUL– ¡ô¶&›³-@~fØ /¸ŽÖþe»»îyÝuìß“»¬&N£%?q‘'k…£ÿ�É4Þ‡¯å¦³Íò?å�†ãBjÈÁ[q.̯ªêZ÷Ì›?�à'䣰VR(‰¿Åѽ À]<„ ‰A ì#€Äèb|Î �gÃ]$4Ç{!ž“ËÑûN»æEö'Å>„ï’][CT�Ãɤ�²|U‰}VÈ‘Ïõ7Øçø!» ­k $cwßÅÔ” 4K”_!|v§‘ÈF–‡YÆ$¿ëXˆr¹Å ^KŒïlj¬3Ž>Ú�·BͶ¾PÓ‘Ó:ãŒ3ÈÝá4B^a'åºXAh¥H'D…þm¤2Ãz·Õ‘ƒÁ;ÂSBˆ@­ª½Ö›󫨤ÂY.’ý$ù •aäeÈ“¯ƒ ìvzo¼ªý‘õrƒp¢à¸¼Î£H² �‘ý!7fè¯{ð-R£q~�í€BHxA„Þ�Tsþ”×—$�3Ñ�‘P–‘l±]¯H†ây≹6 ˆv‡“îá�wv�õ£%™ ©ŽG¹4·P Fî •Ý|þùî ¥]`ícî°pÛ°ßw~%&º"0ÿãØc�%ìEhd¼rt&Àûáÿ¤S§Nîs#p<˜Ö’ô‹^däKk‘è'™O~EÆ£‰ÇB.&�ñ–çÁ>xx,�„¢$>Âyä� •ñˆ �¼dÃqâ¤Çñ å)Ç1Ž *¼— ä‰êßþª|{^ænò o¥Ó@™Œ¹^$J.ÉBi„ Ç{ã�]`ýf*¦"ªü©Œon¡cÓ[œøEÑ+Ý¥\vÙetFP!(ž s|‚úœ³Ï>[á±ìò^|} Å—Üy“CÁ‹hl”1ð„Ð�a0‘4ǽÂ\B€$‘A&äSxŽzŒ;{¼iB6œC,�‡°9›WöÅÃÁ#ˆ‡ÎØO�±¦‚˜tý¤ù<R³ùýH¤§xD™öU:Œ“ç^B��&`ª&—ÕÕ óÆ;»ÀúÁzd•ž¡ qÞZÉûå—ÏÉ\rûëJøc¨#†Ї #¿þ!·aÜñz µ‰‘ê,âu@JìKGfȆü ï“w‘›!”ñ$ @Ž÷@lxXû`¼ÙO­÷!ΩŽñüÛ!: 87òjžWÛçƒdòBÒù9MDÃuéÊÓ öÆ;»ÀbDS„,—&š$ýýò˯¦¯’²Jú”a¥ ãŽ?Y¡%o€˜Œ'ÆŸp�‘ ¯ ÜÕã¥Bƒ¬$.À3‹R/0 "So0ˆ /‹k€Ø¸N^Á¨õ�Î%…×ÔÈÛI ÇàÚ ºš÷¼�âÎbÃ,ß-+Ö¼þ>�…÷8„ÉçøÄïìë+ÒœCMDCŸ¯yYš3dÈ×§O÷ÔSO1ÑÑ×xø•Ó«¬¢Š~e„œ0€"‰”…–o¼<’þPˆBïT?b�‘1Bƒ 8äűØGF95ÞÛ1Òx�‹ˆFž ä@˜�¶.¼_y¼ˆŽëÙp.<0Ž ¸yE\S¨Ž»ŸâϼP$PxÞ:ÞÌ}f|¢á3q®,'/o¶ÄóÄÃ;¬žÄ³æõ7·h‘gä¨�§í?CÌvÛm7ò>tlÎI)®_¾óò�O}JøHH–èW1$ˆˆ r7±Ö1u‘Œ1(g�G„w¤D?$Áv”h(Èd”�Ÿ™· á�¸‡Â5àaá©à9©ï𮛢Q¼#„Fì')6âÆ  «_f—ëŠC5Z‰a¨áöfÍ ÖLókL$(¬¾ïMŸf—¡õ`½bøê€˜H =¼¾dŽs5¨ÔPTíj &7·yýç[·ã—­]Ím6ÔlKë$@Á"Ï�åúåW.c>Ño0¹‘HSK‰x1vTeÈèKÒœx%[^á.¼ 0çF ¤9œxÅó؇mq¥×—ô’«O2â:uÍì#^ „¦ý%ÞÛ긻kí=塺Ô0¬9dC!¨õ8û⌑-ל¥S5}¯³!†oM5Û†�5Ðà“O>qS§N�«ÜyÔ¨Q®ƒÕÖìÎð²‹/¦[@0¢¹Âz¡õ¹ÃÔ+«¬B¡!sj2SÅîWC¿æcsÌ7>ùÓ%0‚ª`‡<" Ï1êI¼Œ?Ûâ¤Cx+’Ëa›º+„†7ð~öÃøC%ä'p>¶Å='< |�< …õ^Ù5]”Û�Û�Uoxèm5‹Z�4‘MF»³©DcIÿ?87ÄÖˆ ñ̼G“eÐàaaSÍ1æ�ÌÞf›mª�9æ¼g–ÎOFÿ1fÈo{Ýu—ëdM%²îÍô?£÷™z M¶v,ǘ\wùå–#„–+R]¿|ï2 )c�·€ñǨchñ*0Α¾f &1˜‰êU$…PYÉÈJ¦Œ'Cn%B“2ŒíÈš!šÓ¯yI­hyYªÛ¹qåÏEŽB„ˆÔ…@Òj周ëá;àû©"_’MC¯&Í 5èù׉*_,,ç‰&KÇ|Íì° Š0#ly?ؼœ�S¦L!¤aPt ¸ø‚ \7 —�~å•€\„j+2¬úì3÷âM¦žYk-ŠA›J4­–�üòkÊÌêYHâ“c‘ ÂL�zös½žùÌÝþä'ýZ#±ž(4¦z•Tã $©Ê"c¸H b!D¦Áh„º äÐ� ûàˆÔb€lPÁ%ôÎäÕp È ÒÔuñÈ„×9ù®‰<’D 䇖ÞéÚŠ�hêBùóï†çB4ŸØgš$2Ñ€÷¸áëì5º~”ó÷†‰†¢°Óp}£�%V­>ÅŸU�¿A]h“î™gº ­ÇÙ_6ÕQÞ $Sa�%0ØF okíWn½õV/ð+ëf—Þþ†º cØetåe@6L@6=H“Kîú ȳ@Ž x.�dOY…©ÉÐê8„ÌÈ×(„§ðÄGh‹<ÊÅ·àøw2‘ïÁâúuö—§FhL!4yNº&ˆE^�„¸ Éž íA²|G ��…ÒjBéó/†Þ)ˆæ3»ž"{D,� p ßg¯ÑõDso8—fVH. 1TY“Çi×_½›¼Ž ©@4âã; /ı ùÎ…kÛï|-]¾7<š„h¾\c¿›ªb$ ø·oª™eH”´ù$œ—R"¯¦Ji¼Igbˆï†ù3ۙǂWóõû1Bk*´�wßízZ+•ý¬µMó\SSãüò+[×€�çŽ]ª2 ³E2ØUÂW„�9!ùEŒ"K†Rû‹æ*�œ’áxªô±AÚÆ1!®�}ñ° ;ÏID«üçº8ž¼&] ’BhxfìÃ9ôD „‘7K¥iuCøŒïMa7ˆÊ†­ÕÛ¶r›ú9PƒÔbD3¸ý.×ÕÙw«•Àq¾ÉrÃë‰ØzÒðM˜£©hÜÖ~íµ×Îâ‰'œBgGZÛúuÖXíf2æÍM]v¨©ÏN³ñÆ;Z7èm7ÜÐu=å7ÃfîûåW¶®Ñ“fãÈ‹jÕ�Ö�±ˆ‡€QUî†ða'Œ±Ân�FLjŒ�æ¸"¢D`Bax1äe¤BÃsÀƒÁ� ¤rãÙÏ ÒÁ‰ˆb^^¹€7¡Dò>U˜ŽÏ ™pþËîz Rƒ ø\��Ä .‘æ³â‰šú ÆDLjf¸£2äZ lH 2µ×r!Gã‰F§?Ž1Ê»±®Ã„ÏòÅ€4&<îhC¾V·¶öŒX¡}{·îª«žÌ�—\âF~ñ…ó˯l™ÑÃŒ»jyx =¡±ë|Ÿ<*4Œº¤ÆcŽáodp5.@¯E:zÂPCRI:&ctÉhd³º¶)ñÏ9ð> ê0¼™”c ÔÇLÇç8x$�‰¼r8„ rƒ|ÈIáù‘â=òØð¢T?¤ïD޼ä©Ûÿ;þÛá˜Íã\Å÷ÉùðÕTÔ^û6‡Œ°'`«·Bi�µ¶/¶Ãc¨öÇ«!_Ó¿w†©Êµ\Í釿n0)ó€ûîsÅ&4«�>±Q܉¢mìØ±Úÿ&"ð˯—?øoD¹ 1^ w×õô›ûhð÷ØkߺúY1!*¼�ˆÇ pVl6�ˆF ’Z®Ä AÇ�ÔiY^�êLÔ¯H� ù7†RÄXË«IÞ9ièïUkB`\›fì'òåšø>ø.xáK¼+®!iƒœ�AŸ×㵆Ã/z’†œož‰æs#ìZÈ—TzɼBÂj9fˆ=Ñ[ïf†žÍ”e–YæëG}´pæÌ™ô2CòŒÜ9è“6ÁÈ¥)…™ì©LŸ>=h_Ó­[7Gþ‡æžŒAf*%Ø`ƒ “?{ôèá~ýõWO:~Í×f™äUÔ#L!1å¸Ó&… ’¹­ÏÇ�8c� cÅÕd¼O ¡ÔYž Æ`d1ʉhœïDa;]Òz‚� MyÚÅ`¨¯ïýd”TR ññy9?ž „ ‰)Äy5½Sa@½! üf¸{ÿ«¡sâµ$<d­ù2x‚x[�ÿx�ï{ýCC?ˆÑ€‘¯T||Çxs|‡?å¦AöDó¼a”!/”@ÿÚ¾}ûw8§¬¬Ì5eQÜ9f̘`Tô…&ØrË-ÝÊ+¯ìŒ´œË-·Ür´«ad1`{dåb6ÍsCËùXçgçu¥¥¥Î/¿2¹Þþü/îìãÆ¢Àã�pgÎ>x¼Œ&†‘¼FcŒ—�È 'k9ƒ–œÁ3Š“• D#5:�$Ã5Ð/ƒsöƒ_ùàWDä^”¼H�¦™EMÈŒkƒH9&䈧Æñðt蘀W£pxv‘ãC¼\¯ˆF] xÜâ˜^µ¡í'r4vìj{?Ñà1ñ�r¾?rÓ {¢¹ÝðDæl†C6†�6¦økÆ?C$,¼ ;!ƒGy$èݱcGú�Ñp³aóÍ7gÂ$ä‘KXž¢1ÑA½Pµ�7®¶í#”[Žh¦5ë¬:÷ÜsÝÛo¿�±Ák~ùš™‹n€AO^’BLžˆù„ƒ¨;ÁàËãÙ@NRj%j#Öâ=xš“�¨0ÀšU#Uç‡ðDvœŸc*ùŽ÷@å¾ç¿ $–ÐÛPn‡ÏÃùÉë��Â;QíÇUèŽý >Èâxä•oÜ/C'ãÕð�Ûjüyy?ä¬xZê`Àñ¸NÆ L¶z›™ö½Uòž[Ÿøâ‚ôOÊr7CsÐ{¢¶> W’̈�pƆB:«®ºêž–gdRf :k<ðÀzËåÔí¹çž5æ‰[ Î óPf[§èÊW\/F#�ã3ôA5ç �ÿOHpEáã{Ï(;W顇Jk¦Q¦u`›_~üvÿ�~'€á“–œYµ/"È‚d7dD Q;šD9 Í•‘Ø€\ wðçøûD:/ã Ah�½ÆHÌcÀ!>¼I£©«!¼Å±EBÉ€âÉà™![æ|¾<y\1׉òüŠ»ßvÃÆÎt§ås~ŽÁk�²Bƒ¢“*N]ŒdþYrÇ« 6>âŽZ<,HÁ¤Ä¶Üöh<Ñ¡:Byó(ðË{3„—T\‰Y¨j��D7!%Œ*OBÅ�sœnxòá|áyÔ&§’ë°ÜN½ås�ÁAŒ%ÈËËs~ùÕœ•_TNc­$;á0…Âd,¹ç›ÜÛcDÐb(W‚ o"¡ÜY„Á`´UH >óæäIØ&¯R„$I®“€çºSzWxmìañYñ.TÛ¢< Û$½†(�7WTÖ¸YsJ õf“€!"$àzð �A6ä˜ðÌ68¤§k¿ó5 DA×/¾ýUŒÈ}Ã쉨Ûóä°sÀ¬0<6(Br)a{H. ¶ð¢UÛóaè �1ÒTkùë­·^Ã[lH¤�=öX×·o_/ð«Éë¯ÑÓI¤+$‡r3‰$Ê ‰¥ zI—!=Ιh 4!93^RfÂQ„¤NÍ’:cØ%vH ˆŽPÀw™p ž+|ÆùØWDÆykjê‡Ô[Böƒhð’¸V}—„ ù|x2¼‘d¶È6Ýt\u~Æ£ñcœ³éhó”á%ë†þ<·Ýšé‹驯˜à À¤ÒU믿¾ÛØz°íb“?Ï8ã AA¨_~¥Z¯~ø+wñªàO„xë!6ì,-�à@Ðñãû`ÄÚR'i¼ p¡Dá5¼‰”× ¹AZ„ÎP·ñ~©Ç $ÝlçÚ¿k¯·\eU dC®ˆ×ÕA3ntM�eÕ’�ã!…X7·°‘ Db¶ :=²�h²š:Ô0ÇP†Õêm´A­)Üj!œ½öÚËuîÜÙ�9Ò üJ¸ ŠË1¾„¬" |#È�7!1ìÜ­Kñ…±o4> €RMæâ׊±V«~ÈDýÕ¤SÍOD,ƒï…ÜäÀ¾ä¬ð¢§!ç‘1Ñ�ØœÂ2»±«#O¹¨÷ÅuñÝÅkl@<üÑ‘M‘á^o¸=ÑüŸ�2îûP=3+ä›—“o µB+­?غMß~ûí®   "ðË/’×ä ÔŸK�™Õ«‹»rÕËp—NHíñŒ(D”Fï†cAdäjÈ%ÚGb®3¢SQg£Ág�Ž*ò £q½¼'Y®ˆ×ñ‚"áDu@�‡çÄ6“âE��œÑ €è"¡>®‹ïñ@Ê<ÕvW‰hD65íá‰æÿ†j~~‡d e�¤Ñy&(ßzë­ÉßÐ¥À×ßø%µ¹Œ¢Œt¢œäñ`ÔE>q¼ 2D÷>Z ·çD®D� ššãáºøL‘ÏÂuQ§ƒÊ‹d=ä™bðŸ¯M¹ ^DCÝç‰Ôë|óË8Bgô‚ã½V³?óÊF° os…ˆ†çˆžö†ÛMk�”jˆf«NðÜÔiEF8U묳Ž;ËÆP:TýÙüj£ëŸüRîÄ•Ç0Ïk‚?�ê3yx4$äñ@R„ºv{&d¥k!Q†¸hGÃgNî¹"HÐ04—¢º£- Rp½ñ" d†à‰~ƒ¥˜KV¨š€8ÃïߎµØv"ÃÖ—»WݽÒnO4­¡,zD¨†S-NYøï#�†M7ÝÔuïÞ=§ùÕ&!ÂLx'„t0˜„Š0°<'oƒq<'œ…Wx,Ó€\ðWaÐÓA\ ^ ÇÜÂX¼Îg×~šãOë .ñv ®¯Žmä¼èP /Oß�‘œÏMdê¨Èf¡�NuÔîyã퉦5’ÍÃá˜ê‰�ZÚÈË¡ý -qöÝw_÷üóÏûpZÛZx³ˆTï!¨'—*Ø5;�÷C¡!¹%µ3 îþ Y¥ƒÜ T>E§‘ê~¡ çàs“§B€‡­×�W”¢¡(çKª¶ƒl¤@í\Ø“�'šV �;øÑ0 ²‰w30Ñ@ýꫯ^ê©§2=´�ˆüš6«ˆzh `’ûŠ’ýtE&‰Pn‘§à}ÇlÅœ„Î R�ñ^’@!8ò=|_M !âÁð�ñ^ÈÒ#'“4ƵƒÔ¶êìÞê2·À’«óc¦Ob©êò<<Ñ´6²yÐðžad£N1©ZmµÕfuéÒ¥ ÷Ãi~}ðõ0r"­LXÒÁ{ÁËáßÜ©Rî!Û€W�´™é›äZ"Ï›(f€45ýL%—†\ mŠH®¹…ïr„ö4=TM„lzôèáÖXc Mé­ Éæ¹¬°7žh<ÂÚ›™†º× Â?èŽ9æ˜×òó­´Ü¯lk;ÃH…¤$ñU1!ÆM3ù!ZÌh>Íü7q>Bz„ò¸.®âÑäÍl$Ú$¢Á£¡fgÒô|_(CM!ê~(² £�d‹­ñDãÉæép–Ne’ ¢ÿ&˜Ìòó~ýú�õá´ìYE%Ô‘@ªãѨ>¤¢nìØÞRÒ ÏҔ㰪, t j+“í!±&M&U×gê™&ÏH\n0eÊ”`ȲË.Û˜l&d›½ñDã çá4žç‡Ûg†î½÷Þ[ï´Š¬P§y!ýÃ0à‰ªâE ñJöTa3’ë쟪= †ÏDÇJuW/ï 90ù"<,Œ®H*›=)üšDÌ>Ÿf^±K¶þüóOw衇¢@ÙT†f›­ñDãÉæ~ÃÐØÈêrÃlÈ&ôn&Yí͈ .¸`Ä?ÿüã‹oZñútÈ(z‡%#¼Œ¡qPóHƒÏûåýÄ )yÐ_Œ| !°T…Ÿdö'|G�£¦S"KÖug#T퟈dS’n^AiÊPèûï¿ïvÚi'”h"›2Ã`o¿²Žh<�PÆ…Ä2Þ0¢ Qb²…Ó÷éÓgBUU•ó«õ­¿†d9QH‡p™È…œ†/r ¯CΆGŒ>ùy)#šÊ£QþG€x0¤©Hµ‚½G=Ö²äÚúƒŒ¨–j1E÷ÑGu6aš o ²¶ÆÆ�GØY`X˜Ã™iÈ�åq* ¢Í7ßü¯_ýµ¦Õ…Ó|ëÆ6'ªÙu3Hš1ì´¡áQP³MäÑ<'a ­¥”,ÇÉH6 Ù%3ÐÊ×H¼Ðf€™_Tîæ¶JKK]×®]� ;”WSo˜eðCÓ²–h<Ù¼høU^�H&–Ç)±*æ gŸ}vÙ¤I“œ_­b1œ �&N z<º o&7ž¡,&UJöÜœagœ“|žžŠŽ ñAnx8�ÇËæ|ŒŠBÉYá‘´ˆù>Ê+š&¶™6mÝØi¼nÏf{ã‰ÆÎg†Q!Ù”ªôN+Ø`ƒ fÝrË- Ö]Àùõÿ®ãg¹]O{PáªHèŒP²í ÷PgC�NÎñ¨xóÜ_ÅÛ¡sŸÔy5Æ sù9&�•!žx·ý0ØÊ µV5×J(/O²pТp`s¢üñ‡³á†�PÃäÝl·5žh<Ùô1üæmfHC5¯Y¯¦ª^xÁ9¿þ·ñˆz ê àÍÐáùâ[@0 ï…d`ôÓ•»�ÊMƒ¹Aòk0?‡±Å=AÎË5àÝA’‚:ÄrPÍ-hš»Þ|óMÕ×ò6äO¿Î›ã‰ÆK¡ÿÄUOѬ³|íµ×®;üðÃ]ß¾}ƒ¸²_ów•–W1_&©'"ƒ¯ùøwözæ3†…áu�³Édñ&Ç•¢ /†ëAH b´#µ=x`’>K®�iHô ¢†ððÊD0*tåûSH±éèöÙøíO~âš»èºNO´å—_^DÓþ.å‚­ñDãÉæ6Ã�°•MI²©·ÜM çá‡v%%%ίù·:õì‡�2/@ø‡PV×^o¹.w¾év;½7ãÙr \¿€B2ó«�Ò“G%oL£œ!<–Λ³M”hðûô»›—U\\ìÎ:ë,·âŠ+ª'ZQ(âéŸK6Ç� üfÈWíMÈ0iy¾îºëºƒ>8h8kÖ¬ù0áÓ¯î}À]yªÚ B?ÌÖ§M= ³ÖX0Éõ@:z>¿®�`]OK�‘ͯï‘Ûy]tà·µòÊ+3¿¦ÁDc¨Ãc9co<Ñxàª&I$GèÖ31Э¹æšn¿ýösÝ»ww#GŽtuuuί̬~ƒ~£ê>™Q$L…å‰zåcZs[—ˆ—‘•ÑT?ƒÂ�–A-Y?üðƒÛe—] ò6uô*ÌÕΞh<áüh˜c¨MD6¢‹.t¤ÝgŸ}‚š€ï¾ûÎUTT8¿Òºè�E¦™‰Dâ-hðl²aª¦ÂhdÖ à9ªµS®zÁUU×¶¸‰ê+¯¼â¶Új+Ȇ:nú r¹'š'Ÿ¿ù)”BW'#°ä’K: ¸]wÝÕuìØÑ½úê«Î:E»úúzçW‹ß#6•SÐŒ™l…êsxÌ ¢ëtK%BŒÚÚ–{ö4½½í¶ÛÜ&›l‚WãYdýö~Ée›ã‰Æ“Îà0¤V*Ôê ˜¸wÃ�Ǻ ¸#�<ÒY-Žûûï¿U�_-[OøÎ~ñ“š²)#íÑJˆf½ƒnª`nPºr–ŒèÔ©“C„³Øb‹ñ£°“vR¯äº½ñDã çó°»À¬°`Cܳ�lÀ*«¬Â�„xsàå<ùä“nêÔ©>—Ó‚ð’e 4›PïKt{dšhVÝó7kNzÕ˜ãÇ�wGu”¼nâêŒt¾·ß]7žhûì3·Ûn»qãp¶ÜrKŠ9?ik6Ç�÷p©UÅštâññTê)@Ãý7 ~,Ûo¿½;á„Üõ×_ü�ÊÊÊšrðkÀǿӷ QË”du#<Æ%¾ÒÖäÒ#$™ -¶¬{ñÅ]º�žï»ï>·Å[@6ܰ! á�¶hs<ÑøÑsTƒ3PÐÀà'<ærœzê©A>ççŸf¾º'�$«¤¬Òu¸²/RfThÔÎ@ q’ªWQ !äÄ>ÙܶŸQ|€çÖQïC–¬NÀs¾»fÍBkæÚ-°p*ÎÄ¢çàyç��Ìzë­çöÚk¯Êð&ï¡¶js<ÑxÂÉ3Ô4�pTZo¹�†ÕW_Ýí¸ãŽ�NP :lØ0/"H }í|Ç›ts¦} „£îÉšrù ¨Æ§ˆ“†�ê�M€L4Ü�Ïé$MÝÛš96‰5ï£È’úõ…ClÑ42†dB,°ìÆüñyÊqãÆ¹L¬ï¿ÿžÉœA)ÁþûïO»š©�M[¶7žh<á¼g˜f(KàåT¥è>@ˆ�ÜNPGСC×»wogc B�_¿® –Ê„Í+ãuÏ3ôøÊÆvþ"  Áˆlh*�„ÈFáBH â@.fûC2ô‡ƒdÈ}ÑÏO“D熆€h¶¸Àµ[dé€hÀ9çœã2µzõ2R<è Ȇh@½EFØ9ŸnëöÆ�'œGÉáĈ¥ÀPÔD�§ÖPjÒé Kä÷èÑ£ØÚÞÔ""hÃM6é̬öüqĉ„pmi˜CãH q6†Ì"aA卸 ™Í£ñÕ� �"^ºÆ‘òÝÐq�fŸ ßOS‹HD4 mtŠý¡/ ¢ ”—™Z&Làæ+˜a³ÖZk9ÇNÂo½­1x¢ñ°u¿aDØNc†!O]š êw*LTPb# *¿üò˺6˜Ï¡q£dÎB"Æ•vøê– •øê|�²u ä€CãP¼‘½ÎzXmþåÙ¤E �&å¶šJ4õ ouYÝ‚+n+’È©dì�aêMfØÐ9 œœûŒ·3Qx¢ñ¤óHØ1zbèÝTH"ÝLäéLºð VUU¹¶´**k˜9C)rçóV ò9̨Á+tÔZAÈ*™g&@–W Ùòùö†‡Ãçƒ(šŽx“IÆ�·ðV�Gµ[x©ÿÍsÏ=7ÒehÑW¯á�yú4º-%7êmKjx¢ñ]£4L�²©6”,»ì²ùŒ/xýõבI·¥Ï„�¸;‡dÞ½ :ðüÇIr“ŸÁ·öd’y#|…Æa0¶Íëø€y™ª Éä>6\fa³q1¢i0)ò˜LÞü\wÝunÓM7¥ÁmÐåÙg¨�÷oSæO4žt^2üm˜z:¥�OSTk ŒBúÉ�Ó Q«µC¶"µ4�Ž< 53j-1ã_cÚðð ¯D¤Åv‘ ÄÅ>MõfJ ƒ G´¿Á�††ÆD³øâ‹ÏÀóÈÔ²�1}iMC�AȆpô‡ÞŽ4žhŠÛ¸û+--u9º˜êÈg ­Œ¢< ¼Šx²œíF<“2D©ÖG~ŒGEž¢HP¹�·'‹¸ò‹kg;$£ãC2Su3 fµ†Ñ†SÚ%^·ÅkÃð®üñÌ…Móò‚âÐÕV[ Ož¥Üˆyû�~¢ñð„ó•y0eq¢!Ijj4ž3v:ÈÛ<øàƒ9-˜0mÕÿ„™D4’<'ôð2vîð;™j‹÷âmHá…Á86žŠz¯A0Ô¸(t•ª‰'„"‚QB>NŠq²AÂŒ÷Ô|IrËCf³ } '!šÍ ãb"F�3Ä,CݼëèˆúŒ›* EË)Þ4ÜëmCš‰ÆÃË£íGVœÀ«Aú™“ƒJçÙgŸÍéÑ_ÿ<€jR’Õ£@xôø¢c@ÒBEˆ†}·:önêU¨[á}qð:dÃq È®)õ2")z°Ax*´äõ8¸H†*þˆ§Äþ&™2Ãç†ÕÚ%_‹#^QQ!]ò'Ì`ÊÔ0`€Û{ï½pcEÁQ¨7½mH3ÑxøšI;åѤ³×²éß¿NϬéûÎOì¹Ö�à�qñS¨Ò0Þò¹u9æ¸wÄÀƒÁ³ÁãiJ~DçWÑe_Џlj é÷–é"Ô†0d6ÂpH»¹¯¡$¿A _Èì¥L-xsÌ1ކ´Ì€²sNIñ¦'O4ÏS÷d¬Ž��•Ó@Þ !Z¬»o¾ùÆåꪮ©u]î|3™ÒJF›œ¡6Š9 IÍK³MHа¦MBF„ãziቑ $ÂkìÁZü›0ÇÈäцy††š@4»¦ÅÆa¸3Ï<3£2ç3Î8ƒv4x4.¬?ûÛÛ†4�‡*q[ÎS’ÿ{챇;öØcÅÙvÛm‡ÜoF$Dò”×C�Ó�7O¹ê…Ddƒ±Vd’êy_ÿgQelÿ?!’)7kκþewþMýÜw¿íz=ó™;¶Ë³œ;Ó=Î ´æ�Lµaf¨0۫ݼ­­TOôwÙ¹sg—¡…WŽ|ŸBe éíCúˆÆÃÍX …U(ÿBŽæ¤“Nrü±ZÏm@zöìɘhˆ…0Z¤Æfë­·v÷ß?ûç´@àíÏÿB¢¬âFHGíYZZM¯AbêD€l™GUóCBÔìp~º�j‰,™kçšÉ-EÚÒhœ5ÄÇ9 Á&ÌþÑ~6œ 3ó°–2Ì”ÁW¨–¢á ý}‘ÿáøˆh¦îô6¢åDãáIævÃ$û!—7–0ï¼óÎîÚk¯uEEEN Ó¿ÿþÛ�xâ‰ô@ã‡�WuªeÍöÛoïžyæ—Ë‹ïaø¸™î¢[ÂRâ]ƙǖTØÇûŒE”e¨ÉP’ ±Nsë£Æ¾(Í8v* pPW‚æÌýÑü`¸È@£Ìy] ¨pSDø»ËÔ(rŒ1„†ÍlCo'ÒC4¾Ãót �‰h ‘ ¦à¨£ŽrŸ~ú©Ó²êl†Q9›Ê)Q€$Ïšé�ÇÌ´ÉõEÞæÙ·~ÀKŒ7©ÐofŸcð^5ñ¤&FP¯2ö—¬ï‡<Ž¤Î¼Î~䀀ú§áQƒÃ>�X些žÿ�`Bl~�[xÓŽØö} ‹µkùa¨Ñ̇ÂM¼xMÛ¬W÷h¤ÿÞN´œh<<Ñô3Ì6¢(‰·ŸÙxã�ÝUW]ɽŒ;)¨Âf ¥Æf§�vrï¼óŽÂ9]Øù×èéîèÎOãu@,„¡äÝàñ4;a¯–2óo�¯9à¼Ç”«á9Ûx-èH°ëi²¯ˆ(xaD£67\+çlvØr °uW·Ð†§¸–Ýĵ[`a:ƒ¯z$-]à uqåÙ Aƒ\¦ÖÙgŸ„„)Þ ý¼�h9Ñxx¢yÇ€�(LÔM5_{í5¶ãùhßȈÈφr}W¸>ý¿ÃØ+”ÁÈÃi®‡°È½¨V‡< ùZÌà½àEá�D„x/”ZÑ(ß#ÒƒTÔ ´Åãðf¶êâZï8×nÑ ¯! MnŸ¢ù3.qæï1“ói.½ôÒÀ“§ƒ´ˆ&=gO4žhúB4 QSMTf�¦ˆø7�œ!”ø{ ¶‚€l^zé¥@"ÝÖø©sÜU÷½Kâ^yHãy´TùÅ1ñ> pLò7�Q¢™2äb2Q{#¯ÍpuÃBkäÚ-´¸FÀ ¹$ Dó› >Pð¡‡r™Z×\s�[c�5\£^k†O¼�h9Ñxx¢¹'¼{œÂ8g Ín»í&¢!K»Ž€l>ûì3Úv¨¦&‚å–[Nd„Ñúôéë¾›Ûb�߆OAŠLhJ¡³LWêC:H¯ÉÏàIL�v²QÛ�n÷¼S»ÂªëTˆ`bdX¢…Dó“¡JD#o¹wïÞ.S‹.Îä�tNC~úji<Ñxx²ùÁ0Þˆ¦ºq¯3£C‡ÁÌŽÆ …IÚÓH$¤0ÚÝwßí*++][Y55ut�¦Ð“0V¦‹+ñpHîkPá4ÂlR�¥xSˆ ºözËÍÎ/q7ÞxcM¢™l8¯…D3ÄP#šL†Î�ç+t&¢™cøÙÛˆ4�‡oAc­h ›qwG‹™üüü„rÐ’’wß}÷ÑŠ&’¯Q©Æf•UVaŠ¡¦Ù; ð¨Î¼í.«´ë[w¡î¥®Ôýkù¶WÝuk¶uÊR–m·Å¬Š´¸h±BÐ …Ìbƒ‰Ë<ï=÷•Ã{öô9˜Édfîûº~¯Oæ9÷yþú–m‚@ ,\™k~{o¸yDÅt�Óq†k2GƒÛ”}�Ž=4Gøsý×@³ðàóqKMMm=¿ÿ¯¿þ:�ÅB5˜~öW‹-7šh†ÎxóFèÌm4°.gLF#4¬¥4Š&ƒ8ꨣ¸}£hüzF{ì±Çxs�¹¸ó44+çfƒD.GÛüõ¯å¯IFmÝUbz ™ÅQ3sE¢ÉÓÛ+Ó¶D?s.üuÖ Îø»1„p'Y¯X·�ßs÷|_mF›Á=P�¶´�s4,rÁgoMVΘŒFh$Hù(QnpÌ‡Ž¡³­[·6_uUVÆf7ôѸ Xîì ¥¡©“ÆTTTd’T¼Lž»ÖÜöÜ— o!qOƒp=èc �á7®¶þzrº)(Þkí¦Çœ;Î!Ã÷—h`üäjÒÛÛhf̘Á—+׿e'ÈÓðŒÉh„€úƒE` 8ŠÆé[èÚµ«Y²dIK=1,Àæuðk}ÁzšÍ}÷Ýg6nÜh’]Õ5uæÛ”•¦Û+ÃY¢ŒÐZL †ÇfÏ¿öšdf/YÏ\L 3ÂXð�— ¯Ù4‚lpÃ~NXê½ Ðh¢%|Î9Ç7ó�ëV–¡%h2! T�…·Ó݈…;7V�!ÓšÑìÚ¾úꫭ͜δg„Ôx˜QDÀNì… ‰¢™gäì2¯~<•á5äYX¾Œrb†±æ²›PÛ¬g¾zlØ�ÓïÛ42´içþ›o¾É—Ë­¦| þh‹þ;XåjØ$á?cøðá&ZZµjo4øœ:F¹d�‰8c2! 9 o�Û�ÜotÊ–qðÚêb˜íÆod öÐ@… ‡sÓ–-[ÆÛ~/…Ë0|†Ð¨íVS >nŤ€ëÁrrƒ“ÒéÓ§GâÖÛ¬úÛßþÆ5æø¼;ct`nϘŒFh¥s°=fÃ$þ-·ÜbfÍšåWÁ$)|ÆRx„ZmFS¦€ƒ��X™öØîýµè¹ª ¯�€j�U¯¼ò ?çM÷*Pá52! Ev£áÒ)VˆÁhüÂ’ÄÎ}�XŒ†lW?F€2ϯ+ûàƒByyy&ªV¡JÒ=N§‚é‘?k2¡ášu6£Á´Æß±J×ïF#I¬LD>ÏÏhÊ@o{øŒÿíÿ�4På5¨‰'Fª Ì¯hýbî¦Ó P&GðŒÉh„€†ƒ|Û­ozœ¾üòË/Ëh¤fÃgs±”Ýb4µ`<8xõk0l ž_3�a³È¨Äo0,nìî>  Èc#xÆd4B@ÿ™ Ük4xÓc)2’½ )I~zá…|&:“5à*àÕý`!(·LxÕŽ!ÕøÌéãÎ$ü��¿÷^°Œˆð9“Ñ­AÐk6t'4�?Þ¯úG’¸§%ì~FyÂg‡€ ` ¨õ”E/›èŠ•�ø;»�fØ †DøŒÉh„€2@ØæäkÜM–�;wï a¨A’lBÒžyÜ‚mFS惓\“š{ƒ °Ç²Ó¦;:ö�7‘QXõÅ_pD’ËhÊÁvÐ3ÂgLF#´lE–'ÞsÏ=œe&I~yš›nºÉgÈ&٠ƀŸƒ¿�… Ð;­dþùç½ðûÕGÛhž{î9oÿOØÝó&£jÚ,O'W<ßpà fÍš5F’üôÔSO™?ýéO~FS Ö€T°ìvBf.j°ö;/¼Å3Úª¨¨à`WLpÿ}KÀ²è�5�дP  Òe4/rá…rí­$ù©oß¾ÎܳæJ�w€¨óެÁ¨¢† 6˜öжmÛ¸ •rî¿ën0#zgMF#4Ź T€¨p‡Ï�§aCèŒ$ùiéÒ¥,‡Gž& ??ª�™fžg!ôkµ[ÁÉÌ™3YQé2™F°|¥s&£Ê�&ª¼yšC9„]Ô’DÙûRxóÅH†[-&c£á¶Pxx&Cfí¤îÝ»³¢’FC“¡ù­‰â“Ñá*Øa)sæ8š[o½µ¹ÆMI@™ËûÃþ^$æ ŸYiDÕWCxpëž={L{©²²’Äñwt†i–€ Xý³&£ZP¶ƒ¨w “¦ˆ¡7;ÉY’üqÞ~ÝIvø±n> ØYÔÞÕŒüó°|�!aj*ë_ &DñŒÉh„€æƒ(ôËÓwÜqæã�?6’ä§/¿üÒÙÁïW@“ùýïÏrFFFLVP#\çô‰56-ÿ›úGñŒÉh„€¾ ŠAƒ'|Ææ¶{ï½—¡I²ˆÆqüñdz Àf4Uñstýõ×›yóæÅ$¼‡4îͯ M Úf·Ã9“Ñá ›Õù Ùä8šÌÌL#IqëÙgŸm5üß\å}Þy籂1#�¸tïÚk¯uOl®i`Jû�5�ÐÌ3Npã򼃯öˆ#Œ$ùéꫯ¶…Îx›Aþ†“gÏžmb!ôéxwçÔƒ…àëv8c2!  `¯�ÑôpRo}}½‘$›î¿ÿ~whŠ8Ÿ£N�:™+®¸‚;lbµ¤ E^£™×ŽçLF#TМѠ±ŽË¢�€‘$¿ ˜yf3†¬Î<óL“““‹ylÞ�šø›i2¡› ³á(š#Ž8ÂtíÚÕ,X°ÀH’M«W¯fˆÌf4€Ui?üðCL&L#Çèô΄ÀP Æ´ã“Ñ�¥£a8†>úôécMæJR04'�tÒOŒ&ŒÓü‹ ͦ½5a„}³Øh0ÿYaùQ;ž1�Pw°4xŒ†�Ôáþ‡Ë.»Ì¼øâ‹ÊÓH~!*î¦ÁÍÁ/|ÆÏO{ ¦;?Src´@F#ô�3†ÆÛ�òT®¿E?�ò4’¯ºuëÆÊ3‹ÑЀn¹å–v½ã³ÊQÜÊÝ+²bÔ?#£ ¶‚FÛ­æðÃ7Üq®$ÙÔ»woßÊ3À^›ºº:Ó^ÂdhöïÀü£ÉëbTÖ,£µf·ÆV}†Ð×;6ÌH’ßʆ©ìy”——·Û4€wß}—#”š&„Àf°"†çLF#”ÖLó&W࢟†�Ö’ä§1ÃLüŒ†&´sçNÓ‚¡1Ô‹ùf ®’æ0?†gLF#4·™ž† î¹çÛxwIb¡È¹çžÛ\åY»­_¸p!§à3ë4#Á:0>ögMF#4%`›-O¸‡—#×mI]I•gwÝu—³*™xKåSRRÚ#lÆ|ÆÎT¸ \ŠÀ Ð'†gLF#4lµ>·®íýè£�¬›%©W¯^δ}¸‹J¾þúëvééyä‘GB0¼pqK%h[AjÇ8k2¡~š� ÊÏh~ýë_sJÀ’%Kt«‘¬{_"óë¥a‚>ÚZ¼xqxmAn“¹T5±Lî8çMF#´u3h7.�bÂ÷é§ŸæxxIr«¸¸˜ ¾~FóÐCñ%šá»ž={† ZV‚m ”‚U`pÇ9k2¡¹ge äg6?ÿùÏ÷Ýj$É[€Û„Íh87鍊nŠªÑ„GáÌkjÌÜö€0·ƒœ1�P*ø cíl„›>}º‘$o"�½~Ó8Ê·ŽhÎ6 ‡É–‚l°À–Tm&£JE ¾™2g6oŽ5ÊúÐ�T€Û‹×hv=çœs8 Z·)lÒœ�?o'زÀf°|ÚAΘŒFè[�ç™@ܱöcŽ9†UŸIñ¦‹RfÛ¾ TVVšhèǬ¯g… d51«�1�Ð'`¨° ûi¸u�׌‰K’[»wïæl<š‹‡c�=– ¿ÑÙ½ýöÛKð]  d�5`t:c2! `ØB~fƒÑÜQ“™™i$É[ùuÍ5×ð…Ä{«�±2-Òª¨¨ß²SšÖ@Èi W;g2! å ÄßhØOÃx»ò4’M(çúd¯Ñüñ�äÖËf´_×�ùóç‡ÿŒ`7€|�¾ë€gLF#4äƒF?£ù¿ÿ÷ÿ²ŒõÉ'Ÿ4UUUF’ÜzõÕWÙOã1þ·�7Fü…°YéV™‘]  ï¸gMF#´Úy«{�m!ÚŸÿügöElÞ¼ÙH’[ï¿ÿ>ë£á猌Œˆç„N?ýôÝ,`!4šM`)èÞϘŒFè �aEã�Æ{þùç›þýûIrëïÿ»éÔ©ÓOŒæàƒ6?üðƒ‰¤ÆŽB9µ³Þ¢Á©8ëÀ#gd4B@=A*àˆu?PæÌ*¢'žxÂ444IrôÊ+¯ü$G&l>Ÿ}öYD{g|ðÁ×í»ä‚• Ç?o2¡ÆÍ|?“q'wo¸á“��m$ÉÑwÜÁI^£ /Ï{óÍ7M¤´nÝ:s '¸›‹kÁz0³ƒŸ1�Ð@°Ô5g6hÌ㲫Ï?ÿÜHβ¦Êûp^}ôшõÎôéÓ'\é6š ÈŠ“"�PzKá3ÄÆ9%àµ×^cõ�$õíÛ׺* ª¹¥5R½3·Ür‹ÁgÐY×Ü Á Ð7~ΚŒFhÍv[™³g<«Ïòóó�¤¡š¥2lf3ÌÉãg%š7oñá÷w>�5`˜GçLF#”æ¬ ðӜټ©µ>ÜWä=㺇0Qâ@W°øäùçŸ7øÃŒë³¸¬�³IÍ2! Ñ` ú ¦ò²úlРA¦¶¶ÖHÉ»‹æñÇç¾"»Ñð³ ¿”lÛ¶í€{g.¿üòpã°û³¸dÄaµ™ŒFh)(j®¯&Üœ÷Øc�™M›6)9Cf©©©æì³ÏfØÌÇh¦„·`П5bÄN¦ÀŸåþn©qxÆd4B@ÁVPêg4tPÃ]wÝŇ�Š’nˆ&§C<óÌ3æw¿û�uj³§ÿŠ�¾S§N5û!®¦èÖ­›ÁgÎýû6€µqÚ¤)£ê–�ÚŠ¨ Oìýæ›oÖ�’F|ð£Ì˜[W‘¯kÉhX@‚2gVŒ-\¸Ð´Qaƒ‘3\®æéŸY¾ŠÓs&£š v�€mFÂW¢Ÿ¦1<%`îܹÊÕ$‘rrr¸›È=ÛÌQ„0«‘›±V+>ôÐC²Ù†Ûç¨á×z ¬"1Âf2¡…hY ªAÈCçê.¸àޤ™4i\I‰­½{÷ò6sÆgpóªc2á _|1Mᥗ^2§�vZ#ò)õn£�ù°Bíå—_níg…?ï²Ë.ãŸå1š2�ççLF#4€J²�+Ðn¼ñF>€T�°âîÿÉ“'›«®ºÊ¹aìã°Ããmå»ï¾3Pø–B¡@?Vï^팛0 † fZ£””–4[Brù`pœ3�ÐFl®�o®LÔžyæ™ ©Œ7Δ——)aÄ>– ÐLðbáÞ¦ÉïÿÉ'Ÿlž}öY†ÕÁ”jQ,PÌŸËlpæ´„ÅZ26ޝAÈc2dS‚œ1�Є–Ö<7ÁðvÖ0Iüâ‹/†›ùØk!Å}•‡¨¾ñÆlÔEbÿßn3èþç÷ËÏX(਺º:tóÍ7ãç=!4ÎËËÊÊ2͈æ%—\Ÿï1š:°$�ΙŒFh›SéÓ¼Ù6ðaÈ"ß~×®]Ë7S)nÅ�ª 0×_=_$œ¿+?Ã<Ê»ï¾û“þ—Áƒ‡`L%žÏ >üðÃfÍ­W¯^æ¨£Ž²…ÍÊl¶™ŒFh ¨²T ÙŒÆ‰Å³Ç7&�¥øU à÷ñÄOäJoï÷ú�C1]»v5½{÷6^•””˜‹.º¨Ñ5ŸŒàe$„h¾ÕŠÅÅÅÌûYªÍÀÆÙÖ—–?ßvÛmÖÕÏÄßdjÀÈ$=c2! €Í Ç�lݺÕHñÕ¤‰²eÐtOp†§b[f[ónüùŸ|ò Ãd¸ÕØMÆN¡ÎZ�ÐàÍ:£aGùºuëŒbâž¹Tq‘LÁ oñ¥Í”û=V(77—{k`^­5šz°ÀèœÉh„€*|Œ†oÅéééFŠ��Ù�!CX†Üš;yOs@¾†ãaö¯Š��¹"Zk4¥:_Í £26l2Ô2{öl#utñÆÁü J‘y›qoÑìÔ©/ËÊÊhyÚk¯½ÆAœ­0šz�¡óEd4B@•6£ÁˆxŽÁ2´ŽßK£æLnÐÄm†yäcœï#Ãg‡~8›7t·Í˜1cxËmEž¦H[4‰ŒF¨;¨öªÉÎo< :ö^‰[Q‘›á÷ %ÌlÌt¿0œþù&//ϨæÍ›Ç�œ¼-ùM%X¥óEd4B@ƒA­ÅhøÀÂàDv}WTT©ãæf–/_ÎЊ7&s�›ùãÿh>úè# áÏá´læô3šz°LÔù"2! ñ>F÷ÖcŽ9ÆtëÖ�Ýj:fÈl÷îÝæ™gžaC%¦ì3à­y6® ˆ€X�ˆpjsF §Î‘ÑÍõ|XØ ˜XÆFE†Ð~Q¾¦™LAA�yÿý÷2sLƵæ�·üxD× ØÀf4N>(¼`®Î–�Ð(š»Ñçí˜IàgŸ}–›J‹©¸` · î –L†È¼ ztñ37³sçN)mÙ²Åv£q›[žw‹¦�ÑMhô1/ì͸öÚkÍ7ß|ÃRY©ÝÅ[Ìwß}Çp(mS• ¦4G|Cê®]»x£±àïBáAx2øp�­}Èh„€²­#hüaóß\À*§ÂÂB#µÛDfæÊ `î½÷^>ð¹ÐÌþ=bn-Òy5„Ny£�ÑØÂ¬!ü}rðyꥳµ�PnËFc/@§9gjeee)o=±eÆ føðáæ¡‡â�;fÜ9ïšæpM„Å»ÑðÏ aj@®Î•��Ñlñ �ù㞟Å�'cÇŽerÚHUaÝ2;þ»ví¾Ir‡ÿ¯~õ+ß‘ýhÎäôf¨=�†Å(¯–Ñ"£êvà!Qa{hµ!oÃB�|�e´ºÝD¦7fþüùÜÁ�„>С¯‰_kç!ïýžá¿³ÊìñÇç¯�–ñÙ�†áº¦¬ÑÙÚ‡ŒFè+°!�r<À<á³6‡ÒØ(ˆ·i>}ôQpÄ4aÏ~„ÊfΜÉ–¸!°z 7ë Õ_ÎCžFtà 7pJ@4oX(¥fxÎû÷Áß3„ÛÖ:[û�Ñ�a£ÁËn4m7ÎÙ›5›=/½ôRöq´>)-áVÈ™ehÀtn~0Oƒ¦Lº¼æškLNNމ¦¶oßN£Aâßv³ ajÀ÷:[û�ÑM<4‚|XDTñˆP Í9¾©K-ë½÷ÞãèÜXZSÈ•ÈßðmmÞ¼™};¶P+^0BW_}õ�­}Èh„€æƒõžînb<î¸ãëܹ3sm¼ÙÐh˜1ì"µ¬K.¹„7•¾Æüº}ôÑæÖ[o5Ó¦Mk�%' ¢Íú÷AÅYèŠ+®˜¤³µ�Ð*°„cµØˆ#|‡9¢l™ðàãƒÐùµÍÀ7óW_}U-‹UzŸñk‹†X tɯFÐÄôk˜žžÎ†QÛ÷/(�È}«óEd4B@)`7¡¼¹5'ÜÅÛÂ… ›}ë^¼x1g�¡¤–žær7øýùÐÄÛ.ûjb©ªª*³téRÎìê¨ æõ×_÷İéùçŸ7Æ cþ&ÆâänܶüŒ¦Åu¾ÜÈh„öÐä�Z$ž«›ªœ¦¹êª«8¾%íÝ»×L™2…CôVð×ÚŠPöJ£A؇9ˆX �öœx Ãc¥Ö'Ÿ|bª««;êMš9V'w¨Á¥³gÏæ-Ëf40 úË/¿¼ŸÎ‘Ñ-�0ÈTÂlœê0&ýÛbxò¡�7p&Šñ{Ñ`€ÓOCð2¹¹¹&ŸK“AŸ‡Á[wøïÂ\È—_~ÙúŠ-‰•ƒ¨.³ ªëÑ35Àè|Éh„€þ 2£ÁM¤¡¯°1p*ð�!Cbjk{íÚµáÜ ä§œr Cj(Ãe¥Þv¹¥“!¹ö÷·œwÞyüûàßÈ]ä˜ú[¶l™‘Z%†ðPg5 ÔâEãC£3&£¶:FƒD}}SÅßôÇŒ³¿ gVª�5ŠÉëË.»Œ‘Ø?عC%--ÍÄB¨xã›8þ­œV€¿A>ŠU\­û÷Jü1gÚÙŒE$•:_DF#4@ПÑèŒ��0 %ºùùùæÍ7ßd)4r> «aÔ} V@óïæSü;�œwé8fƒ5ìx—ZÖ;ï¼ã;%¦ƒ:_DF#”*@Tâ! u½ð ÜEñv³`ÁþžÈû°Á0VÓ�{öìÉ 7sQèûà´iܺ¸T¬å[�„ 8߆]„"u£ù7d4Bêi4ÀyøâAÁ’eLè�äm‚·Œ®�iÒ½wïÞ,RpÍårŠ8ZPöI¾âb;lø´ ûtò{¥:_DF#”BMsÉ8ÚdnQL4aÏ>ÿ�Þñú€]÷éÒB��„RpGÅÍкÙÅ»t¾ˆŒF¨Ôu› áM¿‰\†��Ë`is¢ «¦Ýí¼Á!ä˜ §T¯_¿ÞHÍöM±r“ ¬M¹ø m3:_2! ¾`¯c4x@ìA¾¢!œ?÷Üs9ˬ´´Ô$š²²²XÞl™0M�ÈÖÐÏæÅYw(·í¢¡qc­ÄV£3&£‚ ñ€Ø‹rß°ÑðFÓ¯_?Ž:IÄñûwß}·7ìÃÿcwØíŽ< §!KþKÏ`Ö¾“ºQ\±ÊèŒÉh„€æ‚ k˜fmx�Ê}9F¹ Æâp?�aBßéÒMã6”Ø…›Ÿíëǯ+>K3uƈŒFhôŒ³ìÌo£!”ùr Œ&aßê Ä)~ƒ?q£ãtÉ®©S§ú~ýÐÄÙÔª3Fd4B=4®bÒ´´ÊœqÆÌÑ ÿ%QßÈ9‡9û¬.Žß‘|§+øŽŸAž+\.¯]4DF#d4é Øk4x€ð!Œ]( [æ‹°‡jÚW°ä™³Ú8çM²6½r5·}�s¸Àd‚Ñ“Ñ-Ðà1¾|â‰'X]”¨Û+ï¿ÿ~¿‡%Ë�12ÇY‘ yÞ=üðö—³rz[x´‘Θ��Ñ4ºŒ†ó¿�Ìå˜ÿü1¡·W¢ŸÆ¯D—³Ï¦OŸ.gñæËÒf�õëóÞ€ÏÒ×FgLF#´=¡3çÁË Ç³fÍ⸑DÔš5kXôàìâ÷¼™söÙk¯½æù÷KsæÌá× ãgl7ÁŒ&Ÿ%m×܇ŒFÈh½Fã€æM˜Ü¹sgÂæ0­™Å0kžæ®»îâôi‰¢>ÿüs–6£:Ïó5ãm¸”�ÏÒ`£3&£Z Bn\�ò �a ó4‰ª7ÞxƒfÈ+XQæìYЦg(’°­pf¸/'5á²yåhˆŒFh¥�Ѱi«x¹�²®®Î$ª222Bï‡-±ÍλᆱÏ®¯fÁ1?ãùZñkˆ¡¤•ø-ÓuÆd4BåͶ ªÎ¸ø,55Õ$²Ð�Êðò4~͇üñââb#q )sWÞ°ÊÄù5D2\Ú¼ÌÑ#2!£±T�±´k8¦eÅŠ&Ñ…)Õ Ÿáái«¢â8UŸ1§ÅÛ��)Ólðõ MØ®3æ £š µî°Ù¡‡j®¸â V\%øüÜÜ\N«F¸Ð¶Ä‹á3är’}ë&‹BÐ{ä·U“¸nÇU UçLF#d4K@Ô€�ûÁŠÝþ|¸â!œ4 ˆ¨2ó Ÿ±ys×®]&™õý÷ß›.]º0éß £iÛÕO#£2šEž � ]&|_|ñEŽƒO�?>¼¬‹ÿ~[HèôÓO7ß~û­Ibqã*ÖHX Æf4¤ d&ñ“Ñ-En£qr4ØùÎ2ÖM›6™dPII Ã…¶ðà-;ò“uG g¾=ú裬6k�É�:P Ö%é“Ñ-…^£ì+éÖ­›Ù¸qcÒ$º*´ô‡ÎD»ì²ËÌ’%KLò‰SxËEsŒ†4‚ È=“ìŒÉh„€æƒPBÞpQ×®]i4É2‚eåÊ•\�€»oø jIÙS3lØ0‰´6ó§䂾ÉwÖd4B6óA=yºâÙȸjÕ*6l&ËTâÛn»Í·(ZömÛ¶-éÊšŸ|òI[Ý£! `ø"IΘŒFhÈ ^£Áì/VáFÃågÉ",;cQ€"²}MF��lùÞn‘¿j»ÉØCiI2¢FF#4 äÙj¢)�o÷(oæ�&IÄšÈÅX{EÐÐÉùo¯¾ú*‹’E˜uǦU„#a4Å`j‚Ÿ1�Ð ‹Ñ°OcFØ[²aÆdÊI0#ñËGpuªÓ8*?Y4{öl,J¿#h4¤ ÌNà3&£Jù£áž‘O<‘}4ɸa2--�Vxƒ·Þj:wîl[s�°›H{õêÅ�ÊÏøLà 2! )6£Ai3C%Ý»wçšd\UŒä·ßL/®º¾á†8.nxÏ>û,¿Ñ1RÒðŒÉh„€&€ò>HÏ;ï<Ó£G“žžn’L|‹Ÿ6mš9å”SlãVøßðc|Ó¯¬¬LtÓe?^>¢h4¤ ¬H°3&£ãÜh¼Ò£�>ÚÜtÓM\åÌb€$Syy9K™m›7�[ß­·ÞÊÞ›¯8ãR„S£l4¤¬M 3&£v‚�e»$‘‹°UX%CȈk¬�=öXk©3ãÓƒð"·N&ª�‡â€UÞì¢n4¤6�æ£Éh„€ƒ­£qJœYa•��m’PlÌÄ­Åo¾ÿ;–¢±©5Qµ}ûvæë¢f2vê@N‚ž¹À§`0øŒ#Á0¾‰±Æ€oÁ7àkðIÜ�P°ÉÇhø{ÜqÇqXŠ!Ã>}úp ¦åVÃÿ†=6¼õ!W“ˆ¹*æèðhg£!õ`cŸ­¾` ˜ �Õ l›Á° ìµ @!€R°A%¨µ ®‰ZP *Aì% ä‚™Æh„€2íFC¸yò‰'žà’$Ý‘ÏÐo5ö[§:'`_ ¿ç“'O6‡rH Œ†4€Ü89GŸ‚oÁ �Öƒm`·cŠA•ëß[íú±|� ²šXÒÁ*° ìPÊ �‰†S Áf°LŽ©Ñ­¶ ólÔ{ùå—“u<>«®pcáÜ𬫞1I §0OõùçŸG·´¹eÁÖvfz�!`H«]ÆRJÁÏ�¤Úu#ih¡ "ÐDÈ9 ˆˌ�T°,‹À<0·‰)à“�ЖM�os"öÄ3Œ”¬JMM5_|1+Íš¶‰Åh 5E/,vÀŠ„˜˜Œw[gŒÎÇ?Á·`>È;A1ºÌ£4‚P„Ø À.°Õn>4»y`:˜¦‚™`.˜ F€¯À'íþEšŒF°¾­ŸsÎ9|ظk&eÚóÏ?Ï’oÛ­zœ$ðÌ3Ïp’B"å¨^xáÎ}‹±Ñ8ÀçQ< _€Y`ØÊA�3ݼ ‚@+(Û]¦“‚¹Æ€žM¦ùi»?d„€þ�Y�í!‚ ÃBN o‹rõÕWû¾ÝwêÔ‰ÓP<@SN�°!çÝ¡‡¦ƒ ©KÀ§ð™ÿÌ«@.È¥ Ô8¡­ÑJ,¦RÊAГü·åsVƒE`.˜†‚þàë˜�xKÚ"hVä„äØO‘ì8p sV)ZóY¿ÿýïÍÝwßÍYi‰ ŠŠ –oãß{£±¯HsÁL�¦‚ð˜€å l°×ò u Á^�Á”ºnTõ–Ÿ_æ2›- lÙ` X¦€¡D¡3+ú à¡aÝ*‰ª3¾ÕnÞ¼Ù$¹h¶>ø €ùä´X ýÚk¯™²²2çâ¿·´§6Ç�ZP*@°‰ —‘Ô;¹“8 Ör“ ºþþåÎÍÆR^‚–ÜÎ�ÒÀd0 ˆÉCFä²ùà°7%²¼wÙ²eÆH&33“…È_Y¿^31܈ÕÇq_PXXh.½ôÒ蛌¨,{vQ ÊA¥õ÷°—Hï9`µ«òlhL2B\sÍ5p{ñí¥9þøã9Ž…½4É-šÇ”)Sø5AæooÍwÜa~üñGÏÚ´iÇEÕhD�¥·¦¶å©µÜnöZ –»BŒ£cò�âºë®ëƒdv# öe_ì%)..6M²äÛÞ_Â| ChO?ý4óñª5kÖpÖ[+Œ¦Ô€JPay ]a¯Jk¨Ï¿Ï¦ÄS©¶ ä¹~|7ÈóÁt0ôˆÉCF¼}pØa‡ÕûÅâ‘“`åÙ®]»Œ‘¨�;w²w•y¾ùš“O>Ù|õÕW&^µxñâf§¸r Á'ô#"ˆýëíä¡`=È™ LãÁD02¦“„¸ýöÛ{`q=Âg¾¡ ~ýúy*ÏB›>}:ó14ÿòð¸]7uêÔf§¸ÞÈË- èí NÆ œJ3âLØî*mNÿìp³Î„8묳ªö³ŸÙ(|«:t¨r4–>,‡ãÚkØÃŽ¬Ú‹ÇÞÜÆš]xfY\VŠ@XJ¢Rõ%ê=fù ¤ƒé ‡œÞ,úeÊüG¢*�'¥ŸjÇŽæ±Ç3úÓŸ˜›±Mx>æ˜cÌÌ™3ãîÆÖ³gOç¶æ‹OWû6�v:UR"*4€<�€зC¯ 2Ÿ‡ ×/_¾ÜHöò Aƒ"cèÑBcZiii\MnF?PK›5‰%‡°d�M 8"·Ñh1™° Lý;ü>!Î8㌠4nZ$^x!“ß’]óæÍ3÷Üs�ïÚgÜt¸Óeܸqq5ßíÑGõ˜§KX§d5QxÀ…Jþ—�=žÆÎ 0|‹Ï„€º#<¶y[è‡ ›þ‹½¤‚‚‚ð*æcši|¥ÅÍäfü}-ãgìX†_æ¸n5|HŠVS‚ €Ý`;Ø�oãnæÐGxH–X&ó�ö¦›nbâ[òŸõîÝ›¹,~Ýì·–;Oš4)nŒæ–[ná‹Æ~M)ØäºÕl‘y´Š*W7ÿ:°¬‹À 0À9³qg4B@ƒ>øà½~#U0(RFÓ¼89áÔSOmv.šb¹­4 š.Në¾öÚk�O[Íf�gÈcIræRZÆÒp¹Æµ°l˜à>«qk4B@_#¿Pí—ÈÆ ÉæŸIÌ¿ D¼¹ÃP]»v�‡¹q왺ä’KZk2ÄgŸJ9hˆÓÜÈ´wÀq�×Ûÿhè_†03—›¤ŒI9q�iJ.† ŽÇa¦:±ŽLT;nÌ–d¶À±Ð–e[D+¦ûýôŒæÎ��]iùÞÝ=gæ×LZå¾Ï}°‚Î8¶aVÀ&ØÍ x�ÕÁ^Xé>›Áƒ¸¦ÃBØ{ ÂahðøµB›gš¶º {”žéöà]Btê  ÊaÁð:L‡'ø¾IФ/Òáñ¾÷½OÛ%MhÆŽ9sæ8¸+ŒÛ¥EŠRÝ\Ao ¨¨¨Ð×3¦¸ä¶Ñf³kñ2þ�¦Ç#8qx½ xDjÜ«gÒ_¡1ˆEP­Ðà�ÝЭÐõP ž…D›`,†Yð¢_"Dlå�'œãŽ;ιå–[Ôîj9èÈ“s»{Æ«i¸Ík×® ôgº}ûv57ä©Ðô‡/KÓqvÀóîó˜y¡1ˆP�0˜EÂAè‡>èõ܈Ú<­Œµ°¶A,‚BÐm&¥aOzD\ÀÄ»ZwÍ~&zwÖÔ©Sµ‰Òm‡Ë.»Lö.¶¥‘“ÁÉ'Ÿœ�BÓõP=‚Ó•bÛ ‹ÝgÑ¡±[K9´˜Ý¸„©º  9Šs³L!MYÂbÆŒê4së21rÚi§9×]w] Ý°|ðA×¹9KÅfZ¡ÑCô�q“Ù+a,…•°Å#8 0˜ÄM¦ 3x¶šÐ�Àr¨‚PÊò ÆôAôŽÒ7JÿX"­ël„ Nii©)KXÊ çf5KÄ{üñÎĉ�Ç;A�;î¸C{ˆ²Th† zœ¤÷Ãh€Ö‚ÓëaÀ#ð,†•°J¡1Ánº,ÊÐùjBCÀVh„^†`Á‚F5…Ù³g›º„mÙ¼õÖ[c0�Œ|Küîw¿hñ¾ùæ›õ’‘¥Bò¸ëï¡*¡Z¡ Z¡ C<·ÃP³`!,‡� ÎÊ´¥ã4f´÷öbDkÉÅÉ7Ì}Ø„†Ã8δ™ûyjpsõêÕA­=©¶lš&x 6Âr(‚�P2áU(€Çàv¸žƒ/¨�óë�WLhÒñ$l†£Á/îQlhÂF jVZåìhÆÁ»Þõ.çÓŸþ´3oÞ<êA\€¡¦»‹&`bÓÝÐ?Æ�~öÀr+aw�Ù k`¶GLž…ÛG¹&Áýð@!¬M 9`¿ M æÀ~÷bžh†�]t‘n5MMM¦2DUU•ó�o|#VÓÉp'guªÑÄn>ý™®¾új‡µAš^hôÐáß7ÁAØ ËÃØèIŸ•Œ²^�ûá>x àn¸=S –C%ôÅ)’KLhR/2ÕÐÃFö§Ï¾ð…/ÈjEoáyåååZŸ�È!Ì Ñmqîèèp‚Ì÷èKËvЄ¦ZÂĦBaÿü”AQ˜Ðl…J¨÷ˆÍx~ à¯ð¬›>‹Ì4X »¡1ÎN´ÖÔú˜™Ðeçv=#š!äÙgŸ-ƒÍúúz'ßcëÖ­ñšNz?KMÝÓâÈ…rˆŸ»14`Bî/ÖG¡1`‹GdÖB)4ÁtC)l€×áçð5ø>L†˜&0wÃdxfÃ8”ÀËt¬6¡IÄšìµ7¢¥|>ö±�9<ð@¾{Ÿ©^õÙÏ~6¡C˜T¤¦îò“Ÿ8eee�¬?]~ùåÄ Í 4B�çVR�a†2Ø›¡<ü!¨ƒ=° †_Ã5p'Œò L 3Û�Ë`Ô&xÎõC9üË„& ˆ‡á kÝ`äB­FN_ûÚל5kÖ8y*–/^¼XûZ<ˆ5C�GžgA‹ÊÊJwåAo3�aÓçYïÑ—âN¶z85£¥À!8,qs×�ˆž5:ÔA¡ M’“áuØµÐæí‰7‚Åx-ºÿøÇ�—^z)ߺÐÔþ»råJçúë¯OªŽqì±Ç:¿þõ¯�£G�n‰+’é‡Ö8…«1ÍÐ CÐ�Ðçþ5x­lRÄtÀFšÔ»,‡Ýž\g§w�ªá/ãM¸_qÅNQQ‘“oÑÙÙ©Û+°=�•†¤»K»_‚Ï?ÿ¼sÊ)§d«ÐtC(¡© k™ o@[QJó­­Ì„&½)¶W`ì„ZhòW|Œ±º§N<ñDçG?ú‘º§ò­!€Ýÿjùæ³H˜“N:ÉyñÅu+¼ï¾ûdü™•B™hƒ®1„¦Ì³¦/ú"4—¾ ˜ã€{Lh2ßÅVEP µÐíî¶ÍŒ/G3±¡F¡4 ¾_r#ΧhkkÓ×M'^Ré³»ï¾;(µ. Þí·ßîsÌ1Ašè…žžñn�8ô�!4µPçYÇÜëYÅÑ �aôdhÕAL3¡ ij°vxv®´º7"kBH˜qR@²¨™2eŠÓÚÚšW‹ÏæÏŸ¯VàDk589k±&��©?ýáPZ4@BîЇà„bHuµDi�®‡ºQA½g@´=Ã+æ›Ðd Ä$xÖB9„&AOø¾#v±ÁKëþýïëÐ̓РÖ< Ûµ°óE­â�iݾæškdü`¡1ŠÍ „bh h‚F8åP…28�~q퀢ÜKÑ͇b¨ôôéw�['²N4MË3’7mÏK—.•À&j×ÂÎX.Y²Ä @èûFÛvRMi*Ž7¥9}Õ�ðVØîšƒpö‡ ÎÌ‚C°Ó„& €Ù°ÞS@l†N½™Øè Á‚*˜çzꬠ À9묳ž¢ÿŸÿùŸÏ4uz!zzz4ˆ¼îͤÙM]¥ö¥ÏÛ}V2Je˜°ÕÂþQê¡É‡ç¯öÁs&4y ñ̆ÍP­Ð›‡b£¶gÞÒ3`ƒïï¡Ì,M¢»õÝf ¦ðµšÁÍ@48àJíÊßéö m–· -:>íØj‡*XdBcâŸP« Z †rSh\±‘'æ“9›FêEvúÊɶ8Ë»¶¶Öñ9ä&MúÓ‡¾ [3pÇç5(CÐ5°Ú„ƈ 1Š=6ãjÏÎ1±‘q$ó&9w³A<ÕLk·×,á: éF¹ ø{÷îukNy%4‘[˜›  ` \‚L3äYò¶6¡1Æ…xVÁ>h€�+>Y.6W]u•ö·ä’UÍ®]»TËpßüG¿Æ?øAçÙgŸ• ûÝEÇöÏ`®€4ÖAÃÁP ³9-0ì®Î(ƒ©&4FܯÃf¨ ªCBŒ«‹ÕYU\\œ®¢¼êpJKK•Òâ•®™‰Áï~÷;¹"$)2‚‘ ˜§ñ} ³PÕŒ¨UhºÒ$^^“̶n�£tðO ¹Í5ð„ �‘4Äl(†Z�ñ_Ðņ:†rÿëׯOiúª®®ÎÙ°aƒn ªfÂí@ÎȵuóHu̘1Cÿîaœ+µl#ÖÙ,4^ƒÌ(�ýÐ7N ¥J¡Þûr•E ÁØáy#7¡®†2è ’Øp@ë œ.3uœÝrË-#þcªé´··'+2ª=ðÀÌ$]—N¡Ô�†9ßæ�Ø'¤Ô i¼lÞ®nõ_ oAÏ8õœ}PUÐ ÃYH/TÂÒl¯ÑÖ`°zýn ÿJ³Ñ  f:ÏÔ-UXX¨ÁP:Ã$0)(Ž«CîÓŸþ´~Ï4 ¦Rˆ—_~¹R}>…f�»lvPVUðtŽÓ\ %pÀý±ÙG'ì†MhŒ¬‡Ø�™4š[ÜWJŒÔ’R;4ÈþeÚ´i#]Zjwîèè�0¤è6 _‡S5*ðû§+eæíÆ“ÐìÞ½Ûñ+Ö­[çºdyê¬%ìVSãÔhÊa¿wÏM–Ñ UðºÍÑ9q0M ®À0£í•W^y¥ºÐ¨»Œì×WZìèÑ£j[NSšI¿þ_ÿúW¥Ì܃7½B#�±²²2¿†5ÕUwá…&ºò@ìàí€fhÇe¹Õ³Æ¹3KSfU°À,hŒœ‚(ŒÕ"=‘š ƒ�ª¹üö·¿uî»ï>gÍš5NCCC&¼ÀÔ¥öä“Oêà�æÌœŽ´à¥—^ê”””8>„R�´‚Ë ‡?O¾jA(KE¦ªaž™j9 q8U"ÃA«n'nš�ÇNF‡=©¤Œ®@È´G‡Î5·Í7CHhðióuX“9¥ì£ Êᕜ]`ÄÆ�æhN9å;Pq†¹ŽŒ§�ª««u‹:í´ÓܺL¦øØÇ>&3PŸÖh@ö˜cŽÉ¡1Ú`<‘Óûh ƒX4Vú,†N+ eRä×â¯É“'«½·³³Óñ!ÔPð�üCq·ó*sè÷%EèÛ Í7¿ùMç=ïyO.ÍP L/Ôê¼XålÄÊhó 1X®hö…i{Ýßxã µ%û Iʹ˜d)meŽçfwÉ%—8Ë—/÷m††Vñ¤¿öÔ+š¼öý9F ìsë1y"4†­¨ÞéF3Þ`"éèx›©‹Œ/?—¢©.³téRç‡?ü¡Z™S+2± ÍE]$ ?ãRÍÐð�íBÓ¡™VØ OêÌ¡1 bU¤™ƒ±Šý¼-«}–Z€:œöï߯n'?�“�µ¡T¬evI V¥N»ùóç;>„vú°VÁu?Èò9šábÃ"÷ùËu¡1 b2,�Gd¨wÈ' 7eÍÂPðVª*AºN©;Y·.ã—М{î¹ÎÌ™3?‚ßW©LnœÁ#;á1=ù 4†A¼{#ÍD¹ÅÈè’”ŒÖ/\¸P÷ b'3,fÜ”‘�B#÷ƒ—_~Ù—š‡zȽÑù.4FTy˜s^h ƒx6CK4{Ž-ËþÃÂEV1,9‹T‡ñ³.#3,mÜN«T‘h›7½>«L]~Þ„Æ„n¨ƒÕîó—ëBcÄ,(†¦±¼ÍÂZ–UP§Ø¯‰~ê‘ê0¾×eX™¬Ž7vפ|^&Ávo­¢f=µgj/çûd¡é€VÌÑn²jØ �èùËu¡1 b”B[Œ7ñ¶·½MmÒd*.k¢?€qàÀm’d%³Ç×Ë¡9õÔS�Ç<ã7¿ŠŠ 窫®RšsŒfŽ M#tæØ-æ(lƒiî3˜«BcÄ$X Ð>ô6ž?Ù 'œ ÂìÙ³c]�ìÛP& Þ¡Ì �RWÔJ2~Ä…a¼ù!ý;ðSh† 'Gn4C‚½Pà>‡¹*4†AÜÛc-ð{ßp1œ”u oÃZ6ÖÜÜœ:Läâ¿n ÿuûâë”а¾ZéF>ÃL¯�ý ßÏ1…/:«Ñ¤F`JáU÷9Ì¡1ÌB¦5ÆCQ7½yc£Ùk¯½¦UÇÁªÃD5k–¡Ói–™„ï›Yï¹ç'Ã!Wæ›Æºá©+ï]ïzWâ"cÓ ûa¶û æ‡Ðfˆ9þ-†>ue�yæ™2½$=6â¦IúÖÖÖdlû3þÖþÝï~×-þPhTŒÿç?ÿ™ñÆn£ê¤‘cÌôè;ßùNšø¦ ªa±ûüåºÐñì�䞣î¢ZÆÕW_­Û ^`êÖ€1¸)²È+ŠÕUÅaêÿ(4ºiÝvÛm¿é1K¤F„üéLhb§ê`�ûüåƒÐ1öAO„CP‡0¹xµÙ~þóŸ—U …sÝ^ÚÚÚt(e[TUU9×_½sñŧݑ9É• êúbž%ÓóDrF`›i,;ƒLhÆgš¡ØmUΡ1 â©hSýŒj/¤Ç´tŒÖ_í„9xð  èY½õÖ[��üãîÛx:IvÛ(©)Ý3Ú&ú«_ý*C«&4Ð oÁt÷ÙË¡1 âa(†vöB^~hćì3ŸùÌÈ^~¥ÇöîÝ«({Òc‘=Ì&Mš¤º"ê£ÈÄ.4Ü&µY4“ÑØØ¨ÚUÂëLhú¡{wöç•Ð1VASøl …þ~v hmñ”)STiooÏÆY¸¥ŠÜ¡¯¸â ·Ã,K„F.Ò ½T°‡&¾Ÿ‰M4Ã6xÑû¼å¥Ð±ê` ,]Öÿ¡}¨—µÅòת­­Õ-&ËC럹•É)š¹”,·Ø®¦… †6z^z饱­0¡‚6Øê´Ìk¡1 ¢Ê"ÿ™…é9Ô õV;00àdyèkxýõ×µ’˜†¯}™Ø…F�~¤±2)ørÔ¦Qbüõ&4}PKáÁÑgÌ„Æ0ˆÕÐÃáp¸ôÑÖª�Ž9p“QºoÞ¼yöº¸"“EB#ÇkJ5›©X°`�„fÌÏË„¦*`†çù2¡1 â%¨ˆäº<ÚJ«áKV)«³,ËEF›)M¸“ôAd’Ú¯µ»§¨¨ÈÉT¬]»V+¤}ùL‚ϴ˜êy¾Lh ƒ˜ EãÀSKë÷¾÷=Õh²mÓë_FúO"Ccƒ¦Û(2"–9êfÿ ¥2µNû#ùˆÝh Œ8 `Jäg̈́ưÚLe,Ì�5ýç�w:7nT1=KBâøÜsÏiyoåºø&2É �8í´Ó4`Z__Ÿ1¡æeÃÓ &6}P oFyÆLh ƒXÍ1rJ5½ï}ïs>úÑ�:?ýéOU·á’Ý?¶úAÍPcR 3+�ƒ&2ƒÐ“ˆÐpË”eÿ’%K2â#ÇÍIC­n¸ M”AáØÏ™ �a; 7ŽNÐy$‡^¬ÿU ¾üòËG¬hT þþ÷¿ïŒ´Bßu×]Î3Ï<ãü÷¿ÿUk,ŒÔ”ë_¿~½nEüµ,kÒ%PÕÕÕ#¶)2=öØcƒX“é„–„F‚I�IÆ¥l¾ÌH}‹†ùÀYúL­ËÅðÔϘ �a³¡†âšðzõ‰‡Ÿ:¢˜óÐ&íÃѰ¡ 9•’Ák,•¾\³‰'ê0æÏÔTYš×Åùk_ûš³lÙ²LÔjÔå†hçó,Í �Õ09¶g̈́ư´YKøç”œŸ÷ìÙ“ŠÍ˜ļòÊ+eõä¢?ô@ 'z«9ï¼ó´2€[M&ê4²¾¡1ÓgýP‹bx¾Lh ƒx¶†¯ˆ�"Ó­ƒ[JÚçM¸Éâ±I´† k äê*ã0 Ö ftúP̓S¥!ù Ò¾“fîܹªuåYú¬ÊãØviBcD!”B_”=3J˰¶W‡?®ÆîÖÄ4"q`â]i¯X£¿¿_{dn¿ýv­^ÆøSâèï-&y⸠ÊM›[M&:Ðô{ÐX‘Oé³Óq{éÕYFq_µŽqìGôï¹ý$StWWÛUW]¥™—hó:¦Õé¶cÇŽ�QšìŒ3Î�8bÏâƒÀø%4n­FÍÛ·o·î³ï‹I°cBcÄb¨�t¸±ØJ=ÙÊûãÒ¬�޲±n zÃåÍZ‡>ã1óøˆ‘n�Û�9ÄôÖ|ÿý÷ëð¤­�7t‘�t«éíý'?ù‰sÙe—I`øñ_Reþ �¾öóÏ?ß™9sfFZ�W¬X¡ß/‡Ógƒp8‰zŒ �aË¡>Âb3Þ×]w�³iÓ&‰ ˆþð‡Î1Çí°Ð-ˆ./ÝD)½a#(‘n=JÉ]{íµš4§í8â¯GªN‡³"ú½IáI|U§¢'‘ò̡߫á3P«ù-·Üâ4559é~¹^óýÍE¡é‡ê$çcLh ƒ(‚C0ìi—ÕôÿÎ�;•¢"}¥4ËÎÔÅ­N@G)!Ý„ðGÓ&ÈðËMF›iÉÕÁˆ˜DMwq�¹mÑ4 DJÝù.2þ �{ Tç^YY™C¤»)@Ë⸑æšÐôÂ>x.ÉgÌ„Æ0ˆ•‘Rg#‚ñ¹Ï}N°{÷n·‹‰ÿ—ÐDÛ�¨¦óÐC¹7 ÄD‡_¸ }ò“Ÿt&Ož,ñúÏþã|ðƒL6ßï¿Èø/4šb_Œºö2ÜxeçÃ-8W„¦v¦ÖÚ߄ưM5 Â0R0#ûY”ªš3gŽÖ#šÜçŸEÝ\ØY#“GDF›+ÿô§?… “Ò]ÔpôãFb×®]:IŸ™À$%4îL�>ÓLŽz¹à6š Ÿw;lMá3fBcÄ<¨ŒÐÞ¬7cÞTUlÇrD…ø•+Wê¦Í' Ò�§  Àõ<£`¯T[xGîɪá°ÛF–4Ô\$T&2É ö0ºid*~øa½Ldñg>�°6=Ïš �a›Û¡3ÒÇÁ¯vfºÇtS¡ãlÌ|o&4†YÐ KŸ‰ÑB¼:É`¼î" Æšn?šÐǪ$|ÈS©9 ¦C b˜Ô�X·�Ñõ ¸x§ÄKΑ9‹ÓøŒ™Ðñ:ì€P’‡žfhÝj¦L™â<þøãúë%±A€”rá€2�I�Øè{ÀmQ©ÎLÝnŠ4|™xÎLh ƒX µÐ�ä¡§Î3vðë¶2aÂýµu’%Gœíͪ«577; Ý ˜‹J:}–a‘™—¡gÌ„Æ0ˆÂ¨�6LâàÓaƒàèÆB*Ç&sB#a¿ûî»Õd‘ÁP‡á5×\“ôðf†DæÌÎðsfBcÄ$xÊ¡BÐMt2+¶É1F u¾üòËá4™ÞLºÖ–‘›Œ‰ŒÿBcÄØåPãž^¯ð˜¸JhÔ°AQ^]`>„\ðœ3‘‰à îƒWal…Zhçp2qÉ 1ÌÏÈ3޹% Ëú555Îg?ûÙ„ë4H—Íʾçτưnµ-¸´Â�‰‹¿bì’ܸU(mæChvê?øAÂ;j¬&à fC9o±]Ã&.éc¬Ù^µ/ˆÃ>™:‹D ô×1„ W[[[�òòrgÆŒrצ$ ßO7]6'7ž7Ãn7µÐÌ�ѯC1-¢bDÙH*“Rú˜bÔ�[öBO>ù¤–ÛQÌéX“›Ã_þò™ªÞpà #k±eŽúãÿØùö·¿-goÖ?è÷Ä­[· ‰Ê�ûœsΑÐDdês¤…Ù„Æ0ˆiP�"Âþ{�t�ë² ™%„#æµ×Ë—/w~÷»ßI$ðž“-ƒ³ º×­ÊJƒ�j@0r{ÿïÿý?ýÞÁ\zæ.,›Ÿ;Ï™ �a"³CÈíJ3ÒFxKóÅ_ìüú׿Žé6ƒÈÈ‘k ùØ!"¹ÖÀ1Gaa<_&4†á™»iϬÀƒRíò¹ùæ›U/§ž¢› ?V5n%¹&2CИÞe&4†A< ÅÐä�Àž�j#ÔSÆ–¡©þ (b 2)1yÏ�vÚiªûd¡È4òÜxÎLh [–vȭþŠÍè’:gâĉZB6Ö~ŒNUÀGP"zÔ!@Z}ã�7:_ÿú×ÇýÞØ I˜Î>ûl­� Žã—дÀê,~¶Lh ƒ˜æòløŽ»¤Nƒ’‹-r"„|Ïž}öY‰H¤š ·"¥ß-££†£5Ûì!Š:|É?×íçK_ú’:Ôþñ�¨µ™®3 Y†E¦6ÁÃYú|™Ðñì‡6‚'6§žzª„‚[�·…YËç m>åÆÑQ€Ô›Z˜×­[ç¦ß0ÉTƒ7�h»‡t;â–¤”\IIÉÈÏ—˜üñºídHdz  žÍÒçË„Æ0ˆ)PìT™Á c›~í…éëëÓÐ% É´Á”ú�æ\"ÝNŽ=öXÝD"­~~ä‘GœN8!âm—h5„·T¿ð º!`™™¨ƒ³ø3¡1 btÁ°lHYõõ«_íž7ožFë2Z„öŽw¼#üÀל̥—^*qpƒ_'¢I&·¥Õ #k^qÅJçe`^¦ –dñóeBcÄBh€a#; e9ÄÒ¹CO?ýt÷Ô©Su[¡:â¡O*Mƒžûöís"Eii©sÑE…ÿ<­øæ7¿éTUU9áQ\\¬ß“&�t‹Lì…'²ø3¡1 b ô°‘U5›k•·p³¨¥“l0ZÇ…{YË444DucÆ:¢@ýüç?w:::œðX¹r¥ó½ï}OB“fˆ6x3‹Ÿ/à f@9 À°7ƒÐ]‚vè€î }¦½ˆÉAÒi�QÖ hh“n1Õr"EQQ‘j;‘ê:Ôg"xΚ5K©3nQéþlK`Z?c&4†A,…·ÆÈ4† :¡êa?ì�mP [`Ó(ÛaÔ@+ ø`]£6çóÎ;Ï!Å&¡‰`_£Â>ÝláB£®²?ýéOΑ#GO¨áÑGi·tÖõrgú߄ư-›[¡†…ÝNº¡á”ÂzxfÁ30)ÆÏ÷ X ;½F¤éÆ{£ADäÜLÓ€fm¼AZL.ï}ï{#¦Î®½öZ§¶¶ÖñÆš5kœOúÓ;$¤éÐlË‘™à ^…mPŸíÍ1i‡&¨ƒrØoÂKðX>çW`4à §ÒjÆækêëëÃ<µ>w€Hž)´2šn5ÍÞ 2[©ç¼8úßKSE¦2wSf&4†ÙÎ쀣Л¥é­~Oͤšàì‡bXs`š_oËÄ*¨…¾ u¨ °" wdçÌÆ�Õ}öøã�k}À—¿üeç”SN‰: Ã�~ÜôéÓ÷0ì¹pô6·C¾íP�ã"cBcÄ|ØåÐâó-gн…tCZ¡Â~Øî‘éðXÀ?ãØ“A«Ÿ>§“Y âÑ1²Ÿæ¬³ÎRרXÓý#3;ÌÊÔŒþ÷°¤iMD´@9äɳfBcÄ#ð*,†MPG º wz„ÄAtA'„Fiƒfh„z8•PÛa=¬„E0 ^‚'aR}Ækàˆ_Í1[öBWš^8ú¡Ê`>ÜgÏž �aD²®¹„Ç¢ðà¸â`-æyfÿÓ�° ^�)iüŒ Ã0FÅx=´äøLS/‚µ>x—&4†a¯@In3 Á>X S}þœ Ã0ˆ—`„ ?‹o/�P ³Ø a˜Ð†A<›¡:šV€^A3T@ÂSö}4¡1 #K ž€"¨‚fo'X‚Aè‡�˜‚ (†5°fÀý9ô™›Ð†aÏÀØ{`?Ô@†Fh‚Vh‡Ð(Ð ÍЇ¡j Ê`7lƒµ°^‚§`rî}–Æÿem?ptIEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/rory-flat.png0000644000175100017510000012405415224505336024533 0ustar runnerrunner‰PNG  IHDR’š%ÑÅ—§óIDATxÚìÀ�€ ý©©˜]{’dÛÂ(œcÛ¶m³Y5¶mÛ¶mÛ¶mÛ¶mÕyëÝȱ”YèÙ+â ^fOœ?цåž>¡ø…/M’$I’~2!‘¥Ð�1 ³0õQÞˆ� š$I’ôï¦?iÄB!ôÀÜÇKØ ¾ážâ"V¢/¢ ²!=Ò!5R b $|k’$I’�xUå1gðê/½Â3<Ác<Â=\Ã)lÃT4E„Ð$I’$—�°(ˆ18�7PrëÑiH“$I’œv@"¢>¶â”“¹‰ù¨€ÎóQ_’$IÄr`5ÞB9¹78�>H®I’$¹z½k[¢(ú£2"Ã�H$D?Ü�rAgPËÕ_yI’$CR ð�pë1�áŽ(ƒ ðmçÑðÿ†8¨‚y¸” {ŽQˆ£I’$¹àˆøÅ ¨Ÿx„38€Õˆº(‹Â(س–%siÏ4IÝÒ%ðÒ¿U4Cg @ÔE ”Ô•F Ôƒ)‘QµPM0S±ðʇ9€’®I’$¹Ø˜ô…úoñÏt·š”ÊqÎ’)ñÝï<%¼Å+¼þÄ;Øð×ðÏñïtêr Ð …�ÑáG“$Irâ!ñÂS¨¿ÅS‰ª^ £òøÛU¼ÃcÜÃAtBøÖ$I’œpHBb!”ºU÷Vå¼ÓÊë-ö¡"›øgÁ?""9R!¼&I’ô‹H\ƒú[}Ðµš—ª”'½ò̘HFÀX¯° nðoÀÏÝÂ"+Zc Žànc*à§ÿ.I’dHüa0”ú GM‹ªU(³ò’11à tCÄ?øY@,ÁìÆ}¨ïxŒŠš$IÒ/0)pÊ(=“*ù2˜s˜Š7˜ˆðÿ�Ÿi Ftx¢=à ^Bý¢e¬I’$ýdH|£)^BBÍUÊ3µ9‡©x��èŽ nézTÍ—1Oƒq¹öéÐ Ëpæ/¡â ²j’$I¿0&!°Ê(}йª§ªš?£²fIbÎ�*ÞydHø²¼5Ý�.Õ¼®sÝ/á&”�Æûðo%’$5+ïaѾó×úB„FPøúdL â1”‘z¡y™œª`ŽäÊÍÐCTxgJ¬ªò ±[ o†›ëmŽ3ˆ£I’äÒCá¡��¥Ñ£°;qpó1 £1�Ðc±Û° CP¬iyïð=jþ÷N}*”Ñúèc’'Kð*KåÍšTÕ-šEõ`D¸Æfº… š$I.5þ™Qã°WðÏñÊ /°#ÛW±ŒèUËúܬC©^±¬òÛ\(î–Jµ.Ÿ[¿®¦»�´š$IN?A‘¥0‡q6(;zÓ¹ºå­Y‡ƒUk:ƒ?dÉœØV9oz[—ª^ªOóGDw±4I’œv@â¢)Vã:^C9R›JÅS‰2ëW§*žªXž‘aøEs§´ñzðuÏZ›þ=Ä^f!ˆ&I’sÕ¼‚õÿO •q6(gѼ‚·êZÃbÚÁÄ�´j[ÑM>¾ÿÆ÷¾-©–årsíòØo@€·h¢I’äøN­è«[½¢A�d¨Å�½Ï œQ»*ú�ÀÜ�ïIeL~À ŒˆjP<›~íìî6rk’$Ù§ËÛGøC(„¹ºsTô½‹zd^5¡U±ãZ–Ó¹Jý¾MJŽo^ÁrÉ€'ӵ੤{MóªúŲññ=±ŒÆ7¸!_¶¤ªY霎àbk’$™:¾ %1ó1ëÊŽ‘›�¯émÞ�†ÏVOlýlTÇʯz6,®šêµ+èXÕbúAÅû~U³P¦¯“K^g1"Él-Êär܈KB“$É”'�¨È�a8ŽWø{gWšDáuwww÷‰+Nˆ1à Qâ!îF qw÷ÑŒm\ÆÝÝ]“�ÚoîéO/�¤ÝHòþsîyMÍë×IÝW÷VÕoþxæž¶|R†-Ÿœi‰ êUSHDÐ)ÌÆ…>+Ñ\®N‘7xR¢:‹ÍÂŽU-‰óÀ÷¿å-oy+(äñm_æ‘–€ÇÀçÀÊ‹äÙö¹ýlÆÀö–Û¯­ èá‚´—•”b¾Ó`ç‘h|ãÅdi·1 ¸ÉäŠïÓï-oyòCp3˜ u™G xé?ùv`ãXËËéhÃÓšYÿNá5ŽHus¥À!Ã@I°úKz$¢©ÉãÒ»véá‚û¤B~—çôµ0±bîEoy«üò'Ð,o;MˆHoo‹Ç§ÙÀ.‘”ÕÖ8"Q)ð˜Ô KW§úe$ñ�¯9ëÍõÄ&ײÓ$͆§ ^v£i ïÚ˜k×ÈxNÍž n´a]ÙØÔ0M_æÚ‹h1ñüé܃,/xË[§O ÿ}Á~ð°ŠàþU#¬`Xgç�ÔH ­@)0ALÁ�àèÿœPT`Ë!ÆÔ¾ül&í*‰/"BpÙ…sü_öá#ƒ¡�Ù�Ž m(¤ÁØ™ÜÄFs�óeÕ"ÈY?ÇkèÈ$fH¦ñé|~Ÿ‚/*xË[�ÈAOð0( <°z„Mé›hýCë�„¾85ð*,wçK0‰ lß@Ì}KA�À'áçô3ÉY-mÑ/òÿ#®�®×L<&UÙ‡Ë,Dü׬¾e'ÖµÔf7[ïøº–Ãsô›X¯ø:º¶” ë1Ÿ…wkYËulp:S‚�‚¿yÑÁ[Þ:5�ütûÀq`ÁÄÁMcm*F;¹FcXRx ^ÄÑ„€'ÉEÁŽçìúr—<Ö‘\ð~ü\A`Ìh~«EžÝ{–|I§ÿ^1¾CVb\#]3ˆXàZêúñXÙ×W™×Mû½à«ˆŒSbo2 ŸOæy ×¼·öá×Iö TÖ Ìñ–·¼*¬Ú`‹3ÐC�Çïȵüa]-»}Í&•§ŸšH¸[VÆA@Ô]0²ŠbrìM" 'sõlSǺFÝhÝZÜ*9§mÓkµ·ÆÙJ$4e>×!üº2¹Yd�ר¶�!뱈ƒç!à:Ê>È6ÔƒÓ9êc'J=¡h2p®mózWZ"׉K… ¼¾®€Dò¨íE oy«l¹Lo %ž»w¦-žØ­Æ @‡?•©®;ÞþíêKFÉhq ä!RQ @ŠŽ\ÃQ$ßèj8‚žd­&T,��•Z‹©sùûíEd 9ˆ4 ]»ždodp"‰Þ�nRÌ�Ö "Nm&Q¦ÇÜ2ÈZדFFÍ1‹ƒHZA*|¯²Î‘7¸Â‡@püÙ‹ÞòÖ‰èW�IàE`¡Æ ÷ç©—dëœ~6 KdM'’’S�•Yø›·ã}¦.›-)ÀðD*ýèÎn i¤7¿Å£ÑC0ºÃf𭵍•Žgs6�#ëYl�ËUµ1@"º†"‹öa×!aÝ¢LK¤Œ|%b౞g+c®å•ÊLx-}݆kε%¹^¤Ò‰ŸxF2WÛëzË[Þ�|\ f€W€UÙ5Ù½mŠí[?Úبò_Pc‰¤•âWq© €(Ðëå} ›HŠáÎYAŒ §»d¢î’Û†]+‹À¨Ÿá®™à  góˆ”Æ€÷/2mVWÙ„2‹N‘׋È&D.…H£ϵãR.­çhäÔ¼²ˆ[.UfÂëˆLø¾^Ë Ÿ‰ªº0Û?m¾å-oyK$r!˜\É"<ß,;ºm‚íß8FD29;A$Ò¿S�­Þr Š�Æÿ&øªú ¿CÞú½Ž)ÜE#Õd" hŒüPPƒT¸;¾B�à |YK�ðlnB¤ôˆL”M@ " †6:2¡D׺¬8¶îåE<§ïAºvd5ú]®©óú=®¯Ž)±7++éÞªV ÙÈ=àŸg{üð–G ¿ÝÀÀ* l´'0Ùÿ³f¤Ý½,ÇÚh³†tT VFâ7ý·&6(ŽM�²)YQÊHð;TÄpAéùýÚÕ“¤E@óÝ_¦€Ç·@f¢à×Ä÷5þ€;äì%‡‹]v¡ë+ã€l•¹‰h|×Ö]c~ü/«q„¤ßå9½Ž3á1ÜUýu “=ålž¯å-�@~ "Àp XUÉ#;'ããmׂ�¶”Á�ãz´rDR\“‰d^Oá�D[1º�Mí-¯³\š>¤¢#LÍŸ0‹4ôuYäáÁ]+!(Ki8Òu¤Q’€õ5ð¿Æú¹D¤-|e:È[¥õÿ¸ÆÇ»Îæ±ñÞòHä° | L¨B"yhÇ$[?«—­™ÞÃ&öŽ·!IÑ5ÞlÏnOén·8»ga/{xÝ[5¦½zÚ4¾Z’ÕAÒò‘®|�ÌÁ?@z$qºpYpäËQ¤QVfSòw•ͤ“9╨žÂÿY\ò· “¯y<ìl4Ù½åÈ@3ð°ê‚#['Øœ‘]-?§“å¤Ä¸É¿5z+Æwµ×w�µwoÛf¤w·’LðBTįdG �="¨jRFÒ�)&¾ü_÷¼#Uב‘¼Á×QgSüð–G ¿ñ`5ø¨�¥¾ùö&;ZiXã¤>ñ62#®Æì@“‹·Ḭ̂ON±¯œ^ôÎýã�j-ó"‘Žß’R^üióÕ'óðˆYÌù."üõ¦Œóõ” h¯æQ×;rùÙC¼åm*ÕÜá:Ò«^Oïžn‡·Ž‰P­%IkpR”<‘š.iÍÞÁ>=4ÕŽ=4ÝÞ¸wì±×îý~^ÿ–ÅHZª r&pSÏ8¯–à !w}FH\"~úS”•ŒevR— % ‘]gK¢·<d–ë^Í æÃ{W³ûÔ¸± /¾HL1æz�'‘ñÝ›Û ·�²¯™a_�fGÖ°çv ?¶)7©¨CÄ �ø `/©®åżª¼\5}(ÊBrŸB¯�ʶ)çÞ‘üâLŽ!Þò+¦ƒ§ª#�3½4m ŒIQIðBv«üö™C¼å‘ȵ`­“±ª#Ý5Åè`—¤u×Ò›Ð;AwògB•ÖâÑ�ìK²{|–h†½yïX»k~w[:®£¥´¨¥»ÛêZÆëõ§®èÁ¿’N™ ãX4>¥_»ú’¹ ‘Ì31†xËë IÏWK‹Ý™¥¥LäÉ»¦‰DŽ€ÂI6$%¶¦‰†LM‰¶§¶ ƒDòŒŒ„ldŠ=ÛÛDµÖˆŒHë}Sµž‘Yë›îú+J”Íž¥ƒ!}„âàW£qþ ×ühRf¤7í×[g\EÖtð9°jm£‹¤¥lä‰;sí¾UÃm÷ò¡6m@{7é·ÆÉšÉÉ2×!áøÃ3ìéíCm}n²õj×ÀšSê[MF›¸ÌÃu†ËhV�9U�®µ ®Ñc'ñ8 ŽŸ;›ˆÅõž¸£Èã]“…évÿÆùÍ™C¼å‘ÈoÁBP¬:ã黧㉌Ñvº÷@"kgô´ñ½ÚœÙHSË”hï<0Áe#ÂqHåý½m÷Â6$9ÌG$W‡»mJ\5¸� éR²�+9·«-¡é Ö.â&kÚ†ßH‰òå"~V�4ºög]†""D¸ª¸Ã#9L/É�gRñ–gª‚âjH’²èQuÖ³÷Ì0†2* Y?“öiÝÙ›½ å¾Ñ5©Á°T›Õ kd±#‡âGgÚGû'Û}‹{Ñe­ÑØ›T ‘ø€È# QæÑ¦Éõ"�ä¸Ú–WÇÒ[Ö³ÔæuE( a7XëÆ×¤âËRÎ^™ Y²˜“éý‰¿ú¢~ïLˆ!ÞòHä'` 8^ýÊzó툃Mª K„‰h–ÖÆ¼Þ¶ŽLdÖàŽ6*³E�?°K¤H„±-®YÒÉYx-ÞdÓ°Ïü Ä_Úz‡ŽvÌvI[ñM¯UÇtUÜUã}è舤eÃk Kès«%C ÝÚ4° �„^m|Cßã&ÖµY-Iˆ+¥þYP%jq|ˆ}òã8þüLˆ#Þòš sÀ—¡&…×öϱW÷Í)�Œ%O©Ü³b¨fimÌïm‹Ç§Ùìa�mP×ê™�ø�¯Ï�ߦ0Ö~xz3škƒ»FÙ0ŽŒr)>¼.çu{"ÿXiDRôÈL{vçpÛY�AAADr�zed�KhýÍë_­ ©Jd†Œ¥Œ£sô­Ê@ºÇ³=m¢Dﹿ�8‡¥5áIŽÑï„à|}Sy…S½�ów~MUdHŸ‚áàW5=ŽxË#’öàÃR‰¼qpžÀ×J¡Ž~ðýlaiæºJ|w3þÁmíÞÃ4KkǼþ6¥o‚öÐ9²ºf " ŽBnÿ¶¶lb†ÍÔÁfÎÝæŽL²­yYÅÇšQ,â(_“‘¼r×hÛ<#•®ýf–Ú²™ÀÕ!+ÝuV�Ù÷Øiü1u¯4²eq�J'¤Kl-Ël]_™GÏÄÆÖ›Ì£Oû¦d¬HsæàN–—ÓYÇÉÙ‰Ö›ïñ: ø·j>X0HDäÊyI>Ó9ûMꥒì²p{‰„‰«¤x!|·&ÇoyÛß¶/WÆDÞ·/°wŽ.²w\l¯˜gõÜ›‡æóõ\ÁG6îwtòDžº{º:××Íìiw.L0mcÓ¶ã·¹ît««¬5$9ÚÆvo©Ù_ã{µ¶c’éwb«s³˜NÜ]ÝÃ;&؇rÕ/R‘È#yë¾q¶-/Í&÷inIq·=#i |¦¹"–@|ûš\áÌrj t²¢ë•‰¤‘…¤¶¨kÝ}ÒUŸvMä÷ "Û‘ÑÜ ‡'±/ÌP»sÉp²È~¶dB·b*ìŠûtŒ°¤õ¬ J0Î�óÚàÃP-&uŒª-_¦Ò¼Dï)Š÷ ªÀwÚ~Vcˆ·<ù ˜ >q”üZ„ñÁ#Ëìý‡—B(‹!’%züÞCKÌŠûÙŠˆ…£ÈåiúEö®¥^‘ÕÓºk£*îò!¤!y "‘êJ$Êšæ�J²SºÙö¹ýì±Û¦h”ËCÛ'Êzÿp�#Œ“ÉýãmËÌTËI ·Ä°ëuLïÑ]{À4—9Þ6ìFe‰<&[ ü@¾æø!i’² ÈY®jNJ6’?´‹m.è�·5�Ìr>ǹöì½s¾¸õØã+§ö,^‘Ûëxv—(kxý…å=w É-žóîu‹ ‹è Ð Þ$i^Ev'�GäÌuÕ÷*ÉG9 þTÓbˆ·<¹ìau–“®ü+ùè±Â'O¬¶]aŸ>¹Æ>{j�È„ E�9Š< ¡à�Hʺ{iŽˆ„-¹¨š’ˆ ¦1H:mSAÛ³2d°$äA0�I`Å7Ú[`_=\¢B e9“]�´5wD[ëÓ¡!�þÚ vŽ;釻wU² ŒsIV˜ãµA-cȃ@}³¼ S½Wbcù!½ÉDü'�ïÙÚ¶dÿü™öñãkí‹§7Øñç·Ø×/nåßÀ:šIó�Ý1ý«n‰MD$•´Z7ºN™Qà¼uÎT�!½Ý"DûepΉƒñkD>�„Y !¹re!Ìwlâ“Ê‚œµ< þ^“bˆ·<9ìjüý}e#Ÿ?½²Xk_=·Ñ¾xf�{~£ñ˜ ²Š@³Ò¾|v³\Ù‰OúÒïò: ¾°}îªÜ,ìd$2±Au-ï•Ü6:«…Í�¬ReŠ´Ÿ¼ÿuúàp~©Æú‡û&YYÈ't´?ºi°½z÷h†7Ž´QÝ¢5%¶nÐ�vI;N¶êYd´ª¯ŒïC�)ãuäá²HŸvMOèßAÖR6BæÁÍÄBHÑ‹ÛÌ^Þn_¿°µèÑÛóŠ#n./‘ˆ òÊ: �S2£ Ιó´n­èü“ CªË”½à“ð;—É�4õ=Þ—ªÊ²Ú4�B¤*U¦'F™L,àïˆ`Èb‚I&ï�›jJ ñ–G"?Ë€UðB”‰¸l£øåmÜ�P^ÚjþÇã/lxì#“$u9Ù Ã]ƒ7P­µ‰ˆM°®~DB†¤sÊ!cÃ9æíd›ÉFl«Ñ.ÿw}öر‡K7ÖiH”'òò�£Tò{ûÜnöÀÒÞÖ=±žšƒ$µ(°ª\VÉe’´ÚGÞìXœEîÈód"�ÒªÔ€ˆdÆ Ž¼ïI|†K­ø%‘È Ø·q²µ$“(‡|ä~ÇUŽ©jÌ•÷œ£ˆŽóÖûAærc[D§~¢Q�€{dXÊj Ne`”® ¾‘ r™‰HŽƒv5%ŽxË#’à3`BÈ«´d ÛÛG–Â^Ù e+H`È^òQôšÏÝ;K}#kÁìá]U>K)iu!D¢Ê|•9íÂL?„·óœËDüðáá’M‡’²ìóùöãP¬ê«i¿+'v¶ =› ‰a×Uøn¸1óI51ÓŸ™Î]·¼¤,_¶ÑÄzúd+Ž ‘{¾ŒRg<¡$IŽì“ßšÙgÉvpÓdûìÉõ"�Ò°{åhk�7‘ÀßåŽÿ´‰„÷¡Œ¡#ÒÁ_DR";Òùg’™¤4¯#"ˆ¥tRW©âLÄè÷žø}ÈDïYUhd,üœ¤/‘‹®•ögΘš5!†xË#‘›À�ÊÍFæ*AšÂY)rQ2e(""e%{f£¹÷'iIIiœH” fUa¾ë.|DFœÊy•IéFu–ö�á—‰8”ê�HÒb8£ðŸe}lõä®¶f [‰W¹oRÜ-ª>ªHY¯{¬NôºWXU�eͪvJöu£DuGïÐGhª’Þ²¯A˜>—ÑÝZ¨ÌwѸ ¤¼¹†„U‰(KÙT8ÈÒÈz¥Å•«ü”Å9K~ƒ4J=75E’MáïHºãwdÆÓ}_¢H@Ðû"M~4Tæâ¤>2ÉcÎ?©`†8|¿:Çoy$rxXeYÊUbÉ�ÊI`ø(ÊN6x�MtçKïˆHÃ5ý9I…J®J¯Ìâï"eµTsäœ]ÝÒ“»ñ±òDJ½6ò•�””µðB$iíœ�aÓ´R…V¿N�U¥Å–º¬r7ªŒ×ÉX ¯y£‚*•YºËå<‘G{yéH2±Q�»SÚ²IY¶kaŸÙʲ²ù&+¦÷¶™ÃºÚ`ˆä4Þ‹;ŠÉôòFNFtz/¼?‘™ˆ2 ~ï”ï1Ûï÷�þ¤%sßÍ« ‘<.®®1Ä[‰ü,Vpý#èdòHÊM&ü®åÀæ)’MÐß‘�â ^ê#yŒàn˜&?e,ÙšVÚ~êô´¨O„j2�mTh�T~Y×æÝƒ2ÙK%’ý+ûÚZ2‘]š诧Üöjî|/©hU–žˆ�@rMרZHBõt×í¤!dæi›Ôç’Û¿=dšD™ó`Þ¡È¢ "¡àb£­˜ÖÛæ‘¹¤à•åÈœY®âÁù6�ž«�@y!Bj~“ {¾"dò!¨W]㈷¼†Ã�U8?Ë•þâ“,TµVEˆDxu‡½yd½$½©à˘”lü’>|ÝK¥¦H)úš1•Z�E�ˆGn›ÓWý.Gé¾¶LqþH~É>m¥Ë<-IZS²›S®Z~Ë¿ÌÕõE´ôu£SÞ*ÓKwÖø"kqv…öP‰åZ¤ªZk߆‰Ü@¬a”…/ŸÙdÛäØüñ™jL Pžò$/Q‰¥2_°Ò·€Œt !4׺좀“oöõ ˆ¨ŽqÄ[‘ü<šý@ ”3U’ë+RUU™ø:Ù—ˆL¨Îª™¨áõƒKìí£Ëí©Ýöø� qÜ>Í–Mβ;èœÞ6w�|ôz?Ù%„Ûáv�´-³³mÿ†1öðÎÉ’²¸6îz”‰O�ž`´kºïk»ÇØ´~-­sìÍÁ˜“åzC$ÁtU�ÔÎ7Ú KÙGp<¢0c^M—=lC^_úd ]¥VY�wòÈí³lÛü!–ùM p€™$By®2WºKP¯Š‘ÿ2ðñ–DÐdJeeP"ü(I*ƒ¸* Þòÿ¨¿~Z"އj‡ÂC›ÇÉHÖˆ÷´[á©2æ”W43ÑïþÌÝñ~Ü1Ív¯ LvÙ[5­§ä&à3Þ#à‚)e9O€Ç2–ïgc­çï׈û€¯ágŠHü¡Þ‘Ç· ±�]›*c¨à�E=vÛñ”¹&¤,?‚¯ h‚a…Ë�!SùDH�…rxËTÉV�ÅIA_‰n –Míi±â¥-It”åê}PŽì*®ªŠHT.�±©’«DVÒØ7-Ùmöå*äøÚ?ƒù´­ê8â-�H¾ þIeÍ�|D Þ*†HØTJÎ#»&Ûƒ¾q§¸÷‘ÉZBEÉäØ ß” o¶ÇnŸE…Ô[3£·â—àd¾k³+Lð JYêªOвáx3˜ëô·ˆ,—ü 싇N íÏ~Ï¢žÖ¥Ù-ºc-Ç>n“)w¬,#]æ²Æ»·VH°îà!]µ¥d­Ã[§Ú;d‹öŠÈâ”ÉkË'i…LAöI�= IºÍ³äï Ë©I«Êˆ„²bõ›P¨€Ôv�H‚ÏŸ$D0œ3™Ëõʦ�E.~½(ÇAOðíªˆÞòäÇàààüôõ~ÿêÞÙµ VÏÐ]P×ÑÉPA‚ȃòVmw{ç’!>ÈþéêÞ~I{ªÏ*õopUÅEG{E3¡ùä�WØ�M“$«pW,#ž^¹åSºÛHf�H0•[ñ7Ú«B _„÷x&â¿~äD£ý³CS5K«u“k${Ú�§ ·¾�Š-³8ßdÞæ€±!~ãÝ•…³ª fìŽÅÃøÌçÙWÏnQœ ÇžÛŒ<9Û–çöÒàÆ¼^<dt¹—ù~¹«W/e¼Uš�8@È"4×kBö§Ï8‚—¼Èó’É %/’U•¬öÊ?¬Š8â-/ ù+ˆ1½Û6¾iC^ïÖLÉ=ê9’“‚úã·OU/XÒ¥­ãèéȶ»– áØ“»’ÆxŒæñú[�ä.î¾K!“BUsÑpHF±©Âdrœ•W÷/Ä«` Ç¥ÛÒIY¾2ÔÛ>oPP¥­HeSú&Ò'’Fà,oÄv€PéoQÉÒßÇå‘XÁ�x‚å•”ùºèêc�B%–‚¬ë ¡¼W’‹»s‡Dø‚›�ŨRkñ„L<¢éxa+œ7PFò̽slm^?ËÜÙòF¤XKÎû$D¢ ‹€ìºØ«Ãýº¶øM"lzSôÐ²0¹‹<�Ék©±"ø9G&{9z3·¼UéDòCÐ4#UžO†ð<¾…±w‡‚º6„º{šöï8ºu‚¾ŽÙžo÷­®Îíå“2�æ;I<·-h·/$ÿdïºÑÊPøYþA)&ü\•“�¨´·"dòås›l/•BŒ*Wä> ’Z¨ìrÛÚ ì¯a¥ò·ë½R|p:×O¥¿Å�•ž‘än#ÙCÒHÙ2–#"2YˆÈƒ@%©ýÞ•ôº=B‚–™!í�h•ü íj›òûq-¦Ø'�¯ ˆD€‡>u·o™;Xd®;ù²‰“ ­9`e×a_Õð5-6Ôõ†Ä%qi„=ÂQdžâ«�ó‘Žz©¢�ç¿åÒ÷‘¿šVvñ–G$¿ýt�M”wû˜î­ßۘ߇? Ãs�ƕ߇ üÀšÈ/ý45ו¤Vd’µæ²¹Táð.6m@;ÓpÂ1)•N…îÖ!5y'�‘=â«hzþ~eIþ»&Ò´¸L#Q*š™¼�ñþ4R {^¨³zUnO•yÓ+ß©ö\çÁlÿ;ít¯Ÿ�RªG²hL{�î8‰G¢,„»]!ÝC"‘¤¸Ú h+7Ú�@' æÝ»<‘‘”YOí×N’â«Ç’Ï“�8‘l×àÖ�·53³-éí$#GD®jíHI�gW¡÷JDÖL‰¸a�”'Cè"=y)=5ŠÅÍÿj$báy5„’�|ÆçYÙqÄ[‘œ– Lj¶±_§ÈcËÏŽêg˜;*Iƒ ×Lïa+&gj6#Ø]V ˆ¬ò†tT3³¥|ûn$ª!b¡lƒÌ¶â!@&ªlbŒº“¼t./øH…ìD3¹è~w™ eÂêè|!�>“÷Y �M—7²¥p€-ŸÜ=ƒ••l.ÈÖxû—N‡„˨Ør½$+'u>Uÿˆ4wù ¾^ « _‚«‚“3Ô XBon�% 5RØà6­"û,�ç!’^l°‡3¿drë[[2O™^àýŠ(™�U�†uª’ë¯ÏòðL$wñy@4xS"ý¬²ˆÞzÀ§ý:„yDâ­J'’KÀ=ºÄ<ÂÑÞqÆxm¤¤­\¶7Ž êw/ËqÁ,^‰²š•‰Ðˆ&™B¨’Òö±Sû%j'Àÿ¬)âÁKQÃ:i“*2e+NþÂ;‘Ô‰ wm&ȬWßȧ�s4Uk�:3ùŒ}/öoüF⛩ŒÄ™ËAž­…´§yZê x¼¾fl•M$‹Çt8ÙˆxgÞê®·²š;�µ®:ÓE" ²nVVHÞ;ÐÀL²>¤ÍîöŸµãøÜÖpíwœ.‘è³~ãÐRÛȾ%ckù“‡Ÿ¤¥b‘'R�ÿL­ê�×ߊær9R÷ÿ²R2£z\SÙqÄ[‘\#y÷=xŒ Q–@ƒ:Ù�¶§%pKZÂŒGŽÉ­pÇ:¤ =AF2¨©Gwý®Ÿ –¿¯M�Ö’á�@(Ý™ÜÛÉ­¿d/šø4Z�LE„òøS•©0ábù¦²k)ÙÅ*ièÏß?—Œ$p" {Ü$©…*®PSi²ž¾òŸ,ÿÕ°Æc¥Tl¹¡�…C,¶l³Ý•øj¾Trœ2 ‹»^ÀÑí— ©áŒóG§’éÑ®‡NÒ:]6½²»WŒ¶!éÍ-©.o¡)²�¿”yb´KÖBªžDtÝ!qeú</µþ4¨Ô â-oqG|mߎáÏ÷í^\¢aN² wŒ2D§õo‹ÚG†;f»¶x%h»jªr�cÃ)†@Šv‰*vÒç$c›sPY(Ð+Cd©x½%¹ñÊ”p¨=;�ßäéð<ÅX$£¹Fç:Ä—ï„càãTÞ:²Ü¦öï*"‰²Ñ–*·'aI[eÉ‚Qí´ßÕ>¥’�ÁÞÁWþª’^GÌ� ¦O´rj�B!s)”Tz­ä³^`ë úË'É@jÁ@DÈÃm\%Ï�‚ù Y~£ì«9NGR,™•D¼å­œŒø–ý:E|V†ì¢ì€/™«pDíã�W¡LâéݪÞ*¯gB—úìâMÙ_�¢Ÿø·utyEhÓéëÐ@¿mu>ù9�l�ŒIÑù`\kAŒ{y;Ó¶ÓVûÖ�!sé/"ùêùͪÌBê:-2yäö|&è6 E€•œ7�óÆ'!“š°dø¶&ÿ–.mÝ·¸’Hmm2u"‘¨gD†sª¯«"•Xý*ðÞ)©ÖŒ3À•û–tÁkäÍ]ËGÙœ1é6u`GëOV›B¦ iB(nçF5W"㹌ëLCn¥oy+wÜÐ_çd&ìDǧ,X D—Êš ܈ö Gâ*oF‚GÒ¿ˆl¨?E‰\DÚ¿ƒ^ ~zµ6¤8�+•ü�õ�ÉÜQÉöÐNˆoï6‡š#ÉŠ;à€Í÷wYcc{ƇÌ'™IÁÁ½+‡©ˆÀÍÚ:Õõ{sÿ‰}$ÂãyÚV·Gb}F)cÓ•�°¿¸óAÊm¢wço$·¼YG]K¸ j´“ì¤Þ«Þ¯ùg$‹Ægès� *ŒOÙüêžUcí�µã•™¬£ònx÷VÖ&ìF·Õ­šù�ótN"Ï3wƒŸ…<€xË[k6nýî¨ìäœþ�£ŽèäYà›` «�NãÍé{àn4]é“¥í—Ã#Á`UáÝ ûý�\ä­˜Û:×�ÁÈ/“Õª˜=HìöÅÃ5~ã݇V*x‘�8© l+“HÞƒHƇÐ'Éí߉'KÅ@DRÎ>ÉÛ÷�‡Xc%mA&’²ÜÆI|-‰‡Þ‘HEºº»%Ôµ¸ðó…Ę+¬sóë-¹¥ª-èC‰»Ö²:ÇXf×¶vá¿ÿf—žû»áÒ3ÎþZqg¶Ï©+œ‘øÏÝzšÑóûéG�T4¸RŒuˆ©%"!#Q&É��$"<~ò â-o�”Õ``RÜ›Ë/¾Q!HG2¸‘aÔyNÕU¹3t~ª­rCå?8YÌiÌœ4«½úCî§_aïú ªê²W$‹�¥l,“H>¦OabŸÄ� òR\[86… ®œ€®ß;ŽHÊè%Ù‘ŸÎ ÀLÝÑ�U\ª\‚\D$é­ê¹=ÉË{ÞÚ¯¥mì•Ùø_Ùä_Õäß&ð8.òB[¿f�=tôˆÕ­SÇøg'üøG?°óþþÊto2n"Ôø¹gÝxWö[a@HŒ–ߨ :ÞE(‹© ›Ìø•äõTN �žÖDâþü[òÏé½gWOiì5pYHˆ·¼5}âÈ_ÍLØ‚É~ZAH:ÂV…Ñ‘­ãÕ4èîž]?Çi�‰J^WOïM¥Xd¥í‡>);Q{]ÐE­JüƵÌ/›HÀç¦iƒ:…´ymhj3qÕ[å”¶€öldã mjEW´¼ˆÄ a”W@Y©ÛÁ°Ù`8ÒÖM"�’hq¡Í+$+Ø}—Õªu«HäÛßþ¶‰L~ü=kT矖ÔòFeºlb¥¡™APñÅÓUüäÝvhk.þZWLØÆU¼¿®ÍêZKLûfõ¯±8~+^KÃˬ]ìU–@Ö‘Ì i¯:ÉÀë%ñVh×ðî:!i}yúw÷á’"hVT°cÈ¢d.$.�?Gß?í1�WCmœŸlj „´ÚtЧ%Á!hËõ"”=‡k«-˜Ð3¤rHNJ¬ºÜí'yµôé¿B1xþ¶øEÑš¹‘d#êb§VI0ÞOVB}kv^I"Q–ríÕçØE]d?üáE""ù¶ýì§?°†uþnÑMϱq×JŽ\ÃôÞÏð8üúH‚–¡Íä:™0öF»ažŠDÆôél›W/±á9ƒíw¿ý�}ï»ß±ïï»ö�ï|Çþò§ŸZxÚ{¯ÍÃ/à}\ƒ/ÕÀß'ªJ2¾ôàá-oõN¨ÿm*´¢¸û¾¼ƒ)UUÙ-3²”™ì^>TäA%—ü’r”k–Ö6†"v^Iè!³¾pD²Ý½|¤=¼kå§œGâŽþÞ £W¶0p@ˆ‰$i+U×ñ…+á>:’÷ÄQ\™|ÈðƵS’,»c#kqƒáhŠoBØ ê˜ÖøŠ“7¯ÑÄâ£/sÒ–$ño;÷_¿t™ˆàˆä'?þ¾5¨ýw‹âg¢›œc]Z^¯iË�ß‘§à @Pê€ßMU»_–yãÒ³mCkßì:[¹´Ð^}ù%;°o¯]vÙeæŸQýão?‡(OÌÂZD\d�[\oi­o5¼"‘KF|�ª ’MÀ›ì­�4†ƒ—�•hÃê'Á'¦SE_‰iÄ0[.ÓPI•góÆfVÖVeW¤¤ÛLi0d"/ÒP%—#“Ïéng¯••®ž‘­àÊsBæÁ3È ¹r¢*â�·¾øúÑYE"�RðþÞ‰Å,í]445BSbi@tû‰unVÒ‰ò–ˆäªË¯;ø²ˆ„ŸÑÏÆ4=×’ZÝÈ^0½0Ì·†’L(;_D³m|©2ez›ZÖ*’‚„zÿ°ÌôÎvû®�6jäpûÕ¯~åˆD¸è¼_»s/ Ê´ÜûêŠ|WDr;øq°cˆ·<¹©¨yÍp=W ¬Ñ%4þ©l•ñïhÜånPíß4Á†¦ÅUšÄÅûјx&ûÒ©_ ¯¨4"�ÐÕÎ@Àµöæá¥6gT:¿Ò¦3$UÆí^–£• È<¥ïô‰Ûn·Œ .ÆÊåŽ?Þ#¡¡¦Ë’…›°ußO¤$‘@’± „�ÿ»ŸXXIC‚Àþˆ>1”ð®sòV( ‰k…ãz¶ñÊ"œD×´Á?9ÇŸÙÿøGûÉO~rBVuéE¿=)‘eZ�š_‡U%ÝóûÀo‚C¼å‘È÷Àô ìÑEc¿�2[Mí¥“\×®�é^^ÐŒ—GUU»Ê$y>ëfö�ÇÚ j$ó |t±ˆäã�Èç³OÉ"zQR+Ar‹d¦Y–*ãá¯ñ/É)Mw²H#¿LB9ºnàWC’"¿å¹Sú{¢O9œί\&"|ÿûßµ«.ûý‰? šG^`{6�%;Ü*"È:ÕO4¦{+ý{K…Dâð9üÏû_ÿ…¤$‰ü€ó¿ùú?ŸŠH ¥ó)�®WU^É àü`ÆoyDrx=ˆŒôüü¡�”•¬œÚMæ{‰D;!ÎÊé\™c½eº/a3%º«XŽnÏUÂÛG–iˆ {ch/w´{• ‡Xz“é<{xWuä#ª:Îï—9.å¡Ù>"ÉJf%oÜ;®xJŸVE!$ie%-ñJTÉDçþûWÊL~û›ÛÕ—ÿÁ"ÉO) žÿ§ÍÓ¾¬ž’ ›ð+s{¨ç%–L¤d6uí•°ï~÷DYî?ÿ�5ªû�Sf#][ÝÀÿ]›Ê’ký›=_3ŽxËËFò€ÊHÔì‡4Ä .ÍÞzì¶)º{®ÈFWÓ+“H”`òJ²Û¹`ˆ‘ˆ<Ø!‘10vçÒÚè ¸¾”�2\:1CqìãîO"ƒ¢ÒdÁÏŸXiöÔ|‘‰ËLü ¥ˆ±)GÖ dfT(Ïy¨VÉ ,DPÁÕ™«iýžìN^DÒ'­eÙ~S€Cè—l$ók~a©…unù«ýè‡ßóÏHôøÈrá K'B÷^»´¾Úf jc�· ”*ÿÜ×k¹éùA°âˆ·<"¹¼šf¿p‘ÉšéÝ!“Þºƒv½$国U¨ý$¸kV€ïWId2:«UÓ}™ÛÕã}€z§¢=H^Þ»P MstÅÓyUÆ9�íÑÚ5{ú—T¾,Õ'y|µÙ ëMdòÄȤÀ‡<�/|Í0Çm³2C~îôU”a@ëx2¨d¸kâuöÊ�¡&™ú¹ÃD^¥�Kå½çüó—ʤ(ýUÑÀÏö»æŠ?œÔdïÖáF[5¥“-ÓÙr³[Û ý›ÐIƒMÉbý¦e3ˆçÞãû1ÁŠ!ÞòHä;`Fhï‚¢Ô�=oT’öÔxt—²’Ó"÷ø5¶ÌÝD¯«¦Adž-rRd›+g dñ’ ÝØŸ}°í^1Ša�ivd[®2“)}Ûjw5²URyò†ül<šqöä�Ó\9p8¬ðw–›ä çך=³ÌìÉù�ÊBŽsMxb6(¤ŠkŠMíÓ:”>”šóZRùT&aœ‚HÚ·ºÒ^`bsH}HêýGWZV×Úv�«²* Ipô»Hæºðø¤ç>²W#;º~€í^ÐË 'ÚØ¬8ý;EaÆÈŒæÊ@&õIÔ˜þýêfˆB˜»ö¯óW{o÷O‚G¼åÉ9àÉë²–H9%^Cšv/ÄëÈND@[á–&ɼyh¾¾Ïcd¤YLøí¬ÿxü‡�N�WÕUþÙ�ï¨Ç::™Šß-ï8ó¯×Q<"£¹ªy0֙țfsÙ+cÚÀ’ ø^eš¥œG<òÖUÅq=Oæ“è~ôè ÝeÛK[Ìž]V‚å˱,àXh,ëïw�Ü5tÇ ¥Êh›;óº�‘NŒÛµ¼Â^Ù¿ ´DÂkØ:¡8#±¶šÉ¢J“ä„he‚—E8I WÙÁ5Ùöôvöû×Å&ölÉ^;1¡”@!�VÚ¢ø.ä×Åã3uó³yV÷×¾~zyk{ívoÞ–·‚B$À1rƒ˜Í©´Óá]K†`Lç‚©†ùî#Š9öú�¹.àùõšÌÆÔ^ho^€„4‡,`´¼ *i$%16^J‘ (+`ª¯îÀ†S" Á(è9òül› KOÙŽ 5ô1¦˜¿ã|‘JÍ£�XGˆdx ý9ú™�[i ”/n&;YãÃZ‹ðôBûôÁÂâ9Ã;‡_ŠGØÃ¤¶ORÆ\©”í‰$Æ]b[ ’ìÍ{ÇÙÞå}­0§��L�Õõ å^.ÓùrÙó㌗ٿa¢¶+>¼qÌ1{qÃ8{eçE¼_¯»Ý["‘ï‚…•hZ�bµ¹Ô¾ £5Ô"¡bÙÈ<Š/�Tæ‰P ë-¥Gc ¦ç@zQúˆ@æ�N•W±kaŽH„ç£2R -¦ÂjÖ�NÆÝ˜*®&3>žLBžÂÒ‰ÝЋ•A8¨ÁРÉûV�äzÖèÉõ¥Ob‹�L6A"ëG*� Ç•öä®IÇ †u)žÒ¯-UbIÊþȃ-Ý©‡"3¡Že´­eé µè²o‚losÇ&ÛÒÜ Ûµd°m*ìgCÒ¢-µÕ-IŸÕ¹v(‰DׇO_ä¿âuGÿ"ýB[Ù¦—†PUŸQ¶«r渓gU"œ´v×Ù� 3ìµ»Çü—½³€Š#ݶðu}îîîîîWgîÌ¿w,ãfqwww7âFˆ»+ñ@œ„$ „§»Îûf¯Zÿê—L­PµÖ^Õ „TÝÿþÏÙçìcG2:Û†ÉïÚø.Ï`t?=�¿—Ò¿³½)=¯8{€ê‡*82»Ê®¯[Ïk}ÝíÁÑ""ùYp X¢ [FòÒP§Ñ·§6 ·Ý}éǘk,pD ŠDn™AuN†a˜ |,f÷¢úk A"ÚY]Ä �Be¸¸ÃJ»Ø2¯7ýdºÈ‡ÓÈŽFÊId³`Ä{ÎEwé0·L_(>oø{Ü£h5§ijÒ ¹ÈäêJ®­r�«Î/´-³{0â·‹šhNVö.uãMÆäÞímN¢¼ûô¦±jò¤1P)&kÿ‘ŠD;AdC:>�ÿYF|ˆ„ßy6Ò‰ØÕó|¼ÙÍ�rfwOdý”"_î$øÏŠªˆžTÒûÈÿ†=ù�ß·g¾õÇöÚSƒ6øŒ�ÏìaÅ{‡ZN&�¹#Ñ û|ß¿ûÿ>Æj„ħrôÌiÝD‚å¸SŸXÄgjÎÇ++x­çÁ4ð#-YG‚# ’ÿåÀMTd!G3Q^Íä²N¹{j>d2AxÈ<»G.¿øÄ\ÈdÞGËɇÏ1ÈBsBެ¡±¸—èé¸wv™Œ±pÇücR¥>戈TÏ*ߤyD"4ÊîÑ\0HÆí®ÓZìúbår<3ê™/õl«»iÉži–'íĜ׈` Ï”ÇÕiÌ�Q?¤¹42 –iÏ~\ÿT¢?µ!€@& ý�Yàõ·Û¾¨Ÿœ˜ãÖ;2ê㾺”›]™ð>óf¢5@¬Š²‰ªÐ|þÑÞxúï¬Ûkÿm“{Ÿb1 Ä¡îêvOKE*¡kk5¥°òhÌÆÈÖ‚¹~ŽAT"f}+¤ÏÙ>IúÉáÕÃíÌæñJu‘¶Ð‡ÑßÕEB¥“|=-H…~zuÞ‡l'ÕK$TÄIoâyd9µR‡�v}‹ª‹R——[Iö|E„kI)�¨Œ2ÑãM…L•�RY3¼Îµ¤fÎòóº–¤‚÷�6¼V¥GÑJÂDHõþ¬úr.Î5;¯>ÎêËqMž÷�´k›ûÛ¥ }m'å¾3û>ƒHÄUωˆ©ÊRê‘ûÞ •ÛÉ8ëkDR²õ�}≀¬2+Øw‘úæÞ¼†`änp4›H~ǹü&J1]£©âØ% 5—?vÕ]í\v›*Þ=�a%§kžÈÕ½ÓmßòÁ†g–ºÑW¢�¡8™f-Ípç±ÊÑ�¿ï½Þˆö©L&¤aH==Ïë[_¥–ÝÉ0¸ï‹d¦K‡ªßQw�R\‘IÁáÙv"s´}z$ÀO Ý…n!BnâBˆãÁwµÛ‡@´p“~qEÉFÝ•µræºB¥§2jýùËFc§zp,g†C"œ'؈äÌêD*ÝlÕ˜7lÀÛ�6·×IÑúž¼çîYP�¨{¸…ñÐföÀwm¸R»û–VÔ}óÐ î ë¾f ºæÛ»Â|8j¬ôÜ;=ö³Í]G‚# ’'AM*,€DŠˆBD œ…@"z )4 žŸWgT®v™Å'!äϳ=>‘];Böð›¨ü¢lX©t¥ðÖR ‡kT£!„“ª$"�ÝQ÷UËÝ66²Ë]‘$¢R‡î9„"D’ Õqõ.ä¾v"B)Éžg‡ÐLÖO§¨�.~v¿êM å¥EDÝoáÖÑ#eYçFè¦t/*sW†½¼õáF–{>¯Ôà9MJíɱvuS?[5ö Ryßj¬á|]))"*_DMUyéôÊ‹i–œ BÁE•B˜Ú5ª¹1{OXÄe"y~ëv¦#q)žÌk›ƒö$A�hd¯y…ªâ€RYD&¹ L¢×`n# §ÈD¥Áe§É:Ÿê8‰¹§7ŒSy6ú‰xŠùG�ö¹E÷ÏE.¤Å Òý¹´ZšáÚ*ÙÍ—sžovy‰éù…¹V{zŠ�ZÝÇF`9CÙr$‘Jßà~¸´ï7Ý»¹CˆŒ»½¨Ô+f¡*ߥBLîÓT´itÝïÒ®iFq¤>œˆrıYm–—éô-Á]/d}ç YÙE‘ˆ�sàך³ŽG@$?ÎK6h¤g`œ?up�#�ˆ „qÿ‡‹R0( #FÍ)!FÙ°J…w™ X*"!ý¥�6ZŠÛa'I7ñ;ö¿éJ>Y„T͵³¨Ì‰<ÈYfå€4¡uÒ„"<·(©^™æŠ0vÌp‹ŽÐ¡„®¬æç—ÚÝ“‹E*Øéõ½TÆK߉u®OÐך89ì£g´óf¡ôÓ1*MMS"Yí7r.—ð‰èŒSgîyî|;¾j ÑijƦI‘¤¯÷ï/ç‘HwZgQ¶¾bBG—õS¥Z©«QŒª#݆‚?âXi¼okÙ „¯®qâ®-â¹"¾Â�È#�I$u ]sÖ‘àˆä¿\ÿH’Aé"Õ@C´�ÜE‡<•3—VEKü©ð®ö5•Õ¤l†©‹>gÛ$Åøj½&?-DçéÅ¢àv”É‹�rè,ÊÒs˜ÔHÔ±RÓ+ý 7HÄ‘4à>¯PuÄâš>9)0Y§8Á=K�ì 5žrÊT))»`~Ç|E&,zHNRÅÓœ!oIk¢×B G¾§*-LÝ[a £´V:‘Pš’É�¾ Â㱈°Ð/S´ t±zRgÃóJ‘ÛjÒ�ƒ± ¢1V$;¹W;‘6&DŃì¨tȃT,¥®(}Ößø÷ËUb5~�·÷¸H¤ŒlÎ:‘tK¥j#<²40 ¸…üNöE å„ëñ‰L¯öº}l�š/íž*1sÝ´®"òÔ8IK`Ãò’uNb%Q‰vø\‡R Dvîþqæ¾¹¨Î=öÉÂY®È¨Äºâ~¨»Û# "ŸîÙ��Ÿ°˜fB&«¬„…Ž´ ‹Þb¥Y<)dè¥"ˆEE äô!rúc™ã?ÅHk©I®6�‰¤ñû²F›$´ e6£¦'¨Ë<›(w?( µ ØB$‘àö0Hi+:é©•QuX姬¸Ç�“#�èqk‡Q�Õ‰Ô�wšº†G@"Ÿ+S„H4 j!þv,M¨Þ‘í„¢>x á,~‰$§Ï”�[n×ÎöØe+BÙ¿l°J-gú~΋zr áÀÎ_ùsˆÏ¯x[Wß=sdÂÏ@6«ˆJ–‘Qêë!ñqs“±y0„¡DÕΠ)­†œ–z¤»<yÊpœë*d(8:Y(¸Ê¢VE"îž8T�Š¢¢JDK©3EÓD&T®‰X(öàæŒj" káËŒL;%\7©»º.LdF0•J*BÍch&*×LVÿ‚»Dll_ÔSseuTD<Õ}D+q»Zõ5“n’jd@͆깉9à›²†G@"?¶KAh‡MºF Y,Øj¯ì�Îâ(ÁÒ¥kx,A< ‘IèÚþY!zL¤Ý̦ »*’ÞH‘¨�ô–t‡¢ ›r_<`Ñ@6îÅÇ™wO­lK™4QâíÝJAFO"B–•ß¼¶‚£IDò»à°T&vÙ²}§wCrU+T÷øBñ:¥pȯ'-Íu�tUIŠLHÃ)*Á 7 Dò5•ÓRJ*O1çˆkˆLÝ#!õ$Ü9$!Þ¥»Ò yDg×2Ó;ͦˆrSd·zÓP~½{Sבàˆä 篕ÚdBÓ–šÙÔe~ ¡¡[ÕH˜0J”çq2ˆDzÍDc´é$”«€kMäýqDBZM €˜ Ê9®¯ýÖÖúËH!U‰T6'#åå5‰ø¹š tù¡óÏÔº’ƒ³Jê.¯®K»$“/¨Ÿm:�yåVYíÁÑd"�>ó5¾iL Si뮌¸™1¢j®dWrAbŠ’ÐqÔl‡5=ÝÚO'Ê‹K“û¨Ó ü#ÞÅÎe¢îK¥³™‰X¼ vCgÌî_WB4¹…Ù ë9BàœyßD,¨ÂÊÛ·vL,¸µ}¢WÆ(A_otž )À+ë†Ù)FøfÏê>1«Kdr7-ä&Ñ_áA#ü4»—ëÒXÍBÅÍSVWþ MYC‚# ‘Ÿ§�¥ ™ À3/›¡MÝ凵›…Ô–"Wâšx¨óž&;ß ©«,ÑE$èH …-Šì4òÏQ‡8¥¥ ˆÈ6äJ¥`ÌEÓâýDi/‰òÊÑ_ÖsR.ñ­|º¶®æjæˆÓǦu(;9§«å.`wöÑÔÊߦ¾4VÚÚ½£sí zZftr86½£åmU—²).õƒl7î³¢??šÐ¹¹ðÊ®Xݽ«CÍìÓMYG‚# ’oº²ß4#“y˜b\§Æ6ÒZÒˆœ°œ °û§w£—ÜX)½Mh ‰\a§ö}E÷£ø¤¬3Wɦ”Ên¡ÜÞiJ¯îÕ× gQÎ9~©.¢£Ê³KÛýždÏìlç—ô³¢}hà|º¢‰€VËߪ‚”è�½ÓìÒªÁFò0‘@BE@©G ;EÊD|"€˜ <Ï*òÙÍ£s{¥õ¢AÙoô=&xZ±ÛÛõu’g¯£2Wæ—ÈS 2q;ñBÂfõ’‡饄w´#²ËºýÜV�¦uîÉ Ã­m>¶gG(ŽLî�7-�EÕD,ü1'£"¨æC$‘³´¿ÝØ<–‘·SE·vL²«ëGØùŒ¾vbv×Hq Å¥ŸO�÷µø¸Ðc‰º’œð�Ó+ªòöMÑ”5$8ùQ°XšB¥®¤�™Ð3¡T³ÙÁ5á%šHhæ£ÏeóK>�÷×—Ðùìcº<�CñP‰þeç–&þ�ZÄBl E(BÑ!é(” ƒ<��ž‰%‘À]“BDŽ "#““sºÙéùÌgYÐÓˆZ E! AQÉ¥5CqÑ]—dÙfJÞS +æðÊ.[ñÙÕžïö< )ëHpDò‡ X:ƒ4)�§`Ÿ"×`š‘Ä¡ŠËóa @QÀÞeC [t®-±÷�’c•F—’²Á?IUlYÒL€H2‘~¢TLñQç6ëq˜ïÇŒH¨¸ Ä ‘L¿0:Š—œÞ�¾ÍûÅØÇý+àªKiUd‡òÍ‘€.MYG‚# ’g@-°Ö@&LÖÓd¸©Ì §ÂoX”í¼w´‚„¥·.c�>¼ƒ›~—0Lîõ²ldHïI/¢û?YåÐŽL¤�ÜÞaVtXé-ÊR%G.Žz|{OLˆ„´UL‰„Ô×½ÐåÕ‰$HÁ^Ý+×ðC„îæX]ñYÒYç xEg×TB ¨O6e Ž€Hk-èŒF€Q¢†b™h!E€—[0¢3BøJó°ˆâpkW1Jd–×õµ„¦ùð%“K2ÑH¢ô‘Æ›…�.*¡§D)†¼G ȹ "Ç""ñbI$Wª&" %&Ùà÷€œs['=™S…uQH¢ˆ¤¶è´UÞ‘H/Ùiæ]ãiʇӇDäΛíÌu­‡ï]T¿È½K›E"®B«~xàíhבàˆäï@ °¶‹�»àqÉí÷š-ß^‡·ŽÌ5ª¸Ôƒqïì’/”ˆøú½cº|?®©-üµôû™Ä¨ ’Çpý-Ï]‘òDâ¬çYh�ͼʀ/)ò€0$~ÆYª¤*ÔÍ/k“ ®"+Áшª´*nì·;§–½"Ÿ�”�‹v Ž€Hž°¶ _ºà¿ã1Ž6Ì\sÍ4Ax�ÐÖ«L¸9¢5‰ªÀ¨ž’FÒóÕo“zŠ‹²&0ö{ë1•3‡DÖ0•n"bª#K\¸,ªQ‘*pZå°è&�¦¼ô37[löã&Ã,¥ìèÞ×õE�`Rˆ„R_8l·Ý>6Ÿ¨[Z�á&øƒhבàˆ¤°ªæò}ðT‹}ÙñÌÑÞñÌQ©¨�±êâJY¬Ð�ñ0IDÑ�˜))ä|�ê0æ„ÄMéóæwD z¼¨í9Û'YµKÍ¥™ì0¥¹�œ1 kºûIÕ\9ëé Oîlt7`긳}I@$¤µ¶Ø­cóa4‚+à7£YC‚# ‘Ï‚UÀ¸4WhT——ö-ýá„w•ì^Ø1E©.ʃIß/vºß´™@@”OQS" ‰òƒ¸â2ƒd|÷õ;hô0%ÌNãIhθ4Í-)>ÊYâttš ?'Êßâ÷„9óÞÞ-�Çi ©™2bƒEZK8‚ñºÁ‘üÈ 22ùzu·WÉôþSÕ›çöÖØÞõÓ»‰T˜kÂÂ,Ï*yh¡¡Dµ8’^Òl”iý^“N2”ÙíñêfŸ7ì5[žÙ<^Ä/³Æø»oÐÎÞ¡,ÚÙy®ßCHÙ«°ÈÇšTÜï‚°T=FÄé‹•r¨½sÊJίµ.­Õ(G³ŽG@$îäÑ nf0TxÖÀ7´ðobdî¾¥ƒÍïx÷#5-:¯®†F´E H7mœÝS“ ãa)�þ¢‘¾T�áï5™!-‰$R€w©.ˆÁY§4 ü‰÷wC*;"IÅ!jâ²D²¹/ÎÁµLÿˆÅVmñ„öÒGüöh0>šu$8"y¼q­Xª„é‚SJk3ú¿Î|�vª¾bqÖløâ“‹!u®Ë–>R;‘;luÌÓô((V)Iì]ñû + æ‹ðhŵ¥/‘8_. ÂÊöEëkM…‹TùUz^úé/i*Švò7ƒ-:‹h4òÜØà§Ùd{¯C˜çW”5bª˜RDrYñ¥7º.ö(qüxckHp$òi06 ‹¨ EQìø¥?ÐkÂ,ö*­%Úм‘ Wf+íD_�ìlg¨–þÝŽ…ý¤“€˜ ïè-¤µ¾¥òß) òBÓ‘¿³W\*.]!")Ü+Ħ‰/Ï€sÀ¹ÜÓ`-À™è…4•Àÿ'òÒψ8)¥®šWvIv(åøjœX쪵¢DÁ'UnG@$?ö$ýBÝ…Þ DrUEmÁJeÓœ^�ÇJùueh÷ï/~jf$"‰ì#ÑÏ•¨»Qe¥º1»¾¾Tla )¢»‰ÃõÐŒèìãÓ:*AlWY0였8ï!\KGhî:ñ²D)»¼}o–¢ ¨�5¶ŽG@$ÿJ’ˆX›`;ò¬Qb+óE" •Ÿß6Ib<�+ VÄBºI]ôh$Ò2úÐ4«ž’�Œ×íÛ¶ ¯¡ÇˆH*ùÝŒ•ԃ״fÅ­J+5¶w�¨“]ÚHþDöu®Z«‰èØØ:AZkd@ÍJqy½^Ôc|/v*�äɵ}A_Y«P•¥”Z²V©§ŸDM޳½i[¨Û<·—ÊucÔŒ(Rš;äm¦1µˆäÞÙ¥.½–æD²$¸ÃŽŽ08 Îb>Âü0ÀU¢‘ó˜3–%JáÉ C3п±µ$8‚´ÖÁ€š/À•xÓß—ÑÂBY«œÚ8V‘i.50>Tµ%Ë&%jìîQ¬Kæ y+&•[ÝÐEÐZd‰² B£bKNÃ�ˆ­ƒL¤‘¨ÄÖ×6.�ˮѕ�Jå­Ãj@¼sz9šÙœæɈ†Ö‘àˆäOAa@ Íz‰‡Vâ‘¢2Î4~¨èäðêáê†w\©®ûç–Énåæ‘¹š�¿‹ ‘ô|íÛ6䃧l0X3¥³*¶œðßZˆD:ɳb¹›HÃUO]æñ × Ϫ Ž+):½ƒÆ9Π±èÕÐ:‘<B!´ x¨ï[ßñ MÌú¶Ò[XÑKˆGŸ¨wv;MŒŠZNo/wÞ¤´ ’G4ñ‘‡’äªÖr{k åº" ¡ø˜zKxÁzìzLÚ8\¥é‡EgVhpUþ¡Í"Cyû&½ÜÐ:‘ ˆ &ð¨–òhV4†HI€Ÿ„M|væhJ_Ò¿kùÌ‘oÿ·lWö,¤ª°øOCL¾;0p½%"ù[]ió‘~Z²Š§g¤Ù$róÐ4˧Âëþ¹ ¥ÁjðS‘kHp$òU°9 €Ø÷™tk÷-‘Lèþ"QI[ pß5ü¸TÉE/‰÷.$"¡�°ååÈDC£;ßø}bõ GîÄ­²LÙ¤iŠ˜:ê,—àâì€HˆFª ²Uê+Q¹ï”¦¦³(Ô˜ /Ï#¢^)"u =øü§‚#8|"ùepX€Ø£çë�¥Á¶l\{yjQŠKÉ,0Ó¨ÜR5Uæ´n6œ1¸¤¤Z”΢Ü×i#TŽ©,J§ßô·L!2ÑÙ97 ´«º}ŒÁUK››ÎÒ¿{p~ Ñó R²ÿ§tüøÕOGpøDò7à°q3{”Ë/sMˆ>“rhY3º[îöɪêbÑW/IKlä{CXTŒiÆü¼¡o«¤øÜ– ®o¤µ“‰ ^XßÛ4‘ø£tIkÙm"àf‰¢ÒZDÏËêK�Ö�^ ˆJ‚CDÒ„Ò,mù¸‘ï§Ty°¡]Èœq0 Ú äò*òÓɦöy¥E͈TŒÉ.~㬞¶af¥µîž^ì€6¢t•Ó[ÒvIO-ÒZ<ÜIyù¬æVi‘†�M4Ò`zôøþ§‚#8X„¥£þ© ø_󡯥üõ#ŠËÂdt—ïKŒøÞ-$’GdË’½~´¢œ£³ÙÛnm÷…÷ýê#i³V(Eg¬üú^é#ù‡g6·gDúHÍåFÝ¢O‚_iÛ«h@"_«RÌ¿ª¾Ô�ID’†›È¿Ñs~Fgô=ŽŠ�’G&.J!BQIK®‡.vÍGÉÇråòîiêfGÌo{D‚ø.딢ÃênoƒÑˆ~+oÒôCúFšã©å†]›-"i$² ƒÚöJÉ�'w"¢#÷¼ßÛßÑ×"ßfýþ"¬çDf�ç½HïðuëAõηúw4è�ÿøwn±æ{îw$‘$جËk“ 1Ú‹…±^i“$â„÷[;¤‘´="!¥…E|¨4>ÙAâÊf5æœj·OîžšÏ{é 6ƒŸn»+i@$¿ ò’¹€ö{ë;"±Wº‡Ý¹Ö¾|’Ђß]sÍ¿ aˆLD�‹cDÇg½�ï=U7½ß+ÞäÞ/{ÌßÏðjhJßW+¦ö£rrß×BH±Øê÷òÿèßãI•îîÃzýÜE$¤´\³c›‰³—¿¹ME"ê)9«J-RZnÞHs¢‘‚£3!‘Xë8¡½1Ô�7ÛîJÉ?€ÒdIÿ·£qïq�X mö ×¥Ÿ¿ÿe´OàeõcnŸ‘É’±Øú]½ãë†Ý=¾~Daζ±ÞÅãìäúáv`y;ºf°waÇ8ïêÞIhWGuPE¿·×ÿA9o«t×.!-FßÈ÷ »x5 2y1 Äv ¡zpµÍUi!°khUáÉ%–ïæ�4=")ÊžKâbªþ¢ŽlÏ‚?l›+i@$_UÀ’E½‰½÷]‘ÉŒþ¯Ú€wiÐ Þ³´ŸwhÕ@:¿‡ÚþeýmWF¿ðéM£¼s[Æ L´¼}náô\¸ºÇ}ßáâÎqvfÓ(*š†B*ímL×ç©ué¯ö MˆE÷ŽˆM挔ûjxÕñÌQn÷Øv‘¥Îv"’6éð ‰�¬µ[Çæ5·‹])­ÒÓ H‘.qã¢ÄJðómo% ˆä9Npâô C‘ÆðöO³ >iKƼ‹µÇ÷lUL9ÛÆ)Ä®"ŒfC¿ãÚÞ‰vqÇx;¸jˆ-›ÐÁ›?ì-ˆì] וê„âºØ§à©µ™ÉŒëéK¹°c²ñÁ"’;‘´1"‘ {KŒE$¤µ�à«Õçæ9àÛÖJIïD“ 5õ”ú)ÐBHË9é1ΦØÛ6¿‡�ìølÊ–C"ŽHf|C͇@“�Ëo[…L¶ÉŠ­Ú;§¬øìjHdf³??‰_ò»2ì]_ç5ãoP^m;«h@"Ÿ‹’±“&•E:æ Èyî�7°éĦö €bZÜÁõ$�hL!6�æõ”‹NÐET(ÔÔ˜1úCÛ¾°¢ê,ü”æ´•j­Æ­å‹�µ¡ÝCqi­ë{$°;i&‘”�]ÔÒÈ6»íاDòe°5Îý ®ä–YgDbDóïâ=õ>Â÷ v`’à;»y´Ó4’ ©\Ù=+“Îôz<šL2q}1ÜS àì+O­ñÝ_�½ÊÖyØÅï™n%§©­Û;\ÅV[Ûé�DÎkpUñ¹Õ²Š�ò=î>o‘v(·�̤“½Å�¬aÐ|¶õ¯¤‘ü<8'Ûíœ! =r¶eAV©/)È–¹Ým?ÕUרªâÍœ²Ø¹¨—´œÉã¹g�È¥'Ýð”:»¡Y«!‘m úªì÷ÂŽ)nœo@$§ÌÊråþۺثn±Ò ðÔšOZkzÔïëË»'¨(%²R‹h$V7À_µþ•4 ’o�ŠçîE œ!Žojñã¹@œiÙA?�±à³JgíÉè‹iá8÷FNEðár©®“ë‡ÙÂo+-—ˆtWdÇÄEE!Ãpž9àu ¥¾å{~ëD:—gkl¯]_Tl)µuÜ�J‘´ÖÈÄï9ÇÃ]JiEY¥%-ðÒÎñ>Æùïõi4³.rå¾1Âbðã­{% ô‘‰ñèm â ÑO]èÚÅCX§J�×Ïèb›æt·C«Ù¥]R�8D�‡;SéB~ÎÂY´“Ý‹û �ê}‰¡(•ÕŸ’hÈDÜ\¬áÇÓ+’…!#.ÂèI#¸‡Së˜ÅŽ؃Š-ˆÄ 5!ÑD e—Z�^�8ÔÜ9ie—·5)¥ui×x;µa8éäQ8D�ÒóË|Ës–ƺ|¼¬Ô:WÒ€H~쉱&¢NqÒ0êoè&�Lí󲡇@ =èã˜Y%•’€4‰Ü>2ÃuùF �§(’Ú± '�ø�Ç�LèQÇú¸nÏS�ÐÕ ×v-€¦4ÎNo÷1‰Ü£Ñkóâf’l‘%þ`«Ò³:·6"Á EÚžZJk݈²Ü—[²�÷ìÈêÁêÓ¢‰—j­ñjhÝ ~¡õ­¤‘ü,ȉ5‘�¨Á°]ãc»~_Bõª‰Ùa"�kû¤ƒ¤< WK_”=G‚•6_Ÿ¡³O„ŽP6ÍîꧺbK"ü>Èø1£_vó»3J�DȃÏ6b±{¼�ˆ#’ˆi‰[ÌŠŽ˜ÝËÕÈÝÖ˜Úª->‰¶{—¶hÞHS¢n>�ÚàQ•(­r X7µ“¦uòžŠWSëdðÕÖµ’Dòàz¬v™(’ŠÁËJꤳԥ~"s˜¬IèBOEý#²“×}�ç" HDÂ#„ÂZ4_Ï1°S”‘êRd²lìû&2‰C©ïªImñèd hÕ0y&e¾sëHgðÁ¬@$øQÉ�CÒI@k3f”öSSxÂo>\ÜÄ÷ýÛ¹°ŸUeèçúx6ÎÓ6‹~$&wêýUŠñgEìGTƒnàsÿw5 Ž�Ùg¡º Õ<ø‹på�§¬²à «.ùš…ë~üðçR™H~ äÇD`¾0lt¬‹DðÈÒNç !tªF">!�„žçxJg¯,"0®C„\(”ž^¨ï�ˆ`ø·ØlÏReÆèwcí&¬Ta&3F¶Îïc'7ŒµëÇ„ �/3>7ìçŸ-@=é-*·l%!*iµ•Z…'2šdƒ’³u4ZÈx�ñFºa³ü®·ímÛü¾*%ÇE:i®2ðʧ>žÙyuq¼çUÍõÊoîóÊóïxòêø×ÑTm7/[yþ{�÷]¾öS©H$¿nÆ‚HpãÕ™.urùßWEÖ²q(÷zuïÄTNa‰@ü³# J å3D-½œO±‰��634»AR?Á(rágE(§7Œ {-–D¢ñ¹[æõÖ°ª‹;§Ô=8¿¬„H¤6 �FˆDÉvˆ$[D‚ع›oVñ÷.XEþAŒ—2ýp6˜Õ¨Ëo Äž�È0R¤}”vF׫·¸cã¬"’êøÍú/Ï´YñÂ?®*ù¾Wq{$QIDów¯Áð—©D$îÄ‚H¨\R4‚ý;áñ«Ò ¨Êò®¤V:ËUcu8 âPDAê ÒXj̦†,2D µWV�Õ‘ùb=][ËÏ-×ϵüN¢’)áÕSº„;Å ò�³>è”OkÆ;Cª ¬ŒZË_�HcPD²ÙluDeÀŠH\JèR« ß.ž‡»5JËx™4R¹%½ä&®¿þ0+5ÕR•%�ýøº!ŠœqÑn0•ºxÔû"’8�#¸žkK6û¬W[þi«�Þƒë5-øûŸ¿Ÿ*Dò_àAsÉ#r¤-‘•E�È›jíÔŽŠDR¡7Òˆ|ì§¡¤}‘˜£ºùâó ƒ E€d…ÂhPtăHõöüœˆB⼤jûâ!y½^4ÜÂ(Ä/Ÿþ¦ÑÅ®^‘Š +t=Y|rT""¹µÍìÎ~¥·œFRv±UùoA&JqEŽÕ¥ Xî¿¥¹jµr™«2{ì~hZd[i‹)*úHƒ›˜áô*�È“ˆ¿WøZ[ 3ûA«)íî•ß(‰Ñß+øóT ’'@MKzוÃg×,�}áò¾eêRO AÇð dº‹BXð•ÂBïÐc"_ Y*R"H"ŠÊ¡¸E¾ôìòëk¦ö(¤‹¿ÙDB㦪ÞÁ«ò_Ì‚hRzkd²E½$ŠHŠŽ( Qð½ó­x¼îe•Ut�puÁ1¯8' }m´\$æ{S$RÏ8kÓ³¤óy¶í?ÙšI¤¦ºâãTÖ$ÒXŠBbˆ³àÛà³É$’�×Ò®kÞ”êsÀ;‹në7ir‘d™J K u5ËéD‚9(�E"ýƒ•Å�B"I¤éÈÏ ßÎÎÈîûî3—Z2{�á]¤ ߤì÷5ÙŸ4˜ÆaáÜc÷ý )±`/QɃPD "•² mÆ 8\F³bq®w;gWxÇ’u4Ëm¢ñ’óÿÖæ…â9%$àïµÚH$\óKáò[‹½û×¼8ý� À“É$’÷Z@" �»=CQ ázzFÎo-û…$E!®™�Ä¥°°ÄF™çW^-Rê AL_n�g=�4’Îj ™îZ>jrç—¿}½9äÌ=E´ÝðÎ’ #UZ /˜7·hÎãHb‰Ü¡»³û7<­Þ&E‚{ÁSpÑaÍpw�‰­åyVYxÎdͳ�^òz¼úHÔéV4OÍþG“S pF7_×êH$TýGVU¸Í‰éñÃVðCi9‡"‘6B»ˆ„j-‰ìç6�‚H«�pvý~ ˯ÂRÙ®ZFdä៥s4¢‰4›L.®™Ùor§—¾UÔðdÈG#¿¦ê7¼´�P†)Kx»¹¡Þ…ÒE$ˆÊ� ØìŸ}Rq?ãÆ'ž­îkÀýžÖF,ºŠJv‹H E%Nho#xpë”mX4ÖëùúwšôùFp÷°ß¡Rpj¢þf»Áoµ�ÅÿÑ*n�?‰8ñý§’A"Ÿc�µh#²ÁÄ�7Þ;òŸÂ.áUX~/‡ÒXœýf¹†î–ˆ<€*­"ª°tÆr]Â9æÝ¼áëë�®›Õ?»k»o)eð0£ƒ€ÇåŠì0hÚáòñíé¿o÷Î6PÇŸïÊ\•¶Ñby{ìÂ!GŽ`xÌÂ*âq ‚4âȧ‰{½z�··ûé­Ü¶F$ŠLB¥—ìèÖŒûÝ^yäntŽ ÿÍgú‰ðš)�-gûäDþÝŽ€Mkypõs�ÇsàrÿλÀ�$˰qz ‰D–ðÓú¶S4²wi?ÆÖŽKx9/³|+“¹~#ái¤²D@i«Fô¸¢æÊÚð†9ýŒq$™¨â­?$2¼ý³:ãŽì{j½€e}姸VÏD[QtH)9é1ºØã¢ çœE2Ì2‘·w:²áy+¬ÞÚâc«w§�´5”£—ÜÏ»¿`âÀë¼£¶;šØó%ÃŽGéÕ9ËýÍWÜÿv9àÓ“D®ýèîK V€/%ƒH> ·tЦŒÌXOž=Ù™Ã$XQô�€.íƒÇªÀBópgDQHÒ†==Ô^]gûW�²A¹È‚ÄeàÕTšýî…ë~*\Yô&&‹ ’P" ý�¢üùrðwÉ"’?-�ËÞ“òßñÝžGî�pKÊ|E"4¦(‰d¹Ô»}íþ]uU᫾²ÞÔ%b–ç®� é"(Gõ È™hêŸó�ºRD2"vã GdÂ×\$Ã÷ô5=V*ì ž“ê¸fWU�veOJV2«·œ°¿”¬—ì²!_Ž–L(õÿ6šÞSlŸ³…DϸPë=ÎSäï§I�Å`8ø]ðé$G!ÿâUn7zã@òay¨�/­¹sîHøÞ¥ ÿë"—ÐÝ ×øG48T‘�ø§dÉ×@UË|¶4“�¡K/0§c¢×/¢ÇTj9cÅ!�È~ WzêvÃhN³J·¨ìvƒåë!�†#“Û»« ‹šzgUˆX€pÅ�N.8¯)sÎå x®Ç"—sÎäЕ#Þ»Š/WÙ¥k�ªE¯K%Ç£ ×0SBwdÎëv¤D&vþÈfoÀ‡ß·�>2Qu!Õ\õ¼3Ñ¢ l[Ù9œ"Î/¹Äñ3vŒO�?¿ ¾’›“�}!\}ïMzCòã2¤¬äB~áѵy׶ΟÍX‘=»ëµ»æWC&V}ë„]mÖŒ£íú޹|Dw©\½*Éý£dI÷¦µÔû@Õ;”g°�îjyÍ#‡0ðšB$D"ŽHÐE$¨'!Úh0�â¢RY®ÜV)¤ÓZ¨!Á¥–€+Ãm •UŸ»-¿ããÜhYT¡µ#—+~ê+—³K‰ékŽL8ëkw+êá:u}��Poccã‹¿Ùìj Šq�ýÈg[ì#˜›ÒIüê¶\ßøZÈäô� ƒÚ¿P‰f­n¢”öô~¯‘–ý€"›¡âOooç·M²Ë{¦KŒÇ§+ž^]5 \Y 'ø£x.z•wú…ä•Ção‰ä殿ëØôŽUǦw°ãÓ; g¸¯wޝ³œÃíøŒNÆ÷NÎëiŧ6bØyyCmѹähH-è!qƒpˆF4À ÿÛ“ÑW‹|ü£‘inf:¶'ŠFØ“Q Ôð‚%ma«vòTG©<—´’[ y,B±(Õ’§3Z…¢q"qd¢¨†ßWëÏ)ðšBS<óöÏ´³4Ô›ïzOi°¸À\#Àsà/@LôÂ5å®(\Ì}ª�½{óU>j»ksV�>q”Cö0²guÑùa›ÖÁ®m™AU÷‘¹«F~6$òU°¡¥óGº�!”:pìßonù°™}ÇÞÔsãuß‹Nd‘…‰ˆšþ͹¥ƒ=Ò_�“�ü<Èiié/á­R[ †¿•0ëxôu¬C"ŠHj.¯”Оœ’^7Ü™²‚�«!�¢€Ë�7Æðû#×�^zÆ‹Íîúªùé:‰œœõ:Dà"*®Q•å"‘�ÒuºÄG2ŽLTb¼Ï_Ðwê{Ž8DÈ›DN�Kýe�Ôd‹å=R£¡ Ê“U˜¯^)'ÌÇWÁŸF9Cýóêßò*ní%åÏt¢Wš»ûôñ™�ï7D 'fw­;³¨Ïà É,ðƒÉ´�ª­"w"Ìw~ [�×4ƒ„…>–ð‘ÏÝ<‘ûçäâëô‘DÉéçzÈ_$9³;'dj¡§SžywÏyvc£™°ã‡x´H{Ò]bº8ºèÉ‘‡ž»òbÁ¥îJ­("#‚Á‰0 öè¹"˜®4ž+2qQŠÒcâG$ü½‘¨¯æd@�G'v+g¯Mîÿ®ÒÙMi`ìü /�’aoýR¤½p牢ƭôL]Ø9Åi( À\ðÅÆ£�š_DPÉû H%òqÙ»¸U‰Ü=“1ø@ÁÑÌ‹®¸sj^¯Jø‡£“ƒà·“=± °–÷Zi$˜6ÆËgË ë¤´œ¯Véiu²ËŒ}$I³.¶»2^ˆÃͺˆè2o©çALµ�GtdBD‘ˆÈˆâ4@çª#àÄÁE*¸{F‘PäÁ­û£ëÔ};á´—ŒÐUÄõH4�H”nSöq7~7@ã Þ½Ù¶kõtCˆWª«ùëÅ7lðûO1æ÷y‰óóô€-Wß Pš+ŽÙ…ûà› h!?ª*y-丢�Ä »raÍØ=ˆæu" E!ݼ+›gf‡Êò²I«ÕxeWB…GV‡Ó =ÄÐS �Ïè8ù½d“ÈgÁÌXÉ@R[«&~¤aVñ׉J¦Jh‡HÔ„ˆŸV"ÓZN°uºä¡Q:¿âéJÌŒY˜ÃÞíÝ5‘I#ä¶Í/ÑÝåywφâE&œë%§ýpv%Æê¤?¡3Ïõu¿ìÙEr‡}2Ù9?¥qqˆj6‰‹"]?O´b{@&úûîËœc#;¿lñŠNZP¬£Fóè�¿²gº¢úOÔœ[uq¤âœbýùžùpw¼…j~?\Q�á•_¯NÆ}­)gEt‰K{±è»žÐb"ÑßO©³m.Š ˆ¤™Ñ ÚÉ�³»lê€÷)pn6h%ð†·¦–’áº345f¯Íl£Ajp¼ud®œ°åeaÏßÒE*4=‚&¥¶×صå_–R]òxøÁ�+)t_û~Xþ•{ÿùT!’'�ÅÝ(žÔë%ZÅn*"i,E"©¬Ù— �€ÅŠFüÙêñîX�ìu¾ \tØŽ�H(&M#@~?QIS‰Dòœøž D69ºÞ™ÈkxXgá9ºŽô ÝS5.nu3¹^{ËÈ$+rÀU@$1Xøî^;fûX·ˆéŸ-hr®òáS§ç yë& Žjt\8ò=Û¡Ï¥q¿¤Àdhj·FU_wOgàBmåffß³/ã•õ‘UäßI“{í�Mà/�<ÆR…H~Áõ�Ä&½¥†Äó[ÇÄ’H”ÆòK|Á"Uh¡‹¸#ñ‰FÔ`èÈâpe©n7+]ä k�ò†âÿ“}|SÉÄ£ù/\|* ¡xŽ8N‡¹çW|�¯lE&�‰î5)(E+^É)� ¶ØD%*J8àÏ&¹ìˆ­y¤�IuqŽÜ0߆vx>dòµû]_úÆuúQ*{½þˆG££½mœÝSåÃWåí±ÄHw)í±¨òëNöB2õ�“HÀW©Êã=¸¡”oš <¤í¤‘ü¸;"ùº�îüäA1Ø}W_7¬ŠÆC¥´°D‰k¥–�8q0i7]P9ölí´Ö‡ÀÿE“_³*–ÂìÂÃ¥I–=áa‚qð£g$)�^ Šqö1J'^Ïl ‘€,7vXb¿ú_®h3ØË·,%“n�ÍÝMÓ?[H(„RGƒc-c§ 2±É½ÚÙôþ¯Ñ¯ö¶m›ß—Ñ¿cÙ¸NdnÏ Ö�ajz¼Á|Ȥ!1>Óç­‚?>îºiv�gé:©F$¿ ®ÇˆHÔá>ªÓÇóHº¶¨³MD¤‹PÞ+Á”‘óRˆ?sÝ#‰§ÀîæXp\ÞÞMÔ"w:9?¤ÑˆJšA&X{ßÞk^J9ÞFê)U|Õw�w·<"qN;LS×±$&ŽAÏIEáYÛ°h,�Êß¶Ž�IlRçßÔð·¾o=¦±Ô㺾 çaš5Žz=çÕ“;Ùž%•þröB&PŠº’Ïëu* Biv_O‚?I1 qDò{àF¬ˆ„0” /1Óß—-£ZD$”öŠDHkiPQˆÈƒ³ˆÄŸ~бâ„tn…Ó]÷3ä!â ý’¶{Õ{{w³¢¥¸JN›«“O+äé¾Ç,½Eԩޥю)])ˆNêî^´usG¸õ!–)tª¼4…ômOHG¡Ð‡ŒÈ÷D.Ì•WÚËœyêv��`˜Ïn­‹ŠÓ§Á7¨”ût› R[rÿ�7ôM*-úÙµ}“šY­5UÞYTh©O ‡G"Nq>Yê|ÞÙ0çz%Ün9Ù¹ôÒó‚q]Ó…w¼�•¤ç‚©û®Ô^f ˆðwfƒà6 Äp”ïõã6®ç®y1p–÷™R_nÏ(²;ûkmÞ¿ìI£K¿ûxü·[´S”H~ä‹z¿þ‚ûË„™� /lj®í�Ø$7`Ê~e}‚¸.MÒHHƒ!p30œs/),„_×�žjðîž©æºëšúzÃù›ÍCƒHÛEª(6é-çLL4¢¾wObE²™äZoÞ{¦½$æÄB9ò;×* N_sóÓÓÇÁ×ÀgR�HþÜ+`Þ¨ˆdÑÈwd•raûXfŒ²+»'4)µ‰¨Ñm$1DBøëJ|e"¸Cg7l*u–0dWÝÔ×ë�pQvkOoEïP‰2¥¼c�$À™½«màûq'E>=_ÿŽÞ´0 ‰yi|ÏŽDÎ^Ou"ùP ,–é­Ùƒ^·ùÃÞ´ zÚîŒ>¶sa/ËÝ6†H#êˆDå¾h"‰�«õ�Î=×õˆ ¦§z9¨wïB-é¸&’ é­[»[qz+úqÈ‚"Ð]~×ù8h_Adrxó"¯[»o{ñ$Šíض%*Më�@ÐÜõt!’GA0!FÜÊõ®-÷¾-ÿ�eNëd—v©I±)D’øAUšÈ·ì�´‘ÄÈ/-@í݋޺y#î,8†©,L ="­øÊ‘t¿Oø<¾^WrîÓéD$osˆ¡UÊœÁ¯³I�¬™ÒÁ®î™Ø$"¹w&¡DâfŸ»h„„v§�¤>òª½‚Ýå®Ì1Jxh@ô¥¤ezKŽÂ76´€D7Å‘¿·ÓÅ"L(cì�4-nX8ƈLbF"½^´lߺY…ÕŹag/’–àÚ+nM×Ü×\‘t"’‘q(ÍÓ€«éýÚ‰HŽx›y=,¯i忊HüßÄ Õ;Ê“—¨«šóÉ4JqäQÒ{ª’E1õkÖ¢›¥Å3»¹µÀçon9‘¸æÓí®¯ÑÝM‡Œ­cA@&µ%lõÌ¡-¯ÔBòá³7Ð_Îñ{kÒžDªîl³pÍ/¥…8"�ˆdÀ;�k.É’1ïÙ R[‡W ¤jk²]¢Š‹ê­(‰dq‚ˆ$Ëõ�8;øÍÊH»Å•&Ã�wkG]ÔQ‰[t!Ñ»é×W¢”Ó­� è$M'7*¹`·ßá~Éõ”‚_{0$k_˜ñ½š-q ¤Kƒ¥É1Ôï�'«—LêSrçÒÁkü¾Ú´wS.¿•í…kåæ›ŽD24DÂìv„¾'Á�ˆÛ‚Áòß:™5<êÔý#ñõÑzx~·& îqÂ"•ž»š¢£¡&D$¤sÒóCXx@D›ô–ÈÄy{¹ÉŽwã0Ï=°¡÷¶®˜^Òå¥o>€ ÒLÜ×}ðlxÉÄ>ÕTc….ß\ �”0 ÊE!éŒðƒë—k*Šÿ% )ÄÉ x &køâîÛ½¸OÔDBD7"qίòÑÚï[‰»¦Ã´G¸è˜5=½EépÉ©ò´ÔInïŠazKDâàÆ�æ ¢’øÉ‚T<&/zå·Ï„éq óµVúz‰¬ªŠf˜…åæ›îDò;àZœˆD]îÃ>zÊÖNé ¾’-s»G[½¥)ˆI\Äv7!Oé‹CÒEZS'³‡M¼G5S“Ó[·wW@¦ué™ÞÊŠ}TœçZÑ¡ *I ©€ÖüúÂÔAÏ3³ŸJs qDòàP¼ˆD\ï>.Ñ}ÖÀ×lOFߨ*¶Ûåþ‹m|üª¶X í6!’ÖµÛ¤[=LSe3ª·j¹i•hjäÖÉF"W°Å™”oÑ૤U®,nf?Ö (ÄÉWÀv`ñ‚f”t|–¾’WìÄúa" Ò[*nlIÁÑYòÚŠ‘ÈSk¿Ü^ɳ·²ªzJŠO˜Ç¢Øä¨¤`O9¿£.M«·bN$®BÁIÖˆJ@nPÅ 9͆9VSö½ϾØJ(ÄÉçÀüx‰Ê�étŸÑÿË¡ø%À˜8:"Ék`¼.I|‰äæ&7&·5¦,¼{Íc—ÎkmjTRMå×ý´{½�IËÉÄ¥¸JNFŽæ  \y•k¼PÍŸ~ªu"“ñ&’>o>*"¡�„é‰�Í�Lü&Åñœ]ÿˆHøb{ÜR[ ËŸCq¦ÕЍÞ�ËiS£’°Wtô&;¨pú¤ ({ŽY?‰ÃÃZ‰3v$%ê¢ØFËŨ¸yËjJ{„C¡Ÿh¥âˆäC`ñD÷vßTOÉÊ É6åôÆÅ›ãš‘(­U”=WóHb‘d=T¹µÅYÄ·ÆPÚ+>b! ƒ&ö”ì(�\«Ò+µµ7öDâ�å¢A)Ñli3”Œ×��W.¿½Ì««ø{3Ó<‘ÖN$í@Xœµu¼Oêõ’Y=Ø.îgû–öSt�Dê#ÚïŸËˆ¹ØîšyLÞl‘´Úœlé¹jRUš ØdÑ��Yid)âoYÛ²ÆÄè+º¼‚½¾'Û E´¤FƒÈ$ÀÃ¥½Ìì+n¡mDò-P,@/y\‘É™M#Õ¤ˆ^‘¸ˆÄn™©fDLUþ[÷_í,]€R[ÅÙ­ö MTf�­s;õè-S<ÄëÚ4ªÚ #†×º×ðâ㌰ɬ@ƒÏ@¶"®%@€JÒYS\UV"’?kdJb\ºÞ§ôaøÌÚ!è%]ìÊž LOœFBZËœWDc"‰tyÝé&âµâðÚ³¢#5"†¦‰QNb�öÒ„H<ˆ$”"qp3L´!‘CðÕ¶=¦7@x×+¿)ø¶F$¿rE$]^úº kÿŒíXÔ[Þ[—!�ãë†J/�H(û�)¡½ÆjÌ«fûÂÍÐILfˆ¥i�ú£ˆ*y$™êRƒk®•ˆL¤ŸDùð ’4*=¿éÝÏëH:ë‡Ú*‡D¦¶Î¨•¬œÔÑVOjo‹F¾m¨âò‰Äé$Uô‘ÔÅ2"¹é„v5#²H¶�ŽÚ’“I–ÕÂzý!MƒÙ퇒L"ªèB/q&� 560(¿qŸÊ¬EáÊ¢¼_^£òÞ¶N$_ €%#;}ÏfxÕÖMíh¾m Põ–t’Š Ë ’5±õ×Jo¹qª×Z?Ê.x¤·"‰Ä‹ŽHdä¨Å0µM�¥‰y~y\�›û^–«ª®@3iM©·¼üpUÉ÷Ͷ: ÈäP“`"¡ãý;6gðë¶gI_;·e´�¾u ½$³E$ŒÛ�eD4ÄÊdïBèÖ¶°«Þrˆ6½uN vêɉÔ!épšc¢Š@¢“V4ª7@èÁ�«µåw X£~"ùEp"DÂlwE$Ûç÷¤zk$âtu¶×\Y»™$¤µø€ƒ]®£½ÍÆÝ=íQ½Ý‚ûð÷oïIáÔLžšym)A$N‡Ó¸Þãþì÷‹Á"ÜPYx4\[ùŸc4N&“¡“ zÿI ¿ZÄl÷È.÷¢ì9ê#‰Íp«,÷‰hPÑé¶•n`1#¥çE¥%\YjÜLé×–¿%eˆÄ N+ØÕ:¦+zHµWU4;\[ñ;S|2‘ü-(L™ þàI÷wŒÔ–ëp/=½På¿uW[<·=ÒxÏÍân“�È%'•4�Lä¿•ÂQÉUù_q�)A$ÀÍ1áºÒÛv>°;)gUw3ûrÀÑÉ—“$ºÓ[ò c¶»Ì/í/Ï­ûçKÁk+FÝì[Á6‰ *ýUÞÿZ›BôciëûÞ�(.”²Z )$ôœÔJo‰Hö¥¯Ãt@"•�Hg3û|ÀM#“qv) D‡ïÿ7QÉS–9­“¢’+{&1>|‘H„h$&D‚À¶>àx›$ RÔfF%žW ÿ-/u£’ƒ)C$δèˆÞo®˜ëL‹è$HgÕYõÝfö…€šL$_û" û`�‚¦Â˜ ¿st—ïÛ² mrß×mÜÞV‘»"v9kÍ’Ø£¾H¤í  ,f܇†¢ÐHT’¿%ÄÀ¬pêv¸ç¨)0¥ˆ„ˆ„Í‹›¬È9è)I‡ù6U%óÍìGVh~Tò÷à°$è%BŸ7enÉ[Tu.¢³}]ÌJ?]4Òv?̪àjfT’åy%'S7½ÅßTZN$IÞHvõÖvm^4ï]�ЧS<ÍXÀו,©­®üù€ ZF$ŸÃ� IHsõÇj~þðwìNö«½²&U[®£�ÔN0ŒH îΆˆ4"ºì«KÕôHÁôÖ6ß’g¿´ R9µh"5¥ã=Ï~:`‚Ø�Éo�õ.â•w2éMD²l\{+:ñqåÖêØ‰š·˜ïÒ|˜¹QúS=œÞªÃm·.}¢­äG%ndAq¶®/ï]€Š[WÂUw߉ý0ª€L‡4 H59éþÊ7­çk߮LjÑ=Ž Ñôe’âúÝi^Ò̈$«"Ù¢Ô–>ÈAŽZ÷û“&G%€™îÇ«R6½UvÉìæ¶”!gÍs[ýKêv"’TÂõj«º“au•‡e48Þþî¿~¡×kß6죧B}Þ|Ô†~ø$xÊFuzV†OBgÀÙ'›o¡s´ˆLôû·Îëceç–6O#Éß Õô#ºDAŽÚ•Ì:qº4f/_ÁbX—.&ŽÉ'md\z+E62ÊóoYMÙáp8žƒ¨‚£÷ë�ü×ÈŽÏÞÙñä9›Ç ‘yCÞ°ý_±ž¯~Ë ëÁ™HE$2„ÆÂAï}—ÔÔ#"–æÌu"£­óûД˜A pfK†Xõ�¸ª­`GøŠŽ49*aa # ‡Ró>æ‰ S)½%h#³Ã™…›™d‚ª¬Ê‚ãáªÒÿJÀ2=÷_¿6¦Ë÷NÍøª-þ–­™Ü^½[æv“­ÉÔ¾/Û,¾7¶Ë÷lðûOØ4žóó6¼ýÓF™4-ÕEM3ݼû]þ�Þô’,n‘øC¬ ÷ú$¹ø¨Ê/"y8ŵ«2i°AQ:K:5^&¿‚k«®Ke’ÕS ²`›…ëþ0AËhp´é±žÞÿÕ-+Æ`L1”¹âÎ…½ìÈêA¶yN7-nšÝÕVC0 ñÊZ5é#ÃÍryÍFu|V$Ò½Ý7›B&ŠF&÷ngÛô•ØÞ"ñÓY`‹v€ÚuˆÚ¯pÈk¸+\HSÛ”’SzM©¥••Ü9 �N•ƒe?ð*Àm«¼ýW \FƒãÕ·º~vÕäÎÝv,ìU³'£�í^ÜÛ¯$sÅck‡Ø©¬áz¾Y¾×Çvñýµ�MÖŒÎ6¥ÏËÒ: ¥Á  ѾÑ1¼= ’E#߷ë‡+"i‘À‰¯–*fJT-Iã^UM�JÜżÔ|MÅÇC\c]Š ÈRzUïGádâgâÏæ'èVOôq$sô�Ï6‹÷'!HD6&¹ÛÇ2Õp¬ålcÌ]—OÖ¡•ìÀòþŸK„¢Ô×Ü!oH¨G€—vÒ�Ь†ˆÑžÙ“D6�D$eç–µðC»Û÷ÖŠÜ;À†»Â7;’ˆYÚù§n×e� ®P̈äzˆÆÌ‘(™”+çC‰ÝØX ‚‘d—÷LúÈbî¹� dÛ»¶w¢\z�'Ù•ÝìÒÎñ"˜ó „£¨dÓ¬®¶af›ˆžÒÍdz¿WD*46"¸S=$ç·MÂ"e%‹[sš7øÉ£äQ4B §±TL€;‡›A$',µ;Ý·»ë�y4�L²%k“St8qI€Õà“¼”ǵ}“ÿóÊî‰ù�†#‘H0"Wãr/ï¯håøº¡`ˆí[ÚÏÐL�¬œð¡ˆ¤k#é-€XÿŒåî˜Ò¼FD‘ÈFå£YDD"ÀE$Á·Ñ‘¼ªr‹:½%"9™ÂDrUENto9�´<¢qi×=z_Æý(Ó@`y’ Ç�“¿ aŒuÀ£r!” vqÇ8;·y”´”U?2"a|÷çmà»���*õíÖî*Ž$’QÌr¿²w¦¢Šæ í›Ü+-vwÏD3ƒ$@ѱ&ì¼7ˆ S–H€®/³×"ˆïØÞëJo™ª·Jâ8,,€6ƒÇ@ЭžJDñ ` 0‡F…èDé®™ÃìèšÁ¶kQo[:ö}¥¸(VßÉÈÏXÿw³H"™Ð³]¨ôì²0DÒ|§ßüMn×§ùì¥ç"‰rÚ ºB´Dæ¾Ö¤4‘¯ød ѓ݅4^Rt$þ[A*ëgRw5 ÈäipX�†r™tXÎ6‰ò¶›ê¯µS:H„Ÿ=è5�Ø¥[ÞE'TxÙŒ�oÕÔ^Y[×"£<¢ š%ÙM5j À=ƒ$¢ÑHêˆöÊR›H¸¶’S×v×�DqDr÷Tü^s€íà×S{% ˆägÁ`MAžO*ÐOöSÙE_ŠR]ô ¨©‘Ra52Ž B™7ªcyíÕµ!ïz ºÚ öª:†…d$ÒÌ!Q�#3ìgßïÈKi")>ÖRÁ<¶sqŠÔ¿{�ȤÇj�I'Ö H?9µa8��ƒmÇ‚žF…—HQ^ʘn/†‹O.ò ’9®ªù«ôL³,)‚&Åó�ûp ™áÜâßÔ¦xD¢E<±¤!T‚ó`‚ðXR‡Åqy�î‚ñàWÓg% ˆäWÀJà5“L��îRS#ú‰R]kHyáí%\Ø1ÉšM$ˆ™ªØ*>Ú]$ˆJ ö}"‘x7wÜa¢\yj÷ÈäÖß#ÓtR¸ö€,pœÙà OËÁ\0tß¿‰ý0ø Vå_æzÚ� T Ð"x`/øf«§!™ü>X Â-!“+¾~¢fF'öxÁæ{ËŽgްêK«šg‘("):¤H¤™%¿îžiÜ:µ”zeï¸Óú&'Vƒ�àIðKàGÁWÀÏ€Ÿ? ~|||º±Ï M’Ÿ6úÀÿ€gÁÛ`ÈSÀ2°œ•À4ˆ<04ýûC2ùM0ÔDO G1®ÿD]ò[çv·-àÜæÑŒÚ���üš¦ÙÆC&®{¸$;HiÅd¾ûúÆPëÝ=[L ) 6FK"UàšO í@\gws}ÿËÞ9I²-´ß¶mÛ¶mÛ¶mKkÛ¶=Zc4mTUþ³õÔÝÞß73½½™'nw÷žÈ›·†Û¿½‹/›×À௰à"@®aZáð$˜²¨ ™Ü~ ýà ”™.¬ÙÉé­Ö•ñwÿS"-3%waÙð†²@‹ìÊf]?råÛO:mº{kµá-Ï‹4„Þrj}¨Î‹µ¸ÒµÁûÿ?µÀ»àápÇPM„Êæ6px|C…Ý0Á.8íu(œ>ø3q3Ñ?‡,nœµÜ¯€/Âz¹EpG¡˜þe¸CÈÂÂÊáYð=8~s2vœ4ŒŒd3Ñ!­àü‘‘Ù~ÂðÎ8¬Ç(U[W‚È qÕ¶Lr‘ý"r—�E5¹Ü ^?�¿Áá[ðø4ü6À)ˆ@ä (A;üì\‹ªùçß¡w˜ëOpÂ~î~7εÕèW$”ÛÀ“à‡pJ•ŠîÌÞÞ‰sÑC£p”®ÁÄÓ�«×K"‡j»N•îʹ¹ÄBu!”ÛÁ+a$ËÔI‚‚;Ó€ËOÖ™Z…MñL$£¶åÈn—šHõC¯âGj[ˆéž¯˜~nÈ¢.d2„00I&–ÚV+nÇÚ²3·4‰5™HFo./8y°=;¤ö‡¸"ØPî²°°¨+©Ü¾Q�Ø‘Y’¿¸BŠ­«Êg$=[ƒC¬¬Ð>Š$Ž9¬-q+g%üt5<ÄÕéz¹ÄJ§T°ƒ�,,êp†×ûà8¸‘– o-Q‘”?qk0cË í£¼¶$ÚàT-¼Çš®Š—^.>¹è‰~-,êL&ãàé0©¯iZßàÉù:´UéÔ9­‘Ä[F[$'ý1„åV™ÁUƒÃ�0Ùv£zIW—›OÚf€õlâx÷ÞÆ©Mž^t¬Ô¶ª¼HØê\%2xf,:&càT‰zI¾rVÒ\òº´îàdâŸU“lãs»çdŸ YXXÔgôŸ;±ØºòåÚ÷Uœþ›8:v…]ÃscG¢,F,–/ºo«�½ÏÚy8ýçĸ ÎÀy~»¨xC­âfúVæ]¹uÈ¢~q<&A×�·IÑbûÀ©±ê¨ŒdkžSó§Got[ùïÃG3 oøkUT ¥þ³Rˆ�”bü””â§ù|B…â !“T×�[H}AD&†,,,êZ&w�wÀ(\` »V#Ë‚uâDšY\ù :zÒÊñï×λ”8¯”}qˆhÛ?yŒYXXÔ¥LÆÁ#à·ÑÊÂ{Çt5µÑQäÂNÄQª°®Äˆ h'Ž@ä#Ç„ !È4œV‘@p ÿ³ ‚û}q¨lÄå:ï×÷9Ü— –Tç~lÝ%‰s›$É‘ ÙI®ïp0ÄÅ ®¿F"ÝHdÒ½‰Íä²°¨s¡Ü > ÀÓ…‰ƒgÆpÝ‚á±õ8«Þc qyå·N9¸É De°P‘ µíÖŽÝã·Lw£~¦ƒ×ï’l垣üv2�×õ3"Ñ÷!¸¨­ÞË0ïÖ,¤ÿÂGôÔj‰œ\%QˆŸY¯Ï ¶î dÄŠ÷C±ó;׺ï?ÏB$ãBu/“ÛÁÛ`9;GX$ç�qVb ]ˆqÎûPù!®M"ýÚ¹kv0pa«f!™ž&¾ SØ(tè:ì4xi'¢8M&qD¨oÀYIvì„ÎyV¯! îß¡b)øò@‚¨ô]·!�5;½VúŽ-•Þà ¥÷È"X¬2‰ŸÝ Y ™ ï:~®ïøÊ÷ ’jFBÔ¿P,,L&¥síƒ%¼û=2xv"¹@‡V1Ædµ¸ãÅ�Æ™Åå”_[²Sò}—3„­Ú‰µîRè̵³GJâüf8ê€  rÑ6=¦CWÙ¾½‡Œƒç7jv‘î>¤÷ÄN¯2 ~;$áãËôý=- ”ΆYÒÕ4Gº›çªPÂÇ–I #~›o?4ãw m= �Ü÷‰……5’‰l(xëê¿ÀI(�£¾Ý|‘sÞ3ågqm�|ûV'|b…>¾ÜfZ­™‚Ÿ5h *›dÇ>IÁÀ¥*†þó[´ÎA¶¡’jßCƱ]EÀuÍ>޾�¿†¾¯»yž�q¨<:gKÇ¡Òqpš ¥£a†t·Ì—²”¾£K¥uß$˜<y¼²°°¸&ÏCw‡ÁïÆH(Æà¹,kH2å²’RÛÚ|wÓìBGÃLÍækæÐÕ4W?“)(d š¹ Í\ ¬ÖŒ#Š„b§Öª„Ê9é�À"D'²½� ’éÒv`жí¾P¸G@EÛ‘È×Á6t´°™Tn O†?Áˆ1ZtxÔCÊ qyëdèÔbi§3G&%†“J]tò��—;ôÚò]3ä ­Oõ]¬r@Áð×%ÏѪœ¸®Ÿ¹d"Äk?0Õ£~‡©Èeº¨\öOQ‘@&#’Û…,,,,T(™îËBùœ†ˆ1*g—”¤wW¶\V⶯•Þ&Írtì :ò,¸ ¾¶ 3 ÍÍ^n;ƒèi®l«¥îâ¢ö�•SEr©œ)…ðl‘œNzB¹@™$©²Hÿ,�@¤Êëül—`0t‡ÿ®äW>&…²_ …P½ÊxÈåÍÕ3o—ÄÇgÉ¢ú¯ÏI9S á $ºÀˆNýI’ôDs:ÑeÊÌ“¼RLëðT>Inn'»vˆ"èIB®î¨°½0"ù¼UÙõçfjè4æ¼RH.²XNJM%Qꙟúœu¹üVr 0Y¤ãÛQÔ|•ñ g/g³ká³×”’I¨²Ý“2ÑñØ,Gvo…]IºPbI|¹2:]&±Ä•¸§Ò¹Ãì”ÜVL3_‡µÿ@Žïà ô)•,Je’i�ÕvÇœ_éx‡ÿ[ÏlüUj:è:ÔEñøòw$á.R““EâK^¤ú…ÑÉÍuÐxªñ+Ÿ'7·Š«�R¹P2Líq Š»âÂÞY¾\Ü8“ë'_SýH'ñMMÔz.‡Õ?³›ÍÓ Ÿ¼Lk,iY›K™-.Ëtbf H¶Hn¥ú3_[‡í‡š úQöKóØË�³ky˜TRq-VÙùÝÿ“ÉD÷øsV,áKÄ—<[ H©\ßèèî5ŸGAõ³ä�c[ìÚ c:á¤q¬]Ú?¯>¶þWQ�>u‘¤“öŸVƒÈ°KìÜ�ãÖ8¬þ˜ÜüRv³ãäd¢Q�XLk,‘g™Íð¯àBü¿pï cèz& îe‚Ú+(¨þ˜ýÂNvmgK…ü® ·ë?ÎØº_ê+P$$wTƝÆdn˱óòOn-^MŽU¥BÑÍãQíè2o|ÃoÌ4ïêªKª†™ÖÉ�uÐx<ùå/�—Û@®5çU¨=N­‰uÆÚòGêv}¤‹ß¹’g*€QAdnNQóéR*ß`¿°ƒ+öB K»¹rp�–{»¦zæ„$ÑyeK^§F“¹­ö«¯æ òvíê°b”µ“8·ó_S¹b%–¸(—w)€QÆÌ×¥¨õòŠ¿"Ç.s¡$õæzxI8±ñ·Ýn&Ió"`}YÀÉB¹– ›�3^ñÃ\(2�¯°»*@6»(“Æétp¥< åšR(�å òWv³Þ°žˆwó;½‰ÎÖMIåoº[À å6ÞH^a+;ºÉDÒ[­åâG3…WÉ[\™äNƯ|‘ÜlvèʤuœªG–ÉÕ*¿ &¹oIî§àÒ˜ùj&vG~i9™x˜ÊÄ´'jöö¿×&y­Ê%×VÐ"º±öka/W¢Ý\º|`~YŠ„º,‘šäé ºÃÌWç¸õãØcòðÞ´6—eÑÝt¹ø;S˜F öêO5naï É­Á²FÒÕD2[r;S“í'kÇ><àE¢kGWd:|e±.ù…ä® zCûÕç°k[ƒ»ØnÍîøç¶ÓçIIQÒ”´$ŽÄ’¬“üZòlÉuôG­W Þ|†ÙË¥°þ2)‡§I>&y�äa’'Jž,yºäþ’M÷VØ4>> [ƒC)�Ú+ÿ€tôpý!I’qPù »V_ ¹öz6ѽ˜ùê ú 3Ë)øòêç‰ÄË­dæ+èO‘ßx`Ò¶7RÿÉf¾¹€þ•øõÇÞÔŸŸ¶²Kˆù† úE͇“›ÝØoïÿüeƒAÇÞý(¨þ_ÊÄôÇáC»nÂÆ f¾…ñŠß'Çògöý‘ SPû=3__À`‰s=ŠZï 7›™¡kPbò+³’È¿‡€Á¥ƒæãÉ+.$'£§±4Bö ‡Éµ³ì÷¯ðw ocæ[*|IÝŠüòûÙÉŸží½ù:›ä9Ú+ß�™oOÌ×V0|¤H!™-ѽ] ±½È«=^Àðã r3öËèåajOÄI;û|£A'Ñ�´“[Ú»‰d"1náe F‡‰Ú÷c/¿®G'×½$h=IÀháÄ}�”ɪ)æòr6›ä~ F�Žý»’_ù%9–;é‰Ä/þ•™¯§`4%Ä×5aãÕìWr·§á½ÜvŠÚWÌ| Ô^L~éR{ØË6ÙÍv2Ì’œy2—\Û#¿üo)‘‡(€4Ã|ufºÇí'rÜz»ñŠ_Ö^ù«±“ÿ¹Ù_³_ü¾‰Ú/5Ä7RÝÀs¹Wµ‡‚þ¿v… Ø‹n.ÉÀÖÃIEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/teawie-spooky.png0000644000175100017510000052211615224505336025415 0ustar runnerrunner‰PNG  IHDRœ£m£A¤IDATxÚì™°K†ÿsNw��Ü\áÙ¶mÛ¶m³ìWÚ²vËÜ2×FéÙ¶q• º{;YÛž¯êk$#�ù/::::::þìÞtvvþ¶´³Š¦‚ƒ�uœ‹`ù‹>ÞÅ$f—(U»} ¢³³ó/¡¨:;;íLðòà·‚? ~7øÓà3Áǃ_ ~5øåàÅÁ�Cé :;;ÿ´‚ŽŽ2i–qT–ø/á4ÑúmÌNZTÆ,1ó3,2OD›ó6¡ß À1¡?Ë{߃÷ï€h€EGGÇDÐñ ‹ä,ÑFÁ^p1¸a0%fÏ"-‡¿’rvŽØùEäoÝévT±³öSbªxüç²yœ¦{h¥¶±ÖZ”aïýX ð¥µËöDd_Ñêlò´7¼ÓÞ¹�¬¢££ãÿ=p:Dd*»ïp{ðºàõ,r€KDä*]ïO î`+&Ú�ˆ6æL‰¨ @ƒ?‚x?ë½ûŠ(u†6fkíqÎÙ½¼sÞDZ¯ý‡Ïû®mûö Æ )rdYŽQëAÄ€=ÊhŠâˆ“4Õq’”i‘mcâ䄦iŽqÖÎx3ø9:þ+ÉòB´(SäeÂÄ ð~¦ÈЏH‹œˆJý‘Æ:ºÀéPJ-1óM ÞÜ‹ˆ6&æ�°¤&ŠŠ`%IÄ"-ñöÄ|ˆŽ"ÐiD¸„ó 8…˜6Q�Rz¼î¿Y|L¼iRä÷Œ_ÄJíœMû33yøÃÚº>Ø[ xÿ6€uügQym;—÷ûÛgeaˆï4�Ž©�÷GºáðÌ Ñ’$3Ãy�°ë@^–sÞû}]kO‚wOGèø·Q}5è’¦m€¿�(a£Ì¢µE‘•¥Iv^–æ—9koU£+mÛ^1ŠâK’$=½®«�wÇÆ&9³ÖÎ7£ê\ÉÏ "€Gê¬]rÎí `Ûà+$òÉX¡VÁg­µGj£çÁІ¦‡θ›|"6Üãlzè©Øô¸‹€ùͰ¼²ŠÕ·^�(†m-ˆˆMdL’¦Sqžï5Z[;ÀŽÁÏÀüá?ï\;œÃùM#ï%J¢•ÞQDviÛv§õÕ•¬³›ч!(ò4_Ëí™DÉ¥MSßÂè–&óÁÌ´33ojL´a”Å¢({I’”~.ÏË�ˆh릭w–=ÆË�ƒj»ØD Jä­ÿ»0ºÀ鈢D˜h7w€è>fÞš˜µ÷žPè1vŒ:káZ‹Pœ“±wc˜9Ì3‰È¤ŸÀ¬X$÷Þ/8È{w²µö˜0ßE½=‹¤aL½Áý™i|þé'pÖ ˆÆëŒçPxßóÿI/bŽ£/lU/t°ʆ½ g"Lo¹3$) â:ïa°ÅNØhߣ±ÁA'Ãå|þʳpu&‚(aÀGåÔÔ†q’ìÎJ¶¹¡VúåIøtüc *ˆèˆà…ER<�Æé��m/"¦cE©ýË~ÿèbªä¦;îÆS‹vXࢦi.³Î�¡•>ȇ1&*Ó4S£jÄÎ9 ACuP)EuSS]�¸©kUCY®�¿—°®É³¢,{ý�ìèàŽ‡ÇñZ³W4èèç�HE›Áûk‰p‡ˆÆâ�û�:%03XÆLÆJ&sQ " bž„C) Ï80“­ÄDZGqÜ ã ud¶IÒ´tÖ±(�îÍ¢Üj7ÔM‹fmmÝ1IÅÓι�¼sŸª<ÿ^–‚&8Ä�7œµKÚÙÙF-¿÷6tåf;À�@ÀÑ¢©9Ìí°7>n°„�žø!l5‚0ƒ‰˜˜t’¦}kívÕhx1WIœ½ FÇ_MŦ_öw4Z__×Õc®Œ¢äP¥Õ&&‰¦ó¢Hgfçb)«¦*«º*‡Ë«eµ>Ü4\ÿ-Y©��1³ÄÔó„È{ÏaNF°š¦�ç¬m¶Æ!h@<ùnò¹h�Öš-\æ,ÔÀBše‡±§í�Ö…8z€:ÿS$IÎÚ À=Dtoã½�ƒô«�ùU¯õ$h&ŠR“^EL–QZÃ�…Í6ÁÖ»ï�lûý1wÄ9hš¾©˜¥‹° k££(NH…QmQìq¶ºøPÖGýÙGhW>�m�‰©ôÎíéêú5Òêü«!Lx”ˆ.cæ¼÷#Ž¢T¾µ¯Û¶=X)5ë«U^ÿâ3dmƒlfï1�p`˜¼‡™¬óû ?5�Ï^yhkœµd"›8ž÷ÀAÃõõ#­m=Ùý™í/ƒˆÆÿ¸?“Y][_{(„Í‘ž0Ç"Fý!"öÎSSU´¼ü×֍޾C'l´V«ëkE1äZç °Öh]‹Ö¶`f8ï�$)Ê~Y^LêBXÐïOýŒ�³nÙÖðwº%cÇ¡ 33ÏÎ233ÏÝw™™™™™™–™‰fi˜'ŒYÝO«JMßÍcXÈW9å¨tB²[ÿwÌÔi3X±j5óæ/ ±±ß÷ j¯XTÖ’¥&cVb±éTÕ#€?.8㼨p�©À'ž½qŠ0ÁZ”µ–R±q'”LM Á,�`qœ ö!ˆ©©¬¬$æFžtm Uuuá@ó ¹�ú:»Èå=*'Îdö©W0íø iX},©ÙË1:Bnhˆ¢Wíâ[(EjÑfžz%•­3HOšE´¶‘¾Ý[ñz:°Ægx«q…5f‘-ú7ù¿åí8W§ª«çZkk!œn‹ÅßyѺËú>Ö²2‹¤*dÉö´S·p ‘X%„X@†•±ªZêf/£yãéHM3O?†ŸBa%�:Á ¬"•N·)ÇY›šb­mÇÙX§œÀÍÔƒ\""_ˆDb—%“³âÆíº „„D¢•©¦eÖ,’µÕÒ¹g7ù\ãÑZ#:tëX©ÌUu¸õmÌ?át9ú¸Ãijkåà¾ýôõôàyÖÂn¥1Æ�Œ'™5oW_} ž‡¶‘5«Ö°fÍZÖ¬^ˬ¹sÊçé¾Þ7¾ˆ’h²²²k§çs¹ÎºêúÇ^êϯ,ŒÄçECÄó%¹ˆ”íPCG"Lš1“õëÖ³`þBªR)***¨­­EªÁÁAžÙº…öŽvn½å&²…"‚'Ú»™vÊU4Í^$ÝûvñäO?Cï·�Ë ¢´ІĒø© -Aì™™2�І‰äz;é|ìüÙ}»c)äóa|lŒ >ϼ61™Çi§œÆò%KC‡#ÂväaÿþýÜtóM\{íßÉæó„ÏqÑóûúŸò½â»š'´üÈòE8QÆyQмóY±¥b”DhÁÌ�%+Wq±dzzùJ‰Dèt°–QXKø!D><ôè#Ü}Ï]<µy3ýCt¶w�Í!" ºº‘ÆÀáL;þ"*[§ƒ(ŒñQ€…Aо Ê€« éÙ³�¿ö.º¼°DÇ ê6à"`ÿ»lÞÉ �Dñ‰ ³Ñû¬µ§½Œ°@iý“TUz†A;µëNaÅ+>n @ (žÏ!!r!žm}zw>ÁC?û÷þ/¼†B8!( ´ç³Ù;b™ª?溺òRÞéTYYÃrŽoü×kfz…B`b*‰ÒÓÛ�µ§i*õ‹70ñèóh˜³ aïý·°ùëï"¿J&#ý¬ÓÜp©–i¤Z§ã&Rø…<ûî»�g~ôqLûN\­( a|Œ1¸Ú%�1{ÁN„fÖôT¦* ,XFÀŠT0àžûîåg?ÿ)�øÅ"‘HÔó…�éDúsŽë~ ð^š‚ãÆçO k¿!"§‹R®h­Y¾á0®ºì Ì™ºÆ00¦}Ÿ­Û·ñ÷`Ö¶ù‘‡*zìݵ‹b¡ž“xšÔŒÅ̹è TO[€µ‚!¶d<úD@—±Ð½{ã½tÜ{-Åìà°X™gEçgÀþãµ)À÷#ÑØrÇu"~›Y× ×àƒÖš+�‡FGÛbñ}N$òʪÚÚ´'.KÞòµÀÝm@,à›Òx-¬[òh,xÙAvßõw¶þökäw=…c=‹(eƒh0ÝØÐ½ïɧ~aá �ÛÙñRr;&—×Ç�‹Ä^ã›âyˆª²X'ŸÏ¡µƒV¶®™É�›i]u�“@il1Ïö¿|Ÿ]øùþ.lv”€gÀZKߎÍ�Óy½�߯kÆ÷ó^¡ð`Ð>ÆÖe1¹|0 xâ?ìˆD’XûUÇqÎq¢Ñˆ¡àC (7„}Æ÷/®§kíTà§ÕêçF¢‰¸L[Ϊ7}�XEå!G#€”j9žG�í{ÙzïØù»oó³˜b!tŸ‚øí«&ÔoßÿÌÖ/[­~öR˜WÄcíùÖš7«ÀD¢HD#1 "µ0ž*¤ë¨_ “7žAeËt¬Rà=öÞò{ö]ÿ ’-S©™¿†DëŒPŒ¬v@Å¡^öÝô[üízÚñrý¡‹ÇZb±� M,[±’£�:šY3gâh‡ÿ*¾ï³mÛ6n bé[î¸�¾î^ë‰Ç~o_Ï#=ퟎFc?ü—˜Ã‰2Î –:,_VJ?+6Q@”“çÏç]oyófÏADÀÚÑV)™C=vä\Y\PJGG?ýùOxbËÓìÚ¾�Ž(T/©HD™|þQj“R:ê½í@ÐÇ^ÑÚ±¾ÿõ:7â&”ÖbE«H’ëí!D$o�y ñØ×K¯„70ø¬Ëyk4‘xuM]]ý@Á°ôíߢaÑ:`%à¡J¢ÄÒ JÀ(x^àvþÁž¿þ€üÎÇÃÙ¹…(!;8ÔÄ{%2™ëÛ;¾lçEˆóìó€:Òu#¯Ï懦«ªS^Ñs‚c¬/‘¦9™Ö#Ï%Ù:=,4"„bÓõÄýøù,©Öi$k¥°ÃUÌØÿàMlûå©o “ÞîŽp¢q]2™j-_Î ÇÏ왳ˆE£#câ9'i,ÿ.¹\Ž-[¶pßý÷ñð£�b}KF#Þ�ýûí=ØñŽêšš¿¾”¬bœ(dž[Ÿ�w\'ª†Q'Í�ÇûÞñæÏ™‹”y)ïÁŽô”÷öR[SÃ¥_Æak7ÐÒÐDE*…R ëeé¸çZûÃ÷‘b-#šæ(Bñ1vX|k �-BzÒlnzéë7†ˆM$¢ZëWcíe"â0Œ“ª�gÅÆ�F?‘®Î�¬Lmêùw°¾¬ˆ¼Êq�„ µ‡ŸEíÚSQ±ˆ¸Æ˜‹†:ºŒÆ~è Û}ãl±FcÃ[ÃÑ (š‘ …PZ�«Á‘’ˆQ»´­9ž9W½—äâ£P™f¬µaî¯'•L¥–Ohlx™8ÎG v‰˜ð"¢®vB­£œÏóí¢õWºÑhºà¥4nE†ª g±ô�ßeö%o#=qf(ÆÆBX¾�××Eõ´y4-ÝH¼®["6¾�ÁÀEnþáGÙö¥×¾'Øó̃´ïßMÔq©H$YÔQ¼ýíïâ�¯z‹, " —¶4°€E‹Ð5Íž=›Õ«×0oö\âñ8ÆÂ`ÿ€[•ÉÌÒ1÷�“]ÄKYœÎ0Î ñ‡.¥>£µ®’••|äãŸbõŠUc`m©½(s7eîG䟿ΌÄl¾ñ¹å¶Ûøñ¿Ë“O>n&0¾A%+Yþ–/1q퉄}v7¾ùÖRfºŒ�®íO„k:ý›ïÆÏ Pô<ßËçwg÷CÈ à‡‘ht�1&jübqp2ð cÓüÊ�D–*¥Ü¢ïŸ4—�þ)žNn{ÿ ù¾Öåè¾ùÖŸ\G ÷þã:m<ï[uM�ç*'ó5¬zÏ÷©l™Š `F.“–W§Š%÷,5Üo‡EØë=ÈŽ¿ý�Ý7ý¯cw/Zc@ÇuøpG÷ý1?0ðC Ï ˜ %ppë•Rï(Z�1~ÂZ«âÉn$FdÖJ&ŸtÕ³—…; Ë£Jk-XJ—G˜ÃëdYöÝý· Bû5vû¤S•t´ èy¸J3oÙ2Î:õ –,\D,¡„Rû]2!+uþ¥™Á¿ƒµ–={öpÿ}÷sí ×ÒÕÕE6—ÅqÜÁ¾Î®¿Äc‰WÆÎ8ÏK±9BDÞæ8N:tÖ¸^ýú7±bé²±� ŒX‹’sÜ”÷Ø’E ­4ë׬áìs/ ¾¾.t9`1ƒ½<üýOÒ¾åñC_î[B\ QÍÈ,Ô‚)ѳêI3Yü²÷’˜ºDá8Žv£ÑàíˆTJp lRZÏB$jŒÁø¾ &2v”¦�7)¥Šˆk­E'*™{é[HÕ7IT©ªÀƒïSõ“&½ö x£ñEäÚ¡�Á¿�·¹ƒ»éÞò‹?,Æ‚´€ºABÑŒÄlZñ--!ѪzfžõJæ^ñNÒ³–S1¡DPA™bqBUu渖I“Þ™ª©y‡Ò:ó~o¿tWWç5/ÿµœ—߈iÒZ«Ð.‘) ™ñòO°ô5Ÿ¢aÙFœx” ckQZk†¯íÈu.ôw³û†_pðGbà±ÛðòYúz{°¾¡¹¡™«ÿ啼;ˆœ×¬\M,/qï%Ö¾ÄÑ—PîúeÌuѲ¶––Ö¬ œÎ¼ùh¥¨H¦Ä/a„õ}ý½—D܈æ%€fœ&›«>©g‘ˆ¸ÆÄ÷yíÛÞÁé'�‚ë¸ÿ•YFî|XBdŒ\ZJOÚÒÁ8‚È¡RJ1±­�ºÆF{ä!òù<ÆZ¼®|Ÿ†ù+q£qP%–ÆQ dDÿ†ÅGˆ¤2¯@ߎ'pmí8Ú/úMÆ÷wcL\D>¸”´5FŒ1ç-Öb­Ý/Žs`)ÇÚ•"òÁ 7 ˆ1–æ/eÖÉ—b)ò}Ýtn¾|@v?õt[ +w•ÿOP_Gg¶èyG¹ÑH�€&QIã’�hÇ�’øÌQ %Â*ª¤,„%R¢ãJ“h˜Hå¤YäzºÈ÷÷àõwa�lÿ€DlUŽë¶öôhQêõ.ԉꌸV5!|2‹¿"™®lr\ǵVĺQÚNÙÄ‚Mï¥jæRÜDUê„Ë^³– `ôëʺž¸�­¿û:í×ý×QD"1ª+3Ì›7� /º„#6l$™L”í#,³ÛX°cŠˆîÆBþù8‘HÐÖÚÆ3Û·±å™§ÑŽ#Õ5uGé„uÐ3.8ã<(ú ß™vœÓÇÉsÙ«_ËgžC4yŽaQ¾ëlX,”¹Ó—R:ºE=Lj/¹;XBÑimjÆ}ø!¼BQBÿÖÇqë[¨™6?ì(–DMJ@¤,ÝE¼¶‘¾ý»ÈíÛŠ£E"±h4�ùÆ÷�v\·M…ŽD„àXübÑXkwŠÖ¿-Q*‚µ_QZÏSJ9ÆbMSX|廨lœˆ Ï–­5ì½÷ÆÐ¡ ¿Y©Þ¹eK"ØñËÒïÙuàÀÖ®gºq�|.ÏÄÃO…ÕQ ¥TãG‹Œ&¬‘µ-Ur| ˆ"Y]O¢m&ƒ�±^žBoGè(sCCä²CJéÚb¡0AD¶¼nT±T…˜l~a4ûH"™<)]•©ŒÇÊ+úPÓœk>̤c. ž®¤ÔÕ„HI˜Ñz¾æö>pO}ç n¾ññ¼Bè$bŽËœ@l.¿ìŠp#�Özl‘°ŒFžStdÔ/)c‹L9Éd2\ó|ô¡‡$�Îh¬uúû¼h$z;`Æ#µqžXÏk‘3#±h½ãºÀúN䢳Î%‹•ØÊW)µ!Š¢XCqÓ™œÿÒ_ 80‚uÙñņKÏÊ‹ãC;J)œµ±‰¢ �ßÝó�o’Ù¬³^ì1q -µe³ïèDh  �fµIr®3:»)£îú…¾6wä[Ö?ùeŒxÚx‡ãÈ�‡˜9~|óœÿ «·nù›8Š×ðÿ+‹…ØTÓ4y½2ú·û‡†6cb«û¯d×Oý)›Ÿú*ÊýýX.ûmàÉûÙϦ¯\öú„¥Je¬Ø×÷¨vÒ~Aÿ€Zœeûaz7UÛn¿¦ÝhŒàœ'\]ò˜Çò”Çð±ó+¿öKüâ/ÿïzï»HE™BóøC½ýXëÐݧþ‚É<�ÄAjñ#ÒYÍNl 2ºšõ×¾;²Ó?ާ´é,.|ýŸ³æÒ'Òj{…ºÞKŒsp À�Gq<ª´ViêXyþÕ n8­ðÇ(Eë *ãl{Ò‹‰V¬÷*ÙÚx0+(¥^Šs¿ºï»w2k%IÎQ›8ŒoZ'r3¦KõNºÀƒÿž‚�׊ì;GZ„�ÈèÔåR�ÍW=…ÕM°ÁíçSìë÷ §PqmöäÊáÁ¡§ê(ú­¸á5S(è(Š XF5êõQýr©\^×j6ŒŽK¬|Ü‹Øýº¿`ÍOE˧(áˆä¿F\&ˆ;g™ï€ÍÞ÷ý­{o  ºÿµ%†úxæÓŸÅϾîgX·f-Z‹‡)Q[“Ã.“÷ƒ䃈"X\î6ÞÖ®YÃåW_MÚlÑ9w=088X*–^º´´8¼LX¶Š)×j=W)õãÚ˜¥µŠ¢ˆ×ÿÌϳkÇNùä–_oƒ žèòn>ýI–[Þºž™öÆþGxˆ“33,ÍÏÓn¶|%w7¿ÂÒÜ ãg_J\Éên\6´H!á2>·¯Üäë|Š£«8ïU¿ÎÊ�ç°4q�Ãßù”ÊŸç­·ÞêIQÊXkKíVëd¹Òwó2à,ÛÜ\½± ø÷¸PX§µ6 ¸úIOæe/x‘G‘ '–.³…äzÜ8м;ƒ|S¾aÕ·¿ýMNœ8A«Ùô £º Ý’$!i6hL¢ÞX¤ºb •¡ÑL~$˜tµB†WüRiEil-k.~,›."•±5$VQ›�bqê8õC{ýñœµÚ9÷eàn_¯cíOEqüè(ŠŠV6<ñÅl¾æiX4áyJT§;¥©®Û†eö®qIƒ4I”ÖºÏZ{‰K’`I)õšB©Ô¨xt +/|œo=mDþAN¦á¼“8ñ=Zsœ“jÒ†¡ Ûq}ƒ,|Ý\".ÄâXi­1ÆÔÇÜôƒÒéê\Á¥énkí5F› ”Ö¿T®ö?¦T)�:PÄeV=ñ¥œóªßò¡Ì°¼Å˜ðç+Ÿ�2O§Ýl0që—yðCo¤uèn*ÅØÿ¾œu¬åÅ�Pî³:¥¾�“àž ‡}‚ *ÿ!N=ÒÐZˆšùµ:ýÕ*µF�‰ãÔëuß¶Q¯÷ãøØ£¸w9¤¶l�WJ­4Qi­]³†ŸxÕk½º-Î…#«µQˆ°™’z+¡P'äŠ*Ì„:¯Gõ¸Ç=�•+WQ(QZ{šªÖšr¥âÙd ÇÄW?Þ‘ùN친÷{vêiP©U–Û0FcMLâ(ô1´qg·¶QÚ \ à’äà©Q—½VÚúl~ü @GÄæátlð,´¯ÙxÕ3XóÔWã ÕS³Q\(¬0Æü.ð¼¨Ph+ïýhp %À+¸¼©@œrHO]/,»tMÖ]q-«¯} éøf´‰üv6M‹åJùÒU›7½P9~RűæÿØ*å¾£õóúFG_¹ë⋟±býúŸé4ö{\ÿ@ÿh©TVº:ÂÆýÛžûz<áAÖ_ζ~n’.Ÿ¶[þÖ§Ùÿ�7ÏÄ¥mš­–œþj…×üØ�ó„Ç<.¤6N¹TÆDQY³çžØñrÔ2à,ÛÌT«=<%.JºJ;ÿ‹زisHéw€éù„ÔNÕ«ÎFî&Ì Šõ‚Xkãåt†††|ãªJµ�¡±QÇWPZµ‘ÒÊM”FVRˆ#ÚÇäØ-_¦Ýj"•Y¤·‘‘î|NGá‡Q‡¸:Äø@™þUkðÌ­Bœ£ÊÓ¦y¶‰¢ÕÆí´aÕÅ�chÝ$o"Ö2?�±Ê º›S)Äì~þëÙÑQ#ˆ*ý˜¸à‹õLoˆ ÅWügïz@é(ò ZŠÍ©Lb³c„hE–«2"¼æ—Jx^"qî×y/LÄÆG=Ó Úã[89’F½N»Ñ,£xçÀèè3\»½ƒÿC‹£x£‰£?Z¹~ý¯\våÕ/ꨒ?WY»+.ŠÖ9å U¶¼àçÙñô×P^‰•^� È[+¼Yqm€S û&¾ûMŽ|ìßè«OQ.õugÖ®ç%�bÎóÏ9ïT­*ðV‚rycä‡ÝB€ CÑáº!c-ÜÎåx<°bÅ ®¸êJO$¨ôUõðøØh«ÝzQ³^XœeûÁ™sÏ×Z_^,—ËQSéïçOyº¸±�s3õ „‡¦�l¶`ý ÷€L¸þàÐOìÄχ‡üþšõ”úÙþòßb÷Oý [Ÿý“TϺœ´ØGuåzšsSD:KØ•±²@LÀ€ÑrRê®_,1P-sÖÅ{À©Tû°ÑY»¸Ô{7(ˆ‹¬½üIþº!‘ÒNÁB ád‹ìºöel{åoxQQëœÝ�[Y[X4íV«ë­i?á7SH…Xi×SAª)´e(OkÆð–PŸµr° �Â÷iÙò¨§3~þ5 m;œs4kõ‚R\Åño‹ÿW­å 5<4²}dlü?6mßñêŽåy>°wåÞ;îЇu°˜(Ö½øWØð¤—aº±º ¦ !i2d;ÇÔÝ7qèƒo`ÐÕ¨”Ê8OXd|x”¿ôežö,TfB ­•X´ýQ=r9¢IšVÙ¥ÔQÄÈ–³0ãk9yð~tÒ›ú'Ò4µŒuBvk/{"Q¹ÛÝGâ²ý;ÿºG)Tø0Þ]×ÈIºgê-óút©�þu[X8q˜Úü,ª>OÒN¸ªŽÌúð,öWïåhÊhseš´ß‹W�«,Î/`“„ø08óJ®Ã×âm¯íðu9Ÿ»þ |à½ï&QÐ*2pñSØõÜŸ¤P*ŸÊ“hõðT%H’Lð4ì2PÒªsü–¯pð3ï`Ð-p`ßý´–jÄÅjÕVõGï¦4¼BR•ýÒä3WIeWQñÞÍ9y ücw|“´ÕbüŒ )¯XGª" Ýkqn›9�]@Ö ÍN%ˆ0_×Ó‰ºïë¶Ò ?¤ßÏCùgŽßð9l}�r¹BÇÛœkÔŸÓZ¿ú¿£.­+%Õ˜›»:Š ÿ¡[JårÜW©’&)((–Ê´�fU‡‰¶ýÚ—ûί:”¤�àïò”©k·縳Óýµ¾çô•bÚI­+GÇ;­0^Ì5Wv=›üŸª\†!°œírv”B˹_ò6°žâ¶;¾ëÞù®w-ÆÅB_«Ñœ<þà�/)¥~áÿÌËYöp–MÁãQ¼ªT©T�VjhÅJ~ý—~�þþ~ �È�a4X’÷ƒWrô®ÏQª7ýÅÒÒGŽebr’Z½Î¦�ãÀ¾}Då2I±Ÿá-gõ œ ™!‹>Eþ‘\6 RÉ]èU×jbâ�1ZÖQ›�îŒIÚµE,�­eÓµ¯m0*h~’вÏŠÉ?c•yàÚ°“áMgPF+�é Jè›y©gCÐ%ûL«îñt0@äž"õðÜ“ÒU‡Q¥>Z3'hNÄhCgµÑ¥V³y4QîžÿjKã¤^¿¬Üßÿï£cã;õZÔn·ÑZûj¡\¢•:V>çõl»öUÄår +óp]Àg>u×é‹ÜyÏ] `õÎ팭etåj–¬¡8¶m"”LŠËÐà\P£²*|Ù´ —Mäq©ìÃ]¶Tåð7>�± ©ŽXyÕÓYsþÕ£ADµx-‹/U@½–OçFu'OTJ¨gàè=)F)Š=Ó å6˜ÔdÝ#¬ޞͤ€tvý‚yå?­‰˜¾ç’Ú<©M•u®Š³ã.µß}¤Ý%u¹¨ÚõÚó Åâ_Œ­\yf³Þˆ’VËOøZiªƒƒž¹×ÕsØòÌ÷ù«�,fD^Î2|±…¬Ãr»ýëL|ì_ ¹€Ñ†b¡èåj^ô¢—t<›«3ÏFåU÷«l@›“œþNrÇADàt@šÊq£Ä>{=iÛÌ«‰ºC&ûmÐÁÓu·dÀ.í; ±¹ g¥£3�E6¤ÓAŽ#ó‚¶ÜNRw;mèÈÇxÁ¾ï�M0ZE©µ•v«Y5:úÊé¤oúF‡UcvîÉq¡ðç�œÍÎJ¥¯073íÁ¦ÙhÐl7¡PFm9�³^ý[TGº^JŒN¨EÈs×Z€³(�;p/÷þÛoSlÎQ¯/Ré«2Ô×ÏK_þJ}õ5’�–gâóÐS‰4*_ê•ç­È¸f>%N…$…€ËÉ‘š(R_üâ�c]ÙÙ´’4Û'�µwG–gÙþWMkýЏX|r¹¯RŽL¤:UÛ¼¶£,P.•r¤3Bš²˜só:9Òa.GÉxP(/帮ãÍ|û›_§“è$uçJ+/oÓ¨×ýXZ\ Ó·�ýwÝÉñ»ncú¡û˜¾ûf¦÷Þî×- Ž¡M4ÝʨÁr¢’KyZÒshžœæÀçßG±aã ã?ž¡M;‰´:…µ&`Ñ:Ñ…Ôè lb#s$Ò#ÊðÞdç"ö/òQ:«íqt? ¢•FÔâHÏGC÷8Ùù„ů(dþ딩(¦ºj³÷߉Zœ&MÛDq\.De´žî&Çâb¬û£ÒeÎè¿(–JçXçâ¹Ùi´1^á"2¦\e`×¥œýšß¥åúS×Âw+¼Tç@‰°¡ü‰ÖfNpß[ÿŽ?@mqŽJ¹�¾R™ç¾à…<öQ�öÇÌ5uš¿åä]ò›æ„×8}N(¥xÄû¨”ËêЉÓs“Ó­ÁÁN•ÓøxÔ˜_ú.pç2à,Ûÿ¦¤»Á¹_-UÊ;ãŽ9k¹êÑ�ã1W? ­µðdòÔèÑ`M7RN�� éŸA˜!°cÇ�ñ�¼�C‡�g ´u‘VâÀ&8k±Îa“”¤Ý&í ÛªS;¼�ù‡îaæÞÛ™Øs‡oý*Xš™ðÅ …RÅW…EáPJy°0\ çÌvÃ1‘V¾?Êá/ˆ¾J úF½ð1 ¯ÛJ$DR�—T–Ðo )ü¢ÉеZË’ù²FH }Ào2òb�yn„Zi ÎK„Ϻˠћ˜Ì­ÍþIKÕLu�©=ßF§žQ§­sƒIÒ6iš~X¢‡Ltq_¥ú›CCW*­ã¤ÝƘˆUkס•".‰ÆÖ±êQÏfå�¥²¢Y‹T‰�9ª  _¾´Örßuoaâ+À�Å´ÒœyÖÙ¼à¹Ï£¯ÒG>ˆ�¯þìÂuTN"?w?ÒûÈÏÏ(rîÜOb[±®Ýº*I“Æwo¸ua`hxÌ%vž¶�šž›ú `´YjQ‘eûÿ��¡”ºn`dx‹‰"ã’”ßùƒ?æñ�zL®Ç"cÓ’â,iѤÓÛíWËgà¼ïƒïçcþ õ%ˆJØñÍœùÒ_î€Èw˜¼ùzš“GHgh5øÉîù8çÐÆ ”ò�ÒÐQLit5ºoÀ{<Å‘qŠC㾂¾<8èÿæP8ü¶8›€Rmüë¤ÕDEþ³Pðƒo¤`¤Q…m/ûu¶?þy+’3!‘I«,lPìA‚Jh€ÀëIƒÐšs™G¢º€²ãJQæI)‘+’l4„î$+M«¬^)±ÁÜ*ÖIKÜûþ7püsïDÛ¶¿æ­VóØâÉ“Ÿ@©_V*–V¤iú×C#£Ï. ý ¼.^Gn£4‹óóÔËìºúYl~úk|¯L„&šVòzeçhƒŸÛ‘›¾À�ûz ®…ÑšNâ‚MÛ·ó«?ÿˬï€äz+=<÷ðóð}nþD a Þzn922¶­Ò…™ÙÏ7[Í×Ç—gÙþ·ìg£8þ£êàÀ U­ð¦7½�Õ+WŠÌz^HÀõN’jNô¯ûɧ *—ƒàCNž<É_þÍ_rÛ­7ÓJÚ$…¶u:7n 0.eâ¾ïrà‹ïcöî[hL&i,zçTòî9(1ŒO–‚ÒÚ«`­ñ-™‹ÑŠ.`u›­)´Ò>L„scˆâ‚Ï/¸4e~vÚ/*éÛq1gwr £g\t*ìU4Ád.¼ŒÄJ0ÉôÖRÁ*CR|30Ê€@¼î•Ó@Á<\9¥•ÊÐXoi.É^s!£Q0 XAûž?z€;Þø«pô^lšÐl6𳓓_MÓô·%û©¿:Pì€Ñï+åŸ(Kc(h§ióœË/+Ðl«‰cǘœ]`è’'³ã?ÇÐê h�y6’ÐÖËœ™?Óù#rÓ_¿wøn´ÑÞ#ÞzÆ.~çWƒ�6¢r÷§ Q±»|Út(´Xï‡Û»œBOrŽy:–›s$Î6þêOÿêÎJ±zv¥ã®×–öÌŸœû-à3Ë!µeûßb§½ì?koâB¡ P\ûÌgó˜+¯ O“�!§Bš�朻Ÿ Ó‹a£8|ô~ÿûXª/aXS`Ýã^ÈÀêMh­} ‚Uç_ƒÅÅ”ŽÐ&FãÐØ€ÎípbDÆ€µ€C+0X ‘&î £¡ŒrDFyÒ�ò"Ž]�À njcHR‹)UAǬ¹âZÊ£«ºÌ¯L^Æ"‹13�¤›ì9’ÞÔlô¹q=ª�‰‚F:¬ï £–A¹”à†ÈÜ<(•ÄH}ÿ|÷ ’ 8q×­¸Æ•JŸQQDcii­oR€4Iž”Úô7úªý+›Í¦®×ëíóŸú̉•¶Wm£®•2¸�çxåçþ5›=ð[q=‘)?1äsŽ×¶U¯qÛ›~ŸöÞ›¨”Ë^"h°�ŸyýÏrö®³P�’c§Õ4“âùÆiÀJ…çòHÃmá69<0ãœS‡Ž›i.Õ‡*•J©8pèÁé½ûïùà–gÙþ§d�õÀo•«}+;xc QÄsŸ÷¶nÞê9IQÍ –6aÑXä�–\Mª�™˜¿sÓwøÊ—¾è«ûSë0C+ØùŒWSΞlMÌàæ]ž’<°í\lTôŸU*eª#㌎�P켎K%¿Ä9Š¥¾k¦³(¥<tÿæ=k=9Aœž¦•¦´SÀÄ4­!U1Ñà *[v³ùÚW°ö‚«PZÝ XÓe¾ ðq"Ä%ADÐ¥#%Xn‚òë\@jäMÈBìn,“ÿ’¹&ÿ.˜lˆ %jxdϲý´m–ÀOQ”GV²pøÜÌ1:bÄ**Ä}¥¡Ád~rê{ÀqçU(•v‹¥(Ž¢Öðö3æ¶½ôw†O.šó3,•FY÷¤—3´õìî¿ ‚vZz:áyey.œcßõïçøuÿÎ@µÊ@ÿ€÷n®¾æžúä§Eÿ-S9¹×Cê&·»§Xæn*z®zN!q@¸öÁƒ]P‰Ð±ºmOî»ëþ«\ijnÊìÙsûbdÌW€Åå�Ú²ýOí¼(Š?74>:E‘.hÿþÛ›Y·n}ïŒ"ßAÂÒÀdš -¨Š nè}Ó¿ñ�w¿ƒv’`•fõµ¯æò×ýZ �%Ô[)8ð20õéã̼—ùûo#=xº9ODB=Q¨´å•ŸmÚæÄLÍoWÖ‰×隯µPZÑbFÆF™[lâ”Æ¢}!iT© ûæ^«Ö3¾ë_B±�Êð8±QâÒe^MWË-cÆ9€Lå9ªÎÞK ‰½Úuó68²ë™‡“X©.�…Ȥ"¶Q=díÂÖÔÁ?“¤V§)8²ó˜¸õK<øñ§4û ##£8­î¾óÛßù‡V»u½Rê ¥¾¾Çi¥*(e‡¶ì8qñ/ýS™Ñ�C3÷ÞÊâþï1°õlOPZŸbj‘WòC�µ\Y¢�ÒnÇÉ÷rß¼–bmÚ÷Sj·[œ±û~ï×~‹Ñ‘Ñì‹G¾åäjT°‚ë�€�4DÞG(þ{ærÏSÞÛ�ûöoÿÒ¯íˆJÑX­^K¦g¿›Â�7/βýOíõ¥¾Ê_Tª.µ\råÕüÞoü6w:‡Ï亂VžNH.ЀËóv¤…¡¯Dü·ÿø÷|æº�ƒR´-œÿ›ob˕ע„¤‹ÔÐ’§`´‚´M}nšúâ<$M¦ï½�fmÚ bå°&ö@†³¥}ï~â"¶Ùè,Šø¿#U ñOq~¡´ŒŒpÛþ… >‹mÛIŇ$IPÈÖ•ÛHÕù™ i%sCÙ’ CjÁHÖÞ”8íû>ñVê·~–(Yda~þÁãûzo;mG}ƒƒ¯(K«­M]ÛEíËï-sƒ»¯k×ukæ(­…9ªwy€—r=FK%ì Xå5�ù.4ONñ¥ßx>îØ^=£\,ÓhÔø«¿ø[ÎÚu¦ÌA†ñÆüœ‹s¹¡¶p!ˆä'‰ÕU—ÓÖC °b>x…`šR´#}ÿ‹ŸûçÚÉö(Š˜Ÿ›;TŒ‹¿¼�QSçÊ,ÛÝbgí»FV®x–Öº¨RËÏu’¦ÏxÊS{ÞHa[�ÌrÈ×=˜n.�Ý#·•ë{éš_ý�_á�ûîÅ¡|›«þôý¬:ó" Õ JÉ„»¡ p1µhå(…oo`•_צ J”M‰"ã×AœsHQL'zŸž¹ÞÌ{0š)]m³ì\b�…¤œÙPMz#²OdïÛiÀÊ %÷Ã:÷ðÏ#õp9D(/l¢ëA^ºŠŠßâ‘}þì;PÞ„rIZ«×¿{èÞ½#}ýÕ Q›ÚÒR}Ó+~»¶í?>bâH%þA ‰ÒЍ‡¤w&A²ÛÓHK*tv¬uøÊG¹çß~‹H¥˜(¢`"~â§_ϵ�RXÜ™?q“$¡©öi«�VÊ{ó¿ûë¿EµÒhþS�”¥&Aãt*Ñ½Ž¥zí3ðÂþRŠœR•yÈ'ÄiÕºîÆo~ù?þàO.«ôW‡šõúdÒhtxpø—*åJ�A3«L̲ý�͹GWú«¯( %€±ñ¼°Óh­T*�¦”ÊQp&¢àFnˆ€òÅ<ñr6ŸøØG¼"´Š+Ö±ãi¯Äê9Iô7 å§´Ê^KV˜L:[6FlGP£”L¸MÍT8çˆöÅ!Þ*Ò¡B³ðôûç,k|T/f›ÊÂPòœ'=+! #¾#ÝõÃïJy9áýÈ9Mi)¡±6¡6=��;ÎäÁJ+­pÖ5çO^õËÿ`¢�çôûµÓ×q¡âRæÝ¨°}L(€šå¼´ÎŠTmÒæÞO¾•¹?C¥\ôÒ5ÅoüÚo²fÕêœæe¹¤€|S*웑³_lH D�| I99E¦â|YÑ)ºR©ÜüÉ}h•ÒºOkc’V{ ørpæ—gÙþ˦‹å’äg¢B|~E—Zžð´§sååW �èì™v9:P§së]ðB0tä jÚŸ˜àýo} Ä‘?¿âø:¶>é%8eð&Â?�µˆtvZ²G B"† ,B€‚xæqM%µÏ‚�’¨­qNn#äâÄöbVÙ’Zî/ûÞªG´'ì,‘Úl;낲NJaAH°R�[gëJµæ¸T¡1sœ“÷ÞFmf”¢Y¯7w?÷Õjô±/ë·¦ ñ)Ÿ%Òú"…rgb!»8“ zžb€ÿÉC°ÿmLŒ�2Ô?ìÛ \}ù•h­Y½�ËaVæôhûÌ_:g¹Ž<Ž A$¹_?òp*ôºÄ¹8U쫸ë>òÑùB©8Ò¡«G�Â[ |¹ÚW=Ê� ™•:bÙ~xæ’tPký³ýƒ[ãBAÇÆpíSŸÎÎmÛÉr“à�Ût* -„7bè É¥œ­³%·Ô>ýñ�â´ÆZKyͶ>á~AtsĉI[¨,[¾?¿ ‘–½òA&hÌè‚<‰SÐ’2+ÁÜäAH%Û+ó�ü�´i9ß„m ü9ëîRŒ•É}%BjAˆQªÈÚ jÓâØdÇÕ�²uäÿž�#�•Qݦx‡îçà×?í‰Ö9;²~}rö‹©lÇ6h­ðVŸ>�µ–¨Øo¨Ø€&î‚(® …N»ëÒ®sÏûÞÀÒ¾ïGå`ãÖ­üø+_#uó=•ÿþ‘ÿ=•¯@�<¡Ât¸^~¡§ƒ°àªwm�ÖÊܺïíÇîß·Á]i6s‘‰¿ÞWé{€AÓ,ÛלµÍ{3/Rès7.�y#ˆȸ ¹®AwÝœXww¸^Û„‰LÅ-·ßŠ.½w£MLa`„4¤ë±bÖAˆ[#;Yf‰ðL�+ó4 Fä5ä¿ÈÉ´Rh$™,�¬|—jã̈(d¸.™×à‡´‚?§ Uö�²}æ´3°È:›ì:�ÕɼO+ÍÏ ÆŸ,ôôÔíP[Mæ¿è^ã¤EÚ¨ùk3SùÆ'I—fA�µÖmzá¯Ýæ ´ü™Å¥"å�!´ÖYž*h�Š–àF¨f+ä¿­ãðw¿ÃT'w´j|+ÆWÑlÔyücÇÀ@Žë�g¹EÎù|H¶Q9Ç“÷•sy ·ïN~§O—#~ëÁ*ÐFÌ>SÖ œîyÛœµþòŠÅJ’´ÏâGÔ"œcÙ~¨¶3*Ä%çœؾûVޝEžº@ŽšA'hC -\Oå¨N#‘"«Y0F“8MeÍV@“:P®ú1ÙäŽhß,º? &•ø»¦�È@À!Ö—Ò(0Ji0€1½‰v-ôÑ·àd+©-ÃXa¸KxòÈ>ËvU(ZgŸ'Á¹X”H‰üP3=å9dS�Ø‚\�ÍëêSGÙwý‡H›5¶<áE4§�R;p¯§Ü§(ª»¯1ç<šÄ)$`ªC˜¸pÊS‹´?ÉÔsÝ¥-¶Ò �ëó³ìÿø›î+¡”¢^¯±qÛv.½àbòóâw�_rÆB�? O÷R »Ø*P�Êž\�¼¨Aw;'ÿòˆ9z7aQw÷\ÄV¬* ŽD­´­RìH­¹´áGp–í‡m�-‹}Æ�´Zìܹ“¾¾¾ÓǯUøYN#¶ÅåJ~„û´Öž¢0§Öz5m ˆÌÃÃS6pº¬ XYöá–©ëza}«H'E²Œ !•Œ,6WJw’ÏÂlVæOÊr¤ ¾}kêm¼ÅˆBL›5se8PÖãHFžOÀ(sâ˜<ÜñâúFÂËÞQ—º�±Ânþ§_göÖ/£K}Œ¬ÙÄâÄahÕÐÆP³»ŸòŠýƒ§QÓõZ E¼…‘U2ÏÆ@y×ËRÎqä–¯¢'â\Êñã‡)Ç%/_32< ä°#Ež2·Å€X%·¦&Ÿ $ÙÚc�ß ž|¤ÛXˆ%Áy¨ ]¸aX® ª¥ÊðÎígÒ&áà‘ƒív­9ºT«U€ÚrHmÙ¹)Õ§´Þ ‘1‘1lÛ´­ÍÃ=×ëÇ*FBo¼¦‡ü�Êëãá²·ÖQo4½Â@ÚÎZ\Ò Iúf õ$-Qïb4´,Ýd}¶H�!„°¾ J(Z>¤|eг½¸` A¬³u— ‘ËÉê‡D¸0›h[©ÿž~�JŒßG;™œV+~„Œ;—µC(h¡Ã„»ëà‚ºEö=”÷»aÆz�½_x?³7ÕnR$aþÀ^jwßÈ꣔ÇXûø1zöU(Š0Y[Ɔƒ ˜I6'ýpâF»¾Äì·?í¿Wš&£"»/¼�N˜T.«,T8‡#î Dáûp9Üxn&<.aô _^'?(,¿¨Fõ+KZíëg`p¨‹U­”Z& ,ÛÕVc^ß7пÂY«K&æU¯ýqª"®�Guéž*·k§ÜFjßçþÈó� ߺùFîºý6”Ö8«®z+¶Ÿ (ÑÝ1lH&“Þ`ä|‘å|"((„ÉМHŽ»¬‰š%Õ­29\†îD��ÙþdahÚS¤Î›�?þ¼³ãA‡ì:o  I pÍ<À̃ dol Ví©ÖiʉÐÜÿ¶?"ÆQ*fóúQ¦ïßC¥ÚO³0ĺgü8åñµ�4ޏ¶²ÞFÒÄ$~ˆ"Þ0Bë\ç®gÿgÞ�m-¢•&R†—¼äeìܱ#7œ–_æ@ý)Ó*'Ÿ£òÃeù¹ ŠÙÈL}…Üõð`ùˆðsrrÊyè0¥J¹ �šŸ��údæ—CjËö_±’MÓ8MÓ$êXy`À÷pGOo.O]ÌŒ9pyOQÝøIÈD ö|®Ô©°šë LLQC¤m§d¬¿ËTËè¾Î",”úa•y.~²-š%ÐÈIN2Þ¿M2“­š³ 3óžlV1kÙ²Y8fç“ éýPÁÀŠÐR#ÉB}FᨕJùf@;͈ ‰ËZ ÈP›d³…ÊÓF×ÂÑù^G‰Yµê,--zI ÕuLxgS>Bùìkè_½ÉŸW$Âp²—MÚ=n)ʈ Rå Â%Öe£17ÅÞ�½‰jÁ 4ëuõ¸'pÙÅ—€u§À‘Jl{sùáæü([àÑäw÷”y™PÅCî&¿Å{à]…O>ý¹ÍÈèˆ2qœ %†FXš]¨NÇ“ýËβýW트X|v¥RŽ”Qç]z)�{Ôc=å?aÉ×9±œÜMŽÆS(©.�åÇ�·ÞÌ�·ÝzêóõO|v–�þ¸€þ,Õ\ ¤l‘0Y˜¨m¥’…ÉD$#-úÜ8Ðbš•ð‚<µ8Ž] OÌÈpÉbQÿR°%CÎWx;RÓ¨nî üE<´þZ5ëÜÿÉ·P<|OÀ+Ö­õše6M}î�ÁÕì|ÎO3¸ù A–ôÙè» Ha¯­5ÙÎ!MyàãÿA󾛨#ðûR¼ô%/gËæ-ÙÎ8=�ùô½oï?§mt~Ø+¯ 3p% ër‚¼’ ‘Žp]iá¹æß×µç¶=I#i"¥MT+Õ÷Œ �Ìv?JC³l?3}\Y,—†£8ÖÎ9Ö®]çÕzX(yÝ<Å©–9ÛœÖÅW ²ƒV ­5J'�û&i²ë¥¤æ: A9ê~æ²|Šœ¨ ÝüEl²d¸VÙ�€ÖžÈõü>»__I‰ 2¸˜`G÷ÜdîGL¦íT€�Ã9¼‰¦gÙwo¥~™ZÐj‚¶Öb=%TªŠ WR²³í§÷íaÿ»ÿ’™éž朣T,Ñd|å*FƈÆ×3rÆù2§ÔÝG¶ß¨ë•M–o‹(QT*)щ˔¢UÚ¦¶e£0‘ÏO²cç.¶mÙÎ…Iù¼Ñ9ÈŸwO„ ´œ‡2²Ð\æYP¡€„+…®L^{ëp&Á3ŠŒJ“4ŽÐÎD&Žâx¨¯Ò×·ÌR[¶Gl¶Þˆ•Rk¢8ŽÚ­ÚÁ™Ûwöbç„ ²|½)Â’e‚ʯFÒÆ¼å·Úèëó€ã”Âz1Èý`SÐZЉU÷PŽÄJí­ÌË!¨×pîáOÕP´¸/�˜»žNÀH놮t >ᗈ휕B—¢B>PN…·%Ï9ü.Ê�t¶M$¼+B‡RqÙó_=\“Ìyà’!ÅlRâ&iµ˜øÏ$}ß�z Ýí :=y›XS`x×…¨Î2±Ùµ±6èo£3‚„Íët¦&‘ÿT��Þ’dí¥²‘š$^¸ŒjžwFÝ>vÓõLßüeâ¤N±X$m·¹úšGsé%—ä tªà!Ë…@ zçÉwXòÿlT®lT¾Jµ;};'•VÃBS•Só*(?Qßxà ,,,`±®Ýh.(Ô—€{–[L/Ûi-î¯*×n?Ó —tJˆ•E�Û¹ŸÐ 9­Ãþá�!ZIç¸ê§ÓžRRã_‘kÎÞ'çùì'>‘ÁCT®2¼ý~ë³øêÇyðSoÇ5ë8gqKóÌu€gò¾Û1Õ!†6ì 28BÇ(c@EXެ–$KÞ äWÛ1w‹:r/€r9JÈá¤6°�ö+ÙlZhª¥¢s§¤P'¶×üä†D;i„W¦‚Ž �ê²$Rt½¢¼•H�<²ŸcŸ~;EM½Ñ`ýs_Ïê Åôþû0õYlšÐì_ú'¿‚¡Mg`�êæf²k-•dá«dé)…Œ8I)_—uðK¢yè>–æ§}-PµRå9Ïy«V®ê ŠžB™ùy�ÿ² ŽÌ¹ä�eB�EŒ\™‚s¦ÇÈ/¤÷ˆº‚Zç¸çž{˜�ž¥Ýn¹V£™´ÚÍÏw/~.Û#éЯ•ª8ë´Ž ã#cÄq!ì—.Á¡7ˆ¸Ó°ÉkC “¥âulÈúåI ž0  h�8ÀÝïý{&®/<ð’™c(`¼\b¸¡�£~ð>îú—ßäŽwý5 “Gp(R«� 1Lä‘ÊÂR‰Í -“4 «‘i¡ù`Å×ÐòR 9©æœdœù×~õ¬?—Ì{Imú ¼�.I@ÔÒ8 |B/M^É@9‚b$”—ñCä}Dí’(Àl[B6eêŽoùõ[Í:ÕsÍÎ'¾�á�;9ûRlÿ F6íbäÌK;ãB”R¢÷�¬yç¨3�Tx]¡0§ì,:uà~Žßñm\s‘‘áQúûúY·aëÖ®E˜Hê…ÎNþ>{²5¥[˜?± 2‡f%=цÇêmêHšC~»\‹œÖ#Q3¾z•oÃmŒ�±‘GbË€³l®•h` .•1ç•r9‰l¼©œX¶ÊQ±Uâ}X¿�XO‘—3òM²ÇFI“Ä?½.ž8ÂÂ_¡1}„ù…E¬uh¥¸jý*~ûÊóY7ЀKÚêäun|ão0½ÿn¬s¸0·*$_dN©¼lE‹iasF“UÍ63pQ’`­V’R-Bz¢¨µ2—�åUdÎ9ì>l´È'9@ˆ—¶ºL¸¸Ë Ó:ôèXI°°P›<ÌÌ_§[œ)²þªk) Œ¢KU†·�ËÊ+ŸkÏ ÇDÕA4™Â‚h5 rh�*¶8v(*½¬#7^O:sœb!ÆZËðÐ0/z¡ÏMfnRaØü\cnK‚pÖÏõ,òYfd†¼Âsu9ºhyE¥’Cnƒýr7¹Š!dŸ·Úm’$¡3Tš¤XçÊ)�üˆ òýÀM)U~¶:8¸®P(TŒÖ<ù)ײyã&p!í9GÞF‚Š ãÑäP§Ä¾òäÖÉ—ê°Öräè¾~ã œœžöÇP죲ë2*ÛÏgñ�=(W®á™›×°m¨Ÿûæëœl4ýöµ£û9þÝo2¼ëb*#+Q(¡2 )Õo3/# 3eá4';~ PÒZz:™ˆ„¼“á49_eúkÁdSbŸ‚` e“8t¾T²ãè÷s7lP{$÷áœØ.ÐP˘xމ;¿Eû�[ذzŒtín6tD:)öaµ!IR,S®2Úñv*ý„­¡] š ‚ïi¥Fœüy Mu&¾q…ùI"eÑJ³sç<õ)×ú\B$Æå‡ÏBSù¨pÅ|SA˜@†Òr´ˆ�Ì=ò<Ò#;µ`ÙéÝ{ß½ÜϽ8‡êÖÙ�¯ó#fÑ®¸Ä²ýÀM+¥¢(2em ´6nØ(P.ô@†k!ØH’Û‰K `¡k�ÜÃé ® 4Ÿ»þ \×é…335…M-J ãëØôä—°ù1ÏáØÞ;8ÜÉáX›ræØ Ç…cüÙÅ;yÃw÷rãñiRç¨ÞÇmÿø+\úËÿÀðæ3A)YåÞ¥R‹nŸòôDñ%òé_� ¬¥»Ìò ‘ 3ÉdÙ¥ŒV‘˜ ƒvØ©HðpÐÔŒR8�‚î�¤à�…“Hv^Ý¡�WoF!C‡�…“œ¸õklX1ÊR;aøò'aúG»?EuÕ:*c«ˆ %T¦C—)_[Hƒü“ ›%iîH”ö.ÏﻓÖÁûH D¥¥(fíšµBŒVåIØä3ÈBŠ~öQŽ)No.Ð* A°WÛõýªÒNõ 7�OôÑAèP|&C{á÷W ¥"õFŒv*¡³`ÓR~¤L�³ 8?xsn½Ö溱իvEQTà ÷FV¯\þxÃ2 A÷’Ù8MgO‚™5Ü^‹Ï4š n»ã»üÇ›ÿ�}÷ÜC’´ñ:j:¢¼õÎ|îO²îâÇË|㠿ʑϿ—ŠQüÛã.aGmNÔ›¼á޽|õðmkQJÑ¿ó.ù¥70²Õk± �NQøIw©‘OY(‰¨ ½$Õ]OÔàhI:óŽ$8´�Öà\æõà2/Ä Ú²uA£7N™®›ŒÔXP Ú[§ÖzŠñÄ÷¾Ãüä1Žw¼‡Ý?þŒí¸2�ËwÒ<ÔÑ+²sœ,޳ñ9?CÜ?‚R�G4šk[¤ç$ôÔ„zµXöPÏôó’{ßó·ÌçSD®é NwnÝÁ«_ûZÎ9{7ÂrzBöEX yOHl“YΊ¹r8òÁ.o�à,Ïz�†V!Ȇ =ÿî¬ã㟼®CÖ¹‡²ízsª^V*–¾¸œÃY¶Gb»ud\GçýCÔ … nh˜‰Eñ²õ‚¿eÝÀ\®\ŽT)˜œšà½ï/ý×Á=Ði4Ꞣ‰ŽÜuWý쟰ýQO%îð ÍÉN¨ÌYËX_™�Båì©·U¥¿vÁ.»~ë×›¿÷VnüÛŸçÄ=·N`^FÈúÔdÃúudMLêÂó(vU¡ 21ŸUÔg@FØ £ùvkjü~ŠA��H Fv¹ÌçzHÊsæÕ Ìb ¶>Ïþ¯|„/ÿÂS¸ù�^Ã}ÿòÌÝñMîê4j3°ÄÂâ,+V¯ÆUÇÚ}%Å�‘Sy0´…HlvEã‡ßO,Ùp�‚ì6n�TdÈ€hñÀ}¤GïǸ&¥RÉ Ð®ZµŠ ëÖª‡òrXæ[6ç#CN®F… ñFxL¹òÓ¡9BÐ G>GõTN.´ë™'²ÎÑh4p8Šå²+‹ÓZé#<[œeI¥õöÊ@¿M­JZMFÆÆ|3,ÁN 9 ¨\�ƒÑ=’�JÌpùu¡üÍÌì4ïÿÀûøà‡ÞÏ᡺I‹bôšgrÑÏþÛÎÇéçð7imlÊ`¤èWmS”§ #±á—ÎßÉównÄ(¼-Þ·þï0wàÞn[E9†‚Ér7.(þl§]É™ X4’ÊÑ‚òëYn‰Ltgû)Ê6P½ÖHpÈpY¨#wÏ)(Z�g·¥Þë=(ŠâUå˜9ô€÷¿ûg?A½óZÙ›$hçXè€Îý�°%í&ƒ×M‹gbë¨!ÙÈà¦]h ¾£`àŦ۔-銋 RBj§™@;ÍB�2�„z¸C’¶[¹áó˜Ú„gÈuÉì8ã s¼�ß ?Nj €°¿Â�\?�œ�—;Ê;ÿœu ¸ü¹ÅÛ�Ÿ?ç,õz�B¡HjSÛj6ÓV«9ÔjµøQšeûA›SZ§Æ˜bgE1ƒÕ~Œ‰‚›L,møT†(9— ûý`£rX0ô®ËqÎqààÁÐ|�Ïvr63““(å‹ÏPQ�í/ûe®ú™?chÓNRô©°Ucæ¶¾èߌ ”# 8ñ¥•£…ˆŸÚ½ƒß¹l7%ŸØH<èìéP¦çîå�NHîÁ¯}œÚÉÙ,YMø2Â@Ö‡'kÐò,°®Ø§‚.L0³DçLÑS&»Ìóð$:�¬Ll<©ÍÎCæ£ä3„*Ð]±nRQ º¸Tçþo}ž/þâ38Ñ©eÁY”KXÛó[—žÉE«†QXN|ýã¾¶©‘ÀâÜ,3T9YZEuËnŠ#« c—eõM¢°Õ¹ ¬S¾Ì˜�™þ›èàéA³;$㸽x’Ú¾;hLO1>¶Òÿ¦‡FGذqZëPcLÈ×<"ʽP!,Ì =†àÃq[Õc;A®n[¾7#÷¾w.'DžÏ¿n'mî¿ï>œVTúú´Š4‰Mûx$¶\ø¹lQŸ?0<ü­T¿B±ëÌÎdráE˜Èô¨lS=äÓ]ŽDrNouº~ð9àÁ÷ó‰ë>æÁfêèÑîaÅÕ[8û'þˆ3Ÿþ*L¹ŸPõfjß]ºþƒ~Ý3‡«<~íø©ó·Ú ­=uZ¥Ø:P¥`·ž˜ÁK‡àÐ×>ÁÜÞ;˜¹ûæº�‘mgQ¬�X%ªòÖբ]µ¸\FÔÔ(•…�b-D<oFÆ€‘]>EŸ~£3pHÒ,#½£°LDÊ¿„ …ËT¢ës“ÜÑ —ÝóÏ¿NZ[DYK¬ÏÛ±�_ºð,.]1Bd4ß8~Ò72³q�5> —´ýº ¨nØIT* !Nq|Y”õ×Éê ri©7Ùú’nÞ8q€‰ÎCBº4‡1”bíšÕ<þ±�÷R6!ˆ„ž·4™c|ä–W`ÙC–Fç±>éqrN=¯å» @&Œ$sD& {ˆÖku>þ‘ÓJš­¦Z89R9÷e`ïrHmÙNçiëv½>6?3³�¦ ívg­÷ €žu0ùb�./y™¯xë2O(èõìÁæSŸþ$_ùúW™>vìÔ! Ïäœÿ}¶?á¨B™Ä‡]„´‰uÔgNœ:Ÿ‚Ãy°é®‰r œ#Æñü-kyÚ–5øÕÓ„ÆÔ1šs“´æg8qó—¹áï…™�9çºìµlXûpJq[¨U´$džL’iª Y–,Œä·5™nX­í·O÷Y.§‰n¡&Ëùý †*{í\^©P”>qï|ãÏšü-�s¬ï/ó{WœËëÎ;ƒõÕ Ú96 ö3hÀ¶[œ¸ñ Ô§Žá¦TñžM\-BÑåÌ»ëö¿à‘ÕI‚�NÙFAJ9 ÇÒ¶ÖáR}‰B1fxx„¡¡¡¼â!Qz ùÞ8JŽ\åt‘§‘´Â�å©zwÁƒÌë@š¡O4.W\Ô­i]ª¶pÒNÎÏ£PôõUé — ª•´g–Õ¢—í´æ’4.�ãˆsŽÎÒƒ�“ a"1ïï!U3ç MªÓöÞïìì _¼þ l&�ÇZ J¯ÙÜ¡/¿�Õ».À¹ŒÆ+žÔýº“{n8uÐÑREv.Ê¥dæpÝó©D?Îv-5øÞäMI³Ž)UpIÂÉNØè¾ëÞÞafý*‚U–MÞ²Œ¡6,ÞäÓ=Zû`¹ŒRíDñej3 ob¿™'DF\è—4kI9LÛc·•»Þþg,Þ{ å8w|ˆŸØ½�³G‡2åm›ªì,ñí™:Éì„ê\µû L©Di`äÔœšÊãi°VpŠ9Z��Håù†Ï%þs-ŠPm›öñÑ­Eú*~B¤R(rþyçc”'Ðï÷ò³œ^2®· ¢‡üÝKP‘D„Þ Íþu®:uŽj»8&È÷â8° yÿ…% Š0Dž[¶P­V=�7�Ekmµºi7[sý•ê2i`ÙNLjv±‰ã�êà@Ù9HÚmJå2‘‰A©œz•“h n–<ñC’T&T­uܹçN>ÿÅÏsüðaïqUúû}�Íe¿ò¬Øqþ)v˜TWŽ5~(ðOÛ8‹g©UJ¹ZQVë.ÁL£Éͳ\°jœH©S_aüükX{ÍÓÙøÔW²éI/ñ”ëØ*(¡,Næv=šìèq—¥VÐB‘Ùf¹¿�ìÇïCo]ÏÅ‘*!´ð�W {¶…7V››æÐM_bâ�=¤Irê¡öþ/~€ïþÝÏ{°Q@Õh®Ý¼šß¾d7»Ç†ýþ¬RhgE¬OØ°Š¸ûprè«ÅÄÅê¥jµ{þ‚©×`éÕHËõE6ÌíBEÑd€Û¶¢§¶@:}Œþbkææ¦1Z³níº|½0„ÕB• -ýr£XBVÁ¾$:fç¶mw¡·¨¥æ{<ÒÓq(”øn£ ª}s(ÝáCãüÉ“”+?â8¶ÎÚ–µikÙÃY¶Ó›sU@µÛmÚ­&•J¥¸€Ãåµ8½T‡:ýßB5i“S“|ñË×sìÐ!’4¥X*áúǹàuÆŠ3/!E�}øž&K«¤ 6=•ïYQ*v×U„¦œó£™¦üÉ-wsÓ‰9Æ*eF:#YªaµaøŒ ØÖ)&�ŒÁš‚ÐÍ’óºgÁwÆZ +õÉ ýH»Œr+˜\bòͼ¹ A[FZÈ:zÝÍ�г çïûú§¸ï_ŒáIo¿…�±U¾íëÜý¶?¥5y­žµ}/ܹ™‘BŒ¬Òâ+*VWÊŒ&Qš¥ca“”r— – qŠ\—(¦ÕH¯.ÐM jµä Ia.îÆâÇ÷ß�š›b°�¨Sbhp�üÚ—œ¦j.p%*rèžÊÊWvÎ ž/%‡†MŽf¡ØAoG,ó[å¾UHÿ&§¥»(.Õÿ~“SS(Kµ%p¸¥¥Å‡ŒÖ Ë´èe{$¶}Ýömãýƒƒm Jkâ(Â9Û«&´üšƒ0çãrôAr}OU¼ûž»Ù»÷>øf]m ›ŸþjÖ_üâÈ<<毲§ÿ‚îz¦HahüTÉ~Š0ÁP³ÚtßÃt«ÍÑZ“j¹L:¼’•Ï}—þÁÛyÒ?î§¾Ì{5&. e_áÕÄ:ó<<�ÔADq\Vƒ"(™W“ŠÜ޼FV}"U¦…Þ[*ö›“PäÚÔNÀ¡HONS?þ ‹ÇösG§_Lsâ0cï_t&¯=kÃÅ"Nx«Ê9íœÒ¬bu_›´iÍNtÆq"-¥n²‚ÓnþÉçDßÙ’ÙÈMÖÜHÚ¹ß_÷8â»+ÅÂá°�I»E­¶äuöú½×Ž:][hi¡ãòÅ.eb^iN E~±©³‡zFaÂ?Ü(ñÿ�¢¶¬íŽð;ж©Ýjµ©µøViR·izh°:4³ 8Ëöˆ¬X*éÿ¤7FQ„é¶lnµÚ=Ã]8wúÖÑÎõXº°È.5(š­&Ÿþì§yË[ßÄÔÄ ÿtK žu)Û÷<œŠ°ò‰XÞ" ŸXEa|ý©Yy.u8¥OÍ÷ŽƒáR‰�ý}h+ç]ö—ü2õL†¶îFÇŬm²èY �é¥?ê‚è¢LrË×B š ´R™[‘Òû2�…ÏÚéÃ=Dâ?µ²oŽ@ qÿ0Φ`bŽÞy#ßù·?`þî›ñ åøÉÝÛ¸fÝ*b­Q.E†i´³§<§4FqÁp?‡�Ÿ¡1{ìxDZª²^B0�(h;•@)Ãk˜"(åF€y¤•ÿn&ŠÐÆÐ¬Õ¸êª«‰£”–nV~ӲЫ '`B:u°ÈئÊ�ã…ç‘»JùTji”º¹p5•ó² �sLÎLã8Õ3})ÒÑÐZœe{$¶rfb¢Þn¶œµ–¸XdttÌß ßÏÑ—%ߪwevȰqË×eÅÁƒùä§®ãÁýû|%su`Àkp�ùŸ¥0´+ºi§mÅ#´eÀY¶š1&j5›˜(ò ƒƒDFªQækªðu¶N^ˆMÞ$rVÄqøÈaÚ÷�¥©Ÿ‹¬}â‹YÙñp'kQ2ïGHµí E©:]FÕÁ¹9�Y)ç=”³Ì7[|â¡cÜplš£K –šMj�:ºP¦‹Bþ^^†¬°SR²AÊß|¿j´VÙùÛn¨­w©ÍFêªù¿ea6@ë‡÷щD˜Éxï�#hØÎÉÂT‡MÛ¸4ÅÖÐ&¢Åç—HÓT\/ëÁYá@‰°$xë‹#68?Nyp-ÍÄ9*`£I€ÕAsË0²¤#B˜a®»ÙlÒX˜gÅÈ0+W­axh„c+|×ÚÀBZ  ç>(Iç�E–� °2@É|üQÜ�åÊñøA¯cå �×ÀŠíòË®+kS*–¼d�µ®fŒY\œe{¤Ö·tr>mÔj§ž” Å"h�#¹!^+©‰&> ÛD†ú#¨ž}F Q�R¥‚ÃÑnµY²†U=U(QÐB ?ìÇôO©Tû}»i¥4 Oó|MãÄîL³ÍÑźÏÏD• •~úFVy4ìê!‹]ŽŽi*ëkÒLÆ%’—FÔžˆ°Y”²†iÍ?Ziªh…—`D›ë‚g±‰‚OáÙHM5¿žq¬8ã¢Á1´ÖÄãë¹êéìzÝŸS]¹ŽrÓNm÷¼EÔÈ´Ã*ƒ$[ Ec(DƇçÜ‹&c‘!ÛYù«D€¸BÈÊ"O6ÚÙáÓ¥6ûŒV�©=ߢÑXò˜�¡!víÞ�RŠÉÉIöܵ‡v» ¨üp—Ë“Zr9B—b{•Pyí«É,GÛ,Øq ÒC G޲@�ÓɶÕA¿«ðëgCwÏÝ:gÛ­f­ÝnÏ-β=Ò^8}ÍzÝ‹]:ë¼tûÐà0ZiòK s’9UÖA#§©šcÅø[¶nóž–Ų—¯×9¶>žo¥jsŠZµS'm’ôÔ�­Žºa5MÛA#µ¨RÛžÿ:®þë�pÎK~•X©ðt³ãÈ<…lÉŒyÙ|XKxC©“ (â4è[#s¸F0Ó"¶IÉ:q¦"×U[Ë%¿ÿNÎÿí7óè¿þ×üîÛXqÎU4Ûmš6 VQÊ£w7ߥÁ‘Ã𳔌&ößÝ1ì@â ˜½ILå\o‚|µC0ìDy � ÑÖuª€6…Vmɳgfg™_˜§6?Ï–mÛøòW¿ÂŸýåŸñ¾÷¾‡oû[$I"'n.yá6Òþ{xyž‘,Â$´^¡¹|säè­…¦zäUÉéèéÄ2ü\>\¦6eêÄÎwrm¦6Ic¢}Ë€³l�´§’¦©*‹ ³rÅJV¬X�ÎIƒä>¡…”ÒP:”ÍÈí=222ÊÚÕk,§•†F)�®`ªÞbÆ*Ra•öÃáIÙífSŠÏ`ü¬K]ߨjEw2,î‘!2©, Î% "…nËBâðf…BBK$Ã{uu¢¨Þ€P\–EãÔÂh¦ó \ÅŽÇ=×·|.Æ‘Ï}¸4¥­ CŘùf›Dx¡ŠŒ,@÷oNûëˆChç¡P‚qçÏE\³Hªˆ¨Œ=I3ÏBŠR•»méîWÔ0•ÊK}D…²÷ÒmÁp"mpÝ'?N-m17?ç)÷Ö¦y´å°(3œÕó;Ù’×Ò9Œ� ÈÏí„ÇÌY_åµAëâÃãaËëPm upŽ;J±T¦ØWIêKK ‡=¶ 8ËvZ+ *¥u¡t¤ÊÓÛÖE9–£~ÛëÇ/ÿžt¶Ó4‡I£ö‹q±H£V£9}œÃIüv}Q _ú‘ˆé´û™Re6!bÍcžëÿp|~‘ãõ&NuÏCGX³±Ág£™:Š…"͹ig-ªËÍô¿BrOà�˜ .V‹'?—9 ÿY¶ß�<¥U~Sa )c¥.™hÙl?r×Aª¡`àäÞ[pà™‚ïºç!~âúxë]÷spa‰¥V“n±ç)¶§Ø~Ú+9T q÷ ØÒLð£‘à͹L¬T ¯L‘U+ò;ž˜‘Ê2ÖÒ„^¬�¨-²pôAâX“¤ �V“÷ÿË¿²ÔnzÅ�(ŠÙ¸y³W9Î&Ú ’G�Ÿ‡Ôd¹”#4•»ûPö&ƒ^Šáö.¯;§°Üó ©ÑÙI;affŠZ£FÒjSˆT¤ÍÂ2à,ÛiÍùÉ_;VJÚmÕj6ˆŒ@å%XÉwë…Ò“ý“¦‰xº’‰ ´1œqÆ./!o­Å¥mæî»�£·³»Š¨½ëZ|î$›´#£Y¿¥ &йcbüd©Ñ6åÀR�?»á»|ãè4‰Rèê0ƒë¶>\Øh‰¤<²�¿¨˜/tÏI†ôtÝ‘�¿¬!2:Ô @öV÷u[R„­¬e82"ß#@R2×¢îþKãëp6íÖ×-|xßQ~ü‹7ò†[ïb±�@ן¡ 4 üû…fƒ¦uø. Î‰Þ4B9E²ùÄgaËdçR9 l0B”\’–¦Ž£Ó¶¯QJ1uü„§÷¥}xmͪÕìØºýÔàää0zý¸]0Â}çp‚m够·]豂NNOi*¼·óœ¤lƒZ½ÎâÒíV gm\(W¬Özvp–íô¦ ¥4 ¥I—¤˜®ô?äÔ )Ñ2VÆ�mj=•ÒƒŽ¨î‹WÎØ±“³Î;­5Î9’ùøÜ{HksÄ& s• ”"ˆ´hñTª¯Úy•ˆ˜B‘ýs'ùúÑI¾tè‹í6ß:|œ‰z›¶Òl|Ô³¸à×ÞèVŸs¹2Zù}9á=„§I0q ‰?Â×F‹P’È]„-êµy<()•Õ³82rAõT€ ÞG¢fȨÌKÝr&ñÈ HýZ+h«$Q�©FB=u(AÁud£áC‚¥´Ï¹¥Ùç]J³x@Üë�\«¬ðUKå„PæFH¼9çW‹qÌÀðI;ñì´z}‰Ùéi"§Ø}ιžÒ›¯*‹<©üZ�|€µiÁº!8É7ª9 �ˆ“î4Êì½xÏ9ž–ë²6Ó'ŽG+C\(Ñ×ׯJ•J3Š �eÀY¶ÓšŽ½¢@Úl4[ïaxºcjmo¦JøÞå…Э~ V_âÄÄ'&p§~Ì6Œ/xÒ£¯º†ñU«‰âgSfnù2{?óNTš�1áAȰ“x:. ­`óµ¯ ªT¹uºÆ?Þro¼å{üõÍw²gbšr±Hºb›^þ+nåîKTT( %í ¨“A2åÈ -`  ÛÕ3# Ú:ó†_¯e*.#ø!ëzœ +h b. ÷¯Uï�€úWmâ wOzÏ�\úóǶ×þ>**`K}^)z¤\ÄuCj’0 €z»íÒ਌­ÉXr"'@”È^ìÃŒa'=B'<3„2Öb›5J•¥RƒòµdåB™ñ«ózjJk¼å'ñ{³ÍB‰ù>|À’á©ÓfÊd–<^~ ‚9ºjaÜNkB� Õ±óKi àäÂ<Ö¥^Åáð¡“s“€ú2à,Ûi-­7‰K¥êðÈHÒnã¬ettÔóëqù…jaÜ;Ÿ­†³33|öóŸã­o{3_úòõ4�à‰Î‰ç�s.;wž�–´Ûªó@G¥ùÈíßè‚$6è�x‘m4+w_Êà–3i›ˆ¤oØ·=S'¹ãÄ4F)Êk6Q]±NY¨‚ˆ¼‰7—MŒ.h¯‚A—Úœµ.MÐä�Ü*`©!<4¥Ï€�Í›Iú¹›n®+[¶„g �eÑÂ)EyhŒ�ÑÄå>L߀/ÕÖúv¢gQ7‡Ó›²Ðj‘:‡s¥±x�À#¯�”´Ñ"D& f»Më2…%©ä~=¹½Ãµ[úG<¥¾ÝnùßY³ÙÀ’rré$ýýý§kÞˆX Ï=/¹s=NŒðïá6*£%C.!ßTŽìNØžüæmΑßÛ*«Á©-Õüõ�âØžœžYRŽïðHlp–ͶZ¦]¯_>qìh»Ýjû¶±¾èQ©GøcžâÜ÷߈³³³¼ý]ïà£þ w}oSÓSž” ŸÃ'¿ññqûØÇ±zÝ:”R8ë°ÓG¸éŸ~›C·~�Ô:üdžfšcŽŒ…¢K%VŒí8—5W\ëûê·ªC4GÖBT ÖN™¬×»ô‰¤¦xªHQz2oÐL3ù×]ênKæ¢$üðë/% XÖÙXñ”nuYZ‹êù4Ûž 䄨 ”$Ùº@ƒ¨§ z͸lÿ2Ï"·”´H:cª^箩æ¾Ù“>!¯\¦Ú°¾Ö�3 Q¹Ïo/s16ˆ(‰œ’lçÿ™­“ç*¦»!Ô‚ðvxP;\395዇ÛiÛ'·1Êç%ׯÝÀÚµkC€ “ïR¾)hËìdR=Ÿu¦È·Ðu …?ÃÂ2ÂÍ ×)ÂPXxÿ柘 w¢òDw}˜òð±#ÌÎÍR_ZDkmŒ6Óò!nºé;LÍL“¶Û]–š–IÎlÙ}mŒáÒ‹/áÖ[oñRèós'ý¶ÉÁûøÞûÿ‘¾‘•ôoÚ…ëUHe f¿Œ‰F¼§Dšræ/ý÷}äßà[Ÿ¥®bFμ”¶’”#»nfOÛ ‘”dAÕ�t6)Z‘-EV é”h0¦AyÕá ûø‡ùžXÿvZxd~ÿ.|�—]8%½Ü�6괓ﻑ݇òÊÐ;‡ûyݹ;Ø52„î��GÅE8£g_æ×Sd 8É)™ìZIAÒnNFûkƒE"T¹5€Ì IŒpÊUúV¬g¨”P›�bhØ14:J¥XâÒË/£X(ô î«}šdè,h¹ìÏÇ!½ƒÞžŽ 9‡÷�#ôjÂõÂÐ[¸^>z*ò©Ù¡r4.XïûÍÍ ßú‹‹ ”*}8œj¶šsËβ="SZ»Î˜,‹±Ã‘$‰—­h‘Aâ0ÔëÚû7Ýr3ßùÖש5,-,zåçɉ –ÄöbF666ÎSŸú46lØH¡Xðâ6MXºç&nù÷ßgêþ=à¬ÈCdtéD„qÀaLÌð²é¯aÕ® ¸ôõ®Ÿùs.û³2°~©“—Yq¦u|dˆ-ˆÔ y–î� z­…¬ŒÉ¼�¶ÍÎU†ÞÂþ_ZL°~ ›´‰ãfaFQ\40 àUCVœ…þµÛ)m9  2@+*Ñ215ñ½Ùo¸í^[-æš-f-PƃNip …ôB¤§(Û&dç+œ’¥ÝÚ%á]"ˆ�â¶o‹0¾a›vl÷ù?æçfXœ™Á³Ó=.ü�çé£Io$G3-O˜ ,Æ ÉaT9Ÿ°ÔÇä|JË'„;ê©ùâè‰ãœœ;é{Si­©�‰´>º 8ËöHÍ*Ô>ç¬+ËhcXX\ô®3ä… Â;#T•uX›rËm·ðÞ÷½›#Ç�1;3M»ÕôÏÝw}�{î»�fÙLšÝߊsÏ9�—¿âÕ¾Z¼Š8çhÖ–X¸ëÛÜú¿Ì‘›¿LÚnv«ð¥Ö™è£+ιŒÝ/ÿÎ|ÚË!*R[ÏŽk_Éê³/E+…VÙdˆ(.´¢:^NàŽì½,R¬Ä°@*Õ�¥@¥¨Ï‘rü׺.Ù$«$‹•Ÿ¥R)Ú=¼þÅ `”�Q�¨Ñ‘,?£apÕzžò7å’ßyÃç\Iÿ® ©nÝ�ªÓ2†{gùê‘ öÏ/ñà|͇ßâÁQJÃH©¡–ÍênжÙgd­*q²ø5Ó¨³™F^ò„`¦ ˜ëÖŒ1<6F£^Ç9‡6«V¯axh¸·­#d……ÍÑBJo‡|s9yayõkùôiwšFñ=»¡ò PûMZìBwÙÓ¡OžœóN£Ñh7›�cIšÎ,β=bK“vmâè±Ù……yïÝ$­¦/þ Ø,Âr áÄcóÑcGyó›ÿƒöÞç‹ìŒ1ôy©/-ñµŽ[ž¤)è@y7ДŠ�á1×<šû±ŸäÜó/ hÈçl’�ÝËÝoú=}û³¸v£2štgÈÓòlµ¾µ[±¦D³;!ûV Za»-d¾i%¼Ž�˜è»Û�ˆ÷±~¸J2²Ú^ö±‘9#¿,h(FPŽü:RjGž“/ PKQzIØÆ��*-èßb©évíbãUOçš?x'OüÛOðø¿x?ë¯}9ÎkÓ9ZiÊÝS³XØ”Âè*†Voè^ql2ù'iàZ’²óKº UŒ¤ö\ÀV�6¤Gؘ›bÃxíVÍkò �Œø&lç]p!QeLŒÀK ;ÔæXNB=¤;‡û<í¾¤7&ò€$‡kzm9”kyÑžÌBJ· 5z@·Üvë-¤6õцN'U—$ÉÞr¹2Ï�°–íjF› êÜb©4ª•RkÖ­çQW]C©TE˜$Ío;m (åŒïÿÈùö׿J«Ýö ¾¦ÔÇÀ`?ÍZ›&žZyée—3<<’S�ÍŽZiÖ¯]ÏêÕkX\XôX»Ýò^”JÛ¾é+$iâ“Õåá•Ô‹ÆJe£‘ 3ˆnDF´kbȶ7bôC²LÅû؈�‚<¤uvél0‰¦öû½#…¬cà!Ø`‰FB©•�#Ò‹7h„–1Ò4Jit©�ãwßÊL‡-ˆM¸dåûæ¼b¥>v¼øÞr&J)d±ª"õÄŠë*®Á÷òm¥»^a�£׫¶@ëÄ*­Yì¹ÛjŠéÉ<ö1�eÝšµùZz`J CÇä…½Ä� á¨U°A.ƒLå%òÅ"×ÂD`èä[Ⱦ«7¼ãÝï`òø1|Ÿ¡f«•´ÛŸ¾¾ìá,Û#¶b¡´àœ3&2®¿€f«…êÙ­Ð ½¬ÐëÉ·Üv+ŸþÄÇi´Z 4I¡Êøc_ÈR#ñaŽ$M™>~‚;•Y_q0dèÂ{#»ÎØÅºõë¨Vûh·Z¾…ÁÒì éÉ)î߸åo~ž…C{OIú‡cÖeŒ¶P:_öבÛÉ�Y(4Ù-;¡`ðCÒz•hÎæ‚nÃFe w—4k trŽŠ~¤½dû»d…’gÍÉ6YwL‹lôâq™·ƒÐm“E§.h¹üÀÜ"Ç—âÅñµ¬:ëŒRÝP]v]`E^KvÐâÛ°RýZœS°ŽRPŸ™`]±Æ¶•%ЬµÔ–é+•9k白¨ÈbˆöíÛ<‡Ìü>ëª;ð¡´·¿û�ÌŸœchd”Jÿ›Ÿö*6]ý4\ßJ|ã¦F�o|ãkž2�õ3bp£f±«ÉÉ >ý…ÏòÿúO|¡SËsðÐA¯„ ´ÆÚÔ•4iNæä‘}Bº¥û„/ä]¤N™QY»€HäR+• FWÐõ¿ÌþëìXÑËFԜȇåD¾"+zþHð1�N$�N—õΩÄYHMn£YžËf¯Û©ð¬$à‰ïܶ˜p8› k"n›Yb¢±µ¬¸ð1”ÇVw¯6„Hç)“Þ™Tc 1Ä:�þ›ü7ì"¡³­4 Ñ07í�a¾Ö¤Ñ¨{fã%—^î A½…µ_ù­°[Ž”“¡�·œpYÐY4?ü&ÏK¼–C©GÃsá9Ÿ‰Ñk�½ÜÏÄñc´Ú-’4uZéY.βýWí ¹´´ØZX\ Õlz¶*”¿jzè¤]ß‘�è¾{=‹¥^om=Ÿ]O})£·¯@Å”RtîٳdzØä$¢Ú={ïå÷þàwù·úG>õÑs¸6�Å%¿=€‰c´‰q¦@´bžA/àè\XQÊUer*<MAÉúȈ‰SÜÚB±Ø¤A6h ¦UÛ{Y¤�”D6 Å|µÔ]�ÒƒRBqÀ:h‰�xp“·Ÿõb°‘·j¢Ýpi³Fkê¶ÝÄ&mŽ,59^oÑTšõ�Nâ,ß$ÊN„W%rXðÄÏ*4Ô2AÒ ³ÏEÓ;gqi‹fi˜£í23u{ªu|åJ´1A�Xš +øsû¨|’ ÓT°m^ËŽp½<Ñ•* —îËû9]O å¹íöÛ|èÀY—¦i²/µéÑeÀY¶ÿš)e•ÒÎÚ4š_˜£1‘ Yi€z’îÛ»—÷½ãm^E¶ÕlAßg>ç'è_¹ž¨o€ �~vg9xjý¹¹Y¾{Çí,Õk‚t�y9G�ãoÿþo:Å¢wz9ôf³‰³6»'¬£¸v+ëžùZÎú©?âÊßüw6Ÿ Ú …ìŒL´«@ÇÌŠÜhâü²[C“F˜SÃ>$ȶy9Ñ{Ÿ.Ö´PkŽLP®`ˆ ;N-ÈBXÉÔ³¢ÖGû¥Œ â@ðÝ2Ï0ËÊ}¬¹èÑ^*HáPÆà€æÔQŽÝøyœµ~? Âeç-ó\’:­•Ì·I  IT’Ì!ÎÊ`*èJ¿kÌÏz`,Š>�³eÃ&TPÈœ_À¨?ÓCö_å��´Po°6¤eçÄ´òUžó‰ ¡Ê@ž×•Ç` %zä¡V«sÓ-7�Ö€ïƒS·Ö~!*•ü—l¹ðsÙœk;¬Š Åçh5š¤Öæñø{†Öæfçøð‡>@»+EÓh¶Øúø0~öeÝ H±ñŠ'±ÿ³ï¡5?KZ[{o¸�Ç<ê1¨¬ÀÎ+|쓟`ßý{}Ë„´ËŠR€*”éß~›;­§×_þ$ÆWùn�ÖºL1YPf#-¤ñÉL!„ �q2¯"@¦;q:›…µèž²Ì«8•Õ»¤"?Ô�è] ©VÄΡºÇ¡•v'Ú°- ‚å%D+%ƒ -¢‘Nöä�6­HÐËúÿýü9ÅòB<3¬iÚxù“}Ó7¸ã}ÿÀä·>í?ôû«-b�£™ÊFrÙ¿… Úm‡uEÊA"~’†®Í\õ {h¥(­X‡]œUó‡ Ô^ÀE†¡�A6mÚîtå^Š.G$3¡\)Õ«Y›`�J¬×Ë\Ž·BXÙ›ú–-ÝÂmñ>Ø&d·=tèS'°Î‚R.i·æŒ6×nÙÃY¶ÿši½<”¶Û‘MSæ§&9>1$*sB�§¯vŠ;ï¼óZž=æˆÆ7°öò§@T85ÑDÕa6=奘J¥µ×¸ºûÎïòÎN­Î÷î¹ àðóþ}�ÏöÓ>—¤´Fk�Â1~Ù“8çuÌ5¿ÿv¶?õ•TV¬Ç™ØS©S“v·þ$ð0èÊJ„˜äÉíð«#ÕŒe�«È %ž” æ‚l²t‰=%> Œ`�ÉЖÔ]“í«Û‚yf½7“}…7 (ÞÂ. 6xPw"d*1£²‚V„·ˆ‰X³‰¾Ñ•è(FiMyÝv6>ö¹  œ’ªÉ@M*=[—§)=K!/D bªä¿ƒlµ­}-ÐÉ°Àüô4Õ�"m 4 ÕåÕÐ䀓ôü� »d à’Û÷ø‘õšõUë-ç ÏÃä{° N)¼�í¬ã¦[oñalc"¢(J�£©³÷ó_µeZô²icpÖ>¦P.ïrΔS\yÕ5lX»^þ2³EðÃÝ·o}ó¿3Ñ}J¢»^õ[¬<ïœÒ˜S9ÅðêõÌ~�¥ÃûpI‡cúÄqêI¹gí¦R©øää›þý_8qü˜3|èì’ßúv¿àõ¬Þ})å�aÞ²æÆº,¯!'zD‚)öfA4p#Ësdóˆ)©áèD¤$Lì§.£›,,å”BG æ"@ÐNY;ZÙÒúásf„Ç,�É0œu™vš ZTk-´×Ò#êî#;çÆÒ"G¿ó9“G©lÝÍ…¯ÿ †7ž�Ò ‚ F„ ù` ÉSÈ<›Ñ9¾òµ¯ð/ÿñ¯Ü}ÏÝ`]>ûRRÿµÎ><½§‘�`®øf d—_L-ö“›§ ,l뮯*Ôwˉ‘ëÞ¿ï>¸e ¦3Pj®^¯}†ÿ®-βžš6¦Vî«úÉ^)íCj¢"0·]íCðõ¯~eŒg¦U·žÃÎk_F©¯)9ŸdL0_xîOý‘±EQŒÖšÆÒýÐûyï‡?@_¹Â“ŸötŒ64ç™Ý{‡¾ùiÒÔve÷E4D<9ËLÛC¤×†ú[@¤…9¨ö÷$&wº&Ö*³Yù7¿nö,ª6»@�Zý|Ÿè%õZ(1·ºªÏJ¬‹’�â‹N¤$ð†ýkŒ@z}~RÝ~.ë/Š÷6MððÜJeLx‹bh 2·æ‡u¢×ç@“-(u)ö�ÐnzÝ>ßÚ¢ê5¿47vÞéˆÈÞ±ç>ö±�0=3*)ç²2s-,R&LÔæŒù'¶ Z}àB€ èÑ�DGÍ…\üjx@ þ±:t�Úâ"IÒ¦£F²trjj¯uöÞeÀY¶ÿ®9gí}ÍzmÑZçûÀÏ/.bm*âÃ= |~å ÏdbrŒ&q†��{ƒ«×Ë|в֥ÙtÉcÙñüŸÆÆE¿_眗ÕyOÇusg\vÉe<þ…ÏÏú¯4xàobbïw±îá �ˆ"N Ø–«…�J‰‚Mv’@ª¤áßU™L‚Þ. 2vA½‹픤@•©d¹ŠêrØO“–ßEVø ÆWÏ—ìo2‡ƒÈ‘¥.%Jí!ÀS˜É¼DiêûT5ö (jó^!£¯ZÅ+ÇWøßóW:E®XfdíP†‰ÉIzp0„E !ÛK�¦�¤æG˜¢;ÂãÐɳœŽ_æ:S!øIK’”¯}ãëtïQW_\Z4&þÐà¿mË9œe³¶•´Û¯îŒ¾ÎPÍV‹«®¼šþjá�æíÄÄ Þùö·AdXZ\¤Ý7ƹ/ýE*ÃH˜l¢Íúš #·žM+I©y’¶8›Zöw\ø}ÇŽpÁÕWrôȯô«”Â5�ד¬>ïJ •jwB”ôc©}&Ÿžƒ‰ÉJ58‚*�X±n ЉlÓì¤'<ˆUBm1 Ʋ›%J\n²}g:‹b(¡ËÖ ¿É¼’’„„ Š_‚‰<7ä³d®IÒÀ#B\¢L+òPS’È<¼la~ñ@@àÅF¢CªRйý{˜½ëFtcžÔ*®¹æîß¿ŸÛïØÃŒ žF�cÝúµ¬]³NþH$‰ ¿€Å‘gùR8*,ä ù´èpuEþgäçiB� «Õ÷çi:Ä{ÞýïݪÕjÖÚÍæ»€{–=œeûŸXç”í˜6†cûöùþ5yJÇmß½�F­ÆÒÒõz“­/øY ãi :«yÙn�fЦÔÏî—üŸó“¾R½PîÃı³¸û>ú–·sªð,Š4‹ßû&÷^÷’f½Gù„¡ bXRÍèld„£,7"õ»ÂûÛ ðÔ qºùuœë °Ä¹ˆŽ¥Ùd›ˆ÷*h­£2¯H¶XN³ÂRÿ$gû“ÅŸZ‚‚8Ž\ ä�8¾Bz²^',´íþ›“y“t%¡B¸FJ¶qÈ®G 8DÝ’ÑŽòدէ´¡íàî |â3ŸåžcS,,,°Øhrtj’¥z#,6ÎÑËÙÌ'Èøg`²jXî# ]烉 Þ?¡Q—³�€ü“½ù›ý�4š^©½¯RuXæ�ÿ¡œÍ2à,›ÖÀ½.µÎZëÕææ‘�C²²qiq‰ï|ë›8¥¨V)­ÙÊê]é Ï!4 £ì¯R¹Ìî罎s~ò�éßy!c«[�Òšcû÷³Ï÷蜋¶úâ"­¥Ž~í<ø•�2wâ©Ø·Tà¢ÉÂE¾Ñ:²1Å(›¨­è»e…B{ Á(r7°tÈLÄgÖ¡ºùê‰ÝŽŸ]/"T�á/Y��8§4 ·u“ûÈÅ&Ð/ hÖ€3:óâd=Œ)Iäj§RáYJÿH�¡=“�ˆ ÔBö°`­·EwU9Wj„¶Xʿŕ>L¹Ÿbg9º~3×ß¹—C5Eux%…b ëÄeNLLà”`šX\„i›Ya±mØ`-ÏË‘¹ l�Ó¢ÀïÃN á†J®K¬¹–¤ _ÿæ70…ç�÷-­Ô—úò?±å�Ú²i¥”Qú±J©�ËeúGG¹ô¢‹QáͦàÛ7ßÈ'>ú!k‹œœŸgäêg²éª§âPÝŠülòÕ‚aš,&Ÿ¼WÆ7A=óÚJ³°´HR_òÉß´Ý¥PÞ]p¸Ôb—N2u÷-$Ö²òŒó½NVdÀJy�¤…m5¨Íœàøm_§1}œ¤Ýò€ÕnÔ» àºýR4 ´j†Š²ž36�T�Úsl<d–‘‚ðšH䋿ɉX ®„a]6ÉËP˜E´­V8@¥Áö€œüýPR.J�- %¥Ü„Ž0¦EjÛ«¼EeK�Tȼ×Tª,¹îï­Ù`vï­Œ–ª:ÎüÉyÚͺ ùFrl}‘­ë×x1X£U¯ÂÏGFovä#ƒ·0¤óÃ]áytmùyÓ,ÜGèu© ä×[˜úÞöò®·¾åYMf§¦fëµ¥7w-βýOÍ– ¥(I“k�1¥þþ¸ì²Ë)‹òêÕžßø¯oäÐCb�´2̹¯ü5úÇW߯ª‘Чw*7"qï+ÅGYqÖ%¬Ø}ƒ;ϧ…¢vâ®¶à©ÐQ¡ˆ2Æ3é"eISÇð¦ô­\ïÃ(¶Yciâ0Ó0Ú÷¥�°ÿËcgyðúsä;ŸçÈ�×süÖ¯r Ãv;tËW8qçw˜{ènó3¸¤å÷�‰qh�2*B±@vÚ,@‘ O-ߎ‹„™ÍÛb²â5;;ÙG&òÇ&c¢)á1©Œ=§@æZ²ž;þ=¢æÆ‚“ 6+ÁNj«fJR T‘y�²Ô%æVÐj@†ýb²×dmPö:;/á�9ËÜC÷²º?bq©Q…Êð t¥�Æü�¥y_Ÿ³~Õ—\x*·Eó#h#@ø9âÄC~}~‚|…çÜ6yž°°3<Y¬ºóߺñ;Ü}÷ݤ6¡Ùl4jó G­s¿ý¿BX–¶Y¶F³~#P·ÖôÏÎ͘Ûo¸�pö™g#½:ðíÛ‡W Hl.`xÓ.R⩬‘—u²½q. ['›B‘þõÛ¨®ÛʪóÍÜsšÅÉ#LÞs+ ð©¸�Öþ=Dcë¹ô’ÝŒ™%Þö f¼›©Î:‹Gö³x`/¶Qó äº9J©SE®xS¸î‰:ë(­ÙÌàŽóXsÑcXsÁ5T: †ÖXñÄßN3 ñ^N©`t!á’Iˈ‰\L Â EJ¨YË6žåEw{Ñé´»_!"œ)§D �j+]ps^ôÒ’´Ú`Ò$ÅØ](¢´AkM·þÛ-äMý…Ñ};3m%r*~¸×cm~Ü\)™¿^“Ôgó¢ Xlq¹�Á�;™}pž‰#÷`‹GÙð„²ï³ïò­,Ú­%¢ýþ›.§Cf(]ê…jôþÜåc¡¹pŸáúò½äŸç®kmFNì?;N»Ýâ;*ùbm­ íf»™øË³Àÿ’-Îr=Î1gí7“vû&ŠÌBm‘oÜðíàœ… 7Ü{ÿ^æggˆŠêõ„ÍINý–�¹!FÙòŸg_ß›Æu')Y·8”�Ç�v@`lçylèÔ{¤Í:íÅYZ��ÙIfïùÇ>öoÜuó$µy0¸pžíæ$¿X9 ´CÊÙûu±ÔßïÇÄ×?ξíç²íY?Ά+žBepX<½gž2RႇâP%¥•dØÉIé~ðŸ�Ô0“:nÂ[è¾nÒ£í´êÊR.az®/P›:J}ò(­Î2Y<é[w'sS¾¸¶Ñ¬á´¡<¼‚¨ˆJu€h`Ó×ßMÊGÄ£«;Ÿ�y·Ý}�i³™j ©$nhAV}»î6’‚-û qVðçèÈq––¨®¤¼f+û.¦ÔÉýµ•¢R®`�õ¿£uø�™óšÓiS‘S/C˜?é¡ �"¸P­:Ç; À*Èå¸pñ½šv`·ï¹“=7ÝDE~Ýb©ÄÂÜ쇀tp–íËZÀ'�'i­ ÖZµ·.‹‹‹¾bKµ%®ûôu˜8¢Ýj£*^WË(p"Æß³ôÁZ#ÀLeë È(Ê™g¤IMŸŸÜjSßãÀ—?Ìôw>Mcnÿt®œ¥•:VCbÑ%CiM•ÁãÄ¥˜“{§hŸl�,µiÍ7Q(”ëæˆ’„“Oéîƒ{9qû78óy?ÅØÖ³ Šº` CjÙ$/+øåÜà×% ;I–bÝ4…D4dSà—-ïÕˆzÁ ¦Æ¹LFnz`ì¹4µ´š êSǘ¾÷6;£yh/væ8K‹'iÔ¨–û0QL³Õð£R©2ãÝ•(WQJ ”úÜ~#?Žòе¨R?ªPD;…ÿž¡æœ$‹ðð‚Z§B’† 4é@�_Ššã•Éë‹óÅ8¥;ÿt¡â¿cÚj@!¢T( -1áu/9­¨q@¸}¾TLØ4-Ÿ �'OãÂUCÐÉWƒVy.TdÎ××}ªÓÊ:‡V cLbÓôÎ8.ÜÄÿ²-Îr»éoš(ž-•Kå¤ÝŽoÿÚ׸ç÷qñùpèÈa&¡P(Ñl/Ñ·ñ 8€PB¬må¤,j>æ’,à3=zS¥iÊÌþ{:¹™sü›Ÿ¢5yØOÖ¦ @GÓå‘>Ö^µ‰ñ3V1¾sœ�UC˜’�’£Ð_@¨Ÿlø~?ª¥˜¾{š#ß=¾“ÆL�´�¢QT ¦¾úQ¾½o›;á˜Í×<�ʪÍBù¸ >²�Û‰‰ÐIAO Ý¢G²6šŽ h$û�Rü½ÚXÀYPÁÜä™EÍ‹Ç0sà~jGäè ŸgéÐ}èVƒR¡ˆ¿~€²)IÒ¤ÖX¤mÛÄ&ÆÙ6µÚ’g+ÅKsÝsUôWªØr…ƒû÷p춯R^½‰¡3.däÜ«}6S,£P‚Ô(ÚD Сû}ePx¸î[$ÑRBìK椔1ô¯ÞH_gÌÞ{+…ᕌn9MŠíöxZ\Z`aiÉ7Œâ˜b¡@~ñ‹ €ÀõÔa“̉®¡åKé�ïA¹eB}4l/T׃nº!ªûžÚÔÌ öî%Š#ßud«ëó ÿÔùÐÔÅ}C,ÛÍŠqý“Sê…Ö¥ýFižøÌgó¿øËž<ð™/~ž?ÿƒß£2ÐOª ¬~æO°û…?‹V Ý£ýGÜ’BkƒHƒð d£3Ï4;ô­ÏðÀ'ÞJýÈ>\»‰³ N�6€-Ï9›-Wocóå›é®÷Åþ3­ mZ(‹ëþS°hÀ‰'¸ëº=Üþߦ5ÛôÕê <0éR…± ®aÛ³’».DÊ�äL*A!¦“y ñw@¹+@W ²V²ûfï|O$Ž·4y„ÚÍÝ7³Ô™x'öß�ªÍ‘U·§6ÅŸ£!±‰_FQäÿÞNÛxz|«EÇøÞCQ‘“ ó~‚.+àk£bL\D—«”7ì¤rΕŒ�÷(*£« X!2:Ë£  RöѲhTê§iĪGÇäHC»¶ÄìÞÛ˜;°—þM»Xµû2O¸á/š…‡î¥HÂùgîà1W?ŠU«WsÖ™gùëàMåµm> ¥XKjs>Zn‚È#Ïïäx8æ�N{¬�~ò:ÞÔ鮋Q¤ijg&§¾—´Ú�¦ù_¶e–Ú²¥�F}6MÓgjcúœsêÄ¡C\vÕÕ  ðÖNÕñ�}øär—Ùñ¼Ÿ¦4¶ž0BL²%r¤%ó(óxdR©æa-s‡÷qç»ÿ–ƒŸx3ÉìqHÚ$i§Á#6>÷LóÇOâ²W^ÎÆ 6R*ÑñΈM´ý-,ã!&F£Qh ÿ>Öô�UØxÑFÖ_±…¹‰yZÇj$m? ûc¶&�r¨ãY™ÁQF6ïBGQ×óÈ:~"ê[z™£q"luУ³¨œ|E� 4 åƒl³ÎÌ}·sðÓïàðuo¦ýà÷X<ºŸù‰Ã$­Ö9bOÃI\(Pè/úÑ·µß�ÑsV²öò ¬¢x2W*¨µè•±û¤Qã¡o|Š}ŸzK{oÃ+ tkf¢rDuûWüÌ£8㉻ˆú"ðûtЃ !!%¥›§9ITŠ%%"§ü¶‹²šöL›=_¸“oÿÙ—¨] ífì•6 ­Þȯý6<ú9¨(îùÀ©È<dËé4S�VXZi6iöh�pÊÁH“ )üdzü¦ë¹ç#ÿFÒ™¢Ñm¨Õ—è¯ø¼L­±„1Õ�*••UFÎcä’qFw�¡û5•Á>ÊQ…–nRKj¸š¥=×K'–˜úÒqï›çèýG˜Ÿ>I_±Ÿ¢Ži4›T«Uðç¨Ðå>YÃø¹W2ÒanÙ…)W±=.–~whxxE’$*ÒŠ /½¼£.ð ’VÛÓO‡/|,Wÿö¿£� ‘ɵt› EdI‘ dQ«£•cñø!øÂû8ñíÏR;ºŸ´Qó…{((�UØñ´³¹âǯfhë*ÒX, m"b�Âx8‰üç)©_j§ñТ, Eä íÿ««º(¿}ª8tËa¾üŸåä½Ó>·“¦‰µE«6qÅoÿ‡WK6ZB nŒ¨¶·A?;B‰•Äq-(¹?¡µh”£½4Ïáÿè�þõ#ûqÎQ*”<ؤ.e �R¥Œ«BuÇ «.[ÃÊsW2~ÆJ¢þ˜Ô¤§X{ p8僑~û˜˜¸] YoÐ<Ùdqÿ"“û'˜¿s†ùÛgiÎ6HÚ)I³M!*ʇÛT©�xõf†.¿–ÕW=�¨o¥B‘ÑPµ!ìu&ŽÌŒPçö$êÙõ‡S¤ ïáÜöO¿ÎÒÁ½hçè_±Õª³*jó̧^ËžðD�æ‰nkój<™)ò,/T— ºX*X×õ<לB[\Zâ÷ÿâ�yà�-4õÖÄ‘#Ÿ´Î½ìÿ´öf9¤¶lJ©ý.vŠÕJ©¢Sðà¾}¾~Á9P…C;ÎcÃODk…V™>˜™×‰t q#•œm‰=7²÷cÿÁÌ-×Óœ:â)†ÎçŠ_|4—¾ú †×Ž µ ”R§’Ô¦û_D €r”ë‰Eã «Rükei«©²âþT1¶fŒõndéØ"v!Å&g-õé´mêéÚ…Jê"3™‘*)RýØ!uß²:¥!7­õ›Òœ:Ê‘ë?È�Ï¿—“ûïñ癈ĶAC¹¯‚ë‡u�ÛÄy?uçÿØE¬¼x5•�ULÙ€§ºf´â›DÄ)Ñ͉ ¥Á2ýû<{ˆá³F:kÔ?¤‹mtK{°[\ZôÕé®Ý¢>}”dò&.Q_ƒ)UÐJe¡UÁRD-@ЫRJ*êr�l6‘³—eíú"Sw݈k5<°j¥üùõG†õëV{Õa{í¿ ªÇþBs�LvN“41rˆZ}¿.SξU÷z9¾üõ¯ò¥ÏÎËM¥iÂÉ™™é¤Õú-`/?[öp–íb¥Ô{‡ÆÇ6FQŸœ™%m·ñMß —üÚ?³ù/gÓíAŸ ÉÎ Ù¤:x`Tàoþ‰=7pÿ‡ÿ™Å}{h-Ìøc)¦±úš \õ³�fùÿ{o%Éu¥ë~ûœˆH*®î®&u·Z­–Ôb°e™=cº\fffxÌÌïz. 3‘=wÌwl�m™%Ùâ5Cu1$œýúí«2V.õÀCߙڵB™Y•ê<þ{ÿûßGÁKy]ÁãQ”ŒÌn=Þ@¦ k‚ e@@w*8Nd’WYÝa<\!âú‹WùâõÖ^^¡¿Ùê­)ö}ןá‘?þ·‰›“äÓÐêÌ£°(¶IÅq@ë–ëá«2 ›“eí+ç h®üƇMò¬EA’ÔP�ælÓj0{ÛÏÉßwŠýo:@4\ ú~%š€@AaÀ,ÈÎs�vD©«(AéfJçÜ6W?u‰›Ÿ½ÆöâY?Åá ü’F™ÙÇÌ[¾‡CoûLÙè<Õ¿Ë�9y¸Ž×à+âŠ|Æ|㌺T9Îä_³õê7p¡`8èQt6™ñ�w¼ù�ü•¿ð—L0Q‰ñtØíÁD»@$¯ë�ö[§Â~Û€êãßI˜=Õ?ü§ÿ�Õå‚äy>\ºqãWBþÂÿWìf—áì6‚®ÂÉéùùãQ7;›[¢(¦fšœåÈwþ!&öF^^¡\(ÕQËU“G©Ü:‡É›¯}åS¼öËÿ„þæª-œV¯™K8ø�£œüƒ÷pôñã´’ ‚([8wj4GDdìÄ!(£È%Cp%E%œøòZ¯%3* ),­TÛW§~¤ÉÍWn0Xé!A äKWñ3{™>z«T›]«–-”`SU«UÕ÷¯:#F¢Jê.Ëìûé_øçtžýô;&a.BAR¯Ó˜m0{ç9ùKŒ‰íVEñMOíP�©S3L?6Oçf×vx1—:�mÚ«7)/Ò¿u›Ìî!™ÙG_ù{«¶c£ôd¯gUÀ¤ à:2r-·Ñy%t7®/QøÈ•û7.Z­PŠŒ§Þôf�Fõ7±­r¼;lŽ„Ü$nbÛx�¨òœÜnTtUÞüÛ}ý‚>ú©�óé�}Ôbeñæz‘ç¸ÂÿG± 8»‘‹È×û�ÎC8Ù“ Ó–ˆ Î1yä$w<õ^&�™×ŽYúW$ÂÎ�õÁ f¤yñsæì/|?�‹/Ò¡õ†øFLãÀûß„=oÞG23¿oQ=&“¬ú %""Ño©2 �ÇU@ V'幩dJ×Ñò¹Ñb �¹Œ‰ƒLÞ1ÍêËKtovEAwk�öò5fïy„ÖžýDN�ŠP€1K¥«Ç¹ÑÌ�1GêÊÜ�;H{mÖ^xšKþ׬=ÿ4i·C–g¤ÙÄs5Ž~Ç]<ø§áÔ÷>ÄÄþIÔ+ì¤ÎPÂÎOAQ¾_Eí¸TRbÀ"å�'@p”W(Jíüz«ÁÄ¡ fŸ˜·~ží+[Æ #ѨÕiÆ1²y“ÕWž£6·�‰…C¨�FïY%|%Ãt{‘ר)– `—†¢UQ�©£©9¦ŽÝGpŽÕ矦H‡ÔcÇ;ÞùLNNV©øíÐbÜm )�ñ¦²Û¡Â¸ÕÎíÿp‘*Ð�ý^·aG·3-{ëþ»ÿú¿ï­ï&/ò|ØëÿºÂ¿üÿÃY`7¥¶Ç€‰È{£8ŽE{ßøn¿5Ï~öØ=ä¯ó­½Òô8ö±­L×L\úâÇŒÙt/½B:胂¯yš÷æä÷ÞG§¾ˆÔàð±;x𩇩×(º³H«)! „ÒÞ¦�@N(¡LÕ4)AHHeÈ�jšmÄrMl&)C†hxé^ä¥ÿñ²í”A:´Ø©ÇÞÁCå?£µï02Ææ²P•W{l*Æš�2ªÇ�@¨òÅÔŠö×¾öi½q�´×!!c7ñÞ�þù7pï÷žbîØÄP˜ "%%.µÇ%;¤(kZªJ™RÛÙkŒÃÓñxG(ÏÐò§dŠvÝápÈù�žã¥ÿý9ôzAâzƒÍúq­N|ð.öÇfïS0wW"¯VjW06SFû)�ÑÛ4ð{¡š²4ûc4QÌÊË_åÌ¿þ�ˆ½cO#â?ùOÿ î9yO…mToo3RzÜ.B›�ŸzFd÷oçëvûÞ�Û³©ß:=8L‡|ÿþ+>ýñ�Ro6qâÃòÍÅËù0ýsÀøÿ!vçáìÆ%à_¡º'‰ú$!™ÞCmbŠjºÇWzmªyõñ±ÏA±±×¾úi®|üÇ)V®Ø( õŸØËý=lô ÅdÍø$²žQ10� vëH4Þ©ÀDãKp‰G÷K°ÊíZ1Ä$²S¯(( �R†;òiGÜûÝ÷qç½"ì{È3V_üWný-‚Rµs©ÚçûJ]'€�tµ¡QÇ“-%4ö…:¤©¥Ñ.ì'è^x‰^g G£Ö°þ˜¹“{xì/?ÉCæQößžQúKcêÔwÒ…Y Æ>úRØfà-ö>¡%ðf’K*È(ÊŸ€»s*éC¬×êÜó=÷ñæÿò]L=1K7ëZCi§o#�Õ«¬þÛŸeãÅ/£i7Ö›*>rA« Rñb«ÔÆTG›Ë‚9�¸V£61‰oNÑØsŠ–k¿é$ïø'ïæÎwž°†NÊ¡¢;lÏ!ÆN!'7F£¢*ÆË#l„2•æv@„2T e-M0 )ÚX‘(�Û�DBkï3‡gIׇt¯µ±Az€S˜ª%ÆrûÁÑØŸÔG*½±ÒFäF=NEµ>X±õ¡Â°©‚PȉâÎ9ÐÀⳟ#tÛ4 öñÄc�ã#?–Ÿ»M�ÆvßvT@•¥Ü¾^S½#¿M±�ÞæþíSiãâÛÒ,å‡ì‡ùâo|ÎRiªª·Ti+E–ýð1þŽ]ÀÙe8G³áð{œ÷“AEfnÎü½�“4[¸ê·ñQGüHeUqM®\ãÌ­æÄö󟧻ºH–¦ˆB3âοýAö¼ë^¼‹Y;ý½Å xITãþǤ–Ô-SA2ž’6îæÆ&ËKËܸtƒó/ŸcùúW.]2F²½¼Íêåºý!ëkXú¨&ä.#¡F�²9´då¢ RJ°Ë"ùž£{év»l¾¸†¨§ÛÙ¦ènÓ¼ãnšû#`Ãã®ÜrµN·7Ø÷ð›mÖODÎ¥§?Êâ­ý—_cêÈ]Æ|G€’ÉŽå�Ø@±W9ýsÿwõUâ  ÓÔL6}±÷‘ý<ö7žäè;î¤6Q''§'=ÒßYü½][í~^ªúJöbû‚ *�Ï0¤T¡Ùó–Ò$*ÿöRp€Úy±Ž˜¢€í7ÙtyMÁP�ÈE´öO0qb’þF])h$ Ül8„b@¶±DˆûãkuD Aóc«¤$ÇȆ«ÎVrãÎþŠó ˆ÷&´X{é+xYãì“ozj$§KUÚ$·)¥È€ cF™”q»óû㟑ÛÌá»ÆëÕ ò"ççéøÂç>kuHÕèö;�§CQüw@gpvãÿ×pÎÍ©êh4›SYPwèmßÍ�S�[7y ŒÛô°€*�õ¸ôÙ_då+'ßZf8 ÞAÝsàϾ“¿ÿ ¢ZÍs–¿ù Šá&ª�ù¹}<øÐÃD�Pº”,¦ÈÍbåì‹g8ýÒ+œ9ó*W/^æÊÅK¬Þ\feu‰µ­UnÞ¸Á�ËWX¾¹ÄòµE._¹È�ë×¹ye‘�¥uºë]>×t¨Ã@'HØéó±…[=(à¼cbÿ+g–i_Þ4–&é€^¯Ãì}O0ØXæ…õ²õâÓl­.qÇ[?`[í&ßøWÿ)ýמ#u5ö?þ.T±ñÇ�ÅKœÿÔϲ}ãÓî@|LûæU®ÞÚ·üµ ƒ>á9dÓh¶˜?µ�ÇþÁ¹çݧˆ’¨tȈԒƒ�DJAN@w‚”¨=oÖ7ù° Õˆ%§'Ž‚`Œ&—|dDÄRj%xíŒÕ”å€�Õà _�ìIhž`¸9dóò!+°&Õá�X‡D� ro c  ú:©²p›¾IFŒÇG*réÈœ) 7žþ˜½÷Å Ï=÷ÞÃÑ#G ª¼zæU^yõõ:­V Áb\‚|»ŽÿqŸ£qr;åYÐÆŸÇ·¿æ¸“uõç^{��üê‡YÛ\·Ïï`8ÈW®ß¸œçÙÿ<÷AìŽ'Ø� Àf¿Û=좘¥—¿ÎÉ÷ÿ ª 5­Z÷ëèCïPåâW>Í�/~”lí:ývˆ…‰G�ràƒ�ã&<„t«CÚ_ÆM8*{÷í³´UQþ¨*1 ýn�Ó/œæÂ™³lw·ÍVE#55YÈHNÍ¥_üß!´—®sì©÷qís¿Âö³Ÿ£A „`¿g®�ú½Mÿ§oâècw¢Þ_;µ(c;¥*ÍŽ E ·Ù£欭¬Ñ.¶A…<ÊLƬ¹Òð jE�ÈyóÓš™›ƒ&I-A[Š/ûyv‹Êèw(S�Õ4¥ýžˆà{Z@þ$¤ý”µ/-Óï÷ì:­aN¾z•í¯þš´xËqµ&±‡ñùe®2¶A*Š´q,Fö7±¹˜O¼“Éc÷оø*ÅD“³¯½Æ›Þð$këküâ/þ×®_³1ëçoý]¢ÊEÇȸÛêíR]UeZ…ÆÈí|Ó\õãÔnÄʎYª×)ciy™Ÿø™ŸÔsçÏRä¹4›-ò,ß¡ø"ðI¾Mb—áìF߉{p—÷>Ά)Gßù½fí2Þ¯çd)·îÚM^ý…Ñ>ÿ½�UÌ-r¸ýSÜñW¿ƒé‡ŽÓ„Ík¯Ò߸„äç„÷ÝËüÂüŽ‚L€Õ›+<óÅopö¥Wiol[jάgб- ±àj&0€HpM�B^ ^Ñ$Xšaذ½ºÅÆâ:Ù gjjš(‰w$×O�òwPP.q¬½²ÊàzŸ<Ë(†]¶N?‡_¿äÆ4xËw‘LÎpá–+€»y´ÀG5fî’É…;¬ùõâg~™xs‘ZìÙ8÷ƒ­ :¯|ͳ»�6A•n¿Cm¶Á›þýwpì­wR‹kUóŽH¢ Œz’‚XùåK¹vî ×/\ãúù«¬.®ÐîµmÁïnwèwz¤ƒ”Þ°Ëöú&íÍm666¸rö"ׯ\eseƒµÅ¶7Y]Z`ÊO3M@Ù, ì(Ë~»¯åOiEDco‹úBƒÍÅ †‹›Â9è÷iomZƒfÈsüì›g㽯 *,Wíeª4W6ÆX‘Ý*d¡lNŽœ~ûêkh­EH‡öÞêSŸàÕW_¡Óï±µ¾Î=÷�âØÑc0Þ¤9N­ä65”Û¥ÚÆ9ä·Á„¨ÄmRz·I¿mooóã·ÜÞ_záyɲL¼÷DQÜÛ\YýÊÿ\ÜœÝøvÎ6‡ê»ã$‰³~Wö<þ.sO.*_üBuf?e¾=ϹøùÃ�§?Êpíyšâ¼§>5ÉŸäÐ÷¼ —ÔvºÛ·/œc¸tƒ¢—á5Âìãç&K¹rεK×øÊgŸféê KËQN U€⹺C¼ƒÔ)ÔU Í•Ð-ÐLq…CTLšeCÖ×ÖØêl�Sj“52Ÿ›5NA‚©Û&äÎ~úeŠÔÒCô;›¨("b ¢±÷øÖŒ¹9¯}áWØ?7ƒ8(²i2É܉°ár·D>í"€+rº×Ιƒ@¿×µÔG’$4f[Üÿã¾ï:ec€lÜÎO&)Cz�¾�ÅùÓç¸tþËW–ØZÞ´ÚSš¦àbi°ÔV1È)z6Ç;›èš)Ù0³¦Éa·Ï0²½¹Íæúk×רØZcýÖ}öûÔ¤FL Ò¼HWY�YéxHö%Ôfjl^Ú¤w³cÀ—¥)Ã~lÑÙXcbÿÄ3ûÈUÐ1p ÊrʨŒü.ÁåvNü8ç¬v“n,Sä)7—–xñùçØ*½^Ûܤg§¦xÇ;ÞÅÂÞ}c–çܦ®S}|›æO�Û#Çxs©Œ]bü˜q°ÑñŽõÛüò¿ùˆ~ýë_#„ *PdYX^ºù Eø±Ù¹=¿Æ·YìÎ.àÄÀûã$™TüÜ}+^Wé%ÉÇ$ÿÛ—^áÕŸý_­¹³HSœsHÍSèNü¥PÛ?["–2l±úê3äý P¥1ÑäÄãw›uJ¡9WÎ_áËŸû"ËkdÒˆF¯…¤æû� €ª=Ö�:9:´´›='^#cWçÂ×/Ò¿ÑÁ!Ä>¶k‰’8¢Ùš sé íW¿ÁlÍá$°ph”•ÓÏsù¹/±øùSlà�³4–Ç1ìvöûˆˆ©§¦gg8ñÁ{yäoâ`jfš÷�´ÚÁÕËWøê¯‘n»ƒ†€D ‰CSC9\ÓãZ‰-eLªØó:(Óm¡\4œ x ×pøºÕ�@`aa?o{ã[9|ì0­V“¿À~¹‹I™ä [ ¸ÀŠ®4XÓePÇÊWn jIbMª‡ŽÞÁûþè÷pãòUû¶ž: ‡=>øgþ ßó'~?ßøê7X»~�ÞÍËì™h0?7ÃÝ=„1­î€H|)ñUš­ÓÏòØ?|’©…iª5¥P6ªöµÇòâg_:͵óWY[[&ÓŒ0ÌÑ�(.ñ�a×(\NÑ3�±š™‹<ôÔÀÒMz|áðCo ¢@¡VCÃú|jÖŸD:L-×Ïú¬­¯2ì¦4'›6ApPŠ.MFé?±f\â8aòеV�ö™MÒí!S­)íC ÛZ%ˆ·ZK”ÔPFý]¯ç�Iµi´r_õõÛ[¢Æý•ëäí-«³ÍÞû*žÆÞCfçf8õȃ,/¯ÐÙØ=ù8O¾ë]\8}–•k7°”å`¸¦XÝfïCû@ &&#'5ÃÍ‚NÚáü¹×¸xæ<7¯Ñt'˜Ä;{DÁŒW] (D êH&\äìX¤.æœì"� S¦�Ë…Ð 8ï tBZ€BŠº@áƒpw¥ÍæÚ&{'iÕšDÄDx(£t1õMÕL½Fg¹Cçb›´7´šš§¹©ãé9&Ž Îï8ZdP�êÄÕñµY€Ø�й‚øˆÚô<�½û™àI¼á;Ñ¢ vž}5w pîäÈG*3ÖksÛžyå™Ü~äA§Æ©Š–¯ƒšªc]×£M¾yæýð¯ýªžyñéuÚ–âMÓ4_[_ýÊÒõk?Õœœü ß¾± 8»,ǯjï÷Q4wkóY¿ÏÂãï$®7pㆷEÆùû‹ÜüòǑဢ(ðõ˜úý9öÞÍÄ�}ä®O�ŒXkdÒgûÒ9ú+×ÐÌú8˜��evï_ùôÓln¬‘u2ÈÁØK?`ÌeP‚Kê�A™Æ b )©âÄÛõ4€8@ñbL$ŽÄ;Bl1~û[ßÁ�ÇïÄá [´} �†LÈ &Sn¸‚�°ÆÄäË/.‘-ɳÜFOLOòè›çÎwòöïx;ïûà{¹çþ{ðQÓÓÓ<ù¶'yã;Ÿâ]ßõNÞw7_úÔ¯óòן³kh(,½„‡Ãßy'�ýñ7 uÝ‘'; $ÍŸìÂËçY¼z�öÖ–)ТÙ³äX½*jÄD±±´��† ñB‘PÇÀÚ¥—‰1–0HlÇ i I£†ÆÆvPQ{^ ʼnào×Êó‚¬HÙîl3;3ÇD³E®E%ý'6RþQc¦­ƒô®ö`]q*8'x1&èHö¥13oû¥2)ÖUˆ€2zœ t-€Ñ�4‡édbŠÙCǘ8xÜÆn,¿ðeúË×™J78y×q:„ŒQ£×ƒqÛšªïÓ8ûg4U óx³¦mc�¿J5ŠøÚóφOî3éêÒr<ìö­˜¦©nn¬¿ÜÝØú�ÅÏù.àìÆ·mÔÍ--ŠÙ(ŠÞØl5jéÖºôóÀÂObÑ*ó÷Û‹—9óKÿ’þõó–®@ÀM7Ùÿ]oäÀw>Fá3 ¶©ætn\¡¿¸ƒf÷̳q}ƒ«¯]"ïgØB‰Cƒ Np^ìV"A Cµû8J‰t@Pì1N°:’õB0°'LÍLñèerzÒŠà۰¦E{+´çIʬÌ!DxæÝ~ztèùžI~¯<}ÑÀ3h`kcƒS�?Ä�;Úb'1ÕUÊöÏÍÏ19=ÅW>÷%>ûÑO³xýÛ[  ÎÜ;ÇSÿèLž6°J;µZÃKÏ¿Àµ+Wi¯mP¨àÅ~K™åÁØ\î ¼F® ‘ßiøt±¹¨yž!à\YçÉ"1F¡(vŸBËq Œ¹ya~z/÷¿—ã³wòxó æÝ<‘ÄÔ¥N‡6Æh%5–Š£6Ý šˆY}n‰¬›‘e¶¡!·~o0dòÈÝøZsÄbÊMÇ”hTzqb¡Œj#i¹Ù1BäÀÔqg¾Iº~“½2àá‡fÿ¾ý·wsO‰‰Üf�Ûë8uVŸº½½My{ûTܸOZPå…3¯†Oüú§eñʵx{mÕú·jµ:­‰‰åµ••giúýÀv#b7¾�#¿�¬5»ÐnÞøüG8öÖßÇÞPuù]zéëô—®šQgžg¶xÖ�ïeáÝ�¢”ј˜¹ ¬GFó  ˆ·šJ{Ø!í(È!ÔaŠ*±4¶"»ŸƒÁÎ�@¼ ‚ uì±K€7b g…tìØH1–CdÏû("v1ª„zÁ:k\áǹ�'¦Æ^¿ŸKîóîajÿ4ýË]Bîè¬o±µ±ªcýã ´·Ú<ûõg¹vñ²¥ñæ‘æC««œú3�0ï<™¤#wl�YÙXæÅ—Ÿgqñý^<Ø�J€>øÂ“““;›M‘��IïüH4Ñ Há †«xgnê5ç<â æÆTQ�B0F¡öœKaï¡ýÿ•§Óû韸úâ׿>ïEêÞGf`[Ÿh¥‹K7¿z lþ}à*»± 8ÿF˜š˜z¶ßíΩwæçg÷un\¢Ýî0uÇ n>ÿ%–¿òIòÞ6"Ižï}Œ…÷< Nª¶(8�SG{ñyo Í ònN>0°;OÁØN"Xãf§° ¦@"[TÑ ¶áÀEæ4 "#³-cR^�Rg«¹š2îèÝǘ™›Á«ƒ n 4`f�2¤áÄÔ¸¦W1ù±k¢|õ×^¤½²�€}àyê1fçç`µwŠ;ÿèÝÜñŽ£æ““0””­t‹ÓϽÂÍK‹�–i”`Ž ¨¼©ÒL,ÝiÄ4O´"G 10ôI„óbçf½ÔÒe”ïÖê‚3°±:XÍÀ-8Á7"¶�7¿ù->|qx"²3A´G‡yöP§Ž 4h2çæ¹.ðäähõfƒÁùý•¾±®�¡`~j‚áv›Úþ#4f÷€Õi©^FJµ¼T¢Áë°›Šª¹*@¨;á�Æ�™VÃäø"•&ÊnS´¿ xŒ£„TïÜ®þs[7ÏêCK—}æ ŸþôÏÿÌÊâÅKûffg'c6sûöõW7Ö¿týô™¿œe7vSjÿG?ËÒŸØX^>¿½µ¹ÜŠÈ†¯~‰sùAÖ_ü2aбũÑhÑÜ;ÍÔãGÀéNã` à‰ðZög„ÜXMä“‘+@,à«Ã­/ŽÐ VwP `ÀØJ(ŠRA%¶i-0 ¢,‚ËIÄî�‚Ð.»á5ðÜÓÏÒ^ÞÆ5Œ=•æ”w'�Øí Ð-º¼–¿JF�=²‡X…ÆLÃØÒìô¬ÓÖÚëkëÀX ÛíqáìÖ—ÖX]_¥?è±w~¯ S;ôÈXZQzlËíþ6/~ýy.^9Ï è›R ÁRfs û™;¸",}b …¢Ó6&ÛX‚”ÄÕ �$Æê9Ù /ÆÆ|Ë[#fG¦Hs±Ã›à!1öä#g…}ç…Ð ½œý{qâøq"IðĈs  TÖŒŒ\'"Æ—órZ4ycüFòr œ¦XcòñBØîm³ÕÛ¤ÝÞ¢½¹Æ`å Ëg^ ×@Y‡Éèé¸(ï�'� -�( öxV¶Û¶â Ö¢ÙRäQI“•Û(Æ¢U�…Û7mÞVñ¦cçëhßë��@?ò±Ï|zð±O}|ksiy¡E³×³/.ø8Z¼pñüÏß8}æ¯/²»€ó» .äiúW®œ=÷¡›7®?³§å·“«ÏÑ;ýuŠa�"ôú]Ø×`öÑ;�Í(¥° MÚŒßh"²`}âË>šHˆð�CÖËyÀÕü°XjM�ÊÞœ%r…Ö9¯Ø5kŠ1Ÿ¨éÍ8lçÛôŠª%Pe°>\&.LŽ´°|ùF¾Áký³  ì4qÔ§zô�`ÒT•Kg/�gÙØ"¢Æ–®œ»ÈÕ³—ïœÙÅow·h=8Íô�³HÉ «×ܸró�s&K6Ë›=È}O=L¦)I#){�„8²�jí�Ãzkêž¡ ¬V•wrè¨�¾�o}¤1¹øk_‘�P¨¹;inÏS€ „ƒóí½Ï5#P€‚–«{ÑÍË^�Ülr r®áˆ d@™“9"‰0vƒ u˜}jžÚ‘:­‰‰OÌâg}e‰ÁÊU¶_ü2Ý+gìøjïeI¾p•ù9±¥ÖªÎNF¤T&¯Šg¢YÆÇBߦ�Ü^,PÙx€B+ý4·ŸÞYÝ©K«+üâGÿÍàSŸúTwéÊÕù"1@šó©=óí­n÷‹›‹KÿpžÝØœßE±$"ÿãÆââõü³Ïüì•3/=íldYšbaúí÷5š®L± ÉÈZÿ�‘ùkMðhJ9n0%iiÝéíl(`'½ãꂪ¢Å!„L‘Rs¶iM)4G<€Ä;c!I³FÔŒ1 "èwäÛÚ h®ví áEú£L5f‘:ë>þÌǸھ€!9…˜LyÌÙy[äWWV­7 ÝÙ†ýÏ^déÚMcs3ó4-'&¹ãMG(…±�góÆ&ç_:g^f®eé2 :q÷Þû çŸ?C�m[Ôµø�§è$sHŠ¡¥Ýl A­W'  �³Ó4+¦d+,Mg×j(š°Ó‡b)µ¸¢<¾é˜]˜�B±÷Ýa]Îy8&dÁFYs…‹ èâp4hÐr-"û‰‘r˜[ëØ3Oì¡ünb Q�á€Þ�ó´/½d°E©<£RË1 ñàt´OªÃud‰“�ÊðWÂ,…Êë^»}Œ# ¿Õ‰z[›*³‚ñ¸rõ*?òÓ?‘~æÏ–®_�Ûnoú,ËpÎg½n÷¥Ó¯¾òËçÏÿà»± 8¿ £ïêµOtW×þýÕ«×>Úmw–¬/'rÄ Óxç£D¾€àõ%øØH�—dž" ‘)‡ÙÔ4� ¶¢e:,²: Ö{“)E'XŠG ¹ ”i57áñ·6ŸºJÃÊ^a5 l=%l¥É§@ Ï}ýYn®ß$�2»ÀV¾FDÂ^ˆA6äâ¹Kœ;sÖœ«ŽÒffÇsl8XŸÌp8DU�^§ÇÍ«7h·Û 3XœlN°ï�Z§&­s_†½!—Ï\´�+G€…#‡¹ë‘{xé™o²|õ t�Ô@&°-42ç(¢€™pv²’íÄhp‘@A‚�JÐÜXŒ:5ð-&ƒ]CcÅY¨³ý¦¬ƒ›q̘,­Y”BB >JÑ/Fs+PR2“J—Ê4jç.<&á6�­Qg²1ÅÁ7¦±Ð´ «"Îfö¬¯¯1ÜXbåÕo2ìvÈ+™'‘‘DZ2JäF.æPM¿ÙýÂk¡ÁJ^»=FØvK„quÙí]¡Ç]9Ç£jâ ªÊ7_ø–þÀ�ÿððùgžÑÕå¥Év{[ ƒAk{só+i¯ÿƒýµ�ÿ ØU£íÎïêÀPD&#ï“8ŽñILãĵƒS€à4²Ík �ÒÐ1"ÖQˆÍ\Ó˜E°(@u£t�$Îê+µ™â.qH†±¢"!ßaF:4`Á˜OÝ�Rt¤jÏ‘ƒËM©†sVg»¿É3/<ǹÓçY¹¹J¯Û£7ì°‘¯²Osõâu^~å%:kmOßÀzRP@Mœ$ (t:mŠBÑêâ‚Øãöö6¯¾ô2«kD‘±!ûÝößK½ÑÀã.œ>o5Ñr®LpÌÎìã‰7¿•¼[Üzþe @ÛÁ‰ÃX"˜¶€Õg$ ‚½g5GâkÖ#‚�ˆ¶@#X³+¸¾™žB®ˆˆAšb „ ±Ôæ�+‹P(2(™O¼–órÄÛõ\ÙCe ?²Á†�KLƒˆ&ó~Ew¼÷ì¹oÞqÄÕ"sUèöÚô6W.^fíâY{sc9£”` #ItQy\Å _1Õ¨ Äìœ*ø-ÛÑÛ¦Ù”*:Ý|t¼î36g{~sk‹�}ê“úÏÿՇЗ¾õM·½µY‹¢˜Ã‡�^ü«ýíÎo´76>”ùmvc·ñów{D>r¨Nz5 -(€}o„ZsÁÐrZ§+G9[u`g±)ò!¸`¢ˆ)+ˆÌ‰Ù<»ÈÉ R´(©ÀØŽ-¸(ør)€¸"ª ¤¥Z!* Ò×ðHé!¶²¸Äòå%œs6†`ffšWï¼ÊCo|‚›kô6»VLß诛wØÞ}{È�0¹0i °<ä(Jo«Í ? 9ÑÄBÕÔb+7–É{)µ¸f�ûƒ™K‰'oï7¯Ý0ËšA·O!…]³1=ÁcO½ÅfÑŸýÆ+„Aa`ÄtøÄ+*¶ \*dýŒh&Fc@¬§”æ�Á,‚"g q@äBÇäýÜz ©ÈÁKb1 Ë5@¬2E•#N¬WºoØk,ôC�Žo³Ê*¨Q0ätq‡£F‚à(È„ÚD�ÙGæðŸ‰é_肪í'Ï`k…Ιg™;ù…¯á<™‰*õ°ÇÞ�‘Žš@“JŒá™tšw¹œiW ã&"¯CQ¤2NzÜò¦‚4·q©a¬Tƒ�“xáå—øò×¾¢¯¼òJÞïv£ÉÖ¤l‡-Bý¥×ÏôÚퟌ£øç³7e7vç÷Bäy–ˆÈB^äuµF¾„‰{|RÈA*Ÿ^‡¹3¬3ؾI‘fÄ€"¡˜)¦-ºâ±ôj)3 W‹¬N`LÇAJàPÌI ­vù³Ó“baNp!*í[r‡Ê‚r†–5�ö�-ÒÂsòÑ8tâç.¾hÍ¡½^—å­›LìkÙ<‰œ9Z×ku"ïͶ¿Ûé0·gPvvæÅW Y°¿Ï4Ò:9Eý`ƒ"* ºæ�½ÙÙ ˆ 5ÅØ}÷?ÆìÂíõmÎ_~ ™tP²Ÿ¢“3h÷/XÑ\d  ‘"™P´ œpMg2hë3÷…CÁ¤èN¾0fb€eÎA(†ñØ~•›‹7yàÞSx_6§†QJMÌcÍQdq¨©ú:à†\cši<Ž�Ì=:%§4seh¿÷ü£{9ôÔÎÝ|ÕŒY X¯’:l^x™ù¥«4� ò0Ì� [Ï|©Ž@Å‘«öæŒzxN âáïs+Ä¢·éþ¯ ãÿÑ/¢òúnz»ëª�-ÿú³ÏðÙÏü[.]¼È ËäÖ¾ØGÃþ ƒõõå•—PýÏ�÷_ÜõEÛM©ý^Œ{¼÷Çü­p‘ÇÕcj{&R.˜ZC% *;3÷ƒ¤¤[›äƒ6DŠ€9“ªÔó�I¦mfJ>( h|ÃÛ≪=‡`àbCÕR%tC™ª|ñ,{T¤ÜN½AÀd:jBy‚+¢ ùVN1(ØÚ\5õÙ¾=¨ZP`¾_Û‹Û$¡†¢0 íî¶YŠÔë k´ÌÓ¬º ±tc‰s/ŸåÆâ :Ý6Ý[[–gì{ü�C c+›+›lmnšZá D„£Gîåø‰“ZpúåQ …Úk„(�ÌÖð3¾!]A:‚ž¨á§É\Íj0~ÊRx¨50ðíåˆ êÔ¤Ôy¿@Û'Ö{S:äJX1Wn®\¾dCØJ္wÎÎq‰Ì‘ÍÁ”‡ˆ¹\§ ÉH¹Á 6ƒ¥Ø 8kZ³�v’CsO“ÙGæ™<0EkjÒäÊ!ôÚ[´o^£½xe¤8+JKJgh‰«Œ,©Òì~uÍψõsìöz‘�ëëó�rƒ ª�ߢ¯[×@+pãæM~á—~�ŸýéŸà¥—_¤Ýë²ÝÞ¢ÝÞÖ[u›þõ«WÎ,]»öoPýÀçvÁf—áüž‹�f ð¦ zg¿Ûu®‘Ð:v¾QCí''Hfê´Hë¨g[¬Mòv—¢;€@™Xì¼”‘ V¡eo�¤`Ì‹|ÍC†�±”M�eý"�`)( ô`sjÍ�.vÖ—2°y<‚ª¥ïìXñBNJØ£9ÙbæÎ=t/l2ì X½ºL¯Ý5‡èÖì„y°¥ÙçĆŸ!TÖ'eui•õ•U«ñ„,§ÑlZKs¡Ekb‚íî6/_`{{˘Ä9ŽÜœ�Vÿ¹zãŠZß9`ª)qï]ÚÞÚêõ»Ýíl8|&òñ‹Èo=vcp~¯…ªAõEqü‡kImrQZ!™œͼO4¦Úc[ƒ­58ñì„D΀$/2;V¼€`÷ Z9à0åš›�ßjRKäÃŒ^¿¬8 €¬ÔÒgd‚Jä²—'$ÍaÐî¢VpŸœ4‚S¶Ó-Ö¶×H¦Ä �Z§-ûm üPH‡Ë7—ØZß…,äDE{"fïžC½rãÒu:ím¤fµ³ý¿óø)öíÝOAÎÒÕ먨+™`ãÉdФX}J%´$`ö5íòØ ¤Å]íb¨• ¯ä¥K�U;ßFBX[uuX@Œ‰6VÛ+<óÜs¼ùoÂá  O@ À‰3 R”�öùÊÚWØlmƒS»nJAŠ Œñ »C|”PÛ?Iëøj/l2\ëMâ {´/¼Húä{¨ÏÔ ¨Z�U¶æÛãøòxG%ãZ~#�ãn•…$C”Û<w}¯ÏÖOÕë÷Í)àÏ=˥˗øØG~…[ÀbC ëõ:Eò¼?Ü(ÒìtÚ¼–§é'ëõÆ'�mvcp~¯…8kïþKïýÝQ×Ó,“(ò„Úü„�ƒ#"�£`‘KÑDudÅ�EPDÊak‰CÕTh`ŽÆÁŠå$B@‘–°oî�>ôÍFÃ*ŸþüÓ˜j¬Å ûøº·´YQX£)6ª 3Õ´×aÇ3E z=œì¥·¾fu:íæ - 64›0ÆR�«5 ¹~ù*EVجW2‡ýw`öļɣזVèu»ÀG-®sìÄ preåÂ"ÒÆ˜ÀD&€0Lqb@çƒG|äqSÎÀµÈ­~C.’¤ yÙ<;0×lTK ÷£„Ä‚²‡0,ÈÚ"Ž3^…Dxü±GiÖ›h .Tþ+´Ã6Ï]|Žã§N‰1+@ih“t;¥³Ý¡Ý:>B’iÂ݇ȒӦTÓ\��Iž™#ywc?5•¾›º¯ˆÅÆ|0ÅtNÎ�NF›X’˜3Ù³~�šc<3v{ôqÑ€cfyy‰g¿õM®\¹Âù3§¹¹t“Áph”ÔÔ[Ãá0t{Ýn¿Ó]ªÅÉÏx}¤Qoœßš]Àù=[í­DCø»À¿ïœ›BÄyŽ-ØõNñÞ…Ëvj8  ŠÓ•‚‚ŒHjXAz{h2e‰µ‘Ñø–ßé™)‡}•‹ HÅ>Ä#×h’ˆ5ržºÿ>öíß Š5½ç¯¼ð�—ʶÄQh€X1�KÍnßÀÇ×¼1 ÙõlÅwúiŸ””Æl Ô«)µº«¼zÒÍ!išˆõ†=æÌS­-÷»]V—­‰s� ͯ �™£³V[¹±|ƒv¿�E; 9Ìœ£9Û2`ën·Y_\Bûe: IÙG£¦Ò3V)@" À°3Qœ÷öÞ¢{ ec¥Ÿô˜‹@?9¦´ ”ÌжáKÆ“—âŽ`µ.^=óín›7?õ&f&¦í½u8‡ƒEâòú /¾ô2[½ ¦6g˜ß3¿ÓäÚž¼èu ÚÃz9=õ†;¨Ý»�l±MZ ìÿƒëµIúz×/0yä^\ÙÀéJð(ÛÜŽÇ #°ÑJ}ÇŽ)Ï«:|+krWÔe!yª–7£«"�ò¶ßï³µ½E8àÜkçX[_çÙg¿ÁÅ×^³>­z£Eš§4›ælÐëu‹N»�¦ƒAÇ9÷Bøáv¯ó»5š]Àù=N\SCøïù3ι @TÕjµzçÁ “wÄ9c7V·  ÇáQõ¥,:³M£œ¥×Àa›Š(�VƉd8@Æz$ûò`é!ÚÛm[¬OÊYCQ‘»Ì€Yl'Fpê5XØyE¿06x}í ŸüÂûš Üu×qj.A"!›Û,-¯²¼y“Πmµ°v»ÍÂüN älÞè¯Ñׂ�+E6@ÍÅ!!šk¢±€€÷Τ¦}ëc-Pñ;u˜,ŒðÀUÜ#I™n«Ôu*ÒjegtzÏÏ÷x$éò–Ú:5¯ˆP­Ýذ¸­ímkô=ýê«(ðòË/réüyÖ7ÖAÄ|ƒ@o8°ÿ/:죪aeåf’v¶¶»!Ë^Ž|ôcýÕÝ~š]Àù=±ø{Qý€w;‘šˆˆ†@ToØäÊa·[ì½¹ 8õÀJ/µrʤÆxM°Ü}¾AVl€Z~½$(a+XŸ‰Å×*–¾^pIi®YšdᬃŠbÉrŒÅQl ©áê;t'篟3)5`¬&dZºKûæ/ˆ�^Z ìZ3ÂDUÍÊ_ s:f¦9G=jQD�wÎŽQ†ƒa9±2e0°EP°õÕ5òÔ¿‘$5†Ù€�(õ¹ºý-›ë lÚh=2É÷Ñ{Oœ9&³´x"ðS~ÇÅÙK®ˆŠC¤x¼ö·…âœ7¿µ¢UìÔd"õˆs–âsâv Ø:0Ð"N"Ô¥€Úû1È@\ó£¦�câ„öp“í°ÉkWNcû˜1k3ì,P R¶nn G•„Öz«t²6½vŽ£e¯kÿV&•h¡5"Ö(ûˆ¬½i³—æz qs P0b:v[5ùçÊcªä»­ú®Ù9„of-êŽÇ]üæ 6·7Ɇ©Í×¾þUn^¿ŽX^¾I§×¥ÝÞF­Æ(4§¦pˆØôÍ8I´Ûé ºÛÛËNeµ×ïž~)¨~Ø`7vç÷zÔ]tð ÞG÷ D€¨B49Ëoÿnh¯²ôìçqeSeá3@ pr鸈šµ ^Í5˜¡ô(Ò�Ù̈j‹€Õh4€óõ@1ÊÁãÙáÇBÈì>IJ‚W¿yš…=û¨7k´×ÛL´ZœÃ­!ù°ÀœÃ} wPeò¡lO4‘vn¯Ù/zÈ Ëpùš™Å&Í P™BQ•@WDc¾™n$(ÊæPÂèXŠÜîô†]~åòYüåçIÎ?Cž¦Æ|C¯ÇæÖ&[8„t8d0èÛé÷»Ô’:å´U Šc\ä··Ö×2OÓ�&qr.ŠâE »Á.àìF+ª½ø¡¸V;áo…Z š wpߟü{yâ�<ÿ³ÿöm9”u‰…¤ä2C©‡7¶S�"€“%¡\H�Ìnñ-óð²”�KûiRúw@CU©&„¾²ººÌ…ó—yô©‡p™cs}‹™C39v”s¯�% - ⚆�"¸‚½¶Š�m! Æ|›:ª Øk6'Z¶˜3éì,u¬Ô9ª\½p…ýÿs©:*ˆ¼-ªˆ)&0¨-Ôij±´¾lr`“pg‚oFÜqï L}‡c°Ñƒ�âp(.†�— œ�Ææ�}lŸk‰½Ÿ.Læí1¹¶÷R3@CJ#N­+¹+¬ßê‚9 eoMYÓ b@/ S¼!uÁçVk³çˆA €ÇÎ%öaÇbÈÂAîrsº.(ˆ‰Èû¹¥%ÃòÚ�à‹�!}õ©)üln¶-�–¦©É£½ÃnÇÆCSIF2çP²a\lc\Xde?�+Rëo ö¥¨³t�îå³ìé.“L5‰2ÏæÚk›«lnn`=WYf€"/Ḻgv/y‘[=orbŠVkÂÀ;„¢µ°ÿàÛ†ÝÞ׵߄l»Á.àìÆDT{7ð¯¢8>êœóqR#ø˜cüsœú®?Esá(Ew ñ ö­×�N4ð>±Å"PàˆK³Î Q‡°…‹—:t „´ì³)ÙŠ4œ™o¢Š3`PpÀ°,ú+£QÓš©m§_~…£‡ï ‘Ô±’§fÍbÅv;§´Òƒ ³À¹¥ÌZ‘È1=?gF’ eñ¼T‚©³[lõ­nã}´Ów³¶¸Œ8!‰<ær`)7‰ÅgÌ#è.µ­>ã"HЬM°ïàWPÝ�õ°HÉüHtôÕ½œSãJ§íˆ@£rhLfn8u01)—¥80`õ±‡H ð‚Ç@ÜÞ{"Bd�·¨/g Ù‰àÊT—³]bàJ��ëFØ15T°c‚*ƒt@­Q7–Õíwd‚z4 8bDêþP�èð$ÅK‹„ÂêcHÈÉzmòö:‘܉][¡tÔ!ŒÞžÒÚÆ¶|F[ÐjÏNy+%'„€‹âZI²~�µ®@·CÞ�Ó×iNSd)õ;ö3ho {8 â"'¡‰wbkÈs Q³Ñzh0è}šeÿ¸H³�¦éðGÕÉ `7~¯ÎnÌLÍJÑë}—sîC>ŠßÚŒÙdê8ò¾?Îßû˜Ü„¬P4ª˜Ú¦&cf˜uH˜0`qxÀÈAR¼&¶?Š›hžášn§OD’rnM (ÒôhQ Áe•¡&‚ÔËœaiyÓt–ºúü¾È»ßû.jÍ„¥åe®_½9¨( ! rL.¬@0ÂÏXA±:†¹^Ï&´Lµ"5ñŠ(ä¹¥yhаJžg4êM $õ6�F„~¯�GNF:2,†Ü1Üún¯Cˆ,—C(”= ûm  ;èXºÆÆJ‹R¤ÁþV ìuœ:c4…¨ÿ5R¨cƒäB¢ÒêFp8›W£®”~K™~ÃÒd¸Ü‘÷2j¼ÄÀí*aÑTŒ h B7‡¢"/XÏM=@À@WÄ!¹B…QpB&©¹j·-²45éz^Øï„øÈR³ :â„"‚,$x“�·êMôÈ2†Rà®â—V2tlŠ@µAT¤<Îj^ $$¨‚ì?QDûÊ4ö2\_2v5õÔ}LDZ}œ÷êšeô–®Ò¹q‘´×¥wõ,[ç_¢·±D«Ñ0f *­ÉIZS÷E~|0ü™Õ¥›ßþñäôÌ °¿g7fçæ%ßîü‘(ŽÿÇF«u0Ï2§ª×9ö‡ÿ�ÝJ£ÅI 'BÖÙ¢{óŠ¥â8B ¡è QÊo©*RNû,ˆ4Aðv¿Ð!�‡Ær°—7cIé¤f€%‹±ET" ll €‚°½µÎ—?ÿöìÝK»³�¥»bGÞÎ ¥=¦†cgz¨uÞ'2Jï ¹c²9Cì@­9±ŸwÌÆÆ&fzÏp35c…á8¦ÇVüÏEémuØÚÞ4™o'xõÖ©ßvYé¬ÐéuP[lÕÏäÂ,Æ.PT•Íí5»Ob :xE¼³÷É'�ÀÅÎÎ�âÈ"‹r$R4ƒ``ìÀ Æ.$@ªøÉ_‹°÷,)À=”£¦ÅarižR°A Nvl‘RöU „2½‡Ý·óŠ<”CeJ45ï5"µ1Øl÷·I5Ej‚–ÂDÄ#8âÉ&µÃ3ø‰:¡(L˜²aÐEÅ¡ %y… Øv[U¬IE±ædt¼n§WGð.²ÇHDóÐ &5 µã½wx ¼–�ÌìÝ#bJA+×X¿t–_û½ó/St7è´·8¸ÿ I’¸N»Ýʲ¬5;¿w>OÓ_öûvÎýhE×Ë¢wg7n�Íý"òÏ&¦§Dp´æ8ðŽïá¡?ð—ˆ’ÚÎ5yE8´rG7ñ$ ÆpÊY8¡\D)ÁGu+ЇLpuPçË•(gÞè°”B;lŸ‹Êc¼í+:-åΊ-ž‰C,®Ý`qåÊ”]:ʵˆ/PPvRe°tž%›Ê”zÒ”j8»~£k ±ÏôÔ a­àÚ—.�$‰Ð÷ýµ?Æ�=D£ÑàÌ«gøþÿæcõÒ5êõ†åó“$¦>Ý Ÿölñ@DÜACšåŒÔ�7 žÜ¦~˜Ë@Z‚+çJ¸:˜d9xj 1H0 ÁåjeÍ “KÀK=ZUÊXƒAD•h26ÖG¦dý(AÛ‹o�£™B!8/ à‚9LƒIAQŒ¹àqBØ7–ÓˆûfI" Éð[=gâ�}lM6)]Š""¬vèd$,ª}6"UÁè±<‡=®¯ ‚QÃh°ƒ¡ìCFv€¦ê×&¥¢Ä3yøS‡Ž³ï‘·Ó»y‰õÓÏpíKŸ`ñÒËV+B•‰‰If¦ãzš¥';íí¿¹µ¹ñ³4ýDÅê û~W2ž]ÀÙ�ɨv¯ˆü`c¢u�jˆC~z/÷þéÌÉ÷üˆ”2›¥Ømcv/­ƒÇèž}Õ”bë/Ÿ§vÿ ÞÅDZ@ð¸p21¸VV{h " ;  L<£Oxj¥+t(ë‘BA\¥ù;®+I9'@èƒ8+|ã’\r¥TfÙ}±U’V�郳¨€Cp΃3&‚Sg}5t…ÁfŸÁ°OoØ7�ƒ�>È©‡ï…È{NÞ{7Ý• èA6`ØíÓ:2‰sQ¹¢ ¨S3Ù48ÐÔØÝ›qHišY*Ñ„v°â{½^Ž_H)ÇP”©±žhè¤î€Œd©¹WÛ5‰ �¬Ð�î¯Sâ¹Ä”ˆóf Tвã’¨ã²t£ 0&K7ÁB",CMICf=Hé %”h!bVA•Y9I½‰&b*@qbi·*%›*YŒ/ûmò ”QQ³iEÑ&PÇh•çF÷©€ €ŽnÄ 1*'ŽZk‚ú]0wç½zò½¬½ö"—?ÿ«ô^ùª=ßï÷H³ÔÕëõ©ÚÂ�ÆööÖŸí¶·¿Û«þ2ð‘BÃK¿›vglî‘«7›ÄqÜ@Aã:÷ýéÄ=ïý#®Ìg TC´@Pp^¡×Mæmèš'•.N#@hùL.]øù2R�3Ñ€�€ošÄ«­€¥Ï, ÂÒmHÈNOŽkyp€‚:�:ˆ*`âì<) çd ªŒ•-ë‚õçxÏÞ…ƒìÝ»ŸÒ Ì:þûƒ.!²tš9Ü8}�¥åE“óÆÞ3d?û,€*µZž={Ø¿p€Õ�&6³gŽÖ|“A‘ÚcaAˆM€aµ%@¤˜Ý?š` ¹n�Õa\��Ž\jõRI1EŸsR>ò¨+‚—5/Wó–Þ»ëè ¶×¶¹zýru\¦�CYO ƒÒõ!TE¡ñ'B Ø”‚ tÆ4p$³¿ "p©€``�‡AÐËzhÎj^ "OL<Óbrïz³Ož­ÆÕ¬%Æîpåq\e •4YuÀZu_Õƒ­šRsZ•ç`ÔÃc× Õq7ëœ ² ÆD™žÛ{�Ö¾ƒìôml]¿Ä¹�üí/Œ´ß¶/ÍF3ž™™�©ÕjíöößZTŸç~>„ðIDVØ�gglâz Õ?®ÕîwÞ7CP˜˜åÄŸøÏC¢˜DF)‡¢òóÞãk +֣Ì|¹C!4Š’KŠ+'Y‚àˆÌ)Úd·~—¬‚¢Y'eZL0 ÈÔX^ì9P4\¢;Í 9l*¨ý¢Š­Œ"/g…Ýb×(Ú/°×ªx HÙdª©¥kˆë5f&çi5&ÊÅ_Y_^ÇÌ<�µb’阫ŸºÆdkŠF­ij¤½ {i¶šPF”$VðÏóÜRjΉ¥ÞúÝÝVŸB @ à’©…ωˆ (}íÑ雡'ö÷Õ\9Ý3 …±žRêlÌ? 3í´÷ÍÇžÄ'DQ@½Í ²c%…Sw�âØÉcdƒŒà‹Ë7(¶3ŠBˆ¦ÄC^xñ&SyYë‰"ÔÙõÈzYùþApâÑ<8‰Šý$`÷�•²ê`ª={¾·\6 Çu ÞÃ-Ü•_4ˆ&›ä±Úß“gX .wIk 1”¨NõB|ÅÐ�J5ÅI%½F @€Êè~P°,o…õÅ¢B…1IeòhìF©7€¸Þdîø)üKÿ)7ßø^®|þ#l~é£ ´šMZ­‰¨ÙlE�n§Ñï÷Þ5ôŸäÏ%IíGn�Ò'�5vãß%ÀÙ�©¤kË9ÿ½Î¹f‘„ÈqêOÞb6øSˆ�Fö c…U€àbê3{ÁG>â=½W)Öû$�)‚€à@Ëú�5�z„ˆ$šbjვ+#UZlB«‘H ”5ÍL’+%Í@å·y9M{ ¼T@ƒ–×l36]Ô�8 qØuûÁX�ÍÅ) "‰Ù³°`V6`é!.œ9�xSvQ«×\pãs—ˆ£ØRjQqäèQbíäXz펥Œ¦ZS¤C" 4ãÌL0p¨;ë瘟ÛG=nd6Ü­îl¬6ñУ)hŒ9ˆw �PèΪê3¿c²éñP@?I£Á+!²^)œD¦–‹\ÄÉ“'YÛZ!ŸpøØúE Ð$ˆõ)¹ØãLÖG\Á(ŠE„�çš\p–² I@Íç­L[ZýÎÑjµè­vn pµÈ@žBðI“Õ»áŽèÄEãÈãˆz³ yH&g�j‘£šVs ¥JÍX˜‚¥Í춪l €TÕlFìÉѵ‘ѱT>¡¹ÄQ‰ì¤<� ðÔ'g8þÖpì ïäú»ÿ׿þ–>úÃdE(‘÷nrr²9?¿·áœ›ionÞŠâO†~4„âóIR[f7¾Ýg7fgç%owþ€ˆüͤ^kEq,Ç¿ÿ¯r÷wþjqDB€ªß”VlÝ%Ž™>v/DI™š*lÆMº¹MíÐŒI �±&P·Óc1y쫯LRä¸zY×W΢¡ªV LAƨ¯Ã¾í7Jo0À˜I!�C `j4K#Õ-Ýf×,Ö peº§H™ŠRØ9ă„G�²päÐ΂zËûŠÍµ´ìÇ™�Ÿ§w³ËöÕ ò47 ©MÔ9~×b$ÍQçHÄQ¯×¸~óšÉ¤UÕÍÆGcî8‡½WÞE¤ ‰ˆ±Å¸p�´H‘ŒQ -²Ò1»4…È@gä~]äh¢6BÁ˜`ÞÉɇ)ÏS›�n˜ž™A Ã`é1M� èhƒ…®@0ÇþRp*;ͱj#Á ¬Þ”{P10VÁ®1 i°´~s,Í‘"ÄÔ§çQ ¸PCTì~ºÝ¡¦N#ö!2Aokƒæ!E9ÀˆU„JÏMµ.SMŸÅ2 ¸ŠÙªRna皣´�T_Ç¢“êçcÄ®ð•×ñ%rRÁZ“CO¾�ƒ�½ƒ�÷üa.}ñcl~î—ÌM!Ësúý ñÞ×’Z²°¹¹ñyš¾AUÏõ½ŸjÖ›Ÿ«ÕjgT ìÆ·#àìFÞé6�¿–Ôj{ã$qA•ú‰G8õ}…¸9Å(E=êgðR¹†ˆY‹Ä·¶¬½jEñÞʽÕ-&4€Pšu(N#)�<Õ—Öþãl]zΘ‹¥[Êô–@9‡’Á¢Xè ``¤2c>xoMƒ&WÆc× U�¤r% �Dܨր(ÅÐÒTÆjNNð"‚@5µeûÓ¢¬Áp‚í—Šdº†S©ÁPI·QI›¹JMF*鸢<Ç—LÊW¤×v,Ànk4iQ*Œ>WEã §ž`ßÉGXyê½¼ö™_fåóFÄÔ†hÜôôL ˆ‹+²üž,¬mmoÿj³Ñø‘Þ°wqª5=`7¾]FLïÆÜìAõø(¾·ÞlÖlnÊÄ�ýÅŸé½û©¦!œ`t”Ã΃=oÑš_ µÿq½AR«:ú—ÖÑ\QÊ@û‰ÃÎàÏž+Ý � L-Ì2±0‰Ã‘PckuƒKgÏó�†cª9M+jÒ>»E–eÖÓlµxèÉG˜™›ÁB1Æ”†ˆˆm ó x|ꬦj�?ðÄyLok2ì5c’�ô“±÷ŸX …\3\Í•u™`i:úBéÖ` ¹L9»�½ …¹D£¨1 AwȠݧ½Öfb²…� L‰¦"$qø–·cÑÒ™ºVJ ã`à”jŠà0f)êÊ^©L� V¨Z²œEdrñîv—"ÍKps�{|«Uæ£JÆg/ªÆò|ðXÇ>¡ô¼ká'穦z½­Œ”†`'ir=;ˆdäÅf!Pñˆ¥å%({gmóåm (£Z�/ÁŒÑgJo¯9Cç ú7à#ö=ôoøkÿ9où>ÌÁ÷ÿ)šs¨%us[ˆ¢Èµš­Ú-v:%ɱ8‰ÿz–çnÖZ?Q¯5¾§ÙhͰ߀³Y»½üù¤–Ì+*¡,¼ã{9pê‰QºLF œzTe8£ª=7³‡æÁ;QŸ`ç¤9›ß2b@Âhx[IÕœÛg`åóˆ9A?u?§”fÒBT¸úÚ^záYRé#âHBÂÝGOröÇ_fóÂ:AƒÛ#O=Á;ßûN’8u:G¡ðò /sóÒu3úÌ‹ÅM:â{ëÁÈCn5ELî›Å B±cg++7ÈzC{Ê^—Y­Ë ó 6ñS\.XÚÌEP:À¨£¨±ߌÌb}s�ÕÍU†ah©6‡ƒ!Pì°SÁáÕï8x¼=—÷órx›GƒÕ€ Æ6E)²ÂγýŠ¥÷\`us™C|âmx�ÔšLì?†¯ÕP ÙZ‡­Ï^ Ö"ux«wì{èM–Zï§©²‘‘bLÇ ý~L9fÇãJ³B� R;¶`^ Ê‚Šê±Œ@LÇÓsŒÒy£ëU@ÍGÄÓ{™¿û!ö=þ‰ê z[+´W—pNAœ÷.Š¢d˜¥-ñ~oŠ·„¢ø¾(ŠÞ^„ÂkЛ!r}�ìÎnü5q§DäïLÎΞˆâ(ÊÕq÷ÿ)�~ó{ñNp•f5P¦+•—ˆPkN°ôò7è^»€†‚¢ŸBä˜ó½„(ÇተTŽÈ+ŠÍÊ‘W‹çé­_G³¬ðl 3È1áŠþKçá噫yL"LV¦áJß5‰*¿4åÊQJs-”²ÁQˆ|lB�GÞü&¦§f‰IØ\[ç™ßø˦RCàø¾dW‡¼ôßbØXïÈÔì èÏþŽß}'h™!ö¶/^¹Á…WÏÛÜ›N¿ƒaêÀ sOìae}Ž–;K—ÍÌϳw~† Ù×<ÈêÚ2k+0•YðP`Œƒ ¬_©4Üt‘+½Ð@�ÙÚXw¾¬J XÞá onÜæ^@R�;F¨ä–˜!ªSÕYQöºêPÕÕ  CcY�` P«78|ü®]¸JÖÍ(:9f”°ç “Ž¡BežR�àÈ;)«Ÿ8Ípi 牯3}ß™¾çq\YËqåª@3E(ÓX%Д ¤±Gª_¦ª·¶éèß{Q™ÊØñ#+� ;ªÔ@ݲŒž3P¡ÂЀñ/{Õ͉�LÍ1wêIYxì2ê ¢YFw}E:[€±Si5[¾v+â¤6彿3ÍÒ÷ýïŒÄ½3ŽâÁ­ÇÃB‹ÞôܼîÎnü¿3I#Bõ¯6&&Þ6=7·/ 2õð;xä�ým+ü—@3 )Ýd¬ÿFFû]Tc°¾ÈÆ™o¡éç½ Û÷·©ONCÙ�£l«^Jv1=e‹Zi‘ÐÍÐÒ¤Rjå'5U4¥,z;\ÍÀÍ+© <ª ùXvܥŠö#Øu,]G:rœÇßòVf÷ìÁ‰£³ÙáÙ¯<Íâ�K„È:æY˜ÞÏþæ~Îýô« ®öh6Zæ!÷ξ‡þ¡š"Ș§Jo«Ë Ïr‰~»�wΚ?øÇ¿�§Þùfp¡ÒüQªÔÄŠÅ/?ÿ27¯ÞÄ”Fi:dòø4�;ZpEJK¡ÕœfrïÊ`†µ¥›6íÒùÒžF„2…hàic£ƒ ¢H\oÊP¸€A¦Ö°ªư|îÀƒ/SošSB²‘@ŠR(àÀ@E?é1€ñ«—ÅšF�9ö<ÌÆµ(Ô~7uB}þ õƒ [ƒ…¢’²”­o\¡ýÅËôÛ¡ž$,¼ó0sâa‡Œ5`æªMžU§�2< Ê,`PòBqåÿ6¯çT”…Tªmݯž‹B•½TÕŸRQÄ Œ5³Ž@¯ \0J!f�Ò7²Vƒ¿™oz³¿•äÐq6o\¡èul")@–§DQ$µZ=Ž“Z£519_Kj'�woá͉ïkÔ2‰kofÃ?scùÆ›nõ‘=qméÚ��öæéß|vg7æ-'ªµ99ñ˜(õ"ªsßý[ì¿ï1!T¾‰e¡¤ð‚…+÷UóÓ  £òD Qc‚틯Ð_¾†õ t¸™&Ó�#øÜ@&“*‡Cp€-²v_CNûòYŠÞZX‰©Â¬vSʃ¥°T¹²A±@+ž&’` né0`@ yi£Ž‰æ,÷<ô0oyç{¸ûþSD�È ÷×/_á«ÿö³¬nÞ€:ø¦·Þ�ãû�#7àü�Ÿ¡½´ ª6DíMïyìÏüjÍ8AT‘®(PQJ±‚çÌ‹§Y»±j,AQûÐ×7™{tËKöÞx+ˆ†Y�…#‡©GuÁ€­×ï±¹¶j®* `yñø‰1ˆ[£«]G©ð+1¥Ù„M6-›Þ©åÔ$@‘CDdòm  v|ÔlÎ�:KÏåE†‡”ÀÄÒrÔ6QÁ@+xâfLm²ÁöÒ†¥Ú¬ÿFÓ÷Ü�¯ÕK1BB /¿„däƒ>íÏ\¤÷â²¥GÒœdá-ßÅô±{@\ãG •"ãÍŸ#ð‘븒Ùäö<¸ò:€É—¥R©‚�í—jí§RÛK“Éë<öntnÜʇ@ÕŸmŒÉ•ç#B”Ô™Ø{€½÷>ÆÞÇÞÅÄÝ2 JwùºIóëõ&IÛ¿ãÈGDZwÎG>Šj͉[1=½'IjGãzí¾Õµ¥:ƒÞÛº�íGƒÞç�eàö± 8»±bv?Ÿo´Zwáœ�ÜÉÃüïà›“cQ©¸ãÊø7½j´LÍ™9òlÈæÙçq!Ç!tW¨Ý9KrÇÁå¸H� ÄÔ¼å닼Goé:Eg›(¸r~JÀäÐ9–sâc*©¥ËvÔWq¥!¢«OÔëý¨Or`ß1zâ�<òÎ7q×½÷07¹¥³ÙæÜK¯ðÜ¿Hg{P*9îºûnj[5^ù�pëØâÞjN˜Iç_û‡�…CûA°�9tGQˆà͸Ï7¿ú ý^Ï„E©J;ðÖƒlõ¶ÍÀÒYåP´Fc‚ɹéR\a ÃúòŠê‹Ìt¨(N lœseƒ¬3‚+‘XúÑÀܤ`ý9bòhRE )<¶) ÞÇÊÔ…Âö2òÌÔš7wÆ~`©‚‚Š#ˆ29;K6R9>öVЦæ˜8v G„ "#—h§1a;°ý© ¤¯m‰3ß:f÷sèmßMcÏÁ’‘URV Õ4šŒÔh#rå8 *:pˆÝ-œC¤Ä'`ì\)÷…0e”Š®‚@«“FÇÙKõËSâ@+`UFUð€V[˜lQkŠé#wsôÍïåÀ“ßIóäãh”@—È‹`¾y LNNÙ¬žz½îQmÔ|âZëôÚþÚ�+yžg.·�]ÀÙ�¨(žçÿXs²µ7É‘ß÷§8ô†ï‘`©º TÝtEl³ýT@HJ:¯Øf’Õæž6¯0¸qŠzíËËL=~Ä:É=I 8Áj;> �Áö¥ d[køXX8p€½{ö±½ºiÀà¤ô=SÀCTO˜�Y`~a?“ó³ÄI‚(¶¿¹g‚éÙ=,ì9©Gç�7>α‡O0q`Êç\êX^\äÌ·^æë_øW.�%+† Ø‚ߌ›ô<ýó]ÌÓG´¦'ùSãÏñÈAĘ ®ÌãˆÝŒ$~Æ‚^øÖK¬Ý\fskŠíœ}o;`M¦kkk¶˜[7¹*½N‡¹Cû V¨%5´€í� Òþ¡Q`÷dGIT1/4QVß±´– ( ”⋬4Ñ”¨1HŒ býOe ΫmÞy `œÕz¬á¶ØëaãÁ3c4¥3„ Š”‹<�©&Ã~ߘ&A(L¿‡db!B¥ (™�J@p έ±ù©3ô¯®ÓJ³Œú]qòƒ_o¡®RÈWÆj'P5õ´�ñy7å9€‘Ūj F)¶ªË€E…9eÅH¯2BŇ­†çê¼¼U* G%*¥B©øÃUÿv_ù»`$4hÎícêè½ì}ü]L=üv’ߎßsnf�¨5K­ÖÐH³5-ЍHs/â˜hMp èÛýáàéå•?twg7n ÍIÑ¢øûQ¿Iœkfq“‡ÿÌ?fbϾ&®Ò5­Œhí±…V¶È�¬=PoM2sônÖ®^ ]¹ŽS%[ÙfØÉ˜9u'I«e¬ÆiY+�€H4ÏÙ¾v‘¬»ŽåÄ©“<øøý4'šôz} —è�«-„¹M2¿w?�áÀ‘Cœ¼ïþ[Û)Žß:÷䩸ëÄ=f?3èöX]ZféÒ"—ÏŸçվŋÏ�+—ÏÙ"èJ7‰œ�¸ï±SÔó:gþ÷—Ø|~•š¯“Ä5¦çgùCá�ñöï|»Û°œCM(@GZXbçÙÞjóÚ+gñb¡„,'9TçÐwfseƒ¬“‚Ç@'매·¶Ùƒ{ðV¼êͦ)ÝzÝ6¨Û y0 ‘ vb`,È)®liÁŽu*Æ5-Å"v‹ìXÏÀHéjm©·BP‡�$²kšP@ss÷¯;ß –®‰#{�©é9&f¦lâ¥� 3|c’Ù;@â’ñb£-ˆ¨ï°®öׯ³þñWÈÛRcG�ýoz/‡žú…x¼@UIU€ Ê \…qTS[A+¦šTj+ ªH v¸Ri­z¯Užc”~.÷�©ê|@õ3WÄòت*Mª )£æë*û©aäFÀ#öEµ¶äQsŠúž4N<ÊÄ-ðiÜÿfÜ©7Ë»9øÜù¯|­îãx¢ÞhÈÔÌl¿H³oiÿ-ð2pûØœÝh‰�Qý3QŸÅ~þ þ‘¿‰�k蘟“`Û(]F%%Qn”·ª#kÕQƒ\}zŽxr–åÓßÄ »DÞÓ»´Ä ÛgêžCÈ„G%§Ú¬‹Øé-_a˜®XÊeßÂ`ÿž=9t˜éæ �­Ãá-�.怜..rùÂk,Þ¼ÆÊâk‹«,]¸Áõ×®pîôËœyùy®¼ö—Ï�ãæ�+¬^^¤;ØÆ”Zƒ€ŒÌNÌsê�¸çñ{ÈV2^ýÐ l|c�FÔ`¢9‰4üÓ€÷|Ï{iÔkàÆœK#/¥\­* ´æ*àÏ~íYú�ž¥‚PÜìsð­‡iî�0–CR€¨X-§Ù˜bfnÁÙ{295Egc‹Îæ6–Âg©CS›¹´P¬_e¢†sÞ€Éæâx³ù±”–sv=!¨í-Óa5'Ûœsh%Òä!3À” ˜DéZMQ‚QÌŽMQ PkÔ8qê+—mr)51EZ}ö ­…#8b¢ÐÁáÔS¸!ù Kû›7ÙüÌYc—y–ÑlN0ûèÛY¸ÿ�ÔjqõßeHìVªre*La,UÞîì¯^Ã;¤êf€–!�Wëš”û©¤Ñ*¢Œ"òàÆM½«Ü¯¾~¥-Á^‹QØs•û¾ò¤sUð3–v©ñ–f“ž}èÍÜõÎï稦òP‘| 0ÖôYµô(·Qˆ�)Õd9�©9z›«dËWˆfÙß½¸h3þÇç𥃱#@QB‘³~îeòÁ&x˜™œa᎛²ÉÜž9Äò3K<÷?|�Õ¯.3Y›"Žfæ§ù‹ÿà¯ñö÷¾�F½Ž”‰‚¨�å(ecŒCG ]&Ø“(âü-à;÷ê3¡œhNàR!š‰9ö®»èo÷ÙÞÜ´ôSa~i9íõMc�I“I×ó¬Û¸±ŠFЈëÆ2ÒaŠf–vÄÒb …4Q¼bBŒ DId>fEVNùpI™N!ªÅ&>PÊ^�š #³Çvjd®!h^ºz%ë»Á!jõîôAÒပ՛Ƽ |ã„é£5š€Ê´ªýH0‡ñþéUVöd±�`aV/ó�¼�¹û7û�ª—€«*É*ì¼jUS(€BÇ•a£Ï¸S´‚ETa0zÎ�Êw¥ü¨ÒU DG Wë9ŒîG¨Šª©·1%��[ñ‹«ŠªRot¤Ø+}Úÿaž}&ŠâØ×’šzÜõ"Í?? è®[ônüvâ.àŽ(Ž\ˆì»ï £ÔåÚAUå ÿ [TòÅyN{ùÃö&[—N[º£¿vƒÁö:yw›ÁÍ+„A—~{ÛR+>‚íO½Ä…µ |„Ù§îÂ×b P4&K;d�-›ÁB¦$õ11AŠ�øSÓS<òÄCÜuïq®^»ÎÒµ%6Ö7ØÚØ`˜Áƒºrˆ[˜½Jn‹“æÖ‚¯G4fšÌÏìåÐރ܀zRcéâÏýú3\øÈ9²Å!­Z±Ù7ïøîïäñǶþT)Kí;# Ĩb°½êÝl𞾟‹§/pãÒ5cXš*7?z�ù'öqô�c¬o®ÚdQõæ´@wØæì³/=3`aðÄ€×^y‘áúÀ’©«Ù „†¢ †[}1“SÇÞ^×ÀBÊ}âдlêôŠ+�IDìXÍ2P¯Pš²*Æ¢¤l8ăo€¦åÿ‡ù={˜Ú3ͫϾŒ¦¥KÂÀÁL“ÚÔ<‚î¥D)piBXÌÉ/nÑít°> ‘Ìïgÿ£oÁÅu†^ªiߪJ¬úí~,…6Ö«ÊëP©KÆ®$€8àõê$Õ”£k�³©*å(A°<†J½´ÐÑ&TL@ËkSG­ô8‡P× kªjÄÐ ãÉúlòGIŸù„¤Zس!ËÛõ¨þñf£ù¿°ÙœÝˆ€–÷‘/B`âÀ{ÂUz+²ŽuVçYN{ñ2Ù ÏæÅWY;÷<ËÏ}�áÚM´ÈÑlh·®T5šælkßÈÛ››¶h$Í|s‘ëW6éŸ_çàû§q`uòŒ"ôÊž¡>Q@ ¯x“Ú A”ÚT�ã÷ßɱûŽ2ì¦tÖ»,¯,ßÚn²ÕÙ$8[™¨O˜Å­‘ÜÚêÌÏÎ259�Ô­¨A°4SÎ¥Ï\ä¥_xžµ—WÍrÅ‹³ÑÜü¿ÿÏýa¾ó}ï¤Þ¨—«IïJP‘2}*ú×ÑÊ&²³bÙñ§:Ń�>Dgm›aš+è/ö¹òá ?qǹ›¾ðŠ�&Ãv{ƒÓ/‹‡&Ÿ´´_]š½û.Z“œþÊ‹¬ �éE>2VC �ÕjÄÙcÍÕ˜ 1� ¹d@ZÚ[3¦à1gnÊ$Wn5'ÌÏ.ÔÁ®¯†���Õ²'$wÌí™çîîaks‹�íup@.ˆ¦öÝÉcåÒCÄᵆ¨CÔ“÷z´_¼AweËæàä…£–:9G<µªc3\…¹¨Ua£çÊ—*�aŽ10Rìv|_ªN£s«Ì¿*¬‰Êó¨üŽXj®�ÊgqÜWMƀ͡òœ–¯#Õ�oÕϵT…•tž@Öïqí£?Júô‡™�œ ÍRÒáp¨Yñe×”ÿèñ;ˆ]ÀÙ�ànÅ`0p¡1Íôá»Êà% c*E Ãí›Wé­\çâ—>ÉÖ¹¬Ý¤ÈRŠ~öU@KcÁ‹Ø8å8Ž l’zÝF6÷¶ÛÄqƒ(xò«[¬üäWYÿÊYæ¾ó>ö¼ó^‚ïèH™|¯µj¤>/{m‰F&­6’€ˆˆMNM2=5ÍÂÑ} ‡'è {æ¾×&“ ›'\(aë!Á©�vRnpå°U ?¦è,¨öèT›¨ÇL?+­·q$(Ó‚#æÆ'”Vö‰@ÍWÅ£ž$ªÌ¦šr†K\ýÔÏÓý�_¦UO lPò^¯{^Tÿ°¾km³¿£˜pÑ#"òiºÉ9¹å.@Ôh�›–>•Ag›ÎÍ+\ÿæxù翟 ŸüYÎü§Yékô—®)ÚoCž""”›ªjŠ"Ó@göÐÁÞ‰ûïoDQD:ظæÚüAÜÜ!ÈsB:Àgéâ½o]cùk§Ù>{…b¸…«‰uÁß}ïI¦&§ð8’"•ì¦�Ê×�LÕ•©™@âcµ“õI¢šÙÿ¹ÑÂZ¤�›mÎö5Î}ä ׿x…þR�¢�“¤ Û›ÖØøäûÞÎ_ü{…ÇÞô¸Õ^ª zÛ��:�(D2å­¦ärØ- ˜˜œàúµ,]½‰I½k„ì¯õ˜ºs†C¦¯z½¤` uPë´¿þÚe󛘛1%{-°oßaºÛ†ý‘DäÃRA– Y?ØGR�K}™�Z-«´ÿWÈ_‹0V¤jÞ{{¡l ŠRv­(Á[£­�ûžƒû8õÄýÖ;tóâ"KWn0Ip„â™ZQøAiÖ“Pög›C¶>w–ͧÏÑЄ<Ïð>²ñ‡ßóÇ™9ùâ|Y“[Ä�"TÔ\ŒÕBÆR^"”©³‘2¬ªØ¬Ž> "E.Á T¸U] + ¸ñÖ‚±”´ê˜Æ¥ÝU‡ëª«Gä*ó¨*ÞkU÷ioçURxÕtZå5òö¯üôÿÊ�Oÿ õÈ™ ³( ôzNä/_…ßyì2œÝ˜Qh–À€‹bB•Õh H‡tV—¸úÕO³ô­§,_£ó²1ò kÖsŸ$ ‡ Õ²¢(ú4ª×�Uàù¤ÕL{ƒþ_èn·÷6šM)øcñØÿ—¿üI–¿úIòµEZi/ùÕŒµÎj)n.bêþYztI³°Çj0Z ΩG�ëÉ­oCÉÌ*G œagô±MÂÌ 4ví™ë\ùòE}ëÙhMÕêÐNq…##åÁ7>ÆûÿÀxüÉǨ7`# *µšBQÑ R[(.+@Á"+ÐØ�b›+ÏW&Z-þÀŸþCfuóâ7¾e67ýlÀœ›çìϼB²?áÞïÅgžËÎ3è Êÿožþ°Ã7Ÿþ"×.4·„¹…yÔ)³çyrï;X½¶ÄÅΰ:X´LŒ³ôŸ"%X:s†V9c~âÀå„U'ÆV°”ž0âŒõ…^°}nÂQ£f ªP³)²/ ÝÁÑû�Ã-òÂ@5ÝB,ÄÍ™Hhî?Œ#!hnÀ%‘¹¾5~ºS¬õé½¶‚lg Ó̘›¢´Ž?Àü=�à¢dÜX³t6ȱ¿9I¡ÐqÖ^±ýW`L8P5¦ÍT­( q@uáÖ*HU¬t*Þ|ˆe(¸Ê90ü˜�TR¹6Œn¡âˆ äåu«Q„jg”¾ í5NÿÒ¿bãË7À�¢ˆÁ°¯é`؉]ô_ºÄ}žß¥!×[ìÆÿká5Ïÿ#Eÿ8®Õ›Ñ¾;øà÷Ф5ɰ½ÅæâU_ú:+ßú¢¥ÍWÏûEÝI—yœ¥Ì\H‡ƒ"¨¦¨C×€¯¿áœû ÈZÅ)çý±÷àÁ75›Í)ϰ¹‡GÿÎÇ܉‡ é�ÕW¾Á¥Ïþí ¯÷6ɺ›l¬­!ª Ò�5JÎÝK}¡Aóð$‡;ÌôÁ)¦�M!MGą̂²\y Í‘àlqëmöô‡¬œY¢{©ÍÖÍM‚&“)º½®¥Œj¡ÆÁ¹Ã¶øj¸ãØQN>vO<ù8“3S8—EÕj>¤t[®¶† .-ÀžóŠÊH<`çTüù0åËŸû?ô¿ÿ×/^AƒZºM›Ê±?y’‡þ£Æ,®\½ÂÙ—O3ö #ŠÂ$Ö:‡�ÜÅ‘»�3¹0]‚…³)¤‹—®qúëÏÓënµ"[ü¡,ø—‹t€X ´¼z{ÞäÐ"PB?ì¤ã²^f̱6Q7ÅY^ä&�n$ N¼‡;OÜI/éáųq}“o|õËúÞGäš ‡ÙóÀSæ›'íÔ‰DÍmReýW_àê}žx5GD p|RãÈÿ‡œøÞ¿`rþ¢R,w¥Â±(Å8ç*€êâ]íÇíw@1æ³–…±Ú £ãª}3R�ª—㵜ŠBNÇœ«ã’µ(Uv2î*PM�U^o¬ªÊ¼ñ^°è­/óÊÏýo¬üƇ‰%Øg‰£×zÞù‰£øŸ=~—F$(»ñÿ^ˆ¸P=¾ÊnF !�@+Çù­ìF1ÑÓc­_ŒT›]GÀ5>MÔ ¤[ë\øÉÿŽáóŸ§æ…(2÷ öý"Ï?†×ÿ´(ò¿‹CŠjìÆÿ«ñ'â¤ö_GqtdØï Ηyc˜²ˆª/» n…†�‡†@_U—€OŸBäE=ªY‚<ŸBõ?pÎÿ‘Ù}{÷Õëõ¦‹bz!âÈþ4�ü¡¿‚¯7±Ì’TkGJÞëò�üϹô±Ÿ¤t˜š�Æ;oÀÅö¡ ÏSk"¬vÑlµðÎÙ5âZLkbÕ€ÆBØ3�ñÎ<Å,%—s3Äu˜ûþÒfîi‹~ªœ{ñç.�1g„¨£{nÇy9ÁœMÕf˜Ù»ÇÆLÎLã½3g†öÊ6ëë+lu6ZòÂ@%û–2#Âök¡ Øy¥:ÍX’ÉŸ l_œ$LÄ“¹ë({�ìÃ'¥{ÊöÚßü³äää¾À×gØ{ê-$6n_¬v#`À'8B7gã×^`û—žgõ•«b³ƒÔGÌ¿ërßÿûÔçP-Á> €Šà� Q�ªÇ˜P)¶—5Î| ã£ƦqV{o°ëŒRXYQ™ ZªÖÆc^Æ£šJ¥Ù³’JëoS(A„JZ®: ¨z}*u¤áÍ‹\ûÔOéÚ—~�Þö¶äyF’$šød{Ðëýªb"�EþoÆ.àìÆwòÞߣªÎ€eTìßa1¨æA5Gµ \–UõàKÀ«®^¿(cƒcÞGÿmÒl¼·Ñl´’[BmŠ“òpâ�ßGÜhî|p¨Ê=ÓΟÿÿ>+_ù$‰Nø'þðßdÏ#ÿ{g7²­á¯[0h�!̸ÌÌLw÷233333333óò]æd9Ù8±ãÄDC ©ûùuMEýº<µ�`ïÛ9)•Ç’Ü£ôëœóŸÿ?Í�¢z•Æì$A¡„Q—‚ W �Ð8 V8TalP‡p`ÝÌmõ�DYj`BX%:{Ma•ÃlÁîGÙ¿>)­ >)ÈÄ:ͺ\qO[ 4ãabvx;“þVrç~(¢VK¢1sQ+*«(þ��ò5Ànþ¢KèÆmÀ hÖ�ÈP*Q´PJÅ@ØÜ ü\J¹!¶-:„nµ­ÔSƒLæ�¥þþ�…BÁk4Ôê ‚Åk9ô¯cý)¢¥�í¢aé´5ç&©�¦ŒÏÃu{ÌÃH´¦Q¬hîÜ¢ïýöâÉ;n%iÅ�‚|¾€RªQ�ëת$þÔ/Øt3œn±­ß<^j­›Àà¯m@š¶S²=7¢í%‚ÚhúZ­æ+=ßy¡·§ÇŸ3™®þúÃ9þÅïfÉÆƒ@zØÞî«:ÄJ±í¯?áŽ/¿“%}EüŒÇû>ñ~S¦A hSqµnOÂ7#ÕZ[ü2Á“†²k²Ï7¾9øaˆÉÞÒ.®U*Ó–¤¥àÜY„¶ÊjR`ÀAi³í×"1ëZLûýV9ÎZûn“öxÐ:ö�¾ùC~õýŸ�N!„)ŠJôsð3gÙËÍÌ‹Ð-4(Í\¥bÊWû†™mΠÚjI¹m ­P2ÁËùíÿw»/Ó0’7¦ß¥c…ºý¤-ñ¤%#«ÓS*±tÅ2úõ_‰D“Z˜lI(†îâÎ-[M¯OB")”V²ä¨3�aˆ1¢íôªHе„É]ÇÄ·¯£5U7ó6qS(–XuÁÓØð˜òÑ›¯dû/¾H¿×$ÊöÑ{ì…¬:éSª5Üûá�…éÈÒjÜÛ¥¯À³šù&¬¬ÅZÏV€´”¦±Ç³ì÷²Ô¨mK­À–Ó±ßW�^kÆO‰ í°w6Ȥ×àë˜=WþNmÿÙ§[»îؾ/? QÐçõÚ?Íú‡�‹ùoŽ.àtáyžŽã# TDÜ(ܰ͞ì}žG»„¶Yññ “9½ØÓSÌŠb>˜)W(qǾà�ô®Ø€6ÃÆ.C¤PÕÉ=\õ‘Wмï6– ôòð'>ŠÇ<þ‘f(Ñž]»ÖÑÎ.¢Ä.Ø;Z<–Þ‡[˜ï öèNÀZ¤€#”ìÑoЖW1ht{@C{ÖšÊ\sÚ+Rm`k_çÞ±q¾þù¯sɯÿˆ6‹b„/ƒ|Hv]ŽMOÚŸµg­'è L&a€�ar©˜‰Ú$scsT¦wOR¯ÖÐRcÎk(d`d}0r5¤lûàhP(‚lʵ ]¶x9¥Á’Q�öó¾9ÇC¦Oæø˜¬†„X´KnJró57±wx7Z p2“#·x ½ëÅ'C""²�%'T/bìs—ÒØ5K&È`nˆaŽpílxÂËYt艌o½ší?ý¢6EN´¨ä–ràÓßÀà¦CAl¹ûGéúÏ`Kɸ�Xs:Ú�4V8θíçì’š›(+§7d³ÊZI›˜à�²ÖRX$G’'Q ë©éF…ÙK¾ßþ›oFãC;à =ЄAF7õ©F½þKÏóÞ]oÔvò�.àtãPàéBˆ‡gr¹µ™l6ô_dsc_»ìá/äÀ‹žFÏâåà6oµ%€¨SÝ«¹Ý;¹ùS¯¥>t«×®à�ï{+V­)̦P°éA"NÀÎ`´U³%wcûÑа;[Yå0aûÛ¤Ò ZˆŸ¬>�Ep$ˆ :‘†¶{?&‹Û=¼›ßýò÷üå¿gïÈn³/ß[¤§¿YòX~îJ6eð…Œ}šwL0ûãÛ`¨bJw‰0C¨ˆÞ~N8�Áƒ�gÇ¿gû/¿‚ªUÈæ 4ê5–.ëN½È€�kh«G a5Ü­lÅîõ^ú0”(‡Dà”ÒD{³YoB�]ÞJ4 Àó0‘X@¡-µèÄ�È2mPš[þ:BY½)«Œç[ÏWµ[ãá?|côÞ?ÿ,IbµªXìmWrÑ•JeºV­ü5‚w>àÀ¦+mÓ !e^À„/™™'åò…•žçe‚ÀBJb-é9ä$ŽéûYsÜÙ†%&v°(¨;Íœæ¨ÎÍкÕhP\Ôo†S§zL´C1J� aÝQ4N™ ǦÔB;á8h�SŸ�éçZ·AO´�ç=mÐØ§H ´MNp®û²„} &ÛØ°y=‹–.¢<3‡ŠÓÜÏYcW�¨2rÕN&ï�@ùPZÞ H€ «³ææ­PàA˜1‹aòµëP€�Q.ðñÚÌ»–ˆu “5 ˜ÅD­ñ”Dke@E"÷Oîºíªµ2:b³ý—m 3°¤m=�@f/ÛÆø÷¯#¾y/I#BµY‡ù�e,=뱬=û±Ln½†»~ð Z3“Ä­­F�ÞƒOàðg¿…\© hg&±æZlef¬òŽ–šýcFØ™ƒ�ÜZÉt{N¥Ö¾›5&ÃBwÆF8‚šÊú]ˆ­’^ûcûÚ­R¡Ùõ=CÕí¿øÂÈÎ+ÿ8X¯T– ð|Ï' ÃÍX³^ÿMÅoFx Epºäó²w ÿµ›9äU ÖI)J~àËl.O+ЉDÈÚG<—cŸù:×€”Âñjw›¨ö½Ö"iÌÚF¯¿¯Uã®Ûï0þ)ƒƒ[ü ±ÏƒÆ67Î_µ;âm×ölÉ_»è-4iØw;DúþÞa_ B¤Ÿ·‰V)¯}L´�a6 U H`Xv+W¯2ä‰r¹Lµ\-L&(1ÔFªÌÞ6ÍÜžY’f„ð¤Ñ/ó<ãõƒÆ4ñÛ ~³>A,Ú¢ x´ æœ�6Ø@Ûvš6؈}ý"³ž’ûÊkl¤0ï77=Ë}Û¶™õD{P4,ö“_²¿�Ǭ13{é6†¿x1ñíøøÔ›-”ÖäJƒ,9åBÖÏËÍŒ±å;¦66ZÆš×·ˆÃ_ðzWn@XY³ï¥™Œ´�K©ÙžÊ·MÒHËoÖˆž�6ÓL.œHÇéïZɵÅR™8ÚiÒzíh‡¶¯Ëî?Å‘® ݹ÷ê÷?wdìÆ,щêó<_ä²yâ(ŠÊå¹½õZí[À»€ hÑœnäÃìáKW­ú D¬lԪ٨ى†Lq€`ý!ûгß9�!(ô¡p¨¦–G‰½¿Í 2›M óvßq#3wßJ£Raº<ÇÇiæ„ î_žm¿ˆ5œ‰°ÁæÈ:l€t p»É;ÏB¸S|štsûAf]Ú%¸ö~­ÛékWÆð<É@?›÷ÛÄÀà€±_˜��5[­V327AR¹{–‰ÛÆhͶðƒÀX7†©žöÁ€$Ä´d„-qƒSŠÛwNÒÎjÚ@ŒF´Khíc€NÁF ´„X'ó`sµÝUŒãhÆCx>²X$¿z ZCó¾)¦³…òOnAîi˜ò^'h!ÑÅKN}/|*a¡ÈÖo„ê}·}6!%JJ}Þ;XyÔixB´›í– ¦ÕËÐ&°J;YØ!zÿsž°z;Öþv9 ¬ã2ôL3{kSééVßÈ*ýY,NÛ¼ÍV%°×ó“fs÷ß¾çõŸ}k9žÜ{€ðd±Ç @C«ÕlúÂÛ âµfý³@•ntç�¾ï?$ÛS8»Z.—’(BkMnóì÷Ä—sÀ|f3°þ@„ô°™7 «¶½À=Û®c{íæ¨¤ç}¬=×] I‹±áQÂBŽu×6ÑþÂÎ4:+ÅpËn‚4¤@‡>:ôöéÌ O =iöéÀGûžE_Æúh•ï„@›LÆfE¸µ…]ÑÂ'놎v@�fUB’ï)°jÝÖì¿� “anf–@ú†Ñ¥…Ž5Ù8Gc¤NytŽ™á’zb(Ô2çáIÓ_A ‰�O€�6%´VÛ:ÚC"IDb>zÈvf$Ìy6ýÙÇ#ÔA¤@K@ #k¿íæ»i5šf07–1ù�UH�gîºL|ï&j—ïDÌÄÔë5êͦy—ì’Uø¤W°á¼' …Çíßús·]E._$C³öæ§¼– ç>ÏóÒÒ™ó{•Þà­†?Nðlú°SòÂÙ/™�ŽÎY¬±„BS`°ûJZÖº¶D�gYb;Ò&lC´wM윹ïgŸ¼õÊ/¾…hÕׇa&¨7ë4š ’8®¢ôMh^éùÞo€„n´Å;ƒÝxÀÅ)^ध·÷è0› ²Å^zÙÇY~äi$Z Ü9À·MŸ¬’‚´ëͶ»õG—Ô«ÜòýO2ö·Ÿ £¹R‘¾ö%œ~öéB"´Æí9 †¤µ�Æ.±i_š-õ!q|Ilì°fq¬÷L™kRÒfÉ™Íò)¶Ò ]'\–eÉËX|ü2–³¯ÏCÒd ž–4eËK "™5<Ýž�±t:MšžJ<„' (6ÊM.ûÝ߉ýÄ\\‹ñ™ùmõí ·�‘™ˆ(&jµh4[(Ò¿þ V=äi¬<ñ|“ÉÜü�÷1ôû連‘*B°èü§pØ^jæ«eýÞÙ1I1qç`µh+Ùu÷ŽÝ� "Îy‰•mØ}£Æì�*ˆuû|«|†N ‘J�= ¶Ò@+e«H×+Iyû­»oýÒ;F+;îÙèþ€”R´ZMšÍ†ò„7­”º&QÉË�mÐ ;¼�)�t·Ö–¨$>MxÞZß÷Ã\6ÇlµÎ²£N5ж,ºoÕ¶{ʼ±ËÐ8ì5?¤°b=Ã7^n\Ekå c““¬ß#KV¯hƒ‹ÜGÒ�·ï¦,Úû�TFŽß3¯u`ÎÅ.Í ûiÓþÜÚÜZˆHiJ)`I‘‚�v@Äœ'ÁÚ¯-}¡À�NõìÜ>””´ßÛÁ­Ù°–Uë×UëD'Ôk5ŒýrâI3ƒ×ò¨ìª0~ÓöÞíUõe…ÙÜ;°glU™”½©Ôm1QiM»:5ÎU{­»®GèØèŠ]øÄGò¼W¾�Þ¾^»ÑžJ¼¤Mü4÷B;TXG¨SØc=ÜS5úØ¥¸¶j² œG~Á-½a­åмµÖFcn×};¹ó–Û¹ëÖÛÙ=4ÂäĤ÷4O¾A@½U3=�\1G¥UF‡Jr¹…UEÂÁ,^@¦Rè/ÐlµðI܈�¡Ä( øšÆx�Öh ⹈ʎ22’T'*ÄÍØlÄægC˜É’ïí1D¥5­ü Å£Îaå‰PX¾!=ƒÅÓÛ¶pÓ'_Ccb/Aµx‹VpÜë?GoÛ=ÑÖ@¤²û%Ök (l€ò…5¤)Á>î–à´^˜!ïª2-‡ÅÊUŸ¶‡L]RdšÍØ¢¢`]ƒÕ•ZÓªÌ6÷\ùëá­_}¯ò…\ÑŠ¢|¥:'‚L&£µÒe¡¹¡Þ¬¿¿™ÄÝø'4`ë†JÒƒ'{z )E”é夷|™å‡�]·¥=¤H%:”²kÞm7DËðÊÕ­Ú{Û5Üøù·RÛu7R z÷óüù~ι�‡�éÄ¿Fï»aÛdí–δ¦�u$ Eê³.•ÚkJÐ Ýé}ìíë²TE’`…µ¶Ë/wn†@½V7âŸÛïÝÎðŽ]ÜzõMŒ�ìÁ÷BsÓŸœ'A—Ôã:‘n‘ÏŒ7�ni£JP‹ªäŠy*å2¾ð÷õ~¢F…BµÕ2aΔ( ™"SF;MkMœÄ¦áŸë_„ÊäX´f›N8�ú²CVnÆËäöýìë3Üð¹7S¾ýZJ¥”Jh(Åa¯ý‹6ŠÂð3°²;kÁ§JÌfS‹;±ÜÓ5ì91—,ˆCtÖyæëeÊ q Ùí �ZöÕXå36×Åž±Iª³:Úvý̵_ÿàteÛ===½ƒQÉre®M÷ãF£>×l4.•R¾ØI7þ=¤�,ÝxÀÆr!ä/—,9Äü|£ÑwÒÃ8eÞ½3“Ë:„í×N�:²7¶«!XÙ‘%Áž$ ÛþúS:²Y1̤•û­çmy›ÜŒí’%´¶€ÄɱâþÀÀ>Ï^à áÔé>ר®� @ ^ŒÐ:›5¤E¥ hÌÙJ J)j•*CÛ†Ù9ÌΡ� Ý{{GöЬ7ˆMæ£Iâ¶Ô�)}úÆÀ÷êÍ:Ò3ÙÍF“(jáû�qMC ¤±ÆR˜¾QÔl"†!åû>^¡�•'_Èà�G3xÈq—¯AûaªÂè¸Å-óŒ´á?ÿ�‘¬!ô}6=õul¾èiàh�&œpŽ\7M³yNÆ='V6€X"™¸YG ±¥nîª`ÿ Xú°6i@ép ]ßõ¬Aü¤Am÷}ÑÖo¿¿2ü�¿6¢8éËd2Ù8ŽE»W£€j£^¿-IâŸ)­¿ì¡ÿ®GKtãB5/ñÃð�ËçuWšÍ¦¨%pÂë>ÉÚ“/J½ß¥s÷ÊÍ$U䵎¥_k=F $×f¹æ ï`×_~„ˆ[ÆÇå¨3OâUo{ k×­MuÉ´Â�_ %.–”™Ðé˜Î9Â÷±V·ælìpÁÍ=ÇÖÅ·/,}e©K+µï.j2:át¯­°†Zíÿ¤•F½ÁôÄ“cìܱ“íÛîcÏÎfÇgˆâÈd5&´¦\žCƒÉ4´†Ro ­Læc@'ðCiVJ¡…¦¯oLŒ�S­–ÍzÙžV�ó86?öÅÆ0 Ï·ËMmÍÞ[®dëçßBuj/Q³�N"–ŸúŽ~éñò½H‘&öÍ^iײٙW!ͬ¶ò@ºi;£µýgÜ2—%?£­÷m‡l��Ûn Ò±-°]9U£Š?²E ]ýçúö¿ü¸Yžž-D­f˜Ä±h;Ø’$I<ÿù�ï©TË_X´è@Ó�wˆ#è%µnäâWK–/?5NâlEø«à´·~…Ü’5霓ÝH‘RHÁz µ^[³ öp›ÙfGwqÓ—ßμÄ:¾F"ÿàãç%o|k7¬°³§/ãÞüÅ>ÿÝ�lÀnïÆýíxäØg‰TGM‹Ùަ}¬3JºÜ\÷ °�]—/V�Hc@"Nµ:Sÿ @óÛøÞ1öìÞËôÔÍjݰÎfgg¨U«øÒx ¡dr9C¿ÖJQ( óYúúÉf2¦„wéþNµ\F2ýKØÿɯfÃ…OMEV`y÷}Üö¥wÍ”R5ª4{—pÔ‹ßâŽAc»&›9R­Þ™´r²�;ì™>àX5Î5º„Ûü,õαóÊÒ�•8e2¾«X �þ“€hÏ}zׯ¿ÏÜzY}l÷nÝl4 ‰J|­1jÝfæJ%åVÝZ™›ýF†?æèÆ8ÄñÅ~ºñ€�GúAð±0›Y£%~‚dàŒÇqü³ßl °|)Û}ë>gM_³€Žv²!è€fl˵ÜòÍí¼ƒþÞ^¤'ØïèƒÌŒÎÒåK�/²^v­-^�•Y€cÓYNz3Ç÷}­õ9ÚÉj\°C8t€…X îq�æxœXÖ‘ÖE·¯AX™”¶¯¯=+ÔŠ#’VB³Ñ0ä? 0¯ë Ð�ï- …0æuJiãÂÙ[ê5¯³¹�D)¾ò±/ðçŸÿðQHÂB/‡½å+”6f¹FXQ“­_y'S7^BÒjBÒ$ß;Ȇ罇%G�îz×$€ôe*Ôm‹avœ  -ZHÛ,±~G•h\ÚÄ ÛÛÀ—èÔ,Í-+C§‡)ëšPÔöì¤|çµÉäµlŽm¹ÎÓQ4uYo[~‹½ô–J„ùÜøö;ﺼÕj|5“ÍþˆéÆ*Ä1ù^ºñ€�ÍGƒ|ö©ó™NÔlŠF KÎ~"‡?á%dzzÑH�ß u¼;ÜYMm1†p,�Q ³»¶q÷ϾDt×õxDFò¨³Nâůz‹� ZÛDûVíö]œpîùÚ©Ö9,6[ºÜl‡'LÝÌÅAp@ŠDç ØG{R˜5í)[Û¿Gk´CwzNiÖ&�Á‚Eo{雸Px&ƒÊ/]Í)ü)ÙE+Á­öiÍž/åÞo}€êÄ(ÍÊ4™\�•>“ƒžø sø©gzf ‘èt‡¨â ×òÈuôt1?eª…ÄÊÊ”,À±³LâÄR¶æl”£J´)%Dµ2c7_AíÞÕøuŽgF†¢L¶�i5›~G(¥Œ �áø’µ«§£8ùÅŽÛnûp7Ýx�øátcS6›õ’+�ÐKý À rŽ:‡÷"‚ž@›€B¸~ïíIkë�Ïb븾óÚXÔŠÊØ([¾û1†/ùèßó9÷±ã9/{Ž'=²ÀF£µmv&¬R™Svs«[ZXÇ\ÀÀyíf$¶ ( ”Õ`a· f�?·AÄ.- M›7œö}„¥‚ mj—kØ/„u@,työnc÷ý±·~ˆ;nÙB3Vf(xàÐ9úµŸ!ìÄUN.ïæª�¼”¹»nB¨Ø”ä2ëãÈW}о•kqß*QÄ�D*�R€;4)-=54ÄÚvàLÙj¶Ô�ûÑf‘i'Ëv©ÎXÒLNYÍ¡€X¢¡3ƒ4·gͽCL^õkÕÜqk21²“ÊôŒTJË0“I’ ’„ tà‡SµZõî¨Õ¼%nµ~™èøb E7d,µnôzAð�•kÖ^X(³Õj•j­ÁÀ©çÈ'½ŒL¡D5 rECµ¤c�2w°ØköýÏ lA’¶÷›ã»¸îKïdú†‹QÍùB�‡<é‘<íÏ``°ßÖ‘wók¢ß•¢w¨ÇV/§0Ù7—níDû„Ž$Ür3°R0ÎÖ]S´E ø¨ö¶«­d`}±ýJˆW¬¹ù†[ùΧ¾Ê–›· „O+IXuþ9úyoo{#¥ì¬VµÌ–¯½—‰ëþŠŠB�rKqÄë>ËÒ#Oß—� ‹µ)bO „À[€Ùe+ÿ˜H”ƒ•*Áó¤Y@¥¿íß­ÔGK'¶”r um gXÛWGS�ØC}lS×ýY×ï½>™Ù¡ZՊߨV¥@ D* ›Ä±Ö‚j ý;[­æMIœüEzòïÀ$ÝxN7òÃÌ·JýA�—ÆG®>�óBò½%Ð ýk÷Ç/ô¢„·¯©ê ìLÆìÇjðzi)%e!YòdÕ±]Ü2Ÿéìš×Ûêíë#“ 9ø¤£xÉk^Âʵ«8€Ó)£p- ÐCuª®l�g£dgµÕóqÏ8ªŒ�C;dWN¸ÇÜ~R;㱎/±è ˆ¢s�ïš\ËÇßñ!¦Ç¦hÆÚmú‚w³ÿ#Ÿ €�±î¾ùJîøÒÛˆËSø:¢Ô?HxÐÉôœ·!3y`k™5cH4Ê—HÏÄÚÊv,‘LÛâÈh˜ÕËH4BJ„ „Râ ‰ç�Àîé¤e4)­Ö–ú4vïÈ:_@+1 NsfŒÉíwPºƒé›.&[Ÿdfb\×+µZb~�ÐÉ5¶Ïq7jõêN'w ”ÿœ¨ä§À^ºñ?@‹îé£ÿT!h6� ‚ð½…¾Ò±¥RO³Y—Zú¨L‰âƃYsÈQ,^±ŠzÐCÿAÇ¥VÀ6ëǾZôQ·Ò£¿´êô8W|æMŒ]ökß#›ÍrôÙ'óª7¿šÅKÓq SuPšv1ÀQÐÂ¥/uXgDpö¹àb�ÒAf c8SƒîÕw$X«»%;{Ÿ²�u�~T;®�œ¯}òË Ý³ƒ¦Oz1¯°âøóÐè}¥ÑÖô[¾õAÆn¸ ×) ´úWqøË>Bÿê�§aoOñÛ*ì!O¤\š² ÇЬҪUHš ¥~q„@7ßCÇ™Þò‹–!„‡�‚H‰¶¾ŽBA¬4:IÐQ“úÔ“É”wÞMýžtcl§jLŒˆ|1/'÷ì¡23KE =4[ ’$1jAÖµú.”º=Ž“K„¿+õömÿŸS è†�¦ÿ\¡�k¤�Ïj•«­TNÉærË¥�lÔ™º~7Õ;þAý¨“9ð°C˜¾v±æ0úV­7OtXŒ%Ïš VuGYŒ·¯]XÌñÏ™oT·šÌÜz¥@¼æO—òåb�g¿ø9,_¹|ái}éª?ÛÙEû¥=scâþÀÆ^ÃŧlgQ”Áe%¤Š v8Ÿºˆh©Žv¾ìÖ[J@»ÇÛæ’tHëŽY•Ÿ A ÛM  2WĬ£@Z)v]ûwÆoùI³Î@_‘ÈϱáaÏ2¿R’†[0Û¦?+K)Òc8b ؽ�À'‘‚¨Ic÷6¢F·¨ïB�µ"*õ&hE£2kÊÀÅUéY¹Ñ¼–ù^ …F“D-’D5jLÝv™Æ,~TAÄM“MÍMO%㻆½8Š„VÊþ ˜Þ& ¥”‰ôd -¦ZõÆÖf£~9ð“l¡¸ãhº!Nê¤ÿ´±¸Þ¬?!žÓÓWÚPê}ß7³�F�|O/bé&Íg9›eÑGPX¼2µ-°îʼnJK#‰v¦½Ýû¼JÛz-·ÏS¦'ïºÁ4Ÿ¥”¬;d?^þ¦W²ß�û!Í�šßNÖᘪ¹€µ@ñèðnâVε«(ôÝLÀíå¸,³ÎçƒC»îXÖê”QuJÝ�:g†Ñ^K·×™››ã§?ü9ßùäW¨—«h ™0ää�ÿÆü¼5 �æännœ—¯™¸ý:ò¡G1_ µd'¼î3dJi†«+«µåi ภ4åˆ/¸ù�E�NMut;³÷ÜJ8;Ì¡ËBvo»‡áûîcrlŒ¹ÙYZ­¥ÒÏ@q€R> ÊÏ2XÌÐTšÙÉ1´ðEB+ݪ2½g”Àó©Õª$±Y“IÙ&þ …M•¨)ÇÛ”R»5z«âç2›½Ðtã~£kÀÖ�ZìÉ«’Vk¤Yo¬ 2™Ì¿’ ¢(’AÒj4HfÆh ßÍž»n£Vž%ìéÃÏÀÐÛÚÀ6³jƒ�¥H­5Q£fØ>÷\ü ¶ýâ+,ïñ�ZS)Ï™?ôúL™[®»Ñ¸-[±ŒL& nýzÚOõ®dŒXl„0%‘¿ÿîo|ჟâê¿]ÉŽ;Y½n ÅbéÉ}˺jÕ&Ä  :L€ g¯µ�CpÿS¥.Û9G¤[z÷Çh¥ ïá+Ÿþ ¿ýöO©—+d39‚ C˜Ë³ü´GRX¶º šá+~oˆB%x:¡!3lzÊëÜpº4[æ_Ù^uÖ°0°õû4ަŸÍP³F³´æf©ŽÞGo}œ¬®ÕËå¹Ù‰=£~evNê8:I s.�”Éê³DåYæöîdjt„©‘!š³ÓÔ§'hÌÍR™£:3c$� …4.µ­V Ú #¥Œ”RåùsFç·›U’üÒóü¯!¾&óÙ¿ãtã?]ÀéÆ�J©kê•Jifjª'Q*-„"IbѬ׈ÊÔ‡îâ¾K~ÅÄÈs£D•Y<)Qæ�=2;V(*‰ˆëUj{‡½ábîþÅW¹ûGŸ¢¼åJ‚¨J£2G’ÄÔÊe„ø¾oLÊþö«?065ÍŠUËé@¸SýÂ¥ÿ:å/ç†-«ê8޹ä�cËõ7#˜ãÖn¡µXµf5a&t2&ѹÍâ‚‚vÁÐ?a—sÌamÚÙçžs?í' 8Ëse~ò½Ÿñ™w~„;n¸ÍØ!Òó‰tbìÖ=ôiô.^�ÒPáÞ_}•ÚÞ]�Ÿ›žƒO`ÿG<éy–¯Œåc©,ãP—qƈÌyVÂê‚•-ôК›š¿ß2@™‰ÝÃWLŒ�½|^tt¨233ƒ"ŽZ™(ŠüF£Žïù"nµŒŽ\«Ù4 †!ÅbQ™ýI’˜M aJw~(€8Ž›I’Ì©$™ˆM6£~êyÞ'ò¹Â§�¿Ãÿ—çîN˜£ÿo"D©ã…Ïõ=ÿ L>?Ø78°<—+„*IäÜì ™l–Hiêõ:h…—/QXº†¥ûN’É¢¼´&è >µ—Æ®{ѵ9fÆw““@\‡8&‰bæ×CJÉ!'ŸÀìød}bd÷l’ÄË„�&ÛXwÀF.|üÃ9ùÌS �ÚÊ.`Œ9€ît÷Õf�‰± ¾ûÅoqã%W9�À‚Sqî:ý?è¼Ù`�¯u =£{ùÁ7ÈŸ¾ó3�’ “1™ÍÌÄÚ“†íIÉñïÿ!�Ršûþô¶ýðÄQO+²KØÿYoaù1g¡-Ý áH")g.ËÒÞsì,(=f‡'As£;¹sÞsç e9š•™Ë¿õ­¯?|q¦Ðð„\¥µ::NâÇ+‚ ìí_ºdiyzZGÍ–ï~6—χ™0+͆’B&Q«Y÷ƒ@HÏ“�FÝëé-Éù­ …‰ê�[\ Ì ÄÝqÏI ËRËèÆÿ»(HØ” 3'*x¤ ý5BÊ%òÙ\.ëû¾ÐZ‘(Efðü€8�jµB”(òÅ^úPÍ “ÓÓ¦\–´æ S+E­^5ßJiÖî¿?k7o,ß~ë­¿ß±s8WÈŸ†á!µz=h =N¾èñ¸G°aó¤'â4áîsx°ÂfuÁÜÌ¿øþÏøÛOOÔŠLæ#œû”Gð”g=™|>oÜE;G産&¹¡îRÎ’]»cažîï¹ë^®»úzþúë?²së½d a6C¢¤Æè¬)$3å2Q«É¯ù Ïyº:Ím_“7_A£^Å |–>‡<û-xÙ"` f¦Ì4;éKTê7“ñ,ÝSK'-ðRê5Xå7G¾¦6±››¿øvÖ–$­Ùñ­—þö×;–¯Ù &Fw—´ÖÀZ­õ!¾çïW(‹I’,�T¼Æóý0jEøž7Ûj4v#húž?©´¾:‰£‘b¡w¦Z¯nªtãâØÒÝø2i4W+­zµæ!až™Éf—�ß«µò¤”žïÒCÕjÔ¦¬‚þ¾~´ÖÌ7â…Q¡Ú’ús•Y�ÍçÏ÷+kößÇøøøÐ]×\óÑRoÿà‘A&ó¤L.w´çyƒž�(•°z¿uœÿØ‹8ñŒ“(õ•:”¡4�E4.�UË~>_fúëOKÜŠB˜›ñcžÿö؇GÎû£OÛkÚïåîîLp3÷u§·Zp!ã…sû–;ùÓþÌõ¿¿Ä<Ôê5¦''Bx<ÿ¹~ä‘|÷£Ÿ`rï8•z ¡Ǽ燬>ö,&o»’?þ*#åE-æóŽxé‡XqÌÙûȶ*@ �,@©ôò³^Ú·‰•5DìþWuªÌ“¨¤ê3“Üúù7³º?CNUvÿù»ßzð:Dié Q­Týf³Ñ”x@!ö ) ¡ÿT!Nì²ÔL‘�“xSµ.L”:YJ¹BzÞb4 ülßࢠ—Ëû¾'ýÀ½™™)¡•Nb• â¤Þl6*RÊQ!E=Š¢JÅ{e¢oÑ’´Tr�ô{ÃZµzÞšƒ|•lÅ›æ¿fMÔl¡Ñdrö;ö0ó”ÇrØQ‡Ê뉇ÍëÔ·kUC"¸ø×bbï8Z)”Ozé³8ãü3ÂÀ]«ݸ3JˆûÑ™éÄ Àݧ¸ZµÎ±›>ñJrRÑFÓWýá·¯|0N·¤ÖÛÝèx­F#›Ëæ}•$Åju6Ìdòº¿4°>f¢8I¶F*šUI"þ½¥ŒF³±QhξwÁŠ•«OFëÁòÜœž`é²e¬Ý¼�cÎ<žcO:–Á¥‹‘B‚‚ÎJZÛÓúæÓÉñI&öŽVS¡P`ѲÅxž×izsáŵî|ª«Þ|ÿ½! �˜äÝwÝËź˜®¸†ñ¡âD£»¾R¿a�Õª°tíÎzÌ£8éì³XµnÒšÔlÔë¼ô±�çîo&‹ô�ý8Vu:»~ò3“$*&7¸œyÍ43ÉŸ(K.ÆÑL47ùræh±Ô*ìòY¬ÀuÓ´�dkÓcÜð±W Í¢L4uÍï~û6àótãA6øÙ·ˆntã0‚Z­z¨ð¼ç‡axÚŠ•k6J!LX£BŸ•›×ræEçrÊY§Ò?Ðç"Mç £³è³nФ\šò:m¸öÒ67øþIÚ•­�J¹Âå—\Áßç)Ýwß´)ZQLµŒ{g[%`õûñÈg=ƒæ3šBO�M­5ZÀ¢Q« ú7?ö ®ü㟉À¢ϧ´þ`vþæFÆÏgñ!'pÄK?H�ÍY‚›©‚€/qhÒ–¾™´”Æ•ÓÆ²þ÷¶_â8ƒVƆ¹é“¯%i”é�‰;¯¾òKÙRémÿC—ÝèÚt£Ë{н'÷öõŸçþÙ±¶Têóê�K8è¨C9çáç³~ÿ���Dç›úý§A6(u¦aÃý½vey:vÚ¯µ†8ŠÚ±“mÛvðÛý‚{n¼�zµfˆÅBýƒ”«e#¼YZ²ˆÎ?�Î<“=Äd3#;wR«Õعm;{öPž™åÞ»îDG I½>¿o/S£{^@K)Jë °b=“[®Aú>¹þ%¬¾è¬:ãQøR"íþŠáb£ãÔéKðdJHDÓÏm+hOŽ•¹êãÃÜü¹7ј�"¨OîÞ¹åÖoo¢ªg.YE7ºñ¿–íÔk'4›õcì<ö?är…¼éÉËÀ¬s¾c{ w8É% ˜íþ€ÇÒp±ÎÓZQ™+ÂÂõ—_C¹RaËõ·0>ŽÖHÏ3‘²¹÷�&õzƒ»ï¸›]ÿÂÞ9pɲ|YüÉ’ºêJÝÛ¶ùÇÖ~¨ñÒØ¶ùlµQHFÄÜuV®ª\=¶rßfú)öÛçìÜçùøÃ_ÿ]žâ)®N/eŸsrMlmåø(ŒÀ{ §�殺L0 ™\gk!c²lCUU’èPו‘Ršj½b8›sÿuo  1IÂÞ�»ôÆc’þ@R‚ dvë&ÅzÉéÑ!ÚC?Òìݘ’Í^Ëìõï@±®'P-s¤wiîZ²N»ÜV6ý™@·Ç’_Oj�»ØA+¶ª­.K\ u[)5.èð_ˆŽp:tXPVå£Þûßô_<>:ül¯©ªR¯ÎV<úûÅãô×¼åuÜÝî<¸ÇÝý{ ÇCÒ4¤µ¥ñÞ�ÿGÞ“iog§BÀS–Õöóòì<<þè¼zxÈ_üÁŸðì_>ÖE†³óS\eå}­”�D^dH髪¤gc‚€l½”àÌ~2�}›lCDâNóΠˆ’‹ûûìÝ@2™0ŒäwŒ¢7è‹Ã¬¶uãTsò]iIq`ïÁk(×ÎO�)³‚*_³±†é[ÞO8œ`ýÎ]j”�KS:S´I¨Õƒi�ìñì^èlç­Õ-“@{t�fGT¨¾ö(„aDx_ä¹:ü«ÐN‡÷&7LQ›< ¼w¯õÞ¿µ—ôª~o¯6«þåÕÅ`<š •ZÇQl¬µµµî2î V«Í²²Þ¾šDÉóf¤•sî1çܺ\m6Kêo2ÆÜ,ò‚‹‹s™÷rzzÆ£ú×ôGywg4’ zÌïÝdqcÁx:–|¸0 ¤§µÆyGUTHTO EYÈvNOxùÅ—9¿¸àåç_â¹'žæüä”Õ«g F#NÏÏX]]�‡0I’TÔˆlQKI¬×ˆ2©]M7ÓU•F”’ÒXš¤DA$sмRUŽW0½wŸÏ~Ý7sÿõo”&0$ PJ‰òyõð%¹Ÿs É(D�jâc‹ƒžù³?e8š�©%xÈÊšÛÃÉN²›{d4^ƒj7û�‚v‰Ì_�±’i«–랉vºtÛP œº1ŠžRZA€÷…ÅûÊ{Òáÿ,:ü›ál6ÞÄ›ïõð¹05Š~Y—ã¢.²u/Œ¢èîÝAQd¶,Kjgë(Š]€Wu]ÙÚY¯œ.²bãµ6äe®�*NÓhÔïÕ®F¡$Æeuy)å¨l0d³ÚpztÊl¾'rüs¿B]Ub5F)P^¾»ÚRlr4€õj‰‰òl#ûʺ–mh!)ýXgIÓ›,§®- -¥°¼ÈeîLFbåÓ”q/•ûöÓ”ÚV²ººÆw\—b�sÒé”Ù½¼þ�ï–²Ûoÿä‹õ{6Ûcqówì¢`¼Gk™ï"ßñ¥”|zïh[·�³B‚·ßô&VË z@'èÉœh0Þ–¥Ñ�@H§=÷Hµbj4ÀµðM@ίZ/w¶Éêzù¬�>jÀ�¨+ª2£öÖ•yîŠÿ"t §C‡Ñ0� ý¡Ùí›ß�cÔëõ£““ÃØd™~†‘$K'i*cL¢ Ò ×ø(Ž †®,K¼s~ÜKUÇJ¥ó,CÂC㘽7¨òRÞ®W(êÚQç…ôOf÷õúcÉT»¼¼BˆCkYüEXÛ, Q(´Ïj½DGƒÉŒåòïš…]iy¦¶VT�üu\\`z)ÃÛw˜ß½ËâÎ}Ä9fk²ÕгãC<àµÂD1I¿ÏâÆ-¦‹›ò|q’Êu¦{sVçüù¯ü"§¯¼@ž?ÉÓóWì¿é-˜0B!EÓ¤_ �c‘!lò³ìƒ�ãMïû åjÍñ³OÉœ„تàìÙÇß=§ÛÖ]Ê(¼ó(Zd£hJh²ag…v×B´ÚVs¸ò�æúô QR‘�S‚­ðÆyí|=Žþ9t„Ó¡ÃhÜSÆ«Ï;ü×Tey3Ë6±Ç3M8. ´6²ØGaLžgÌfsYÔ7Y&cÒ~œWÆ”UaR*‚ FÒ`7&b0Êâ™ôÒ¹5Ûcôð³.JÎ�å8ëe¾Â{°µ5⬣ª*9ÇÕ5®ÜÖŒÈ×kªª”E¸( ™ŒZ…(cŒŠc¹þééUYÖÖRÕ¥üu˜ Šb¹V_J{f»�¦3ní¿†z³&ËÖ¼øŒ�£IÜò/x™^éuÓôm¶QxÙŽÖZ¶÷GcÞü¡�P¬×¤Ùå—Ïþ wßøZ®®Æ˜½[õ¡Ùõ_TËaƵˆ£À+0­Ü4„´Új¨Ý빪½� ª‰S†oy?åÑsÄQÆ£aurÐáCG8:XŸo­9?Û„JéoŽÇwgóEœÄ)ëÍ)3i…­*qU ÷æ(kE=ô÷ïÓ›L1Àx*Ä�VZzõ«¿ U‰Õš7|裲€fÍ»(iÒ#‰¦‹¢‚(¢®J¼VB(ÙfÃÕù9ÙòŠ²È›1 ^fÝ¿úìÓ¢p²<ãîëßÀ�›w��mZk@aPüÁÏÿÅfÉhºàKßñ=b?î†r(ÝH¥¶=‹<Ï¡™�Ä1Æ9(ÞËqxQ)•DÑ$M¬ qØq$fïë³S–çg 'Ó¶©A«™Ï¿3 ÐØŒ­l‚´Ö �Žæ >ù5ßÈÅÑ«>û$Ê�ðèß:îÌÞ$ÏîÆQŒFá�öˆèÖØ‚ÊCå Ô;"2”oHJoyQµ3ÓÜ5O†—m(¥µŠzCqÖU^?TŒ¾ªª>þ1t„Ó¡C'®* œµ1¸{a§ëÕŠ0Jx×ç¾(JàøøPTÍhºG²�ô‰Œ 6âT Y,n1MðÞK9êO~ñçXž�0˜ßà�û$óÛwq­�,O;„Sk-Š)¯J¢4eEÌïÜÅÝ”¢�fþǾôˆ‹÷âÓÆÐ‚Óá‹ÏóË?vŽöNz;Qœ0™íɳ9ײ[AC8»²W3î<ÒûiÈA¶i¥Ñ yžªªÑr/œ1¼øÂ³à!«*®..¸Ód¥�Bk’^ÐÊssÖ¶œu4f+ßãØ�FLçsžâ1Ö¯Z¡êÚ³¾:'ô%išê8íù2ˇÐáGG8:xÀ.öïH†Ã›·nß 6›¥V¼ûŸFƒî÷©ëk …Çc½“ž‡múÒp–ÓW_¡X. ´áèùgåíû¿Ÿ�©�Ö¢¯åšBlBLxD8�G ê"J!E{’´’’[CBdq“­®xå! ¼úÒ Ü¸{¿q˜É1(VÊr¶9O®/ÊÁ6/gúæ9”bÛkÑÞ`Œ%Ù¾°ªäüÉ|�÷r.«ó3œ¢M$²Ø©¥ka¥J$ r!6­±ÖŠêŠGc)^œ�¢Qœ<úÇŒ_ÿ¢Éœö¨èæÓ³ËCS@hvæ€Ê¶ AвK7*†v&[›¬ંì…Ç©——P%Ê£œwoÕJÿ&þß@ӡÿumßÁ¬(2­µáuïy¯¤7ëj£¬|÷ø¦ß`À# ¼k9¯êª’ôãl³!ÒùfM›X´ÖÛ!dJ«íÿé[[cŒ–ë!ÛjÊR‡ôvʪ’›j½�ã„”Úö*¤Ä•¤=é?Ý\ܦØdÐ>Ä#çm¡2‘þOUÐüõ ‘ʱÎo C¶KŸÉ¢[/«›"‰c<\r]±oùÓð~É׸©4­üÖÊÑ�È¥Á{wö¡;¬ÙaD÷Øßý5¯—HMþÿ¢8–çhH„Äß¾…,Gâqš!áòà)æÏ>¾lî>õÓ‰²E0;ˆ$òZÚ}'[H€¼îòBk—�„B¢�è�j MÄ{GXm $I¢²¬ÓXks´x!´„Ó¢ÅÞdoÔI³qLPUÆ¢C™þá oìe«þ �u&bìe(gη:'ÇqƈಚäS‚âÇJ¦‘$�)<%÷i\QäBvqhÉÁø(W>>:¡�Ùf»Æ�'‡‘܉ þ®oBÔaIšò^k!}4Üò¾Ðî_ˆH)EBu…J˜ðÞéMNÑ)¥¤1OEÅ]$Å«�¨òÿ!Ö;|><£NI¤ ÷n�?øU_x7çq Hk¬'¿ô "’‘_žB`—½ÙlÐjq%°F¡7#Ž£,Ên§§Ñâ…ÐæpZ´˜-¦'Iš¾<Ý®“º®18<ÂÞÑ1@õSt™dHŠyF<ò×:Y/¦ØnÑa%~Bq•ça>€Sq*¬€7$*P ɹʘT—œ‡‚’ˆÃBi ç4É~Á�|¿íšµ4È‹“—¼Œï©¤öE‹  )7ÈkG`òžò<‰h\ÍL)2l­¢XsúñäÚKq§|Š÷¦:B•ˆãìS eª²ßEHGI±Dœü™j£ôº�ÂÝ;·QùpµX¼Þn¿YèÝÔ ŠÄOö¡«ÉŽËÉ„Sp>ƒËëÓ9wO:¨ ÷®£ÎW(Ê­].æºÌ·ûi¯wµÓj‹6ÂiÑ"Žâýn¯ßït{:Š ŠXÔYU –«%ÿR¢ÂZN©<$›~HpU•2ÀÂ9C‡ä‰�,�XËz6F9ƸèÄHôó É³@�ô˜ûÑnÐ^,ç"Aª"”‰ô@dÁš9’ºn¤_öÉ­±†Í#u2 1EÜçó¨„Kú¬-¢CA¾\Ò M…Ì ëÞ3ÖÊ÷™@L!éO)E[Ÿd8D–f8Ø?LzQròü_þú¦Î\* "P~k‚üM#­6@^{RjL@("40Ü– ¸å½r\)0Ç´½{1ÿK�†šj­Ÿ~A²iÑN‹ýÞ`¸]¯qvïŽ-Ї�\ƒÓÌÍfíˆDk§ÊrùCⱨ]t¢I³Å”¾aKÇrŸƒL9Áò¯W)[E°+eóFÄ>“Ä1·ÜO>‚ß…,ë0Ú�DH!l `äÔ4$.>Gk!,îs« µ;N]�‚‰Ær:n8™`в¶Ü¬qqvÏ}‡1–¹$cÅ%ÁHZ’3‚|7†÷J}Nœb0óÿß8Š£(Ž'¿÷5Ÿ7;ìâ#3�• jËF„¹ÈVv]}!bù-KPËtšƒì¢•[Í Ò„âŒd¨_öú×N£Å ¢%œ-¦³û¯-«bd­Õ‡‡'xݼ1B0c�Oh[k\-‰+b„Ó'sµ\@Ca<ÞÁèASà�r‰ ’× •ÜÔ–•^ ™D.2P’ϱN2-daHxlFÜ%«|ˈ%Öºqꢤ�äB^#òn—[ r8™v«¹oÝ·Ã9=«8¦½Îz½¢[ÁéóφZ¡“€Ë x?”M8zë�ÒN—¹±Ùlªз«Å«N6ÿö‹/«ï’¥+?¥F’èÄ/�¶é(éÆÂGD•‘s�|�Ÿ®“ß–ì6ÏaLåËU§É-^-á´hјæMã4…Ž4î_œQm%ƒ:ŠeP–i*°Iî$1>ÙÝÅd´CœÆ4’ Q>�£›ˆ�ŠÓv ÷#Wö'·Ž¢ØÉ…«ªqu7Z+hE2prk«€ÍvÃci–qŸ¤èøtàÈ'‘|Œ|#LP7ã ßͦ5g2eƾÇ{û5¿áþéMXC2’è�ßá%äáw ±)¹®iŒäqü;ôlò,�-‹n^äo{úo�õc `µ —`#¬4c= UMP«Óx2 =ÔÒȉ¸�U`‘Sl°Ý¬Pä[ª Çâ,Zü¿B Û E‹ÿ,òM“u³ÞäààH¯7+lÖ+þåÊ<U®R)´ÏA„�Fù�„8Âr³DS•X,æX¬ÎNá:jBV¤Å�<Ç*#ýP´àa¼I+D>_ãŒ=µ–QQ¦Ø*Ó \¯@õ[·€“O¥ƒ%­…PLyñ'¬#€ð;ùžÚ †“:¥ÁëFã Œ}ت¨ÄÙ­›XÍg´ü1N¤à ÊZ-ýxxõŸa åÑ“ñö�±ž] M³ØTõËŸzôÑÞäml·Ù¬°žÞ£|<ï#é� tä’þ�´Ë‹«Á†}K£=¹„u9q¸ÞN#RŠF¬q¤P55ª² ÅUh#œ-Ô (óÝm¾é¦)]‘é€ ºÏ7 á¤±áÀ'SKe€…‰"õõù�tvæÿl>s5;áê“2軼ˆ%„Tã (­‚v$gþ`¿UYƒÇ8EÖP’m%òžsSZay¾áû3JRеGrÊ] X! ’`Ý»ž¼Ç×^ÊA8ët�²Äfµ ÓU$ð8ŠH¤"½†õd:È¡qZº½GN�& †ƒ:Y'~€îŸüÄϼz/­~<žÝÚœ?öwø—ù:üí·þíç¿ ›³[N,`,ü>üo# ð[-SxX'$I4]&ÐëõøÏ8ŠâØZ[£ÅUh §E‹Ý�ƒƒn§×Uv÷h:i�EYæ²| ò\6Xï‡Öàè HÑIôº=$†dàÖöwÉzÙ—EÈ8Pj!/Ãsì–îÍ´Ñ‘û•¿°^v%)IOö°?Ù÷„ÇëÂ6�4£ˆýX(&÷ýtÉ@®Õ0®–Ç Ì¥Y #KXo¹.O¾];²qy0(7­º²+–ò¡$=ëõxïb>Åb1�ªªêËõKz‹g~óm_„O|‘=_�Ö·ažù;Üø¥ïÅß~ËgàÞ¿ý5‰_0†Mga�§¼#x� ˆx"„Ä)5S5ó9 l«†AZ\…–pZ´XoV�±uY©õz�u¾aÞCGZr'Ÿ3kJ‰kÉ{(…N¯�¿òµ8:>¡ý?Š‚It¥t0¨Ê¬Zè¥f­Lóxq‚ #I¤»ë¡à *…üó£ˆEŸ›Í ³ù”£¬–¼‰–Æ+m`%c-s9JrB$3!�“xÉyK6Cë�ÙôMÝpZRì%wä늬'o!O4ŒÎx£ô¥£ÛbËBÜn·§:ÝnR”ùäÃßâ-�šÝü¾³ú“¯[�ß®TS¢UÀÍÅ¿~˧âæïý òù}#²è@í½L¥+—ëñBÜ ùM²¬+hY )†ÎÓ$ËÑâ*´„Ó¢EYæÇýñèZ·ß×T¡èƒ™^D²+6¦¹ŒD(Zr#,„ìœÐ`½�ÅÐDYf\®…Ï$K$/îż¯®j7`S\`CQ8 ó#:II"Ûí†Ê±¢(\ÝÆgÊ¥îLjËA¨N³b›ã‹CkZíHîJ©_üš×#ëô £ˆ®çg€µBà¤N!S!O 4lBjBHVÞÇÀJ4Õ•PkJ²ãnEok#ê‘›?ÿµßø†‹»w¡ÛíæãñöÆcŒì7~êëñ¯ßùYX<ÿ(LÍÿ/~šMÁC”i¾öÆNÑFÒ™?ÿ8L¾F¬)ӆΒŽZ\…–pZ´ôe¾ÙN{�¾É²ºƒ!ë=Öš¸Á¡–”Wð¾g2U¶Ýl1]Ìpûî-ŽNÏïQÉddZ $,d¡5Ð�¸N»ÜX•/×1Ò 9¨0ÉF8,šÜÜ?2cÔâj^”Vh„8p0÷¹�`l(÷Q‰¼58u§µ�„¦l9ét8©•âtâÙ�çÝÈ.â9ZØð¹—rK|w—Ô±¦¨?óûÖ› L_)}Òíô®ÄE]”Ÿpûú�o½wïÎ|:½°U™c”)¤×ÿO}ÓÇáæoý(ò;ω(Á½›ÏŸ=Äm X¾º±à´éx0t×&qj#J [\�–pZ´È‹¼»]¯’‹Ù¹-Ë‚Rß(‰]qb8ÀâRýˆl>'²{p€½ýž ßëc~ÿ|vD’[¤æ?ZûKôâó% ,@,øPn�¢�PÂÌçÇ�Œ÷E:Âx0rƒºÜ€®”WãyQ��CXgœ“~xŸ#¨¦qy¨Õjù -0½_¢:Ò4ãõÒØ·lÙÂ>��u;ÈC‰|è¤%q—eñxœæÛígÍî�}Þr6»¾Ù¬ëù|Š4VØQ[¬ã;ðèâ¹þ»?‰íù-X\ò\³�~B”jlD”u°š�ÛÙlŠ¢Èís�?¶-›ú&Z\…–pZ´H’ôh8žÄQiæ’V )g³¬1Nö+û$‹ÐÉX’í®¸3I3ìÑQx8"KS,§3˜&(Ø”Á?Œ0¬g±0râÖ«Ãl6—Ì!Â×�Dh›íö!Ïuƒ=ï‘ÛQÛËÒïKä IbÜ׿É_'óȵ—Ðø´ÓéB+Mõí}øL+K{‰$Ý~¨4�Hµ]É9$ô©‹u^äýHG—åÈ¥�õ7×Eù�ÛÅú�a1O“Ìj­ÑM5z«Û¸þƒ_ˆüæOÄÝø#T›…Øùøz©ŸEí–9`sÎÒIo%Q^YÕf¹4ÖšçÑâ ´„Ó¢s;ûûý4ËT¯?@"‹T©9Uš@¢ÉãRa­8»Šù¸ÓÁz½f‚{9Ÿ±…yÇAB=MR'ov*0X¯ˆ³Œ"dßJƒC@)L°ÇiÊ_«Í 25ÅfEu:$ÈÔ¡'Dkà!ßÇkià)žn•„ŽÈ¸@\UUÎ-_¯Pæ¹7é´q‡«Ês½8�SuûQ•wƒÑ„BŒÁpˆa â8ž4Æt¯XXï÷#­ßEUæ;n\îÙ;wno›Æ`<bÔCró_ðÜ·|,þò3ßÏüÊ÷cúøß#ŸžQ A~C`‡£¤ !¥Ã1l”Ú$IL’¦ˆ¢(�£x�W¡%œ-ƒáôâÎÝÓ$I+E G�)ª6da ªÚ$±n-‚HÀå:è’œ9û,ã*�0rNV !böåw!8!‰ŠÀü‹»6¬]Ñ~ékæ:&ÜkX€kû–ù!±°ñÏVZ!IRÉñ�T$Úhø~B¥#é×}{oŸ¿»Ý§ÍjÍZ’ŒR.�e=ñp?Œ¶„„ÑX?Ýi:p7uÃéÏ´ÓÉ"­^þéïû� ÇÅ|½ü싳{ïwvzúË·nÝ<½sç4ï÷v4a˜Å¸þ“_�G¿þcð÷_öÁxæ×~ó[Ïp±·¦Ì¡”Xü]«ò<y2NºeU6hqZÂiÑ¢ªª"étôâQ�NWÅJ’Þ„‘CXÙïH¸Ý>Dq¶stD™p%ÐP4·TÂûÔ¥H+Ï·Bc“hƒÇ¬W¢šÓÞq€û*¨•É:ô';˜“M@!Á\Z¦À‚µ>2=(ù#IJXš@H¨‚ï�L!nˆ#e�ív‹ùô> P‘Ck¹Ç�ß¿ìl õA$`X@Gš+ÅP òN”:6M£p5 …÷_^Üÿð‹;wçAÀsº\.ò,Í�&1Ž÷v0Ô%ù9îüÜ7ã¯>ï½ðÇŸõîøÇïý<û¿ˆûOý3ÖÓs”e…¢—ˆ$SJÅVUE>�¢ÅUh §E‹õv•nóm×ZËAE4•\� Ö�AÞºé5ëìü#™úÑn æÕZs™å�ýt² ˆ4צÑZIÓ¾®%P½ ‚c ¸T¯G±¬•# ¡¹bR§øb±êêâ‚×Åq‚HiF€7ì¸÷hÅÔÎÆÇÞq>ï«ó„udƒ‘ô#‰"¬sx^¦öä�ùÁû“è½jÌ 9ŠÏÌþñ övH�|gSÐË׫U„ÿ9€_‰“ô}LQ~ñôÞ½ß>??;Û¬×År>«·›•©Ê-ú™F¯YC�=‡Óßüa<þ½_ˆG¿ùSñW_òAxìg¿ÏþÎOáÖ_ýž˜²6H’Ôh5hqZÂiÑ"M³�­ëuY–M]׸v×E*B.â�y_¿Ñ8Ge^Ëß²ßí÷iyR”ŸWl6Ná&*à<Ó¼p@9™w‰v>ÊV¬ý�œlÙ刢8¡[B¯×Ç6ßðzNÓ)8Ù³\ˆP5¦<)†K1xøcòÿI('¦¤|rt„<ß²_ùö@nù­IK¿ž´+�ˆÑÓó3LgØn7Œœ²N§»wxøˆiê1þó˜øÎ4ÉÞmzïì÷/¾eów›åúùÕb9+˪)‹JYŒ†}ôu µ¼‹t~Šùoÿžû¡/Å“?ø¥(n?�ÍrE"6Š/Úâ ´„Ó¢…Rj£•ÎWË…].æÈ×kP:¬Åë+ô¡DªÌ-kÍ© ÖX wöQÇ1,Õ[CäB8Þ½X®v5/M˜· ‹/ÝeF”Q003BáùÐb¦ƒl8â2 Œœj㣠‡ˆÅ˜þ°vÐâ§G. IJ¦•�£sƒŽc©RAÔuE—\É"²�— #<×/݆ñ+^…AHÛž�É.ÐØ|~óÎéd´£ñ_Gà×N¯}"€÷±uóõÛåú§ïݾýÔfµy~³^ϪªÊ‹<¯óíÚÄH#Pp0P%"Û@´VZÏ­µ=´¸ -á´hi½PJm'“Ýòøø’(âš+q| \��$ð/ Ž"Û¸8µ„ã—½‚×�G#¬/*§hc(R�œŠ,/í%ÒÂ>9€Ë²®Áò¦©Qç[ #ìîìãäøºÝnQ„P$X#ùÉ ˆÀÁ†­ìåG¡, hžñÄyvû–ÿ.+‚�$ W(½D® ‘(Õ.˹µÈÒÓ4CžSE˜Úê”#ü÷a< à›×›Õ'VEñ^¨›Ï­·Å÷Þ»uë—ï�Þzjzvvýîééùf½^n7ë ŒÉ“8©ã8®ÐØëhìŸø´¸ -á´h‘$Ù-kítµZÖy±eT°šÏ±ZÑÐ.¹n�ÒÌ„,ñ¬yÃd7ŽaÎe±˜áô™§Pä9¯›R©£áC. dëh¹B4JþG¸"Õi’PÆ%IH’�Ë€_ßÎRÆ \}P@ â*íÃñÂ`!z—÷òM‡9"™R‹Ã¥¸I8JGÈ‹-É7I�<€Öúÿ;Xø;?'ñg)­?ÔZûv¶1_–¯7ß¾º˜ýôâüâoVÓù?¯—«Ç›²þ“ùtú…Q¤¿ÀZ¼ZÂiÑ"/ÊÂ,fSsq~†Ír�ó;·©€bmŠHŸºJ¥bm èôœv·p°<À�˜¢Å„–pZ´°Ö–QÛ(Ž�¥–ó9Œ˜IBÁýåímX¼Á¤ À.o¡‚�úE/†ã F“ N^ôNs‰”Y?–ûg)!­‡N7ñ7k€.-KDFŒlúI‚~oHZ®WÎËMÈÓ}GkÈ>l.êâoUy•`•e)ý ñI Pw2¡If'ë"MSئf¾GÖ¸q÷(™Z ˆPÞ‹ 7ð��¤;ëvYt™fëœÊ²P:MŠ2ãÿÀ À)€Gü€ëhñÿ1Z´ø/ Ie­]d½n§“u°»s€õlŠª,dåO‰j �PÀ׉pßÉœ-¬#iVÝGÝ.ULÕ6ÇéõgñÈË^. áeÁnÿЦ¶¬’÷=³¢„ƒH°Š%Ql·¸wófó g4ÀÂr—Wñll�#òÄs9§â›¨×.-êæ¶Ö¢×"/ tÒ”‘N½\±øs¼»ÏÜX(rÐ$ˆºOË9F�Ð"ëþwöÎX–,ÝÂßN–y¤>×jƒî±ùlϳmÛ¶mÛÖ=¸}m–�Úïö;vFÅ}´kÍì8unåÉÌÊŽØ«~­å8¹ªv4‘‡t;‰BÇ¡ÓÙZ‰ãxÞB¦˜âIK8SLÑív=Ðïp\ïUÅR©<õ9úö·pðî{%R˜´<Öy½#¯Ô[÷Ê|óUò¢XªpÃ�Cœ|Û›é÷;œ=~”[ï¾!2Œ¦$±Èûæü99d²DÚEîÇ! ЖÄ -–ú†ˆ]¥JžšÊ2ì™é8“†‰èÌÊÅ=3¹^À„ÙÛ„�€’ç““ïÒÎÝ¢I—f©h©Eqdë=aX°&n(,„He>G’Þ¼pŽ,J$âÙ¸xAþÛ ·¶èw»ÖuÔ*Ç‚ SLñd&œ)¦H²$»†·:� íV«G±×ß\gíÒ ÍÆD³‚IWJÀº{Ø5ÖÓÕV[D-çon1ì÷ñüÀÐÒ¹–& �Íuâñ˜íµ«òÓ t’Èûb}mE£!£ím®,•(lÐÙ¦Úl‘Dý^OD2 …¢¤©º›dIÊF÷*~Ðáç¾íë)—Ëô}9f<0�D ¡R©PB¶6Ö¥/ŠA€\W´}JÕš5EÓ™Ô�¨Ô›4ff™_ÝÅå3'©ÏÍÓš[ …„û[\ß“:Ò#ïz»ÓÅqLw{C4æÆ" “ Ù^9qœd<–gF*MÒlu»” @jF2ÔÅc¨uT­PY–U†ýþb¡ZËÿ4 Ç+†zúL;¨.í`Š)Þ ¨a0£?¶²k׋Jår=ISvÞ}«7ßBbÔŠÍ"G^Äo4Z,.¬†±h›òzë?ý=ÇÿóßD9º7±ÿ�g áx¾O2qúÝïDG ëW.ŠÛh¹X–ˆa}c @ޏJø!ëëW(гKçB¡$ßþ×ׯÊ&!�Êñz=!"­µF{nŽr©’« HT–ˆòt’Ärïƒá@6rÏ$ÚBŽë’f&ò1ÝiŽë Á¡�±ÙÖ’“H)ŽI¢XžGX,Q,•åÃŒFC£ÒàXKîTºú<ϳµÆÈ榻ŽÇùsgä¾Â Ë­ö6·þ&Ž“ÏNð CX):dÚÑZ{ ~ `˜04tëÀ¥bù9Å”p¦xüQò «ÃÑðÓê³íO-K3ÅrYäç—n¹™ÆÊ²lŠ:Ë$ÁœI�ߨC7j |å.�?#^þ®ãÐ__çÒÉãœ{ä=’úrš›—t ©CA "—M¿ß£Z­ãùžJfa„ ŠÅ¢lä[ۛʹZmùÛ^¯K¹\’�†’n‹ãH|hú�Ž_­Õ¢Ñhâ8B²‘‡ahÜ4ÅWF6߬ÕB’&r?FØ”’F×ó€Ü–ZgÚŠ~*ÇÌ%IÞ½')4-µ´ ±±fv=_žS¥R“Ï"©A�É, !­µD5AXÀó<¹×ng¿V§9;GÙóõ:úÒùó'·76~Z¹îwOS¸ž¢u˜îvr)™ €PΚŸà©áûG€ßÞŒR€ˆ)Þ'¨çÍ,1Åï%–]×}ÃÂêêg§IÜîlm‰š_*3èÅZ•îúýÍMÂR‰­‹é\¾L¡\–Ô”ÔP²y—J%â$’){À('ll¬É&<37/…óáxD¯Û•�¿T*#3:hImooɆë¸.Ýî¶lΡ B˜YF­Z“óEq$QÅææºR±T&é´ ’škµf(•ËÄIŒØ(dó—ÈfÐCkD`T´Ú|!ÙØã8Bukåˆ0è„s©Ìê µY�í-âxlÂ'Õ¾¸nw; õDƒ��“ûèûó¹êÔku©ý ú=Æã±|n¯T¦ÜjQ›™eåÀAn؉øüݯÿ ëgOÑëv†[këo>úéÖ9V(—´Þ ¼¸xà’“H`^+ ÆfmhâQ€O®²p ø3à÷�Ã@—)Þ+¨{« ¦˜â½„¸Á ÕòwVÊ•[Ò(QqšÈÐd±RCùI–â¢d�±X(J‘Ý÷<”ëJtÇ‘ü{£Ù² žïKj(ŠdÓ‚hÍÌÊ&Úél 9ž'ó$™ÖB&š4NDƒ­^oÒèu·™YX”t[³ÝæšÄ>ÍÖ,CcnVŸ™#N¹n±\¡X*‘I)êƒèº•ë j]Ù½—°X”HB)%çßÚÚ�:�F Ñ ”¤ä†ý•FCÈLf’Ò”õ äsÕmÚíYºÛ[r¿WÏžáÒÑÃt¶»èÚZ-4¥  ä¢u&3A±´R§8ñ 52Ži…“FxÉ6½�+Ô›3¼ð£>Ž]7ÝŽƒçFO.å?ÿö/xóŸü:Kô ×ßöûß–î÷<êÍr­Ü|,ð P4³œ" ü‹!—±y?€$@ÅDA/f��À2P@ ÇþðóÀ¿x¥â¹ÿßó›B=ØZ`Š)ÞK”´ÖŸà¯-á¼RÒ†,’è×÷%rñòŒµõ«2ÄY«Ö…,’4‘oá‰M9i!™8Ž$­�Æò�Ýó)œku‰Ž4HÄTj4Er?N…€ð ÙLUåFCÚª“(’£Öšaï�›¨Õ&…•R(•l'›´Ëœ�#„§¯“¦¹Þ`mýФæ\בºM¿ß½Î³Ç:mú˜“¶Zs¬,¯ÚúÖ¿üùñ–¿ùsN�½DpàAüæ¢ídÚÝ1@¹r¼B£ã1ø5 ©‰%B<"½x.¼ /Ë é�/{÷½øåÑÉШB°qéø?Lc�4Mâ,JÞ].–?8ÊSÃd¢õ €Ïî2$¢€·(£Ô±÷9¦õ*p ðJà6àVCN ¸d¢žŸC©·1S|ÀºÔ¦˜j©-ú~ø™~1üôrµÚî÷ºÒUEcã¡to©$!N5þ("êõ(' ÛE ß÷P·Tdv÷^‚R™R±Ls~ARN…rY–ëI�\ÒhVÔbàä©£Ò!Ötb@ÓðƒüýÒŽ�ò“\©rµh!´ÆÌ¶Èï†`®÷�A“ı±rv„4sŠÒä° ÓBL“^6�ÄkÏšR©@¤\RíÈ{®x™"!–LÎ!³EA?pI’ L3Biè\&t4q”²±¾Æ¥3'%"U­„h1õ©n¾•ÃÿøwTÊU/ö¢ÅíÎö§_ÂS …ZEeQüàS€½À¼‰\þøM”úO`Èû�3fý JÑúvàµÀ‡7˜ë¿­ÿ ø>CtSp=¼÷"œbŠ[{Ãþ·¸ñø¡ZШ\¼x^Ke=ø|ª33œ÷;¥VòÈéuFźÔ%|/ ¸\À©¶ÉŒuAã<Ë3ŠV«ÅÝ/y³KË A£¬%h+óâ8ΤÍA–ŠÒ%fçpÐØ¡O�†¢q$köìÚúÛ`• Œ%€=(Ë&‰ÌN÷+‰Xu}�@®s-¿Kôf.A¯ÓáüÉã {=†ƒ1žrl­'÷½Ñr\eT ©}ÉL޵xH‡=tg ׇD)’ñHÒ|®ç ±2–”$ÀÞ[nåÈ¿þ™ÎÔ(ÕÓ4}±ã¨�À)ž"‹Å]Y%ð cˆæ€_tÃàØc˜âÿöèJÇÑ�|°|(ð€i0ø‘÷¡66•¶™bŠíh¤¢8~–RÎ�Tjµ—”«Õêx8«\çÛM­çó a¯Ÿ ü:ð`ž)þ„3Å—Ç]Uñ‚‡|ßÿžb¹|_£ÖçYš_fõ¦[yð¯­é]]ÃsF£¯ÜÀõ 8~Hœf¢e&JË@¶y�º3¢ú2h‰Z±î•6Ê0¯s 2Ð�Z§MmÉEfY² ÀúßH ‚²jrýIâ±~<æ”Ïਠ¥£of4åPSS1â 9Ù(r¨ü¼—Ξ!�ÆÒÎ�Ʊµ´_ãPšŠ�@‚IËÉu#ÑXÃu*�]|„Z¥*Ѥ¸†z¾5~•sob«�Jš�Ý·Ü&C¥Žëª¹¹ùr©Rùpå¸ Or¢©_[ß|;P>F¹Î™Í^óø#5×þ àEÀ·™žƒÀWß ìaŠÿ�p¦˜ÂCç¶C·½|yÏžo©Vëw~œÄ2}OµÂÝ/{…à/9B$‚—cp</@£q©×˜úCF<Ò»pßIÅàìÒų²Nh›)eRYù7|×qð\Çúëä…[ЗEnA�ûÄxž(˜4Æ»Æ ÊŠcHNOZØóÚMÛÊÉX5lÛòlÉòt𯕩Iûõñ÷<,µ ‰|\�$Š„Pâk ð=@YÛßsQŽ�mÊP÷Öñ’Z§ÒtÑénK3Eçò%!ziZÕWöÜ|+Êóä¾¶¶7�n¯»w4>ø$&›Óöy@|¯ÔMà�O¢9¢ À7÷™¨+0)¿_>G9ÊãzL gŠ)|?­^·}éÊW&ýáÝ�¦^\וî¬þxÄ=¯|5�¹9¾<úÖ73êuè÷úh�|Ûv�GޱuÖšxÐC�¨,!‰ÂB‰J­>¡(­³ëÔže¥YfS]±í¼šŒT0u!‚Ll®­à¥ÆœWøc"z²'0ç3¿MÜ›RŽÑXÓ&ÒI édäÇʺÎ^ZË5R!��«—Y;sZˆØñœR¯\³ä¬‘Ï‹çÉõÐ D¤Pää†`´~…f8ÒëuEb'! q†pe°Ôe¡\áÐs’h¨(Wª…jµö±¥b9|’Í ð&‚èoP®óƒÀ6ON\UŽúJÓžýÓVý:Ó?ì'Ç”p¦˜ÂqÜJ’¦ß>�¾h4Þ3 Ç‘‰zŠÅ ·¿øeÔgfdóÛ¼r™ÑÖ–Læ{n@±>#jQœÇ&õä*D–%‹)ú.Ji4ȼŠ`:±R#Æi£ +ŠI^ç1––ä=ÌßäÊÐ(9ÆnÌŠœ8àW€›Œ ÏG»…ðÏžê‰é–{ ’n¼Êç‹•ç:S™bÚöKn|�ã¹/'ã{², zƒ.2{2è3{`?»n½�,Ó²�]>yß÷äu¥ø3+¸âÍ"i Sä–ºéöÒQP2™¹.~XĸqšèÆ:eZ"L«s6q�§ár«gûZ–yc)€ I³üZ™Æ0›f³çÌÒI¹$Ie53¡fó·d˜»˜ÂdºFc±xfýÊeKnI†D8Q,u)㛓˜(Ó3oïÕ¶I+€î¾Žèzáh-ÑŸ¬ XÒ¿�bIHHÒk¦!‰ʵËwÞE¿/ Õ…BI£?³X,O²9`ÌÚnŽU„xŠ Yv”ë~#ð©À1`ø-�¤_…RÅg.áL1…çµw:øƒ7ìÛûajÖ[þÊòZ�6¸ó‡n䎽D:WÑÛÚâÔ;ßAš¥"Ñre«KÖЙ–M]ړ󨂂ë273ÃpЗMsç¡›%ZÉÓQò:¯‹ä0D¹Ëf^;AcR^9A€éðŠã‰KR rKh¬_Ž!!ÓÊìäó3òf>#zq&ú`âš`Rp²@®%›~wk“Ãoy#ÃA�n·‹Vžtc#%¹¦‰rŒV›±*ðm›£4áÖ9?�FžŸëÚAO‰n²Ì¶’ã’4ÁxYáЃwÝ+iÑá O1,x¥Rù9qÝú$ ›Uà·M ê0ðÁÀ;yj"þÚ´Mÿ!¾­ XyÆÎSdi¶/Ž£o̲ìù'.«khÖ›"mï!;ï¼›g½ò5”«5ÙÈz[›ø™ݯáxL\i‹€R¦Óм58tñÓõJ‰J¥*‘C­Þ0ût>ƒ¬‰ÚÌd÷Úl¨y$dq}k²Êôíùä:(£€ÝÜ(óZåD¤˜¬y®lÞN�¹�üšòÏ% —ÏŸeãìiùü¢]¨â–¹ý5 ®›ÀD&)]v7Ù¾pŒnwKÔ24ÍöŒH EœR �eBèžïÉÜ’ëzÒá—(W«,Ür«¨`�Æ#¥<§Öï÷¾GƉGÞM,b JºÈÜb¯P�©MaYÖåTHÍÎólœ‡xh™­Öl1»°„gì Ú3s 3ë* Žšˆ˜4ZdˆÞ}IKtä{¾?»°øR¥¹ã "›&ð�F>æ,ðñòóéƒ>ðeÀï£ùöýÀÜ”p¦xZ£ÝžSÑpt÷ꡃ?]mÔ_{õì¹¥,I1%ó\ÆqÄâmwpï‹^*QŽÔL±þâé“\:òˆ˜”•« JÍy‚ƼlŠ®lÊùæ-ñ[¸|æ„øÓˆ…t’P©×qòcÛv¬óé~ò&r’MEN*Ž9ÞösfòHɰ€¤¯rÀ ŸbÛ¥5æ\røäŒP–¦²L 9—"�%–ÏðØùÐR»9òÖ7“¦¦^£ÜÖ ˆÊ@Fl-$ †\“Ô<—ÔÔ’¤Û.‰Pk§P@¹Tí´öÂ2~£)]„Žãˆ§N{Û6\x¾O±X6VÔ �yîsË7°ãéÔ*5!¤0‹þµ:^šéÊãJ6%¹ûa#3¾xO?l£Ôç™véx9ðY(*S™âi‹«—/=÷¶‡žû½ÍVû¾‡ÿã?¼Ñ``§êË•‹7ßÂý/y¹| Î7t%Gþóßñ]—BX¤Rª./@~àÙÔ•ü?Kh•«¹ŽçÉç±�θ‹l¡”&Ó™sósŸÇ®C7‹Ê5À`8GÒ|ÎÈr ‚iึ�(Ô%ìùÕ�t:Ûbû0ôÝÀóïvu?�'´þt3µß1šdÁÓ3³óVÀ>Í+žv³:S™b�½^¯û¬•÷JÝäá‡=�f’ûo´Ú"£_¿aÕÔlª×©&o\¹Ä•S'é‹pç6£8ÙÝešÌ·t« ¦’ˆÍ+WvûF¦%áÀ=÷ËFgˆùi ôÊj–aIÂ3ò¾-~ÛúJ¾a絚¼XŸ7¬ÒAOÌÚ`;Í&ÎeV~�¶Œ†�ÆP6M•ÿ�•âI…@Éñ'¿K¢;9V+œb”&""‰æ É ��漜ìð†[dÉÏó%æ†V÷î'�"ªµ:ÅrI4äD‚GÙ&ñß)È3w@A&­è©\sÇÁCççA)ÂBˆrÝj–éOÇqø8¥Ò^l$a|éLSê‡xúcCˆêÀWêLß꾚ÎO “È *åϬ̴¿m4ÝûÖûW¿³¹)yÿz­!-´ál››_ðJÕ*€%i»�8õŽ·S-”YX\Á÷ då6^©jZ—5¹ÜL&K;èxÈxÔ£ÕlS­ÔYر ò¡H«¦r’±5uØT×dÓ€g‘ð=/ÈIÃò…à´ºÀXžBÓäõ)ØkÓž 6Íe1ARz"Jt»\|÷»rCµ „S›#Ó§ óZVb"§4W4@' îú¹÷J¹J³ÑfqתõÛkW-±ÔÊ<ß3ÍÒÙÕ–ÒVí8¶!"ê®Ó¹z†8‰l[øîÛï Roˆ±ÛÖÖ†ØjFC½¾¹§ü3–JqLE mß¶E|Ï¡i..‹ºv»5㊅¶çzŸäûAõ1¬Û„À÷ëç5àk€#<ƒ <÷€2àƒÐúÞ)áLñ”Ævw»V¨V¾~×Í7½!‚ý½µõÐAÉ0gœÆâvÙÜ·Ÿ»^ò2‚BÀn–V3Lk®œà‹�1ð]ÀO>ƒ3;À·[¦%üÓŸrµœ)áLQ(”VZ³óŸµcÿþé÷z­ 'N�œ qÓlìØÉíÏ�üžo›“{ÏõD¤³¿½…xíNXÁŸY6‘Ž�à’r Qe ­€V³Êx</ÿZ³-ÖÑ mÓ†`ä'(&‘wŒÙÇq'ˆÅF&¶poë:×G*®ƒk”—“"“ÚLÞP`HÏžŸ¼}:�­}´¶9€ý}bTºÁŽ¿óaÑ›ÃJm~IZȵrlË·ç¹Áx®+×O–ï¹6½—t×é]:.ׯW뢿6»|µV[þÍ é2Ûîl1èuY¿|Ѷp+e£D©ñÌÏ/Úù¡Ìh®¡”¸®VWo�g,tŽ*d:»£Óë|h„vø¡P.»ÀçMà?LJi›g0”ãüð«€|^Å7N gЧ|·¼ó¦CßrÏC~C¿ßßuìá‡)úÍF‹ùÅe ¥"Í�»$�–JdY¾Q*åXY—B¡ PW9"¤Ec±A¦Ø@…UÛj<ÇD‘Ô4l´âFfš3´ê Io ú=*óóù4žêšÔIË /¿'�¥yg™�r&ýl\˜$ëÎiõÔ2y�%0˜TV~9™ªˆ½ßœäò”˜YbÙpúá· Ñ™�NÜB][�ú� ¢-ðjãó]Nt˜"5sH§�6ðEÊqÜ)áLñ¤GoÐ/åò7·gg>üøáÃÕ˧NË?e(0Ó¯Ýæî—¾œj£!›a¾iç.žçKÍáèÛÞ I"¶Ð¥Ri…^ØO¬•͵e!=Ùä¥&ƒ› ÙÜÜfÐïƒRø~H{yE:¨”qóÔæz“©ýœZ̆hRBJäv& Ë’ÅÄ ŽB&ôå:€×´db[�]O$nd �æ~7&ŸÍ¦òd3Ÿh›6Ñ�U9¸zñ<ÝË—ÑÆ*;ñ«\ê¹à—l:Ð�¦�´íðËLúM™Ï˜&2{ã9Š8ŠIepT3³¼B¯ß“¹Ïs©5ZBìþµU©Ô$âAc?;– *Õš ‚éK¼³‹K´wìD)%ÖnÏõvëP±^}µ õò¶ùz@Ïûg@ÃNœ0Õ#àA�eL gŠ'5FÑxy÷­·üÀ�›nú„“Ç�…kç/P ‹’Jq<�õ�5*7¬rß+^-…b�9Nnö’Ã$•¶qå Gÿõ_ˆÆ#"q‰ôp 8íecŽæûžX$+DZu™ž�éo­Ë¼N"›dF£Ù¶×q�í�²ó7zÂÜ [ÃÉSVq_7s£TNN 0 ²ùÊ>k£L÷œ¶ç7\b®k®i ì5Œ?�|F›b“e,OÃaÛ�¯œ;Kjfb<ß'l̉�c¢ÀÔi$­e¯éæÏQŽËú[¤[—p%Eÿ$I¤~Óh·¥¶ƒD'¾ÌÕxžO¹\¡X,I P ­2¶%ð0(È}iu0]uâ•sàÞû)–*BH×–ªÕj{ço¸áÁ¨ß¿�÷ÞmtÅr²™B‚÷˜(ç“QÊŸÎOJÄiښݱãÛ–—–?¦»±Õ¸rö[Û[Äi,ßhkµ;n¼…Ûž÷|„lŒ)™ÕÝ$�­Sæ™w>L¹X–Í©T(¥Š¬>�ãû†@�Mɹ®ì⢖ô¶Pdr.× ÐŽšL›ÙQ{ld`v}yÏB3� ±LòÈÌÀÈÎ&2²„b Ö×I–« dc^ÊUÈtÞ’�B~Ï£³ Ó6Yƒ^�oütfÅ ¨4ë¸ÕäC¥ÄF�ÓÔ©òF �Ÿn“Å#C®H�¥¾¸ˆ„|†ƒ[WÙÞÚdskãÚZg4J£Ç¤ú6ÈK�ü÷lµfѦŽCþü¤–ã–Ëow6mñF½ÞCË7ü‚áhX~?¢›eà€ð#ÀSL@¹Î{€bà~´~Á”p¦x2ºt Aá víÚý¼õË— gNžBh7ÛÌÎÎI~~vÇ.î|ÉK)T*¹Yvó4i¯@R)’ºpž‡ÿþoeCS¦60t‹P_@£ŒzsFl¼\0q,ž7¾Ò„®’”šçz‹Eá䃛VnÆïͦ8©­˜ &µÎä§]&�–»zÚT›í@3mÈÖÐ-=eBìÓ�•xÈŒ†CiAÎm¨-ñÙÏa[¡�¥·¾Ž2µ‘8¨±­šx…’Q°©.yvqœØš‹i¼°©½xó�§ˆÓXÔ¡+õ;o¼Y¢’TR{HDÓl¶˜Ÿ[`aNš¤KÐÂ’ažr¬×›~ˆ©ñØ´j¥Ñdï�w³°°ŒïÒ™X ‹­Ûn»ã¡™ÙùÕŠâ}ÇËÀq“:šâzdÀß—€ðz¯TPS™âIƒ³—Ë�^çËæw­~ø©#�,�‹»÷Êp©i¹¶ßì-4hr(59PG±}�‡]¹$ 2¯“ëµeyMr ksš »Çurß 1#d'¯Ù°%Òñ=O®£”CÍ™GÞƒ25Bg¯öÁ L»s –´¤»Ï.C¢v6[;-êÊÌ6UªuêÍ%‘ÊMä*Í–({;Ž+‘U¿×cýÒEéhà o¶p¬f\„Ì´çÈep0éÍŒÅ={H@†ukÕ:n’�Ç+7>pß§«TϾÑÍ,ðz ~ñ‰ªÝ¤ãèÀµõa×Ö'_[w^[•' ²)ßlÒ‹;�eù}•Nb�ü¸‡�¿Á�rì…)áL¡Q•Ù{¾¶ÙnôÚ…‹•h0¢\ª©øÅP[ZæîW¼šåÝ{Њ ‘H”%IÕH®Þhµf8}ôg~kWe“E?ѤÕy”ÊÆhˆÊÓ�džc4ê][žãI+³v”¹UžÂ³Çç:gy:+—º±°59þzLÔrB£×–+'Hä`#!Íd!Ý’Ž,È KåEÛt›ÙÁH!˜è¬Kã„ão~Û[ò\ã$å‘W¸2òQ®gÌ×2!smù}ÇŒ£ÄFiˆ‡}Q‡®–J´›3xž/ä–Ë„¥2¦ÃMH¼6¿Èh4¦Ûë°võŠÔÍÖ.œér#Oýå •J]:Òòºr|£=ËÜþK%4ÐénsñÌYwaqñÞB¡ôPµöÿ–¼yØ\ó±'†lžcêF?¦µþàO�¿IÇÑ·][¥Ç‡k”‹ÖŸ|¤rUê|í•|…©oéLW1phGMàùS™â AwÐ+Æqô9ѰÿâÎúú®í� i[îõ»2WsÃŽÝ,ï;È]/y9s7Ü€©E˜4ŠÎ7V“Ç/‹²iÍÍ.H„ò�ü{ô»rDº–ñë x­å¼É@c`¹+ÿÆ® U.²k犜×ó|©UçæÀq¬)YfîI`¥ý³É³ñç2,“×$§¤‰MTa¢•IE‚üwô„Œ©‰Ðld$µí«}–ÊgÈ[´±@g�a°¹É ×•4T?†$¬ã6&fœLêÌt©™nAò®=!ð,&ˆ„¾/ÿø^°¸ï�Ÿg8Š „ú¾/ÇIŠ´\‘ûR¹ÿœ¬üyŸß7µÐö aavß|+™R8ž+_>¶××ÙX_kÔw.}Ü¥ gþ�¥„ºñ¸9Ïã óAß ”š‹Å¬\«:×~úÀ ð)À÷dqâòCgÙ²QXp×-¬¼ê`µu×rM¹J3À+•ã*C�e`Έ™Ê3TŽr§„3ÅãŠL3‡Ö_]¬U?²X(Þ2 Ýh4¦R©237OX*Ð�#öÞÿ�¹9+i�KÝ®ëYý­%±ð9qøÝ\:z˜Àó)‹²Ñ.ïÙKyîTPÌU\Ež¢QF˜Ò•ŸY÷;dÑå:ro�V›j¥f[©'¬¦5(˽k°©­I¯˜ìvÓÖ2˜iÉÄv][ò¹ÿ ‚A�“ÏÁÈ2§µPfÈR©ëÅCAsü­o¶þ5Y’B¡†ªÎJtã˜î³À÷ÈÈçm2!7RSÓ£Ýí«\]¿Ìæöq–€ç–Kö3æ3EŠR±$sRIsåâ9Ðä6æå�uÛ`ÑlÌHD6ñÜ•CkaA"å¹ùEú‡\<{.³ÿ–›«;öïk¶ææf”R1ðR�e«<öx5 ΃õ;Ôó¾ê•zÿëoW:Ö0¾>‹ãÖYö+À¿bÄMg�;u¦ožÎ�¶:[ýzù'Vöïû˜b¡xc¯Óuƒ>ý¡,†ÑˆXÞ�Y 'O¥Èæá(û _ZŸK¥²¬¹¹%©Iô;Þõ�Çâü"µJ]6 B½ÍÑó]ú… —“IÌ ˜(Mœ$RËÑYJõØÞZçâ…sò ÜuVV�õ´“›¦�M§©Ii«`HÅh™ÙèÊ“Ÿ�¼DnÒ}6²ÁÌõäoÛŽ:Ït{‘ÓÖ9£4q[ù“ÂV6jÑR3Ù¾xA^^ˆSiãÖçä˜ÉLv¸Ó|^£¡æã›î:¸-Ò®ãJ$:3»@»=K­Þ0'ѹÏxL¹,óR€#Ò5:‰ Ñ0á¦jÈ]–1d“gfv^þÞDa²×eçmw�i !’~M4:ͼF£ñšÀ VøP¨Vày@øåyóÄ`Y]CXÃJ«Éû·ñ™_û5ªP*ú~x (òØbø| ÓãÔ½é£î¥Ø.ë�íÓ³ÏßQ@)€’i®x%°è:î£ÿw� X>vJ8S<^5›Ýa¥òE�zý…£noussÝíu;„…³ó "â˜(‡_ø"–wïe67³Ùj-…`»™†a‘f³ÍÜ좵O~ø�ÿÆÅ“Çd€pkkƒQ4¢Úž“v^åäï'ë!%o|×5–ÈŽÈØâ›ã8Bk¤öà˜ðDånŸfM*-k0]a˜ˆÇÀdÄFN\5Ùý–“‹\Ï«&(ì9µmÁ6ç%O­ÙFƒÌ£štúÌ2®œ>M,ž?J]ª¸¥A©"ÏÄÚ0LD�9)‚�¤m™&R-©”+TËU™m E‰b\Ç•…ÒRWªÍÍ á;žK! ¥î†!ƒn#’juï4àXα-à2—cÒ–(åØçÖZ˜g}{K¢Ý(‰Y[_{t…+;Wª.;’ÿ:I*ÀK€Hº±@óÄ €RådóKKÒpqåÂ(W ‡8…R§ylñ Lá¿°³ê®Ü½SgYª*³5^ö†ñ–oØ_w=¿ê¹~àxAج/VŸ}Û+ÛíÚ|ËYr!-M gŠÇØÏ¦s‡ ½ïZXYy]ws«�Fè4²©Õ´fgiÎÎq÷+^ÅüÊ*À¤54ùFg\!%ýR«5É­£/ó–¿ý+º�-z�Ž|ÓuƒkÝ1ncQ äq$rúÝ8®" = ¡/›R%’‰Ç€C2‹Xh¹\%ÛÏL¶Ošª‘ÈDç•ëØN.s�1†øR[s�Ÿ&ŠQJˆÖÖD²|™ß%Ådÿ6?.?�}rÚ‚ë²VhòèÄ—lì'ÞúfˆÓh±\Å)Õ Ô´Í ¹|ŽºÎ‚Àt÷JÈÛIFÄ›ÙÚS56¶ÖeÃކ¸¾Gnü† £f:£T.õ�Ng‹íí-z�-+£ƒ�Ü&ž…½·ÀEÇ‘½G×óÙyç�h�Ø! 5ç�¥ª»üФ7˜ç¿Ç]&ºÙUè'w($픤á¢H�‰Æc=Ž2à�À˜ÇN(`�rW_zˆÊB@ûC�‡œð­Ÿüƒá7|òOT>¹ò½Ÿý«…Ÿú‚ßvßðÚÏSâP®Ó Í³lM gŠÇ Q’ìô|ÿ]Ç}a2΄n Ê…²lv^àã t‡CVヌ¥]»Qõ‡<‰cÒKÒ[(”DÖÄúñgoºF6q·+¯?�Íoì×V‰,(!¬'›?AàáJñ_ʼnI½È5‘"ø°ËÒ|ƒx<È1B¸(•“›9ÆU.í’¿§Àµ31rÌ„‡Ž\;‰óvn€ÝúyÓ¥YÂ3s9YP²ñÃPÎm`¯glòºJþL$b‰.Ÿ¢V+5ç T”/ µf±¦†ÜéMŨ~f–Jl"’Ä<;&MíD{þ< qJ ´T®"·ŸS:Þ–n¹U®]ªÖäþ/�=Çh0¨gf^8ŠÆe&wú"ðBÀþc,Ö”9­¿øàg€@ëŸAëLGãO— Z©‚W*ª÷s³_ÐZïö|¿�e™ |�«ç/püádZ”R§€w>ƃž-`�úå@Íš—/A¨¥ÓKŽ?öA+2•{‘”úâÒEý[oþ¥l8èu� 0QwN gŠ8²4;èúÞwų]Çm…^(R®çIZcqÇ*µÖ «wÝÅÜê*ÊÖFòÍÅB)Q¨TjÌ/,æ¾öñlyç¿ü:ŠP(?™ûÕýÉ ’ bº¸0 +­ïºbI ¿+®R„NÊ¡;�V™ËæÖ&^©„RŽ•ÞÏ[psÝ2C”6úPJYý4!²I5!>[gÁü4y´“Éûv–…²„F~hûSH^^fv÷¶)5ôÚØü(Zg2>ïý,èïUJÍø¾xžÏ±÷Pr\Y¶?ü»7 9‹Q%FK2CÛnw÷´›™y ‡™yú1Qуafæf3ƒlY¬’J¥âJ º_~gÝuÿwÕ²fúq{žïZ¡¬JŒˆT�çì}öÙô(÷}îó\:{ÖtÛm <¢´n±eåIZÉûÉ{Û÷ ¶× ~¯ó?¾Ú@&x»­TuP†mgº<¤Év²8£=ÔVOÞð$_ØýyV— S˜ èÛsh€ñÿ¥sržœçW?IŽl;¸ï'ö¼ê¥q�f}q)fb|Šzsˆr³A’ÃÌ5×2½k7¾% àJ!6XHV3ÔafzN¤­ÿ_I Ž x‡S�>Äêêë«+òº‰¹Y&§&)šSÂ×X{~y]’f’Ù8îÁf'�Íp( ’N›öf‹�õ5VV–�‚µöG+ €Ùj¤©åÖmŽX2éô÷$ɨpàa³y�dp€*#Àµuìr€åñ^PGJd6 Ú׺,bþøÓdýž B“ûâ:ÑøN´•‘[±þXlqpê8|ãÐ"Cµ—å½µ³P¯7Äu;ŠKV�W€ûå¸EI§¬òN½N�À(Oyçoöøx çfÏþs†ÆÆÙ}Ó-‚J&”†œ=~\•Ê¥½�¹é·&½^ :2¬€@¸_iý$JŦ(¾ø¯Àë\¥•š µnDZ7·#¡V�Ám-Ðz\ÁaàklöóóÀšÿþU ‚@ƒUo6˜Ú±�ùsçȳ<ϳ¬< ä[šDç0æ7€_þ%ðKó EšF|‰ËŽ8bòü›eÿmÉ0og<þ·©µc+LœšPZ)ò §WíqúÐs×Kî6ósóôz=SôÒƒª€4_{MQÌ=8ϯÿ%k³Ý:ÒOzì´Û_1P¡5ûÝžXÃl´6èg}ˆ4&Ämj»kðzKpŠ/¥$³÷€É©i¢8pe”^§Í±ûïfùò™�Ö•KÌlÛÎ3Z„Í1œ@ÆàJgZk�Ü׸Nv¹-Ðl,¯099ÃÔô,Û·ïbtl§C‚º Çk¸ X>ÅIwA:æm“¦wk¬9e�[Rª D"¬´—½<‹ NùDº}?_šm¹l¹0 O@€Ì Z�ŸÇX!Bn4ÁÐ4•ñm–ç*püàªSH&k[îs×òÖ2�2rþG†F©Tª"PÕŠYç'eK¥•Z�áÑqŠVkSÔ†I¿‡ï„€WfÅžû-ͳҨ[¯5ð³N@öeßµ×KÙml|BÎ}¿Ó!Ôáø-·Ýþö±Ùm7Ú€¯à7Œ1Æ|ø °M+URJ¡Öº„j(.©ÑR™‰r�Hk"¥U u(ÕÐJmlïþÔdù× Þ;âKZ’¥”Œ17—*•©¸TÒÛvíb|r’³§N‘fi×søø°¹ø¨(¤$«r  ¼Òæ¶/­ÁÓLØãýðs(Þ¥´j ¶¢wq3yèß"ÿ“7ý7þèW›µÎ­Ñ¶yìæÇÍãG7½r ¦±½É¾w]êRÐD‘Yð®{þ žœçûlÖkE‘K©Z½¶³±QÙ\[G!\ޏŒÏÌ�¡ØsË­ÌìÜ…Bg"é_‰*”ÜW­Ê(aé¯�,Ü|˜{>ý žN½ZCÄq‰Éís\\Þ`)¥ <žÃ•dÌä=ü`æ8…8“白ùùyášÜU=®©’ÂtmwŽx4®«ô³ýÝ–Œœ‚, ÁàÎ�v$ (q1׌¯€óÁÍÝgËoîç"Ë89ànJJÓhÉù6A,êT9�ñÍE]É3ÐØÍɵ3Ë ™<'T�$ ’Ùb_CÅ4ÆÇ@᜷¶¼R®ÖHò\ 7…‡1†µË‹n”´sµ<…S÷žD[ÉBÅø™ žx 5Y[]�3…áøã�1îì¤Á¼2ŒK1Æì·�ñ$ðŒù.à�F TP£oš˜á»®½ÙüÌ�¯-~⥯)~îÎ×ñã/y¥ù¡n3wÎíd²R³|˜Š¥Jqì~ c>hòbøKäNJ öA0TªUÙq`?—¸xê4½N7SJ}Å‚1¦lÁfL²Ø­C­KÍr¥6ÓlèR -èÜô¥”ðlÃæe¥T&øièPëBSäÝ$ýØgÿÐü§?øçæc36ó;æ `´Œœopà•Wq³¼�!Ù°·“ÏÎóëj­n¬ •J¥=42ú®í;vÍ*Pý~�õ�5Âr‰û÷c¢ˆkv5;^åzó;ËŒ �‰1#(ÏÀRa€ó§Oðä]Ÿ§ÅRúÚØ\§_¤å'W!¨�`0ä¹/Vî3 l)CõûÊ@Þï‘6á8”"é÷$s²Û•Åðù&›eøï©½>™Ü^Å+­‘û½ŒAyýFæ;O[‚\ã7›‚»õ,pÀ•õoß,�ä·Ì :Ïòüy9>Œ?4U'žë‰G¡ãnüÒ¹Y펹AF¿%²Ý~¿K»ÝBG£¨5‡äÂÁà«åŒñx«”ÑZ‹2ðòå-±raž,ÍØáñV.Þê¾]qŸ¨Vj¾�+�ŽLÏ ËŠGÚkëœ|ô±rÚé~[èÿ|P·¼ÃW[¾&Tíl ézóæoº­Ø?4Ê# óêož9¦>sî¤y|i‘k&¦Í7_}cñ}׿ÀÜ13G#Š)ŒQYQT¥jÖæƒ(êüë©Ã`›‚JµZåðu×ñù�~Œ~»“eI² DþLÄ Æ|µðFpƒˆNjqxÍw¿(8ü-·Zé�#JkÅ•Wú)¼(”RñôÄîò[îüzõMø'æ þóê?ú=ûòƒ‘BEÅ`=vì æc¿ðjãÄ:íÅ–:õ‰§Õ=ÿõÓ꯿ý·ÕÇ¿ç�tºÙ¯[� ½TôÆççùõ?¼Ö6×K þ•ŽÂ¯2ŠñsgN©µÕ’¤/“9kÍ&AµÆÞÛî`zû.…IJy„º‘ñ�zCJin~ ¸²ÒÒÂù½ßdyþ,½^�Í�u PŸæés«#s.+G¾ûÛ¤ÉÓ–Ç\ ÈÕöF«Å… çå*¼�«p§ Ã÷Jìýrk¯øµWúÁñ<~ƒ¨ò\ðn=Eš`ð4=ÁÆ?.÷™Ê5‰‚LÔìv,N:û9çó'Ÿ!ïvä÷$MI‰(MíAU®Ç¦(üál8‡lCa¯YtÏí¯]“€)¬³A %µ8ŠƒÐ�#­äÖetÍ‘QqúÒ_ �pZ…ëMrH�ñ¾W×ëöãã’»s„¡XåI²5ûÑF™ð-v†­ VJZ)5^©2[o˜��;¥~èóŸP¿øøCêoNW¿òøÃê§¼Kýó/~JŸompdtÜ|ý‘늛'g¨Y>©0&Pƒ% føöˆ<7ƈ¢x"Ð:ŒJ%jµ*çNœ /òvQ+VØðà«lfóN Œ±uúOñÌß>Jžfoɺ½Ûò~²Øg SÚ¢¾ûV›: ¢ÝûG?ôÍÿ–¯zõ7òò‰W«#£W«í·î6¯þ·o+޾ý¥´Ö¦0æÂ§N˜O}àOÕg?ð—ê³?ò<ò“ŸáÒ'NÒ]Ø,ò~X )ìmªÍavÞx3“ÛæÀ³âPps\ÖA@\£§§g=²§ØÊ3þü§Y¿t‘RéPJAs{÷2»cÑÔt©B‘×ßâJ>YîÈý4“–´• Ö(Såõz…N{S J• ÛwËXäñƒ�Ûr+·ö<Í\)Ë�q ;[¸°.ÓN|€O”cpý0~ã£A>Ó:ÇçØà,Ê3�ϰ�ž›\<þ4y–I)2#‘A«É½VT‘ÛQ™d‰VÆíkEn;þ+ǶÞj(…H›ËbSã&Œv:Oæ€Z£¼ì¨Zoˆ@§×�ÁPcˆF£&Àæ›úÊ4¥‘elÖ¥´²ß%Ôju™"jÁÍÿê¥Kî}t¨©�Öå8*bË5”¬igC+%GVCžXYâßÜó9ýûO=®Àj�Faäç€s­ þí½Ÿ×�,/ª±r…·í;dFJeJAàõ–?hŠâ…&/®ðw(àE:fŒ‚‘±1‘ޝ/¯Ðï÷7”R—,’+¥JJ«Èöb)5ØòV¢–¾pV­Þ=¯(ˆ„ƒQê0ð1àóÀg1æ>›Ñ\U$é”5Í3yp[pý׾Ь˜ež<ý(ýä_³ù+,Þ;O'í°÷UM}Ǩü‡Í6ûjá g9ÿ‰gLçÂF–§Y^äEf ÓΟ~ x(žçpž_ÿC+*•u¤£×Ž��} V­6»­¶ªUk¢(›˜ÝÆ®ØTÇ'¹ù�o'ÏñØã¤Ä„AÊ"}ž˜˜&”+«ÉgÙûŒÁI¿(å鹓±Çq,£Zí ó�¤ ¦0öµ>‡Cã¬m¬±º´(r÷ÅÅæÏŸƒ-£ üi§øÒÊ 6PÃØè¤œOWÚLS.�=-Ù5@¹^&®Dg†‡öâK])´]•ïDÑË3.vZäÆÈg½ïÀó]7Üj¾ûú[Í`(0¬'=~â�»ÔÙÖ¦š«7Í+wì1€€ŽQ® :Š”ÖTYTŒý$šƒÛÉÁ6”uºÓJ©#A6+Õª´,Î_ÀdYÖët–€Àr,E\�um¸F\Ž(UcÂ8Dieœy+®gL)­*J©)5Ø€ÃÀû�¿³œÍ6ÀA'«}õÑßýsõêûùçÿõÛø¥?þ÷üÜOÿSõWïüýgoùEýÉŸÿk•u´ÖXôVE^$(ÚV€³À’�ô^¥Õ÷XE�zÎN+ëóüömSƒ:û;6:›?«ÃhG¯Ó ¥C}c�ÄdDÕ2­^ÊÁ[_ÈÐØ˜ Æ~ðåÊ)•JUäËk«/›+[µÖ×8v÷èl®ƒüÁ‡†'§˜_\çB1D�(«Ä¦Æubz�¸° ¡,ù.÷ûƒÝÚËýIŽ?ò ë«2Ó„4Kh ÕFh«!â¡ ¢H®ê±-DQèÊ=.x#Y \‰HÙÖ÷ZS-…¨†êì.-Óíté´Z2œ,Ï3G˜{#°9­.Ó‘€ª€À‚›¾Nõ¥ÜØ&‡ï> \F#›W6sçG+GŽ» "Ï j–¹²LöåÒ™ÓI*çº2ØtT¢ßMŠvY qçEYW†Bz˜<þÊ~mHÆ9ô6ÄÙ ×ë¸ÒYµÑ Þ¨KùÎ5õblY.�óZ.—™˜ÙFµ"„¿8~kÝÍ–€”ßX«”Úbh*Ë·ÝqÏôP¹¬oñüY ë`PiVˆ¢P$õR‚+5yû˾•}»®AG�»*’ã�5:ÒÐ_»k/GÇ&¹çÒõ'ÑÀ¥NËŒWª|ÛÕ7™J逻€yzcM îW·ÎÌɱ„V4b!·"²cø§À- öØà¿8d0£Y’¨Íõ >ô‡Ä/ÿôOsþìÙçl@u Ã( ˆÜ:.QFu—k%š£ F¦†™Ø1Îøöqêc ¶¬ÐnZƒ6)Í,¹zl‚—Ìî`ßÐCQ¬Taÿã–šÖàÓÎ'4ðZ¥Tô�Hy®�ç×ÿù¥+I_Ô«?Q­7¶§I–Ä-Ê!SSTÆ&8tË­2lËùÄ£"�^]¼H¯Õ&¼6<6ÎJ§ÀÌ]ƒŠ*6½÷&CjBÛ܉Á¯íƒµÑß’i¬t39÷(«çÏa¬Áä “£ÛkË\œN§MQrÕ<ܦӓÀM—D�729-ý'&ÔÒ|—b‘ˆonPj4 PA@}h T ɳ$‘‚s=—ù6¸À�]¼@+âFXé1ÆñFœyú—NŸQDQzi‡êÑ›Ê5°Üœh²”K‘³°1È÷g³C7íί|¬œŸQ’RYŸÁÐkwI@ÆP˜@2ÍLö#Š"{R&®TÙX¹,Òæn¯+ÂŒqeO'ÂÈ ( Övž³¸ßã311ÃS?ÈúüyË?F4 yO‘vÑaõÂ%Žì8Ê׿ñ›9úO¯gþâ9ªÍ*Õék§–Õâ=çL 4§7×yzmE妠Fôóœ´È•”#à]¹U@&3…ÉŠÂe ¶^'ûzpd\½i÷~3[o˜r›JŒÑ…1´Ò¾9¹±¦þâÄSz¹Ó©Ú‹«÷FøäEg³K¿Õ§T+Q¯–èµ{¬-®3:3‚¯fLºÉÖËH¨;·íäýG®£—”âR·Íc+KôҔ̖„§kuv7†ùg·¼˜{/]0¿ôøC,uÛÊr�A… M4i­·émöJÀ(†ÿ\ @ÿƒ£žœç×ÚêòncÌ÷Ô+µ¹@¥R\’@:±};(Mc×nv¾Z®ªgX©ðˆÊ*Ô†‡F™™™“ä-'%6…áÔ±'8õÀÝ”ã�i•÷m�ÐI5ÁÔôØ ™Uœ^s¥--Ye—r¥3m;åe*¥­ZerÌÐ ë* ]nAk‰Z½A–¥ÂM)…ˆ„–V/!iš Òko²qy‘@Þ;% ó`šC#t’…RÔš5ÖV×�ÆD•b&&'Yߨ 16Á¥Á¶¼w?¥j™Í?2FcdT”rBæÇ(åš -‰/ÀªŸLï´[�ço¬¬ðÄ>Ëê¥!σ ¦4:ƒ�CÛ‘ ©É] ´È�ù¨+c9ÐV€Ž€%ûbŒ&®ÖHó6­NËöêhÚíMêcã®YU)¯‘S)eˆ$KÃPf­vKÎ{�ãdWæxç–¿¶ïë½F)1]»xÁåGq%–ýn¯·é·û @­Î&(C¹]fº3Eÿº>Xš¢2Qc$é¾{á<¥ $+4$¨D%_ºÄ Œ&ç-ÖƒlÇÀ¥n×¹CT‚ˆv–€Rlo4ù¦«¯7Eaxjm…k«j3í«¬(ÔÁá1†K%.w»œoo (ì%JÕ~ÓoÖÏåÝpÍfõ’N_Ä3Qº,¨ßéc³ceÕs¼jç^5�r£QæóÎñ©óg º v¥E�DÑl«ÕyÛÞCÜ<=«†Ke" » ¹Hl­µ©6*¦ßN”2¦b s£1æȚƘ!`ùyÀy~=ëÚ>6;ºÙÞø‘V·ucks£�”Æ&'©7G˜»öZæöp^`^ÙÈ÷àtäìèè„ O»Ëçø¼�VŠ‹óg¹÷ÃÍÊ¥yòvmÍ03r–[}⇧öa”F™ÀYÍ_N†@ Y‚;s®a¨]yMG!TvJÅù' »N{c‰¬ÀÖ´€kWD9­4q®A Ýh„² o‹åyYš²¸pÁìÖåË kýžHÀ‹<§µ°AÀúâeÖj5VΜ$ŒJTk5áO¢R‰ÂÉ�¶í;ÀìŽ�”«5!ql°ª;G)¹QÕBÈËg,]œguaedèÝnŸ`ç‚° Œ+…9SRãdÈrþ�ÐB�"pã,þ�õ»Œ•鯮IɬZ‰äøµÖbד§©÷]Ü·Æø¶ítZ›ôº’É �w;mÒ´'Cñìñ}ê¸cõäÓ¶¬*VÊrgŸxÌõK•keÒ~J{­ƒÉ­1© ”FŠ(‰ØqrgwœÅ(#à\k"2î¼Pký>õ¨0VüÁc+—ùÑÏ}L­ô{’í®›˜b®1D'Mô½ çPn ¬3€•Œô÷Ž=¦^^$pã$Äxhé2®»(œt<®WHÛ} "Ë·TZË-9¶¨! Âܰ5»)òÂx/Àüš«®åÔÆ:¿õäÜÞÜF/§0 )©‚Kµ�:ßÚäg¾—›fyÏU×ðÍ×ÞÄÏ>x7IŸ<Ïø²r®cu6/o*¥Õ¹)èQªÍsl<¿þ�¬=Gñ¿h ¿®PL…a¨â8qÀÔìÓ²ýªÃîÊóY—rU¿}Çn&Æ'm×¹+iy ’°ºt™�ÿþoÓ[[eíÒ"íV‹Ák%³ÉTŒ™8€šlA`çúgãÏgÙ:–Ø·çw£”?©ÒöÔkèÉ]’A©ñ�”ƶVš*"-�àW*×…p¯”kR ’Ø£árâ("IS±õ‘±ÆvÎŒ‘ÏÙ0%ÛÛc³É–B½V‹µÅËtÖ×Ù\^bþÌI)Mu[›,ž;Ù'ãì�sîÉ'xâ¾»9sü—.ÎKIÑ™VŠ2­k2ÿå‘�2mñô ”PªSÞ{3ºTCÊ?^£§zæý,üÊqI(œ»@a ·‚…ι'®W¤/¥×í Rè<'Sšñmsâ þøpˆã’³.-ÌsúñG(ÒT@¿Ro°ãª#4ÇÆäÚ^I÷Z¿OËWúM¾Ô�OÝõY^­ º°ñ\ “ã»xï+¾™’ŠÑFd½FŸõæ:Eºš²øè9ú+€¢Vû=ÕË3 )¥äL†Ke¾áꛘ,WdCü鉧T¨¥™rÒNS¹]êu l«@-Š(±íÛéZÅ¢RJ\Õ'_¼�ͳkFSh££uü†ráDw³K¿›HöVn”E<`—di? d>póü§û¿ÀB»í&îîj qxdŒLoï43ÕšdD˽.J)é;j%}^»û€\|�\_•s ìß]m¸Æ`?L‘ E„!@®Õ_Ås+Ã1<¿þ7/“¤C™Òÿ1-²×U=¹mfNuz �+ÍðþýÌî?ˆo1â–×1�2Âõl›ÝA£>ä£�ÜøÃÊÎ>ó4öß~–dsÒL8‘Íö&QµBb4ŒïCÏ¢PÊØÎzg“ÚºBçªp£l罓&Gb·‚+Ë6˜ <� \&ˆˆ›�qŠÉ=�'Y*Qaò”,é ³Ú+l&]t‘ rq�nÚ¥–QQI-ÓBD%¹šÎÃT¸¡(.Éhe!Ôµ¢W¤Ì”å�j�Ë—XLú¢Ö ˆFcˆjµ*³´|]*12:Nsxˆ¡ÉÆwî¤1:FXŠ©Ôê2`íÒéSwáW�OrçöÝ|ôÌ ’\Égäij#Uµ~iûy¡¤…�ñ<‡óüòW9.«^’|SµÞx]©\žÌóLey&W÷º^c÷M7KÍï†7ð,óN„¸erbZCxcšu`»%�»÷nøø‡é®¯ÒZ_§Vª!’Ùj£ºÑjö �ëÆúz�r¶7¶ì#@ãÉ�ÝŒû,—ßIR íPÉprË?á[͈¼Ú±íǪÆ,�áäÁ¹—€iL†éwé[Ošbs‰zÞ')24Šdý"*ˉʊ°\Ť …½Jì¶{òÙÊzºé �‰•yž‰€¡TÊ‘fVcÄ}am°õyH^›%‰ ŽÛwÃÍ"Nû=QÎaŒªV‰&÷R ÐVmŠ@ã,bœJë´m¹9­†R¶iÓEDl˜ ­¥³Ä*wö;a 8ʸ  í¹>Mzª‚ )ãz2.¡/#ª+uéa‘ß“4•²¥ßÇeŒ/Áq8ʺ6¬¯¬sáä3ž ©Fi(�TÉ.ÆwMsè­×¢—^d<'îG  ¾ÚdÇ™9î ïâ¡ßþ§VÄdt ‰FTxAÈ-S³ 6uÕØ#¥’‰”æñ•˃²Ó=ª“IæÃþáQŒ‘׺ì¬Å|ÝáëȦ©…‘ÚL:’ k;-P׸…Û®{1¿p÷Ï¢JÐU¹Z»´dDÝ�.ƒ¥z‰ÚPUJÖÆçxR)w�È»‡Gyfmyg µV±øÁëop(äøãgžäB«%½E“•*_uÕÕr¼ËÝ¿öÄÜÛÜàÅs»8:>É= ó¨Â�tS’~Ê`?é”»X¡‚ÆT*À<8Ï/·ú­Ö+††Fß_o4Çò"S¶ÞNyd„½·ÝÆØÌ¬‡0®Œå3ÿ.@��MHS§¯¨±?ÛrNÎÇÿêÏøÜŸüå(’ž—vk“,Iåêwht‚õ¢„š>€ K˜¬›Ý€²åæÊ@6‹²ä¶“ý#£ œ„V¨ÝåÀm³#„‘UŠCúI†rj;7¡ÔÙö«(ÆHpEú„Qs©‘mdZ‘[åýJ¦n®Ñë¬cúm‚°@e Q·Åòò²ô¤LMOÓ›ameI²ž‰ñ1!——/Kͼ^kBYø1É Äe ŠØlmrúÁûHz}Ò42¾Tb#Å”V•f�ãΛãmä \6§JKS—1âÊ¡˜Ò¼ KR3ýM["ÕdY*€‘g©—…çƒ&îà•ŠœOñà«ÕåøÊµi¿/Çl€n·-€ã38?<'qbc Î�:N}MöݺZš yÁ7ÝÉôÁY“ˆJíÁ›dîü6öÛG©]¢TXûóe>ôé? ýÔ*…•ƒû¤d7OùôüY)ƒŸd¾ÝVŸŸ?c>qþ4›i*YãÕc“Ü2=Ëï{Ü9JLWëüÀ�·qÕð˜¼Ç ›Q=s’˽Ì Ô&ç¹W¸ó–×Q+¬>±l{°rQ`:À±2ïr­$ÙEw³Çúâ#Óîy±@QxgO)ö4šÀYÀÀ�½Ã£¤EÎ/?þ ÇV–1@¨ó­ ¶Õ›ì冩~ûØ£dÆðÅ‹çy÷Á#ì¨7y@_”f]‘Pot(×Ê¢Z[:¿ FÄ@·iú7ØîyÀy~鬸Fú§²<;°¾¹Ú€C¹9ÌžÜ*}FÈ_IëÛ»ø ¡hÉlãe6( 8…‘@tßg>É=õ§¨, ¦Ršj¥*ÃQ¹Ê:5Øy­L¡,¬¾�Ì•TÀH0A{s\܆rÖ/Ê–ÍlF$ÏÕv†M`@~w&•–çN YØN~å\–‰ ÁÉr NÄàD®ÜcÍ@M¥‰Ö ëÃȲŽÌâ6Pd4T¯ÅºÉÑYBedi·Åfû2Ë+«$‰!ÔK«ëˆ‚�-D~FTÉ,L�3zÇcò‚vb`v*û~¿ÜâÈŸ“­ÁñbØ Ðɧœ³nŒ’­Tã2ÕõYÅ ¼ ¿lçJ¯›«+Âo…Aè2ÛÁfÉïc ‘K;®Ja½ÞŒ ǯå}WøäŸþ�sÄŽ*i/•}ê.µùø�ý wüð똽a;Ф”pbßIg9tü*–¾p™ÿò»ÿžÍµ%ëd`Ä9¼2\ ìov)ú*€�ž=ÉÝ ó"T¨5`$xïUW›¿;u\lo H™êM{ö›‰r…³­ þä™'Ô'Î�!´<¨‡4ê# 5†YYXbåÂEЬpF±ny6M•†ˆ"ä>· ¾:MV¨µM+M•cv6‡P ž\]æ¤ÈÚm/†Ãh¹ŒRˆóBRJ Øb Çž˜Ã¸)³aŠ ¡³ÖA¡Æ æGPêÝ@ïÿqÀy~5ãÊHŸäƒåjeo—B)o¤)Õñ:‡^úRÆçæüq¿îJÕ56ø¥_eÛìv´%LŸ0–+ö/|ôïxôc°ÉìJ€r£I'Wäz„`ûÕ¨JC‚½8 „Z¼g“ãJ<Ú–Î8×$¯U ¯´–Ï’Þ{UÚ&Fz„³?ÆBHšÖÁXy·Bغ¬@”rJ{�¯’IÉl˜ÐzœY»w‘.rjc0 išUªbäÕ ðË•ÂCô!O ó²”(ë —”m®’ç)d}èmb’ÕR,Q¦RV66ÆPmŒ[Ðwà!ÁíÀ™ÂŠ žÊ5ˆ¢�Ç”´r3†¨Ñë´(Wíèî@¦xRo4H{=ïÿ‹r %=1Zqñü9 cäùiÚ§61ieöÆ�T»Ý$Üys`èF/{n“~ÂæåK( �̹Àq;IÎÚ üíû�mo<Ä5o»™©£ÛˆkeÚõ÷¸—¿ûwÈêÚ%ׯ3wË®þª[=8 ‘ŽóÌ=ν?ÿQÈ ëI_‚x¨4£å oÙ{�ÌÌqÿÂ<Ÿ¹p슃À_[a� ñÞNS£°å6å*JÆf$]¡=N�>AÚj¡DVÖíJÒKX¿¼�ÍRÅIÁ¥$IÉ’ÌË Õ0RY‘£œªÑˆÒL¡¤d6Ø/9ž±r…WlßÍh¥ÂõÓäE!²i²nŸÙ.À³™$¸Ñ€ÿùõ¡š”ülùýNŒùZ¥õ/ùÿÃ*µçW5.ÿH¥\y«Òªf0jtdœæô /xã›Eeäx \ö€ï�¥¬�Ìôô6f[„øÃ»”Ež|äAþä~Žgî½KlP¥ ƒP¶ÙÝ{蘈þø!¹ÃÒ˜h³¯'D»y,>Q ®®ïì_l¾2…sˆ66háš,q™Xž[°qón_àÞÕ  73ƬŸñxÎØrë»Ø÷Á“ãŽlS§òMOƒ•d ÊUtmS#�%ÛN<µ‹`j/jbYT§P!DlQÝ}-A\B¡à@9ò…ãÄäñP»ï ðm}ä<úYœÉå³ÐßÀä…dk"ïÍ ¦öî•1^YU²•‘á1ùùÁ/~Žs�?J§ÓÁÁ¡;^ÌĶmnž�)${…Ÿ\Ähµ…Ëí2n¤47²m;kë«ô6ÖE 'Ç›»± ’%¯?r‰Szœ‹O_ ëe²ß'>uŒ'û‹( <Ýàÿü�Üþ]/£yí(ñh™13ÎUˇxñìËWÛyä±»°’oš¯;r-qòw§�ó¡3'ÕånGaWVœÞØ`-鑘[Zt™‹ŠLV(l9sßÜž8ñ0g�K™¹Ú¬ª0ò%Ñ îæÕ’ð&¦04Çšh­Üã½V_Añ~Z)Ä\ÁC‹—ØÓ@+ËøäùÓJK?Ð7½žC£c¢>|ty‘_{òad6’1¼ïÐQQæ}äìIÎl®SØOhŒ5�ý´à“¦Y?SvLÃÆü9Ðâ9°ÔM“Ó<¿þ—.•¬¬ÞQ.Wþ0ˆÂ ¥T0Ô‘ù47½ííŒmÛæ×Ì·ªÒü2šð5sÛv:é™ßÍ¿¸0Ïç?úaýèßb #ÒÙ,K%è5††E9µ’DD»®—@*ßÂõé8#I°ª$Þ¾)¼P…·{>¸8qàfçdÖœQÙœf¹+âⲟˆ<[‰tŒtV çÀHÜpÙ�{­5Ç”ß1.q€å7¯*dÉ~c$s³8 LÓ\ º·Žì‹”$•”n¶�çÌíÀÛã¯ä™¶‰ó1“ §�Ïw ˜õ{dÏÜ…Z>Eos�<ÏÄ…ahbŠë_õZv>ê÷âˆ"mÇöÝ\ OL`¤·ç‰—õÉ?ümú››òÞYÒ.ÄX W7%œ,CÙjSÀ‹~ò-yËu¤:ahcˆƒOdlaŒ¨Ùs_áŸýú÷òÀcÀp6q ybeÙI›·,ƒ ]±áà¶±g”Ùí¥4ZåÉß¿�Öc—Ñhî¸þµœºp\-nœ5I·'Æœ6�cŒ‰wTÝ}›+-ŸJ½ìú­\\µÄ½+kJ)ìí{qdl‚Ÿzèû*ÌÞp›ØòüÓ»>%rg@îñì^ZäîKçée9I‘ó¢Ùí|ç57ÓÉSþåÝŸžAjŠâpÆ¡Ÿ…™• «˜ÂdƘðà“<V˜¯oðüú_ºfÂ0ü ÁŒk­ƒ4MY^¹Ì ox³€�ëmðìþ½å�kÕšØÂÛ_‘¥Ì»?óqîùÛ¿¢»ºJ†òŸQ‰(ªŒÎÎqnC¡wBÕ†�ùûÆ‘ÙD‘vˆÍ^¼ù: mož�ÏÛËÚædÆ*älvFArõ¥$ˆâl<ΗKJa–ç�çÙÏqÁ°â%W�V9瞇ðD†X‡òØÀ-ÜP¹>–"P±`bœ­‹Mš¥p÷å…4¹:Þ 76ÛͰ�(¢F) àÊùtû&•Û\€× [@�!€?A4F:A¨èë(ÆT‡1+šZ½N·Ó±€ NÃÚí€+ ‰ (xòÁûé­­ Õ« ú¹ôâÈó­§žÎVkÓU†‡èšTñû«*µ7½è¥8z O=úŸÿË?e}ᡎ%Ûγ šjvÙ–þ´&Þ^eûí»Ð¹æª§³óéT6+  P"¡¦ÈÞù²÷qß“ŸAç©döÿˆr ã²c,á¨8ôþ›¹ú}·×KZD!&WôOµyì‰)û~ñ‘�È™S‘B‡ò}û+c :]²´�œÓjIÆø²é<Ë�"4.—ä\j…”÷�ŒqÍØ$_X8Ïé�u:-fk iý¹i­xpq�û.]”›5–•>�¯>t ¥0àCgOÈ{yü“ì+øõpúÕ °í}ãçøì6•ô{a‘ç嘒)Š!cL¨(¥uQQaL( ¦FÍðÿÀ*” ¢0úÑjµöºr¥ZÉ‹Bí¶_w=×ÅËl7½§ò6o‰QãܶB`K°3X¥Ð ~ý'ÿ=}äoÙ\]–^±—ÉR›"ª�³Q߇š:@PªJ0±eÙl9Í¿w�&Ïsë$lü9:n&v_ à»ÂNÏôø+©6®‰Ðžó^SBu¸Ç\ê— ø¤¸÷;®åÀ`KYÍ�€A@/°ûî²$…ëÊ,(\¹rK°-œkƒ=@ÇwØ^#·�rÜaàx°(Pî\m)ñJÀÈî“ì³=ÿî¹F…¤Ÿ¦HÉÌzÆ ±ãªÃ⧆,åÌJ[««|üwƒ´×•Œ¨ÛëŠ1êîë®§\¯QØŒÅrF$ý¾pUõzÓß7{‹[ò˜Áe…Õz�Ù»9ú‚Û juΜ”€®CÉr±`(ZXÙ~m¦Éˆž7>~#³§f “È]ŒäaNRI¸<»Dvîyä³ô7EP !TT·ÑÜ7F4ZÆDZ¸#­”ËþË3uö¾â0q-–Œ§œ•9úÄQæ¶«'ž|˜^¿-ǚ器9KÕ’ª4*ÈR˜ôZ=‘<î[›J½‚?MºµÚ’>@ÄGoº‰" UÀF»Í¡¡1᜼¼ òç�Å·oÛÉÎFP<±rÙ}_öÿ”8GÛµ73[o00ñä�?Éb§-çMxÖJL¥V–ýênveº]c cQ‰‹À?6þÑN8<¤Š~RjÔG´á9æ›ó,ûzS_UzW©V{¹ÑxW¥Zyg¯Óy‡‚·›¢xyÑëOk˜É³|™ HÃ?ÂU¯Ô¢øOÀ¨4Ú—ÊÔÇ'¸åõo_/ wAi‹ÂIǶm;¤)Qi% ±±ºÂGþìøð`$ôÒÉãdY" �`¤§¢91Ayt–~y”lú0Áð *Ð^Æïÿ°�œÆç„œI¢1¸²”¹ m ´•ûÚÌÈFo ´~À ½@è-… 4¸¬ÏŠ$�ÚQÔ9ÒÙØß­CµËÐl@ôKj~‰K>»�pÀ‡¿”rØN$áÙö»�id[®�]9Ìq4[fì|>Ê�£-5:©¯ÒŽÓ£é4Éæ y–‰´[,�ví’AkÚrGq‹êìñûïáÑÏ}Šn{SD•VÄÕ*‡n¹�J½‰ ‚ šÒȉU›#ЕÚRêtÆ­øYA¹Za÷�Cl¿ê(½6ë—.�1˜Ü8×슋*ïœújƃ yLE¬Ì®ðä5Çxâ蓜Üu’Õ™T%à쇎��ƒ_w+·ýЫ${¹æ]/àÈûnfÿ®aê†,Ÿ^$Y‘n}Q“íüŠýTF«(£9|ì03§gÔŽx'Ó£;Õ“gQíëTiVT\Šhot$�W‡ªè@ )_ä…d7ö< YájÛªÛ4Gn¿•÷×wPå�Ï|–�n‡ƒÃ£ƒmŒ¬(D™¶Øm‹hà¶™9�ŒqýÔ,Ãq‰‰jUŒ;ß±ÿ*Þ:Ø&*UÎnnðß½Ÿ íMúV=Z z’uuÖ;¦ßî 0šÂ´�ððƒ: úG§R3PIW×îÞ·ÙÚØ×h×¦çæ¶--\,åyNE* CLA(.¾Ey ÀçUE–/)òܤéßø˜Òús@ŸD+I“—k­'ò" ËQI—o¾…¡± ‡±~ð1RÚÑŽ /—«lŸÛ-~YE‘ÓÚXçãñ'qžÇŽ-��d½'ã”Qì:·›™3ªÔ+¡�æo'¨ü0?óçÿšµE)Ó)›¥bÁD²šr¥„)ÇTëV.­Êw`/ÔDDPä…T)š##¾úêÁßæ_ñÔà h'ƒíoO?ÃŽF“—Ìíä¡¥.´[Ò[jÍ;÷æ@s˜«†G­×»qAÏl¬ñK�Ü'�Ÿ©±¼¶äÝMŒ3XÅ­;aô'‚réÄsé^]–øÖ Àwé ¸-Šã¹(Šâ‘±ñ¥$(6Mi”ë´ÛN©†}kiš˜4Ió<Ë“,IÚÆÉóüƒ*2žãËäù6­õ_7šC×”â²ÎòŒÒô¯|ßW‹A¤Rú BHɄݻöÉtÅn»Í#_ü,wÿÝ_±¹|Y5ZlïSúIO|ÇR£Ð�qLs’òì©÷û²ê0 l&à¤Ç!ºåÊ' P8i4p™L�]–|v Bh3š<Ï] ~ F¹ìÄY¯€›ÌéŒA½.{§HÛªæ²%'7y;øÍ–ù\YÅ÷“+ŒôóX5�rÊ,”ËJ¼}Pn µÂžËrì°2Ï Áî7 À¾· þ�òšb]‰Ðe …uÔŽ¢À}W…a¬ÎáAHùÖ ›Ÿÿ=TÖsôe¯à%oz›»¨×�ùwýÍ_²tîŒðhZk¤œvÍuÜñ–w—ËòÜææÑxWôެvÇí¿w8.Ç m l™û�¼.Y²ê?ã”uxY—Ëå3üŒNžD¶|úm°klç.v: J#ã-Úmž¾ïnN>ò \m‡¡Z¦vìâU_ùµ2'È>¡Á‡ÏžàãçNóðÒ‚dÙI‘›¤(ð U# n":îîóÀÇ�GÿÑMüŒÂè¶Â¬6GKƒJä·(ET® Ñ”ëMFçvPÓ1´Ûb)_Ÿ†rÈâüyV/^`}uEF ƒ|™a¨ëÍF­ÈóJÒOÞŸöû¯( óýE‘Xå¹¶´Ž€;�1á Îú¹Ó’fi& ¢Õ0¤×ï l‚µ™}Ä“»QãÛÅÓo*ÔJŒ-§�ü않$¨új0ƒqMœ²6(¹ Â#˳±u—AU¸>c_kïsÀš¤™'+' ÀÖ¥±ÜÆ¡±Ê4 ]ïŒãP„ãq``Íe(…ÁªÛ”ósB,@ÚÒX aE`:…m>-Œ·ïÊãvðû“¬HJá|8<å—¡¬ÔÚËØT ¬K´Á ìyR6HSªÎ"]›—òU‘gl®,ÂÁ@aÜÍÝ<3P§a÷9Ï3Q­½á¾�}‡¯æòÒ"‰H«T7%f\•>›Á{ʸ…Ë�x'17X]³0Š3�?*’n<Ù{(Y'â*pü÷¢öX™7¿ò}˜ºŠXÅäE.ÛÉ3Ïð;ùßH7ºÒó}ÛAnîÝÊRg‰V­M€–lû™Ï<οô¥eÈ §?tœwL½�êF•0Õ›.D�0?;Ïñ/üÌÝwÉ`«4IœO–AyhJ¨ú(•G©ÎDÅ› ¸Ît÷Þ~Æ`KNÎÀÙâàÊCþÕ¶ã´ „®nï dßmÉÊ ŸëÀ@YA&åÜ ØZ>*ŒðÂàTqVví—k\ @dÔ8eœ÷<¹Ï='i·ë9r½Dö½]1Ô�³÷Ë{>B¿¶ï®6Ýyp÷oi’´™‚¦ÛKð}èøîÑ~¶æ¤ÐZ)H›{?­m/Óæ ›Ÿû="•#ïF¼ç‡>ÀØÔ4 Æ%|ñOÿ�¬ß mƒð-o}¯÷W†‘\°\¾|Qš@mï”ãïŒ)ä9F²¯P¸Ÿéé9¬­~}ʳßqj+-?‰Rò3ó|ì·~ÅÉé­í‹Íˆœ2R\¾g§v±oÇf§·C¡8}ñ|üstz›(‹Ôï}ÅwñîW~ +õ%v-°<ºÂÆÄ&üÖÍ~è/ѱv“6˵:ÿò~^˜;DœÄ iœÒmóÉý3û�ßgå‘‹díDÕGD�†ümÊ8éÝVOfß”ëe©8ŽÇV×eD�3ª�)µeý hùyãò&Q9%™±YîÕcìãÀð¨ V bÚ Üf` ÒƒS€»ˆ5ô Ëc¹ñ—±ÛýÀ{tžÌsp´.)c~jhtô«Ã(ª¦I"c_'sý«ßÄu/}5S;vË„YÆÄéJ�ñŽ!¢ñŠ+Žä­„Õ‹mÖ+%NuVùü‡ÿœûþê�è.ÌË—[ä¹½’Hºýn÷Á@¿Eñï Ï�51:y[š¥œSLåy(pèÎ;9rÛí8ú°Ä�øÒ©“\øÿo�>*ª¢J¹jçù+Ö×WŸ“dQ•p|•íGjC®üá:ø˜Âe nt€U– ÁîTe ´qÙ‚ve²rϹY¡\¯Mîñ:N�`=©ûžVÙæ“ζ~&\‡;�1¨Ëx,Ú+ÿÙ[ÉšVj[xïmÀ~¾ËªúiêÞÓ²{O­}ðs\…•Ok›ÕžØxN 8¥ŸÍ ]6Ù •f¹›AÎ�=^w¾#à¬\‰-Ër÷}Êcàñ}kßÿWÄÝe9Žn¿Çío7;÷ä/þëÏÒY]q㪣0bdÇ.¾îGþ93ƒ[èEp°tù’, æb€êÎaŠ*‹‘áQFFÆ]YÍ»Rø+Þù¸4žßüñËò™SC(rc9.c\F­\™S‘ä™�³q µ¸^Ù}+?øŽÅpuˆBåFÚÓ>}ï'Ì/ÿÄ¿ñ%úJkm¾á­?ªÞpÓ[ò€<ÈXê-ñ@|¿yzç1îÿOŸ`óô*Y'QÍñ¦óC+ÕJ";n­µÅÖÄ@fÐD±d½¬]\ABa%µ‘CãMyýÀÇöìˆØ€^§Ïúâ:RepŠL˵E±©„¡ z¢D3$ya³E—¬ù¢†j‘<¼ÑI±÷­¿�Rßtÿ1d8·iüÖÐØÈ®ÁmPä#s»xÓ÷üS®¾ã¥ ý¹÷•N—ÑùËDý£•MÂÑŠSaä›}Z»lŽ4Ø’?‚Åù³|ü�~ƒÏýòÏVJ„AhkéY>�¥,IÞ(~èðe¼&'¦´É‹Vó=�~g(Ë2†FÇyÉ{¿Š©]»0¾OS·+åÅgî¹›s ‘ +#�3‘¥5‡)Œa½—Ñ6Áð4áÔ‚ƨ”SlyȽ§UѸ¦M§¢²1~6R‘cS#§¾²rg\™Í†,ñm3%(rןãÀäsý«_Ç™ØëÈoŸWJ9ÀÂgaÁcœ@ Í2t�*JJh7ã�øçÜ�›ové<ñq^†¼¾¥Ü¹\+]“gåG�. Çä‚3N Ná‰ðú˜ /\v$´Åñ/’žy�fMF·ï #.�9E"}7¹üÛµ›÷~÷³sï~ÏoÏßziñ¢”‚Ä’Æçïžåavv»¨ÖpÙ«{žÅ vh\š[Äg~ÿ·Ü…Õ�Ùoºa_8¾È#gVé$¹»Pp �Î!]¡�:õ«'9òš›yßðûÙîE¥ À)ÙŠ §Ýoóï~ûŸñ蓟1RR¶YÎÌäõÁ¯ÿ/4kC[|’Ÿý£˹óOH/~8ÉôZ“ª9Ù¤T‰`Úëä{*ŒÈ¢+õ²È�£r$Ïé·û*E–Ûx¬�1¾mÌñ�xç0é§l\Þpn²ë8ª í¥)Êû™-å3‰«¥€›÷ŽšW�åìr‹_þÄ íe:ŸÞ\z΋‚ øîj³ù²8ŽbS¢áÛ�ý{®}ÑËQ:ôƒ‰ü\iw©´:Ö€0-# [veë}ò�„Îh“´!5Ó¡a\w õí;9ýÐ}„JÙ/­ÐJ©jaŠƒ¦(4ðä—7Š«’)Ìûƒ0p`j|�p#£-˜¹ýö'í÷ä€Òe>¾»‚d¦¾xÀ‚|o�lm�H+œ^kƒr¹$Š4™Æe޾ôå¼mÀÛlÛ¹üïZ+d]šPî¸,Ø9~ÌQ`•+y�pæJ¢"˜?}’¿ýµ_$ívíñ§xÉUS™fçx”aµ��ä–Ås‘Øà; ÔvŒ°óŽ]Ô¢!ÌJ!{&³£ÐE€.1pí�›8¾pZ-¯œsYN«³l–»uyc‘ÿü§ÿÚ,¯ÌƒR">�Þ ‘5ª>\Cz¸’L@¥Ú¬Èyè®wI“ ±³lèlÊ ´5„�µ¸~«J£,ç@ÆlotÄ §³ÚFgG hmÁDîôv7N((ÅH#æU×Θ·ß²“—^5ÃD£ÌP5æ±ók’åØ5!`£ÔýÏ)Yô”¨)MŒ"Õú�þ»‘±Ñ›¢¨¤z½.G_÷ÞûýÿŒ¨TvŽ»î[. có‹T×[„I ¡¦|`œ c ò%wϬ“µR–vLÑiÔð|*åÊ왇ïãÏÿë9wß �ŽeäÝN÷l¿ÛýJý0°Î—ánŽ k¥þ¢R­Ý¢Ã Üî´˜Ú³�[ßò6Œ1,œ:ÁSÜÇü £©Öêr…i�9¥‘Oë€L…ôL@¼í¥‰9¡ ” @A"©¼»ê÷úK\öâºÓ�J$Ô¹U,NBŒkìô­ðµ/Av2a)•ùÁÕ‚™ žNÉ…Á5z…6'ËNóœàÿS|Ù²Oè8ÀëþW8Ãw…ïžçìg|Þ�C‘ bªïgpY‰ËZ²,÷¦–ú¶B.»qrj9»ÿ€{NaËšö½Hie�ù~ì}ÈëýÒ¡ÝGÛOd0øÁßó–ò¥ÓtŸ¾‹8Y×ñR)¦12ÂØô66××9|ÇKxñkÞD­Ñ@Ykìùtಹ¹!¥µ~¯k/Hr›ÉºAkž¸EI?ØøØ”œò²I N<�% ú£ßá®?û#÷=í›iðÍ/=Àô°«r°ÙK9¹¸Á…Õg–Ú¾îÿ­ ¥ã@3Ö(q`¦A·Ÿ37ZãҦhV#cC¥)Œ1|ïiõ÷ÎûýŸ¾í¹Äy«k£2[Ö~©7›ÛãR)¨”k¼óßü×ÜñR¿ŒæŽ8Ìr¦NÎSêöPE�®F”ö�!N­€IsºO-ËÔÒŽiÚCu¶¨F¹pê~åßÉÒñ'5ò„,ML–åí��c¾ëË1}œž˜­fyú •Zí-Yž×ª#CŒMÏ�k ˜µ Â(ÉèúÆl�1EPª¡j#D³(OíAWêFùJ(¿ŸÅuÛ{€á2 ×H 8Å—,gïâq7¾û®ÉS�Y^ß…#Ž=MŸ ´vûˆ ð�{½7ÑÓ=^xå>­´“[Û îz{Ÿw^¯äΉbyž·?žB°“k»óå8'\�’Ï-Y¾Ãe"ÊëͱÊ3÷³”¾²BøÀSÜ�/ ðÅ’Éi׿à„YV˜KÙ¨·AçÉÏn^¤ÈzÍñI^øÆ·±ïÈ5ŒNN‹`¶ÊïO%Ãã./]BVýž_J³%>' q“áá1ÆÇ'å1À™…Jvˆáø£ó{?ùèÚ™/q¨yÓMÛyýusr\þ2)Ùvú׺ÜõÌe.mvéô2.¬tégÆ?_[›O±àd ”k6¸4Ð?ç̳„ÛM `”r„« øŽ~ûWZÏþ˜y–ßmùZ™±F¬'†Êƒºyï»àVŽêå�'ç7Xk'êÖýã Wcƒ·.oöøWöˆZÙtS?W�ÿŠRÿ è?'§•®A«�Õš�Qƒ ò$ç_âá%´Kÿqg´±¶ÉÐÂ2¥nŒA—‰w�8ù¢ŸÑ9¾B¡4K;§éÕ*ø�³~ÊñAiíÿôÓ,{˜NkIÿQt;��,I/Т`[âËh)­«q}K©Qû–8Œ÷Îý~ŸÖú&¹§ð36P²±¹�Mc=¹‡úÜ~t\%ó #]wº�ÒR¦Â9ˆÂËÚº8×f—AÒÈf¹W&³�+Ù p¯ó3 7ÿƀߓb�ÎíÿŠØ惗ÝìÏXN/w*6ìmAù èÉï_¨¥ôå€ µs¨–eœ‰*¬Á™kZÒÛ‚„²û¦�k¶³ÂëÛ±e¹8 å¶� �lÒ[[�,#ʬ» :B§r¤=ò~—ä{W:@¦T…¨LÔƒÜ6'Ð�ݯ´Oÿø]Ä틹عÐç%o{7ÅËW²ó.eÿý€ßjmH“æ w�Û^Ù| â@01>E­Ö�Á~JiZë«üé/ý<'î¿Ç  ÛÇj|Û+07RÅÙ9{L�¿29ǰÖés|aƒ“‹›»Ð" ÕËè'9I^x~¶íùV5úÖ¥ø_¿Ì•>Ã6„X‰~£qãžQ•Éf™¹‘36û Ɉ(>xf…Ï[Tï|ÁNl†è{y«O[à?öŒoiø1à�ÀÚs²§ŸögCÃ5­u�å9û÷I×ICýÆ1c(·»N‚£³‚ lÜåA7E¥9*�§ø©.liðÛÝM àßó'?õ¯91°ô&é!žF¥RÃÅ«M^<”šâ�âËB�61¥Ö——j�ng6)²J¥^gíì*#C#”ãíN[ìà»ÝÆ(âú0¥ñ�d#s”·$¨Ô)”kÆôÊöÊÝç]¼¤Ü·Ý—+ñ;Ó¤&w¥°ÔöÜ^öc°<��ÜxNÌxÎò…kÄt%"'§.Œ—m(Kô¾Í1¥…dNæÏË·ŸSà¼ÏBy_)!†¡ƒc³>t0rŽ\™ ‘€Ÿ*_Hàû´IyWÎKÓÜq9Þ«”S(Ë_eR²C¶Þê"º³ŒÚ\¦ßÝ$ܽ69�rö:¢>LÓܳœr¹L)(Q´WH’DÊcéjJQä, ajcdQ=¾Un°gÿN*ý‹§O—ÊZO� ½íE2lÎÏj Þq[Y»V qIzâ ’ô{nâ¬OèûÙsQˆ³4•J¥åçqúø1Î<öˆ“ W〗�οžéuRy·N‚>Ù¬0x�lë�ŒW]3K½R ¼'[tz)í~F?ËYÚì³ÒJ¼‹3t§Ÿ“æ…ÉrS(%”‹*Œ1jkÒ²uy´œrZÜV —0J)-ß'D�&$#—Œnj¸lšåЄ�V£µÛFªf¸Z€ŽÅX£ŒV…ò¯Éت‘º´Þå‘s«ŒÖK&µ;‹ ÌáÙaÕ¨„ Ù¾†CÀ»”Ö¿ äÏ1À‘ §Óëtú:Pe™5uôz;ÿ|+žÇ�>q»‹*l±4PÖ¸'f«]T–C’”cW’Ó8œò¯˜˜ÝÁ;¾ÿ_ðç?÷ïyôcѧj�ÆöN»ýíf°o:Žãÿ&蘢j¥Ê”JóEQü•…éÝ”&É”n·ü.̽~¥4E¹>F4¾3¹ÕœÀH¯„v�xEá—\ ¶�S2I–9¿3á"œ�-é ±ß·œ�Þò'!–4wàá,T|ƒJœ%‹ãÜU²q¼�Üú3m„›@¹î{�T·ûâg;�k8ì')Qè5”‚;€r¼�¾nì�W¦p™wh,`;!‚É- cœRÌŽ>pûšá­-„×ðeÚš‚¢·Ié“,Ѭ×8µzÓ^#2Ú–ÀtˆS¸ÖVÆ^bgo»öíw‘…šR� Ð+ÇzÂe¬ ;ùêYâ¸L{f‚¾†Ü‚Éð蘀W¯Û¦Y.á¸(ìò,{l4Å€ØØôú=[f ck©<õ`§Ûîgtd̺nô»mž~øúí–ünŒa÷T�ì� \i©­Õ/?]nÓég6³£UfGªšr|Z¬löY咽  6{«�D Ô\A9 ¨ÄË­¾ª•B’¬Pƒ÷¥ _' UXQLhÊQ`â@ñôÂ&»Æ«f¼Q2‹›}5;T1…1j¥�ÊþŒÖby®VJÀ¥j 0J#?ã�·LtKù·vixåÈì�j¨‡Ï¬rËÞq‡±ñ— éu7ÌòûŸ?kã3ÀKLQüp�/󥮋«lYÿ<Š£,U+5€}‡ùþ_ùSª�&øŽ‚¡ËkÔ—Öò‚¨Û—;£�C„5ù™¬ wì2¦“�TË\8²›Ü( [¼™øòþ•…y~ï?|€S_ü”XGh±˜HÓõ•Õ'�1ß|žÿÃK…a Š¢QÅ·Aø®8Š'ÑŒ‚* ¡H°‘’špqƒÒöÃŒìº j#¨0–ûýÁfþØa«r��ùá?Ov¼…¿ð\ ó•!ð›3•ìÒÔdÅVuš-¡Ùf½¬/ç^ÀS”_Þs3ê=Ü�HHº2ž#ÃAøWë÷S7Ž9,ÏR”m¨ ¹ €î3�ë4¾ï—S›Y³Jg8ª”MgËâñ)î|�r °¾�¾¹‹'è.Ÿ'H[Œ6ªZK™*ËR|%žÅ^I‚9ȘfQ†r|VŸ$}´ ÇËÀ}—�¸B ßíF¡p6�v[ŽEÌrK%n¼ýEl;p„C7ßÊøô´Ë~=à´ÇîÊ6^iwML;{½žìƒ�σs~0Èþa¥¸,RiéSŠs'Žó+ÿ⇥á ¾ñÎÜ´{ ù(ßätKIm+ðó+m~ã³'$ ¾í–�ŒÖK°uÙlŸqA9é±Á�æ…€‰l¿—ÉŠBJƒã­PNX©ýßsF]¿s„ÃsÃ&Í "'¨±Y®ru†+•íÜcWÈ`üûy¶ûŒ�ûN-óWœSß48Ÿs£U³å}ÍÅõ®úáß}@ç…ñ¹œoU�þ# xNÉ¢æm: oˆ¢82æ®»™[^þ:¹ZóÏ )§•:}‚4ÐÑ¡&ši l/B±Þ#[êŠ^£Jk¤áþðÕFoË)öÁG9}ÿÝ,_^ .Ń­¤Y6œçù^ŒùÌÿáºe“¢ø&à§‚0z3˜m¹)šQGa�BœzIŠ)5¨|!µ«n§4{J5r[òþëÙ#å0GÜÊæÙÖü)Ä·ê~’¹>WÎÂÎ¥|‹p�£€›ÖéËwýr”V (ÈW.R¬^€¤ƒÊŒ (8|×]Ÿ¸PؾídÚ^PT.ƒó¥Ï>/dImÇiù 1»|ÃO4í¹pgËϸ’¥ì“4~FÙ}ÀÅP¯œf¨uŠ0ÛD÷7Iɰ)$`§IJE$j�æfv²cv/3s{˜Þ}€«®¿•«n})‡_ôJ†Æ¦˜:p”‘ý‡iÎî 9·‹ºÜî&¬5db§) œh)›íFrÎÅ  Ûi³0–rs˜‰CŽñ¯äìR(ÿ¼º÷êtZX§�gȆµó—¬]F$Të˜"çÃø;\|æi×­õÎa^yõ¬Ën¼+Ó-1Õ·ÁZ;á‰ùu^thRdÔZ©g¯}Éí~Жd¶'M$óJ›É¨Ðn‘lÚ 6"+&Ér£>öøEɨ<” µVö3œØÄ…+Üæ/…¿]a]9ãqSºI®8½Êp5b×DÝÿ\ è(ÐæÔò¦º´Ös¶kÀ6Œù0°ñœ*©i¥Uâ Œ%ŒdÆ^�n9�FºŠ‘²š®¨Rö¹6…•éÖÊîK3²¹šÿV swÌŽMñÞ·|-¿ö[?ËÂåsÒGP.—KaÞÚÙlý”1Å·ÿïN#'fgÕÊ¥ÅmÑ@�›üõ¡Ñz­,.]Rý~W†¥ 5‡X^]¡�¡²ûåG¡ÜRÙàûŒy=)^çyQ8®9Wiæ59ÈØ …U.¡pWü®'EáHn«Ô’Ï+IÀ²™. #™ˆ®íÍ_ñÇ K©JǨÑm¤k—H/Ÿ¡èw� Ò˜ šÙ� *BôËÕ–È„]ùÌ]‘o™ÿc þX\�vûnlödåÅD<‡hvÙröù¡”éòz{¼þÿ/~7eÔö~ÊSÇ)œ5OžÀÒif¢eL ..\¦ET‡¤�™õÖõrƒêä ŠZu˜½;ð›^ÌÌè4�j]ø�ùÈ‚ßÑ;È∤R’Þ´Þà6Ñ …l´6¸|ñ<§{ˆ'ïþ4‹Ç#im22Ô“³¶¶Œ¡`sm•†1œ¼÷ód�6ÛödǾRfË ?_��Z)×Ptäo½Óé°i EáÙðØfà—#Ÿ]ôÎ=õ¤Íè †Aù‡Z)ÚÒE«üÒ™¿¶€�ȦåéÍJŒV4¯:çèJ½–‡ù‰~µõ1)礪JòÂþi~i«0FJv(‡Þ|«+ÖÖlgkÿ§®Æf¼QRÖ:#j:wD|ð‡¦ÍÃ'×”MC`h<ç?cÜYoÝÅ%iú¼ê+^ÅU7Þ >Óe�¢ºÑuZÔëË戴T乤9ÙÅMTÑ¢W)ó¥|›.ƒêôÙYnR«sòÔS¦ég<ø¥¦²$íÔõÏÿïl|Ò9“a|MµZûúF½9“çY0�–ª,I@i¢r�Í$%«�Ó8r�Ý×”knŒ-ÉX5ж€þ@.á-�x½…Á©ÍÀõÄ8w€ ÐNæk›'mÖ¢ì¢ÁfÉo×_äŽ$v%1ŽMáwñ#WîQuUi�w79´c˜ÉFÀêÙ¤ºL”ˆù…¥”ûl[ tæ›[eßþ¹�L.Ô. 6¸™2`¼ êͪÁF™”è€ü,  :0Ld •ñË›n¿UÖCÏ?ÊH²ˆéuÌ~"ŽK›�qÞ;·íaûøvŽì¿Ž7½ò�¼îůç…7¾X”]•Z ñ6Üf�€©ó‚ ˉ“”Úz‹¡åuF–Öil´iê�©¡1ö¼šëÍÖ‡_x'ãû‰%Êêâ%9�£#£ÔMJQ‰´×çÒü9æ�c³ÕbÛî=h›í(ðäÌ~É@!^}òX€3¨Ä–7¢"—rÞÓ�<ÈŸù¤;·³£^qt†áJä@Á»½•á~î¥w?sYx˜ÛLRŽB÷ÿýJK�ÿÖÂýtÓÜúäiþ;c‚¼–�BL JX dÊêKQ·¥y¡žœ_W¿õù“êÁÓ+*5V/91Ä–¥þ¾“àmTâP ÊfæñókjÇX]Ä[AspŸzfiC]^ï+�•�ë0| 0ω §A–gãý~7(W*ž ¶æ¢¶iuÑyŽ2 0�ËJ¾ž@/ãºÛ·*Ô\–³õ@>#@ó¢Ûî$IûüÆoý �~Û†t|KkssÃ(õ_ÿw(4Â0ŠzI÷͵FãkÒ"›ëmvU!öÿ!a¹ŠjN¢föS™¦44NP®¢=nÅØ+5e;�1…=Î-€Àr5ÎÆ�€mht âû�¹lÛ¢­+±“Ëç;[œS´Çk¸þpàeÀ(œ¹e¥Im÷õœ^>ËlµÇ��“œ<ûÝö$ÑÔ´]Ør…íMÈráVlO�§¸ózŠ\†gËmrëJ»fIåìmû¸{¾ý.|£M�X9ž³ÀÒ�¦’yF†å…6ëݶû~t)¦Z®²m|·ßô\µûÃ�ai”Ç.ØìÞXÊcö~Š@S¸!q 4Ò<Ý\X�Ç1°3®pÝõ_ÁÚ�yê�óð}ŸamåÏ<õ~ust„Ç>û КÛ^öjJå2¾�©Ÿ]£¹~¹[.ÇW+"Ï\½ ìûˆ5ÓÙ§�¡Üèð‚³M&še®ˆ~ϱ€Õvm›Ýò{'xö¬Ç§7ù»‡ç9¿ÒQ7î5·ì�0;Çj?ƒ¸òR·û¦(ØRÊ»òk��GήòÇ÷žUóËò¨c6xéá)^wÝœiV"üõ%r8W0a~µÃ¾©[–ªF{&ÿËkºo6Û,—Gб± ¾ù{~�Ÿû/ÿ‰ãÜ'*Œ¢¨T-¿(íõÿ�˜Ž=þ¬4MË`F€Fš%”kUªADº2O1±ª# 0…p0Žœu�O®‹Ë2œ�Šo´i—Ïo84 º ·ä}üÒ[¨\ʆq–ú„‘d¶Ã¾°%¤7@-Íé'™3èÔ�ì—«‰‹,Ûw%@�Ï‘Góó�011YÉi]|‚<í¡&v‘Ke�ÏØsã²+7¸’¢lè\aº›iB�§„ÝU²Î&¡É1i�~k•|xš`úzhÂuι)/ƒk5Î~>M3w.�cu{�ö“_ N‹¸Ñ¤^«'%T°}ï¾ò»¾‹ý‡¯’{¨+�øÊÃÖÒ€±êrc‚L/Ådy;¡Øì‹—ÝTžklª. …²ä© Ú‹¹æÀ5r�ðÛ÷ëì9x�3Ï—&ãc�<Ì΃ØX[gtf–©mÛQ[&}zÙ‚ü�5GX^^”É�r¡‰ñ‘]ÆóÌ}÷HIïFÌ9]v³üåÊz[Àù•–€äôPÕÙ/ùåˬ휿}ø<§.µÙºÚý\ À ÈwŽºÝù ÂÝTŽ3ô³q.†-«ŸJ£é³r3ÆP|ü±KªQŽÔk®�“�+€ŒË¾¶ÞhÍàõæ±skýì/Y™¸ñ^#û'›œ¼èÎÁNë:ð$Ðû²œ´Ó©aÌ�¤ß×Q‘eÒa3þ-¯(D,€U'ÃeT¨å÷|aS2,·Sš¤ãL;�wæ<¥šËf„y!ïaBM®C°œÆÁë®æ>ð/ù±ïø&V.€.¨Tªµ<Ëß“%ÉY‚à?ü�ð9ª(Æ�1C�ʈà$ا£ÒŽÆP³^ K ��—+œ9öY²™Ã”göaçÌ8ëãú@pVäÖ�Ë])Zw³ðÃP;•”d‹8gñžÝ¿§ìÊòÜ‘ÀÊ–�‘’š+3…²oVÅæJSz‹£®ô‡ôÈH?�'Z0Øå<ÄÐ 5Ø"¿Æ€Q²9�×ÚÙÜ ³¶†‘ß墇ƒÓMÉJ¼uEuÆ�²ËbòÂ{ d³)ì>)À+A?vn•»Ž/Éë·,U†A/‹zÉ¡)3T�Ͼüþ’¼P¡Í¶Í³ƒƒþšZÚè_é½ušæc�-°{¢Á5ÛGÌ{ àJœK9’!�á`SÏfߣ€A“¨ÑjAÙç„À­3œû²Œ))¥b€ÜJGW/œ_¨ (»‹¸Š¸  ªE0VµÜM�¢� À‘¤ýjÙFß+Q}1�Ÿ¸Û“Úµ Ù�†R 3-öìÛÇ×}ÏðŸ¾ÿ{È)'¥r¹êà»Laz:~H‘eÂ"Ï˽~¯Rì�Â(ÌòlXë j Mö“þD»Óš ÃxEUkmjCC�j£Ñ=xäèž(ŽšÚ,žŸGzFòœŠNHæ"KûÄ;£ÃÐ÷¬’LA)ììpj/í®ñ¼AiÎÛ ¥|òG–+Ø:À·Öw–3Ef(Tá[ya?ÓŽð¡]©Ä|d¶;Ž@׊RºRç÷áuúk-"¢©]ôò>——Ž‘g=ê•�îå'Izm‚�×b⊼_†pàʨ²�ik�dùÅÒYTg�jh(W«¬¯¯‘¤=B9¿šùn jc0.�=+=3K‹—Ø?2Æé§ŸâÚ[Çñ³ã½ÚqòÔ«u‘>Wk5%/3^†³rñývÛEêÝ“uq6ö/Ó·Ì.ñ¹wwã—ÓÖ: ›ÝL:ù‡k±ÿØ•)3ýé½g¤ g® yð9jPV37î3Z©¿tP ¶<í~&ö8®,…VÏ\ÚT�~þ÷ŠV[ p×)U-ì›jų‚޹‚x@@d¬^ £@s…eöO5mÄj~Ø×o†ž€£”6ÆÄJkE1E`-è}0(3LR”U …¿Ÿ­µ… , ࣶØ+mx˜d Üî!ÄjîêQÈûZùí�w¼Œî·ÿ3~ö?ÿ ”ÖÔª5•gÙxš&è¶Ú·…aô§¥¨t¸R®DÝ^g'¨‰á©‰I’¨îÆf9ŠãRPŠ‚±‘áx:Þ+Ôà–(ŠTÇY®C]an|;7º�/œx„cw}’4éR«6X?óýö*¥�×Õ†Aáˆí¢(¤ Ô�Na0�’æÐÂÕØ\€r¦•ÆäèÀÍ_!¤§ÂSY‰Ë ¬Y å�°½4…-—9W\Kî”`Î,SY/±"4ÈÛ$}+grYŠ7Ò9M3§ö²´´€ÞûQV–/Ë•prünº+™xÁëQqÍ`œí«ŒÚ¥Èr_ÆìJoÞ¿³oÉÖ·„ø6™#ò-GáÆ@[ÚÆI¦½5N‰†r“1}ÛYÆ�.PÈò%ÒyáöÁGäã M´û’ÞZud*l©¤¡Ûeóøg1Û®!šÞ#ßiÑiÑ_8�^<ÁpE34=ÌJ”²ÙÚ$Ë iP4¦ˆwî¦>2CX©£¬½Ž8d{^mN¶›;O7bk “ÚÒ—¶¢„Ì (|… ‹$ç¡KŸ‰ø™�6'xÍKßÄ^þVâ¨ƸæfXƒÊ]i@R2õµDÈ­·�Æ¥ ã^�‡¢Èû`1‹PKyZ²ŸJˆdC¡d?2þCøÒ@¡kº•‰¦ê¼töµœºxB2î~§#Ù¸JSú+—™?uRšBñ]¤íEг´ #FGÇY¼t‘0 xìc²%ý>x™v­Hæê¢¶º’$Úà/åIH.­wY\ëqÝÎÑ­J6ßôÓ•¡Ï-wøÄc ¢jü‡Ö@&-Ôd³Ì-gü ›q [·,µºIÆz'ýR ‚Å�žY7¿þ™ê}·ï6³#Õ+‚øT®2gšÎð œ�‰C­æF«æØü†£ä,èè/û 'PZ&�ûÝ«¦�²ÌÆ¥‹rF!ÆJ•£~*gR²›(@7Ê( $‰¸CûSšzÍ…}­' ØÊ©º[°ý7ƒÍÚeIa-v¤zºÜGæ·¿æ�òö¡Oþ9:í¡Ee•ÇE˜OcT\­)jŠBì9²4S ‘U@km“@ä±!sûö²sï^ø¢;™È‡eU… 5Ã㓼óûþ)¿Óípé‘ûȳDæÚ´ÖæYò.†Ž¾­#çž ÎeÀ5kZÀ´súeþ½OˆºNYn #nŒ²2j0¶,dkév†Š€¦sh6Y�ã`•ÂÜæ=:àó†š)÷˜&Òd‰ìÌ¡5øÀVj„W¿œìÒqVž&Ò)õz�¹¡ë§éô7Ƀ˜`å4ãa†©ÉjÖÖÖ(L„ªNÂÈÕ©]èÚ0Úfw2lMü¬ŒÍàâ&ijÙ/›A9ðÁ›ó¨S€¶ç<ï¶àüT‚Bl—©•ëÜqë+yÅK^OT.Ca$[‘ÛH²vÙd)/%§Ê\ÿ Ú)¬ä1¶G¡~Ú/=x’gä½Á¶¦P•PøU�ÐÝ)ÇÕb@À©97ÁÛ¾þëøñýaFÆÆ ŒœÉvÎ�<>œý(¥�èÄ÷¶3€ª•š¨Õú½åR…n¯#ç²Ûj±vñ‚#È#µ~$ô�ýKY6³ ´€‚òÁÆ-:rÁpï©%Î-µ¿¤·?µÐæ�3Ëê•GgÑJ]±F�d‘U^‘ÌwsiúêØüº{Þ—âF00ã$ Ôtö˜‰fùÄ«_ÿnªÕ†€ºÑö}C…Q²Y©(–4yôS‚,#©V¤Ô,ÍÐy!e³0Í0JÉã:/Ðià—âܾ ˜Y$Å“&ÓNÉ[ f#‘‡‚jHQ�P�áBMP‹„&g¦¹îöÛyæ‰Ç˜?uŠ®p1çyˆÍ[n“¹VÊ"µýZÝ´QmG|��N°¸xQJŸaÉà6g£d3ï±F,„¸×ÃÖž™¿o)׃“…Ú�40W ÚðÐÙU�õÂ0ßèÂ|î©EuÝŽÑbz¨r%g â ¢…Z)äï[Ya8u¹…PlÉVô›K æÑ3k|fì’zÃõÛ)…úˆ†¨ÿo>ÖŸ§l“¨O+—} €I êËp”Ò9˜‹q¹¼oPR‹ƒR™îÚ yžÉˆ‚ÀvAë,G‰HS 0¶÷fUF8Ð1(6†^'ù �[¾µ ˱Ÿ!·F�Ê�Sÿi�ÊsT¹Ä _÷v¶ï;Èßü�}êÃݵ±IÆC$YJexTæ‡ì»þLíÚC­Þ`OXc¢“¡�åLJšÊÌZºùsL)„,»*ºõ*Ss;yç÷ÿs~ùG¿��‡Î¡0Ô«eÊK')Ê5˜ÚK^àæ¨…×té�MVJ9ŽãeÆø†�¬ÒÌoucˆ ë¹EÚ�ùì8岟BÉëmVàÍâÑÚ¾Fvö Ê>†sªðrå@×ÔiÁñZ ßIçòI:—.)#™h\©±ºÙ£ßëӉƈßõqþÜ�°äF²®¿,I͇`ˆRý•™þY©Š²‘,”xÁÐ*|4nd×L» +§`J\ÍéÁK]� û×qÛ/Ð9¹‰sN³î}Úám?ëR ¢1 p8CÝnKm’~F˜” 8­^ƇâOQ°ÁÅ‚tyÎÍ+—¹~å ƒ•u3BêI²‚Óe Ðn·éöúŒFCZZncÆøð@E%îj?S)›†E‘óön5P 2­kÓm%ª-‹ ­ú_>w™a´ðÞ è°u0cëpÊpÑœõçžä¥Lò ºÝì¹çå¢<š… H^úð½_¹Æ<džw\Ü qù´² tÛ)‰Hx=kj¹“…Ók]^¸>”zK@öÿà�^+ó<ìîí€KÙzåEª¢@º08«;͈?K-èªÛƒrgDÈ+{`Ñ`Ñiax]q¬µÇSDWK~sÝ âÀYÍ”)èÃHÞjé`vç#ïàÒCoãý?õçk½ŽV»MšhIg–×7i÷ú ÎXC­ÃÉå›Ý.”�PVˆS?¹Î‹I D ”x­k§Îòá_úø·W^a´u]åã©&ì?óIúY›lób;N¢Å(¼€­w‰ÃTT÷,P„ªf}P«ÞiTk˸�`ÛÁr{¼n‹Œ¸†Š@¨Å5j´n�øZ�Óc‹Scà9¿ QÎ ÖðÝ·Ql¯1›i.ÍD4iTó˜Ú½bÔk£/Yêj ØžH‰`hÕI£”¾å�éõ¦©ájég,¡ÑC51}ás¬·Ðœ“ƒƒ=ZËë|àçþ2îûæ“§)ǵ:ͺ©/iV^ܯJfu•ÎQ zQ›$Và\]fëÂi}Ý™N5v9Ø9$-ÔRÒÄjÄþèàÔU§àDáu’ç÷¦HGïW�ëœì¬ó®w=ÆÍ×^£Óê°¿¿ËÍëW¹vùeîëÛ"‹Ñâs&gcMDc9N„áÑ!­Tss4.ä (º.CSikQnÎhVªµÐk',w" ×Ì�^]iª$à}àÍ´I^…g®ÊgWB–¸ã‚ôaVT2œ”„D�¦{l”‡˜Ì±¸¼ô‚ ãYÅ¿üøËÒÉR>·zœ¾©yµ—ºYN HØê€8èdlÚ¼À�¸}Xþ¯Â°ï½¯fÓ‰ÒY¯‡ˆƒÒ³~õˆ¾iµnìIÜWë†úM6Ú\¥Š³VìalªB›ÁZ6Í¡v¬èF°ý«QaT@Jâ§£¤Ìé;.Å] × #Ö‹ÊŒ¨«#šLJ5•µH?d„�È0 A-¼àãExë\XqüÛÿÿm^·G@+)º|ÂÁ“c󱟮28&-£�õº, F@ƒþ†¾èƒì |0rœékVyd^L7Í‚è1yÎô¯RI¢KL¨bŒ'¶¨­Au³–Ê2&`Ö�MÝ”‘GŒ£•%QKN¯%@‰¹ÙpŠ„­“Q„¾mÄanE£k‡�ÁˆµÁLôw´ß$º #íÛ\ˆ–Њ‰q uwf‚göâçÉò}Òî ‡‡ô—×yë‡~ïþq?MÝÇÇ)©µ¹1†6—Ø~µ{~�ÇŽ¥KMP�ÛÃõ•øÌhOs–öi�¦$!Ä�1Б*øY£‘¾vÝ”w?ð.>õÑ�ðÂóOX®×SŸû4ßôãrd«u}µxoÖg——×TܳÕn[}$qB —Ñ ò/– «gŽ:•Wêðær+‚³kª¼º}ÄGžºùÁÆby>Ï\;`뾜]µ€}3GI7-eݱXC-Œó’�áÌÂm,¬… ·0g”…oÿÒUußÍ…Bë So¾•:™Ç{BCî'4�7U%¨µ%àðòÿí,µØë/-—››'tpJ³6ˆ°4ð ~%gîºÄh&ПbÒ´ÚåŸY÷°÷ k @D]Ó»g6¸vï\»ïnÝ}žéJŸª•˜èg´Äãkxô‘÷qæäº�.½^�b4äÊË/bV›sq)&úÚDÛn§Ç`°D™ç˜Åí½Ö­‰êÚkíMÅ«aµ×n(È‹ýFO_;àãOߢ²<«7Ýd8)Ø9šéuÓB’ˆª;÷ÛiHޱ"±e‰ÈÞhF¸ý—”æÔð&…§®úzêºD†4Ï¥É×YšCXs™&¢l¿Úº0ü¿p½XD¦Híõµ¦Gk—¥[{êSÁ­tHÖ»hôÁÌb-ty´¡þù†VZò¤ñð&Áëç£ uŒß¤ÊÆÐØÍ¼×Ÿâ²ÝªÉ¨..>!�?½O6j1…(QR)NPQÅ^V?—\Ó¯¿²q’oùù¿’ïQõt:fÐë’?÷ ªÑ>…ÖP·�Þ>ç­¤±5+!¬Â’e]ij®úì¢;ßi×x�~¶Šõa”úkq‹4U ˆL5£dÇj­(PÌ»„Ƹ²DA¢òµ—,%­Yf>@À¶´µÛšk’Ø ¯I�ÙÎR]j©îXbÚ¬?‚p´XôXÄóÆkWe„ÄI]‡N÷«|´žv/SoB¡ÖMYzþñ?ƒûÞùXó^­—=°Î¡YY,”µXÏráÔØö10 XÓûu:è2Zpížó\ŸÐÖÅ3ÌblÈèõ!¨Šõêê % ¤iÆÊüýµW^2Ý=eáÅÏÀ×éøÚaiiUk"ÙýéódÃz g�H§å!û#ĵ\Üì×-Q³t&óYc=!¼ywZ}hùè3·dN<�cöQ 9µÒ ­4‘ø�ä¸�=Â\½€£iYÛgñŸ{Á:jBŸ2·¼˜~Q­œ ·/g}\Ñ·pîÿzÀ‘*x%m·*—$^³°÷÷é½z�¤¨,v™œìcºFSP-¤`™×êXêóðÕÖ¤Ó"ÆT‚YKI?ƒø.ö¦„˜¡çÓš"‹]ª²ÐG¢ÿôᵇÀ9‚ˆ‚¨nM×J_Ù¹Tn§é^qÂýï~?÷÷Rê Ü¦ÛîÐO…üò84öAå@"ƒLÝSÚ5þ�Íĉw·®w5ec€,ÕÐ EUÆe s?™uD°Ñ¥Æk:í”$Sa®b~½¤³·Rz.]‚ „F_¿¹½j £Ç2‹¬RŒEáÒÄde¤&®WÕbUeé�)-üŒˆçÓãS”•.µR­JîXùk¡* ¦Wž¢*´®�æ› Nœæƒ?÷—Ò ìþqܾ…cúâÛÌHæbkX½›+²9F[¯íã³n›Ãõå9ø\`ïÜ Ôbt›keœ;u‡c4:Ò<´WžzSW ôæ2‹î1}=ËK׌Q-¹¨€ &¹£ il:Õ›³*�…‚±•æ& –ZyîR¿w0W¥l_f­ã¸ãüôœÞþc—|ÍûšübWXht‘F<§ ®ADhžC×(Û!²8nd¿»öNm¨»mxbM]pâçNœSbÄd<æh¸ÏÍ˯pùÅgõþ°CÔu¡îúmµ2ÖNœª£¢æ}8©CŽ©@[š¼¯@�^¼=œ)Ø\Ü\BÄÕ?«™ü{æEé� ù£m–ñu_ŠÒËq�Dï9 ‰“Es‰0+=GÓBBŽFûQ–¶¦ò!¼tóH]b Àêv_>äUæ¿eŒò¯£ÒÀ2ðÓ�·‹Húu³¥ï9uéâJ’$�‚)JÄÇ@÷ÙåX])0ï19N[ÙJ9<¹†²¿X!4zý‚êHO@©¡-gTN©×Ñ_MÙNÜä¸bOÚ¯ è0¶�º-0�&ÙIÜIs$fÚ'NÏÙšåMâ�öͳwðá_öH–V�GŒŽŽÐº-Wž šMã¾X�–„ šu`‰VD‹±X¥Ï¸HRgº`R‹™HDD�±Œº5kÖ‹söÙø¨Å ÖȼÛXÏaÖ`5s*µ‚â$¢%¥e®Í�–Æs6›Ž Ÿ¨î›å é9˲ŠZmz.³ÜÄév«¹c×/Q(Ó~K%EX©èpó9V–ûQaBظçA>ð3‘Òé´…±FûPê P ?+­!4?Ó˜85ï5t½m�<(ËíæùS\Ÿ[<‡§ÖUcШTÕÊêi€×^zžÝ½me ª7J~Í…¥B ûÛ·°ÀE�˜Ò¨oÓ ¸ÔÞî†J4Í�Há²²Ï~­Í_Þ…¢ò¡i%¤NÂ]'—¸c½�°¸ô´¨¸¶7A¿úâ& º;è²T4¯§v]þ8KèõЏ%V6ØÖÏðµ6þ<ð�€ï !|/ð €Í¯àh;?�—étº º}æ¬5”Ýr{Þ»¥0Ê›G‘5„Y“å³nÝP{0Mé6�Ý9š˜v•¶vJÚ�¢ƒ•‡Êô¬P+ Yä'·‡0Ðhµ}Ê,Å‹ÔÀH¨rõù#Q2„Vj.H‘çÊì™N�8ºþÕî•Ãp�ˆ–†Y-"`y%q¶®B AÝMF¥NõèöÙL]]º_š¦z eé™N µn"íÕ ° 0j.)i4×VL:ÕcX©q­À¾‹‹Ç¤ib€�‹žU°6Å(à.ZbUº2*Dú6õº,uê±Py$eD/‡Ô…�ñkÏ’äj­åyÎh2æm?æ'³~ê ² UDjÖ·, †2�½¨»Íš@â�‹ÕÈ÷œ4îצT[6G¢"K¹uö$w_¤¿¾©¢¨êêíö8ÚÙUÙýýBðõk6ðV ¾V›)Ñ÷>K ½…¦+í6­þE”vÇf?NŽj@‚7Ûå|�šéóòØÑ@¢´N¸ÿÌJpµNc)ÝV*Q‰àÍwí Z­ÈU=v½«oß=šqhÚn`¸Ì×ÞJà«ÀQ™ÿø/À{ŹäkœV«s¹œÎ¦swG‡$zL’ÓK–tèÇ…&�0“!ïwm,㳤ô¬÷…M“Þ4×Å©5q&Lêô`åá)Ì•³~Wé M_¸sñõëÇqpò^‡˜ém„�Yæ*ÒI‰uèu,6`ßÉ,¦þò2�ÎU–Μg–Ï4�!àæ  ÁÀ)ö‰�ó”QÕÙ×â<1°®ûFQJÝKBÉ@ Á�²×4þR1FXL5¶›‚[t¯Y�'´²Ìf²ŒO¨LvƘwöVb!‚©tó >^º­N†È’ÄÊO8 fŒ>u9¶#ÑÀâ6é˜z�¡*Æâý“@,² šV΂ÜdsŸ9«“̪¯9c:ý'Î\ ×[ÒœšN»C>™h>Žs “ñ£y§ÎLkp~�‹¥âßDU˜}4Afakê­EP‹:`ÍýÌeúõl�_!ª X89è˜*®ßNe¾”¦qb—˜ëö–ó«œXiÿ(=Tà"дS§ÖÝ·¡ò£ñd|+ŸNŸ(†Ÿ@x>žçFõ¢mk.ø�ŠþàAø'~1ðNŒÝ§µ¾ÒèfûÞPV¿E’äßþ¶€3šŽõÇêv{ƒ$I(dFwm™ìâºÝáa³G̹édTY¢‰žÞ9¬…××{h>t’ª•¥GЧFÙi®�š|N¬Mb$…ª•è¹�€_ i`éb…$M qVgŸñÁtÓ\;ƒN¦®6ñž$/!n‹Mè`u•‡ûfv_zŽéîãɈ–¤d»—)7ΓvºÉ0&Y¬ÿ« êÖhH·˜ï½>`[$H]l3ZÁrµ²ª0êYtQÙïc›7_¿É¤è¾hEÍÏ î¶³ã%&ßc iZï.ºËœ8 O% -Z?vüÈ‚ žd÷UÒv¦às4:âÄÝñà{¿�¦UáŽ�Æ>á¶Çh.r'‡†ÎZ#©ßî£ÛÔ13‹Õ@pabi<––_oÓngäc�R¤«Š E®qñŽ@Ið^E>�DåìF)ê¬Õ¢Ýí)àˆ8öG¥&fö;YÍ: Á¦ DAï„A7�à!+Nõ7—Ú¼÷î M0ýäó[J“6eÊåö�V=NL4Ÿ¦«É¿è½Þk¥ZgçÔ¼Ÿ\éª:³=«Ñ¦±äÀîhÊÞh†¨5ÒSµŒcaMK)üÜGïT�&PƒNϨóõg�Y¯E�ôò¦[À…F©‚–ÜÁâÈKÞ÷¾Çøå¿ä—3�ÜþÁ�ÛÙÙܸyc0ïÜÚÚb|pp×ÎööƒÓ<ÿ©¾,FUY]É‹ü«ù,n6�|W€íPù_|[€W€ÿ|øÇÀÛPyËÄÿ„sò©­}÷Ú8‘Bý×BUM“nç¿áu'MRW�'®•µ‚8aZŽèŸß@ú­˜å_Q¼²g ž>M([™ºÒ&ýŽ™ò4\ÎD4ˆ¦(J ¾ K—ZíÊIuÚÀ+èÆËŠ4Evîã¸6‹­m¯ : Z^“:‹x:ªÒGê5 |’%�{(±Aê.�ã2ϼãƒ?žW¾ú>þþ)!j¿%“)�wÃæBí²1û¾žKC|}ÜÀ-ñG­jŸ¢=˜ëÌÇòVž@bl†hu¤Ñ¢R¡O$~V·Y¾Kýj%•žH¥µÀ½u�Ñ^ÿ.Á¬,ý¤;æØX?M\dæU¾A” à«x\'Ô];z^ ºõ2“kÏÑJE[UÞûžo`õäÜ›mqî�YQQ ¶Q=Yy8 Œ|_'(ÄeÓGÓúª}V°Zì Ùq¡NTHRf•'WV¢CÉÃCU.«Õ¹ô5J—^.WUΦ^üoýÄiÎ?ðO}ü#´^¿ê“iKƒ•7Þœ+½‘ób‰¼l²uÏ©eµt:¿ªù8ÑŠS�[ï7åp¢…ë䨼~#ËÚÆ®W-¨ÿüÙWµHšðè}üì÷\d)Ê÷`¤qìVË!KRè¤�Z|Í ¡a$…xî°Xê“ÅE¤±bg8åhZY ®Õé0XZR)£sçÎ!"`yj•>g³Ù”áp(»û{«sðaëæ­ðê•W/ݸ~í[7oä×®\ùÍy^\!¼]D–"qà%`ð¨×ß§o[_rßvv#üÉ/¿(×&¹«À߬&Óà#4ZB­õD~QÖnß=�ŽÓb<åƒ?é's—V ª[Cüöb¢YÞë*àœ\§Ê2 ž4ýÝá Ð8–ö‡ZÜ Áb)éZ—d¹ ò›#µr$ ÖÐd¹Ï¬ßYÃÑ.ͱÇ<ÜYYÑžLMN']j“tR$qÚýX‹`™;«è¶´NJ‹3ÌÛÝ.£ƒ=^øâg´|š¦´[-�·~uQB¨c7–WT~Öî¨ß}I´@lÆ%ø­Ö¤B›2uU*«î©V…>¤v<]˜‹Žº5IÎ Œ0A²®@Pú$n·YsÀŽQ·tâÿÍšÛn€YhÊÂs‰’Œ¢mÇ*f¸+_AfûZH0/йuó ?é×ýNúË«4växñÍf|®N�®¿§ž¤i½žðÙ´è�×Zk’ 9yZ°ïâkÒ‰Îös_%Œ÷¹qõ ʈì÷8qÇ�,­®¡EÖÒLï±ÊWZÝ·(sœK¢`gÐ÷×®¼Â«O|Q«àžÓî?½b…¦3¨N#Íõjq7¿a}ì›h-w3�{¬öZ R‹­+^½¨Ó·äL‘ÄÔ§#ÃÌÄ=UªF\|�A/~ÎEk –¨RPKxõ°ºA µí˜ã(¶®ÃÑKý%Ò4U†Ö~Ö/fãì…c@fqìÄT¥Y|P�ؤôTi¢ëª,!Ì{µø\,_‘Msµü‹X´LSýLÇ3&à™Ðô4Ý|ÍÏÅ¥³mú;»¬‡k Ô-&â8{ö,Ådl5™ª²DbÎRé+Êñˆ¢(ôw –£AØï¼´'¤yÉ‹KXXU®ìŽø�'¯³ÚËøð#gYêd‹hõoq»‚üJªxéÆQ� íŸ/Šü“ó¥ä³Ù‰²,ïpÎ-3›2|æižyò 'âÚBheIšözÝîÉsç’ ÷Ü+sk‡Kç/èxÿ™O}Ò܈úm~ß[ïâd§Ås‡#>»u ŸÜ:`wVÐMy÷æ ÝxÏ¿ssE~óCwø?ú¥—’*ü<àãÀ?_PñS.qËΉ+Ë’nw ³Äjw¬IžÚBÐ'ïv8ôëxlš&Gh¬‰÷úÀ"Hgq“˜djµvB ÄU©#Ì»=dÍÂX `¹�¬«¯9“%ú©XYÉâõÌJ@È&¹]Ó±.WÁô ¼…b0àèÚ«ôº=e¨ _{–¥Sw#ƒuT"pøZ ÙXiÍ"Yæ��þš2r@j:iy^S,K“¨M+eZÍtsk™ô‹ QKÍÕhÎ&ª‰ Àa4ÐĉåÕ8�³y³Äè6;Ÿ –³T–šØÚ˜ˆZ–‚#M�Õ·©ošJüpèdþ»¶F[ʪʋ™*?ú3‘Vhm8üéNgô÷�èŒ&±Ê¬ÇUZÖ<&úÆIEýe( =‡Ýt­¼‚†–ÏÐk,fJ¡¯�'JÁ1·t@ÅnQ+a´¶¤õoÊv /±ºg�Œ ¯ßߺ¾3 ÉgZn„£ùkZ-V×Öõ= ÀC°Rª�VUyQh¢¬�•åUV6OÐ,1ÜÞPÙxV6î¦[­veµ‰žùÛÅ€áG•Ih6›°4gºMæÐ ·†|啽xM É'‚eûžÇ_ã�3Ë :Y-Š# ¯ ¹îÉ×öÂ?ú‘ªý£"ˆ0ŸqòË¿ñu.)Y\OÇšæA=wã@Jol°Y’$ÿ`ãÄÉ¿¾½u+ !t|U­Îû:Ð*‹B€ ‘Ƽ<…3Ó"ûþÑÑÊ /¾ØMœkµ³¬—O&¾¨|¯Óíd \èwXÊRVÛï?¹Æ`ø9wæ|nû€2À{O¬R÷^¾ws5ùwlò^Ý -àwD²Áã4'Äõ“$u�^OKK·¼£Œ>BD‰ 8;ç#/¿FÃ$,Hˆ“c9ÖÚ¹º-jE7Cé¼"»1š²â’WUƒª•šëª¾ � îf“ÅÚ#ò…ÊÛ@´z\æH{)Å´$ˆªÔÙCµ¨d� éŸw\âôýñÊ—>£ßÊï“Ü¢;X'1·‘PÚq•Ú‹þuiþ¬M½.�ÉÕèº,Q r,B�6âCJséAPp±$PdÅáE^âœ&c[�¦�MÒ X‰;•¯)5 o åL&?ªDÐKì¸Þ[ßkÖ|I¹w�A»ÅêÊŠÎÒiw¹ã¾‡@DÙ…IYÒ;iï�Õ5ê" Þ uH;!dÒNõ5.ÖFJâ æ@¨ºKbP¢ ¨Úxåu©qG<0+QrŒ&ë=dùg¢ÇB«{VY¢â›e–Q´[šr�wÛLú]µŒìªÇG�+qߟ¾ó^¶ŸúùþŠŒ}Q2;Bˆ.Úh‘"u©£8IðìîÞÒßr©¿Ì…ù}|ëåu[^z^Ù>ÒÀ~ü; MŠt,ŠïÑÔaë›Q‘IQ©\ËR'›wµÂIä˜=UwŸ9Y6†Vú ¨Â±€%áÕ­1×ö'Üw:³Éߢ2ÍvuwÌýü9UQy”óÂk2œÉpBÓkÚ¼âE STž�?w‹/¾¼W㈳ |H“±�+`í³éò’”ãn‘ç«@8Ü? áœÀz»ÛûÉ w|zë�çŸáÑ+|Ó©5¹g¹Ï™^‡o=�ÒM�æÕ/¸�8ù N†¹¹ŸlM < üiD~0NµXz¤ÈsQ7BqsD•­X€ûh}™½3V:z‘'3Ô–ÍZvMøîGjå@tQàIz-p¢ù7þh†5/Ž¢•6(  $›ä Íš”É„YP —ã�$ØÕêJ¬Ð_:Ë!Ô\ róYzl^wåÉü.Êá�2‡ÖVÚ;Wð§ï%fì[BÀD,#å³qX0=õÒ5‰š¢,hÈ".’$–;ˆå¢¢´�Fl„z�ÚÿH, �ç¥Z ñ!·²×µäPST˜bµjz]‰/nÃù­®U�U[�K´<‰ˆnê Ƈ´Žn±´ºD¿7ÐÙüêwrÏÙ;Y»¶E{4U‹#›Ì�Ë+¸å²ÔÆõZ(à$šo†M;Wó¨g@tÑ¿—©›¢ƒm,~*�1¢¢wRÆræÓ’Px˜PTø‘.õ9Hg~Þ K‚^MUªup€†k˨²G¿«Ka�ú�`⛣Ɍ[7®)+m Žk/>Ï—~èûÙ<{žv·CÖíê6¡,£ÓÚóÞÒu»»[Zâà½÷ñÅï•èV„|ê†&Bƪ•µÖñ!óiÅ�jQDÿ`”ó•WwÕ]÷è='4Aò6ÚAµü´ã(|4tˆ³¢b^ ”¸®1;„89{ï=ëZ¯ga(iA›ä?üô ™+A“—ÞØ’DSW·w›-<çå�QøôóÛn8)\­Rów¯¾‰ãŒc'&‚~2[Y–òh”¥iú€?|¸ ¡}kZ„o¿²-ßýÚŽ»ÔïÈ;7–øÐ¹MÎt;ôb3åû ý®üøùöùÒ ™·à½ÿ !¼øHJ­ei+‡�TóFKÒVÚ…iÀ·\ÂdeÀÖ¥3 6ÈëfUÛŠ°XÚF[RV:¨ë &Ãêzzó+Y¡œù&¥H)Ñ‹F·Ö´N´Âhâp•MuÀÐ&€hm§Hª’ Bw4a¼Ô?6ϨɊ¼çmïæ?ýð•ÿñ!TÌòœáöÚÓZ+'P÷�R‰Tl«®%«²]kµœ�D¤)¡b !N,¶ceg26ÑR²Ù,úiêô3V 4~~V”ñ÷cÅé>5U͇ÈËÈ‹#(õ9¹K$©×§·\ _a¿¢Æjté¬6�o=æE°ã„"§xåËt¤ÒÙøÎÞ6�î€÷M?™3[d³Bï5ç½vé¤$ë=Mê5K&M K�,²ýRu¥"ÐÌ$Ýj'«™^·ÛzµzòR×Ù½HÜææ�4v¨ γ Æ3dZ*¥“©&Gû$aùÖžÆzÊN¦–ÏlÐcÜïªUT¤)Íœ°ý›×˜S_9A`8:DœãÚSOðò—¾ ´ù´Õ¦7XÒ²é.Ë´ðb«ÝáÔ�w²yî<Ý¥eD²”Á‰“Œv¶áúî”Ï¿¼Ã‡>£÷LýÁ � !(à¤ÉB°Qéï{âŸ~~GU”Ÿ½~È/úÀ]ʆ‹PÐ4,°"²À(ˆ«›®½NÁ:˜…‡àé´ÍóùæOq~­§ÄšF¤H˜ëù³/mÉGžºIQú¤¨×Þ¹wÞ¹Îj·µÈ…v[ë)@¸±?‘�á,TÞÊ >üå¤Ó>ú:hÎåz<‘ŸOoMôü9V x~8qóÎg·åϼë>zqÂÙ,¤ù¡³Ìã=1�Mh»åÁ6tW-á}ˆA܀ѤËéÇ΄1„êã1LUÚö©|ÀXi.@sX 8Æj.;³”ˆ:jb–V�x|#.P×~«Åƒ¼Å_ÊPÕµØ <"Öòuê¬7©×g©'�ª%ê5)S\Ž•ÕeýýÙØ8ÍÛï˜nH–3\«ƒkÇê±íT™‡ 4Qõ£ÀbK©ÔgPÑÕJ¬Š|žÀ~{žUðI�’&ju³Eñ?6鵬“íWT0.�Y£)<./iæ B½ý#–Z©Æ}ŽV—Ôõ6›wâåÜ1'M|á{ÿ;­N›N»«TñáÑ¡ï´[eê\+ŸM)Ëãý\-Õ²(Ù �^ÀµžS*ùú¹ssð¹‹Í³ç¸ëïâñïÿžHb~èÉs@XæR´Âñ³/ûiÒ¨bº(6ñ¥—÷4{¿ò�W·Fü§Ï¼ÊÏyô"sišZÒc¨-uªhˆËc‡m«û³ÚÍøÖ‡N©¥¿s”sn½Ëj¿¥çxÇÅuúíT»“†/îõã+#ýìËÛò_>{…I^Õ†M•GέÒk§‹-¤×Ÿ/ËõýñœÂ}C†“"ngøwâä ðõkà3ˆ|…>ü%à±m¶3–³”h®ákÒDGeʼnN‹o9½Æ3ϽýDlVÓ™¤ÔÚÜd>}îÒÅuq®S–¥J€„n‡¼ÓÒ`¦+cq9]'‹U8&!3½ƒat£a+]·FÖ˜‰€b�5WWBÚ0;æb]ðL5ýœ0^]¢5ÉÍ\ð- !xÀ9u±„²8F7jqy>‰NÞûWçÉ ËË+$.cïò“„Õsª<Ô‹ŽÅ°ˆ‰`–µìïha Â˜„˜¼™ØH­~@’DÅ5 „QpˆVJ-£±ª3ãbóU°ãŠ@´n,û?.šŠ¸ÔjŠâXò¹ªgTÖd %Æ'FâðVZÛ>II¬$·Üz‰•¥%Ýv4ÒíöyçcßÀÆùMÚ­Œd©¥Cê´‹€%ƒx�Z³]ª…QBŒÁ„Òkì…fIÎЄ§bJæ `ÄlZ zîVZSúŒë¢…EâþªågQéD¥‹* –��ñ¦YE²?!ŒrüQN1+Ȧ3–Ó”ÑÆ úÜzîÜbyu•Ñò2xO{Ð ÃÑpot8¼é’äÔl6ë;'™ˆsi’Hr|Q0:ÜWwÜÁö-®¼ô<�/ž“wÞÝaÑÕì„ë»^Ý>R9—ÄÉ·è„*ZÇ šî3Ó‰ #{îÚ�ý‰—ù™ï¹ÈÃçVIiäk-„€úz¢DÍî;¥×\V�»N ,NÚ8¦Ñ$eñP¢mVVá;¿|U¾ç+×MË:ì)ßi©›ÊÔšW¸è}sþª±›O½°Å3×-›#äÿ¾¾þÚœD²ü4VV^#„CÐ@ì£À/Ö�PfÎe�¬öçl´‹ ²sÖ8ã½Ô…yNŽ|zk¿šßø´qÞ ;Û[yo0 Ë26NŸ'iwði¢€Ãb E¬©ßH�«ë<5^[FyQZ15œNj³Äª ÑÕe¾{¼8 ÁÖi&œ×/²_›Lºérnî"1¾Á¤Tß~ >\iŒãŒ ˆ®™E6°4W¨ÜÍ:ïž»Õ.åóz¼áø€éèéÁ5’Þ½F'Æ’A£5Saµgb=f ›º@ÌÌW‹Å”œ=çµbjE³B=;^rK¬é¹¢$ŽIÏP=ÓRs8 BGÚr½rs?ŒK_I¬©ó}M–šfËÔe�^BÆlx@éKÊA�ÛÐd6’8%¾^´o¼VQzqÈ„˜å¿¦" êçGyT0E&ò^»v5Ûbu“ÚYÖôP×Ñ߬Ô·ï2g÷d:Éë¿EóØÇ°Ø„Þþ^VÏ^`ëÕ˜N&¨Üþ­Wèž¹›PWö‘gõ51ÏØÌ‰å±%®Ë$1P±ÙqM½@ˆèˆx»¹ë—ÁëyÍ¢©‘Jb-šDýÄ ­nÉ-*ê4e 5æSMaÌ£xÇk‹@—@*õ}ti¿g+MàpHá+æRœ>y†ÓËó~æ ÷½ã�ÕFz½ç„�1aû†3Ì"1€�à£á+:–˨iÿ u!I„>•º¾ôZkqI±?®£NwGP`’4‰ì¸D­h–Ûê^³'º•€S‰e\–ò …m=º½×&ñ7-Iç=—Py˜Í8±±Ép}“ñdÌÙ Ö+«+_ýøÇ>_®šÍ¡Èt.yoše?^œ{_U•K+«k¡×$ï‡C9ØÝ ÞWâjd—ypœ÷©—ùUß|/§Vº–æUuGÓR»°¸-wZM¬®“=¦�â xl&2«”ã> Vì€nŒ5 Z 6ãé¤è¶^-þ4+á(‡Äeú™tÉ«KÚ%ž[W_cwo‡�Í“lnž”Étšâ’²6×:œË—�o�jÀËq½ºb:;\]]zùtšˆHÖî�´Z­ÌfàWå{-üŒ÷\Ô¼‹ÀP•yæCx]ͺ·œ_ãôZ—«;cj- m 7FüÍï{Fc0ßúà–,§¥" �oéõ]¥ž«Ê:8š<~eŸÿü¹Wåæ¾~¯c�)qÂc÷žs€|½ø�‘$ÒdE"ë�2š•až ÊS—â*³n¾[œ{…¯Oû,ðSb�›ŸË œ‰à#8ÈKùìö°®¶àEð‘ðiÑÌØ¯Ç„ÏdÔì&àL¦ã×ÎV{K$ ‡Ë=|–",n�×oBÓÕd µHEv6ƒ”–º­@PÂ@ˆÆøI•&M=é3ò¸¬ê`" Öš Í¢lµÚþÞ¾[¨Büο{?ÿífŘv«­û�9{ž·=ò.üÖŒñÑ�0šábÌE”| ôàœµ|.Z7>IêÓ%“UòÎáS§û¸˜Oå‡ð‰N�.ÁXj2h3KJ"˜_ëJ0\sy§„›€�)‘es€ Ú%dÐkÁJ©”Ѐ‚�’nB{Ðaãì)F#•±gN»[[•¯ª´1;þ1Ào6DÄW³R’vâAª<Ÿ-]¿~õ`2ÿÕ$IwúÝþý�pNDÞB8G¼M?õüvXíµçõgè¶S³ý‰ϭéZm·õ~‹‡Î/+m¹NQƒOØ>œñ_?{Eå]>ôÈYîXï«ÄN£-ŠóÑ „ø·Àt#d1—(é3ãæáTæêj‰å¥_dáD8¿Ñ �Χ�.´n¬Tô+ÛC™«dÛ¹GÓ‚ûé—å£OmÕ?t ø'À¿vYøúµCàľ ¼/–x$NB@›*fññ� px!~þ;âûÅjщK^îtºÕ`°Äl6c”Àx¥O§^~¶©…ÖœÙß–™fM“ï\YÕÄ‚\“¿Q±Ìºüj½XZ€ºõâe\7­¯P¯Gߨ'Ê�´F“H[óHMÏÌ¿� nµª×1ñFû-äøÐŸˆãîw¾�åsw°õüÓ¬®¬2dˆ?¼I9Ü�Õ“ænJÓ´^—ÆXc¦ ‹å¢­Bf0YtÝ_÷«Ä(ÔU˜# L;Ä’Üfq(¸Q”¥•uN]ŒãD©›Py«.Z¯+ÆëÁnW‹YÆé±m"øE�3Ùšh yÓœóê:ân<ÍZ;c\%š´ÚÊ:´Ó.íSLw§ (Žh—xccD·VR#8‡fû‹(€x§¡(£Dœð@0¥¬ +Kô^®ûPõÜeU¯b«ÇÑõ €Ñ%ÝxÁ ¡–"Ì”—RÁ„0Îñû3Ä êJ[í"óîV;ÐÎPëg¹£akª.¶£­=ööéÌfSï*?^]^M¨µ¼Êߤ"â%uÙ#¿â›8ØÚ ¯}×Ó™W%pv>6<üràf‚dÞÿ<ùÛq0J‡“’ù@,«ý,|Ã}§BêDB,&µÜÉ8·Þ�,òQ”ÜÂ7?pZ©ÑÃIAsŒ “ûÀÇŸÞ⫯ìóá·�პQ�OX¤Ü”™Bý0P±:ùjõ<•·#(嵘ìó¾ò–üš&xƒlzHYåz~_•œ:{oè=ôÒN]”* Q«¥ ZŽ¢…Ñ4¿«ÄãÓ”²•j¢åx¹¯.2�>QKM™`Itû&•.©ˆÒ?DK%&7wFSÝîŠRÉ2€ZÓiÕ¤Y)¤ö:4Ôh}PwÜ|Ç/{¬Z;u"|öÁ�¸ÏüùïLœs>øð­À/çþ2P‰ç;”Ý? ´¹Ýa.ßõåk2¯+æÝªˆæÖ‹$Î-Bû~gV{ü¸9ˆüÇO_1XÔ†ÓR8ßuiCÝkÎà"DK¦Y À^Ø3bM lÔ••—•ì� >÷Ò6×÷'ZÂ`Vx€Û��–³þÀý'5vKñ°˜Ìªž™“!$K�‚Ï<.Ä÷õF�„0Õ’Îðg€þ÷5\Õþ5µ¦…“&GeQî�FGg”ÑŠg¸·ƒ–—�­fõ/2X›²JÍú–“N ‚€jÍ‘ f¿ÏJ¤–ûœÓƒP?qS潬oZ9 W�ýªÈ¼A,wû¼*ˆº4d8# ú=l¯É®MqŬÝå�þɼúùO2h,§×ïRH ùŸd˜Þ™÷vИÈi3þV–Fy›XߦôÆê²¸�Ôã=èú,Kt¢KL�+>¤‘ †i¦E©û…8ЪÅåǼ�ˆ}ï:x0×™·@ZüLT%hprô*`õ{}ué�9už‡z§õª‡D·mÞkëÒé�(¶Ye©ŠlzÑ}T>¦LSÚ9¶[ÝèV·VP©¥p|™MÌ”èwM*Oâ+ÝuÜ€ÒY¡qÁ¤(AD]ÎZ{É5.ÒÄ#�I¥K‹ûŒ d¸ƒl!§ÈÉ%e|ö–—èt»¤iªê ó÷³Ñxœ'iâ‰m¾-ΙˆxïI–„²“óÐ/|›á�»½O^u@üêàý÷_†‘ÝôÎèóï~î“Ͼ¸­¥”í”ÄIÏJÙÍ‚a�ˆ´ÍØ”¶ü®K›|ú…m.o�aq‹„Ѳ¦G–l:ì³Òméµ Ò:B§å¨*4áº' yUqmoÌÁ¤PµƒnéçÇ3%h ê^;Õkž3ÞÂw~ñ5ÊÊ Jà)ào*Øü¿Øš€Ójµ¦Óé““ÉøÞ^¯Ÿt—–xö‹ŸæÜ]÷Û‰¦µn§:±@bÆ’¢0„ÒxH;ÅÅ ‚~¤ù6:sŒ:n͇>CCnˆ‰ÖÛëJâL—{TÛ‰ùÙËQ¡×E­2©L7+›æÇZu"Ç”Ž®+ï…¥µ Ö6O±Üï«¶ÕÒÒ eÖæêkÏÒºó]*£n§˜moZfÌB¬"¥ÙÕr] �|ÀG€ÌÒ4&rF�‰EÝÕem ¬T×­(pN؈NýA΢ Èâ-A̺)KSNÀrê…ï$ØuÄ�=^³¹�UgÀYêwÖìø|rÄ kQ•û‡û*gÓêHO�b´²„¶`V‚1ÏŠ~WÁ$o·•$RE5ç¦;Ö„´ow „íQXØZ=M¨HPÌúŠ)U§e© +[ûJ¨qí–ê¿%e¥Û!è2Æ=í«Ï°ÂÑL'knZáSp¹G*µ0iµµT|¯¨Š½ÂÙ7`’>pɉ·�KkÕT:ë=yì7Û|ü‰ä® euøõmç~à�ÏïÿðG€M õ!„ïùÊ5ÚY’~ðÁÓ:�v[IèÏ—õCˆævcÎ/§W»¼çîM®n_1—Ò¢ÖNU nU@- þÑ—˜Ÿ_i×ÝVJ'sôÚ ¥Êœke:ÁQ ¹º=VrìP‹Ú7âr;iáØ ?ç}—¢2ôíÛpZÈ—_Ý•3k=æ*áßêÕ:ØTÀð×¹ðÿàäEQUeùš¯|‘¤i[ÄñÒ<Ùë?éçÒêv ¡¡†|;†‹Ùjí' f5%ý‰2~R6,Q9ËX|õ뺭E*+VíÓOK0n¼e¼Û’©p-γÀº‹ïm »óá·Ó^]£ð[»»ììnëLtmó­¬ZÑ7$®³À­IïˆÓk±uT^¯Ë ÐUUMF¿ÌIŠ)ÓéXUʲÐSƒKw’¬¯S!Hd‚!ZÖ‚*IÔ%[´²È.kÞÇT†eqÍœÀ1åÅ›åŸV¬Ziâ(’–‚Ðhe Â¢K»´¦9�á4FŒÂ�”¥ºt¡^*!°ðø‘Ö­Q¥÷ªPí?µrvw¶‡UQ\‰§e¥×¿3ÉPUEåÎ뽄v:?™¿Ç]ᮟô–äÕÿþdVU8?!xÿ`¤½VÀ¿‹¥‡à•„Zøðí_¸Z̓áòØ='\;MÔå…4r\Ž#t Á!òc>«qœ|ü†Z‹Ú�ƒœxâï�Ôæ Cšs”›æ–“ÝÊþ~VeWê“A QÃm óogdð�û7Ã/|ì.S„^Ôšµó�ÌÏhÃûÜž+ôËâL�¿(‰û7úþÿáÖ´õª,øïýOI³tà’Ô oÝàâ;ÞÃÆés ëÛÈÚó¢&Voí«,G�Ú•nöH—Û`vcH˜V„ÝwUÐäS-ºÖ°`nSä­Ö'iºÚ¶ÁÞ¡Î(…€ÒKW:ÔcC^Ùsš„‡G›«„ UÛÚíãé„+_ù¼ÎÐËR­ 66N0>:Ä­�Å¥iMÓ×Ü5FëÄ×ÕÌ"PË¡&•c*Í6à{o21Ѥ·ýM«,MTqÚhÎI”¥!`b®^1g4eÐã°Š uPQsF™®ez×s€Œ(bý¥�cZ»W˜íilB+VÏ»¿õ'r÷ýo!ë0SÑËÉr�¼×Q+ýÞÔ[ÓB^¬Ë×üÌ‚{k�PqskÒH`ö¢‚�Œ–ú*W3^[‚DÔ¢6’C–j‘ñ¡`¤P¡ôjé<ñÜã|þ©/2š�ôÇÜßß¿:>8ü.à©•ÁÒCÀ?>BÖ»gMÞÿ;?ì³å¶%&­$Y¹¸ÎÓÿî "Ê¥ Äd¾Ang‰cxY¹²3–¹åÀöp*;G9—6ªW& (Çõg·•j©i&e)/ß±¨åE¥”âço*smk8Cer.ï„xâºìÎnǬ�Ázë.v©-Ѥ�|äTø¹�^R÷Ýë‰Ò7· §…|ö¥mùò+{¡¨|3~òùó@ÁÿãÍÑhÝN÷ÕNÚþR™—‡UU ¾àóßû?(ò)Úd¡ PÓÏÝ\ZOÊJ¥áëOž>Ëm€X§25€w¢æî8ö¯h=4¶5?g>�æD¦÷Xê €qéD÷3¦]`1Ø4¯Ñ9NÝq'¡ÝE¢µ!AØßºÁlïÕÑ.q°6z±&]Æ€yšj7 ±î#ã«^Ž@ fú«Ô?ÓYn` Ç�.;Kô\ùY^=Ë jé¶½FÏ6+Ëò~ŠJeuâ �B>¦*gô:]¥C·Ûm6Ožåž·¼ •ò_p¸±ª9d1vCX¬&ÞŒÕÛ:o×Ú´lâþÍÏ4îa±ËY8æÜ‚M€Ìp´ÓfûìInÜ}ž½³›TÑ}T%޼�1ë¶É;­(B+„hf•yÎp|VWÖ•/÷oÞÜBxqeyùàŸ�9qÞ'kkäçüž_6Ïœ ^ª8ØBU•~ãž“aùÁ“Äga ø "hóI»ýEà×D›Ô$YÂW_Ý+¿ze¿xêê>ÏÞ8lºÈš¿d½Z}è·Óðç.¾wܹœ°°U>ðòÍ?0·†þÕÇ^æýÐóá¿|æ ¯Üxó­9ŒH|4ÎoöøÀƒ'ÂÛï\ ½vç6Ì×m†Ÿõ›Àí›h�ùIß÷ø5¾ôÊž�Ä%‰$�)ÿ´„F;µ±1�cïŸöÁ¿»Ûë-%I"»W^¡»y’³wßo÷!3è/ Ú×›hÍ¥Dך7Ûg—�D;)oŽìÉ`ºÒçàÄz�¸ˆAð%Ó£¡2q\’êº|<âæ•—é--ãDîQ3’´¥l&êÇ�’ö÷�,GHR![U¶�Ñ‘«a®ìµªðL=}Ðh`XUT©›/~ÏÅ…Jƒ"Ï©|Éd:ƒÞÒߨÕô§>€ÇRËfØ@í’ô� ÿ^FW®+ëª%c�hñh�*,I<‡ºÐÌm¯ÇâFu+ÆØfÁI¶Í‰ÄdS¢ºDÐsQ¯Ñ#jß9qÆÀÓîÆô§»ôT®¥Të玷¿‡÷ü¬_„ ø˜µÐ°‚�Õl‚R\Ê=ßæ3ÑL#À¶/VklærÕ'Je+ÓûípcßÊ”pàÔņZoe»¥ªÑÄ¡½á_|îËlí“WyžOƇ‡/%iúϲ,û=‘]VHÚnýü÷ë’ßò³Ÿ¼=y‡œ¼~ÂùŽ—ƒÁaðÎã¼ YÖf´=”ë „\¾Øk„ðY ¼‡hø «„½v"sZè¶")ƒÅJÉûtÔÒ™‹W²u8[,4RûpQ…È·øššDÆ�Þ·Éœâ~ÂÛÎ…oºÿo»c�Â{yîúPæ1›¹ínVzYh:Yü¹U¥`^�Mþͧ_âãÏlŘ�MU~£%Nþ8ÚÒ¢xÞ‰û¶¥•Õ3Ýn7�Í&\{æI–OŸgýôY’4;F�yAù[i¼G´ÆS¤æèvýŒl£¢:åîÄ÷vwyjºÏ­ƒ^zò˼úÔWyéñ/ò¹ÿñøÜwÿW>ó�ÿ‘Ï|Çä«?ô�<õ±ï›[dÿ�/}ÿwðÜ'~ˆ¯|äûøÜ\)÷+?ð|þû¾�ÏÏ—O~ôxþ3g<:âñ�ý _š×©)ª‚íëW©f3–&9ÃÝmHHÈÖºH­ö5.Q×RzªNKc‹šÔz½%‘5´wý5Žö¶ÑiYjP¿Â‘mœG’”8Â颦ê,ˆ ì¡ &kcI¤ˆîX™e*#ÔêŠDM]&lëûAÝýeŠÓjÅ8Ï� )h$æ>3Ê4`õs— ˜ûЮϛ{P-5—öþUÒñ.ã£}fù”n»Çê¥{xû‡~I«M³…𕲸~­úÚ§6ÄI#í£BÍÖt±9+³ÓpÇ-pïá³^W­8­�«1�Ê>ã£Ëmçè€O}éãìNöÉ‹œááÁQ¨üíNgW c!’¦­öïûe%ýÉïþétZm(¡5iÉ™WÏÈ™[§e²Ý¥�^i–Õ«ˆjNQY�2@“Œ¬Ý&Á3èwY[ß`ÿà�íí:dàK:ío�SlßòÍ�²º¹ÁÉÓg,/SîÍ~?×8Îá‰5öNo Í<3/šÛ^}úq~øÿu.?û†ÛÛª¯¦®+×"=÷Ò3÷’ô×j‚›ÁéXcÆ2¨%l»Åg(ŠÊ¬¤$>3—¦Ì÷Ñí¨Å(ÎÀ©&à©Ç¬_KK©ÖÁXi*âI *#¸ 1!=ntñÕ¤jÐk­ …]=îo±�ªR@–ç>Fëð†º|«(zú¡·ñ«þÚ?SËqQk& KcÛB’IÓM,‹-š”xkoL]¼±½®ÓçˆÅ:‚z 6®n‘Íf&ÅsõæUþá¿ükìŽv´ÚìÞÎέéxô�³v[€ßíÄqÿ¥÷$ô7üUVÊÁWµ�Š*­� ׇáÉw=Uí vÂwÿþÿ”¼ö_Ÿ¯îF@ �IÔãú·±–ý øùÑÍv?�5{ÓĹÏ/ócßr6<|~UTcìöua”hxuwÄÿâùâË»”U•ÂñMÞ¬0Jó|w�ð+¾ùžpvµ‹>S"DehòÒk_ê¤UÂ8°ª-|û—¯È'žÙ"/<ÄŠ€v£úó_†üÖämY‡mCÄý¥¥µµŸÚíuVæ:Kn:™2+ Ú«›<ôMæÞw>ÊÊÆINœ»ƒÎ` ¹ȲÚü6éëÂég_& ¢Ùë¨{ÈÃÙ.{Õ�§ŸxœkO¾ÌåžçåWž%H ¯r�öI]ª*Öy>ÓR¹E^¨U €³RÄÖÕµ´²²ö÷vußù1ô}Öê bרÇM²–®_^]âô™óZV7Á‘uÚÌŠ™~ÇÙhÂwÝÇâÜæyÒ“'9¸ûÄ9XDX¨ |áû¿�/ü—Áõ—Ÿ£˜Î˜ŒG(}5ë3“.rò.ÂêY\»‡$ ®ñ#AÀÀ†šSK†�ÅÜ43ïAA!æ-Xic1«ñ0 `šrïæî²¼g4kj%šÊªŠÛSR½¦4IDâ�ä›’,üßOØ{�A»­’v›_ÿ/¾ƒç/¾®ÔQh¾ÓÀSw»¸4–ÇÇŠLZíMNÇMõåãcAIUqòêMzGˆ\¾ö*÷_üer)­kמ-‹â�óIüS`÷ö‡?è~ëÏü=rjp_ª �º¦_Õ®¸š\ ÿôêß �ÿ»OÉôÊ¡D©# 2VlÐ$*ÿVD¾@ïˆèý@7väÌZ—ozà¤j�ÍÙeѨ½íŸ/N æ5ݧžßbë`VÍŠJ‚aòwÖB¬_\mNð¿à±»|K+l|ËK¯ ™¥®Ö^|¾Êž»qÀ?ú‘äæÞÔ8…µª�/¿_÷€çÿÃ&oMÛ°¸�ÈÒÖŸ[Z]ýÐÚÆú™ét’¹D³Ò52™Mé­nÐ_YãÌýsòîûu€É§cò᡾žÌ—‡·®³~æ<ãÃ}ÎÜy/ճϱ´´Ê¹3”a4 yáÚ³ìîÜbt°Ït8&„ ®’¥Õæç¥, ±n·Gš¤”U©Ì.D]?”e²4“9h NƒÜ¡ÓîzI’Dœ(@å“©ŠebEÇ�-'1îSU%:¸i,(¡×ï#I+eim]ËíêgªJ¯¡d.c压œ}ïc¤�wÍéÎk§N«õ×îtÕýˆ4ŠFêÑþž3žúÁÿÆÕçŸæpwçZDãܓӂµ³„ÞÙê)$ká‰*ÎÙˆã\Ýõf–ˆ�HéÌjåxÝßâ'ÄÏY‚'1ÅŽ•ÖÀ"–ˆÖet¡i¬§Y#>Zcv-‘]gn;SÅVëÈEÑÏ`êÕñÉŒÔèÿÙÞ5K¶lÙµ3ó xíö³mkþû6ߨ¶mÛ¶íoéÙ­§6®ú¢ná0sOEFFMEÅ­é©»VD–�³ÎÖZ Ë/Ã?uysÚäV._gÞõÓ¿�+︯sûþCÉgN:ÔC,è™ñâÓEŸ×²ÉeýwÏû]ßÇj|~¥µ:Þ÷Þvºù¹O‚Àlòùcǟγì«kÑÆS`þ.ß "­�V³Óx?ú%¿„ñê8¬ªT.ÁlÅDñ³óøÄ3ÿÀ¹Î4a´¦B¡Ï/B B+jè(n6…¥ ÎgÿóIÊ±ÖøJ' 9ÕM �'l$ñæë¶Y;i_ 8˜Mˆƒ»ügèøjËúûï>ºŽS »vbô©£ô!²þ Ï­ãEþ’{/4³#E’DÝ‘m·Åý•µ 3–6¬3*ýå£G¡ ³aî~okNíç„§æ0ÎRÐõ… Nƒ€¾@*õ……Ra,,•†‚0ô²4µÓ$nAy˜�«'ÀnÜEGÆÞ¦@ <ßu4iÀ7™.‘ë *P­T‘¥©5϶ø--9xʳªÕI!ËRŽã˜•çq»¾”žZ\XK«c£¥vª >·eëÔÉcG“4M’¸ÕŠÚ÷ÛØ¶ë¼å¡jQ)%VWVÒƒûö-”G†+¥reÂhÍ^àËÌêêCå&Æû¼¡ àûÖÖW`´¶Gµì{.—ªh¶êî= �A‹¶&“c;Á®~ðMß²$¥õ‡?ïÊkÒ º l€Wž ÿ‹?ŒÖÑCh¶švãÙ~_¨T*ð<$�+µš%52 ol+¨: Š62ë´w"Ó™5�ÒJüw qvuƒ¹c×<ÑI�1£Û ´£Ù&¨{ž¦cøÖIÓu9“öÌÜ{[Ga¶k`—R³)‹n]5—z#êÔ‘ ]3GX›Geù%dõSˆ“Øz½¤Y†«ßóùxÓ—~¤×xíU3ï? áNœÖš¼3³Øçº~)²~Ê�§'G€}л_O×u^­Ž§ë—qðÈ~û¿j4ê�¶hçÇã¸õ…6¢zÀ¯x+äYî½öë>‡|×xèÈ‘�ÂF‘ŠºŒG_ú4¾ÿ·¾‚&3lÄPeBÞwËpíÅ7bbxÍFÏxŸ|â|èÄ‹©áÜ‘pÀƒnï½à¾ÀΞ²˜ éæ ÆqíŽ1Þ1VbO ÂéaÓZ//làã/.ÒžcëhD9Ã�æÇ®z5�f߀ºnïÎ Ÿsç.n¿&[C²õJ"�†°\$´ÞJ¹-‹Cmý5̯ŬM¯7æ|- ñw4ÎrЕ^ˆ3@Ì7úAøÐÔömwåQa0é>;G-Q,•a´¶dcŒ±$ᾄah7 J*¤¶Ž’#B»ÏóÜxžÏA�Î5’$2®¾Ûmljò=¡µŽ¢Î:_ɵ6Ú˜õ´Õš/„Åe£ÍÉ8�–ˆq ƒ¢ `¡Tªkl¬O0óŽ‘­[B©TX;±°Z®y�?�´¢©6ùPÖ± c£Õb!œ®”«¢MXáÂÉÜl6ëå°+Ž[ªŽØ´\’Ä6âB Ùj ŽbK°V±xf+�f³°±QZàùð}õõU@š „#£6Í—F­ÎF׃,µƒŒQ©\¦°XôÛm@)­VËn¬É/atjk­ Q�Ì^eU“Ññ€w$ÑQ]¨+åÆ:§íqwó€q~‚C×}D§éÀEC. êê¨sQ ,9Ë.¡Q) ÂEšYn©�+(:ŠÔ Ms{�#œŽxiØXF¸ü2hcÉ’TœÆXßXCed_õ‡ÿŒ±Ù­� ªó|‡çû³Ã££#L¨”+UÏ“Š4KTÅB�(F½•!†B*¿ Y†74öB°TÃÂÕÓQô �¶çüg¬ÂÂMüw,¢¹«Á‘ÈEÇ*AªŽãg'2Æt¬ ˆàÒš�ÎŽ}�»½‹Ê´«ù:sIIŒÒñ§`NµŸ�ÖÚ¾.­_ðU¸çÝ_`ÓšÀæÂ®´‰¸,õÂöú‘V¿|MßÓ›iîõ í½ пÆÔýاž~GÞ÷·0¬ÑjµôêòÒ¡$Ið8žØóØCγÄè(nÿ¥7‹«Þqša(_ÍÌ_¿ó·dãÀ �è ¾ñÛð¼ëXATjqm¬ÆKSK¼>TC¨ bä±*Þû#EÏ>õ‰œ@ €ºS1xè àë|¶Ó`n)¤œ6Úõç�âò-üu´„Ñr¯Ó ÓœkÆ©FŒÃ§mâY£Nl ˹1væÅ©®›.I@’Dú¾²¤‚mãE´;â0=TàKf‡P NcÑÂ@d‡OkØsb�>¾w Q¢;¡]+°êf•~�”\°—Ÿ# «üþðt–íPž7ÁÌ^{ÝáöN<�Rßó‡ÒìšëhÉœøßšï‘1&Îu¾  @D H©ÿ§Ši%?,I3këKcyáðÈØˆR^ *¥±±©©Éõù…á¸ÕªNìØ¶³¹¶žK¥I­u©�Ò‹””~±T&�ç0lDûXH©Èó}RÐøØ$GQdZQCdIš*HÖFK)l]©Æ†Ÿdc~^Ã|$ŽZÛàp‰ïù¯õýà’Üèm¥ry¼\­5kÒÚ X,"Ë2DqŒõZÍlæ¤@*U&€Ê8Dy”‘‘²¤€®ÙÓ±�z‰»Î :wo6;ž5ŽxÜP¨§lÔ CݪQÑŠ´Ã†�.M¡Üd]nš±6È5#díÈ 6‚Jµ�fƒáåÁ+‡!tåÈ¥Þª£4·Ÿó£¿Š¹ó/éIœ^ü•¹÷fÿ6Lg.­±ôC¯õú>i¯u;zjN&‰qè/þûžþ4²,F'©ÔôÞBXx'€äcOœL–�»èæb0xüÞ�þ›~þ³YT î‹¶Ìß\Øà¿xó¯ˆ|9�·¥‚Ïýïå±`†Œi”c£…¦áÚ0v½´KL�œ¢ÚÊ~àW¿GOìO°#—_íaiŒK|#€;LðÐ  AØ1Y²N˜ÏVíüK $KA(Š¥ ~A¦uÐ\o¥¶¹]ï±3@J$¹¶þ3¹a+èÙ&3+²yþt#EŸË¡gex›¡3Ðj¿•æÔV À�¥:ž;¸LwaWª®é>ï_J}üÜ!šn KÿUó<çt–†*C³Ì<Õ¬ol+Ÿ7:1QÒàáùKD¤ …¢iߦ§É$SÈt.�1åÜäÃ¥R%•$ P'óÆè“¹1%%e*…ü!Äß÷*ÂŽ�L”K…âLš¥wK©®Ürþ®›ê+kÕ P¨4£ÆH”D�ÎsY’"Žc†x`¡ ý"¨PEîW€Ê |°�`"Ø&9rH(H�ÛF ²Ì#AB€À�l q�=ø„òlĵEÈ“Z«‹–H|)(ÏÖ¾ò,E¹T± 'Óc(* f¸á]a4ˆ€$M‘æb!øJXE!$˜E­¸‰æÚ"j+ ¶•Ü6�hEMä‚pß—}xè‹ìóòéÒQ¢�m¨o·X5:ÓV¨þ�O´Éké3 M¢10VÛÑÍòÛxjïcÖ:>Ž’ZA_à�áPk¬}ž«ákÿ�ßø,ºèM—q®3t#_Éñýªh¼´b0áÓÛþì‹0ºk‚-íS!*ð®Ã;ÅÜ�9*Ô ÈE†J8†_þÇŸÀ_¿ÿ7@ ¶Ïó‹n|ÀF×›/ƒq%€¯p3€­èí8sœª¤@¹ ÈW‚*¡B[Ü“·Œ!YÏÉJˆá’ß[b£\Î]jÙ}�vji;Á¾®ölêþLsc,Á¬6¬dŽÕfÛw¢FûŽ×°VÏØ€©'’é�ÿ>jȶŒ7qŽ‚î›ÞŠþË 6ê~’&¶jN`)„òÆFÇ•’êÍzPoÔB)$‡a!c6$„lJ¥Öͺç*%-œwÝ|o˜&ÉÎ,ÏîiFÍë�1ÛHÐTÎz«çûO)$©}6 CHi%ê}ß.¤9A £�&=KBFHpžyŒ,Kl»»Pr—Ý–FÃcm›9 aÁJÙHÏ·QÇ©µ>võ5KJ)›¢<…R¬Q*VP)W­ÓéP¹Œ±á ´l†¶Â �VÃ5˜Ž�ï{˜žœÅØÈ˜íüK³ÄËÒÒ<æOÇâÒ¼­§��\ç–˜üÑQ¼ö›¾ßtGŇ>äp¦Ä@èÁéîsÆj§*é5ìndÈħ>ŽOúýxnß "^=µ²7iµn‡Û‘÷Áü›Þ ‚)m ^÷{ŸË“Îpš%NÂ_!‹S`‰ð‡Ÿó³¢õÊZ^¸`X¾õ¾ÞÖБ‡¹…-tþÞóÄðòiÙIá¾£…?=ð»üOßó{Ä93®umh_ð^çù7ê$…`mª.sΣo°ËµP‹M$p\ÐKä:ƨJÞ5UÁ–Ñ‚UŽž¬øüöù@IDN–‘¬Ê‘‘eBÃL xÒFÖ”iƒZ”ÚF"àT=ÁóÇÖlm(JµU’n%ÚÊõ"ftŒûÙÊÍ;’ÿ}[³9ÇA]v#ø�Q¢æ{¤R·LLÏÜ’4ZU4"”,DI$…”¢\®Ø�u!m-Fy¾MÃ¥ibµÈÊå*”"Š"¤Ib ¤¶CÍS¶%¥gëM…B¡µ°–Ò– =Ð>n¢ÖXG­¾Öè$‚Ô4–d Dš¦µ÷¯76%QgðT*[ß²dæ{Fµ2Œ‰Ñ U‡1\ÁèðB?´„ÅM,--biþ$¯¬­Ø–x;c²3SÛn¼ w|î—[³»®¨ «¡¢7­ÖŸL¸o=¥7âésÞ¢ßðéë<ج¥~¶2h=ò0ž{ô#øÀ'þÑ6­4ÍÕ$Š~TþO¹¨ÕbÙóO¸VåTT°åÕÑŽ»/áéK渱V§úÑ5:ôðKXÝ3�Ö�u°MqfxðË?Ÿ÷†¯dï�¡b½&�:Ò–‡.;Ì/^ñíþç§ø£_ù,413$b÷üž# à“.òù˜ðÔ6¹öÁ|€ë¼ÖY‡®Á@`spÏhk2£e¶6#­�Ý)ú<ÁQªi~=¶-É´%•…õ�(ïüf´íbfc¹¥§Ë¾Gà¹køG.ÿ(ìm ¯»ë à¬A9ðƒYOywDIô6cÌhš¥# *åjaffŽ+CC)­s&!ȦH8ý3K¶ÁÁÛ]­µí*,—Ë(JQÔ²vàðËx᥽X­�‚tÆnyžCaïÇÿ:žoIDmIajr–O¶â$ ý0¤V£‘ %µ2ÔyNÚèÌε֢T,û¥REoß²ƒªayubx‚G†FDnt)Ib¹¼¸ØŠÓˆ[qk,ŽZH³Ô>ah3W^ƒËÛÊÃ3[Т—PNSðçÓO�žùåö¸·Yàô�„ÞËy½~i?²ãGðþ�ü#yâc0†ÓõS§ž1Æ<àÆçæ(oEoð.Š0B 2™Q²ìÙ9¶<É™5çv¸SŸ KÌÎí×Ò÷î�a¬0?ƒ!µDc¤�Ý7ì1óSóìÁ£�üà?‰ýÑDT¾pÔ3¹†®¥&Y‹Øfãˆ4s'õ4€ï$!>a_‹ï‘IRÉÌãnqÑÎ�Îp!€RÂÙ¬ ÔMá>o&¢îÛ3\G‹:)=î7®Õu>ð0€'ü…‹âzRgмéó1ÀYJ’¨L$Fˆh;mC½Òt¹X™(ŠS†Í\žçãÊS%C¨ÄI$êµÏLÏåB)%II ̬HP˜%©ö}_KeS¯o$ó‹'¢¥µeì~qwlt®\0Æ$i§yžG:Ï5)/ªlŒÊ³,fÃ)9‹œ°XjE>ƒ=éû­<Ž ÌLRÀ³Ò^u-²p\ªT[3cÓµ‹v]bvmÙ•µ£ŸJ’&ymcí“iž½#ËÓwµ£¦ñöev4 Ý2‡ê¥WcÛ-w¡8:ôs¯tÿ0Ã诹rº´ZŸ®´œqë6£ Z£þÊKࣇ!êëØÿÒ|ôá¡V[áõµõ§âFãgd©ø'½w«øaVO ŸëHG‘„€SÎÆ¤`·O8àŠúc† ½åÁ/¥/|ð+‘ç²0ÃÑ �áè¶£ÜjqÕTé•÷îã÷~óQ¶§¥ÿª_¦.��i2Nì9‚W>¸‡Þ·2¢ŒH0˜…óÑÿnâ7»kÂÌ,Á\0`Ô‘Î�N§m'€qG@r3—ûÎÚ\TÞ­¹˜îëûp=9‚Ùã"´ƒVʇè{ù}A?ûÐWc€sÒS¾,†EjFͰ½ažËò4B^”¤ñ�vUÊ• @AœÄa®s].•½¡áQÕn[ Å;ßõ¸õ‹îâ…éy¬ �‚G>Ç'ZØó{OˆÝþ(ZË5›ŠÚùªkÔƒ¿øÎÜ%K’BAgµCkxñÏÓ¾ß{Ùj¢Mš'nƒÿeþ›€ 6\0 Às$x€«Ýë/HÝåÒ­jш޺Ð&QLîV `ÅéÄ=æj2«>¢C^©�œyºlú‘·~0À€ô=Ÿ¨�¥•Ej6(•*j¨<äUJ• QÎòìT­¾N… Ð8tâöcÓóÚ˜ÉõAøæãOø Ÿû ³œ›ï×0oÓZåY%¥UðGÇ0|õõ½ú:�”›ú‘w{øô«éôN/i`si£nÐé…Õ:™<Çú‰#8ñ‘÷£¬B! 5ðès�ááÇ?ÊÍzc¡U¯Œ™Ðyá÷…k"�lÌ€m|÷T1 ±WøžÀ:NJ®øýF�ê3÷œ‡]o¸”M#§ôDÌÇ;@ËÏi0S©2.¿óK~Š·_³/�ÿ"·Óm¬…†0" _8þøúà·þ)7¬Õ™ü)Iñù§�`1b­G–Æ‘Ä(á§ €ù® îzh·Œ‹VN8 `Ùσ¨F‚R’Ò¥0 ™ÿèO¾ú‡1Àg¦ü#ô6<æ¤}l“„W® zÅÕ¾æ:�çu§×ú[ôæ ];e»ãZžT`AxïÇÿ I§kÿ´~üÄ×8‚ÿB �ŒNøY'‡CHkM‚¤š!`k¨8[¤/~Õ7Óë¯y'’FÝ^·2µÊ'v7Ƕ')=,ï^Àág^¦ý¿õHÞ8²^cÍ!€?&)¾ì¿mæŽ!ÁL �Kåù JÀ¬HÉXHÉÿ=„2ýé×þ(à,ÃEÆèo&%_GŒqf…bÑv¿‘P(^p!J7Ü ›ÖþE·nÙi[¡Ï\œú¸ÂnÖÍÖkˆ™Ç÷=�åÝO­ÆFª \£±ZÃÒâZqÓZy´t”jÃû<ÿü7ø0þPv©µÏPD¯ì› ºp7}÷«øÖóîÄŸ¾\x©g X0’B‚ƒçâ¿ÚýÇxæW>žFGj‰PÒ8໼rñçϾ�þ pöa¥�úÛ¿Q¯ GI4A¹X( ßó­28j5¤‡[ؤÙ+i]`™V‡zÕ Ô/ ê/EÓÛ‰F}ÌßzSk&K±~â(N>û8N<òqdÇ ì ”ÂÑz͵L®ÑŒÈH#÷ÌúÁƒ>uâå—R¾÷¿qƒ+Ïû «iT]Ërî$¨Œé²Ïº‘/ë ¼^^§01²4B‚ˆ �(`ß§ž§?ù±Ÿ«ëzÖ �kLÀ)? ²²/)8ë N<ÿ8à,ÄËíßöw$yöȶ�ó®ìœ›,Œ�R³Õ´f|ëÝÀJ} ±P…"†·ïÀÄ®‹P(•1·ëBkO^¬ŽÀó}›’BôñÆ9 茆F;W&Q­å%,íyÉñƒ¨VK˜*(D¹DkµI¶–s{ÅY‚á™IPÒX}ñ™‡~íÄÉ?²-¹ÿýȬL Ñ'Á\p‘S-8ŸsöãÕ&qj@üÒÅ/ahµJã'ÇAš�àág?a )v­È{\Ä´‡-:òàlõÃ`€³—ƒù‹‹å¡·•*ÕѵSóÊB"R¢P*9;X!©¬Õ@˜¼ä ŒÌnCy|¥Ê�½]àLßF'g0>·ÍZI„å °Ø^k"�›6pOh¤“YÔD²´ˆteë‡_†ÊSäYÁH3$ÈF3VIA*TGG–‹xnß³­g÷>³çèÁWþxõÄÉß°�ÿ �Ž)0ÿ#€»…’ªrÉ8½ùw¿Á¸�Lf´õøV\ýðU$3‰,¾ñ—>ß:¹{¹zÐÇ1À9 ¥1Àçk}?€/pŸ"R*åy¢\­?È�³ØÖ.ŦµvFqž§ „s#*­:‚�ÊžžÝ¶ wÜö†&¦� -J³[á—Ë  Ù^BâúêóÇ‘E-˜F~Ô°^ù�ç�´ATo ÕlZÕ¥”]IÃ/02=‰ òÞý{VþîþêƒG÷ïý øÏýo»DVK•ﳎ•‚¤(+zË_} �]2b‚2 —?q͘&ɾÿ�¾Í<ºçŸjÄ0NIz�j9ë0 œ�¬õ¥ÞàmD4®<Ï ŠEá~ „ðÚK*å)"œ¡ RÊ™Úi� ŽƒužM3ÀÏ]rÑ5¸pçEÂ"Ò$‚­I‰4×6Uf0ØFHÊSVÕ!P>F†ª½JJ›.³Î¹I åy‹E�’…fÒJ�?²~òøñW>ù‘þÒêòÂ?ØÀÿŒŒŽ½Àï� 1’$îo{î¼íB^Ù½„ÈDtyõJ\½û*Œ«iú£�ýÿÆ_}_Ó“>ÜÜÍb€³tíè8àåõi7»q€-€U,žRú^àWJ•j)KÓœÁ¢ “%©‘R†í뤧­¬®â'û«£L›:€¿!!¾â\QœD8 0€bcª`�„Ê$˜g@ä;é”Q·¶˜‘R@TRX6"¢ªò<凡öª•á¼Qß�ư®”ËjzbFAÈÆÉÌ6b©Žêr±œ/¯,ÓÁãUàI»©aÃÀdSssÃ*Ž|ñÅ÷Y<~òØóÏ|x#‹–„´ž÷ÿG1>:ñ ~˜ˆ <±ÆŒªÉt ×u&=ürX-Mx+µãq’Ä s-Ö è`@8 0 ¥˜}7±¾Íéy•ÌYi}Bqtvvd}a1P%Fâ ^X,Î$QĕᑒN³Lë<¯–‡š•Ruá•C/ê$n¥lLCy²8<<–JkK‡_yÜŠ{ÔñÿåêЃþ¬#… 4A¬9"7̬¥”¾s |µ üß>óhm€á 0À€ŒB0‡Ìì‘ÓëB`‘Ò€@�˜Á#¯äyºL$·1> ¢& ‘Š0Ðÿ7À?¼ÀïéRQ^ð.3ÎÃÿ GÐÊ©+¿À8ûqçÄÌ` Ö` ÖÕ*¶×GÚ«Þ^kíµÑ^_rÏ–ítÏÖtçä¬×>¿£½¾¸½^ß^[Ï�Ïf°þ̲ ÁÕ˜IEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/kitteh.png0000644000175100017510000016300515224505336024103 0ustar runnerrunner‰PNG  IHDR¡þ�2’åÌIDATxÚìݰ#H…ágflÛ¶mÛ¶žmglÛ¶mÛ¶m+¹{:›±­óUý»ÃL¹«¢¿�ŠŒb!{d�LÑ eB¥Q9cÙQd†ˆˆˆ~ ª—:uê1ùóç_•'Ož5I’$™Š_늼P¦1#"""2«X±¢g¥J•4hÐஇ‡Ç“~ýú=¿W®\¹ƒ $˜ƒ?ÓY#""¢ïÆ444´V½zõŽ7mÚT7tèP9sæŒ<þ\ôz½¬_¿^_»vík2d‚?}7q«W¯¾¶qãÆº��¹té’<{öL^P’V«Ì�Ž~Ï%9""¢(¹råêZ­Zµ«:t�%K–È£G�äuêç'N,Ñé0[â’}6¨aúôéW6lØði—.]äêÕ«ò>‹-,ÙI�=fãï$CDDD_ÍU@0msrr’•+WÊÓ§Oå}Ž;&�:u’þýû_Åß)ÈãÚDDôµÌQa4-/V¬Ø9ÌpäÚµkò!jI®oß¾‚=£‡ø;�Q,DDDôEÌP4íŠ9òzgggýêÕ«?8 RÔ)¹É“'‹««ë›>ø»ey@�ˆˆ¾„)J�£h$<ěܸqC>åĉâãã#Y²dYgaad|]�ˆˆè³$F=Ñ&´¯@�·ÝÝÝeÆ ÿÉþ5S8p 4jÔèI„ g˜››—7!""ú 1Q8Z…ög̘ñîý•[·nÉçÚ¾}»888HéÒ¥�k4š®Ÿš EBÎh)Ú›(Q¢k*TÙ¼y³qôy?~,xÊG͆ž%Ož|fCÅMˆˆˆ>À ÕBsÐN{{ûSùò哪U«JPP�\¼xQ¾”º3ÔªU+)T¨Ðž¨Q£úásíÑLQ~4mER¤Hñ¯cKÍš5oÄOÄ}™›7oŠŸŸŸ¨WâÅ‹7Ÿ›½!ê‚V¢³vvv·Ó¦M+Y³fUËi²|ùrÃÑë¯1vìXÁ3?�°´7 ŸÝÙ""""{ÔMAGÍÌÌž"‰;¶à…Ác¥rþüyùZ‡’ˆˆNØ�Åçû Ì&DDD`Š*¢^h!\Ž%Šàÿ†A(GŽ`\Šû:êkÔ UªTQ{M~¨!/¯‘’…¢hyüøñõ1cÆ•ZŽ«]»¶Ìš5K¾Õ¨Q£Ô’Ü>ã Ê×µ‰ˆ(òxñ.N°=N–,™D‹Mðí¨R¸pau¨Àð�AßJ dxòç6NÜ…à߀2 ""úGÙ Æh,Z†{G=húƒP5è!-²DDDô�I†£¥¸ t—QuêBiùòå ‘º ªî©oH}ðà�|·oß77·'Økê§–þДÑ?Dƒú %xÁ`KÉ’%u^^^¢Õj¥Y³f†A¨bÅŠ¢+ݵk—ñ™žo‡ÏQÇ´ŸàÂê8ã ´ #DDDÿ+¤E‹5;‚ êëׯÿ{g%Ea““0)[Ò¤;l0 “PÄÂî±E0 ±¥•îîÎ¥{��çýÎyÌd›Ùõ¿ç¼³Ä²ÌÎÌþ÷ïÝwŸÑ÷ùùçŸéÙ £6²"V­ZíÍœ9“uÞi’iwèÐ!If¨½!!�ßÍüqdszR_fÉE]A~Ý«W/c• ìÿiÖ¬½Ã1›?Ç„”~ŸGLœ8r¢LGvÃü覙±‹¨^½z rçÎ=ܺ‡*~UÜž½%ÛÁá9Êoçž{î¬x4h`Ç7ÈcÛ¶m¶hÑ"c(õæ›o¶ë®»Î #Jr”êøÈJ>¾úê«F<ÿüóÖ¢E ãó±õáï~ùå²¥ÿ$¡† F¤¼Û Ll�ÓtÅlE¯ìYš ¸@äó™6œnÀARlc5È#¢Ù 7oÞlo¼ñG_ˆ`�!P€�Ú¶mkŸ~ú©A@ô�n¿ývãïÔSŠ~>äÅç�?þ_™¿‡Ðnºé&äß<Ž]Gyä=¾Å4—m?ë„�Eq˜âxÅÅŠfÇsÌOòÛ™3gNcþ§ZµjÌìükö‡ßOš4ÉèÕ©SÇJ•*eg�u–©lgôŠp<èß¿¿õíÛ× #2>‚‚Œ3\}õÕÆpëï¿ÿ¾ÏòÜèÑ£­yóæÆ×TIW†¤9r@D3£ß»›BAE@@@@@ã(w¾.©¨¬¸Ö›üm/û�>¬X±b»È€´b›�>öÓO?ÑÃÙ¯x`Á‚öí·ß^o”Ï>ÿüs£_D™-11‘P´D¿hÙ²eƲ»ï¾ûÎ^ýu{ûí·mË–-¶?@t #¬téÒ¹koÌ•+×\ïý¦h¯8K'8Â3„ŠŠëw‘5Èçweã5pºè¤“NZ§ì‚²Ûf9ì»H‘"¦Y ‡rýŸô"…®]»”õ’#צEF…7å9|ê’ (°JßÓ8ÅÅ«åC@@@@ 2‚; œ¼yó~(QAoÊXõkf+›`õvbõêÕ#U«V�T©RÅÊ—/o8_s°CBê~ß}÷YBB‚eR¼kˆò ïè!]xá…†K¡ïq½¾×™Š1�m¼ºm„¾GnÅŠsE½¬L0ô^~ûí7£\¯ $G¯êØc�5H2:ôÐCMßÿj/ËUPÄÍ¡�OQSÑÚ×Q¿©èàÍì�øz‚=ÚøçÞãÑBÑÜ£±«Èrdâã?NQÈÿ߆®{KÑIñ©â )Ú¾“âl9„é°JÁEôSb1%ø\áãÌ*©”ECŠ9“Pa•“ЋNÔqQªºÀ³ž�ݼdÓŸP?a²Ô`ÛÔ¤§\EV€_Yä…^H¼õÖ[—JvDÈÿy�±K)× AƒŒ…zsMÊl ˆ(I%¹néóD „@)nÖ¬Y”º’M|Ä´iÓ&›={6¤©á—ÑÿµC7õ•’¯>ì°ÃÖj¶eµê› >2z:q.*€•wX€çÿ9Ê©îò`*¯šs#,,Ôkd°\(°Z†«QGÅ1Š€€€€¸Á¹ŠG$ñ�¨~Âve= Íÿ¨£;pØe9qÐÑ7¢ù�±'ezHü; cÇŽÍü2ZÏ9ë¿MëÆ!!\¶!!^£µz}—ºŸÜ€€€€¸A/Ó|ªföX)å–6mÚt9Ínz:”Ð0ÊDh@¯Ùö�wÞi”|”AÑ n JBHdRx­±etÀ€™( %:²K„�}> A ¡�?<¦øÜU^E@@@@Üà,E;—Q!õìÙs_%6H%º¢¢$é� ÅÌ©0?Äì ™ûuük¤I© "$óIÈ„¢B‰> b…Kâ¯TÄ Žô©úÏ]Jpˆ’SB1G¦Ä OãÆ�éAD”é89 ™=¢¼—²AÒ€%)%"2×=zP–‹>ÿÊR·ÅHág+º*NUÄU6ô¦“P7 ‘U§ ´²p BBÄ@ÙŽƒ�ýÍ<úMå´eXö¤VdÀ�åëPlÅ.‰“Å�Ñ·5j”$«7´ÌRJ¦ØáÈó®m±Û�„V)&»eÓ銀€€€¸Á™¾¡—µ§2{²}ûvK dÞÌAD˜£âøÌ¯%€°_ýÕ’åΰÉRÊ©¨YÞ‡Á)»†6h8šæ²í6ñ´æ! àpßÔG1U†¡IóçÏ·´€9â2D dB²î1¶˜Ò;Â|Yw@º ‘0?„bŽþ²z.›äP1Ãýú~t‰~@@@@\eC]“¥n›ƒJ.­s?äFÎ. QbˆéñÇ·x}•?þøÃFŒA6˜eeÛ¼vêí¡˜Ã[W…ˆ¤øØ%AB�¹…S@@@@Ü,¾k­è§ø^ó>›1M ( AfRÜa×ÃÐkT¹…X�!Ëx3Q¯â$înY–ˆ�ÇÿùçŸ<ïxÒ£ÛuÜqÇ�tê©8_78Çg†z(:ª¯IËÒ5lyˆ5kÖÙÅ×_mO?ý´=øàƒöæ›oôLƒl� øüòË/ Ë"íI“&e‹�[¤ñ|ðRyŠé­SYn�oÕ},Þö „lèž=sCo½õÖÆ-[¶¤ÊÝyܸqÖ¿›1cý¡¸,»Aެ« [@ZN„¼ÃÖõë×g— ¬ì]¢?‡tsS–ÞÍó­¸ßú¢Ã€€€€¸AIWÊuQùæ¯ÔdC˜Ü'£ 7|øðxòZƒé—D÷µmÛÖØÖzõÕWÓ³Â?/%YŸË¾#Ä8J`ìŠL=^Êyd£\èÍA²¸¦ïÌ•+×t/½>oæ¦!j¦ø"ÒÁ5Ÿ!Ô” wïÞܺPefå ’Áªˆ^½zKò˜�‚xPŽ¡ÐC.Žó4=ˆß/\¸0Ù„Æ×¥ÔUúb©ÿ ŸlyOÉgD�‚'ZÑgX™ÊeɬÒ=¨ë(7víÚÕp¶@¢%‚;\¤ð�¢B< °äW¼ mÚ´™¤Ûtä@‡==2Ê[xÇÁÆàÁƒY…�+’ñDìb?Hë›ä€,§S§NÖ¼Us+y%;½ä™VðìB–ÿ´vÚY­HñÓíì ϵ³.8ÇÊÖ¼Ì ã¡C‡òïšZnéÒ¥d||ÏHådÝìžrí'(â):BBDË–-§@(û’mÓ7"#øî»ïŒLƒ_÷íÛ×–/_nñ~Joo¿ý6dDï‡ì„bB>ίّD%Y=/¾VýÆ ìÂê—XÙzå­Aë­íÇOڳ߼lú¾g/ýøº½öÛ[öÊ/oØM7±ÛÚÜ�¥¥Êô"¡¤ÔˆX�N6„­Dtì±ÇÎ𹡫ÿ±< —¢¼âvî}Hñ ÿú>Å-Šj.æ9Yq¬ÿ›œ>w�v>7)>†„t`õÖáµR‡/7zúH°)uÑô&‹ˆö€ ¡ Ä“º,j¸ºdÉ›7o›^ùYR®‚€’íq]³¬ÂÕ•­Ù³wÙGc¿°Çtµ7ú¿oï ÿØ:�ìbÄÇã¾´O&t³ö½ß±ël‘ÀÁ,MRd-ý/ˆ'‹í.Ùî¬(pH(E_ªx‰y€Ç7€�¨¡~Ú8ÅØ£Ž:j”o®ýÉNÚ)Z***ò¥�Ôò)ÜÓÒ�5Ve«í”±è©�M0tŠ';†�Óoá �ó©&Hˆ¬õcm¬lÝòvÇ‹-¬Çâ_­÷æ¡öñø/�l¨n³+ b‚x:�úÜ ¡w†t¶ëÛÜÌsƒï ß(ÿX`H ’Ðë8_|×Ì�Õÿ�8[ñÒžK—âk'¢Ÿ%iÿU=´rý˜-oÅÅ•+W^U¡B…¥ú¸R‹j¸{ª.i}ýß}ã3Xï+Ê¥Í) €fuq¿~¶Ç[N?ˆëè+è�’‡�nc2Ÿ!ϵk×Zv{{j]WÇÈ‚ ˜‰Ñ¢}k+]é;÷’bv^™â!½;ì#ƒœÞÜÙ>r ˆg[¨)'"Æà5d)áZnü~ðúfüµœp~r"y?Ož'!C×G&Nœø²Þ¿ézƒ‚„6lÉ'¨Ôù‹þ¯&ië „¾P;_½T‘àdôg£F�žÊì~žoxÒq¸3ˆŠ£CfXä@´d|ÌÝ|KC»²É5vMËë�9 ZMêÙeWV´eKY©‹Jq f»¬»EÄ(1/ÅöÛ"EŠ˜ã�z=ùÅ¢�";"§Ïþ<©oó²ü‡ë½“îjÖ˜@òxõIU·Ì³®‚Š€€€TàH¯›�vÚꥸ?¿ÿþûn2úðGÚÌ|N×(»ØþJ0{Ãl J¥ŒvæfΈò$S¾by+~~ ;ãÜ3­À)'Úñy�·ÂE‹X•*UìᇶU«VY<ãóÏ?7æ…$,1HÈ3¡©ŠNÙpàòp/‰=«èr '|~÷ÝwÏà}Ã%&½3y.G­Zµ²Úµk£MÒºõiZêxoê$ð9|þa‚“Ð6Å&Åp©ÅþʨRäFÖÃð+36Ü,¯»î:CÕ…¼˜¹$È€žÔÍ7ßÌ==JM™‰QjaFŠ…qdE|d•9 üXWaf … $Éã.\¸°åË—šîeÖþŠ¢Šì„ãÝ!¾³÷ÛºuëÎgÞ�÷ Æ®<8Iðz’Áâ¬ñ /¤ê½ÄkOÌ,�ÛB±M8¢ªÁtíÑzÆéðÜQ®L–m’ò�8 ¹�¬ÿŽéõÝ—Íú@w)ÞÉ�;wwe'“E>HqëækT¯nWêã½wßm÷‹˜Zéc½ºu '�~ýú1ß•â÷.I˜Ûraâ²të­·FË.¹ä’—R.þê¸ï㜄6;M{å•WÒ»Ã<Nù®{m=»¤fYà ôòÛ¯6üؾšû£!øtbwç­e‡6† ·3J�ekU2nµ°|­ÿ Bnî?üðCr¿w>‡AKJF»�8âˆ=™ÐTůŠs²IîjÅg„g˜ú@IdÌ”U! ²®£ÞØË"ŸþÚ+5MÆ»¿¼õ–=¦¿¯­l›,›^ }´ä–�y(¿û­Y2Yħ,l¹Tsßù k@@@2ÑU1ÂIh½b‹bŽJdkñ]KOpƒç†Z¾ry+Sç2c0_6†B'�2fsˆ^ŽŸMþÚ:ô{ßêü¸U»©–]TãR+_£¥»ÿ aÅ-[b”š’e„”<唞T´hÑ"¢YNDãÝz&gwü8Í¿�Ïd³ÓCd²¿@ú‹�Cƒúõ­F… v�Þ+åhaòE$"*¹þ¥~QSýy-‚×`J²d¹,^}”gY2hÐ J}æ’y.vw†K’7Èð‚K´—9mRÌ“wÜ*Àô¼Ís˜²ã§ØÅ%¬ê 5ìµßß¶�»FØ�Èh# "Ü ­Ü š‘!Ýÿ~[{wè‡öâí ÿ¶Kj•µÊ5ªð�áj¶uqàÖ=fÌ{ê©§ 9ø£�>šªÝE°Mš4Ù¦­««?üðåzm!£aŠÛ³ø@êÊ.’ ¿3bĈ…”Ê»tébMuÙ¹N¥¸çe¾›8`�ÅrÇx¨I«£L‰÷ã³Ï>›Ö÷6Ï3ªD<wj•Æ8TzÉSÍ\é¶.Óü€Z©˜_²dÉ9H¥Ó3Û˜4i’‘ ±‹§ÊõÕ­ë´o!ž( Ü9‚ÌHDSÆðiï ‡‚VoÜoŸMúÊ0 ¥<‡½Ma Üö³xžüq»öÚk�²Ò“O>‰xjH<ÐÈwHº¼Ný¡5dºÞ ,•E=áj»#ÂÇÊû©lÙ{!a3õ|š©÷óœÊd»•©Ä’Ðr•5ï×ß_!â9f=IZMqÉ�¾úê+ç^�à'y3D…ÝbHh5$$UÕtÄéÙ¢ŒñÀƒû@ëw™ JD”åpª†|ξè\Ã&§x¹’VçÖ+¬ëôïøLCÉ’ !Tt(�²C6U^µnÝAß·jJp¬éáÁ×Ü(Ÿ„˜y°ŽŠc²Xîì=‹Ï8ãŒ*ñþK»Ïóx».;�Ð7Ü`Ô‡Œ%¡Ñ�;Ûu•+[íJ•(Ýñüìó½Jyï¹çž3“|d§Öœ9söû31yòd²!Ä ;Uþìã�óTE@@Àrïx¯`±PsifÓ¸ÕæÕtŸè¯}c]+Uñ|à ôñ®ÏÙoëY½ \«!H2âó(ÉAVô‹žøâ9+wEHˆàæÉ!’e‰ÛŸ/¾ø‚ïÒ‰~_<÷é8Kïyr’¬|ÖæÈ‘c•_6&P6ÊBƦyÍÉ€TZì* ýÊ}ô¸qãìFI¨¯ÑºóÛ%ûÿ«cGKÒ|ý Ê‚¾TfÉßÝ«,iÖ¬YÿtÌœ9Óèñ´ñ4WÙ·™f®Z‰Ô^–äûUI¼%`�„�Ul¥n<yóçÏ?Ú³µÚ¡7pàÛå#Š?½—àd´P’Ó2Öô>xŒh•®©b�PMyµ=ÕýEûnѯöóê~ÖñÏOìÚV×ÛùU.2²%²žz¶3<ÜîïøˆUª_Y7µ|TPl}ÍÔLˆC‡9œ³GP‚A²{ƒnÜܘù3'—y¨¯¢©Snc·iӦ̛�¡Dɇ!S2>/½ñ×_¡âZ_¨P¡IÔú`ò Eý,@DG*®P¼ éy� `_Њ»OóA ô¼Ö¾ôR»G%·¥ßo dîÚKÏí«’ißÛ°¡ W.ö½Ã@ôõ�x_5’°áU‰¾zúi&Ò÷ñÇö³¨ÛéïîlÔÈ(ãõìÙ“×:¶�Çû‘×�Â<(´9ðüP@@ÀÅ.Ý] ƒŽ4ű²Oï¦ûC¶j××4ˆ‡jÜ«ýìi{òËç�Póv­íöšYKæ*^[Å�jŸwIq«V»:7U£9Ø3e>G>dÖYeʉN„!œ¢s#LÑÓóÒÊcž"!óc¶u‡“v6ײ|1 Fñw»T%_�AJná…•+W"ÛfÊŸÆùè=¥WEÅIÈžãø¢TZñ,¤%Œ¿èµØM¼¿÷×’dߨ²f�2ºè\r‰µiüøÒKöQÛ¶ö|Ë–ö³dÖ±îç� rmfÑ®Ðëx­ÊuCE>»dÓ[Ê›üÙgv¯”u5åžÁ%ÅX0dž\[нÕ9sæìæU†"Š€€€Ü2;»ÇØrWÊ-QÙfÝE]dÓ§OÏ�RÔ/ƒ~µ«š^cg”<ÓòžœÏN;« {{.D‘�<ûÜ ‹ÙÅ—]BAÉ%32 ¾wúÑYÄLâóg�’†zYuA–Av„\U䃚�?c®a¦¢4­ ȈaG¾ÿž’bF?dg<þß™zm)Å­ð×z¢}Ç›€[*>•EN/)7Ó¯9áâ–P‡ ó²Ë¬¦žûFê=¢KÕzÍx.b³[2Z.¼NWS ¡n£ ùì(î®Ó×Åá…f,° j+¢“"1Q%¹ž.¹Lp€›æí~#ž eý²ë½÷Þ³Œ5y²²ÊQ›\ÉJ^PÊŠ]PÜÊVÕLQƒzöØó�³\�C#SV€/]ºÔ8˜°|a(‘çaذa(ò’õp#ç±’­AH’c€I†27q>'Ãˇ(ëh¨ãÍGÆ¥†þš#�fj»iR K°åÔý¾Þ�åµR¥JÙÅçœcɨvoi7ÁŸ=¦r,$Ä…áX°W_‚Âi{µÔˆèñw8° 1 ·¢¡JKBs%kõº‹²CfÒp÷ã4•§ø÷dYØüœ^° {ÔQvtîÜ–ÿ¸ã¬ð©§rðPRƒÿKÚL_‡ i÷ HˆÉ*qû¾à‚ Â�|Ž9æÜ´MÎ ÆŸ,Xpá‡JOh€¢£ SÊ*òÄ©7ÜgêŸ ©n[¼x±¥74G�UY8å8úGÿÊ„èý ÷Ì]êí¡˜£D»b#ã¥oH_h·HhýÖäIµ‚�\yE7oVG<¶•.]z: ø¬ä6À�L¦˜áÓòç·¢'ŸlDÁìÔ|ù¬„²Ê/ôpþ+«€€bƒ^ò\TpZwa4³Qr#¦´lÙ2nÞ�¤•êr !óõhvÿòË/|D¼ßÏGUG9é¼óÎ35ï áqüñÇ›.¦Ò®ÚvÊ)§lrêáíмq8Xy>Yšô'IÖgë5�dDù²¢äÚ�3d�×Tü¨’Ú9/léÓÇ�wÏêÖÍÞ•h¡ùUWY3eëÍ”YS¦åµÞW‰cÓ%J˜Jˆ‹Eø’ç×PPñ¢Ï�ìTDüãh܉9³Âì �~nµgi©[©3ΰ:ê/½ À[’×¶Vyêr•äÎQvTNÙ}z3ûß/ëP»1¬È¡qÅ®�à�—M‹üš5‡e0f}ø|äì!zíµ×Œ]?ô2 `Àî…àöÜG3C:t@)Gƒ›^ DA ‡,Š,ù/‚Œ´Î7¡þâæÎ׿fÎÿÏÿ�¤×u·b�Ø>¨(GYЛ4õ•m.Њ†HF¯y'+eFˆì†`î ²A¨@©Ž-µ¼§¸<+VŒË;‰xí÷õ>DЂ ‘×5QÏ5Rø ÉÀqŠçÝ[,Á©:¸¶òCÊ¡›`ê9-.Òü°£l“ƒƒ(ܸ­Ò#‰âˆÊmS¬ˆn¯*Å1õU:‘ Qb)­¢€2Êld:QäFŒÈ‚[<‡eBzU”÷¸y3ä ±³'ˆC�¦»[Á¤÷c�¸!FŽy%2/ÊudCIîœÐdzà\q�•1÷RysŽ2Á]ôá2”Ð(�B@7*#ªªç¨¼.0eEB%Ï>ÛŠ+Š)‹=_¿§oäå8J«ûs§àùNÒû焟g(’y\ãv. [‰*9íFþ›‘ 4ù�E TâÖ©EH%d ¥/ÊT±Y>³!²�Ì¥jÒª>ÉFÍ– nZ%GéöúÚg«'T\=#z-û;ô!\ D!ªöíÛãFMV–¥÷1”IùR'3óòÜžà_+*zYö`áXÅ=*!NV™ˆó]F� ²êÚ( E6¼7ˆ3¥¤Ì+u!Á¥¦ŒÊ›5< ‚„ȼ÷ñ~à}È…cÛYg�õ¾Ï`¯H& +>U¬Š–ä’ö®gþ$£@FB�…ýšÄ4‹É~øèY=nôd�Ö¿†3YA�}N1&`bù›D¨�ðÃÞŸ ©†¾û#Ä(Îð’aÑ7àÇ_ì#-Gc–ˆ*J0YŸ|ò‰qˆž}öÙѲ æ†6ø†Ý޾�íðƒ¤Ð¬©>ÐÇz®—Ñã’‘Ñ@�ÈÌO%9+§7§PY]~*•.m5ô¾CÈr¥þŽ÷LMÍ\Õàcõê¼/LE5¹/±CD¶R3TÆ#»¬¬HáêäÇ|Á]$ffh#‡;7Óô.Ñ—¡±OCž@yF?… ó<4çÇŽKy‹2׿äp³ãB´ÄµR5adù¨¤®J- $VÒ=7íý•!J~� ~¦îyl¨ßø5e37hÊkܦçÍ›ç}œøòq²;Êr(è¼7E¯h›^çQ1DtØAL}JÙÃ(½ÏvsAÈ k&.\j.UÏ ²yZC«Êy;A¥Sz‰dÐ|DÜBiº:*�‰S.ÆÅcïžïçD)±ì¹!u¢�€€€J;†ˆ¶*KÙ•žRY²�ϤXCÉÅÁÎ�B‰A@šýè©É28¼¸á¾ª�¤M% ¨¯ìŠ›,t©n¹ÔøÉ´(ûAl{ƒÿ—¦>½lb0K¥Lˆ`€ 2ò IÍ�NG†Á�3¶1ø<–xlEч{BTÊ-Ã͈2¢¥nnÚ]Ñ(KsyÍ$œè.g„^ÿÌZÑAI–ÿ� J·[õšÓ?Ü_Œ‘øä) xðïx_°’à óÚˆ²¸%*wa=®Îô~¢þØõ`7ƒ0!½Wê�h…óµ²+Ì,«ªBfÃÿÇÿÍl óBÌð|¬r¿F¢Ë É7Mk²/%!!�&?$ƒD›ƒˆ¬ B}Æ×¤t1Q¶ƒ�(Í@ˆñÔK¢„DöÆcEÂM¯  ̵0C4E1TñŠ¢L&”áj(:éदÿnŒ_3 ”€éï<¡•ßóuÙ€hï´nUÓñ#ÝGÎÏãnÓ¦Í6=�ú0p™à�úƒ¡™Ï %: %éÆ<)rT¼Ø�2“ù eeГò†4¢q8 lÉyöÂŒt"Š—¢ºƒTøz¨Õö—Éðç)òñ|�Òó@Q2BÞ 9ñBH¨¯PS1ÙŠ<ž'. Ì+á¶ÀÇ5n`;Îwâ<£ÈH\ªxOÃ�_ëuI@’I=6þŸ¨OásÊŠgªˆ”ÿ@$ÔM¯%Y¯+Ï!JGúCO?ýt¢ÞË{ŒK;+ŽV¤Õß*ÖÇdCKž|òÉ�iÙ1ÄlµtH@7p}²�ÿ’€spó÷Üâñì¢\Ãï“-zàæê$DÔõ!Ê�±NY rIq¯… ÓUˆ�ï/–�0…t‘÷2»™“!¡êûRâïøÚ!€`®‰ÇJYŽlâB‘ÕÏtd}KQW‘Q¸€ÿR9s2ÊF^×L´xоþ�ôš¿­×Ke‰ýä\1RƒÉ;$t‰% úCÏëqÞtÓM¼W˜ãCs›H|¢ÐïŠGÒ&ð8ÍoÂËbHh½ä½«XÚ– ñ¦Œ¥¯AÂð5#óèÑ£%¯ýî4ü_’©$ 5”jô�8<8D“Ó¢\‚Kr,ÑÀd+®Æãkóy(ã8 Ò$5GLტ¨ú¢ƒ�¸&`,ª<¿‡œ¢NÎr€Ä¸Q›†4QïÑ“Šö¦ð%KOà�ØÄIˆ€ˆPÎíÒ°è½Þ¯úFÓ£2h0ºƒÞ¿è5X õøL¶y¢|f §’ 5ÓsÑX 8j °|HdÃvU„ s%qC}A²äX!M… ¶Jò>Þ h€¢‹Ïܤq—K\�x¬ÓÔÿ"6t¦ $G‰ £O„üÐÒ‡ âöˆgƒ¦ûì[p“­0/ÄÁMðk² äÒß~ûíŠ =J|”MÈB8ð!ȆÇáÙ Ž ‚lŒ¾Ozo4…,é=D3$äÑ|/Ì@aßCFˆß$ÀŸSÓÔ¨¹yÓCKGÐßBžÌ÷ÏcÂa;JDz ô¸~ó9—Sé‰âd@Ræ /ç5&‹< ›îÒ¥KÔ믞H¨¬Þ'a©”7¯] ËÂ5RÃÝ%áB ½_ùÂI(¢×sµzY{\ÉûúxC…ôQ´TŒQlv?¹2#¦;8À’»)4†—ÃÈh˜('ã ÓÙûP@âJfÀlÙ½² Ê&øµá�¶·x�LŠ�R$Õˆ <~Jk±ûtÈH �‰Ç–iû‚( ÒÈæ{ƒdø¾xL}1²$B@<Y:A¦˜ï•ÇáGöÁªœ !„)·tz`²THŸçˆÝC”I ‘^i=.à}¬(™TН%§Ã{¹^ã} Hà`ƒç�Ag.-<÷^>�–LyOy¶Ä{l—ÞËC%)ÚÖæ{KÛ£€€€£ýšÅ(åÖè ÜBÆ�Ú‹�b‚2 35.å0E �\òùW#ž�r !ã! àŸë\¬4Ê〨hûŠp,JHüù /¼Àã‹Ç�+¥@óÅ��_”^”+O•]�zBÌñq·ÊrS\¤�/ ЕÝE@ƒ”õÍW&a9Þ¶Ö²ƒç�‹—!Þ;”Þœ€�ï'*Cí!ºßݱódìFÚ€€€[|ãꪽö -Ô»å1<´ Ê*4m9ü)aðË*�Œˆfý>W]fsèQ®ã‡žx²dÄü»äJš)Éñï¢e<2 ²!d¿(ÑÜ¿¢Lêå9JsÑzõoÆx¹)‡"%È©¸Jñ½†a‡ˆäfë �ð^aP8N÷Rá€Áû%'6Môy?ï�Šï;e‰”©‹eÞ j@@XïÐ^1— H±Ë‰h›šêÉZ —º’…@Ü"™Ÿ`‚œLg_?èü 2H‡ƒ�^ ‡ ŽÓdL{“_‡l�>Š9²Ÿý–VøÚd)&"Ê„9(É!˜ §–¤RÝ"½æýÕR˜I7W €€$vX þV„}L Ç+xïá*q‘âr„N3m½%Ìi̺îÌB (£xG1Á³¡5{Êrº'¢ì¢‡AÙ‹ðì5™É~ÁiB^dM�.dS¬4ˆ%æhAü®]@�ÿ95ÒjÉéïìÀ.#.ì¢WåêÄ­úýd²¡döòûZ�!ò¥›B ®zõêJ³ §ÑN+V¬@®N ‘uëôr¸¸ Åç×xþÑ#Äõœ>#CÇü»d;y Êäb„�Yïµ5úþñ‚«ž¹}Ÿ€€°Ö¡¨âeÅpŸ¢_¡HðØ¦ˆˆ„’@$ÔÒ»uë¶_ò d; ùA(Ì©@.ˆbÝÈx°ÄG Ç�YѸç`Äï ™7%Ê&éߤ'[Q録f» £%>sÖ¤ýÜö©¯h¬2ˆ5R’E(k¡BK­(„Ò-YÔ�<&2ow7'«Æð7J¼øº‘qA,Éö2ì(óRz‘\tN¨·¸D?öEç)2a6(¿ârÅ{ nA@„“ÏVÏ„v*v³€¬„†›é~n±ˆÈx(½AZ ˆ)öó�2S�§<‡@�ƒ�¹†[! JDd]ܨ3Hå@¿†lˆ¹&ž'£$õ‡f±ùTq¼¿_�±z:[ñ˜¿o¨œ7\ùj^G$昺R†M‹ª�2}>ú}¼ïœpùqÙ4¿F10L�Ü>ò~.E|mH(¢Ç>MÂŒÛ}ÏQ@@@yÄ�«¸ß—š�PÌQ,r[ÿŠAÞ øM1Î6Æ8loT©eNÓûëýÐg@0@FCöÃ-–-Ö8±ÙÄ‚JŽÙZýðã0$‚˜Xëµ» È89*G#¾¯(g¡àn)Ü–ëuÿÕ·±^¤(§à°þÆ�OGJ‚=I™Êþ=æ°éÕŸ£´Æ^+2ÞÈ©}É!2!Js”iS¢„¤|•÷×­[—ÜP½keìÐi@@Øô’â{'—ŠÕŠÿu_÷Ž{Æ]Ž«øA3"òŒ(âYÑLÝt7ppí}`@Nd?4ºùçæŠ2 bŠ%*ܪé q»EÌžæV90Q¯¯OÃ8gJb-�({‘�r1P v»ÏŽ RtõÒÛgüZ¯Ý ú?(ÉÈHè/eÄpéÔ©S±6‚€È¬y�‘Uó¾Jé{¢¿È×!ûã=:E8ä��8Á³Ÿ1Š%N>KãyI®7ÁÒÊïÓáS÷Zz·Zþc8°¨¿SÊ¡7„Ó0ÄãŽÒܪ)‘�Åìm¯C‚?�pdÁ¬è&cÂÑ™v(»D7™ÖÒ‡!#%C¥7¡ ë–NeGôšM•’l½ÄI¬'³Í¢d¶‡bj2cŒaQÁ14ÌeÂ]¡ïƒê@@@@& •¢‡—Úº+îöM�¹ö#AåÏ.t—àÕ1rí튉â[ÑH†Ë4ÙOÔöUd{P@.˜h25O�Æž”ì %–ƒ¾:;€þ%9.\ÈRwè5Ÿé÷cuÉX®ß.¨Ò²J¦‡#e8‘'ý -Ê‚˜i ÈdF¤`øðSÅh—kG<æ+¦é¶¼“:=sBú5þg”Òbëó ã ±"Nˆîð�ˆ +„ ñÓï xóÍ7 5#ŠÂ… CBô‡˜š¡òÜÜ:uêa©ÙOVÁܹs)Ã!Åæý·K*»Oâ¿ =뽡ù1V>k]A÷—f+’�Íâ¦@“8V|@)'Œ<)·A>½"ÈŠ>R@\ɵYÈG9Âi|øè­SÖ»�¾ÙOec²m dàI*S†>Iç³CÇøÀâ'¡NB[¼WÔC³?3ð7jÔ(2šXƒR¼àP6q¨EW( «Eçdg^j¼^¾ �r)}!~M¶áu¦lšÅÀêÞwô€’4s¶@qkü» 5]E ·bé¯XCB|\¬øIÑYs‹tÛdÀ…æ¡Q%å1ˆˆr’ë€øë—�•¢dä²@æÃkFØ51d̦ܬ¾/[¯ºêªˆìx&Hí÷^ü®â8]ÑVÑÆçBê)šøúçy±$#ïž®+Gìô†(è׆³5³`~“¦\‡âƒP‚#k}÷ÝwyÝØ3D.vhá Ö8Y‘\xe¾ˆ¬n•j¿Ô{õêøË‚ (^ñŒg´"6U\¦(î%¹aŠõŠ$Åf'¡MNH‰„vÓìÒ€£�rÊ)(«¢‹ÂúF¬iˆ/SæmèÑA@¼N¬ÿæò€„ž êG<Ú² �d3*@Iq³VU0çÔÎ3ü€€€8êûœé³B£]l0Á‡V‹ò÷ǹµÏŸ/JˆUËÅÈ· ÌN±ÜAYÅ ”`Ž(¾Úì|"Ë¡d›ù Aj�”™0–fÉ,è‹/¾€P¥ì›îöBW(âÇyÏç7' Šçös[<[ÑÝÉjžbµgD[œ�’bIˆ�¡%ÃŽ&SKÃß‹žQ@ü(ƘóÁ�™ ' ʨQÂn‰R]�›Y ˆdå+\Ýù¦"·" Žp”÷}Ú(!+:À|Ñ[оNVÓ+bÊr»ö&!B¾bÆü†§q¥„CV•cÓ»£oç3\,,D^ŸU…(âèmU‰xˆÞ“ÝÜ$ N•p9B/Éýì½£tïrœl]va÷B“›’N|-5 À©k×®ôéÈ~¢›i! ^/ȉ==Yx2‡F™qª¯£xÙßãYG*nŒ1¯|SÑRñ”—çÖÅ�Ð6•áÖª¼ löÄŸ{’kïAD”å !ÜXs�¥Á{õêemÚ´IÒ{°·Þ�{¶�E.QÜ¥xBÑÞ¥Ûy�˜.ôŒh¦bãÕœÊp3Ô[Çòº€ø.dA¨Æèû@B¨á Ôp(åX©žEÁ#†¤LÝ&AÂ7Y3 й“m¼”ÑÝɧ™â4'*ø®™9ŠåÞZ¦øY6)]âÓ.ô‚Ø÷D)Î ô… !2"ú(Y¹ÌÈ,&¹x.óõ$µY9×(sÙvWÅûŠrüÝ>JtdEý Ÿ˜˜¡xQæ–X@Ü ¦b$‹R‘ù_éMF-Å17”U1gÎcè¶aÆ)2ÉÎßÊZYP@@À©ŠŽŠ¯½÷s¯"Ï~&Ì ûç �! UŠ^2¹ŒÏ¦BØÄ:HˆÍ¤Ø*‘ù@>”çèñ9Yõ{£„¬•ߎ}:ËleÅ%êK†,( ›“Õ¯Š)>äšèö=Z·n}¿Äm9²yñÅ1“¥$Çz²"s(˲ìàmß¾}éu!1OÔÆÞñ¾¦> ›¢„S†Û©Ø¦è7oÞ¼v·@2OÉŠí©ô�(�aÍ“UÁ_ö=ýôÓí="³ €€€‡‹cHh³÷ƒ>“B)è²ã¼wòÁ»ƒXs€?\«È‚�eãO¸Qº”ˆ+²q)®›K²çó1f­Ã#�%zC¬çFÎü×ßì�t\Q†SÛ¶m¦¶mÛ¶m§¶­ÔFjÛ¶‘*u´·ß›óºg›šAïœóU»õÛ÷¿™ûÏÌþý²¦Û·Ó8Ä�€˜zâò;È5Ø‚‚:ühD€ ¦ø\„‡à ç¹´P:|Õt߬jZ´h‘…òâ}qFKA.ðq(M;ÿÂoì3Ò¡CGRØ �MœMöѧ¡Ïƒtü³!¥�¾§>}ú¼aìVŸ˜™â“ZÂ(èé! �:~)JÀpöÂZÊ:ý”ÿh<g[b1gßqæNç,þ|ˆøøƒPP'rÎî³R”ìd!{·%üXoHþítèÐñÓÑŽƒ³Éx 3±÷U:tüåxõê•aD›9�ëöS: ¸Ê~’B¯ø…û­)×zƃ:ݸ×î6ßµPÉ¢æî9™×ÊüZ6¤C‡Ž.pœmx½•R›”9hJ•uùòå³DŠÉpe‚b>! 2Ï|r†ÊÜi\ú Ãw7é»ôu»a+Uýž‹„Z]ç¿ËU{ìrÞcÿdB:"Á˜ ÓaT‡,ê—.Z=à ¸Â3›¡¥µ”R7•y2}N²÷ˆ©ßFoÚQ˜ á|€ƒ2± ôYX¼ÙÔëˆ�§Ãìíjà”-ªa¯Å"BÍú;º·°l<ï a§ÃO[ˆSAWX»à‡#°ÖÁ8h ±!0èø±è �Àîà w ´Rê¤Ò¡ãï•áä¨xñâÒhËÒÄËæäŽ²àÏ›($TMR|પç<ï5~ƒZátJM]º_ ž¶UuµVu¹F�™¿ëÁº�gÓÛéð³ º€#l…�&ûá$œ³á †õÐ "Ã÷Ócým–عÁ+¸ ¹”RºQUÇ_³“³ŸÊ8Ê’%‹q¹Cdo °P'qñ�k(·½62Ÿ5;Ψ%›Ž«¡3œ ¦,Ý÷äô¥{ìtøÉ ¥ÀŽÀi�Žþ€¾4¨G°`Á_+z TäÈ‘U”(Q<øö3³áòÌ�ß÷¡Ã †§à ¯à-\…”J©+J‡Ž?oÇ–¦ÔN�:©%JS²=bÇŽ-e8¨þ¼Q€"‚PÝ _Ÿ¹M­ßuN9n>i �S·x Ÿµm×±ów2ØéðsÒÂ@8ÍÍžnþýû÷(�Åž�!Cª0a¨"¨ˆ#~Bøðá=ªçfÓåQè ¡@Ç×Eè:<x ç!¾Rê™Ò¡ãÇñãÇe8)WÅŒÀ2¾ëæÂº1Á( ÔJQj°cƒ^‹ž�š»Ã*@ˆ�Ð{üÆç†¯ÌûüØ-<ñ 28Àr2š›Š+"cq\3*f̘Š=ó*I’$*UªTŠ‹W¶GRKV1+ ‡ ø÷¦QU?²Ö¸W¯^rvįBô B7qÎiêWÁÙf‚örÖäUJ¹‚ruu•RÊêÕ« [­|_‡ŽïÅÇ�ϯT&ìíí ê<0=4«á t‚€Þ”¥€ñéÊ»]¨á$e�³ÖX•©’ƒÊPq„%oÝñ—y½è¾C_Y¡%,ƒóð,&ž†ã�3OÃíFZî5 ãA·nÝäüçWâèÑ£RÆãlÈ(ÇYm\vÆï÷Òlx¡ÿsêWÀî‚dB,cd=ÎÎ⧆›‰±±Sؾ}»Ò¡ãå7qÀ1 [•*UʨhXâÇ�ÿ–óÙËæù£3œ�*È›(< Œž§ç¡|õÆ{d«>Z¥-7L¥.;T¥)7Tå¯?áyîÚãêiò½ÊÁL8�ÀÞ"0¯0¼7Òò¼yóª¬Y³*¾/â€H¡B…÷ 7?)¯™ûôåi¼C‡ª\¹rFs›êÞ½»¾òA�±ðåË——¬Ê«Ð!LïÌ?Û4ˆý‹P¸ ÎpÓüz eP'Êš’Q¶jÕJ5nÜØº½³eË–J‡Ž/„˜…(¿É`Á‚ “Ð; wlšÊï °‡Þ$@ e¸íˆÏk2!•°h•¨è¢|uÇ¿©ØvVOß-@:¢Ã`8�á9<ÆõæÌY�;såI G›qÁÊÅJVdˆhÑ¢I ùæÍ›Êˆ[·n©É“'Ë¡&i½˜0Ãâ)½GŽׄˆ__jÑ4´ÚfDOfn¦=ydþO�s á<²yJ½Šåýu„ Å+V,ÃJ+Ù)ó½D”tèðbêØ±£Ñ|j<\ZpµÞásvÂF€À4Hî�æ C€âès0wí±o 4˜ ™Oô<½TÌ|½çBîÅšLhìtøÚa-<WpÁ$p“,ÆÃØžh...b­Æ-#Â@Æ"½?Ì”2„Eì×sæÌ‘'ñL™2©ùò©‰mÛªCˆÒQÜ6½6ÜWµjÕ[·nµOa_ÙY_¦L"21!’ßÏݜưšü‡«„+Ày¸g!àéÕ�ÿ3«p“©Êû^ÔŠ+”6{b½®P¡‚Ê•+—ÑÏg4–¿àºy`žŸƒýÐ Â{óHž„Ñóö:ÆyÏ{ÊmÆù�”à2WvP¥[LWµ»-p4ÆöØéðÕÆÃ5x¸Û¨Q#WÃ0à58ä–snpr³ãŒH.äåË—‹@!.R[.T¨�Ê�!ƒZУ‡zƒ¨¸mÛ¦jÃxð$³y|áÂ…/Ú{9ó‘…V8gäFÇ…l”ÏĉÅÏ1Ä¥ü“�æ¶Ê”àn;:*ËÎ�"DO×®Uï¶lQ;ÇŒQe©Aƒ©k׮ɓٗ‚M¡RÖ£‡HÄî ®<7s|Íyp„Ö�À�O嶇cpÝä9xxý·A”$Eè•=ýúòåËjæÌ™ªH‘"*Y²d ×›q�x B¯L·å>˜ U|Ju�±ÏPiÄM² û*#�ìG,ÙÍ8^oÒwIÞãË Œ r£#•7Jgb*X¸p¡‘%I“ž© mH÷_¬_/"ôdÍucñbɈ6 *¯W¬XQœ[Û7§÷"T²¿„Ñ>Ÿ ‘Í4i¸ Ça- ‡r�üR½85ìƒ+pÞr#y 5%8Áiw7ãé–§Üd(_ŠÕV$tü×дiÓäÁ„ëÄ Ÿ3‹Þ “ ‡OkG`ò1áDÁ=?Z±iP5DÈ9Zîž x=¸�_±a.ܰ“®×“½|+.]º$Ö_ú Ä}…èˆøØŠP:DhDÓ¦Vòؾ]purR³»vUñ¢F•™qK—.ÁûÁUil�0a‚*\¸°ÑÜ*Pù3{#Û];OÀÁ¸ Wá8,†J¾¬ 68ÁN¸eL ç)דÿ 1‹`Ü�9_»wïV:þë�ÏVØ(q•]ÐHÊ.PH[!²À±ÀG…Qbc;ê¸Üuƽ5R™§²V¥J4›j!ÚÀëqítøúð�a¿MôŠóÏ´iÓŠÅú[ÁDq×༒W 8´ Pÿþýņ�{pg„Š2œL!’ò\ËãQÆ£HΑ~ÁÝ#™ÑéÓ§eÉS¹mÍ ^±Pvp3ÅèŽÍ”�7&á0¬�ñ�|r$'“»ØèÝ “_‹#.Nœ8’]þS´ËÍÄ:¢éñÓWjϱkjÒâ½*m±*@‚J*`’Ú*`Ц*`ò&*@¼2ÊÔOü…Kîh4bJ;ŠPJ& \HZb Ñ„*“ # zÅ÷kØéðÁUð„W4œ>§_@Œ\Ìß¡ ¸ÜpÄIޝ¥QĴЬY3qߨóDîÀ·¯s„‰ÀÊ�'cFéaa@¢ˆÉW¾dQâÊCü¤EiJÉlvõjï¶À;nÖ† ã™MønÃiØË dõ�Ž»²à;ø{<1Î~Ìž- ËÞ)Ãèð½Ââáá©\ÝÜÕÓoÔÅ�ÔŽCWÔŒ­kªöZ¬hÎ4¦E+lËr`o”¬R•¢R–¢Xo âèå(EcKÀä�E€>!u›gÜè·@1ðçÓ² EúOX¤¿}@F$"T­Ó\K�.óœxÝíÓYÐ!g<Ì�;÷[£ÛþÁƒ_4°ŠA²–'Nˆ ²rþüyãÆ'‚pýúu²9¿‰„ §$Ó<¦%¬"SªR¢„ìê�uûÑ£Gê;qégW3ÈS�3F{ü>r Ë�ZJUÞ[ʼn:¹X½mG�¼¸ûat‡¤>@�bÂH؃Iã'£d �ñ@ ‚¬Ãw‰ÎÛwnêÖýgjšã~U¹ýl¹ñ&)>P1!@…ËÚU…ÍÒEqH¯Bd쨂g訂eè ‚¦ï §m¯¸)…¶ˆ�AAHÕZLÙ“×Ãzˆã³ pé* ß„ z‚j_u¤Ñ�jLÇ~]¬é”Jv:üDăpÛæ¦{ÎØýãèèè5Ë�éô IW5‡ß2�FTiNE€D˜¼ôúÈ�p1Ù†éÞ§T$e"Î-Ä6ŒØý茹w¿±_ [8“b¦�”ù;Èy gHV1âkqùÙ–óLQrg¸Wᬇþu„�Ap€s0g\‰’™%8 Ýä{ÂùÙ+µïøuÅ*c´4aâ“Q4Qs÷´)Gw1G7>›ˆ‘ƾ‹ mßYD 1ÑùL�D€>‡×,à W¡²óA#o¯j¸à®šŽ8)Ço:ÅÂêîc~k2¶î :ÏÀ/ºgðàÁRf³™)%…¹MQqV$"Âùƒ -Z´¨œݸqãKå;°³gÏ*~]i˜ÄÆ-âC?‘ Ùü‡a¤+W®ÈTiJVr–ÄßAʆdL²ˆ�LÉk ÏÞÚ˜îÁØÓ!øÿG Å`çp¯è›2²;NFõˆkPlî:|t¼yëªN_¾/Ë×:ŽX-kŠ4ž¬ŒYhÜ|UüÂýDx¼ "dfC’ ™Âc‹¼2S'ë÷~S„„wp ºÉ‚�ýÌf:Â[³1UJq¥[LsÃ%×ÓN‡Ÿˆ�0ÎÂ+ð„¸Ùn,Y²ÄV€äp›�JƒðÔ¥´µGÚišÝÆ÷ì©Ò¤J%ÂD¦#óäD„¾!<¡K9ÏœUˆ8r%Ê�2™�þ&™}‡XŠ0q£a¢!×ºŠœrÞ+Ó]÷ÂuØ‹¡1„€¿é`,¦WÊÑ”²!éÕ¢Ñì Ò%·+·œUŸ UõNsU©ÓD|¸ÁJï‹Mö#%8SP¤ì&¢bf:�d@¼.�I¦%W�O+pšo �-®p vAOˆëÍ"-M¹a[³VåN9Î8ë’éˆÒu^Kb§ÃODJØà¸ÀQ¦[[ž˜O¶s òDx¤<ˆA2P¦“Ëþ%>6Äݶ÷ø51ã5Û!{‘Lqù¶¹À„ŒÈ*Bd’ðkŠ î‹B$ô Cê®âì¥"gÇÌ�¶•!6Ïáý7Äè <†Ã0JAÐvèißù`ºòÃß"‹CNÎÉè²h0q“�?㊻ᜎª«FJÎhÌ,ˆ©Ù’ df2v²�åýúÙŠ�ôù´ã†‹c–L™d5·« >|X988HOçdâü#3‘³láo18Ü1ªž�ý°úC9Há¾’!ù‡@ ÃV¸ÈyÏ3ú¶Œ©">¸àdZ9Ã'e¦Ÿ÷½Ã%7±LÓx)¥³ì�p\G³…F;ÌÌÌÌÌÌà<‡™™ff";l‡QaV˜ÈÌŒ²-f­æ�ï½N×”sWæÿ·¤™ªS+ß½0»©ÌÙÓ}¦›Û<›d’Ç›%$Bn�Žºˆ�|’Z ¬œˆTì²£×mhwàÊIçq¼iÉ:5Êq× çeQœ/% @v'_3³‹•.ºÍ•#U%¡‘ùò0'�Z}Ÿ›o)H£UŒÅÌ÷£0Hû‚ÈF�Ã+”;­7—n9-°gËP0Qû ÂqT@¸Cî¸uåÈÚG}…㵕AÈŽ:v]ºt¡>NAœi„È #šûÕšã°N(F™JúIøc�Í öú�å”~ÚùŘ!bƒDe BŸä³Ò˜5aéþ:^ù"JåBŽ'_˜ —ä‚)ÁÉ2I`Bà:@¨Î …Õ!ç=ƒe9€Î~râ:Îá>òãs�9Íȳð¼|ójŠ�³œQ…ÐGè$¬^0“†šÕm¤ïb"óg®¼òd×¢ç^�F«k k¸¬Ò¹ .¸À÷êÄ íO?ýöÝn»°¤¬Õ�Í}*h:VùòA=ž}6tTÉœmõ«üÚË/çü¶˜h†|½#%!:Ú&(<‡e:_ù b �޲n•ÃøÛÞ› Œä˜òMUùp_Kû,œ{K£¶®!<õú˜ #y¨,�òn(X÷€Û À¯ÈÈÕ�‘ ç8™@pT“>áªÿSD¨…8Üǵ “;ò뎹ô9Î'ÄÅ9„óÍ­)Õ–3:~f„è6?òžŽÊ�•ë³Bœ|æÞ¤Ïñƒž·CA­bl#B@úÕ^ÆžqYùBr·ˆ`VWÞ§½lË+*ìñ\¯ÅðH%é·�k¬Ã„�Ú¤ÚÖ®?rh:täŠÑF³ŠÖV‘!.®ê„ªÿù·g”já5’óAU¡°0E° wᬛwK£´¼:¨¦Æ „ß _äó5@[{ª†*X“ ãAF,ÄÜ—÷yuB�HØÄÉùŸ”DXi�ëÝ5Çó gó+d…z"œ‡-œç7;¿H8‡ŽT àD<�P$¼éáwõÒ{›¤ÑâÇÜ– ÿQ ïªà°š\x‡ð=…ß®;ùäp Âs§ªäÎÍ áõøý÷�F6Q-‚þ-t¡46ÀÆ-·ÿ*6 šøorÊ)§„ÏDî(ŸY>Ì™µÝÈÏ*Ãt€±€|Ž/ü,¨(r?Y‹»)ÎA© t ” dã†ȅ瘊Áîí$Å+n9òP÷‰ ÑîOxÐsJ�ྥ‡ óƒ¼TnÞM.ž ¿K-WT,¼!€ia:Ihqá*Y±sF°ä®°¯z¨÷6(H£Å�öÂÂor[•Rý€ŠS»ÉsÊˤÁ†_ª|cb8÷Üs©á›`Q<ôabÿÑñÇï!·¥zÒþ6žò‹�œ ê#^°Yü ËVc1Å–íª#ªr“BØ ¢!Ã߸ÜP¨Š–BD<—EÚ•¡8rDG^Ôe„’øW8Žó ®ž =B_�V<¡ÆŸ‡{@xËîr-6£„/-O4×t�ÐzÂ�„!T@7U”šö õhUÓXڔЪ9VNȇ͘ÓVî?�4RèÌ›^uó‹x¼hêºA«ìy# ?j r@1Åî¸X­@XœKelˆˆ|×¹ª‚¤Èžƒ”²ˆÚãHe r ;çY{H…gz¥…IóU‡ÍÜÀ³±ˆów�>T !Ù¾¢‹0,L# íl$ļþyÆ?y¡"/´šF‹/Z }'*ÅZLÁR i¤‘Æ”�òÊê¾±Hºƒ ¥�g“)‹9*‚°š/¬!0^£œML*¨Ô a9ˆÌ�(*Ô—ÕVNrVk C�1H�yò¾“�• òg™“Ž9Å*Í Ôiqæ Ö/¶ÝUll-Æ W óO )B¾ËHˆ�!σLù{m»4ZôX]8XøZTdzç¢nÚ4 ïáóÝwß…Ç{,<ñÄtòL{XÒhÕ£²º–úo„±X�@šÛdÊÂŽÊ@Q`@`q…Dâë�í�¾i–ƒ(xÄÆ½8Ç K8*‰ã,à( ˆÇHÈ ê‰Ð¥p¸ÞòD¨!H�y:ñ,”÷ÌÇÔs2ßÝl|-6ÃB©mn½Ah7•$t—B—ï@B|&{VLBK¤ÑâÇâÂ~B¡’àƒ÷Þ{ïIpŠ”R]zj7lÒ¼Ž•hÍ@ƒº­·Þš<•³[¥�8�Tû–'>!$åÊä3ÄAcÃäC®ÈT”ƒ?"!i@2%…ªrÓÂqs8ÝlÁö¦vR)qX0 :˜Ê …ƒâ²:v>o6Ì¢ª0=@œgvrŒ´ À½–k¿ååeæš+º 7 í§‚„žUáÒ¯DnN�>Ç�®¡ùÞBi´ø1�°“ð¢ðå®»î:˜ �ÔDûâ‹/¦87„›ç×V*æ¹ÎZk…“Uuú•ø¹Q ëÖS…6kÒÌkqi´¦�¨�v-"uCB†úaÑÇý› ·eg5ýM-BC… ÃìоV ¢bž>gÎ1Cê²óó1H2\ÿàÛtM•Ù¸+„FD‹L! }§šq¿@ªÜ;sþÌ -�?Ö¶Úqß ßÈ©5V�Þš¨šðñLJáÇOֲݧOŸp¤ömCcº3ÎTQ mwµjË=v³\6K.É&Kú ÍœÝýi4Òø"}íã?CÆé;û!–Hùð7‹g>µ1p,&$™E¹#Žy5i Ë¡¢ ®ç<ˆ‚8æ F7JDÏãX¬¸<âÞ=(‘8Äȵ�!Öqª-Ĺ$ˆ‘cXÃWÛëÆú¹Us."¢¿mSëbSBB2 üÁ³!½ˆ(Qt(!Ú¤¤Ñ F;k Ýà ˜ö“r»á†Öxà�¨"ÈhŒÈè'È¿:,Ä·Ýzk8E<ï¿è"êÉQKÎkÊ U ›e9^dá… Ëµ»q©‹,‹5 9*b`ÁgF�°pÇuâ²6‹²˜fìÇq;·[²-ÀnµF‘ˉÂrî`ã8ÖlHèØKŸ÷ò=êÌ÷%ñ€,‡ÇÄ YBR^��Ïå­Ø·ÄwÀ÷SK~Ȉh¢™nŸ }°Ü®×þÁÜ!Ø8?¦Pß'nvH£UŒ­…ÏŒ„Æ ÅFY[‚"©£ÂaÆÂt� {^¨ pÆ©§† ‚ëûâ‹�£N,k?ý4Ë/ÏFØ)%¡Y–©ÒHcØè‰ì×ÁP@NÇ]c�¡«S®ín{êÓpÓã‡S¯íæá:’üá¶x?N>7dåî·¨Us€ð ÂnÞôŽðä„¥"âÔDሷ^–ªs5Ä= >ÕæÅ5 ïó<òC̉¼•$ÈG-¸ùeÕFB�fáþ]¸WX2Í#|¬Ï4ĈÚI,¶íU_µ®¶ÞiÌ!œf­ ¥Vñ9Û,×.þajjWM¾ •Á‰ÇNS͸¿:wvU«²6íŠÔfz#•¬¹á†’9!�ï„;ë¦×¼5‹¾-È®N "È"êô`!EQ ˜\‘`©‘ðç^yáÙ"ì÷! G~È‚rƒ —‘·9ã†W˜ÿÎg˜àó÷gp¾)<Âm–ã¼hN�Ž«%^ ›ANfÝ&\ó5AD ÏÕ›}ûá>a… Z^øTó)Õu�t¬Ô˜Ã÷zî‚4ZÕ Yx§õcÄÓ4 jUPsÄW\† ¡¯D:‡Š€ÝqÇð¥zAB ˆö% ÏQm{'U(** i¤ÑRGccu×bËR,,¸Øµ!Ba(¯vY°@›"�Ä|hþ½EH�p÷Gyy ·jˆ°÷€T $òFØÇ/¾ëmsféögæÊl’g½{œ0!"ændçסЏ'„ H‘ï€<÷‚„xå;‹Â� í¶¸Œê �ÉC“>µús_,»ó5µ1ø÷·Þå5�V5®>¶~7å‘ ‚"§Tˆ†„PEôÚXJ5ôÕ„~ Ëõ—[®ðöÛõ*?³³ÊuRëïúúú�F-u¼òÑïüÒw÷‹¶½:©þÍ‚KHŒ°a,lËXšqŽÙ"êçaLÎÞ xÄý¼‚‘áǸ'dÄ8eB(.#!·^Gʆû¹Ú²¹ ¬<,‡¢ãža×`˜ ìˆEÛÝtQy Br|oʃÄÔH/§cUêöZhMòÖf3:\(j·åå�|·QN pŸ¯[7 ¥ÊÚ[ � _[N¨:n=°Â +LxôÑG=·ŸZ ¬¸ì²aiY±×‘ n/¹äŽQËëÍT•{£ÕV çuTÕ½{H#�–:ú‹’põ¬�‹³7�‹” ¥çŠYÊb¡¶Pž+)^£=;Ü×I*œCx õCÈÝr(” ò07sä|„„r‰� “ª9Ô¹ € ‚lâ<€\=ôÇg†hxþ¹·¾áA|.ˆÐ�`›7ŸW";÷çÖ:|GaaS¡§îÑQ2È‚ƒhõÞW­Ÿ„ÒXTè`]?û %ÿ¨"UžHHŽn4¿£³çfjà¶ŒZÐÞaÑvíÂJK-P@·œyfèýùç!�4ZpŽšpüwÅÂâ n»üÞwÉ»à–cÁw»t´ÐC Ñbì-ü½¨Bá,q,_åjdò-ÞÆÛª%޹ �g ZhÕ`aA\l¨ æZIx]8»?÷AÉ@4®†È~´0ÄD ÅH.Šk\顾¢ýQÌÍ+EìwæM:œîÿ¦µ‚8Th'ôRkïZ¾Ož‡’´®Üã¯Ò�F«7,ì!Ü…çšÔ~ L•žûQ5D~¨[·n¡ƒÜo{)7tìÞ{‡«dÇ~å®»BÙ ASÕò€Ê „íØØŠó®ÿþ�ÝÍÐ�F/¼÷ *Æs-,Ò¨~•CN]?ø-|XÔ+<üò7áªûßÇMØ + ‘MÒ[Š•ÊÊËÔd�÷�$¼â5„m8õr:¨)«ÈÍ¿YÄ!LrWCYàˆËêq­—"¬ÆÜÌ ÇyNÌ̉ïƒïâžg¿@•1‡¼aEâÂÊ}R§—›ö9ýqŠŸ¾&tÎ>™7@Ìl¾¥6@Mªk;$”ÆŠBGáma´)¢aíÛ·ÿꡇ*=zt 6¶m,ÛÔ�$â™’M©œáŒ92Pòç / ä›(¤Jklº‘‚UW]5ÐñµS§Ná×_M„”Ʋ0)yÔ@fó<¿Ð qᘃ€n|ì#Ì Ø´Y¬ �y&¾ádäpEÃB PF,Ø™N9ž�ª!o8¥\äan9ÏËPb‡EüŠûÞƒ¨2ï E>/ÏGÑ@¶œåŸx®wmµÐ¢¿Y~Ý3¼ûew6²¢v²ž‘{ $*m÷“i)Óûï ]!! ³¹ ùŽQ�|‡?µ-J9¢Ý…ÎB¡ØlÜ¿¶k×îíÂÂÂñ•••aJ[ûõëh~šŒ ë­·^Xb‰%‚-è^aamh¥ÄEÇã.¤ó¨‹ëjÊ1©¢CÐsCEEEH#�™9Þüì/Á¤ )$ÂB�rá=ç°ø£2XPY4Éð`²P£.²û|ezXœÝ ¹˜¢Š‰Ì7ÇÆ{� $ˆ9P:u³çA–/¾÷+äk Ô—«eG�RkON޹A²ÜâDáq?•$PC^SEßRf¾FB^Ý�×u¼­Ás�LOÝ»N×C>�J‹ï”çý¡÷Y›Òh#c}á&á#¡~¶±õW¡P­«¿¢%8$Ã@±©¢xðÁ•¹;vìHý8Š›6­³Î:t…X21ŸòJ�� 9‘S�Z^×é8¦P¥œÔhF­=ñÄÛo¾9Ӛꥑö�~ý+,öY!+w²™‚qS!&öÕ@¶P£ˆ .w”e”Ή;¯r Äûûd‘‹³õr÷χ �y>÷t#ªƒœÍ]�?'Ì–¥R<—Äçáùä‘È¡jlo÷õp çAФòà‹_‡_ºE ñ�K‹?/×CÜÞ…f•¸ó¤ÄPí'­ï­†knxô£©Aˆ(0劺·-JcIá$á=¡·P/#£þBwi©¥–ê¥ÆxÅ"šJCmØm·ÝrÊ5n·ÝvõR0eÚc4JÊf¬*v×,¶Øbõ·¹‘ ¯ŽgÙýÇù•Úë ]ÓGϪØk¯½(DÒÚŒ/�Ô’½Û‡¿“¯`QôÅÙ,Ù¾·ÇÈ"!ñÑ'ˆD9 쬉÷2ã¹!”¿üY¸ãëÜæUÀFAAvµÛ0 °¸CЍ·w³oˆ�÷6‚ÊWl„¢ÃzÍ3ø,|(Wj�4sƒ`ùû‚Ûß =ú�ƒGLàù܃÷ l7Æ ìgî=¯.!7ÿf—L\cß›Pf2æ‹c(¡‚4ÚÔÀ»¿�ðŒð D`$Ñ[·íß ö^µ&x) æQÃ}ìú*#žž rçUeÂD{„Bvã×^{í°Í6Û„³Î:+üøã�Ó]Ÿ.�4º÷EEãVd^íWˆHŒúÚ±Šˆ(r‘2ª5)ƒ|ŒÈŠ,Ä×Õ6ÇÞ"œ.!ì"la�_¯^‡˜ìyQi¡æ¡\RNù£€Ùa÷Ýw´Ž(..i¤15cBi9rOøb³ðš/¤ü‚'ÔE®„ã1IL?<7ƒzp‰äÙ×ù° û&Zˆò©× /Ã1W+&J¢3ónN•¡ö82ã³¢JlïŽç…tÌíã�íêšú0f|9¤ôO­;7SĦ惺$ä‘ÓBÑ­ºçµ¡Ý—BB�ä�›w“‹'Z;ˆ5 Òh³cNaS«º=Ô**Œ±�Ûh…×ÄSÎq#žBá^#³m„å……y…¹…¹lÒl†Ù�[TØL8Åö.½o ª—¡·ÜuV^yå¦u×]7`ó>è ƒB—.]’y!�)õIRßÃl 4–e³ö0Û $àök‘gfœC˜-%õ„›äáJ�ܕٵYôÝx‘� á=È�ï¢áüm!9žÇ¹Nr<·¾¾‘ÆØÕÝÔ ó !Ôsõï’ð#ŸÄû�§ØnÖw”•)· ÜÿODMÀH¨³°`AiÄÃTÌrBûÿ‚2ÛBxØ\zã…j ÕåÔ~¢AN¼Èhûí·gŸ}vèÝ»w2/¤‘9&–U±0‹ÍH~±Fâ•EŸ_ùæLƒâ3GþެŽIçÊBîí «Wç.6ÛÓòîBõ�K‚88—ê‹6vž·‡àÆ—TêG_#y)ˆÇkéA^Ì‹ïŽ9fT„ˆÿ ÅDT*Ü9«öJ#9øv2ß÷fímƉ RGä¤+ÑÙܪú}ÓM7…‰'Ææ…4Ò ‘NnÂë�Y…l¯}ƯyÛį{ÂJ¨�,°�ÔŒTEÜ ’#7Dþ)ë·A3ÏÈ­æZ£¦v’W* 4Ç|¹&_nŠ÷QOqˆÒª*à"DqqÌ7Ò¢¾ú-†„0)@‚qø�yñ]Æ5ö²Ÿ­ÏEDT/Ü2+÷J# rJ'šááw³ˆWFöîbª6Ø`òEToHû‹ÒpW¹ L[À³r0¤Ä‚ïÄÄ� b±†$2TËô€ç¡¸ÈÍx‡)Ì)1/>SüY˜û�p£a€X›mªÇçDí1‡˜„PG�û’xN¼éë_Ž£¶×BfSý™—Øþ*‘Ï]€QáIa®‚4Ò˜ÅÇ æ¨ûCíC5rÑ•ŠŒjW\qÅð?jMÞ½{w¯w—FÛã&Tð Þõ,ÚÓh6˜‘.9W#(!Ì(—üá³ìªÛ„Á˜SLNج!5Jøð™³CˆN¼˜#¼Ñ�m¬ÅH)ììþ¤Œ™�0Æ„G»¹³/Ï&Ý Rµï_÷šgcÈǰÁùaö¥¶áÿ壅4Ò˜åDzÂùfíî… ŠöUÚ¿›DHMk­µV¸òÊ+!º4Úä lDè UC˜ˆÅ”ð‹/“'báüMˆŒÞCn3 ƒ‹ýŒ 5H5„R™\hŒ÷ùì¼ú= /Êí°Ù•q0OÔ ÇȱQ¹�G¦ ^ãg‘PÜmÖ‰hŽÕ�¶7qO!�4ZÄÞ¦-„¬uùਠÐDHP2ˆ2B;í´Sèܹóÿ²w@N�Q¦î¥^ îî.ÔÝÝKÝ)5ƒº›Qw£în†»»/‹-º‚¬[nï7sçŸ××$$›}t“ýÏÌAâ›MÞy×Îõ)ºæ¢`Ü<‹‘4Vp²Ÿ1çH@ÔÄ�%µ!+°GM¢Ò`�"|ˆ-?·A×cJÏÁÏM]Œ"«àêpsV@ìxÝDSÉÌ[y¾D]�&D."’Ë./ö=>ÅêÁMë)o´•”Ó¢°Ëƒ60Ô·nݺþ¼óÎckl3i\ð(˜»�y7œjäàL£bƒø@Ü©)èC:̨‹p?œÙD" YIÇ!²á´c0êIHKëQ_âýJ5-IäÃ{Æ}~‘PÂú¯•ès…�o�åw¾Q–Yµ5"„ïd©ÍúF�¬íãÇÛ‡öå„(ŽbTÕªU«¹íÛ·/QˆGnã§c¨µ¸qáuÛ¤š$¢¢#þÏ>i&«ud‰FhÏfë*µ�`$¿Sm¬@Pâ¬&kùFxt"(Œ”'ÑNÒ&ÞëíŽÕF…-Ï”eÖÜ ‚¸îטãÊJ�¬èžÛ^y�µs÷4 ZÄ'Ä:Z»uðu@çÎ�û²€Ï#'×wãÀAщ�¹Y'$Ýb%d-‰bL ˆ„¨ñ„g“Ri€ ÚI'=Èóðþ !bˆ]²î9çN´Úeõõ�á¾Ï²j’o£ôðÈ ¬¦ÜMÙÑ&XÓB,«”yZ3ú\7ÇN÷)ºœPui5ÎèiB»@®C¤8pru]]Ñ‹bSøR\´Vó3Ç­?Ytò“×êDhÂÔy2vìXÁ9µÕV]#:”.jbLjî åVJ�¬pmiC®O){( ¬�»FYHÕñïbå�k®¹¦oQQQ½d¯fM£&Íf⟨ÀµsÀ,™suºçHa‘¾âöÖ$u3‚AÒVV¨o´Öo„Íj7MšüÜ4RL™Y$ ²²Rzè!aæÏ„ˆMÌøX~¥ôÞrYçƒ×Jy–¥è†šÓwaœè¨L9]ëEt«ë,ßE—ýø½ÏxAq]ZD:æšM‡—[[ñ=3²Ðè£s®&mFMÊyÄY +çI4HÊûŸ_€‰"ÜòÙ)f"³ôú'ÙZ#òð‘ÑÖÊk¬‹nb‚Æ…j»|‚Î}«)º|Õd/ž~·+Õ®ÖœQAh°ñA„hÏ¶Õ D$Üv©Eˆ BDJ*Ø6Îk±”`N“t¡m�u‘PPˆî½÷^iÓ¦�ØvæZ¢w³¹kÎÛ±î«|Àf‹æ(ëâ,Ó+±{ÿSN9åÓâb¹÷È6«Ö6 *Aw7HÉ��ƒ=]q¶<¶_h©§°x>Ò„¤y]¼DÉ6®f+Ø”DˆHˆ™¤i³Š% :Xµ“UXl¢™Ê—l Œ‡GÖ¶tŸª|Ëv!U&Ø;_™¯­¢öÙg“}Š.{°pqs2‹›S1"€ŽsIàv�Ë3ê:)=·¡{  ֪̊'ÛÓl©l¤uîÛÌkMŸ]"ñ0cÆ aeKË–-ƒB”¯¼/ÛZ=<6U^§ü9NzŽÛ峕£Û¶m;J½è*²¢‹Î7%àÇÆÁ=®[€ FxÂ?Y*ŽB?·OjéÃA•ˆÆ+i4`Q-ÍÔ§ˆÌ8 ›€ewd�ˆ)‰6'üüs I"Œ1BŽ=öX:åÄ„¨Z9Ú64{xdýFÙ½•Ï*G‡Ö˜—›Ij¡EEÓt¶hÜUW]5nþüù5âÑdñgß x±% ¢#”ˆ€5*@gÔ‰Ø Üߢ¦°X%üÚ¨ï�VK6ôÊÁšÛ“d¸Óm%¥µÚ^wVÒ\â pRA.,)Mš^ýñÇeï½÷:æLˆÊ”½m±¦‡GÖcuå™Öšg¢3Åö§kŠ®÷믿ž_UU%M?÷CÛuÜ4)8j0ˆ'Ô‘¨ñ7‚@}Æ¢È6Y$äêMFD‰ƒl2A¤+Žæwó¬ËFRK;�ˆP³,/ØžüòË/‹nV¦[NìD±DùE.µn{ø.º6¶Z¼§ÙÿŒµ†âPݨ‘Úa‡F2¤¦É¥è¼]«¼ãΤ@Ä…¹ Ú²9ècÝc46¥Å›S<¥ë’·]‡…Zë7B˜èàíêCÖHÑlHäY¼°\–„ÒÒRéСƒè"K±h¨^9Wù˜�Hzxä–3‡î.Ê!�h(b½r±Nwç_rÉ%eÓ¦M�&¯ … ÂÁE�mjˆtbADBzŒ ¥Öº�Ö";ž“úŽ=.¢ˆð!T<^V×l –‘LFÂÉûQ^‘ZãOAA®ø˜œ†ɵÖm�V¶·è/å¢ÅÊê8^t%[n¹åÜ|0¦® âñÿbÜ”¹²ßùÏ»X0Gz qÙ팧˜#"-‡£6¢äW/»ç¢$vå`Ûà5bCäµÓ)Oð˜8\#Já·ã`îjQMµë�×Jz’èÐZÛaF)D"Çt² ÇW"BÆÛ¸|m®µn{xl¬û‡bÚ°0ɲ¯Øj�œÅªÊÓ-üŸæB´T›"e£�6’Ã;L:uê$ãÇ�—ºº:ñˆŸý:7‚DLR_4&Ð!GÓ@æõŸèÅ(�d)M„ ÍщˆÍR&èß¿¿ì»ï¾u¢:ó~­¼ÛÒ=uì–úúzñȼ�lVu5 Û”µ´ù#þÎ ‚›õ`%M!µµ™g0~øá‡eÛm·%’VX�狼u¥‡G³é¤ÛHy¿²·¥éJ¬“®ñ GE|aˆŒÔ}AN<ñDÑY#5j”Ðýã‘Þø¢�ín»ªÀ=›ˆm~Ô}ì}j¬)«®¼òJ¡!h¥•Vâ;ÆP+\Û÷Òãٵu? üÛ\æšéb,!Dpƒ 6¾@ä·‰ŽÞxã ™9s¦¯e�’£íšáÔæYBEwϨEhÃî�¹E�Û5:eÊ9餓Ģ!NðêT�úé÷®³r%¥‡G³£mÍ�îO£e]0M·ÆkÔkgO½¶šÆL�„èˆõÆtþÜ|óÍòÕW_ùt]zPñ®—Ž�b_Í’°iA^ÊÑ’¡V»ž*…E%ÒØøë¯¿dÿý÷NêhÚi§�dýCy¡ÒãYbYå6Ö­ó·¥éªB†¨ ”e*JÕÊz†ïH)¨@ _¤=öØCÎ8ã 騱£ð%+++K!�áñÅïÃð�C`œÍL‚¹þ·)Ó¼Ðh†¢ž&@ÆåVj)]ºt‘ÆŽÛÏ<óŒì¸ãŽ's4,0Nñ]swÜöð 2ÚDy—9u¹£ädþ!ÆR/"$öªœwÞyBýhРAìã÷‚”‹Ë*åœ[ß§›n9fƒ—°A›Çq©9š.n“Í«XÁωø2jpà~´VÛð*äß)î Ð&ÇI‹e–ÒÏQÇË/¿\ Í7ß\>øàJ;|Aém}<¼Y7Ý &F…ÊšTÄȆ`ëµ–kݺµìµ×^‚ 1;fÌßЧ}÷†G¿ÆUËÄȹXÛvÓðpˆK¬˜k:…l"Bc‹ûøY‚ŽÞÌEqYÚ«Äiç~ ˜2ßc>{4~¤&Ô�q™–Ûà€-¤£óòò$ ôë×OØÈÊ8ÄᇎÅÏL¢½”†–Ê“”?( ”eq¢£ªD“¥í¨% sçœsŽtîÜYtµ„54xüÒs¬Û\ÊA4žQhø`{ÐE/@<Ó²qå‚ !ÄñqB„A«5k…ˆ(CThkwâÌeÜÂo¢Ö†9£Û ›œ±å¡¯’+¬.ˆ¼ôÒK%*<ñ„ æQG!Ddê5£0NŸó­ÿºm{xx¬¬ÜSù²rZHtJ” SŒ”j•¥Úþ]¢©Žâ{ï½w‘ZÕÒÐМ Mqȶ a†E†V>ìÿÁ¤“ƒtV¦á‚©F«‰ñ üÍn%·ÒÁáoeî|_‚à8Q`¬Šøðþ¤:@ƒˆÐr[Ÿ+š p"D‡hTÈÏÏNÌØA´ñÆ‹®èÇï±—r?¥‡‡G‚&†�”Ï*Ç™Éle¡scH�Ì'UhƒÃb]3QÙ­[·ºfX?Â$Óµjã6#pàee�ùÉe…C�9B$µûA8ˆ~0i%Š9øâÝ*‹ˆ’®‹°H(©¥*BõËï|cݲëîfäH '&�&ºLÙA„£‚mL~{ÉN «Ù–ח̹{ªEE®Í;=« M»úê«©ªª’愊Êv‘š žñ‡£D‰ú;†8H»tTS%i°pD&Bj+Ìb~>ùñó!"i§øŒÜ?eR.¿ó Z,¿š„EèÝwß/ÑŸF¢!š ÇT¸Ôj±{+=<§ôððH«ØÌQ;å‡ÊQÊ‹�J¦TºëXFû*gˆl©l]u¤!Y ˜B�\ÁAœC¤«ö=÷9· ¢‘Èa‹+h\n„¨q›T£ ReoåÉJÒÍ¿(cAZyå•g±DMCãÓˆ�ž�Ñl[R¹ŽÒÃã�µ£–Ê�”WYÇÏ÷f º0‰9Û æŽ(ÖbtË-·HAA�ä: KJ©÷9¸N8ê!&H´ Qr­Ù9F¢ àVWæ|x‚é6þŸ¨Æ”îðnµ2Oy‘}nWPþg!dË£Byy¹œ{î¹Ô„˜âsOÇé0å¥J� ÅhÛo´»ò:å»7狼%Xº·á† îÃ|I{õê•󃯽‡N¡>ÂY¿‹€¬Œ:);Rq\U´ÁÁÜ ‘R³áoža°FƒL—Ò…Ÿƒô“�\ol¬ݘr¾òU‹€h¯œú\V°k‹m©Q¶ksÒ… # –Ax»ñ‡W=<|ýh%³¯ï˜?ªŽ#B."âK¹å–[Ê 'œ o½õgŽ9½æá•O{Qÿ ûÇq­ËDI"€ŒHš� Cˆð±`Ò•Çu´@�…_ ¯5|tbJ+5³ADt4%0#´Ý‰�E‡×A$¥-Q©²‡2¼j›ÿÿ^wB÷Úĉ%*tíÚUöÜsO>ë¬ç³Ïómém�ˆ°ºòX‹ŽÆ)Kž0©ñåäL‘Á>ÎKKK%GÁ6OV{svM‹HˆFÂ…{.Gœ2NÑYË4D$‚.´P‰Q—BDâ:¥„…$Ü¡ÆkçrÈâ1Ý\�¥#£d­r¢òÜññpxö�¨üÕW_�.[X( ƶjÕŠ kJí$íz¥‡‡GÄXÑòßÝ5ò) ‹[íšþÍ*rêDÏ?ÿ|N7,äáŠ@êʉ�µmÇ�.ˆNö9ç9扺˸/QŠëDC Œ<6‘Žó²C|˜á±tX2ÃTĆÛ›‚"Ú°Ã.K£í<¦œ§|]™h½ööʼPÃŒ�’cA]ÀA¿Eºä8ábH¶ÜWŸ¶”¶‡‡ÇRpeè¢_ÀEñ¢!Á=Gt½óÎ;9½>¢Ç É4#¸™›ó6ži8)$ÒD„¸íΧ>É<s9Ü/L®Gˆx„!LeÈ žvˆ¡ ™r}˜¼wƒ`„Åí£ 2åßÊVÉ>‹ÖHSLS¯a‡VTøâ‹/¤mÛ¶4(pÒ…_ãë2ÝIéáá1–W¾«í© %%»ú!úüóÏszçÐûß ä`¾Äù"—®}“î9ìä—ä6Íãrà§Ññ€D>DDDJ¡¨'ùóÛÀi˜ ž:Q³”›#)>Oüó¢À�Ynœò˜KƽÊÒ`4D}’ÝYQ³ÔSN9E0ÿe‡—>ç \=Méáá1–S¾ÇÄx8Ò9 ÒpL”CiÔ„ ¾ôìÙSrÕ5µÒþ±¯v„Ù� é;YIs5ÈØq²Îµ”F*R|–Îs‚FMh®ãvˆé:ÈÿIÍñQn�� •÷*S±ÄÙWYZY"]tQ¤­Ú^x!>DBbóu£”·.�.9 }­k�Ùþ}¡áÀ”SO=UèŒÛ}÷ÝiÙ& rE!—ëY‘Ë&§çÞöA\!â@nnÔø:ÓþÏ�Ò0Ó¹”T®|WIª-¬ªØa‡$*÷RË:t�­·ÞZXÿméÀ<åÛ6îáá±ÛÂúå+·º�àšÀ|PŸ>}dΜ9Ò®];Ùm·Ý!B¤¸ÝrË-'L™³$L¹%WQ´  ϸ¸V!cƒ;äˆBh~@ÔˆJx«çpä¹¢³ Š^€¾QnÜ"=|¬¬ ŠíÓ³fÍ’¨ÀP,BGÄoÏ;Ǻä¶SzxxDˆÍ”T„ª¬ NØAtå•Wʼyó„µÈãÆ�D‰ÕÈ ø¢ZÔ$œ=¶oß>§ý抖³‰±ˆÛ`µ£tnZ`'©=ܬƒÄåšfºæ¨;Q7 ­UhJt5 å¯J:ÞÒÅ¥Ê 'BvrôÃ?HTèÞ½»p¢Åç?°¯«‡òD¥‡‡G„8R9Z¥’/;³ÕV[!B,Ásm¬¿ýö›°‰’³E,Nt „�–ãöˆS§N�rºu»¬¢J®{äKê>Á¨ÄE1™¼‰‚èTClÚ¶{I¨1Ñ4@þuðâŽÉ÷}&7?ù­<ñö_rjûwxî¨=ãÒ»˜Yò̱N¸ƒ[4 ;ç…ìs)7ÜpƒD¢yFh‚à9�ãmÓ±‡‡G„¸R9YÓkVïjBg�u–üþûïήë”û￟Õሩ¹Íí²Ë.òì³ÏrûœnVøöï‘´Y»ÁNÉ,m2v°%qΡ�Ökþ6—Š™$žjQµVóÚyÍÔ²‚V>¶âQä9Èï1Z ¤XJdD£�-Á#EGê.ãm¨áz� žøFæ/–{î¹§&�MW^®Ì}••!Š4Lj�¥ãÜzqå åAJ�ˆ°‡¥&*Kƒ©8Î ±å)..ŽÛÒºxñbyæ™g°ï ׇ17C´Á°½’ƒVq"¡HÓqDì–Ž‹ •£•‡+=<<"ªvÀ¦,1Äc‹-¶`ë$ö= g+HÍ]uÕUD<&<®.„�¹ˆˆ¢2v@7Ýt“pŸæˆ©ÅòÈ`ÏCê,³×ø³@i7�®ã~0,B¼6êO¤øp3!!ªµuéºi¯¡ãìsŒ4ÀôîÝ[¢Â€8A"ª‹™Ž5¾¤ôððˆÛ*QNÔ³À"uÑ®!õÁ…:¦¥Ã‡—T1~üx9í´ÓX ájC<âƒm¹å–Âî–Ã;LŽ9æÁ¢¿¨¨H<Ò‹šæ—ʨI³…´ÙÏ=ÇÈo½Ç ü½Ïx�ÝN–Ñ“g³!6Rç l�.»ì2~Çñ¢¡BˆNÇÐtg` �-ùùùXW�ñ™5gÑP¥‡‡G„XÕRSô R¤OUtÇY1X‹ü1-ã3fíÝœ!õl·ÝvÂð*"ÄÚvyä‘ÁÂâ'‹�<þþûo~¯D¼a!ª¶šÊæi!ꓸºG…Ÿ~úI6Ûl3†´y¾{ •')=<<"Ä2Ê­[¨u¡rK­¸u߯½öšLŸ>]Ò ¦Ð�>úh±\»±‚�¬Ô… "÷Æodñ0«GEEiXNZâEC3”(—Kñ³x©²$ôu×\s�ð<�z¡Œ'˜¹9¡ã”ã%;ë+Ô3ÁÒàŠ:ÚØ´Jº"]`×óñÇË\àP�ǥЌ8µmÛVú÷®Û¤ûXzHª5ž•+¿²ˆ;)¬¥û1îzŒêŽ;F¹½—öoDˆ¯˜Ep…VÓ:Béáá1:Y1¸P¢ÒÄ"Ñ. ª_äååÉ}÷Ý'ûí·Ÿ�wçËNªîŒ3Î�=zXªÏ#›ÁïñÀ$Ú ‹P½rXŠö7›•‡Û³õ1¿ôÒK‘~Nû‡½³²ª ðÝ�ÝÝÝ4HccawwŒ*"&(vw`ƒ…´XtwÇ.¹]÷÷{fþÙ9söœ»‹lÞû>3¯ôîÝ»Çó�ÿ‹÷ëÖÍÑüàkBù>ýj:Å$„¨bZÛbCÎØ4°Gh­:ÙFøÐ�9’SK)¸ÔKÉ‘n�: ešž©€ƒB ?#”ÊZ–÷ëׯ2NË%.†|бڞëÝ[e˜˜Ž6 !ª˜£L›(ˆh(hÛ¶­ûùçŸã:­„Rr´ó“¾� B…Þj[Sšî3Í ÿ[›)Ëa½|%Pàb¸çž{×¹í¹¦¥¦Ÿ}÷¨¢ŠÙÙôkLÂ#ŒN6‚P\JD hd‰ Bhj9f ûøT܊п[ñè£�&.\è*� ÝœA ¢ï×Ï´›IQÅliú&¸Í2´1•|?ë•ãNBBÐAIý0.\zĤ丽óýZ‘ÜpðúòË/+«)aEL ópÁ�ÛlÓb¿yx“¢ŠYÏôžiQÔiˆ'D†ï¾ûn!‘,%‡íøQA¨ÀwÉmc ³µ©·i–©8ôoú‘Š«$–ŘðrÒÎ9eù9¡Ï|k¹¢xÒwÈ­ !ži§¦ðŒY©qÜqÇqÎÚh´F ÓÑ4Ì´2Âq¡³ue&\å�ç{ÈÎ+^³Ý«M |zPQM\oës÷Yá@´Í6Û8œ²ûôé×¥${†hÃ� B¦§C)¹ÝL_˜fš B­Ý˜NwU ›¼æ`ZåW9¼jBT�M£¼iãl_JL;ì0ÇÒBDAu!NÏA¨Ð4ØtHÀ1»‡¿îVEì$êjNºÊ!ábxã�7°• ¡•¾K¯›IQMìeúË?‘.�JËaRyá…:¼á„ˆ« µnÝ:ÎÐ-0}jÚÜI3- ¹f£‰¯¿þzwûxEU„n½õÖð|Ó Ÿ¸Ý$„¨&¶ ¬fš2Âë›¶lÙÒ�=Ú Ç�7ÞèvÙe—¸ TàkCC|ºmA( ‡òmüB¶·V5ÙÙÙ˜èâ”|½ËL#«ß²G­úâOŽB9Á „%Ë)§œâX…,D½zõ*õ‘KÒ®=×_c…a›³w*ž2eŠ«fÏžÍn+:ú‚¯u�_o²‹IQ�¼oÊõùø ÿcùº�cц�qüñÇ´ôSB\?QÊ {Äùåu æÑª«ùå§Ÿ~¢ó3€J|*î-ïâ „¨Fòó^¹áºÐn»íæ˜."W81cƒC 7hâTb)¼F¥¤áª‹®]»::?}*ñ�q´¯W !ª™æ�Æ„¹Q­ÚXø´k×NC«"©Y-µÃ�vÚ‰%q�SF¬J¬;­“ÜU«V¹ê"''ÇáäÎkôÆ¥Ëü5?ÔÔÔ$„¨fv7�÷AhŽ? ƒ\ËÙ'uÔâºë®ãÔ.ø‡Åu•°S1;§ª¹ë’ÏÇbEÒÌ(áGÆš¾¨9»!´ê{°>KâêBp€{æ™gœq¼ùæ› €–!€vÜqGÇÉzÔ¨Q5²–œ.>]“Š›â¯ÿý@­¢V}¿mZìP¦©8œ’c°ï’K.q¤3„ˆ‚ rà�Ò˜„HqµhÑ 4¨&R†ì nü-öË÷úûãBˆâª@*®0ÎÐ ŸñãÇ;!¢` ïqÇ„ø9ëÝO<ñD:-kÄŠ…ŠÍš5 :g™†{7ùÃMBˆâ@ÓØ°™$ n[%—þþûï;!âhÒ¤Id:Žë‡zÎÕýû÷w5sH¡ÝGE¦a>°‘IQClã ³«ã‚b¶Ç䢢"'D;v,Mw?Ö«WÏ5lØ�D5µ€�òpdz æƒ�ª =`Zœ,ÙP¡cXFF†"Æ9¹È Dì˜cŽq“&Mª»à&Õ€¹ê÷¦†5„„ ½™iqT ¾g¯½örÍ›7wC‡uBDñï¿ÿ’v‹ Bˆî¹�ÖˆÓ75M?”ðÞÞXu “¢†ÙÇô‘iyT"ÅÂÍ…tJÏž=# ËBdee¹C9¤LB~ðÙ™S¶«n¾øâ‹Ro;‚O ôi“¢°©©«·/)!&Ì™ï¨_¿¾»óÎ;Uqi/v qâˆMÉqýT3|Î`=(Û4Ýûŵ7 !j g›~Z÷º›h±e%2óBª ‰X:tè@‡\dâ:jÛ¶muž¤¹VŽå4\Û0ÁÔß;†!j û›>1Í2•D�†öÜsOǰ!ë‘…ˆ¢G�±rˆY¢ÂÂBW]àÐÍ|�Ñ_Ï MãLo×.—!Ä–�ÕËùQ]r¤SXùýî»ï:!bÖ:�úŠ­ Ñœ°råÊjsIèÔ©“ÃvÊ;$$L3|Î=&!D-kÕ¾Ê4<Ùà*k‘™b]ˆ¸bhbƒjÞ¼y® Ø‘>Æ/®8Ж=ÉûÅkBÔ2šš$›"­qá…FZð AÓÊ 'œ�¬C®ÚÖÅ6 —®ÙÕþ5dùT\ÓÆ&!D-ãHïž0;ª.„X‹ÌÿØØâ«U[DuÈ�wÞy¥ë³Q¸Ý¿oß¾Õ‘Š£>…UOv Ùf©OÅõ4 !j!»š^ñ‹î âNC¬r~úé§#7b ѽ{wY"›øý·ß~»Zf–®¾úê„CmrLžéfˆé“¢–6'tõ3¹qAhë­·v¸'üþûï: ‰¨½=¤Ýbg…h¨j~ûí7VKL÷�'×kªékÓ.&!D-mN¸Ëo[ÍŠ B,º£ø|ÓM79,ü…’™™ÉpslºòÊ+yx©Ò”`·nÝh®ùÛ§–ó¼È?þ¤¿žIQK¹ÔçÍW˜q�hóÍ7/= jNà„ð!lݺu•¡Å‹c4È¥Îñ^q‹}ÓÍÕ&!D-æ,Ÿ7ÏH„Èí3د_?'D¨)€¡æ8×ìŸ8­T¥W©·?L}}3ÃÿØÇ´§IQ‹9ÖÔ×w%kÕfpõã�?޼¡5'pê !R¹Ç<® Uu cƒ*+æùSÐ? ú§éy­m¢ö³‡éCÓ°s æö÷Ûo?n6ê’a8!ÓŽiÝÃÃKNNŽ« ÆŒ“íWv/ñ×ð¯ŸMMBˆZÎÖ¦ç|A7;.1¶U&ÒÉÁ dÁ‚x tÊhÿý÷gعJÒ€?üðïv�þkÊð�h‚w‡ÿÄ?` !j9š3MñÝDL ÂűchüøñNˆp‡ZÓ¦MË ­ò#Á‰ºÊ&;;›Ó9©äé>-õAh¸©»i“¢–³¾éŸC_–$1/D~_u! -ü¬Ô¡�wÞÙ±í4 ÿë˜4xð`>ÇPÓ„ù ôƒé|“¢Žp�é'Ó"SI\ÚtÓM­¸7ÜpƒËÍÍuB¹÷Þ{™*„ø½©S§VöÉ‹TÜò@7šï]áßSWœu‹CýºïYaûžð²»]wÝÕ1÷1cÆ 'D�.]º�²-„pÒ5jT¥× Ž:ê(N@ù^¦i¾U»«?á !êÛ›Þ0�Š²ï »"ŸtÒIîÅ_tByüñÇ]½zõÊ¡m·ÝÖ 8ÐU&Ÿ}öYÂZÂWúk³8Ð÷µ©­IQ‡XÏÔÍ­zühѪM·Óõ×_‹�÷ÜsO™š"0½öÚk•:tÅWNíE¾9áoÓ‹¦�MBˆ:FC?´º(. Í-[¶t'NtBœsÎ98$” B,F|衇\e1nÜ8wÐA« L“}]ó¡ºë'„µ_ò­Ú…ÉC‰,2{ýõ×�@÷›_«]ªàõrÍ5×TÚlPÏž=éÔ ¡,Óß�ÐÔ$„¨£\bQ^J{Üî»ï>§Vm½zõŠ\ç€èªd;oe͵mÛ‹ bÿñKü�ê_¦^¦mMBˆ:ÊÎÞÂgN\«v°ã‰.¹E‹9!SÒ³¤â¢‚¾ƒ\+•Á AƒX²ÈÇçc£|ßš=ÈtŸ_O"„¨Ãû…n5 ¯v WmWµÚAp °o*l×89'pÚXÛu4ÂÜvÛmn§�vr�kq•¯õ1�iBÔqöô¾[3ƒ‹î™.¹—_~Ù8‘¾»„®»î:öMÅ!®•,³gÏ^ëÙ  04¼ç™Fù®¸z&!D ´k?à‡þ–&›b0ñÚk¯uÓ¦Ms"=ÓpC† qÇw©¸Ø „Ì5�í§kõ¹Þÿ};ø\Áëpª-¸]©8!R‡ƒM_z…åqAh›m¶)>ï¼ó¸©A! Kq͸ùæ›Ý;ìéžž/cÈùÛo¿uÿÖ‡tèÐÁÙ5ü¸Å¦±~@õ“"…œµ»›Fz;”¬ˆFºžrqNþàƒ©‘>h•fÛ.õÁò‚¿¦U›Î¶aƹ5Ûlz�™¡ù ßMoù!D q’ékßy”å)g¶ý96/T‚{€TJ#&MšÄn© W¢MÃRº'ÙªÙUyå•Wbhº&§.|éø·áà–HÅi@Uˆc3¿ìn‚By)¹b,<ùä“�諯¾òËËD*³zõjÇ)èè£�fãniÂáôÓOwŒ»îºËyä‘%V¿) !Z褻ûî»+|­ð÷êׯÏç ¡Þåãx“"9Öô­i±)'®6Ä0"�r­Zµâæ¤F…¦°°Ð}ýõ×®qãÆ¥'„öØcÇ)ç‡~p§ã„5-dÙŸ×}³øŽf†wß}×U„¾}ûÒ–•æ[dzÅ´•I‘¢sCWû¤¬dëtGQ4>æ˜cišÏ?ÿÜ­\¹Ò‰Ô�9�¡C‡: Á-ª|ÿ=ôPwË-·8RuV�5.dRS ";Aã¢@ª­¼ ‡åu§pBÓL} S‘Âi¹/Ê[ý�)vQ°¾óÎ;ƒŒÌ’ˆºß ‡aí<À�2MÁS�Ã�ï9‹íhZÈËËK´iÓ&ÓþNV8-‡ÿà„ \2øœgœq?„ }SÂjÍ"õikšèHJˆËZòÔû않[iꥮ8!Òƒ�LŸšrã:å¢ZtI×0C‰ˆb¶¨»ddd8¾�|0Že¾×»í¶›kÞ¼¹ëÑ£‡ ³lÙ2wÚi§•x¿·RÙƒJO¹¸®ÊÌÌLêŒQ]qÅ>E|Žic“" 8Ý4Ì47:5ˆu¢¿ÿþÛ‰ºËܹsÝM7Ýävß}÷2ß_R°Ç{¬cè—_~‰¬ëÐ=Éß \+<¤à€ë?øÑGqí„»âŠMËLߘöO¯TœjR¸Áô½iæš!žd?ùä'ê,t<Ò 鎰ÝvÛ9ºåî¿ÿ~êA‘–;|ÿiFÿ[šYn¼ñÆ2[z9]vÙe¬�JÃÍöÖR›˜„iÄN¦Î¾ \TÑ@DgÓÃ?ìDÝ…áÒ .¸ 4( „ƒÁa‡æðä´+?˜Š|8õN3eN¹4+ôéÓ§ÜVkÖÛºâtÅúo_7äGRü~øA§Ä´ÐÔÛ´…I‘†llºÞ4­¢ ˆ•¨»óA¬r?õÔS]peÃf›mF$i8þN¹.Ø?þø#'©ÈÓë8nðqÞ|óMÚ½kæ LM¤w-H±¿ß94Û‰óË DX¼p#uo]§ lv9ä†RK.ì÷áÏ+h¿Ãl#²6tî¹ç²î�>~Ôfß<ïృI‘æ\lúÙ§GV–„肪ƒëDNNŽëÚµ+©8N1Á4ó;´msz©ð‰Š¿ÏLQøú 0íµ×^î”SNq¤çb£—™Ô)Hšnô«À—–„¸a1ð(êÖ)ˆm¨W]uóA¥­ÒüH  `àž¾&üõ×_î ƒbØ52ù„Æ™ö^G!<›yýñ¦¼p ›[2´(êÖêîwÞyÇ‘rãätÃàûyà 7¬ñƒ-ÜíÛ·�ZŽ’ |ÓGáŽ8!„ØÚô¤in² „EˬY³œ¨[ª´^cVtIðFµlI]ã:ÿ¹çž#õÆi¨¢-ñmÙBQ†zÞä´0.1i?nÜ8'ê4P ””%u Ÿ2ã�‚AÒÿmÅ4}útöØ*„ŠLC“¹e !Äå¦ì¸ ÄÓôˆ#œ¨i¸W_}•n5jyÁFõ!,uþo·#ÎÚ¬qÀi¡¢Ah¹é.“BÄr�);nX•ôMÿþý�¨ýpR¡ÞC;5§ àöÔzõê9†NW¬X±V‹ñî»ï>LO+„ŠL£|ÚW!"YÏwÊåD!vÏ`Á¢»Ú?+¤ÁT6§r ¢nCý‡ï!"%·çž{2¸ºÖ»‰>ýôSNÇ© -5½’¬!A!61u5åŘ2ÏÍ«–ïlÃ¥Ä÷‹6l†Rƒ'�t’[¸p¡[[ Ä&VNYÉ‚PŽéSS“BIJ£Z-ˆ BÜÌ0©d>;;Û‰Ú[ úóÏ?é6IH½-zvÞyg÷ôÓO»J€Ïƒ‹Yã‚P‘ižéK? „Im|úÄ!žv÷Ûo?סCn>: ÕÒ4Ü‚ ÜÍ7ßÌ0)î¥ö: N³ÔõXéP Ð)IŠ6YÊòµ nå¥â„âSS7‘¸æŠÜlÒ$-GJGõ¡Ú€/^ìºtéB.€«88ñ畹‚Æ‡È äëO¬l`:Û$„I›šxûžbn ÉÄM‡‚ô-·ÜÂFM¥çj–Çq*aßÛQI»…›p7 4oÞÙÑ�µdÉ'ªÏ›Ú\ïÞ½Ý%—\B0`Y]Ü÷ˆZ^e×ñHÇr"E¥nöz&ÙõÔÝ__BQîŒÐôòƒPtÓøx”M˜0Au¢*„ùœ)S¦¸÷Þ{Ï]y啜DÙ¬•YÝM3ÿ¦’¡ ".ñyæ¦ÀõÔÁ$„åÒÉ43>¯ ;k>ûì3¿ŸFTvú‹Ü8!4oÞÜqÝ{ï½ÝV[m»V�ÁT\´¡ƒõ ZÄ BÇ›„¢\zšæÚ $;ꆶ&u"š®¸â Z�u*ª¤ÙŸÁƒ»›nº‰æ– 2·Å{]Âß3~Ÿn¸ë®»Ž_UA1.‘L˜[Ãh»¦ö5 !D¹¼eZ`i”•vs[ã”\øÈ�$OáÜ4¯¹æÌ2quVPúé·Ÿ~ú ³PNt¹q‰�;wÆ.‰“N…:YÛ@½ˆÓSU3cÆ æ’¢êQ<|$š4iòKEìz„âPÓ`S†ÝPŠBSïÓË8À‘Z;ì°ÃxÊ]ãAq3%µD*G”ÏgœÁ §Ü÷˜÷uß}÷uíÚµsß}÷]u¤=qL ó.òõXg\¢aÆ_ù4!„HJït¼Ê”7ì±ÇŽ7ÓöÏ>û,昭ÿöÛoxÇÑÌM)i­ˆ™>>mÇÌ Õ$¹¹¹î�?þð[Fk'îþûïwÌü0ouÛm·¹wß}×Q/ªapP甄Ь1á¥u„" ëš®ð{„š ¬žgÝX¥M �7vXö—ÇêÕ«Ý7ß|Ã�’ÙþmdƒsC!RIÔg¥ ØqseJÄÂÒB6»F«úzâr»žZ˜„"–›}kv!ïóæ°ƒáfÍöÌT£wïÞ|�á7lp’Ï0 ÚÙ1¢åDµÑ•Æ„ùv=52 !D,™–NA ;!”à’@ZŠÚ íÙ©ëǃ“þœüH+„ná“'Ov"é\Ž8dD $Ó˜0Û®©úqÞqB±ž©—iµ)áÝVY}¤˜ý 'œàð†[¾|¹K5&L˜@‹v”Ó7¢¨.ƒÕäàHK{Ô.!‚:«?è¸<=n•ƒBlfêcÊòA¨Än«­e™ ä8 ½ð ØÃ¤äŠ„ .¸ œJâ×Xá@](Þ•Z°ÐŽ@ç˜N£«ANòÑBQ†ÝMLYãÒöÀвŒõµrÿ)�ošâ\¾±ó‰OE f¼81F½¼¯¤é~²kêh¿µW!Êp ·ëYlL°§Ø­Êì±!¥êiàå—_ƽ!Öd•“ ®"šo¿ý6îýc€µÄ›â—ŽBˆ£LÃ� È/$sG}45!æ{RõI;¡Èš¢UË"ë:gÙC]�–ÿ¯M»Å5&!ÄI¦¦ÌPâæÂ š]6©ÚªLª ÓØµ´mã}‡ožˆøÅU#nµ7Í._˜¶4 !Dì¡‘Þ²§8„0½þúëé‚JÙ­¥;vŒ»‘Ò²�ÍPÄ Á ïUW]YSókÈg{;¨ÍLBIÓ@* !üÔ˜ÿ`Ø1cRyk)óB±mÆt öë×Ï �‡ÀL{6A(â}#°O±kémÓÆ&!„ˆ¤•éÏ`wò33ΧUÜÏ?ÿì°hIEF�M5 Œ8Ë<ÑÓ qß}÷…¾~ñË/¿ð¾aÙu‚LXšd×ÒKÉÚ³…âl„JÂA!Œ=1óœ7o^ªÖ6pͦƒ Y:ï¼ó.àÂð¼þúë´gÓE~Ï8E[pe×Ò+¦õMBÉ…¦±¦DPAÒqì¢.”ª<ðÀtÂQLjº¤U;´|Ožq4lD¬õ&…˃K¾oýÿ$ÙIH!.5ý„Ø´õÌŽ-¤……….U5j©%f["‹ìÔŒ:uꤵß�÷ o=êA¡÷Š÷XV8üjê—¬&$„Wúí쨚Ýq,µ2dˆKaÆ%åF](nðÒñç™™™Î Ì_©•…Sq´ºó’Ú¤=û_Ó/Þ•C!"¹Ž ÕG{2«°¶ù믿\Šƒ[8)9n¬QÝ^X©KÎ×Ð8ÆlïAh±iŽ©Sœc‚BÜ`îƒPA0·ûî»»† Ò–+ ¦OŸŽk8)Ȩm¤ä¨¥û¶UT˜­ŠÝ¦Š§ê\ÓÓ)&!„(Ã-¦ß}Ê7%‚7ÝÆ�sãå�.×tÃŦä\�?¾Kg~üñGwê©§Ò€P‘ TâOCo›ê­#„!®7ý> „Ø.JñùÎ;ïtXö§}úôa_dšé¨£Žr~ø¡KgØ´k«>âP8¡¦ñ¦Î¦ÍMBQÊU¦a¦¥áté–C9ÄÑŠ;mÚ4—,[¶ŒddJq:¼òÊ+ÓvÇz×\s ]q @¨Ð{Ž3=´ñBˆKLCMKÂA17Ó¡C7uêÔt)º“~ŒœAxÌÕ¯_ßýþûï. Á]‚Ó1­Øk„P‰wå˜dê¦@$„¸À4Øw2šáTóæÍ Bic[ó÷߳‚¯=.%G']ZÎ ½ûî»4¬”ŸŠ‹W¾iº©—RsBhï7«.2™a·†8ÿùç†UÓÆº}ûö± Ü„™�š={vÚµfßpà Xò¬MBŦù¦7Ô¬ „hé šŠÃA/5:¡8 ±Ø.]`‘ Á´Sø=ùä“OÒ­Ä©˜zÙÚ `zn±·õÙÅ$„HSÎ2ýìƒP…¶cr* E;]NB€Y)µŸÈY†YñÓ»÷Þ{� iÞ� ì’’\› V¦é[Ó~&!DÒÈô}Tbkfg¦L™’N5ê_™Øúë-è¢c�AºÐ¿‚/íë•„Ð SÓñ&!DšQßÔ×´(„ØsðÁ3'”–›E‡N'Oþ‘§¡Ã;,¥WŸ‡7ÐvïÞ�‡’µ«Å+Ë;+´6 !ÒˆSLßD!Ú³I¿tíÚÛžt\_M!>Ö#�õç-[¶ÄW/-N†·Ür ïEU!”í-¤Î1 !Ò„ãL_ø"q"|“=ñÄÝc�=æFŒáÒ žþ¿ûî;wøá‡GZÔð{ü'„œœœTÈÌ‹ñ`R•Aåšþ2�gB¤™>õ'¡Dø&»ï¾ûºÖ­[³ÞÛ7&¤+W®¤;jãjéi±]»vÌ¥zgÛgIÑVuBù¦±~†m]“"…ÙÑôžiž)µU”áLjt‚¥¤¡Xm¾ÿþûG¶kS¤ÇÚˆ”%ÛFSê^˜Ùò`RAx¿¹iˆ62miÚÔ´¡‚°H¶0½bš Bá6m:Á&NœèÒ†R9íÄú¥ñû,¼c 7U™3gõÁê@aϹIþD´~ —ƒMíüú”GMÏ›^1½é³ùûx}ö¡éïJþœé¿öîJê*ŠãøqéFP QÄVllÅ ;1�ƒ ŠkÓ%ˆŠH4([Ȳ°°älÏ.Ûüýþæ=ïÌ™Ù��É}÷œ�-±è»óÞ»ïÞ§p;ÎÆ�hˆ8ذ5ÑðÞ_Ò§ß®]»zzªÕÇÐ1ä;ï¼£æ¥>wCü5Í!Òn1&Ó� þu’2üƒë£`0^¢%zaÞÅxÌÁb¬BÖaÒ° ù(� ;°.d#n¢%(…” …p#Y؉Tü„'q@¤îªlØØ  Á_M½ï¾ûêí@·Õ«Wë8ÊïnˆÝ¢ºkÇä»!ýšO›6Í9øàƒÃ‘„¤©¸�"(Ù4ÃA8 7aFáü€%HÁ&l5‹·LìÆH\�íHB"d –`%Ö#.ä ’�<ä`'6àoLÃ=‘xÌgÃÆeXå/ éÞC�Ÿxâ‰z;€ê0ít4GG;CŸã¿é°“]t/6f̘à–gW­q+š ”ÑMÑGà Â|ŒÉX€UFÒÙ‰läyídŠŒ�LyÅ.È6¤"‰¾‰j1`1þÀbÌÃ\ÈtŒÄ™h 6""ºàw# ù|˜9tèP£B®þÅ‚ œÞ½{«"®ÒƦ ½‹©î|ðPá…ÆX„+yOi}ÍÄ�Nc´Çi€7ð æ# [� ·‘XÊQ�=u$./;���~Ó*ÌÃ,LÇ ü„¹˜ƒ¯0#чÛ]‘�Hˆ˜ÆbRŸŸòO<ñD-DfÓzY)÷È#�¨lÝçnH•„ê°0pà@u˜ˆ©;±G}T}ô™„L.ŒA;4Eƒ:H:ÍÐ �áSüŒ•HG.ŠQ†=!ä†+;‘n$¤ÕX„¹^Æc8ÞÀ( F?œ ‰È†�×8v+öµÀ°øè¨É(C®¿¡äÒ§O¿»‚¶mÛª‹‚ b&as©þ�z#)IHŠñ;Fá&œŠî8]p(:â Ž£Ð gàF<�Ï1+‘ŠíÈFŠ�ã²p¨@\^²� ·Yˆàçþhc.~F<ÞÇç…;p ¤lØk\ħ{7 ŠÏÑ꜠û½©ïñÁèŽLÇ”>ïÏöÛo?ç†nðôž‹…(((P º~¾‘”„Ìqb.~ÂLÌÀLÌÆ/XˆeHF:\È÷:FÛA*�—!ÛØ‰•ùøçsŒD”†T¬CÖàwLG×Çex:JmÒŠ¸ÄèÊ�n”gÏÄy—£l؈¼èÙ³çs½zõ*jÞ¼¹ÏÅFŸ†ãããí���w4 þÓ(t¿… eªº0*ß±‹«t˜��¡t9Ø…d¤ +(ÕiV™W%Üv$b fá} ÆÓ¸­#úQª ì€ñ(Ç_“Nu ФQ„WlÞ¼Ù0`€sà�ú,eVñBçÎ��Ÿ~ú)êvzÇ×n®º}ãÜØ„Dl1ª¹¬ºWŽmHÄBÌÄ»„‹Ð 6lDE<¦$äoÁÑ(ëeË–96|/Ö~ø¡çØ�dî÷XNÕrÙÙÙQÕA›÷NUNTw[‘ˆõȬ“Ý�Uá#íÀRÌÀû¸ �£q×cÃÆsÉðhÕç"sÚi§y.âmøŽyóæ9ýúõó; \;$Íä™8qbTõË»çž{”X«›„ʰ‰��µ*R°J�ƒ<¯G­«1¯à4‚ Q/sä–ǽ†Ïã$«V2´ÍÆŽ;4îÂsÿSÙ£_%ª]»vEMm~¼FËžj%"—ÑTs½±€Z�)…™Ø�­HÇz,Æ7臖°a#&âMÐ,zÈùlÝsùå—{.ámø?’{ýõ×uwæw‘ÖnH%ÛS§N�š$Ô·o_}©IÊÆz$BÒlb Èn£ËÁZ¬Âr,Æ�ž±VbmÃF#|Ô®]»|mhhÊi“P¡Ž=zô¨´Ïš:mkJ­Ûív"=Ô5ý /4~>ÕJDy^ 5³êíÝMàÜÈÀcÝ/˜Œ—pXý¹ë±± 1šBš@£ .Fþ£è€Ï¹Ï(òw©NÓÎ*†ÚÙÐ}eî•ît´uÑEEE>½ ;óÌ3N@âgN.Ê£ô.¦"À¤S€l¤aC Ž‹ŒÁuÉX‚Ix�lò‰žÄÑm°: +z¡7úâ ÄSŒWð6>À§øãŒÆ"“  “1ð>Ǽ‹Á¸W£NÁ±8‡ã€l›~2Æqäs¦ïDô齊ñÞ6&Mš¤�P•w(Œ‚ÐÃ_%õˆ¿ç:þøãkš„*På�UÝ(¨ÆÔT,ÁjdÕ w\0Ïá�ÈN>v—҇⼊Ï1b-R±Û±Ëè³”ƒ\ã•q ±R ÝsÀÔNd )X�…øãð9Fc(nÆ©è„6h¦ÿè.ÅO»”øZXöÙgMµI¨Š˜2eŠC׉*«É8öTÕYÄ(¤¦¦êçSEâ‰é¦¦Yp¡,€�P‘‘Œò«Ñ;¯ÌH`àµÈK>6âÐGâ.ÌBr°;‚g†ìFr�‰mHGª1lj æc6&áKŒÆKx×á4tA[4 Òà@,g)ƒãmÿý÷w{ì1OÉ® ßAå :JhöR¥w(f¡ÇâÅ‹#úkºjÕ*ZÔ×$Têk¨\Û�ÍX�Ïpdä$Íq îÆ¤¡å1paYŽR” ØØIåå˜éX‡•XˆYo좆áqc:â‰èŒöhƒ¦h„†��8È^†½ñ)Òàs”�T~l[öTRE6fÌM 5Z÷Tî”SNQKœˆn壇rH}LB%؆Mؤ°ŽÜߘ�sÑ6Â�q^Ä,¤ Û¶„÷$­"»Œ$µË�vãñ>Å»ŽÁxwáVÜ„«pžÄGø•%!ŽãìNÈOiö Aƒtwp‚zÍi4FD¿½b(ŸvÁ±�„Šàð~*k± +�€ X�]p×Á�% 30­a#Œ‡ƒ0ß`%v¢¨Êã5«¥(B�q'µHÇFã $!Áx·±ÓûüÚ_m5àt¹\6ëxÅ„ TgܦS§NÎÀ�#º+ù[o½etÐŽÊDTޏ ¹(©d´óð æ`–ÉhÊk±JÆøðï~l´Äù…¹Hƒ»ÎÎ]­2” Å�Hie Þ_uÜUW]å$%%Ù¬ãu GmnT{‘>à€œÛn»ÍÙ¾}»©ñüóÏkŽT´&¡ dc‹ÑÑ{=6cr|$£]ø3�Qø ³1K‘W «þܘ…k×€l´ÁíˆÇr¸Pl“FdñÕ¶Gw“'O¶™Çkºê³Ï>@ƒOß»Ëk¯½6¢û£�>ª Ñš„ÜØf xK4$a2�ƒBä ³�0ï#“0s±¦†o�ò0Ýa#ÄÑ W «‘¹»Ë_Y1•½º@Û$ÄB]­£8óë©G« ,ˆØ».ÊÈÕï.Z“P&þÁŸ˜‹…XƒÄJü‰oo¼ù‚Ooø‹�^Íu¬ßàhØQ4Ài�¥Øù…–ŸÖ=^‹¦ îÈ4ÞÛxœ¸6mÚ8½{÷v¦M›¦?"Ç8мԘ%Q‰¨»QZÉb¿k1×0#ÑKa2â!c0d(†á ŒB<Æcq ÖãŠÐÇÙ2ë~˜‚õÆ/–áü5ßìÞ½»g7”™™i3‘––æ\vÙe7øôî¨ME�J»#²êP?¦+¯¼Òa´G¤%¡b¸ >þ~&¶ s½üiÉ%B~Ç7x¯ãcÄc(û0ã1PRÍú3zÿM��“1‘�=VôÉ�{î¹jOc|z¯¿‘’’¢5Z ÙYþ_¦�ŸŸïDZð~I;_•�GZ*E6\†l¸½þúV$c!æ–c¶‰hFÏžÆÇÆ‘œ/c1ÃUÍŠ¹L©hA:†ë�…Ñ9UÑò×|³K—.žf¦Û¶msê{,_¾¼Ú >ͯ%ÝT¦‘ÃIŒæ¤ØH¼ʃ »°.6c™‘€# ™(Ãn$áŒ3_�áˆÇ0 6 Åp|„ÉX�­5ø ]ˆ¸4x½$mœ�EÑÛÂÝòwŒtâ‰':o¾ùf½ï%§û±³Î:«F ´Ž7Õ�àæ›ov’““#ò¾ëÔSOU²ŒÄ$T6»™T¸¼lG2–a)R°Ë(&¨@ÖâŒÄôÇÄC>ÂX¯ÆÆÓñ V#Y5Üեഅ� TÄ�ij݅ýwCê pÉ%—8‹-rêkèâ~öìÙš·SãEZï‹t§¤r‘6l0ÆRDÜ.¨Àkr«l‚ˇ�Ø‚ dû86ËFVág|„Fiö7˜iì¤þÄ"cÞÏ*l„«ï™va:N€�:ަx‰ÈŒ¾¶î–¿œ:JR…­[·ÖÛ‡ªêš}饗8ôÍ÷œ¦ÓN;Íùî»ï"îŽmõêÕÎ1Ç£Ÿ[$&¡lc'”ŽÈ…ÅFCc\è`Þ!MÃ$ÌÀ|,ÇZ$Öb)°9ØS …X�gê¾`ÁÆ^F™öïØ‰ÝÑñNȪìb�QÕºX¯—Ãî4eVIèšk®q‰^ã{!Í>|xÄ52ýõ×_U8©÷A%Æ–’:®¸Û†-ØÙ IÇflÅvì0FÄHQ]d`<‡�:ŽF8 Ã1K�Ž\³æßŠ,U•ŸtÒIÎW_}UïªåTÂs:tè­Ih7Ü5HBi^eß>Š!̱åî ïö’ñl1¢N@? Åh|ƒùXƒtd†71Y•UytÐAÎ�7Þ¨*¯zWœ°téR•­×x±–ƒ>ØùòË/#j7ùú믫Éjt&!ߊ�‹ÂJ’P²1?¨ÄÏ SIoÆ«ˆƒ�Ý5D3ì‹Î8W` Fb<" ép!»QúÁw6éNDG7꣦®Ðõ)rssõóVÅ`­Žä†1wkJ†ƒvÚ·oII¨ Å(By vF.Hi%I(ƈîbc\J\^ŠB4ŽbÆ¢%l„9A5@c´Æ8 Çâ,Ü„gñ1fcµ13'ÇØIÕ° ªêXIm}FŒáäääÔ«¡vÓ§OW9s�ï†è¨­¡�jˆ1÷]>ø ŽZ#) ywIÈ«F2rp|–í§¼{2 [±Íx›â±Ó#¿`Á&©84D ´GtÃɸ0 ßb1R°™p£Èœ·cU/©Ï˜F>|òÉ'Æ‚ûÁ;µ3ªq‹ÍìQ¹{jjjÄ”Ÿ÷ïß_MV#5 ™ÊÜM¸(fÈ„ [�‚D?’±®¨ÍÇBœ Qqh„V؇  NÄe„‘˜ŽØ`¼C(¬ú^ÊVÌÑE@o_êMéöœ9s”|kÜâF3{Ô,ôçŸv"!øuSéy­ .‚tQŸä#±"¸ð�×ì¡-ØŽõH4ìCcf7ÖàÑØo÷cU4BS´E\€Gñ&&ãwã23 à•MDÚ̘1C—÷1ïtîܹÆÝš7o®tžŠ´Hˆ¢¢"=Â�´1æŽ& .äÕñB³J.².C:ÖC¶!{B,ëð©çC´ {G…N8�á}LÆRlBŠëc"¢t[ŸîC0ª ¼ 6o…t¯Sã›Âu'Шðˆx´J±…ºƒ‡aŒCØí6¬&ŠQ-çW˜f¤å! ³pž÷|"695¾艫ð2â1©ÈF1*b1 ™‰ˆsjô³Gs´·ÑÈ-ص.ÓVwòôôt'ÜAWo©†aŒCع½FAl†«*aUS�\lÂ<Œ}+kùcÃ&¥–8§ ^ÄçXa´‚/Ay¬%"štê=MÌíˆH¬*eVyºÙc­Æ÷B:ÂT†pGBB‚qÇU�’�ï2ìL¢ å(óJV’ƒ=aPa ð[†‘èVU"²a#�ÑÜx u.îÂ(ÌÇ:ì‚ÛHLQ�ˆúöí«ù;1ÕÞ篿þÒ݉±c¨9}ÇwœóñLJ=YSí§©¯‘9ÆA¼wuyq£"€·GÙØ&…pa’1ÝkšˆlØ»¥¦h�c�J½qXŠ´Híè8kU€­X±"hºOÙ¼y³“””¤c2í¾‚öfI‰âþûïW·ˆZ' h'¥â½ û;+³êŽJwU‘š„ ƒ”ØÌ†¤¹ìB\�ÝØ&F¡†lÂûè6ê,9íƒãp)†`2V Ýh²Xé‰H÷&ºkøý÷ßëôH,##Ãùã�?´‹Ð#KÝÑhW¡Õº`÷ìXê:&L˜ 1 ÆB]{êºpÕUW…õ½�’ù˜1cÔA;2Ç8 H‹~þÁzãIFÿïÔ]ÌÝ�a3¢lØJ4@;í'1e‡c\F  ;�:ê(-â5>šÓŽgÓ¦Mž¦©~ø¡sç�w:çœsŽÓ³gOçÐCuößU«©Ž’‘ªÎêô0;;ÛóަmÛ¶u’|Ì!w}úôqV®\¶©µìðÔõBó¢"ù>¨"ÿ¬)ÆÛ �¨¨«¤ä ep20'Æ��'¦#p>žÀXüŽ�È2[…3é=�šcÞ}÷ÝÎŽ;üÞ�(q¨ó‚FM¯[·ÎY²d‰3uêTçå—_VÃT½­Ñ'v�ÉÖ®#?½¹Q»%!ñ$¦'žxB;¦:kZúÌ3Ï8]»v­³äc'ðsR5aØ:N(ù=õÔSúšF{QB�÷H†:2¤#’Šâ:Þ­¸C0èO¶áO<6 �â‡ÞˆÑ˜�%HÅ6d!(®Më¢ê^ÆëNE ïm·Ý晫3pà@½»Ñ}‹ZÙxÚÇÜrË-ºOÒQžî*Tά[ÿ¾š¯ÝÖÿ;!Uæ½óÎ;u¶¨O›6M».£r¬î�@uħ±áz«¶a}|íõµ‹ö$”gì ¶ ;Àû�­Ø(u˜4ÊC0YÚ{&Òv|‰ 6l42ÓIè�w1‹°k‘„ ØbvDMk2©U½ÊþOì”D;µ�Ñ}’þ€ºèŸWrSÒzöÙg�eË–y•Ö6RRR´3{Ö-~~ÿ'!·Ûí„#tÔyà 7Ô¾yiøåÂ…T$"ÕjWP‡÷M…!ꤰiØ€é°a#â#MÐ]p î§øK°‰p¡,ÐDj*)Ö=�æûèÎFã~ùå-èµnDñƒF6è˜JöAKB§Ÿ~ºÞ yv$á¾V*;¯u߸03›‘nB"Ö£¤Š;›MHÂ6óƒW©À¬ÂŸø 6lDíL§舳1ñX€dER"Òâ­OSeÜc�=¦~nž;¤¼¼¼Z' ÝI½ùæ›z”ª#À &QvBz§¶÷T̃Òq£Ž£yªª÷8†$üƒ¢*î�Ö!iÈž(TŒ ˜ƒ.°a#ºÃ2Ø—`,–¡8‰H»%!èèN… *ÃV…œªºÆ�¯G±ª`Sò©“‹zUòõîÝ[ßgP“©Ž%O=õTÏña¸‚7VJ„ÑÜ-¡ ./iøU”8'!›�6Úào¼6b.¡žÄ¡8š3 țã*©ˆA-sÆŽ«j2OÉv~~¾’F�í"øvô¸VEúþƒv gV * ýý÷ßN¸â·ß~3º%Dõq\¶×n( ùUÜ ¥`½9§(ÊìFÆaoذ“‡„-Á(V0“�Þühjé…^¨j9Ï=Ï—_~é9jÛ¹s§J¯ƒvt¥oÿé§ŸÖ1œ±(7 ©g[rrrتªúïè£�®ñX ‰°E9YÈ©¢Ûu2Ì‚„(SŒ4̨/Çp6ltÄø@ÛØ×äŽG�ZuÇsß}÷9¯¿þº³hÑ"g×®]!é­¦jºÑ£G+)øí�Œ£Æ“O>ÙILLtÂ_ªœ]íƒôã©oÍK+àŽÒT‚�˜†3`ÃF½‰G°½®‹°ª²´óPçµàQ"ÐñTHGA�ä4IvF©rh�„Ô÷.¬Uy‡=IÈ*D ¾Áù°a£^Ååø³.’�Þ uèÐAo{tßóòáÝJÈ�¤6nÜèÙ}uêÔɸ �O* [i2}áteŽê®µšÜ�©"púôéN8‚™L}at…ˆêwB{bH9¶câ@ذQï¢Zâ ÇÜê$ îWÔwM]­õÖG—ïaê�æ{>Ç�Z€�{ ð$¡nݺ9'NtÂ|¿:ÕN5r’�导‹sÐ6lÔËäs0à$øzOáo÷£¦¢:æÑèê™3gz.ÿ#%H„ê §¶<Æ1Tø’�ºB|ýõ×ay#ôöÛo;Á0'!«i˜Ž;Ð6lÔËäÓOb–"Û_K_e×<|TÛµ×Ñ;_÷>a½¢/œÚau£¦¥êJúZ…:¨F4ß…' YåØ� ,À‡†°a£ÞDC´Eo¼ˆIX�ÌÊzÅy—]s¹¯Âu:Ð}‹¯{Ÿ°ß1F[•yš=Tçï�jZ²Îxr�,Ke%òúu‹ä$”�”ÇhÕÛF,Å(œX�ÞlØäÓâELArÝùH‹-t¹®£7]t�"+6oÞ¬ ¢ÓmôI êØ±£óÞ{ï…|ǘššêôíÛWG§•u´ˆ„$äBAŒí~vb%ÆâL´€ õ&âÐçb~Ç.y?ø«ªßÛ�èÙYLž<9ÐqÚa{�Jq„ù 5b’Çaº› ùΑîU¾�âïI8“PŠbd'T7�‹Ñ6lÔ«·>MÑo`U Åæ'c5÷T»}ŠÖ ¹¬¬¬º÷ñ]ˆÀNC…ÚµéçQIˆ‘æ: ù×�j¤_ÏJ“½ýì�PÝ$Ÿ$|‹«°ö‚ õ&â\�YÈ tÁÔÎA ‘>±«ÕŽŽÝ¾ûî;�¿Ž°{ß`“&MRgî 6&­M==â}õÕW�PÝ*ô~«Ò�¡ªÛ´icPÍT ë17aÿúøàÔ†�Æè…'ñg »=^TõØá‡®£:rS5O‡�œœœŒVßÀ¶k¯½Ö(Dˆ¼$¤Â€—_~9äEìbUɨ¢’J�\[·nm“PõT 1qP}­x³a£)NÂ($øê­å}Ô¦{Ý�\yå•Úõ¨·šªÊÔì2‚�Ý|�­¦úK ­QˆyIH;´çž{.ä;D½•RQDýþl \2°CÐ�aÃF½Œfè�1X‡"_ ¤h�ý«TøœsÎQ{]â{v=¹¹¹ž+Ú"--Íy衇œcŽ9&è�±k;Ö‚ê4½× ù{):FhŠm 3ŸlªZ²°£ÐMêó½� MÐøëv ä£»¹i œÊ—5ÓgË–-žËüh =�}öÙg�“N:Éø<µ�2«ã.í8Cš"{Ï=÷ÔÁƒ]›„PŒð9Ž�=v³a‹p Fbò°ÇÄ=@…úº�yæ™Î]wÝå9rKHHÐâEGn¾{ 6L÷XJ°aL@�'!íB5Q6”ár¹tWVó6 •¢Û1gc/ذa˰Ñ#0™Þo(:(Õ �²1b„ç¾'///*�ݼÛШK÷\`TÂEGR7ïP84G¨z;E›ˆJ�…•øOâ0Édž -13‘�2¯#¸Òž={k”µú•¥§§{v?ÑŒ×nN»õî&j�(¨€"”¡I®'Ÿ|r`#lª@.–#ƒÐ `Æ # â‘ì«�·>»µà�?Þói¸¬¬Ì‰öÐÏaܸqS­â ]¤‡!Õ< Q‘¨£±�~ ³¹Š6ªá`“P ¶aÞÂ9þ[ìØ°a㼈ÈÆo,<%”æj’gLì€t„8mÚ4% Íå1Pô$!u×cZ=þ U̘1CI(ÃübJR1£cåMmذ» ›ðRQæk1¤XO5^[pQŸ€4‘T H/ÿ9j cªyR ¹f/-\¸Ð U,^¼XcÅÃó5‰|ÈÁZŒAßÀz¼Ù°awAñîÊC•å^wÝuºŠº¨f?8)*©È‚j¿ˆL@È;îéôÁ dÇ£±Þ«W/»‚—2ìÄ�“Ð6lب$âÐñØH'l]H«+Â�!Cœ?ÿüÓs±-¡Äùé§Ÿj0�>Ík'ÆTû$$�:uò<®Ý¶m[È’8DŒÂØDT‚�ø �ãÐÀºذa£žÆ|dºêøjŸ}öqN8áç–[nÑ=‘'�dÐ胈LLzßÂ�–ʰ5;ÒP9Šj’„´;ÕX…Ÿþ9$}ùØqéA¯JÙm ‘Œñ¸ {£lذÀ» ñV£¸:‹Ÿ¨BJ�’5žA—Õ§žzªÚ÷x.˯¿þzGåÜ/¼ð‚óÑG9?þø£Ê{Ewž»…ßÿ]»)ý±Úü-ymܸQ­f<l÷Ûo¿H¼*@vM’ÉT÷Zj«‰§!¹OSq‚úêÅÙ#¹\¬À8­O@6lØhŒ~˜Œ�¨¨nò¾Ÿà~E‰I £*·ôŽEsxÔ�À›ZšÔüTÇ<êÝV§}Δèn»í6-Ôú±Dêñ›YÕMBf7íK.¹Äùå—_Br7D5žz}~+TŽX€á8ͪ÷þdž m0ó‘í½ø…ƒ’—:p¯]»¶N&¢êê…^¨q ]€€"äbOMwCGq„Æ:h7’{!Ú©b²>É•bfá4¯~ùµ 6¢#Fc¹÷˜_ÇnìV´» ú{vOj TãDÄN@í„tY¯ê7-”á}„¸’š&!èŽFÍdu´ôÝ�ª"§N�ª»µúv$W„|‹+j×pÔ† {×ã‘„s‚8êÑ(g%u—6¦e�‡:è(-Ð(--õÌTäQŸŽäÜX�Ñ8­n� v^Ð ó�ª÷¬ �åV2xýõ×U§BÝ­TÙ²…¿¯]S­ T}×·o_½éñûI—äªÈ[½zµ’�çèí°ÃSâTK›0$Ÿð$!ónH…!«V­²Uru<ïǸÿ9¾öw?6lØhŽ>˜�t_ CË”ô0U÷ ê–­Å^•o•î0ød¬OäJº¼®ôÞ€D¥‹OŸ%ÓZàôiû�7ÞЪ qÍ,Rµ›ªê<Ÿúo¾ùfç”SNQòÑ?¯ä¦ã·ð'!ýÜ�<òHgâĉ!)×þõ×_õýÅò‘\9¶cÆu—€lذ;¡K0Û| ­ÓÂ>pà@gÉ’%J@úõëç´oßÞïBÂîIÕhÚÁ(�铹’�ÏÝ’Žùî¼óN½ÀWé´Ïo�ã?-tz £ï[Ç‚JLz0«Š:%D%°0ô~ �êV(ª\þ±Çs233�`߇º�ë×7“P)6b<.A›ºK@6lØh‚~Xˆ­ØcR‚Pɯº"¬Y³FÇ^:ÓÑ—Ù©ÚÌï½÷F:¦Ñ¥”úÍi¨÷?«�&tª¬X‹¦�ß#49•u‹ |†9…? ™»GU&'';DÐ ¨�l¬%¡b¬Ã§è�æu;ûdž �p.æù:ŽS29ûì³=Éáï¿ÿþ¿ÚŠß+ é¨Îoe›î�Þ~ûíÿwNJ4Z½“Õé§Ÿî >ܓؾÿþ{ç¸ãŽ«õýBøPø“�ÞBiÞ�ª CÚ)«»çXIBXƒ·p$šÀ† uq8³±åØ#¢cÍ×Ññ×”)S4úZÉB ô×ü& íx4sH 5•€4±ô‰'žðNZ:BÓ�‘çŸSüõ×_Z8u$g“O-’�ùfH_ÓP�.ôÁC»ØXøzça9žÄÁí~`Æ�˜† ¾J´õ‰ZŸpuñ¯6-* ˜7ožvH~û®)9i§ÿ9Št|ç]ù¦.Öº3Òl"µñÑ�’˜M@u�„ÔRG;”PÅÈ‘#õA#š¿æåpa1.Å>Á€jÆ�N�U(ðµø‘T’­*7ípTWéù¿’�>¿óÎ;žÝSaa¡î‡TÈà½R¡�îtwáùç�:ê(- 6ùÔ2 é¾L;!õè Upo¨Ñz$W†í˜‹^°aÃF+äúc>¶ Ü×HQ€*Þ¤Ê*(’–š˜*¹hפÎjïâýÀU‹•Žû”Ԕܴ€©´M>u”„è¡ì!‘A7õ:é͆´³qbxv?6lØ#¹qX wmD½RrÑnhĈÎ{ï½§?ö™¼HDJN:ÆÑâe“O%"ýh—©ãÓP�Ò�cך Cš�³ÃsÿcÆ�æ¸ s�ŽÝµ]U!×­[7Ï.窫®ÒÛŠ·Zªn‰¶îñ²²²œP;/½ûªõ‘\ˆÐ4\†V°aÃF˜âP¼ŒEØ„\”×fQd!R2ÒNGÇC6ù„0 )é:ÔSðÊP%dÿþýkýp5D h3&ã*´‡ 6Âqè„aø)Ø7ŠP^Û…Ñ&ŸÚ ´m�ª¿þúkï¶=¡z¸Z뻽í€&ã9�Pmذ‰è@œ‚A˜‚%HÁ&#)›IÉ&žÈJB*Q�€ªÕÂt¿P¿hI@"/Ù°a£1ZâpôB Àëø¿`9Ò‘ÇÂQaOhò>H=øô.K…Û6mrÎ:ë¬ß …àn.C[ذa#Â#MÐãHôÂõ‚qXFw…TØÄÞD¤·XêB¡ÝˆŽâÂzvà 7ÔxÆPî€Î‰¾6<6lØh€h i…6è‚A˜Œ>ý²�챉'x*{Äc_Í{R"¨Õ½Ž˜è� 5·ÍÉÉqRRRœ &¨Ë¹ŠR"á×Ó<‚›‚3ck� 6öB \ƒqHG‹I©€à$Ëß$ZÂ* š<þ–L£G�ÖàB¨²ÎÓåâ©§žò4°}øá‡5*ÝÓˆö¦›nr®¾újuX׈}Ÿêš®ÇÆz ëé„ÞµkW%¡HI@Û0 gÆÞ6ìî¨%öÆU‹d¸Ä»�ØÄ„$dtŸàM–’JÀ£ÐçÎ�ëÜÿýJ êå§VJz4¬¢*;Q¹µŽÖDwN¢]—GÆ õ}Gê@»rlÇt\[�lذÑû 'îÃX,Åv¸�ê9+H¼Ë²�9ægÀ€í‚H@ꌮvJê ¨kÅ$娉™¸.ö� ¶z®#úâEã]Q^h“�e6ƒÕ,¦G}T÷3UÞß°Ò?«;$ífb-UÀ…Ù¸3¶ŽàlذÑ]Ñc2Ó|,s'Ä]ŒîoªLB ºÓ}�vN:JóYà ^~�:uÒ=S4& ,ü‚Çc«œ 6¢;Çll5î}¬0&¢ÿÞvÛmžsþ‚qëj*«b%Ÿ=ÿ”œ4ü‘Gq.½ôÒ*‘¾oµfRÒêÒ¥‹Æ|èÞ(\I( ð"šÃ† 1Tw†›Ý¶­ð3ê‘è¬Y³_¡>rü±ŒÏ; vS:ÒSòÑ AÝiôºæHù}xª¿®]ÓùçŸï©¤{饗Tž­ê8%¹P' <,ÁHì6lÄPŒw±EØcE^"êØ±£’ˆvCf¶ *9iâ­v*>;-è8OeØ¿ýöÛÿGz4$U±ƒvH~gGiW¥Ý•Žùõï{Ý ]R¨P’ñ1zÄV+6l4Ç$Göñ›ÅΤB£4×§¤¤DN5lN“ku_¤w<>w5ûí·Ÿv0>Ç��5Ê9ðÀ}î‚Ô­[Þeá_|ñ…vUJn¡H@eÈÀ—¸*¶£Ú°a#c ±ÇŠlƒ•^|ñÅ»§M›¦Î ÿßiÈ]«V­¼“�çÐÉ'ŸìI\eeeŽwðíølHªÝ‘ŽêÆ�ﳉé\ #ÂP¼ÊÄÏ‚°aÃF E'ÌÄ.ì±¢e×n nýðÃw�3F»•qûL:žÓ#×uëÖ9¾"))ÉéÞ½»÷¿ç×pùå—;iiiŽw¬X±Bß§ ‚�€J�€÷qxlÃÙ°a£)cŠ±ÇŠª;"7£¶—±#I§â­Ü_e›ŠÔŽg×®]~»bÓ™ÛgòºãŽ;œüü|Ç;æÍ›ç\wÝuJBÁ‹ŸÐÍ`Æ�ª†;�‚2챪­%(„yÈÇî}M‹I4[8¢+ð7úAVUÕ¦»#_±páBÝ%ù¼Gâ>Èg³ÔI“&é8N»¯`m1cëQª 6átÌÁ?U>Fµ*P‚dcÖc-Vb–a dÖarPŽv?*Õ>âˆ#Ží”„|¶üQ‘�ªî¼“�ªßžxâ gÇŽŽ*†xç�wT˜ ÄÌ{Ä]˜�‡Ñ6l؈¡h†Ë0Ë‘…=vW³ùpa3’ð;~Â$|„axOâ Àí¸×@Æû˜�5fÓ×`3wBJ0ê ­½%2CGmê®Ð®];ŸÇqwÞy§“žžî˜±hÑ"§wïÞ«•dƒ˜\ÝX‰‘è›�lذ±/NÁ·X‰mõ D»ÌH4yÈDR° ?á+¼‹¡x·â*ôľh‚¸»�7ÅÁ¸ß`-²P�=ÁÆQÝ%½Ú¶m›÷ãV�xPן�[•À(~ÐqUÕém‘Ðrî�T*½™AL@0}b¿3‚ vTÃÁˆÙX��QZ¨P�RãŽ&™ØŠõX�ù˜‚±‰§q=.Æé8Áˉè�&µ¬Ìj„v¸ó‘Ž’UÒ•1Æ¡X3ƒþüóOO•Ü{ï½çñЧO§C‡J:>é��þ¹Ï?ÿ|-]gjˆUØŽÒ üúåa#ÆâšúSŒ`Æ­’k�#1ÓñR�æÝQ¹±{‘Ýp#.lÁz¬2ÌçÆNæn\Œ“Р-š¡âB\öÛ'"kCØ©„dTÀ±[6‰%_ó…:wî¬ê¶J»èMo�t¯õþÄæ �ò(A6R�óÑ6lب'‡&h‰Î¸£ð-fc Ò°9(DqŠ »!…(€’‹,¸° ›±ÉX…ß1³0 _a4†anBotÇ!h‡fhˆ¸ß�vÅ‹X„á* ð¡i1 ƒôa¤ÙHÆt¼Žî¶+‚ ö˜®ÚáHœ‹~¸ 2OâEŒÀx ïúñ†a(žÅ @\�KpNÁq8]q(Â>hƒVhí¥šDq‰ü]˜€ÔzÖ2©.¬Ä7�sÑ quø5nÝwJ6lØhhh„–h]…6Ø­ÑMÐ ëà,ÍÑ�bà­Vk\ˆ·ð;²cüÍV1¶b1¾Ä‹8í‚ðëÙÐø bÆ�zäÚÂk'Ò8  ÇØ›­.ˆo�ƒSlÝX‡9ƒ§Ñ û YîåŒ$‹;!6l"j†Vöì¾Îk7 ÀWX7J£x×ã L»¸=Œ�ì^!Hðq°aÆ F€ëð–b "ô¨® Åp# ©Xˆñø/âzt6KÝmذaÃFäG#tÀ5x ‘†,³b-ˆ*PŽR‰f+R±‹0ð†` ú 3ö6 HlذaÃF—Ðï�¸ ƒñ¦`Öb=6!ÛáB&r�7$9ÈÂ.lG6!Éø+±3ñ>ÀpÜ�Ëq:b_´Ek/Mbó8̆ 6ìãbieŠì�®8çâJôÇ@<�!xò<ãqÜ‹›qÎDOtÅÁÆÝM\5ªãÐ�aÆ 6b0ZH0ïWlØøÉÊ´� R…ÆIEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/rory-flat-spooky.png0000644000175100017510000013533015224505336026054 0ustar runnerrunner‰PNG  IHDR’­<®ò‚ºŸIDATxÚìÀ�€ ý©©˜½³ncÉ¢h‡á3ƒ™™†0*Yffffff,XfffæÏÌLØQïiýQÙckd)–ktOÕ)k¿åª ÍU÷ë÷:g¨í>° Wà*\‹'ãY؈ØŽ5ž X�çzž‰§ãiÞÏ­0B!ò"Jaþ�e�ã» –•F!DÆ� ¯Çn|2~ÿŠ×â]8¹”ë&^ñ=U§Ÿ?³%öPŽŸl„B,^¸”ã >Â[¹Ü²äê&í»ÝŠÃ…Dšöÿ· jðeM[N2B!²¶r9�]JÍ�Ô@lQ­W„OÏ(§¿þM=‚&G­P„"‹¡²‡ñ½xå’(Âwî·„�é[à¥fèy-Û*ú¢:ŠBd1TNÁMøVü;Þ�î#'¤¾´a4•m­À ß»‡ºËïir|LuÇFã\n„Bd5X p'�æ¢þpQeû®¬�fád–[a¤(Áïc…òN{½Œá‘g!„Ù‡ºÅS¨ADùjé8w= Ym^,šn^Äøx•þt%Ê ¯¿—6ŒŒpJl•B‘=x�ááé:Ä€-¥ÿƒ@ÉÞVWã˜ñ0‰Á�r+ÿÿßTT»¡ŠÊ #„"+A² ƒv¦<Œ-ãìõ›´îðmuÅ^£é8…×ñ³ïæk#.3B!=L:pgX´Ü)’Å~“].ÀüÁ�R˜¾ç~k�BˆE ’eøfŒÎ~@g9LØR‹�Vað£¯^â ‹ô½ ?ŒÍ‹·BB…É|ú¿fnïÆ€-®ßìmse¿ï„K±˜¼5vyá²�0qÅÏb“B‘y�& 'ôò wö~#«£Øü.ŽÇúQb[aUi‡Ë�øhTA^!2 —R­gÉ5ÓycnæKol=«%îŽwa—ÎL¯I|®3B!2ÛW’xZoleÂQÝ%}¯<5–Ø�HƨĮÿ¥Oe¾­®/bƒB‘;r–[•$¬E°…Ä÷,Ýða²tÃ…Z‹k¸Œ˜”Çð^¼wàr#„"#«’W¹‡m²0¡F‘!¼æV-åM±"þ”W3ùîÃUF!ÄÂ`Åq3¬î *b2±0á~n†‰ß(£Z&Ø»Ž_Û?ñex²B±0÷þoLI`˜6®#= aâL”6ŽßʯíøÊö›!„ w¤Œâ:[>IÂ¤Ú “¶]aŒD9N|7¿¶Ëñ�F!ÄÂ`ûê Éz4øž&îæŠ0a»ÎÕLîÃ+ñ� X™!„à¾ô1¶°¦’u“óý˜ŒqE˜°ºŠ~<Œ·±½wÀ!„8>(@/gëêçɂĻCû-ï�ÏåÊYŸBÅIV]×qø —ï-3B!Ò§¤~d3µ÷PM¾Ås çOr¹ÎýBc”)È1¦É!„HúEÖ°*ù£ ‹TÂÄë/ÉiéÞŸóë+kÞòî†/4B!Ž«èþtÿQà€P‰ɶÔIv&¨õO1Ïëý|ÿ#„"íóg±mÅ�ó¯J*Z¶‡ §d¼Nâ N:þ³Åõ|Þ³Ú!„H{UòþTƵói>Ç€é'IxG<[w·óý}¸Ü!„HýEê$½¬JfÝçKhš™<{kËû:`©ò_ÞSo„B¤Ss)ºþο*éw†rERѺ}n� ·ÅåŽ 6�z‰BˆsŠ[—16åñnUâ �Yó¸*B2{‹~˜D÷økwÉ×½¼ï�ilq !„¨êØ}6ŸÆoó ¯1t+úI\ŠÕ?#Lúݪå¼·Ì!„H�Ò†‘�øN4aƒÝ]%�ý2ÞõÃSÕ{ÞÄ{×!„©AçzPßö†2Hè‰  3½ £óß�âÚ‘â!„5]ûVsWÉo ªÂ$ ¡Œÿº‚·¸øÊû~Åû �BˆÔ éð™¬J|×�Û=#nL~Ò‘0î5Á:IMåuüÌ:#„b~x`³¥“ð*ÞÊ0 òë ¡?#L˜|ïßšâ).!„•­;¿XP5÷Ó:ÅçP +¬yUÆ·ºÑß¹�5B!æ‡yTc³?­»í.þ{¨‚]@¸ ^•x�ZÀ)®Êö]oàgV!„ɩ튬§°þß™«’’†‘Ð…H\êBaÒï ×gؘ^#„bþNwVï*ªÝ8£ÐîMþ ©ÌزÌ ¾%½•ÙO˚Ƶ*Bˆù9rìu+Eæcñ¢3ó§B$Î~�4*& $>�ëÿ{§B‘Bä|ê×Rp×PˆßˆeÅ1·^B˜Ì8.üc\i„Bs~Yçrê/tEwŠÌù$ž^>àH°ëþ0B!æmN<ï¡!/ï‚ÄI�º-­ ãÁÿb¥všB u‘Õlqý5ïBÄ?rÞw£¢ÿHôÐ[Ëš·.7B!ÃI¦U|êþM~®HüEx.ÿJT„?J=å€B8.eeaÍð/ܧr^çµ®“¹[sW%5C×ÒsSb„B$ìp_EÞ¯ØÞò¨ 7~žUšïžæ;•í»W!„s‚d5M‰dU¢ ñ©ÏïK¼û=ZÞ¼õUõ}^f„BLCmd-Ÿ¶ÿç–ÚÞJd$v¯Iiã¸;Ýu”×#„ÂW#YÏŒ­Ýö ·'*8’X\¿™@½�0i3B!ª;÷žL�ÜíÕIŒ¦~r=øc„B˜ªö=§ð`>J\b@r²‹eê`¿ìšåÆ4U¸Üµ媵!ä.ƒ5 cÊä˜αuÒÀÿg¨¯ÒÃqV-¿Œœƒÿ�¾�Çoƒïš|­o|é¹6^çœpÞO{:ÏüèÒØ.(W:t£}Z ×Ìrw¯è¼–—ˆ3%CeŸT&Žä€ ­(”ͳ²åŠ%>drÏ:Èc]¨‰!Ð9„rìßèàÂ8ü\~.ÁIǽpÉW Êž8RÎ`×#*w®X ¶âËk. ϼ�á=¿oãû�Ý~‚cv>Á{ÞÀ…i¿À&Î;p/þ!° ìÁf×Å8QY�JëÐÑ…6"žƒ?Ü÷R½=Á"éím‘†»Ý$”›ýÌìCɃâ(OZ™”Èâq9rÝštyò@–;œ#�ïN’{ÖG€p¡„J%X(W&§0^üËÙU±Ñø÷úm 6´Yï¤O\ÑùXÈ¿�…ûB,ì½. Mwb¡OÁ×9Xøýx,è™5¨GDÖ"ü·ÞaÜ�¿¿ñÿÄçà¯àßxÏÿŒSpÆØ© ûx9ѧv÷×�‡Çïãóffu× ÆÍßÁc¾×Ù‰]@$?À‚òw5ò«%ü†ÙTq�PK ‹Ùä›y$•‹;¥\3Ê¥_ TvÌÏ•weÊ3GråçG @¾"f¡ø‚Ô/ P»Tø³ð÷P×¹mÿ#m:tèèô;ÚËÕå\<¬Y ¾ÆÓÔÌÃZ¾böŸ\.®Ôrñ¥—KjV¹ô//•­³s䑽9ò‹+ ¬@(yrìHމòëÞž_á=�—ñýk8¥ùS”Œ~�ÿvŸ#Óø#~ãÿþþ¯�ÄCµ°·ø{@â‡@4¨ñ?°|˦C‡ŽÎ(9Á£2�šî)G¢¤a�s`ˆ#‚“VªtU.NdÞŒ È£Bò ËeÖèb¹re®yd§G˜�l ÊLØ3 .qµkã�™?$ÿþê–².äXÔUi¨}I€dH©µ\ÕV¶éС£S‰$å“z$â–‹ûx¤WdèÜi�¸ÐGL¡¼ é@&Æçùs(–¹ú³‰�cSŒ¬„ço=†¬„½–· ‘ÄYDÞÚ"a/äèü0]‡¦:åÁQYu@¡E�¥¬ÛÁOl:tèèœ�“^¿ƒ}Ac¿ª±ã–yCâäÚ%\ˆ¹(£„D‘P"ÌF(•‰p‘Wý�o1%r늂 8^» â¸kœÉxyýn`<ò{Êïé ;1eò26ãÍ<þZœÞB¯Ä,o¥òï�{K¶9N‹d#EÒª=’ÃóÂdl NzÙ]*ûhXùJó0èR–�ù’"lxscãÛ k6å‚@bqœ‡¹øbÆbÌSwÑƒà‰¼ÁÙHÇ{Qr2yN\©r3£œeJDÉã�» &¹‡�AByÍÌP þ ôL,ý’�ükáï�å-NpQ$I‰"qbr‹"‘ûZO$¼ÂwÇt§„Çú¹IÇË4|ó æ3�Þù��סC‹ä<”^vª³—~ å&È”hX¢DìÂãÙ‘�à�Dô ÈÒÍ&{ $Rœ�p´�u–³†‹*g±dA¼q—ÈD$%‹LÆð³è™°¿‚I/••ð¯…æ>D’+OQ$i¦HÌŒD‰„Íöðÿ½vq”LèŸ%á®3³ŽÑ¢¨“'Àå6:ttþÀêpœ¡ôC"!a.Y<<#«Xd)`ÉFP"¢H°H³„ô ¦¦˜‘˜e­ÓÙˆj®‡F D5 ŒQ™E¡Äñæ=“äÍ{ŸS™ ßçXˆÄš• ñn)o=‹é-dG‰ŸÉ£ÖÒ–ujk]XKmHd/dãd¯x’jÉ<´Dê;"~cíÕ¡C®H=ÝaH$Óç�½3ÃÍÅ2¤z6‚ qT;7ú=¼Í‰1[¯Ñ›`„"á¦Ã/{#æ˜/³£/Ì’–YÊRQ"QTËJj*oUÒVP³ý~ˆûH�I…S$ͽ›ä”±Î�“iUêÔRh#±ñU—²tèè*Ñ3:7;ÙÿWš‘ ×/µ[Í l„‹0~«g™ýØ#yd——å$d$ª?b÷ln6´6Ø«‹â¨žÎJ‚EòE2D�S$ø{àß ö’àïËgŽÿÆãï7†ý‘f”µ��Ùå€!�A©âOI•Ðø&ÞW¯y8µDtèèB�͇g]ž¹Xa<ꔈ¢¶²E‚š"Á~ f8�÷´Hn¨.’W¾ '´L‰XDrOí"AFbi¸�Š„cÀØín 2"Üšè7ö‘ S2í[ÑÙh4Ú¹ÿ,IC/·byo˧”d§ ²6žxŒi-„ÆsÜ.²éС£kEinÞ…+FÇýõ¶UÖEµzFr�šÖ2D‚þDÂßøÝécðÌÑž}…½‘Œ5E2Þ,]U‰µGBÔä÷�P$l¶›"á^%^v…ÃyDÊC[UYËè�4*aùj×ôH)Í46bzÕ¡ŠœÃ=$‰Ù#áNvŠÍvËÄÖëFVbˆ¤†>É÷‚à²ß_ëøï ÿe&„Œˆ—dabËèßðx”û8­©²‘z¯Ú½u¥]v͈–�yNdÁǺcRe%ƒê“œÞCbf'æ# ÞG¢F¹»�£¿Â�!­ÓG˸1—]©£Q,ÙHX]%-ô†Âeé(·ø“¼¸ðÌëgµDÁqð;Èãzd Iøú<›:º¬HÊÀq �IpiËËãÛq4 n,,žô´q°¼¬Ê[·©É-d%Ö ‰B1ðøÚ²³]í žÖRÙÈÃRi^xÅûI(ŒS$ÈFÔA�5JäNpd~¬Ìœ$Qq©FM·@öŠÖG»×ÿÀ108»Çît:´D"Á�4Z$Œf»‰Óhjó†Â'± ðØ¡l–—Ty«zV™p xA(JÅŠj®«¾Hu‰Üh)i™ÙˆyŸ;…†L‰ƒ÷š“ZJ"jkÇt—Œ¤‹=Ê{ µHDg"gpÊÌ:~ nA8צC‡Žî·® ûþ�kB^mØØkõ©-ìGVbˆ$Ž›ý°Wƒ›ÿ0¹ÅÍ€XÌ™!˜Y‰¹»½ "á±ñêÀF0 Œ†(L¡XÏ<ý—å¬3%Òß*ω€Bî´úP–9òkHÄΖ«FÈÂaIOÉÀ]ãif¢p[á5·¼#D‹Ce¯�+Àp`çvÏ#MtèÐÍõ¯£œs%Ñ$‘` Ë[ÑÜ�ˆ©(LG±OÂòÖ(oaS åøx«LTfb½ƒd¤’Š"øèxó.5¡$‘kÙ\'”ÈUÅl´C" 'û‘‘DÉm«Ãdë”Rè”�p�:νN. I‘>ñÅÝ5Û8 þ~‚ ¯.WéСÃ(g}Ü ¤á‘ ‰ª¼µYe%fyk—‘•¤ÈS‡²8†ûÜ2yÑ"u#â+�³�Û€yç¿'J * QWíVr÷¼`„CA!/¹zpGª\±Ø-s‡ùÄãòÉŽ뗇*eᨘnrOHàØøš¹·c˜²Áåà‚3ïÑ¡C‡–È÷Àã@ÍËJ"‚²’‡v°éα[4Ý™_±Ë9ª,Âf<à×|^ÝÙ$Ng1Ûù%P™šëùòÈÞl¹zU–,›.þTé‘Ì‹¸x+aÀ}!]ý¾ôà}°ä�¨G¹ž¨Ò¡CGƒ$r ø�f‰„˜"1³’ûŒ¬d {%ܨJ\8Ä1è‚+Ž› x åF`S]œì2!–® ŽÓò0ïfÇϰH¤Ô(cA9²q–Œä—”ä L^%JŸp¯ô°{,T)ØÝß/œ: þ~Ö�DpAcû:tèÐ ¯Q´DVrï—Y o„LŒãRØxG‰ËÇcJ,·%‚²`¡@„b!Æ×|Žã¼×÷f”GðTÖ±ÃyrçÖLÙ4#]JóÒ%Ι"±> ‰ôI¯0�\Þ@‰\�à/6Ô»X)ë°džyÓàúñ¹g�ЬGÀgÓ¡C‡Ž$rxHóa¯$8+Q2ÙÍ�VS\¼7]]võË«kŠ ¾69- N|Qeøl©�°Œê(•G&ä+éŽP�\\ LÀ�Ãón·žäÛ†2Q×ð£oÂÌDžÜŸÎ³·ðˆ ‚‰rÅÒD™?*YÊ S¥ ;E²3’%#-I’!·'Qâ>‰2{}ÑûàVˆ™yPІì IâY ÍB"ùX¼K:„@â)�h%� çÄËÊQY\ô[‘u “�ëСeq8ôCÁ~ð$x|þŽQÌêÇA„¡Í�Bs…b7³ …"ytwª\»*YŽtKI¶W\Ÿ8]>”«|iÄ%ø$:ÎÇÌ#4ÊË죷‘}°yîV�ƒ1‘•.½ãŠ:Uî(bù*ÕU£@²\v™Ö/QÖŽÍie‰� �T$:tè0Å1\Þ'€4„Å#“q a(dÒ" î�\µ(\æ‹—Á¥I�…!�ØAh;0¾ï P¶bæ�É+³÷¡R[æá©s_Èå‘Ù�f¤7ÌQ ¡ú%Ñá�t‡]I£¥)Q²hhF[D1ϦC‡ŽNU¦ò‚Íà9ðW �a&øS%, r¨KŠÒ²ib¤Ü¶2¤Ír§y)Ô¦I.Vä’ðH7Gq/ qS=eAÔ÷x ™åA‚û =Ë‹2VŠ\†,$¦€ÍéŽÞûˆuäˆYš#’¢¨�\O˜L(õügÕè¬Sm(‘ÿ‚ ›::…DÀ£*ëh*åÙ©ÕÆ^/I.§Ì+Û¦DÈu‹írg Ëãæ¡Èß·éС£ãÅ‚‘ùgÍ–îœáÑÀFð7 Íapa:äºÝõtKD¤K*³0é-æ„A!‚ãä…Ô�ià=!r ¤qdA´l™â•éÑ­*ÎZ®ôŽÊÄ‘ìÞÚ³EQ� µfKVá5ÎÃ2ÅQDq¨ì õÝ÷HqÄP�aX¾CÖ�kk‰�›A{]P¥C‡ŽOžÙs68œÿâÝë¾÷èU‹zÜs`vÈÑ5c|fTN™=$çåff dfU¶Tægª£ÑÅÓÇî�Ã)y© 2(/^Æ”ÆËäŠx™Ré”iƒ2dz•_& È’!�IÏ(”(w@Â]ÜÓ€E¼€‹ºêU4• ùy$aÜ�žˆK¤’øs1®kÈ‚÷£³aÞ ÷~D8 ÅãHTc»��™ÈøR�¬Ǧz{0æC‡ŽvÈOÀD°,GÞ}lû‹7mŸò»Ç¯]ò»­sþeËœ�œ²j ‰ff`ñåBÞBxÔ=µôÒ‹úeaéÌú3ff¸i�G´È"•™EϨ\nÄÏåq%h�wúƒ±ó\Ž4U¾j‚Dì2©Ì×N™9rl:tèh3yüÀíàoà”ÉIãñçwÉ‘5cä¶]ÓåÀò‘²xLQ³%2ÉIIS¥ ÂÃŒ GTN½ %{ö#�-PhpSÄÌ(0zËuñ:&Þ òÙç1í]ô¨(G¾$9â)„¦2®Äýßö“ù;ècÓ¡CG«Êã"0Ü~ ¤.îØ;S¶Ï«’� ‡Èì!͓Ȕš2|É-(ö ¸è·D£ÙDx®;ÀMŒnG²*c5™�þ¸SØ#r²ý$B~.´éС£Ååñ5�n¶dÒ~uûjÙ1¿JVL(m–H&Vú%.Ö×"Ù�@Pbbv�ÅPÓÄ2ö€¨ „Í¢<-Z0âۤŽ¢¾ofFó)ø�M‡-&�Ÿ‚1à9%�Æòü�kåê ãeÞð.ž)ËFú)Œ5c³eåè,|>“¯ãy@¹X?×PÞß´éС£ÙSWa`ø£HSyæ¦å²ù¡Hš ‘%ÒÓîiLsÛú½õyË}ä-‘&`$hzóá„–Zì×�5csð”]ðõ…CÓ§ýRãAŒ_/Âó³¦à=9²`Hº¬•Å÷C8ü4F$w7oôW‡-‘^à ø'�fC‘¬�M³úËÜa¹�–Èè2LP…y›]‚@ÐìÎmÆ¢ª‰vä‰ÏáRiINañÿ2fÈ4(‡UÀÌ*(–åx~Ѱ ÊdÖ€[âæ×ø,7-ÎèŸl¼�_Ï”jˆ„ÏMxeUã2’é6:t4I ?[ÁŸ�´$Ïßµ"Ðh‰ )J—Þ¡A™H£GnÕU²!Z ͛IJd -�ßeBxwj¿D.þ³�Yàd_³\å§�QP"ËGù™yŒ)vË8d1ãJ<2¯*UFºd"„±¯ÉMógñëÎÆòøp‘M‡�Èy`øHkðö#Ûd÷’�’Hÿ¼4ÒðŒ(y0ûè™Å½ZÍ;L •@Z‹?WfƼ€…ŸÆRô9&—ûŒGf xžâX2,“ÙĂ璌÷R&ø,_–ç�~12²Ð)%)‘2<ßI©”§G³l¶®áolà‰¿:tè0wž§€GÁI ­ÅûOì�«6Mi°D†gH�O�§ÝR–�~ßåæ¾>ñMÛ£¡ MbÅ:²Ô$V«�Ë©NæzÃþP™{™$à’ùƒÓŒLƒ§üN­H4ÊT”ÂܪT<: “$Sâæ{eÇ£Inˆ_ÇI¾/œÁ­‰Æ¥W,àu¨1e­Ñ6:tÔ+�s�ÜNiM>zz7¹ïð<™3´þɨ@¦\$ #˜>Ì:ärÞ=ÞŒ“n5Õö»à.tªØšQd‚O˜'E2›œ“ ˼2¼ÀÉÇþ™±”I 5§ÿzùž±Ån â¡0rñù <ág…òû�Yq|mÞW˜!“ šJäŸ Â¦C‡Ž:3�\ð$8¤-xí�Mòƃ›å9Œ¯˜¨uüwêÀ)ÏÉ{¸7ø BpQwŠ£Yq¨£DZFZ"¥¢Žuw8Ò%UéÞ6ð¢ªlw(ü"€Ì‚=�2ì))ï!éï�å#¾7„BQäyÃøyȃßãkþ ¼—?Ÿ§Œªrâ9"Ü‘<¾jÓ¡CG5�œ’Á#ª„ÕVÇ£¼zßFùÅ­«ä—`ýŒJf%ÖÌdò€)Ë+”HW ϸºÔ8V$Ü�£E²xha¯˜‚Ö—†ÞP¨®´mS2�ßi¿)C¥©QÌ.P’¢Pª‚ øý)ÈáDqr¤ñµ)‘P~>ŸÍ8£‹Ïã=Æ÷Îè"7Æâaõ^ˆõo0¶æþˆú(“Ãàß@Úš÷ß!Çn\.�\µH82Oö..“ûga¤7[rSS$Í—&1-‰v¼„Íô ÕLo ;Å)P(S±ç(¼Æç•p²Ü”>Ǭ2Á£)ã9ˆ‡�v‰}îI©]$�Km:tèøR ßÀ@Ú‹wß.7ï˜.7íš)sÇVJJbŽo÷¨ûÇÍ��ešv½n*ÄBÞÁ°ŠAÉƒÒ ê5ˆçÌb%¥4ºÈ%ƒQ«ÊŽç8pðô7>ªÓ~G�³m:tè D²Á@Ú‹×Ú*÷],Ëf—$d?îé–ŸZ'®‚�j×´)‰ãDKYÈá 8¬å°êYMµï)ôIظGCžaj—ü:€¯9"Œï?½m6:´@¾ æ¶GëSðöŠÜv`�LÑ_"¢“�ux¬SWÕŽkoÛs®´<Š$>!C’±\h»8” Êc,u Íup’ cÀê¨f&ØÈ¨Î÷º©»Ÿ­¥C äR° ¼ ¤ áXï#×®�ÕóF‰?#K~ÜÃEQÔwÇx¯è¼V^85ÜKƒé¶˜„,ñ&¸Õ%SÝf$y¾p–¼òñ‘pãÜA©�O FÿÄkLse­%à›ÝôX÷áàw@ÚŠwÐ@æ¶µr`ýT)ÎÏ—v¯Uu\)«$¢û"­Ý@‹Ë‘„ø$IIà(o·„%0s4½ŽczË8ƒ‹;æ—ìI1z$ÿ�DFtÇi-zœ7S§ò¶6o=º]¾v¹l_1Q ròäû—»TÏ£�P"êöAM‹@ã¼}�lc‹c¼é\L5fs^mTdf‚MŠžÖ/$ rl:tt³km÷€ÿ´êþ�§vÉË÷o–vÏ“Y†ˆÏ‡«d›~7:z"^_¢÷ƒ´ÜÞ�HG�¸IèyÄÔr+¡Æ:é•iöLÊ�™ÌD¯UóýIŒM‡Žn"‘ð�ÖâÙ;7ÈÞ5SdòÈJ¹$Ä'?íÙü+l/¤DtO¤¥*fŽBžÂ›àÈäI¼õ�вÑÜ�Eyœ.q©i/lfäFʼne>žß‘|„“�¿aÓ¡£LcíÿmŽ$>®­a~l·+M�天“3ŽG†™mع�Oe<‘”¦ÆH?‚”¥Çµs�ê}ÝD,Õ³4ßy@$î7ùD’aÓ¡£‹ß•þl«ëþäN6Îýéþf”¯<ŠjGº_bOá±îZ"5KÃþ¥82Åëð±AÞœ>²ˆ@%H™Èà|· 1Zà•¢ähŽÃ$ER Å)ÑÝ®_’éä£:‡ëoh¶�Cãýl¼®'¶ttI‰|< ¤e³‘Ý|¼ãÐ",ú�AODâl²@€ºHŠ;ÕyÀb|I·/MÅ8r� *âv$‹ �qŸÃËŒ#ÕÑÌCƒ²‘¤(f Å�E%²�ª<·Œ/O•1¥¸f¶"M&öK“a…^ʯ¡OÉ÷·^oÂÞ�û&ÉÐ?ºß_ÎêŠ"Ñ¡%ò p?�Öâ™[×Ê%}º’†ºDªwœ>Ò]æ(‘xdÉŽ¸Ö>‘ÙKSæÂX‘/ý³,a�(N”qe)2¹2]¦È$3ª²dú ?O[ž?¢gf CiµK«ÕäG§=„ò/pHz#¢Ž.yííQ ­ì‡6δŽôZ.�òQ€%*åÎý‡¥T¥é Â�¥âòä"Óð°!Þš#¬™Á¥,Ê£ 1‚ešÊ¬ŒLcT $Rž"S Ši)Þÿ²`d�,_"«'—Ëú镲xt‘ -ôBB-›A@ ÌŽ²!9öcØŸ°[ÿ9j*˱ÚZ&¿i6:º Hæ¶ö�!Ÿÿr¿üæ¹CòÛç�È–e“ Š,žÂÛ'¾„µ(j!ÜU.iþJ1 R¦ -‘‘�ô$¢[EyÞp.Ìy¾ó1\Ý¿ÁÅ·¶ƒý�²ôX‰,dx‘Ï(a1™�Lè—™UÙ²dl±¬�Z!‡V�•«6L’ëÇɦYeɸé‡,&÷ÿì]TTm×åëîî.}«ìnÁ ±¥»JéîînìîîîîöíîPáü۽殿˜Pßÿ>k�u%ž{aÎ~ÎÞ'ðý- @sШ¿@Ÿ¡ƒ}s¿ãv*Œ.°0'èðþL $-°fêR×—@~‹„Ý�‰±�äí °zJ�ä%…É¿T�ÐiÉÀ1Eb‚£fPá÷§Áà> @E%“Ì”zK ™ºÔõ%‘°ƒÆŽ—”?ðñ«‡*å­ãuòÞé&X³¼~´NU$ɳþÿ ä°ž}íä¹þö2Ê|ªÄ{Éòâ09²4^ÞÙŸ#­gŠDÎIËéByuGŠ,Êv•·1pš/”’HðtŽ(ƒÖÕr�LEä1Ý¢/#8i¦ôÚ�4þ�¢:¨,¨,DhQîeAN�¬¯ŽÂï¼YÞ=9_Þ91_><·ø‹Ë;ÊZ6ÖÆ´î\�ò™ïÔÑz�€€è8¾?}Wìk†E_¾žjÞ‡¯'ÿ'Y€c=” ¼OÔ½,J˜B{ PÍÔ¥®/ˆô�Ý6j‹÷ýe“ ͵œ´Ö»§šä£ó ä³ËKä“‹‹åƒs dE]†ô:ùKÿìeGÐtšî,‘!~h-ëåÂúTyso¶ÜX(ÀñéÑ\^¿8‘/¯nO–Ï�çÉk’,D#S‡Âñ’Ö"ÍC*Â9£€Ów½l‡^3úðÄkPY‹Qˆ?¢è!ÓCÛ€H¸ó8Éš5• r}w•|~y¹Ü¹ºRZo®‘ؽë«@o6}~~ki‹~ždŒ<Ü›¯Ã0öÈ=ðh@‚�‰øuI|î@|Î`qž0 uÊŠ&Ry–à -ˆ €1h«4S—ºžpé »SÐY¯®–7�ÕÂyÔÈçW–ÊW—Óî^_‰�—áºJŽo*—É“]¿”Ò{ˆƒ{ºJsQ”Y“-×v—\ù|䃣%Àвw÷eÈÝ“òÎÞt9¼0Lάˆµ• ±º/uC§¦"ê �êŠ`a¤�+?V5þ�QH[*«­Íq2—ÂH'9¸<÷X#-7V‹ÜZÛÖðokÐS­�ÎZ�=SÿÀž lÜ ª †=`­LDä‚è‚`0NCii@÷1†÷šŽ÷>c\_j/¦)Û"BS(=-­E?; ûº™ºÔõ„‚È7aëM"ŒD`šH¤Q@g<äöZZë­5ØëÇŠ·‡2´ž<°èÑÍ û¢Å¬×@;3ÚVB\§JZ˜‹¬© —C+RYKóÈ(•{g´AäÞ©ycWª\Y'×7ÅËÊOiJsÄÕCœ-ûѱbN9NÙJ1! ´© Ô€P4`ÄAÇL'MA�¢J$±�XO+É ›N yûÄ<-�1:Ù½8•÷‚Ó?öÓåŠz#…fÃ^ɬ™c ø˜àà�ˆÃaäKŠþC¥ÅÿÃ})ûg‚€ÿ¤ã÷¨0jqÀNF´‚gÃçd ýäØ7ÍÔ¥®'H,`-¦ˆêm¤žQHËÍÕ‘öìËËdÓ‚< öóÀÃNF�°’I&Š÷$ š>^æzØJ„»•E9J}š§ìš'§7dË�A„ö¾V4BJ‹4Ö‰¥seK•¿ìk •º¤:s„„»ŒÆÉXï %#‹fÉ‚Bêªg£ÐUd`)W:]¥&D‡@¢=&‚~›*UIžrj}¾|qe…N ¹‡(eCCœØÁÁ9Zt'!(ø4“.€Cä¤PsŒ\`×uïåƒ÷ƒûTî™ ¨1+¨/ñBÔƒ¯e4¨PdzÊØ/ÍÔ¥®'p–ÈdØ»01‘�Úªd4òÚá*­  y¨Ý…£¹¼·AK’ÅËÝ[†Žž‚a+¤[=òyë/ô·«±Äw²¹Ìú/�C'š>� Rï*;æÅÊÉu™œæ¨²°;§‹µ€ämPYŸÍ‘]õÁÒ�2SŠ"${– é,djÁqéQU­œ¦a�™'sÅÁzÀaÂy‚)Ô=`<áÓxŸíÚ\—q‚t_Ö‹Ô¦zKM²�Œ—` Í÷:X‚¦h}�ÍЂۀ @”?4™R¯bˆÂJ?3u©ë ’¿Â^‡‰‰� òÁÙùLýU¨­®Ú'É�5¥R˜!S§8ƒF²bU»)A¥'"ksK œF‡Ó&Ciê%ì%Ž'ê!«+f˾%‰‘âv¢µR�ÚÈë;SIg­+õ!€øb’—ý ÐNÏ ôÓuÐà RTŸfÞ‡B³ËļÂÙòdÇ©h�¶dHZð$Ü¿»ÌË ”Ã+³�L±Œ Ñ�”'yÉR¤6»ÚíB‹‚!£€!£VÐ?d¿|žB]áùï]4þ$!POB†›¾@²öIk‘¢.µün˜˜Þ˜Lz ÙZ× ݶh)ïŸ["ÉAS¥0ÞOÂ|œÅr‚ƒ jƒjokA»ŒµÁdÌ(kÒW €´µ”@;)FÒ”á#K‹‚e÷Âx9¼"… Ò.¸Ô¦µîœÌ—ÏŽåÊò|Y‰zÛ�b> §^/€C¹R@VN�§rôÃÒT£“ºjU¤«á:^êR}$?|&"±D¹µ¿¶ÝhÆL®št?‚ÉT‹~�½f–!" !š(´Ñ9Lc$ô#%ȪùŽ?E‘ûž$Á]]*�Dë=Wÿì-dnÕ!íw)é-XwÁ„Âü±uè$\)Ç×åÉŽù)²wY&(’ÉR™*ÅIA2År‚ †I~­äé¾:Û«t*šy¶Ÿ­¸Ù�ïбdÏž"[¢¨‡]•&¶äÉå>�·kɇ‡²åƒƒYÒ˜:mEÆÁ韉E-"3¢Ò: ƒ”úRBŠF ¯…9Ž…&äKJëèêœÖ»×Vµ "0¤¯–-M ²²"BìFõêt­ „û‡#§>=G¡ÝLf[ ø(|äžtõ÷¥>fбK¥pÛÏÛû®™ºÔõ¸-ü¡SG#ÆS01FGßk3äÊŽBÁǸPè@/ah.}€„vÔÈ'—–ÊûgÊõ½Urû`�¬†SÚÒ/›!âÎFŸ'ÿ©xÃÛ�w;Ð6æb7ÎB,F�'À b)[Iï�ÖÒ 6`°µŒa-#`–c-) Ï´ž ~S,:t*áÎRí(¶æaÆJ®ÜlÓé¸#{ï¨. É’7w§Jz�%Ú�°ˆN# „ôUžœí!jO7ï+ ÈŽ©…ÌÒ<æ2SË\b ´¯* — µÑ,@ìD”ôßË;+d¾޶³IÜ¿t ÐpZ» �„Ï€Æç‹(Iˆ+ýȘ%†HPÉ’kûy‡`?6S—ºC ùì[m€$&Æ’RêÇV§@0o§ñ܇erih®…zƒ ŠÛ@•­ ]ÚQ&ЊceÙöu‚ƒ§Q÷´d»Ž�éc¡oŒA¶Î8QšáãЮ�hÙˆN×ñ˜-eUùì®=;Ýõ#¬9µ?Y¬S#9¶xRS{wªzÛºø|‚ ïx FFi éAÝKS‰=„'jCÑY©�’àc#‹òCäü–bD›‹ "JúïÅmeÒˆ6*v÷3Æ ÕLÀž;ÐG8óµ.Ø!˜+û£0¥PS™µBÝ àßö÷ÆTaë¡ÏSœÇ¾ÙìRiÃߦ–¤‡™ºÔõ˜€È×a/À†À~ {îâÖ<{8ñOatêŠ]Ú–Ï«¡¨­ƒË’Ñ #µY²™J«q:?�¨äì&TpPÎmÊi/2aà�/,‚SY¥˜ÀJ+P1^‰â¿)ˆtb£ÃXOK¦änkNda 'ÇiÍßå%!²®jŽàYw­¾vï´VÆëG ´ƒ†â ½Sâ³2u5t&•§Eç…&†ÔY3Ѧ–þÆÈ €:Ôž³Zž ê²N©b˜ñóN¬Ë—ú¬I™*ÙNìÜÁ½2ËlÒè—$ŠÀþ�ú "#&-ØC/AB ýßbIÒ_®V™r ÐѤ[¿¨h%÷`“GõQ3·ÔõxÉï`£`^° Ú�Ì3³?oOÑ÷Òö|ÒN‡–'Hàø $Û›b¤)ÓGVÀ©6gùʼl?žÐ÷ séôú,üÌ”‡¦¿s²�`¢/ |zy9*È3eYñlž�sÃgHA„#5Ð0†r ü^%1N’] âð| º–x€Ôß–3E:©­ùÎäÖ;‘©EgÁç+-ÔŸ%Õ-Ö‡Ž  £­bB}µdi%úÚrÆÈâ‚P¤ûf#™¢#mD»²ýö�:YU)«ª"¥(νÃ{F²�²Óê# >RÃseÖŠ8Ù’݆ùû &Bà�HÀADHÆ¿7þþðyõ¸ïG_á®.ui@Ä#ÜÍÊ7ÆÛvgS¦ï�ÃhÉq`i’l¨™+xMêiMeÿ c �­Å‰¼2Áí½Ñ_i¦äÏ�N»07@v΋ÝOÀ9·9G Ÿ0JÁǰÒƒ & ºôŽL^;Ú·\æ#jp&­GGšË�N$#t²8ùl¯w1Â{©¿­guɪ"/dñdÛ¡ˆ>ZCg¡ƒ-³‡P¹MA»ž�A»(T–R™n¨{ggß$?Ї µª“<™î‹ˆ¿¿•]Ò`êce}mŒøC4ï( 3׉RaŽ}”ßÿ_©[ BË|D0ŒQØH0àïÆ HÔõX‰?NÉù‘žv'Ã-àä¦àT" š-,Çë- ‘p®pò¡t憊HV•ÏBš º½NhŸ+Ýyb¯Hp%Õµൣ9†z ¨0Ò]Èpj«Ù�êBz0ëL Â+bz[ ¯;¡™ÀÞ>µ@ ür�Ò’¢YÒ�îkȈ„�¤4ÖYެLSêE:mï¡Ð®ÓvÔ"²x¦ÃHda»çqtæeqθº ^»Hå¶½�dIAé�D_ŽT�å‘ÔÒtYI´“l®�D¿dFH{'Èþ%‰¤ÛN®Ïd„@á÷ hÒƒ×ÀV£:z)Á•Ð<ñv62ùðü¤¢ÆÈ© ²0?ÄN„KÛçÅÉ)ÜCWžÛÇNjےí5b‹ˆ¤��@¢4X„ã Bʧmå6Ì‚4òÂg õÀ%e]—AF]ë*t­fD�Üu‰Òœ‘3R4}ÀZëq1ƒ¦Éã•E:¿ ­çª´]y�b\Sg·«ë±’Â0'‹‹8ußûoË Dv¤š2B&¡}…é&Í)ôEq}�ÀDIò³e6Œ7&ä�ú°—Ô {‚ÊNüܵ•a§W•Í’-ô¢‚Æ-g7æÈñ5rug!öVH€yõÐýÞ\ Y'òñ…¥ò2„Ü;׺¦¡¼n±¼r¨Nò#�á@˜þ `„3�êÒsûôDû@²®Ô[ìG½Ð�pŠ!µP' °†*ˆÒ¥—fÄŒ&Šì¥±®8˜„ 3GîhQZ�§·Þ½µƒ¼¼°wˆÔ¼·¶@� ºˆQ@òqšÒ%€Â=LîtVÙ¢ÇHÔ¥I9 ð>àÔèlAG>›ÙKFHÏeñ¢8ït}é-¶‰ñ˜Ø§Êš ±¼¢eA}¨h ¸@µ…�SwÕäM}aS]_ŸÕµ¤0Q Á…ÕáÇV§É¶¦œvÑ‚þô9¶6b|—S…IsÅxYÃyPw©Âý€B$øuö™}ÔAD²¶Ä› œj{龬E�Ài %€À”“®)œ,iB´Ca橇…nÒ¶QT.¥‰ž2fž#X{¡Ü?˜F Í�÷k 1FÁbWöx•u_êR×£^sݬ6µó†§‡ÃcÔP—æ%ósü@ÅdÉúšp¶òГâj]T:Í\×ÏVœ-A%gÎt©O÷AsCj' Û*3 È'@Ïð`³Ãy˜Å�Ðb,x<¾Ýp&“ÞBý­óu&kåèº"Ò}Æp¨Ð • Êë´öôêÝY[0±r·€6�ÕÒ4d±(B�ŽË„Ž¿OüÝ‘ò]Èi‡ú FïÊÇšd@)sÎLIB:±/"ªI•‰Ð€ �°/€“¢¶’öû%²»°Ÿ™©K]�re'Fô w™øIGÎWRO(¢£ãFks¦é’â‚cS¥;¶¨ ¸¥Soì#Lc‘n©/$bO(dMF&ÄúÜ9S%oÎ4j.)v¤äV”„’>Û¹ YÞ>9û­† ¿\>ƒ$:§—\\.±Þ6Æp¬š_Y:‹QÕE¤VÃ:õÜîžÖ $77'H”ÇX:P@¾YÈ\"/Ϻ�NÅ×OÕ]…ææ&—µªØ»EoaàÙBYQAá½97X³9£<ïC©ØEѰ‡/›�3S—ºÕÊM‰þQlÀÌ�JÙt›@çŒJl%-ÙFLËe‹“îɦúH½Oõoyʨ�Š›ëL­`cÅl°´ I-›âeÀmPÈ«¿ŽSì§—–u*£ëãK+$ÎÇÖ(Nû„VHíg§fþˆ ›6f†X+ņJÆZžü‡Cd'�hD¯”Þé–¨;ûw™†«§ýñŸ2†Œ¯ÉCÄõN2ºß3Ô,&�éE¡[C×�ºD}¨Ç„Î >´�ãT¹ŸÙ\,GVçÈŠò¹R‘ì�”౜žj‹‰É4šÞBÌÔ¥®Gµgy¦#Ý·³™6ÔJ$‘pêàõ¬1é&�€vÊ„aøÓ¾0apZJ&ZQ´›,. E¦W>@%‰=¶”1½ ¨è²/ Ò'øÙM/ȃS­Jt#×­ä•€¤åt¡l«€ó~‘úÀ(‰ g‘PxÐ<ŒFB”ì="’ñ£ÿ"ŠMýW™0æ¯|é'Î�‘ªÊrÁŸ™|å+_‘ï}û›Ò«ÇŸÄÍz �¤ÉfÔ€ÜS€D0!Íõò¡z9ŽŠ÷}80d„Í�_;Á˜[ÒZ¡Êý~ù,ÏL]êz+=:äÅ»»úG›`­Â‹UîèÚKÑ[áö¡tU/!-V‘äÍïm ‘7#dŠT'{bßHñ],ì³u´‘TWG3LRƒ§s_襡�›Ï²_g‹Zë™B¹°&™ocQ'ÂþM¯«dk¡ý Ú"údf�#¶ÿ‡–%Æ…ÈÅó礹±AÚW¿úÔçwâjÛ—´dU¢'u-`Зê:³f fº§ÉŽ)1<^óœ;uoYþ1£ ©¸œüèl �ûv¶éÅýOù8É~3u©ËÔ+;!üë1~ÓWwù„CvDwOŠØÈÚB·ÜY €�æêbD”Ýe³%ÒÝÊ$oºpgŽt%µrnK1OÄûÀ­P:ÔIJ“Œº/Ôí@'éüóû¤ƒàWw$s:"„Zã>Eª BCDb�Ó¹£õK:�dÔпc ådyö™g m�dp¿ßñsfX½Ýk,g³ra)AÀÀ€ÂÁŸ]ZŽ"Ú9L!�øÐ{*ÏM–Í 2tð ù>¢¨ï~ëòío~]¾÷ݯã¾þ(Ê=Z�ý§8Y÷Rt¢GmoÀ~g¦.u™²Ë/Šç ø°µ…d¬È†xÍN½h¶È”[TfŠÈñw‡Þ¢xŸÌŸa¢È„u 몣äuD#Ûš”L.]Ý„ùo‹K£Œº4Šä³¼¢5ЪÝÁVŸÊÙöu’%¹nprÃQ?qŸâb rVNO·èc°:è!:�d4®™ÁC�ü–Ÿ£€ ‘JΨÄhöÚáÖ¬€Rk÷^¦“ÈP¹|áœ\½|Q òÚFS’±Ãÿ¤u¯6æÿw»þøúáÔ‰Üìúñú2·†™©K]&‘¯Â’`w»[‘Œ“Sk‘jË\8?‚Èîqá»;—äâöBI ™f²7_²¿=œØLLGLB£Æ¬¶-åy6q\F€yõp=z:Åu?H³F”4“UûW¥á™<ŒÞúôÖ½öÀä£ÃÙ­{CîÅy�C ޾´!eƒšCˆÎ£Ñ ·þ£å\ÍGüI~øƒoh Lõ%�ˆbnöýÐDž󑈄ÑÉÁå™:� ¤ª,Çü]l­FÈáƒûåøÑÃ2cÆ´öýËŸ}›ûmǨMÐhE3­^45�´Â,ÌÔ¥.‰­"ݵœ93˜V›ìžÛ†­J0cœíSôÝi››î;ksSE%ÌòZTÂ6(h§[öv—­ˆðL.O4¾ŽƒþbŒòÈÔ�éuaJ¢¶}z4·¥(Âá^ÐÔ‘¤³à4 žµ'¬Ó±öü×O´@ä›ßøªŒü­Ï�ãcŽÊË�•||~‰VT�‡N_ÙGß—~%¿øùÏåOú£ÖÞûëït$m£¬�GCwÍ0S—ºL4ùðLô1d?±ÅEt†ìÙSekcAmKônæxi{£S½ùpÌâÚ {÷ÔŽÞ½¹¿†Q„syG9Û›Ǹ}?‘޲±v.«óÑ˜É �Ýϵ(—×Å~é:®ÅH{¦Ó´û-‡:¤ÿïä[ßüÚÎøïùa»xߊt£ Œ­Xö.IS"-�gØÀßË7¾þU�‘ÔsOýŒŸó°¨ÄgòàG¥“Ä›©K]&’HX«¡Nó®ì^Ë^\ÈâbKÅñuÓ(ÚG¸YšìÍíaI±wô™+»*ä̦BVI#ÁФ<9¶&éɹå7ÔÅ}?©¨ÚGçF$ûa� öþ±²6@¢-À¿½']2‚l $Æ2è:k¿—~-?ûÉ7åG ¹žéñ3EcÐié‘“�$0¶ž�õ¶BýËs:÷ñËŸ[ D ÷È EÛißX[æhºö+ˆ®Lž¬.5¹iè6èñÞÖŒL–Q|G{w:?}š8F{Ú˜TtÏ›;Sr¦Sp_†1»oo–ó[KXçpco"®xô„Š–ƒÐQŒŸQf�ZgY�Þ[GV¥µM¥þvWgß­s ¤õb�L`¥Z€Ò‚tà‹kb§c £p?eÂ3ºœ+ÁöP'ì>­Ÿ|pn‘Ñ�ä Ô%Ù´»�ÿãÇ:’¾*#‡�’k×&[ö”¹R1…WÆÿÛe÷hßjèê¡fêR—‘�$Þ(ŽÏÉ‚ÎoAŽ?‡QaVÆÕuk®;Ú¿Ó2œ•Šo“€I¼�M+ƽJ>eMe$Ç·^ßS…Þ]>‚†€¹Û��(ay†‰è¶‰¨sñàì•6ʼn-íçJ®IäÆ ‘‹µ"ç+`B#˜œƒ`ZQ¼¸¥*иs5¦�d¥;œj·ÌfÜ?åÖÁc jLˆÓ$ÝiË %÷ë_~zÎ׉|û[_“Ð{,Fý¹Ý¯q›ü¢,És‘yi®R0g²€J4V…’ÅkN˜¤Î‡Û–èo§6nT—QA䇰W�yª¯Õ4LD;t:À® ¦*¯1³»B2ýù=1�°?›¦ßþ�,ZQSòE²¸š³ÑEx‡Z•Ç»£åF‘@ å¦¦¬“EJÕ)ÞŒðÐe¹-]ø…Î4àõL�ëËD.7‹\¨¹T‡k¥ÐΕ`ÊåÓcù’dgÔ};Ûôî6�LûW¹¾¯Êè@r~{ 3«¬ÍÿÙî^,FþY�@jnÔÖŽ´kaƒåÒºÙ×4[ªbgHZ€�2MÓà–9k*ß(ääÁ—œy_=Ó÷öSØWÍÔ¥.#Ét˜Q9ò$[ôá²GkpOŽÆÅIšÍ!¾(nï/Ç|�J�@òÆÑZQ¢‘³› ¡‘Ld¿¬âhg¾!qúÂkj€KsåkZÛÚn¦5· ha#H´Ë/ˆ@„çµ?;§?D.&ä¾Ç�f›Å6ù(Ø|øÄÄ šžUkD®Ì¹:WØå€I•XΗÉÑ%Qm€Wy~¼ƒµ?‡ö@GÝu0ù³¼r¸Þ˜ Â4àæ‚€V´w‘Ài#Äa|O™Ð=à£Ù�ÿ—ì›,··&ÉÒÉ ¶“oãi|èÊÀôû]ó“e-¢ç†t?ÖC½¶'w­ÜZ×÷¨Î%Q—Q@ä+°UÆw| ±“ül˜ÅufC6ç•P5‰×W)Q?ÆkXOÔ¯©àTÈêª(ž°R@)…IvØ4ÉÂ)l^v g§'øÚXàÔé4 ññóÙæbu›Vú˜¹¸Zñ}•td“[¼Ÿ=Æü†tºå À„ŽR�ÈÕE"×îÛb °,�Žrç\µTÆ;�Q·L–pR;ASKê%£)¾ÛXüë�¢½©Ÿ•É0ÄâŸb7î?|m?®‡Ló7f;ÙMø·¼q¼É¨@‚̼÷ÜÆ~ªÐDx-îöý (ˆPº€–cÿ&Ë ]ä�]©rjY¤Ô&8J¢�4 ‚²±&j29ä–qî^�ÌQÅŸŸk¼Šûû�™ºÔe ùì ˜Û”!T r؇ �¡5£-z úªåõ#5š¹êµ À¥šÿ«G¤&=íVÂ¥>Í—³*v/LACÈX)‹u£N�ì)juø„õ�,Mh�Ÿ-«+#øFÃ)›§6t~â›ñ-È êEx÷šfÒàÍUÚõ%¼*‘ʵmy÷ð|tž³ FÐL0@Ê=«¦ ’ÙNŒZe>º/Ç€²cërää†|)ˆrT”û¡ÜÉî%tdn4ˆpJfy‚‡·!â•«»*qÈiâß&rѽ˜€× Pr¶N¶ÔøÊ‡‡²ä@ólŒ7–Š˜í>ÞxÑHèý1 30k'TÞ:ÞŒ÷S#hÏr¯–K˵e×p�Ͱ™©K]F’10¡™LضC¦8’½·Xùþþ™ùò:¢Ž×4€‚(o†zÒ]]XŒ,©R¼¡}˜r»}^"„û0Pd)LÅÝ¿,“§¯Ã+³0;=Xvãß2uû`=&bšáÍÕ›¥ruw%牼u¢Y.ãó¥°X°9;€UF!›b™Æ«ÉfyâmMu”B>ÌåݹºâAGJºkU+)¯›+&+åÝcurÏ»^Q¤ŸUJ ƒAR„0J†f–ˆÖûk«"1v Í—érò 6çñ€ –¨n_fœ”_"â X0NÛÄåÿÙ4rKc\kiŒ+@Æ‘÷€BKöߊò0G´1 QG�[Âñ·U÷ ¸˜­ŽôÆq±�bœËZƒc³á�kMj8fuC@Æ £*t£§àN ÞY ¤~ØP_o_Ýe¿Išx÷íù^ˆ©ÐIö>­\ZU½)P‹ê->ó”Mõ+2@&Õ·÷ÁÞf=«Ô C‘T �0ªÄµq…TøoQt�Ô $æ\ÙF²+Zè/=‡Ç|VE+¦£ênïè±1Óùùþ¹�£v½—&í–†Û¡Rqm•äÆ,–ª„ÕÒ�¶ETOÎ`ö6b6ßÞ i‘óåÚþω€œÌ `”¬ÇRDr$T."�û>I‘Jë U¼‡D‚â E"ÔHMü%©‰û9‹^z™L$¿ Œß&ø‚,øè›�ºHÐ)NéÁ×U ’ ° á9KWA&¸k|Öè[þ -LZsN"5�¿qFãd¹\:¸ˆ´û1Ù,‰©ŽjS´uE$øÚ—I›ß¸1~W‘ˆ}ƒ½^Âk;úÆ<,ÈWDK'RŽIÇWÁ–e)õ'lXìMÀfKRqö±bðu«ÝK?ÀÄÇEŒëÓ¨{Œ'h%Ÿ »›è�³…Ÿ |ôkSiß‹£\ˆÁ¬m¨ÐZ#Å——É]”ûžÚð1Þcßpûs¢€t,+³.î_‡í™Ô /ì›�ôÔFZ²OJ+`…¦^G>îšk•ÒVðóþÀì`½4‘|¦,QƬ¶ªÄ!H„)-D# ‘(Ç9D#ç•Ó®³Àߊ’� ÌþHTÙW¶2=�… ª[˜öBJƒww¸Óã�#„Uô„°W…X�´� …at¹�p ‘�< 5©HäÉŸƒl�*-à 0*!Z3� ÷'Ó§#*™®tF)$YÇ{uXQ‡»iìfhߎ;ýñ&bèá6šB¨î‡Ó°íi¿ÇÍ¹è °Ÿý7<v 0†]2£ÊÕ½Ó%)­1Ò{c½¯ø¾Ã{“)ÀE¸á‚¾Ça_§B¿äûé@8E줠žM0çˆDØ™/•ŒB‰ˆ4¥[¥½¸CïëˆD/Ó‰ä ã�s{æÑv©)¢F!ê¤à2†™ WL2iË=ÅЩ.Z›@�§E|xÈTÚ�œ �AÙ¿êS”ÕÎ&©àî|–D°±0=„ 8–Ã$±�Õoµ÷2UBa"‘ý:(r�L¨™\ nÒ�ʹ1›ÕÁ†ÕsØÐTŠ…d¦,\ž‘ß'�ƒD±qó®ß‡Àq¼H£ =ë÷leˆæÐØ)Ålî&dÒx+D’Ãf ùúXV7Lóm@Þs*µÊªBø»‘`@ÖLÿ!µÈHððÚÉ,ˆ�¾Wš¸3z�FË‹¤¶T–¼Ÿ•Zx¯#¢Ì†»"¥"]�¬À¯ZôÒËDù �ñsæ Óq'Å�ßS@TC­ û΂ӌLpÊ»»´ ›1Äj?¸˜KAbAú†ï‰-_úlD‚�‡w¦iç7 Ë}Ûpƒç~èI�º:2 Ç1’E РxT};ödÒ„ßæ3ê]8È„‘IWî1IÇuʼ ]c;›Þ�VaÏ:ª‡‰÷ »FP{ã@~�É! "+ÿ¶ïáÙsO*¢é@Ë™’‘²ã$|M"iJÙ(—vMåóë}�y7¼ À{Ž)«½Ðº…0E…è˜zÞ)¼÷rQ‘x † HÇ2�•x|%ß·èy¡v„›PØ*bTBuÉ‚HD@ ÿaÑK/‰äW€v_¸£¾…Ízhdä¡ôSa«$¡H{JÅH%ãÒ�ȦÁ bÂÖe2«Ã¯™ÄÃ"ÜyûXï 7«dDU)°ál•„ ‹ãxNaL vä•Ú‹qžb«§ø4#’'£’ÖìgèÃ+=…§ADGñ÷� •v�„rÝeôèàn™× dARAôÁ rù$Þe ŽÜ(/í_(Å×÷¨ñ¹þ\å @)?D²ßcšëÑõ�¨ÔzŸï¥¿�TQõö*¯ ^7þ â>É‘¯"=ö?%ƒ ‘FÄuZ€ù=»¨ó¡ŸäÆ÷¶Kg~¤ ”ªâ‚„¯ˆ„x|]H"�öD²Í¢—^&É×gð® •UŒ!ª _¥´<¥P”Dn¼^MY'Xí…ÒaŠ˜ñËÑ ?Ü¡H3,Ÿ<�½JMŒW”‚Mˆw´(à-ªÑø¸›A uÂ9µ±§8š×dÂ#È„IÙY%À3í¥´’Þâ¨á (Ö�ê+<šoàïu!¢ëÈ;-$à‹¡‚¨ˆ‘Ý�•Ÿ2ˆ�‘•Y¸Ž,ƦÉ4H™¢6ÊTUe‘ŸÉ%ª<Œâ`3§êÁ1àðøöV¸!Jc„†>&(ƒ�° +ü°aÜ8¶ÃÊÓ¿ $Ëž(„ ÂÜMñso8L ZtœF‡Šê¼ö†H[!Hä¡"…Jà-zée‘lñ"á‡*ñän~uéG¹¹lD;Ís%´{ƒTl´¦ Îï�ÇÍöðÌ飻™ÞØ$A ¬ßWþS^‡*@J‰ö]làhäã†-ˆäòP×OéM¸¦'I$�™ M¥Õ8Fð{Ø|”0;(MiC “gn¨VüÍ:DsåÈÏ#2¡è‹�‘D'‘’AŽd¼ŒcrQ¢Ì�ôntóûm9§X ¡ˆÄßÀ¾nÜ$Wu¤Ð�Rf¾‡0«†EÐäb.fs, øÞAC-£èt,d¸‡ë‚2hD”»øï1…@ÊŠQ‡�x®ŽOGõU‘Ö<Ç(h7S'ÑKë#™€ø¢wÍ…øº“¾þòÞM£„ ROÜè½�Z ÅKôž°»µ’P4©€qÕ^v–ëã•À¶„wø1¾qÇʈnp”*7E( F&(³N²BnÄF^ 4ܶq#ª¹öl2Á2"£íˆP âwQ”GY/‰#ùÄj¦ÞpNMªüæ~DD_é6QÌï+="P`c„{ž`è{áõ@TA’€ÿI‘ûTÂ×Máµ�HΤúÝ`Ù9ö~Øp#`TÄ(’røÿ'‰´d�0*žF$w€ï´è¥— DòÓ@¿¯Iîö£wÎaÞz=³Ô$Bt¦«�Ñ›`/JcÖ  xBwaÂn¦´Täñ"Qi.¦½€ÑID¡þ&7lJ,…vâÜ]9He©À9Û¡á ráÏPè0¢2À …J[n$+­�ðzà53I #„r§Aa�é,F"cá¬E/½L"’“€øPjû"óê&i:— Çƒy» ®qAcÖÉ!DJ´ã¾~l{P º3½t”C¶Æ§l:z׺#eD[�Ä3ˆ‘DBØmä£ÿ~M¬*™” ÚDé&¾–øv”)¢x6XôÒËù _:wiŸqhõ$Š�QŠËt `WÑuÑë鮾²+Ò[ÆM<.T*Q4…‹0Ç£z B;÷ëGIl,Vpäšàw pèy×ßRié.¥›høHì ÷@"%’ñ‰E/½L ’?Rúˆ/VLÁ–m5„Ùžb–?ªŠ.–ºŽGdÒ]|v).A5=»®^ŒÙé“Ç…HVÃ5Õ?¬Šb‘BÙ9GŸIÄaԧاKØ!�ï�÷&ª�Tˆ]½6ŽbHº«ÿÀ¢—^&É'€ø2˜2Ú0�›eå�CìF¯ƒ±¢¦ºòãwŽ“fr™UdÙW¶Q$½{6„ •ª·Ä›@2S�()eÔ†(ÍsϽé�ý†Åsnbu7‡#”X¿Ñ+¬Ðn½ÁÔÐðO‰i¸Ãt£Ã¢Ð]•+}MßkÑK/Æê†âã@ꆳlÒ*º±›i.”Ur.IR9㤗з ¾`,W>ˆÜvôq`h–×úGÔ¼ X£°’ìNt‰Äðäó~ ²hÉ~ÊÝo…€éñ;7¼I†õá…Á�âhÛ�šò XѶ¤–‡—6IIôÚ¾GW¶”�Ll~“ÂBõÓXm…¼î.¡§ö€ˆ(ö³QsÍ™ZZ2ì|ë @?¢Ó.Ì™öCÊ�sZ<ÿ¼c…dÒxWа(Dã=à.�l‚ë,#±xšLŠN¯*!š'9 ¥âò&�É)X·\‘ÆêB´V󉄑D…†”ý¾I ˆ>ªBSÐ[›^ÝU_¨­Qô2…H"ñ7Ûü%DíIa™Mmíù‘TeT±šk"’‹Û»ž� ãW=y±„ú¬Fذ‰¹ÜÞ»5W„•\�Dênàü&ŽI8& ɤé¾àΑJÇ#¨Š¢š3Íëµ#�É"y»EÚÓ"¤7ÿ4€yå™Ç¤Dñð"š"�.y’Dˆâ赂™ è L*“E³`먰֤ÚYôÒË„²ßGþH$hX„&ð’`^6»ÌQ5ÅÆ.x‘T¼\ Ì>Œ[gBØ”2ñ&‘Ð…øôö™û¡Õ¨>¯z /<Ö&�X@&<’H:ËìzO˜^.Ü–~´$`¢ "“¢Ó+¥,f½”Ÿ�²FòN`ÀTØ|þÎÓÐz?ÜWRX*1¶ö2iÊ;×i~Þ¢—^nÉ_ƒ€ø)¸‰ßô9ÓJuiG$¦x‰ê©;rÛ?cõÖÍÓëX‚ë͆Dh$´EÁógtŸ*ûõ&j®’Hx¬�WDÂt S[vóûuɦmºíéGÇ"’J&€s‡P�°c|Eôæ,U�åtWÝ5@ ´¿lÑK/7‰d2 þD&lÄ”C ñõG¹™2ÍenªË['¹Íf?½;V÷›$¤´ ²ŸQÖûÞGÍà*Ç‚u¦¸:+ÔD>�˜Ö{Ò‘y dA0U×¶wŒG /®‡r0ÖÖB^££\ŒÎ‡¶ÚŒ“½ÃDR|ŸE/½Ü$’C€™pß½ì#ŽÏí)‰fCŒ ¹Áz¥9±(ŠžI;àÍçÎAQðúâóÄóÇ¢ƒX’ å1ŽuILe12é(Lʲßà˜¶A߉ۛ0*­TÄa ] mòzH{‘Ç"��†,é¯Ï�¾ÚÒ\piä¡"’D‹^z¹I"ß `ið^h(�¥Vñ�©l®ÞÐ øÀãŠvò‹>ö^ÕV%ÁŠ�óZº‹£ÇÕƒ ~\ŠH€xŠïÐI€4ºÐŽØè:ËX‘äVD’u|é«A3H$óð|i{áHc¢{ý& ]¦÷Zó=E „²î©¹'í¥×¤1÷læö3Æ^‹^z¹I$¿X $lšóƒ¡WÐ䱫ð }°Úó½Óg‚wv—#Jðšà�„e¿è¥±ÓGÆ›L®*ž½&ŒNZ‘0Í¢4¢ƒdâV3b냈T\Õ�\%F5•WCM‰6TLŽ@*$±næÛrûì&Î AÇ;'Ï!íæAè&LyYiòøÿL M±˜ï.Ž¢#0lRXà…&MŠûÕ÷ÂHžƒn�#6_/AdB­„e¬9$„‰#µj&·Ýݤ��’³½èP·‚œ!#÷Ø©¾¶Mðï=›ÆjLõ8� !� 4æ@X¿ "‰ÇP8ŽPVD2 ¿oÑK/7‰ä*  ”ó±ß‘³»æpf8îÖ)B£4˜›.¦.’LF-˜hSbº;Àßbfs{#"¡uü®%JÍýp‰M¢O Ö¾4˜Â;çc4Þ}Š;m~ÇýNx4!•_ØØ™yxÞh¤1ˆøÆâ¨5Ý¥1ë­ˆbz*¯„Z[î¦ß–‡ûAß‚JsÅw¾ßáqÌY¥îŠð´@©KÚÃæÃ²ó!6�*D�·ìS t¸ðä² •ǃxÍl=X�eÿ|=�Á¦YR17„©‹¹×vpdîˆ~7<·crbâ‰Õ^#ÉdL�̹º]•ýú&T•Rí n®Ž4Ü�LütÎ {B¼…¨ŽõAY–´\À{0Œ¤ñôlÑK/7ˆäÇ�N@‚ Q’…HM‹´1�á#m×Uið�ûB5u˜Âë»AXS¼õ¼8ðë"UùrTÂràæt¦¯»S¯`/Šß�Hm’@¬òVB Ñ�hÊ•ÖâX©M‹Yì‹HݵFÑKÉ߬XòÙ‹rdãt4 N”â{¸·çE’D`xèîf<<e?íä=l?,¶CÎì˜�­cjþˆoG%4w¼©*·ØGÂȃJÅSR^¾™T_%á¹a®è¦À^ˆA‡·¥!ë”Ji�…t�ÖÒKë#n ð¯�H®E,cßGsÖ è&û¤8q�²Sq9Mñž„tfû,Z»{²äB; ëÒt(p£Ñ»eÁLo5¥²Ó�i®ö"Vq$”Qxo Ê2§$Žš�ªBó>XîËÎõއ‰,óî ‘½ôr“HöìX:éÅ>¤Ÿº`½Î ‹°£'™ ß„Q fáèt #”“ Ðiï±Ç¿cÑD’ÈÞåcÈ×–5û‘(ƒGï´i¼ÇþÄ¢ÿèÕ\É>0Æ7–óÑ‘ÂRÄ7îè«K“¶Ò8Õ/ò,„YôÒËM")ÐDBÈ¢O'µöBÑô° ½Þ©™Ô¤†“Ôæ¬šñÛ*‚F‚)‰ÂÑ”è'àÕÓ_‘¨³ùÄï¤Y£ï“ÈÈ’`ÚÎ7? � HÅm¡‚½((©u£ßÄ[“›ö•X>ƒþ†LöŒ<;!ÖYôÒË ù U“ÁÑ‚�'Ô¬œöjé­38sýƱ•œ Ba¼”Õè ~bOÇᵓeóÜ·9½Ð�}Í篢2lzVÖ°±CYÀø™(Í$i˜P2œÛ@‘Nb§8ç›ÄzVÿ¨akâC¨`j }#$ pxÞ¢—^.É¿CšDF ɲac¶Â#K¶.x�C¢².o•Š” ‘á(¼] ëéb{ÅyiEoÊþUŸaRâ ™ñ†Gó®% ÙÅ�^ ˜ÇÁÔ–_‰½fÏŠ.vµwU8½YSànÍgª ¤by0�Öœ©sù$ <÷¡æ|œÞdµ–ƒDÒ üŽE/½Ìö×ÒxAVMm6g›܉ �è~‰“+ïfdÐOkŒÍ‘IÒÉÕì¢÷TD²|ò‹R’´—‘š*í"?%¤¹]´¸w×O/¯Bê-´¯¯M$)0bQ©yp ££†»L]¹ÙâÕÒ߮ʛÒV§J~Žôr�Dž"5a<;ݵö‹×dɧ߿]?¢ öiàÈJ.¤­`µe¯�v ‰ÀvÏß�Ù$ >øºÌ0|Ê,o-ü½¯áñ} Ó ·Ð_ åËjæŠÿwÿÜø›Ó<�>ª`©1Åúöj,¸pÎù³ÿ§Ÿ�ˆµµÕZ7¤.ó  ì³è¥— DòÃ@…&‹g½¬¸ZŸ®[§× ôéÀF=P~ž Œˆ˜ÞRÄ¡ÎU „o–ã^Ü¿�ÿže™å̒⥠¹«>¥Å ŽDÒ[rÎ Âpjî;«¸î:°©kØ1 6çÑшêq©ÀwYôÒËI"ùMŽc˼weÅ”—Øo{h‡e5f§E"òÿ¨äq<!B±Gá–ªââ9¿§‰$r_:ʤ1ç4]~�OkÀYôÒË "ùX‚ë€ð>„¹è"9†È$SOlþBÐ/MYÇŸ$~ÝSÍIŒ¨Þ2ëq�È :Ú9‡¾·”{à †½,Ý¥©cã=VT�LH0¾ÙÏá}_-TiqÞH}æqWDvüOÅ£”]ßcÑK/'ˆä€&·En•Wô¶Ú½ôCÙ¶à=nèå7÷ÛuÀÓèQ�³ÿ$QËè-æôŽL % qQdW)­ÀA¬²š§VÂnò¶| ˆD¥¹‚œÃ—_v°×¦…ÃÑà0‰Á"iÏ;QŽkþ1 ;Üõr˜Hâ4˜ÒÏþ¤˜Øhx|óçr;j=‡JÁ…‰@„?ËA4$2Š@‰ÞZ,!>ú%Í%ûËbŠDH$µ´Ma_ «¸T‡yGðF$èa¹owÕm©KGÚ”$²×i©½@§Üè¸ÞýÀDà9‹^z9 ´çj"0 $†�³Þ¢û.Ê�YÕÒÀ‡4‚Ñ‰š’ˆ ŒljtÓ˜ä…(ˆÖñÑ»fc gĘDr…).’ M¯G(iA]µÅÁU YŒFj@j»3¨½E"Ç�·¿æ�_³è¥×3ˆäç�^MæbÈ6%¹uË{ožZCaÚÑGB ‘ ›]¶»‰l[Hm„¤u~ï|ö³ ‰Œì)±Ou5y£bg¬âÓ¡‹œP¿N£îÁAÜèœEÓì¹' !–XôÒË�Š-Ñð ¶.xŸ oE®A¯É*9;ú|غ§žÛ g!Ê£\Ø­¿¿îË×ä8„ýÈm3$c‚óâv°G% ‰D‘‰àR”Å|°’­Pº«R¤öA¸KDRsoˆäÐÓ"ÙZà-zé5‘|ég�€#ÏGþÜ—À®õå“^D(tûÅÐ)¦¢â—ʮ¥¸õ·7Ïy›�ñ¼Å¨C¸”·Wp€%Á©Þ‘Ú FaZ«¯öN“TJË%"iÍsB&ð#½ôòÇŠ­¹cÕ÷ÔŒr}˜éÓµî‹×¨ŸƒEʈäî�´F8˜äHKŒQâ;¦)2½Õ^¬V(´‰oÎ?ï*‘]…§ÆÔÖÆJq饉$ÛwDê¯�ØTçƒ,~ßž4ÔïãœP¿³ÚƱÈfü…QÊÊ©/óèÎcA#$u Úb';šƒ‹Hh1Çò_Ô<›ÀΈ¤§æž´\`ß*µ\)÷%:òO¢dÌh¶øQ‹^z=A"ß ´�ç†joŸ¾fúË#*˜æ25ô Ylgj8,Nów |½bÊ‹üþ¸ß®ûâU{ï©‘ þ ¾ïŠ‚ÓÿN‘åÖùïÑÛ ¶+Ãã~/‘qœžÈž’®Š ²B±""i+½Æj­ê{\Ø¡Üw¿Ô§}5Bú̳"˜fÑK¯'ˆä7€þqì—M³ßT_Ã'êEô`¼ÄóÐyo“dÔl�e“¾- Â2ôKLí]þ¡±yÁGýQ;f@pþÒˆÚ9ÓvrËtëÉm3;/ZÒs~ÿ|Û¦¹ï±·0ÀÆ=žû«Y$Ê‹é§÷aÚ¡tphU�â K�9=±³,¨¢‘¡æ¼áºÑ¬Örµù°1#ójNáFä¼#×¼ø ‹^z=1Z×:ŽD¢"ôë²QÅÆYo0º8¶q z,ÞÇ\��eç¢÷dí—¯G7N•C˜u~ ¶íɧVbpÓFãá­]M•·w7áC!8b¼ìɎ݈c¨ú°À÷j›\9¼PŽ¡ApÄéÕˆ|ü´ Œ¤ŠëF�e/\~÷­øDM? Z°—¤5;¨ÄvD",÷íª¼…¡UÇ•1£+ ˆÙ{K¢”ÐîvÚw¼ë¥‰d`'ç\ô@| }¯1¶î3ty¿‹*¤·$rûL[nÜ£$q»<¼¹vèÛ $o5*o;ö¡Œø^Å­]R–´CJñ7Ž.‘£›¦ËÒÉ ¦¸ü¸V Ü—XõufûLLg\H;”`&v¹7gUZk¨¥€;RZ®3²o¤!ý0nDN°ĉk>|©ÈD/M$Sñ" â`ŠfýŒ¯ª—ÞbzëHÈ$N‰|e8H"ð8J’vÊ�èµFJäJXÀ/@4Qé/¾ ¦úÐ ÏÁU÷ÏmD¤µ8È#’Xö�@# ®y#­…ÒRxIê2Ž»Z©ÅNö–ì#Ð×" œú€,zé…�i¿—£j‡VBÍcÃÌ×åðšOå ÒWÑ;gÀ“j‹Tx„DŽd°9¯åãòA‡au¤1df˜”ß:ÀaZmy§ìÒA(¶ƒHp‚Êáw 1‡å¾°Cqùý^ŸvˆÚHÄæâõ/~\駱H½ÙDaŽ?jÇ—¨6z[‚P’O.G3Ý"xQmâ›ÛÀTZ8RmxÜ>PÕÅsï^»•S_âDØ«Ðå·öA#’à%’«$è#A!°ìb/⼑ºŒcìbwQdW–(îΑY`Ñ+¨Iä{€l3‰Â>òìKUY«*¦ ‡�LNlšJq¼,y§Ï�ˆ}t¶P(gÞqÀbè! _¦ÛÐÁŽÈíSÜLTœ"I'WË�c+¤>ý(]…ƒ‘DTg;ÓZ¥$“@�D›rX©Õ˜sÚH¸Ãïg̱ïb§6ÒœaÆ HðÛ½‚–H~ h7‡HH„*OE%Öð9I…¢új”à¢:‹’uyƒ¤]X�78SY>‹”ÈÔuÆ#AÉ2­á×¢Ìvó ßÙÐD–Ð �ì´§oÏ‹ Úh„`IšH{aÀ—ÿ¢‹�V(ZE›x ­rø¦¨•‹êkô�P`(7mðÙ¡àÞ5‘|j¾ýÇ×U§6"¦bØ‚žTM¤˜¶ö3l„+¤ÕXª Ë€P_yǨïñC¾¥¶±—ÅÃÂ4˜csw-ý�šÈ©­_ÒÙ7;ç±§†«ù&Á-´£«�3IØGò�n€F&ŒFº*oJ}ÖIG«´X­Xt}+KàËùƒT8oå¾f¾ýÀ§ÀwYô *y¸jÞæ Á  ºÔÙ#²²uþ;rñÀh;X‚«ćÀ»;E"¨fáwn8îçÏðõpŸJ(ÆéNåFï)"�±#=¹¶.x�ƒªö­üDR0»$éÄ*ɼ* �ÕÁ¼ˆ¥ÐÎ’º"�©$¤¸€²ë)¦J}&*œ“åqZ„ÃïëB¼_Óùg\ ‘¢„­|ÿ¢÷J5š +|·E¯ !’ç�jSço|Ètn×|þ šå>dEV4´�1ëTI¯¯B5g‘<�?&qàk ’êûö‘Ëõ£K<&ÆC!!Ý0 ÿÏ Á`,F!)žZ-ø°öŠ& pmXp/£ß%à¬Pšrè©ÕZtYªœ˜Ã¡{pÉ$ñøR¹µ3GH$fÃæZô ª©ˆ=fŠì ö‡ •ÅhäZÄ"ê ·N¯bâ£äaÎrH�këA&$’8£'‘Jw)ñòðÚO=A$´>9½m¦Ý8�sKœß$�nõØ"lCÏ?ćÕH0ƒ$B\ Ý‹EšÓ®1sØißZrÅÙ9ìˆb— ôI6ûÙ0ýZ“A* ¡�ÄHOq4S£&§G»ŸÞ_¢—ˆ<|'ðÀsþN$?j‚Ç–½u;‰dåÔ™ÆÚ±è=è Ë¥0a+6@ߊDTÙ£2¬y¨h„Æu­9G¥-÷-µA&¨³? /¢0ˆÚ'Tº‹ ™€ Í'V½Q ‰™ÝØ# –ùâƒÿU.º�X»±»7„DÒV@­$°J~Ëà©U€ö³ÏìL•R±ˆfg1õ m�è–FDÈTytç‹5pƒâ �­ø ‹^Š<~ÐùS[oÓÖž¦0éy|Kºk⥯ù¤ô·¾/½õ¿ä¯Dò“À€Y¶ïª¼wçâ÷e÷’÷! ÏFUÖFE">I„BÒ™ð{ GOñié+�¦êì•¡3*ˆ†ÑISf8ÿŽ–+[¼k*‘,Ÿ4�šH2ª³*ï†Hº¿öÞ†&�§D%µ‰Lm1­ÕQʈ$�ˆžZ,÷¥ŽÍΡ=íÂ:j"ð®5â=´f’^ß ÞcókÊ‚yÖ»aú^Ã:ð2Èâ¼ÑUS#ÝÕý£¿Æ•´à<ø'àý­ôwЄˆ„‘ÀMgwÍ”˜Ý3Ñm½ÖðÁ²^’0LŒ8@ìîí*ŠDÔq\ºqDY¤}i$…ÉÁ‡1ü=Ìl`ÔÒU‰(†‘ žoˆ©D² sJ²¯l“|è"íù‘ÝÏŠB4‘\IK&ˆ¤ØnzàCÿ/ù‘ 6åÑ¥)÷¬4d�"™(€\ì£`7js`\zqߺi�ö¹E š ­�Ç<ùå¿dÑÇÈ`×GFOmžÑéô �Tÿé/Dò§À�»]êópãE–ùXù‘œ‘d^ÚÀ7³¯¥²P}ÅHç´ÍnÍ=FýäÀ¨EÁÝÿ¨á>¾OBAtÂßÇ¿çßËŽÛÕ¸ì³ î�ûEÔ9þï2Ž DQ­‡Œ™ÞŠ#‘pn{sºÝp« NL K”\JOõ]î—˜êj+¹ÊRàÚ´v¸£L�rv즖¬ü˜ŸÍQÞo|��À¬ÿ¶¼HO¿N%À/‰¶ß²õ6ÞB„asó5/~ˈä›;Ø@$LiÁ²ƒFÇ7N!‰ ´p¼‰Ã®ÂŠš†šG �<˜ÎBä�ãqÖÓ#â�œ%I<…Dø3ü §§ø ÿmOɹʰ�/š¹ÞcÂù"èd'PµÅ -]âë`C"*·èÜ’3\½õ(©äÒlve“Bñ¥À±ÛSsWº«RŒÖâ+R… ™Y TfA™É¢꘣ëp,/oÌdDâiÄß੬•Þ†J_óÇÀ¿û:‘¼.k#xb*!ï¢A"49DZk�–‘`Ò]tZ ‡�@@Œ«Â£­5?ª$dÎD—+áP6�ëøð&Ü�ßBÃæþ§lœ±bG®É„%ÀœÛ¡�¢;u’¶<  +ì�ˆVx4 ¥½µi’·›³|ਭHä©8¶ésö%y©©õÛ�J"C½-¯ØºªÛ<ðzwӀ︮öyà Äu–û®šú+µîD­Aí¸yf©ÈƒGèÂ}-ÙG™ÂêeJ*ŠÇÁ‡çxމp÷ÕrcÈü�¾ÝïJ”R挑ük;ñáÞ Œð§{KqóŒ£>à™ÄtzKE$-åÓ�4Õ˜ðèm(�ŠìÆ¥c[­{VMsXûܶà=Ší}eçdHÍj÷*€Ÿ 0=ä;¡‡LGHØçÁ×·ø _%’nôްƒ¥„ìP£ÎÆÃ1kÙµîí(P̓<" a*­†Ky�“Dy¨ôÓRˆ@•ŒHg¹‹²“;‡Î�øuëG¤V£Paõ´W8¨ª«ð ÿçCŒ$öL@à‘¨¹ ÄÙÿ¿§¢Õg¡Êe‘\”NB0*¹%xñ•Øð°u<”ªü›²}ÙTg² ´.ÊE pSÖqi÷¸VBœ”Îw±ö}· ´…€D†¼ðOöU"ÙâfµÇââ.šå…§·~ŽnÙU¬[÷Võ•:"ò àmßLâ ¢-ƒ„�ÍYi*�e&‰( åfØ–…çæ¼7jZ�ö'¸nªh�ǰµ“áìû™Ü�Ù½åìØ)-�…e•ÎyœÀ> F)Š(j¡ps%T ˆ$Ë¿PQ ž3@adÒV ˆ$hÐU›'§ö®rf|vì¡ÅÖ„#ËÙœè…×Ê?éÏ$òøq”ó�¨nxéµýÀW‰ä°»ÞZÈçsº!†Ra\‡hd»7ËxIÓXHg1AôÁ4ÒUÔ?pdÔ�Ä›æ†Æ`Å…î³û–ŒzÝV!ò€ˆcsÕÄCZ¡tœÙ]zê]7Q›À»nzK5Ýlœ@Êp¤2LuÉøZ‰Ð8'ÑÄòHâá9É&pÀç¯È$…nÀJG& ´–5®�;©Òq…¡oΔ™³ÛÝKŸ“D%\ãßRñ"þÃW‰ä”[¾ZØü@ ¸“þ”“ !²{;¥E]‘…�è¨ÀbeˆÍ(D™Óy ¹zt�,øè[*ú Ö#"�%ֿʉQñz7áù�tƒ”DHO¹&ó?d• #àPº¸°¥[Ý„£ ¼ôÚÕâc¥½¿“Å%FWUó8¿–—�ç|‘H~hqÓ>žý#Ç1à)ãR«µ¼™Ö‚'‰$â³¶Üœ•^—ÈcWéEØž#:Qæ[yç çUÉÓÈ$ð@5£©îí'Ž ¦„„�s’ˆ(ä÷xÎTXÉÑ ‡G¦¼’•ޱ4E:ï‘�ê%Q=$A £ó‘¤\;‹Ï«ãCØö.ÿHP ›�ûç7á­r‚)ZaE¢Â%O™<þ/ð]ãL ß…ÒÞÏ�îê:¾6ÒUÕî £Áe_ÕGþ°ºjo®ˆµ› [¯ÎA©¯ÒC ߸ ޵·4W%¹L-aƒҔơ´“§£êdR§ŸÆnOv:wÚ�dR%=§Øí] Žêû$;íÄù*šâó±ù+]eì¨Àq"©¹j¶Nbg“Ùw€-l’sÔæ ™ OŒ½M÷-�«aK`£rœi®®Â(4.^¹œôÔçÉ$/¿xõNÜ0ŒŸ‘þ¶3 `Ãü±²µ³â~cCÆ%£øÂŽÊ¼SkouUÜ3„ófŠ‘LH–G‰G¥î~ ’ wÄÖÎ!Á1kÏܬv� 4’oÑckߊ]%°:þû$ÕÁ® }Ë�–QH#e×Á;ÿa]ƒ¤Â’];ñû©ÑÇÈÜ?É©½¨9Ûn‡l4T¤P/P)0•ãyŽ|ŒªÊ‹Ñ S^‰Š$U'½ÂØ×„Õa #Éc$))r5¿9QEd:2aÃâåÈ� ÉÏ9µ¼#!SÙß”»•£ : Î𼿌¥Â¨Šd–'úMÚ�G@2°øOF+bú kgM>_•\^S|yBÆÁ9CçŠBEB˜t?º/y‘qhž¨ïgš/¯�ˆaë(9 ›�LžÀª­]K&²›Ý;¾‡xŽÎuŠëÞ.ëÅF:F3Ü n`°Ù×qSXšËH „Gnæ­ÙjcN;¥ñß?;*îPoÉ1@ ’J§ºiI*Š\’ IDi. ¦µ T3:‰�ü˜îbép 7hU•¦"‰Ñ"^‹&õÜì«Ábí‰ƒ× _{¦ ˜V)™¼ÞxŽšL:+óî]éÙ¹ò cö»ÿãðg}×’éµc¶\ýí{òãwJiš�Ó�JMj¸"¥ñy*õeb�€ß1“T†ú;¿%½uU© «Í,‰=pAQP„‘¶€ç#1oÛ;¶Ž’›ÿå‹$òpÁ�AV{é³µmþ;Lmx¾bkxzá!F#(û%‰xŸH¸y*R±ï¨fB¨J*¦UTŠ©ÔŽPFÀL%áïŒM$ ¼ÓÎ6ðw{UŠËU�PTe“íiÞà ˜›|[Ÿwc*¿ÇMš‘W¢ê’'ž¬TƒþŸ“DT¤õù{ø>ÿvœé"#«Ôî^»ë®Ñß\l˽[²iÞGµŽ¦¹�}0Bç¿‹¨×åÔÖ\7_®^"7Ž­DÕa{NÐ{Â9ð½pfè)‰ötw|7Pl~Öu=ÄøQkoóA¤�¾.ÜìÁ$xˆH‘¨(Ši6M£‚óL2’Î"…õ-÷‚¯[æ¾m‹X?Õˆ _Ê*/h(ŒVêÒŽHù­ÞðïþûÙ‚úàŸÙzî©´°'ÑVt3D`…$:rN¬Ëm)¼UÖœ_-äÉŸÀ1_h57Áƥйo£�dšTz�H”M|C:;Ùi"ŸþZŒ¤psÄæ¤«?’ÑUáÎÚ�º”‡£’úd HÌCÜŠTFœJÄVúOk.#3\’†Š@.¯Iæ¦:>›Hªìá ‘¨‰‰™Œ¢žM$šLÝ–ã;W¸¼'Ì~—•^Hy¿Æj¯‹'Jê¹�´[Q•]ÐP<9õ3íiÞ]"òsÖž¦íˆB:½W)÷¨ çØÊô'I¢2åÜE[÷ã2¸Û†š‹˯î7²#–HVØBÁÏk�¹Àù:‘ìuŸHhóAçß”Ó+=2Ÿ¢ºT c鯲DAé/ß”Þõoâæ¤îº¹ARÀ¥XÍ ÖœÔIG©Mj#<&Ujl4¦Ú0×Ûð™À‘ŠÝׄª+æ† bãõ¹0zÁ÷H¶ü>­YR�»|Ü ”§—W�lj$™DO´åkÂpL7‘°-Kdù”WÜÝ#èßB¡fÒœuB ÂsÖN¯ÒOì*½LìAù·'äyc°û¶žºŠq)l¨ÏKÍ?³áaVøâÞ’‹;ð½g…°?¨~2鉶öré¯Ë©É�\÷›ß_$’+f D7nïÊÙÝsÍ$��D@ŒLP¥Å#ÝEÊW+Æ“^M#ËqY©§ˆDE!¡= `djl;2q°S¼ñ~—gCv{a��eK™2‘ä¹"Xü¿G4Q¤·¯ SÝô@¼c$B<û5¤†Äòe6…:Qþ«1ÔV.íÕYf# w[cÖÎ)˜AÞ=»�NÍhf¬ºF� Í!ûJÏ1j1�T¾´Ÿ^h õ.‘îš¾q¼ž�ü°c°© daŒösÕéŽß‹Æï|§¿É}@LÐH8ñäÖfF!ªO„$á¬D"Àq’ˆÇçŠÔ°éN•­Üì¨�$ª1®LÓx²ÑÍhHt!­c5Zr½77œ Y´a)²·lyâçEªÃž•a*bÀun(PDâ™ØC0¥–ïâk¥›ï]=!‹?}ÑŒO#dæƒ×"–ÙŠ®ïFCc¨ÜD^qâ^ê(µ"8¥Äb׋’bÀ™ÏðÄaù^[OãÜýÛüàZ·›�_óy(pŸHx§Á‘±WÃA#17�Q�é,Ì¡ŸVgÁW8Å”–ù½##»®¹©´‘Ø›PÜfz†DâÙ¡Iôʪ¾j8«ø7¶6Šï^‚}tbWæü$Ñ(ò¡®ÂÊ0^g¥›pãW)©*w‰„=?Shœ”è*‘hí¤øÁUY;ãöŒÿµ­ûòµ¬ðuSªQé…b�wÙ“rãøJº WÝ=,MH�Y+i^Ê¢vÒw;æBÜü‘ÀlQú[/øÉ5¶?cÁò"ù�®¿nÍmÿºÄG,–‡7wšVâ‹h„ȃD‚(D‘ˆç¢}¨¯UÞžãlãX]„ $™¾iÉôŽ=ycªáŠØl{ ÍÄþñy?BáqtŸ« ÕÉkÊ…¦–Éj4.¢±»†ÛD¢F svK¦‘T¸@*5ÙrrÏj–ýº¹o !›QµðÃot.Ÿü¢?/ ÃP­ëÇV"õ"·£Ösœˆƒ#§Ñ9Ïzx}9òÙ/“ÞÆ°u=.ò£k›p‹?É�Uæ ÃU hš.E×·™¦� …EÁp*:ü‚<”.‚÷¼'mßí"‘D!Ø’4\•Á�Ï›ã[™ªŽs©rÉZŸ:n9aâɆG{²±¯ô‘i.ûfD+î ÝHP õº¹‘æÒ°BκyNVN{CLØC ì+¦¼„ù뜾~ŠZ3I�¯BÊkgô<º}=ë$ýâh+'ÆNs5¥ÅJOm�]Óàày#’Ÿê1nuxÍ'’~a½²O‰êLku²O„Äuzj�L˜Òò˜6R °AŽQ��©$nB´ Ée¯„×›ÚšÒ]"wä¶_ªTR$BØW{�š*�ºM&Ê Ÿ¯)«ÈJ™êrOxר/½'[}fÆ¢úPXáR¡˶ï‘X’N®&‰`6 »ç³b·aŠè�§ŒHn„¨Þäg×2ø –¿ÉÏõ€˜�5K�cÌîõ£‹Y¹åδÆtB êÀY‰`èÓ�¹š»¯I ”õö»žŠ3°5P+ñ;�ÀkâÝ×I”ÉeK£N¥Åè¨ÄŒ~“29s`½·ˆ„ˆ?³7Qv—Ÿ^3ˆ~Á‚åÏDòk@; faݯÊÉ-Ó.ÀŒJò¯m‘âë[�* F¯5t­{§cys�‰j0dˆL=„ÝêÜh|6T7õ;û\­Õ×üwÓl¸ë6‘¨�cˆFÔŒåv¬ÉÀÓG‰:bxƒD6ÌùPš+ÒÄ�Iäðã?_jÄî f–ò4n<ºa2£’«‡ðèLD‚”–ÒC¼C$ÜX’…I3��{ö:ˆOÂÖ^Ò‡�qÐi2Q¢»¿¡5ß"a SÍ¢gG=¢NM$&¢¯©Hv¬˜nó�PŒ�Ü·VZ«ü:-ü„…Ëÿ‰äSï~øu¦¶Ž„L–s»fR/¹n­“©­p�ˆ—'rŠa2«³H&­9þ±¹´d7:M$uwüóÃÇê­«.’ˆ=®´²W†’v•[îWqi4–ß·­�ù®é$²dÒËJŽÑeïׯQ­ÑUý¬@!’w< €1½µýK9°êcJæ¥gúG¨‘ OÄkD¢lƹ¡ÐÁ7�ð‡�ÑUÕ�”\¿sÏ÷š�´�¦¸êSÜ#"–@s"�£}÷½*®Ðp¿’+tñdÓö–Íó?®k­Ì¨÷ïë‚‘Øý-ŸY°‰H–›M$(ÕCiÞ› �ILqÛ8EÊ“9èÊa�[$Tly�HÔ,5¨ ð¤Æô^‡Ÿk•B,Ë_}àñ»ßGC¸6ìJV°A‘†’¥&:kÀŠÞXôÉ·÷•£¡ ³z ù9‰2Ø*"ßhD²ÔɦÙo²1ñò�yraßl)IÜÎÊ­Ò¤í "ÏWlÑ%VÎIWvç~u·nt–â¹Î P›ä·eÀî Ew5o^õ1¥i.‘ho®ÓûÖõ97Çä` 9ABæLÚ³zFWÞí u68æúõu†zšÎŠÈw[°�Hæ˜/„}MBf¼†‘›á™óÆmÎGõÖ¹½šMŠŽ6$¢ƒÝ3D¢ÜdU©Å#ç«—)ñÇ”�Õ騤:ÎÿDæŽ2ê$n‰ê)Qd¢\ Xò�oâû@£ýq~÷æ¥_VS�}FâÁ ó¬9·bz[¥[š‹@D�áœÜ]{ÃfüQ V É,“‰¨/°r –òè@}ÉZ¦ zËQ3GÚ£ üDbzjËÞ^Í\WÓúìÍý¶ælÃY"!Zýíî»B¤6‘�Ý2adBÛzF¥R–gº §öåzØSü ®1bëÒ–•Óß´Ÿk‚sÙ8ÿcëíØc�èE±\TÖÛP!†ñK¬@%’))ÏÃ).X|D±=éÄ2¹´ûI%øky,µÅ2_5GœãW³ü~ã0PÅbÔÄ9O$Mé½~÷|SÍ!û e�¢ÞíÒÈ4¬Ýuy}å™ ÖË'vÙbOìê+zpµg°¥$ÐDM<ììëäìø@&’U}$žÀÖùoËŽEïIfGïø’Í‰Ž¦¶àöë¹Ô–šwÑtx06â¶ú;Îë$�otù™/«ªP,`^õ‰„ ™€¨Ü  ¡éƒ0ò�ˆ<èDò=@±§ˆ„nÀk?•‹Ho…C/)KÚá‘Ðýf�ž¨ÚRsEììÄ‹ý%�õLÃE¸ûrct’L ¤õüOȬM2#"I$¬äbº“„å¢}І&‘^�ÈÇ"â¾%¼ÿ�Úu_/ �÷6H„¥À$’g ²‚u<�ÝE§=WþKq=H¸†QsMœ¯ÞJìFi¢¥ZsÍÓI쉄ºY<Ž 0Ê�*HÐÐ0zê õµý‡%x‰ä* žÂâ�¿I2¹¾�%ÀeÉ;(º�¥�Ô§¢i£çˆ$~8}‘p]ÌÖ†ûâ‚ènCߌ_E%ÜÜ«bÍ×I5–—‘kG©ÖK4Cw•Íèk‰‘Ÿ·`‘ló ‘pžÀšé/ËíÓ+¥&Ž)‘+0Œf×pƒânâÉ©ˆ˜Ïîш„›Es:‰$ÐÒðßö”T9•u·›ié/ϳ­dddE¸[Í�LXɇ™<=Í¥¡ÑSÛaím™!"ßcÁ F"™áI"A_ J�_•+‡ç³z+>bá0�ìa—�Å<ÓZˆF8Ъ§ØcDÄÒ.^9ûŒÚd›“DÄ6Án¥Û¿úI.™B$#Ë�•V’Üøÿ½%öï Ýp‰c_s’Xûÿܼ‹D2OG%;¿'»–LÄüå%­„róÔrûh„ÚH³ÙO0"1Ók‹„}ùo}2{GòÍ]wk€ª³QIsV‹ÿt¸çÛ܈Hî|'”ëAG Å®ßH#¸SY�¶þŽwDä{-XÁN$/žžÀ®Õ%Ÿ~KÎÂ8óÒ)@)ðõcKìý·Ô¬v´Bù¯ÉD°Ü“¥¿¸Ó ÜÜwKNŸ+D‚J¶^¿Ù ›Ó{Ôã÷™(¨ðc$Ó¡9¬æ v^£¥½½ïZ,z)"ù}À ˆ7ÈdýŒ7änÔj¹I;¿Ž¢¬S‘Ðg«5çËA$fWkLWp¨î0óMï-؈ ½å¬eÊ›´ûIcXÓƒ!ï‰B,ß3¬ök¼§Þ;Á‰é®yãÿ—öj"ùA ÃD2ox˜ÿ­È•Ëš8‚DبX9¬‘ µÅɈýeÑ ’ó­Q�°G 9= Î]¸•4¥þÑÝ~ßêE"áφˆD½ÔQ#8Pü‰fŽ‘DòÝ@µWˆd"‰DÂÖO–;gVIaB¨Üƒ™ãƒ˜µÃbû8›‘Å`«³æF$$‘DF$,ým+ è<7aÈ%"y|�£&’±�ˆHÍóÅ-ºBk(�‰^à4kŒN$?ôâ €HØñ~iÿ\F$—γÝYµÕ]IÓFXÉ›?›½ñ£’@^„çgc×:7Vlj„hÍó}ãÆº›xnãH$€Q¯úLŠíÉÃá1ÚȱÂð£Ç[]ä5Ÿbw¢‰äÇ€v@¼‰½+>–ìØ�¶ö35Ó]Uoqf»ÙDˆ„yî[ôØ x±v*ˆ.l.Iƒ��âÅF =g\IDé&ŸY´¡È„Ñ\k®&Š@j2ì©»§É?Öl16‘<$y›HV}ñ&fºÏ’p΋ÛüÄÌv6$ʈÄÔh„váÉj´jà׸7Þ·¹”Þª¹æÓwÔØ¬Ý˜IbvTrG¤9sxÚd…Ž¥7à€Ñ]S&¶¡Ÿh¨Éd¶—‰„\è+™Ìdcb¹²Nað! îÏ™K$�ã˜Ö²›WØh/6pç®R@êèHs¢ïÏ)©‰wQ=IFÃå"MRñÿFW�¡Žêë@÷¯ivpŽH~ hõ&‘,øð›hR|Ÿ‘ÚâEe—¢LÊωQuɼj�TÞIFm’Õ�;÷‘?¯Méô]²EÚΈD‘ Kƒ©»1ñã*. ÃÖ×|×:Ôÿ šœ'’ç€H@¼‡díÉ‹¨äìÎì+±·Jé†EJ§$^4Ñ>þ6»”ƒj&wS7Y—Ȥ9ÃwŸWÃ]Ÿ!êoÊ-¸-ß_Ó¦Ý5C¶Þ¦="òãš\'“¯V¯¦·0IqÕ´—eߊ‘("iHç`+F$n §Æ2"¡ÐÞœTÍdô¤ª‰7"“ªZ‰ïnŠ­y>C$êýÅF×–LÿŒHôü�vtª¿ä¾¢‰ä{�ìñÐJ¶/|O².o€ÿ5Vm B�i#S[n§˜ÖJdé/¬ÁÕDÄ .(7ZWÈÿÖðQÁÝÇÒ[ñj<�îz÷?i“ÁnÇáj2y°y™LÐW2óJIú…õôÞjÊŒ ‰˜�ÖRÝìÀ ÚÆ#µ|>IÜt\"8 ¾ë)ÆÂßHo]áÍ ÅöÖ\ÿiLÔé¬nL2ü_‹^¦É÷©€Œ Df“IȬ7%éä ‰?¶B’"×A#‰6-í@"aZ+3xsØ-9®E%ÕW¬F{‰Ígk“|‚HˆúdF½L»ÑB¥Ì§£]Þ[=û÷i½•ÞâMK[¾Òâ|vT�ž#RiìiY­5ÏÉwñ€Œh5¿kñ˜¢xb{ 5ÓÒ[mD°¦@.E%FÍõ!ß�KRèCÕ[±**Á¥ ðÁRs�Îê1†zçjñ<™üÐ5Ê´C¯ˆïûV|ˆ¤¯ì«ª­K¦TÔmùA�j`J¯&Þ…¨$Ö€½ü€�ê?#ŸSõøF%$“æt ÍÇ ;4`wRc ö|MD´g–—lS–<™âZòÉ7ÑHøuñôhÞ#!S¥.íJ€ÍhHŒåÈT4Œ‘HJké¨d¤èÞ�Úë»=%÷|¨øŠÏK2ÑDâ+¨”þÖ£"¢ ½L&?â¸2ã5Yôñ7$tÞÛ²|òÙ<ç-ÚÁ«èÁ$�; ÿÆÑ Ó¤9û5‰+æŒvÇ8ÕCÂÎc]EC�Úù¨äqB/:Ý}3*i+ð™ê-õ$™P“Ó©­qGo]-¢�WÇi•^Ë&Mxmz<ÖƒLBf¼.§B§KÄúI²uþ;²>,á �± ß_„¯}üMW-æIBQ;g3"ztÁõÞ‘€âg"�ìj×H µúª³Q‰!M>+ºƒè|„Hì `w¢×Ò)¯ÿÈᵟŸÙöGãžß3K’Ž/å¬õ‹ûæÈõ#‹G~?"dàs9¶qŠìYö�,Ÿ4A¥»œš ¸mÁ{‚ˆ„bû ˆÄ%y<�4¥±‘ÑH[ÞˆFD�âJt”L|ß6…V0×|‡H^_úoòñA/ñžF§çˆ¤Šµï{,z�ÿÚ²ü».ì›,ùä2ã¦^?º˜$’{e{ó1G$ùÄ2I‰\!)§WÈåóÄ‚ã\Y�Èbóœ7e%"E&JK#µÅŠ­¤“«¤&5Üu¡]‰ë´ŒÏ!ÐÕþ¤ð©ÑšÏ(Ãq"!|Öô’�«&aÐgˆQ +·ØížÇ™%^‰H4†€w-zùÎzp)ôg3.nˆ…}‰-’qq=ç‡ƇJÉ�mRpm‹ä]ÝŒi‡@.«åÖW¤l[ðŽœÜ2M‰ôÔPVM{iÌeõ´W�›RZ+ ÇÜËQÓé7•©бí…Ú•uÔ‘µ·A8ŽæLŸ}N¨.2�HªÆ„ãQ f•ð}Øâ…´ † X è_[%I;:÷êæûŠ<ÔÜøbÁhq‡R•&n'¹ˆV$jû—’„h%i/T}¡Éð}NC+"ÁÏ8‚·ðúniÊ>é�Ô\Qb'?ÀDKö˜¥¿Zx¿â\TÒ”æ»Ï7nI•³pdVÉ-à¦ç»Ü5 `›�’ˆ^ �ç¥ìžX~sg7ˆƒ$ò$@.œpXš´]ò¥ ‚!¡Ü<¹sG^•ó{gIøºI²½(vd‚sXñuïÜõ‘«J#.¿Lç|v’H‡®ØrªƒðÃ9%-Ù®è$æÏ·ÿ÷L³&{¶V£X|·E/Ÿ&“â )C„R’¸ é®�mE Ö#:Y*'6Oƒ�Öll¡�8–Mú6� «§¿"wÏmr¿‹½.™wÎÜXZóô‡x,t”ªr`ÇМåËÝûöžb†Ë$Rmbj¬.Hò\Õ Ne�þ΢—ß�Éo•€8@(LƒåA�ϺÂèäêáùrpÕÇ(þ½(o3RÙ2÷-û…å¿•©G]þðÖ%ªiˆŒF(´·ºÜC¢£’‘1ø´ FG¹Õ¨¾Ö¯¯›QˆûÀM ®™ùÏUöû_*KÉóÀÀ ˆ †R–´ÚÉ ò (>ºa2›ÃÖ~*ak>U��˜KòmY:é%£«ølŸë%—×™Jà«–,Ά‰8 �è¨DyV=Mé->?²:Φï8’K�Ä#шÆI@—øú)™ü6Ј3PJQÂV64^ _È2áÓÛ>—5Ó_^4>Êêï÷ô—Ÿpq¨•]4’*ŒFš†5(WÇ>“HŒÆôVÞ úlAñÞ!À»¢Ç›azÙ¯ŽD¢€²èå¿Â;p Wð(e—&„"ݵAnŸ^)èQ‘HwíXô£”ù˜ãþèn˜� ‰nXÆß$™pcì(qªv_—ß|ö¦[›\ ß-›o»_ó&i(ô÷€3 äiHgÅàñ˜{�4š€yÀ÷Yôò{2ù= ÃU2AÙ0£ˆñÔP.ì�-ç�ˆõ“éÕuïì:ŽÙuÇš‚i­ÿcï€[éÞ8œ¿mÛ¶mÛ¶mÛ¶¿kÛêµ­Ú¶’*h¸çüŸy“ÙÙ{o’JùÚ÷7óÌn¶›¶ùpž¾G;ñ•ìÊ`EþEŠBÑ ê òç@ˆSHÚá$l‡:¨‡r8 [a%ü ~ïƒGÁ�™5xk±Ãõ÷âwù#´Á(¤&]É)ìƒÇù4óª*y6”€�JuÂ;)a@~3³»þÍz“óÛ~cGª7Mpe{‘+#ñ_žÌ3³•`‹µ]GǪJ3TßXП£ÿÒd%’€íð<¸%ÜB¤ÀÑ‹oá÷¹¼^_†�ð[ø7ì†Ó«d¥¾2§öªLžGÁŽ#Ü0vò[ςƺ»ö,þ®¬C鏰į›¶�sÛøýžmãYwbP%2…µy1�òÞ‚Ÿ<Ðud¼IB'l†×¬l7Îïy ¸e† á³ð'(‚ˆ,àq�øÜç™÷B¹,‡äxe’svÕIÅ�?S¥üÛv]\bC5cW%T ‚ˆD8(Ó~§¸ŠV%½gò‹¤ûd· µtÿ¿npøïÁC"ðl¸Õx+�Y–Ì#á—pÎÂø ü ¶ÂE¨‚ØyB|ká ¦«Ln ƒ~°S� …ãMüQ¼†Šdǘc%"Žžrt¥â¿8Õ.-%Ø8ÆcyÄÍPMá7^}çS×Ϧ‚´ÀOàÞ¾›uD:·†§Ã¯à„o¦ƒü£ðGx°O³ …ò8Xa°“¥ãüM6\»ÙFê¶ŒC$ûÓ=w�Êt-þRJóV%ˆ¦ð×GŒ4¤øï¢‰ÿVzà&x5<n훇±,΃{ÂKákðgØ m�*àn¬‹ð�ªP£2¹¼ Î@ìd軺ÂU¬ËÛµEûe¢» Eÿùéš­¥°ˆ“íFœ|2Aâ_ý™pÏolûžûÊØÇMF.o€ßÃ6¨„PÌ"KÂosMçÕ¨Pî ¯‚ÅÐ °ãç&;Rµ~�Ô= GIßÉô5lЬL1Ý׌±÷Vª�?ƒÖÞÙ§‘dÆ^n ÷†WÃë!AHÎ`Ro‡1¯Q¡Ü ?‡N0`ÇÃ`ùš1Û‹äyé)¿§e“Æéè’¼›žÓ±ü]\‡âtqnŸ|¤;‚HéÓä“Ë£àð!x< χÏÀMpÚ!v’¤  ¾wõi4“Ê“`øÁ›�žËKm¢yg~™ôœ”®-©Fü—gfO-•I Y 摉>�f†§ ?~ í¹„¬Þ8F×Ö±´HúÎÎìÚxwXà—Ì¿×ñ‚þwà„{([í�|�fÞ åNð.8šM&é™[{`¯Íº‘£�F3ÿ·ªg ð}õ›~žhÞÎ9ý7PbíȬ6\J°ÅØÞ³C¹·N97÷³´†9¶XƒDRƒur‚Ía´oƒO£Ñ,Œ0È~[ÄñIèÖ¥ûhz }d¶×0(ü•àY0YÇK¸.ÏNŸ[‘4ˆLRCõ69Pc9çXm‘‰œ;¡Žþd"öŸF£YqŸP÷lXÃÞªDd2P¦�û\à¿ÊY•ôœ2TÞ ášî&ôìÝfyH–TËqHÕë+‰Dº.IUÂ"ä"÷Ć.YcîíÓh4 J(wƒCõ5ÕIßY]Õ>ÐX'm÷IN™øK’î˜ÄP {¹ûÞä`­�û+E.,PEˆ®ÐP½+ Ä G¤ ï¿a_°`³{¤q„;.Ø¡Æ#6Ú[bGZNÊÏH¸?áoûHËÙEwÝ@P£Y 2¹%<¶ƒ²Â]§ÿÎ N°µ�®¬hÎ.®¡÷Þáæ–ÊÄmè£=%P,×,×"�ùšŒcxå!¯9—êÂp�ûùò)‘†T7ˆJÄ„(ä¾`Ûi‘G ¶ÈÔ°þê½"”x¹ ¶ž¶Ü#B²ÑÀÕ¦¸5yšO£Ñ,8¡<þ ýtqéοsˆ ”µ!ŽTö­SNˆ$hè¥�t¦»™F»¯ ‰R¨)²4êRaŒ´ž1$U¡^dê8/÷ós<ã Hᨼ‡×r=Þ_aÃÜëpFHˆCÄÕSºÙö–o·Ý%›l_åN‘ B‘ê$Ö_f‘Ò°¿áô3É@*ŽºÆD£Y@2¹'¼�õ$ëI Ö\<C wÆ™©Õ“û™%Å–ÊBÿ¡†#"‘*ŠAÎýÕ{ä5]PTg©"ÒU 2áúUâZ" •†麌@jD¡ös"£ho±\¨;hÕräûŠDzʶØÎ+ka�í*Þ ×ü5{¹¾Uº¾¸7ÔqeÃW�Ç3àn S$�ÊäV<ØêÞlÍñzµB $Á*³‡3Òfàx®.®HûiÛOãN¥@C¾‰¢JØ(Õ�T&ÁÖ3v°þ°„ª‚{Kƒ4Dq…Ü‹ÖR6E#ž© ¶dº�¶ÙnŽ ]OýU{¤Z¡ûID‚$¸g»T3¼A ‘AoÙ69OW[ø\«`èìòjÛW±Ãv!"òh9·˜óå¶íüR$’JçÕuˆä& [�Ç—´"Ñh4´[Àíà1ðh«Ì Ží¿ÔŸM$©Ö=¦ëÒrÃ_ÿ™®¦u¶ƒ½¨¤Z踴JªŠþÊ]Hb+UÊ>©¨\är»§x�ˆˆ8º8ò}-ßGäÁ5¡� Iëù¥°D¾Î{åuF$CÈc)è³Ã5ÍõRi»•Ê“iàNƒUf�`s”®¬D6™$šwYkK>LCA�º[5´]\î  �×T+Ri ‘’´_’®*$±DÎù~"ÞÏëU²0­ç—¥ÚÎ/Kòó€Å†ûm p¿åk†n-@$Wà^>�F£ÉQ¥Ü>�Xöq«Ì �’@6‘€T4Øq÷A÷ �{”F=… \S- W*ò:}t%Âû¥ÚhMw[9È#Áµ~¾w'C2#X”‚8¿‡ëá¯>�F£É›ÞÒŒö¿Þ†;Jg°!UÂí Óy(vC÷VËn©Hh°Ž~A†[� ÕB„j"Š,’ˆ"IwTŠsA¤xm¤kŠãbËû ÷ŽŒB"™c ™£s)HfI7ÜçÑh4c %ô3êÿ‡jÎPcª VDl{QÊ+’xÓNo£m2$Ó2q�D`ƒ8Ä<8YD‘Üë‚“ ¯D<¼Ò§Ñh4ã�µæQ©ÑÀŸìh¯ÈOUâ˜ÞsÃ^‘8­{RTYo 8I!aˆBìÚ£�€¸GÄÁÉ/®? ²ŠäW>�F£™¸Pì½èöúš�ô´Nß”aÅ[ã¬l÷n7oúKÖ4Iƒ�‘‚GqˆxD’p«p»²\¡ �8¼ÄaÔíÖº‘K Ó5Íäb�¹«ú¢‰tש¦‰`sÐvq+“HÝÖÈØ"qI ã/ñ("œ¹)áV"ùù“O£Ñh¦#ÆIÞÉ$cï2‘Þ"îÔ�ù)Ñ5]'ÂÈÄ„k·øó4èù0ž #•ï^O÷VÂn2>.Âíôÿ€ÿ·w@²ýhÀ³¶mŵm»´¶m•Ö¶müméÙ–Ón®ú&ùÎf­a�çWuJïÍLùTîÑŠpOdÁ»×/ETNY C¤ÂI}KŠâ¹ƒÖÞ_K)‘Ù†ó‹,žØ'Zbqõ|ˆV€kÀ™»" Þ#IóJÑÅŽèKb± r�~®.µ-ÊÛÚºLñqCüÜ“ÑZp'—t^�´ý#ĵ£g cqWϧ­cùâö_ †)ˆ5ÈçÑZp5ŸëÀ&w¿^¢ê•¢ –Æüq½V÷uªÎ=rjã÷F©T®û6`"â'0“F�•¸õ‰ª9 æ}Sž g²¸q¸^Þ{Z8ä ~øÌþ÷BŸë*"¢‘X©ˆ»>lúXÖ¾át9]0,�Ùã WtiGVÜöK»ÎEbFò< €ëe‰~¢$í/IT9 ËcŽ�ŸÄÇùÍ?ÎÖñóÖ»Ѩ—Šdñ“�vÎGXäõö³$ëÓ~~Rôo‡Øu(’ >×PDDãÀYó`·>-q}“DUÍùwD“^qÇ?Œ×+\ ­vŒÏ}Ñ8pmgím�…OuIç3Hš{%È'ЙîB)8]ÞÝÌoùI´‚E/âÿ¼JMÆ©zý: º?wº´]¢²žÖÙÊ }LWöžÖYÃ�]ßVDD“ÀµœÉî(ýRIZ¿B\kAÜ” âmëøe}ÿ¢âZœ;96Ñs""@n(6{¶ÄÍo;=sX¦¨TÒæ¡°¸ý׫]&Ÿ("¢ià6é>ÒÅ�¯IT-Aç'¾TLûˆTöüÉù]]CœYÔ¿Ÿ{*"¢i#Î\Î<]âæÏ•«“ÄØ:~izjã÷‡-ãβBy¾RDD¤¹¦dá³\XÙ |MKTÝ“Íìøu4Äê¤=ÇϼSÑïü²iÿÕ.›ÔŠ òQýðE¥%–HŽûª""¢Y åš0ñs‘4ŽMb™Ø~Ζvÿ1]äð½1Ï¿¿MÑÜÜBÒîg$,õ'î®®ÖaWØú3³Œá»õy˜""¢…¯»g qãè‚�¤2IýáÅu†,’>×TDD´8®nú•.,•'¨L¤yôÒò�EòAEDDKg³øŽHÛ—COÆ0>,ï. Q"ŸÛ*""€›º¨ö‹I8̘vr�SW}Ç-ñŠùW(""Zwm›ô¾4îwxÅõCþ áwd ÷k}ÊçꊈˆVæ�Ö Anl‹D—w\äÅ>¯Xá;r]$�?ŒåáD=S*ï9ý[ÿXiT}®ô9ü�|.ñù´Ï }n¢ˆˆhu¸’Ö•ãs™cHš»`â‡ä6~ÿº¾$èsŸ«ýG®¡ˆˆhí8kîåt©:âb‘4Nsƒà%n¬ˆˆh´À¦OATÑ#\& €;+""ÝSð&é¿Íù=_¸Ÿ""¢Ñ.“,l~X‚|6¢Eò@EDDcp�JÔüôh­L$,µÜDÑx”‰‹[�°�J‘¸¨öEDDãõ™ 6}Ž„¥ÆºIX4°ÉS�;ˆä¢úÞuœ�8w~ àÚŠˆˆÆ€›Û°ö}„3k:7‘¨R…‰>àúŠˆˆÆ€k˜¤÷"‰Ê«÷„oüÙFÍ!Ó„MŸl²äF®¦ˆˆhrÀÄ7….| :oV|2è}GÑäƒÎ_QùéXá÷à½Â¯M—E/wA!]¹ÝY¥ \CÑôl¶qëÃÐ…› òÛ)œ‰±L0è~º`V`ØþeEDDSz­JÒ}¢Êð3“°`‘éç)""šîËÕ.D8c–~f¤v.€ë)""â[ð0ñSœ¿KÂr:/ \b%nþ�[)""¢ÿœ�ˆ37ƒÈs‘4¾ˆ´s¡íö¹¸¹ÃŃ‹y$Í Mª_ºä�ZDDÄ™ŠÏ�Ñ(ù úÀtUG£�IEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/kitteh-bday.png0000644000175100017510000020026315224505336025016 0ustar runnerrunner‰PNG  IHDR¡upïOfzIDATxÚìÝT\×öð3\ãHðÁ%! ÄÝÝ ”º¥îNênä…zRKxu7#u/õM½!…Iôü÷ž5'ÿÛÛ2À<*óýÖúÖO\6j÷þ§s%çĹ¡{…Í”)FnÍH™Aù™Òbì? > ð¸ÝýpuHé³’Ó÷â«viʨHtn ¥‰ è’¼ÈU>*'Ì Ý'„8@9K€Ûó¡¼Jé ´{òNU>úøÌœ×n+¢+:) V. wnIÝÅ¥£Ïñ³­%TG9A€Ûó ¤HJsŸ˜¸wm|d¯£"òÊ×fûîýœZ}uWæn{ôãCY{"xî·ýÿ]*€à@Â^ŠTáÕŽÏ´ÙûÝ÷ßZ} qAÙVCõºÃ e\@¯_Ÿ\©/Ÿ]¥Ù{yŽ?B¨œ-H…ÅQ )媌ü—¯®×Q¿+oúÍVD%VÈsÕ‘Ñûõtó ±ušòùвžr"%\80ƒRÆeÇú߸~—�m¹V[qUð)8mù¼w[Ú.~ÏV@åjûÀ/J–m«¬ˆr>¥˜r€Ë(ðØ5{T õ¿¾X­†¶pÑðŠGÐ#Çï¶•O=e‰èDåIJ“Ñ`’A删Ñ2¹ßéeò•jh•KÇwÞ’fUDÞ¹[ø³É©Áö¶ß*))À�ÊÛì9HZBæ·¯zÁïWf=[}[î;M·æ¼}ð”¤uÕãC—=p¦ô0zIµ=§Šˆwx¸·µ€î>cxµfûm°p°í¶ŽV=mCü†ËéC ÷ß”S¶·dÂ'²³,�;ç€�Ù_­Œ•�gCã‹W'mµÐ?àÀS9È'ªcíè'Uù8•#G^ù£æ˜u‡îµ�òœA7 aØ „¸˜²@[J”Ý–�u´íÖb¯lÎMÛX59<ïÀØÁs¥6¼m§-}üûË)áù2‹¾;<(Óú}Zq5ñ5'œ�f¤œék¬;6þÚU<¼%71ly£®lê(?èR­J' ðxm í›:¤`]Kjº%ç­½7ç¼Ùpiæ#û&†­h ô课SN™#À­-¢ìêçÚpNꆋcϨÔÏ‹”ö„õ÷œ—ç÷³·‡Q_BÍéÛ'HN’/OK.iÔ®¬nÎÙRKöiÊhƒûnÓ@*åSJ#¥ÁV [)Â>3ÏUs³úÉÒ ãåø¤ÀFUBý¯»]K1åbƒ0|5Ð;BN¦m¹óӨіÑM–²=´Òj²•Qe„·Ó‡2Y³µö\'OQ ò÷6½Â´é‚‘ßÞ;ºqÕÄ�ü;m�'œ*û]}‹¤Wu�(‡b¦C©æ²™¾²]Íé´äÿÔÒ*Lòw¬¿ng¥žRÞÉÖØ“QT,É ¿º+£­bÃèª g��##|xõ´WÍ yYÆ«ÕÐïšm¼ ÊÝŽŠè²ÌÇk#üFH£0Ö»gÀJ”°/Êh• ‘>²|}zÓ¾§rÚ^»>©5)ÆWúû˜–sé¨ >÷RõÐ;N™m5¤Ü⨈n¶¼Ù0„ŠÈö »4@ü([cC½eyIú>* æ]¥c[��5XšLâ1[qIßÙ Ôýä¸�¸LTn̉"Êvi•íwªpwm0S^Ü×CÞyú°¾%OÍã––k�Ž–Á¾¦ß„ý)Yé·,ÿ7UB>Óçè熖P´ä":!á¦JÛ!…¦£â¯Þ7Ðk¯‚Zm¿s±·VÂti~d=gÝš¸Æ _“ÔÌøpÁÈÀ“ΨP%\pŒµ|‚Í}T ÕëN¿õ§ÔqÑ lC°Ç@.Î~Êk”󸵳ÉÐzò¼Ð6.•…–þjÐT)âk@ÿsßnUBatT›ß›Ò7ô`¨§·öAv~e�æ?;(�PN¥DPŒÜ–�òfä O¹óþ1uª€î9sx¯ŒlÇ®•GÕ¡mø½%ƒ¢d~Hl‹f[n³n«o‡íýµêX8@>E¦Æù5U=š}P•Ðò ¸0êt…ñ•£šÑ?L6M9¢^³âœMQ²)“)&ÀlEÑàåahÝ|áÈÖ½Oætü²)«!s„¿þ´[¥•j§- €…Ë­Ûq FJ9ý(ùùØ¿iЍ•2A8àA™Cù6´ŸÇÁË�ˆÚ7)%è{³AÚÞWVP•Oß‹¯lã�š:-"žKÈš‡“ÆWiЍ²³‡Ý˜(«(»(�š£Óý)ʃª„|fΫUå3 �—õµ$>ÛZ@*WÄ¥5„xX?ã|Lñ¦(̬=�Пò€æÆ¦5­=©IE.-ËÇΗ¿Œ_þ‡j�Z(ü‚:4ߎâKaFŽ*%#e¥ÂVïQ”,ÍëzÊʪ„¸tô¹aX&_Ÿ»(žšòáW`+ˆ"ÊW” (Z ”µêžs�•¯Œ¼Œ&*ó¸±}ÞíË!Ãù„Ú(GÛ Ïd+ _ŠY8Ïq 6L†D‡H˦�‡2X6–È!CC¥íN ‰¶U�‰ã²m9@ ¥ø÷•–Sòþ4O4ýŠÓÔ¶ÜJ°­|ú¸ª„%Ä[qvK(¥t“Œ®}Ô·ÑVB^  *ººâd=t¯Œ¦žÈZH1wõp@™³×„ô™yõj5ÔHIuÙ­|%ôþ˜9Òl0ÈÜS ì–Ðʇnî ÏUUP€+JˆsYl ¯†¬E¤_Üy•4pýŠ€«Jˆï˜ÀEÄ+.#¾F¤Â?›è}£I[Dù §%¤\åkD|X�OÍñ+ÿÌ[v¯gÌØ¥¹Ñi=%Aô¬„œOñȬZMUPü@o”ç¼è¤fM=+z«„8üw„* €Þ*¡9K*uOd�"ØàÊâ”&Mø]SDÕ”pÐ%Ĺjhz½¦ˆ>îµÇ<Jˆ³*$¦Ms}¨DôV ÕLÊÛ£Y q €«J¨xDV§ŸoÉœ¥dM=-¡Šœ%r°‡wWY  €ž”ß¶‡·rŠ®²¾(º[B\<^¶—>›:¥;ƒ¬Eº%tzä¨C…rðL§~ç»Ü? ²ÎàÖ® H.‡ÝVVuuÄɉuº¼K™´[7È%Àm©2ù8kÞ7ή‚FŒˆ•�Þ´œ_ù±=dõàÞ%ô\êÔí3ú‡É Áƒeahœ¼".MnNšÀå$«'®²Hݤ<™™/ß¾kŽÜÿt®,)šÇ«"~ø]OYoµ�ð ´þ¬O�ÙCFyùqñpY³}ólëjè«ì…]*¡ÚIy5dlÇ鋈W>®ZÄåÃQ« ~Ïúøï8É+©5#å-ÃGË'S&[Ë©iJ�Ý"zwôlí k%C® qå ²¾¿ùú¥\6óås¸ˆTxe¤_EñL‘Ý2º#>»F7ÈÚ_&ÔO^ÍEÄ¥ÂEÓiéWFêof�e™3»:Èj€Óq\DSú†:UD?>2S­„xkŽ·ãºûDÖ"(!UD|N{ A¾NÄåÃ÷’»{”¥Ë§å~·L;È*Ýc�Ž9tD;mêV{Á‡ ¸`¾+�m·€jŸ/͵ðü�ºîÓ­<“:E;ÈZ÷ïd€BUBO§NÑ—— ¯nô« *¤éúë@üXW?‘µÜ�YPB¼ âÏøî\6ß7— Ǻòyïž¹Úâpÿ‹'²nà~%Ä×uø€ÆÛqêÐÁ‚�ò„ðáü¿UY?Ëèç’Ú?yu�îúÐ1Ü«„6$äj‹€oçÇÔç|[m9ñá~ß%ù0k®v�µÕÍYPBjÄ7(}1mštphA[Têö=.É]£,{4Eô‹› ² „xÆ'ÜÓG>˜8NW�ßð´çäx�õU7d@ ñÓR»rܺ$>Ûzÿ¸³£\S@ŽY¯ðï-¡îæÑä‰|hÁå%ôËøå¿ë*Ìð¯0ƒ")| fgO ãíѳøÕåy>mj¥n�5NÀ?ÞUBÅ#³~t¾z?×Ëܧdõà”Ð?{�PB¦¬V«!•5PB½•OÇÎÓ²f è(!u7‡jÝ ë`(¡ÞÊÅ1)MÿóAV@ ]34Ý™AÖk¸Jh«e?ÓÈîg¿MXQ¥;¨°@À?FEr.�M©ü;–ß­[Áƒ°Î ²ÖÿsY J•ÐYQ {þŽ%ÄwñBðãÃ�dýªÇƒ¬€ªÈY¢ŠE†yúte�õA(¡žnÅ%&�’ÿ½e¥õõ­NîM×0¥@?Èzº”PO¶â¸€øâüzZD|§ßÿ|ì‚ßtƒ¬9ìA Ý=Ê" Cãì&?$Öºú©(�m-¡m›çÈÜÑéü™£ðáý kå?x�%T>v¾<&l˜uUâe4ñ«õç÷ÇÌéq µN-¤¡Ód~ ž* µýÆáâáRáŸÕgÖï©ß 2{ðóŒ ²–ýÃYPB\7 Ë´OHtˆ´œ’§Â?[‹ã²Øþ^�˨,s¦õqâBµývØð÷øû9AƒøÁw‡d½AÀ?§„¸€¸lrO-�–MeHé³*ü3¿ÏŸs¹dk®nR?™•W8jä0üyrr‚|À×~¸hþ^F@?—PÕÄ•ÖÕÕã·¯:T<»Ÿ¬-!þ÷uåoòI»Ͷܣþ¶%Ä[q]*!?“Ùe%tW¼ÅúïÉNO“OçYK'oùLëëÓëò�ŠÛ‘³Øé¿Ù4åˆzÝõ¡³EÏ™)”_Š'Å  WWBü}WnÅiËÂ:/ô­e‘œJ"ee¦-i”@ŠQ@¯]âï»ì„oũٟMIãÿpdüê¡éÒdû÷¦ûw} ðá¤ñU.dõ£¬6lØýÙÙÙecÆŒy322ò!Äõ”s¹œz°"@ ñ *—�“§ã\2¸Êy0qœBXW=?Œ[j÷;eÍ•q>þü=žêÎýè4EôvY�sæÌ9gîܹßäååí?ûì³›‹‹‹^qÅõ3gÎü6,,ìi!ĉOÝ*!Ïÿ85'tvT‚˶■Šâ’8ìlýäÕrÅàh>ùæø{ÎßqûáÃÚµk—®\¹ò»ÂÂÂö»îºKþôÓO²­­MvttÈwÞy§cÙ²eÕ£F�ºSÑW@·Kˆ‹€‹ˆW:Žî˜À¤ Ã%ù8k^W¾Ï«"'¾çÔ ë qxƒ-ZôVAAA{QQ‘¬¬¬”­­­RáBºêª«$­�v`KÀyuª„zpï¸N²&Ç333¯_¸paÕÉ'Ÿ,_~ùeÙÔÔ$µøçM›6IÚ¢k§Õ¶äœôƒ¦„Ü)úAÖ ƒ¬^”üøøø7òóó[®»î:YUU%íyñÅ%mÙÉ›nºé)!D´”Pg9/:©YSDOR´úPfS¦úøÔSO•o¼ñ†lii‘öTTTÈk®¹F®[·®J‘ƒãÚ „Þqû| 3QÆqQ^›8qâ/´Â‘ÕÕÕÒÞ’»ýöÛ%]3jBPè Jh«e‘~�u*%™ò¥< à�ÓN;­cË–-WAŒOÉ•––Ê3Î8£ÙËËë6!Ä P@ uu�µšò åSÊÇt ¡‘¶ØdMM�<œ�;wÊóÏ?_&''¿m6›/µÞ]PB]d­§|m±XöžuÖYòÝwßUG²;Å+¥õë×ËÕ«W7‡‡‡?n2™f @ 99ÈÚ¡®…††6ÓÜ�µTêêꤳ>ùä¹fÍ9mÚ´ïüüü®Çj%äT~¿¼E;ÈÊ%ôÁ¨U�S<(éV>¼j�‰‰y’VC“ØõÕKy!mj›*¢��ùå—_J{¶mÛ&¿ýö[‡3CÇ{¬ÌÍÍý"((èB!„tÊþ\BÈ-ñYRÑñÇo]Ýhµ··K¾Vtâ‰'Ú]%ÕÖÖÊ /¼Pò]¨ÈîBŒàl !çÆ¥*">§U^^Îïs¬‡ìyà�$Ýæ§iÈ�!ü4×ã)ÞºSBdå»%0^ùг½?}útëJIoëÖ­òÊ+¯l§v؆`“8[BÈÎcÏú?ö®<ªs‰¾âîVˆˆ–@Üqw4§h[œâÁRÒòÚÔ�¶¸»{íw·º—–Ì;÷¼?÷[¶á‘„„né¾ï|»ì½Y¢÷Ü™93cFC0ȹsç(F% ”“AñãdD�é|¾víZËT‰5œ Ð¼yóøøQ`ücÕ¼ª¡¡¡¡E(÷'~¯ìÚË¢Æ�KTT”¸•ª#CüRdqÄa©Y>FjÖ¬)ßÿ½ÑÐÊ!¦ß~û­HMM5RrÿQ"4áM×ÖÐÐÐÐ"4µz­G!>Üi4Óµ…ç~»�°)Ó»�¶ÕiÈó-ÙÔ¿¦Ë›Ï"F>=šãs‡âÒ»Õ()‘¿¬ôéÓLj–øúûï¿/›6m’qãÆI³fÍ~-P À›x=ô54444rS„°ý”¢€Ç\!P¦¶ÀBL2õ~§CÚÂE1©XØQ;õ"ñœ¯!òa„çéÄùùùx?–-[ö<~¶54444rS„Þð ãÅ�¹�‚£ØQh(<Ç),8?Sï‹4?ÿ.CŒ”¹0lƒôñš&3ƒ6É„€e<^0oa)Q ,Ÿ»8ºJP`(ŸN'mZµãóèè軕*UšŽç›À$®ˆÐÐÐÐÐ Ö况¨àÀ 0sU„ ŒrTt_â8Ïëïà‘©÷½Ñ�ↈ”¶›�é|ÖÿßüúÊzR„î‘®nãdnÈN ~²_¯\ÔEœŠ{‹£££ôë×ïN¹råâõÍà{`]PCCCCHU"”ã©8¼o®§ä`>`íBó ò¼Ú%Êfú½‘¾c n˜ßB™ðÅgQØ~iUu�äËSÀ!Kq蛜‘˜g4´þކÕ7•mÇqc«††††F΋�JÅ‘¹�’ƒÈeI„`.ÈÔûÞŠì(ßDu–J 5 ÌäÀ•R®Póëª^ª–ÌÞÆc–Z#Uòü‹",ÎÎÎ’˜˜˜æååµÞ!Å5`XÔÐÐÐÈIhêPÉY<Ý�dݼæ|ŒÒÅf"!œÏ�ûÑÙ/1]%-®ÛŸÞó�¸Iö¨'åó’…îué†ëæú¬ †!φ¸Ð´@Ö(Í4œe$ô„:Ž‘=rùòeN[hذáùÂ… ïR5¸à*01ç,ÛZ„xa÷¬\Tv¾#߯•W'6`ô�×m¢&„óùq{KBåj2ÄÑK&V«!ïùEÈ–Ú äLhy×7BJç+ Ee¥zÓh �ìÚIí q¦øx–®GAÂsFHcj¿+ãê| ‰I|-ïùd×®]FÓ*E¨cÇŽF4ô-V;œÇñcà)píƒSsZ„Øcs>¬íƒÈ‹öÈ®^†‘'ÞˆÇ ùz&>>«"ÄFT¸Þ2ëŽãùwýœ i- Œ/àëðܽhI (YN\‹”�  µêK™ü¤h¾’Ò¥Ú€{RŒvÅ\Å®¨+Ÿ»”ðÃûäI��Åò—æ8Ÿ_~ùE† &íÛ·ˆ�ÑGô{þüù¿TÛ[�‚*!* jhhhhº—úEJ©|,Èÿ?2ú¡‘¥~L¹üùþÙ‰†` ÈTŸPóòü?ÒÓq�Ÿ¬*ù +ë%I5gJ'ç®Òܾµtwí']]ºgøyÆØÅK©2:1+`>¯êXÝçsðàAc7§*Àª-pÊÝEóª!DÇÁàjš‚¨¡¡¡¡Eè~¼Þ^bÊT¾çâåï)Ëg55yúíXKâ¿-Žó|Ë�oP® ß÷!&&PˆÑÜwbÂ3H»y-%ÕŠgjð»èxyÑYQ¤ ‡ûŒ5\oŒbʶãsÔwéT3{�`B` n^è.IpŸ$ÕKÖ”"ùJ˜_C3ç~2­Þz‰²ëHç\©‚îçóÙgŸqõæÈIHHˆøúú¦aºöw… :£jD«Á`5PCCCC‹Ð¦˜S6/lI±É,7.hÉ�ÃÇó}Åì8UÛ!çºç³þTª‚´tŠ—Î®cÒ�Ó‚½0t» ñÑœ„0´Æ+&"µODí‘*E«ó˜G麒:l§¼8t½¼4j­,ö�$´íg6«¾õÖ[æôí‹/ÊË/¿lLáfzÎÝÝýnùòåoâ¼#à>pê?i僆††Æ�:Šéú]V.ú‡ê63" :àŽ¾—ây8Ÿ‡�³åùÀjmŠL‰¼ùå³ r°nSiZÞ^Ê(/Åó—6�ÎWL†×|M&.‡ÀòµZåcMz!ü�ÌÛ*y•@Íþ†¸:y™u!+ržÜÍ›7Ó'lsáÝСC¥F�R¹re²L™2ßàÜà!°�-NÛÖÐÐÐȣ‚n X„¯gãA Q8š�ÆT¸Ì(,ۓ鈻yçñ|ÕÀúH8©ZMÖg:Wí!=\ûKá¼Å2 —’þâS6$Ãc±v]LZ~@^ˆ<(¡•[óXÓ&Íù8}út-͈gΜ‘t¤¥¥IRR’Ô®][à–3jD‚àiùòåûïsüÐÖsÁ*`8ØL±1Ø «ƒ¥ÕÅ+Ÿ¦'r[„,/ôA^%äâ;Q мŽã<ïaEe™t¦M ·#;ÑL‘Þp òyõ5çW)ÅBy‹°ÉVkNAÍ~<²äY¦Gô4rKäXçòbºåììádLÉ…††J‰% ¢=ñ¿Ïå–J˃¶ŠIY0N瀳T1{8 ||Fq�:÷iÅ>`oÅÎ`-°@‰k/¦5;cò8Î{èµ ÞÅLƒÁýV/˜„é�QPǪ}dT-ŽÞ‘ñ5Iõ¢vB�Š<)‹‚×[Ô|`P鬌ÝqöñbWÉÑêunWeª-“`QDD(�pÌ Œ 7•MâÍ„††††�¤áüUÔ³|S¥l6D=ás�Ÿ¤^½z2dÈc­tÚĉ{ê©§®øøøìG·þfœ»||Y Ø(ð :–Ma ÅÚÚš}hɽQŽó<œŸ]²6X¯^È�^e‚9Ï é3Shž÷›$v‹ð¸™º|Í’˜„ÀcöÅܹ¶JïyOg—ôçŒhNœ8Áß™ÅíÛ·¥I“&¸äÄÓÓS¼½½�úÑ-5Úg©-Y¶5444*(zS] Þ7W¬Xñ¬á°2¦5»ººR„0«Ì¨9pÃg·nÝØ(Y«V-:±0HÓèOùÜnSÅp1¸«N£[ÙD"üøÕs›SlN½Ǩé7>âß|}åìfÂÿ' QŽ �fã|7ËÕ …«Èœ€Å&§Öš ƒÖiب)2SjÎg¤Üp¦ø$£Þ“vÐtËÁjÍŽÝ31aX£aá9×}gXýÍŸ D‡6n™±òÁxüͬÛñ¾À®¶bPÐÐÐÐÈÖ©hæ-X{� ºk¸###¥E‹2pà@ÖV¬X!Ë–-“wß}—cc HÆJc†™„……I```ÜYw=ÝM¿°n lüSvÄa°£—Ù/ôúä†44�ëìxp+>âß|]õñü0ðùgß{V/äÏS�Â�¾®U@瞦±cO J”S[s]g×Ñò”ûöÁx€> ‰FäC—�N}„ÂUw …̯\ÿݧÝéÒ¥ ­×Ùnxsñ1‰]ÜrŸ«h(Å–&)hhhh”[©tÚk¸€}ƒ»h^¼�ÚáBCáAjȨQðŽûÎ�;ü÷?ü 7nÜ0œZìâß°a(ñ^äöضÙ3­¥zÌ4™"ÿm™.Ãù9f0(øD³Ñ5  Šþ)W4)i_m¨ Ùsål7ëóÚáœ.nc­^g³ªAA�¸ÐnfÏ÷eÇÖ=F*ÝnÙÁرc%&&†??ÜD‘ª R•Ò¥Kÿ¢¦)¬²µÝCÚ à ŽSccc%<<œ±   `�wØ´ _¿~�"tðâ¹qãFóbûÉ ‘Y ¦ÖÔÇÓ,p¿Í¥x�Çͨ«NƒL úÚ»K\ÙÊŒ|†ùN3E¨±}{�(]‰ç!5Çpéý:QUÚÐH€ÉÖ<¿b'k�á„Uÿ¡u[�ä¡U»n¥&d,>÷ø–=eBÿE2ó™×dáð¥2|èH¦ÑRSS³ 1�רQ# �ŸŸŸ ²%!Dw‘’»®FúŒbMPCCCÃFPl.Dôc¦q H¬1 ÝÆ»ëV­ZIÏž=™Š;yò¤üôÓO� .„æù‹Ñ“Ìè$«©8Õû“©ž"öÝ@T²j0¨PØ�«±ÃÇD4fmäê¤äL'›µá …‡õ£âf½Ç½t€ùîN¾æù) ;$2 DF$žÅã“&M’�>úˆõ#ÜÕ›ó ]¬£“ÿËËaí¸ö@‰V– «v·4°î3ݵ6 –µÌÚ …BE10 ðùˆš¯›«·�Š{™k·gmþ“5tätl:Ý,_¯ÒNǶD�ì9F>ƒ»N�aݧJƒ¸F²cÇŽŒz€ý šaÍ-›`�®qãÆ†!�?D°¼¡À\¹«ø:zô¨¬Y³†¶í=z)ö•(TŠÿ"ùL³@Föçlôìdj‚�¥Á€ äá{‘/Õè+Í»[Jå”VŒ÷D/}½çRdp̯S¾�L¡\Ë¡áë«�`dd-PÍ�ûó},ÅpùòåòÆo°½VŒT²ˆ4É"~þùgZë!Bt9bÕƒ!BwŒÅwŠÿƒe@ ›A5p:ø–š|ðò‡~héÖbÝ4Y‚5Ú·,u+6–)A«¥»O’S5–JEœÙGò9æªÑþœ…ž�Ì *)¨Ù02�XÇçÖý:ûql°ä¡Ó� M Öéµþ> L±AzŽóßx,î tk1Ȭ#©­§44à‘ÑS•J²xä ®_�Õ t»mß¾�Öõ�?þ˜‘Jq7;BË<�$ˆ„L£š¦‹Ðup;ØÔÐÐа仂¯€©‘‚»'}„hˆ…ok!ðµÇ0ÎNe^Ô ©ÔÒú8mÎÅò•2íÏ ×Œ®ê÷Sj-KƒûxŠàõÉ-Ló@¢ûóŠSø“M�G®ÓލÜÎriE ãèŒSÿ'Çé¤÷+~OzmVð3JʨFç�õ e2ú *Z�q?3ý¦ìÖæÇOnùž4qê�ÚÎ aHrù2k`ÖQj>ŒsiàUÉ"0Ù‚µ(¸)B*TøE‰ÐMðs5²ÉÔÐÐа¸€sÁµ˜¨ý…Ñ{òË/¿ˆ8^ÇÙ`é_.Òº¶B™JO±q³§ç4å7‡+¬-7†æÏ“O’|Ê a»,?žsÚ2—1ñås¤Ò2!Îx³þ\&®`JΜõfa­V4#)E~þ–"ƒ¤™–DdÃÈç™§&ÉØ¾óùÜêG€ßÁ,[ëðyÓÕX§NŠ%v }‹]C†U¶íA¶´æACCC#/Ø\~×0$Xbÿþý¼°wŽ m\ž‘íþéâoWôO©0S€<‘º+WÈŽÏ{y1 gIÎfSF 5 ŒjúzÏæëÊÀȧ™S?SH¸ã'ã5 |¯Eûdqç]Ò=n¤i2ÀçOÑAÿ�Y÷™2h‰¥Á€¯¡‘ÔŒ.Ð'e¹û‡Ž´l"·� t7•GÇêC´Õc‚Â÷yòä1æÉ—Ñ¢¯¡¡¡acÑP*ø9ìÚ§á’cJ.ý"Œ /‹ý/µ>( Ã÷J7÷ÉÖ:Ëðñé©.3Úx²ˆ‹Ì Ù! Í"?"ZŸ!"¤ÌÌcêý8‘zVðVš”ƒí:—ð¦�@¬,?ˆÌ5¿-6íÉtÓÍW‚ÃIUcª¥Á€ŠŒR†NNNróæMS„ÕëUôˆ‘–�þ!Ôöè˜Ãl9#²Kƒÿ"¢Ãª�uG8ihhhØÐâ»àFðƒ1cÆüpëÖ-èöb_L@7YØ|§$µ\ÊÉZ&ñB›´9¬“b¡æ´áEfFÐ& ÊÈZoÂ$`-&œßf¦Ï\Júóã,„#�ŠŒ¶¬ÉºÌ–Çšf)Ž| ·�¾ýöÛ²sçNÖ†,]n˜—Çj&†ñß.\Ètñ£Œ˜êò­å'*I™'Ë’åí*ˆ“WU  dd…ž¢¿ª6dþì^{í5éܹ3gËaŸÑ¯Ê¤ð.lK ¬åÀ$0Õà Aƒ>ÃÝtzcîiV� �• ÀPIêø®uß�)&í}žeMg^ý�™äðT©¹YDZN¯YîúA³%mÆx4_1bÓh–ƒ.�úC„Jßo‚w!aB8°b§€;88ð¹åb?,ˆãè›L€QÎâÅ‹¥w¿ÞÔ(Tœ½]Ä®º½”«R^ªT³GOg©^ÃMªù»J`L=6�b¥?î¯rË]¹r…¾fÖÂ�ªüAÍ”›–5444l5Áät!„æ“UªTÉ(Ò (D8¶e­=8ìÓAŠÎ8F[t\T#iÔ  ÿíî†ÍŸ>µ,]m÷0È/šÇ¼<½½¨ô…äõ×_—mÛ¶Ñ`�é J0V¯^-óæÍ“±cÇ2ÚÁûò‚ a2–øñù²eËXCÉ„ëŒïÕ²s+©U[I«ídè’Ñ2îÝ$™µa¡L^6S¦­ž+SVΖöÏÅK·AÝi’@M-§DènvL pý1Â4t Q‰%Ž«¾¡f÷¬×(�‰à ðYðõ| ØŒTfžJ` õ1Ùw§¡¡‘#ã|ÚƒKÀT³„‡‡ÿˆÙrFŸuDFŒ°‹ˆBÔ?`ž¥£�âñZ6›2Í3AFçQdг“Ý Œpæ˜-ØC£¯]»ÆtU&ŽrgPë„¶Ü,LÆõ”—¿./zMfoZ$ w-‘ÅûSÉ%GÞ�ò¦ÌX7_ZloDC4 üU@íŠB�µ¬©µ¿(Ëv Xþ_:]¥„y³â!5öSpêiGÀÃEŠ9 6×.W'ÓÁ¾`XöáE]CC£,øLz}¬ÃH[ý‚¨ˆi2Ô:¸ì[<¶_3:1·”:8Ý“Z{ÿý÷åƒ>0 ä\¡íãQƒ6kËž�矽=«î74Ç&à‚œmËó€ƒ$°A�tŸÔG–^Z%ë~Ø!K>~ƒÑPƒ„&†0QxR¼Jš¿=EÚ êÀhîúõëü•BDçÒ“fM¬GÔâ»NVÿg¢:89ý¦ |G Ñ ün¯B mŒ,§0[ñRXXØÍààà+x¼�E�ÐÜý¢Ê êãÞU=X‹Àº7ICCã ÐEÛ¶š-‡?į�ºþ™ÒÁELJ—.M‰�ÄHŸÄY¤~}Ÿ6£5™Û²g'»CAÿÊ ÜÛÛº¾Q`6ÿ¶—ì3c€ø†ú‹[mqð¤ -ØùÅiÞ¶é8¬ #F[1 âL'ÂŒÁŸ¡‡‡ÇWêŽÿÐþñÇ*ÁY®„dQÑ¢E“à"€Ÿ÷›hÚ¾øÃ?ÜEZ—ß;ÌU¤7EÆï!Ο?ÿ.„ý,ðûÔûlTÑeøÃGDz®\Œ²m¯Q)‰í*Eq¼�ZQÄØ_sOtrìØ1>·JA=ìPпr‚� õâ‚¥~×F²îûí²õ�ý²é×=Ò¤g Š�G —¸Ör—Z1F]ˆ©º1oN”>Ãû±É€ïmèÊ®-nnnànÿcuá|Ì÷ªûtTb±\�Y_¸cð=J‘ÌZÍt/„é¬tÿ7fÛÕßËB°òù544Jª‚ìð¨âqðx ©6ŠÎ¬Y³î‰Nð‡É-£Vx˜”Ú_=Á€<¸Ý(:k¾ÝFÚðÓ.iÙ¿-EÈ;Ø—"äQ“â3ìßc¤ãà.òâ‹/*±´ `C.·°B„˜*E}ã ~{À7ÁRàãŽbÊt�¢¢ü H7E¤:VD®ç@¤ù5²ÕÆâ®Ó×ÐÐx¨‚mWp›•ÝÏàbFgR–ÑI6Rj¶ ¤9'´~¸Ävn ožú�õ �?ïbý§i¯–РžxùJÓÞ-¥Ë˜DiÕ½­Qáh [ÂîÝ»y€z† ÒO?ª}C+þ)¹¢`{eM_Œèg4ê�-qÓt„®Ã‡o¾pcõ³¢dÖœ444 ƒÀ]à ð´¢ÃàÒ£G�2걈N²•R³u À4V|BiÞ­• ya¸¼úÅ»òî¹å²ôòjyýøRZ´›õi%1íêK§n� 8S�¶†óçÏKbb"W= oÊØ9d¸äÎ�;@÷Ç<Ž2ˆ�®Ã>ýôÓ$ü.~/9ŠPÇŽÓ ò·‘ê\‰ÿ+þájCº.4Ü^o+1ÚÓ©S§1b…\L©Q¬¾üòK9~ü¸qqgß&:<Š9MD|ì)êÐ¥£4‰o.Íû¶aPl|C©×$D¼}ħ¦Ó‹Fcí®]»Ä�”gåa4“8:: .Æwðó¼¨n,Z��# ‚Aàpê6I˜�¸ ¿;9îÙ²e‹!òœÕWÝUuÙ�Ù@~•7?¨è'•ŠÛËõ›°ýæúÅÖföç`Ò5�]Øþj�½7S§Ne„‚ô£•\ûŒ�^£È…‰§Ÿ—Tus‘òOV�ReJ‰ƒ“£ ŸJž{î9ö5Ù20¼•ýB0–P„T$ô¸ø1l¸Ì«RbãÀT¸9_íÕ«×qüÞð&†‘|Î�7Gýúõcö9Ýźõ£+VìŸ= ¼††FÕÿð‰¡ŸÁïÁ]p‹íÅp®õ´ êá|6ôØðβuëÖtuÁ^ÌÕØ;tèÀ?zÔl˜jÊEgS-è‘âÂ8DE|D/Wj¿óÎ;L ³mƒ"‰Ï›#ŒÊ–-kˆÐ1•fÝ:��J© ñ)Ü÷4hpýnü��!�ßL’àÏ,'kLœ81[¿KøÙ3F/�9ªaÆiÈ ™½e‚:Z~n ýÂä„+påÊÅ=Œv:&t’€èºâàÅ¡ îŽbïæ(•]ì¤ hïê ÕýÜÄ'ÀOâÇüŒŠ(޹ˆ;wî0 ˆG[~ï�®ä$ˆyŠ8rwÔúïôZßÀǬÔœ_¸pá·�|ñIƒÑœ��è¨(i‚Çþ½zÉ`S?<6lЀó±ì�ý]YýÝň$Ö;qÃÄ›%Ì*L¯Ö®]{rÖÍÚ÷xD‰ÐJˆŽbÂí®Çð~Íš5ünТ¡ÔŽ ä0ÐF‰Í8�íí3ËhxùÓ·8§­ï¬A†�sܪúT“�ØPÞÕâË÷ú§BÈ;wlÂÍì×ÎsÐhi¤ŒþÈ—/#!•’[º>&i¸fL'ƒœ�¨ÝEÄÌ´*ëÏë£6–ñÙ„‘TG1m|åܹ2Çãbbe£È:ZfÓÇø90u¼`ÁîrB”̨Ë>Da×àš{Ÿ�¬™Ækà>%B߀?‚§‘"ûŠý=9ÜÁó5(,Hê×cc(æ²±)tÛÝFo¹ö»mœHðÊçïȬ�‹äÙ”‘Ù>VjFב è`¦îþ)B„Q Ì5"ÏKj:CíÌ5²jhhLTí«Jˆ¾ÏbvÜM\sônSîøñ¨å%m£eÚšy²å÷}²=í !2¦°4¬U$#¤Á‹†Ê‚/ʤ�fp~[íØ@ ‹çÏÁÐPFI!ºèàtz¢!Óy5`À ðµq’9.²¼°æÄD¼çwãsÛ¢,,þ7KÃUO_̈¹†K‘âµöîóû˜Ø®EhHÛ¶ò-ê�–"t0%EZ‡…I\h(Sw¥8ñ»Êá¼ãÇ�§c�œ�xúôéûþM|þù猆`V¸ƒôçzõyV544à�›¯j—ÔêŒQÌ6 ·Ø¼šãýqíˆOˆ‡�Ž|m¼¬þf+£ Ô‚8µâC‚ñ<¤ä ±b½hÔëã¥ncFB$î_(Pà¦ºÙøõ7lZì .Ajñ5Xèod$ÐGŽ‘v°P7ǺóDØþ÷&'ËÝmÛXºŽ(è�Ñ£y¬?¢¤“'OþÉÐqâÄ Öx¦Cxz#훀ž«~µ$X¾§Ââ SÇ#}ÿý÷<_�ã­0ƒ�?ÇråÊTÑZœ® ih<øîr¸G¥ân+1ºËil¬9~áÝ|`‹„6§Å`VÛ˜·&ÉûWÉŠ[%yÏ¿¥E¿6â^Óˆ–õLüp:g¸ N&¡-#hëF.Ÿ.(ì2z¤‘.:ìÃÁälš#�‚¡e·mÛ¶¼cÆk¼8)›7 ÷•Iˆ:ÓmhÄå&Xô›0êQâÃ&S\Èx^NcïÞ½†‹ëLEÿÌP«“·‚-ÿB”l Î1ß§ƒ÷›Kˆ 2ýA­ð}�«SGžFÊí ö]�Çp×µøÞN…M»Ç޲kÃËß6DoÞ¼™¿W�`l˜ ÓÂÛØƒµ¢sdÉY�êé8Ö£S'¦ñ>üðCþ¬-S�ø}äÏ…³Ê 0èÁýCµ”u÷EHÑhtDQœ£ìsºèþÑŽÙ&†B„ Õœ^=ü•çeôXê=}€$NìmDA\2Ò"œVm÷ÚžÅ;ULŒæ…ýQôç`™¤¤¤0�ˆæDŠ D‡“ÒûFЧšV°ŸBÂȽ%†»�'ìlâ„kŒ|D%œïV§N¾"Þ…çnܸÁý8èò?Â)*õ .+‚ymøFÉg–0®ÄÏâFÀÿ~qKp;¤5£p£S»¶ôƒh,›‘È9.ŒÁÕk¸ÔÞ Â|Ñ‹š×æµÿpjDßË¡ê°Ü3¯Egt+¢u¨pŸ×Ç� ‘>ãî=y\ š�F*軺0ç&%wh!€¼(^­ZµEœ4?]NÄWwø-‘~éÛi§�š‘29yQ�(¢¤¤4X6¢ƒ§>óiÍ“žù保LOÑ»ßÔüà—?l&Nœ¸´Œ½!oöwÜqGcc"ù¢)Ñëpæ™gbä éo8‘»VÑ@ •˜¦úŒ4�“¸Ÿñô!f�‚:m>W úw=þñ�¿Á�£<Ž+i£Íº0-çzÞTF3œ•ëÿöÛPë�"W C OÔU màkŽL &ç�ù�æU‰T¿›šÜ¼ ô±YH~ÇýHhpH¢¢�5|Ij«§ø¡Õ…ªU«,¹?‚ m ŸôaÜ�°MtD•Ÿ))DxÒé²Í|SÁ³ñ/1EÙfüñÇ“ßoŒ¼~á _ØÈç�;v‘`r饗6è»XOz¦zÀ¤N]¯ Í舼Œk—úS« ®ý *d“Ü­ü×éBŒ¿MävržÇu™nÚ¿˜)XÀ �:èýN" éµ§=íiÍs¶Ù¦Ù3Bµ©ÝÜ×~�t,r@ÑËÔ…¥íéa#ž�ëÿË¢YˆÕªUS<ýPٔxúJîÎ?hŸ´ÃhÐ(êÇK•žòû¢,2?›o´Q³Ê +4+-¿|³Öª«6›l°��GJ >µY]‡+Zw½B ±¨’Ú÷3ŸùÌàŸ•W^™šv儯×6Úh£ûýèG« M@Ó.Ä”ÄWìRm¸}S?;= :ë¶Ûnk†ÛÒ§F‡Õ& —ŽS?ú¿HH}è°¬™Ï¥¶‡1'E»`ñªª ÍÉ(=4ªvµjUGîÅñq¥XÝ*>ëéOú5 ð½¤6`C–†yR6á ×Z«Ùl½õ¾ÑÚk7¬¹fó”DÒ/j8U NW‹@Ïł˸‹F1ƒÐ‰XýhÊ”)NÞÀ h¹–%Nd�§Ø}ÔQGy�<èÏcÕI'm·ÝvMŠ÷ â_mµÕš&𤆍j7믿þ€.íwÇ×èÂÆÊgˆÒ G„²~Cîik$Ò—/ ]8#ŽtpxRj£¼ðÐ '4èÝ×�×ìÒÂÞö¶æ“‰Ö?™ÈZšÖ½^XŠ™°éSžò”&)ÄÛø— M¯¯ZµjÅ]úGæ¡òþBêÄ6À^è½QèwªÝ*CÝž¶ÅÍR_úUÛ‡^ûµ¤§Þœ”Ü6‰Ž^˜hA�d°é¯ž¯ñØnšm:€«sì„M>2- ÷±µMML¯�ŸW 7‡èøCcÖ�:"¹îô|B6<=CùË_0å¸Õ¢0ðô¦`Ù z­ ³ÎϧŽgók–[n¹ðdüÃÌ=½¶œÐBp³DÝdÒW?PØOñŠÔYf�P?˜A¢– G©>ù·4Aïzö_í|/€óî|ÿ-Y7oͽ}sÒŘŽî-Z˜F¢H(©ì&iÄ™9ˆ†ž¯V­ÚXrï.Úb÷wDC3²±Î´yv»aÍ©a¥¦Ù8QÏs ØL¤Zî 8¡ØŽ‹ÛëCÛÝ<‘‘ ~òäÉ˲BÃÕÛ£yTD²Ûn»5Ô$°äPØm8€I 8u’,¸ Ñ5[„âÚÖεI‘e[³^®Cª €I–ßó8ž‹ƒÞ¤!G�˜{NãN÷þž¿Ó H¹§}%íŠÁõµø&]F”yžH-©Ä“R䟀GŠ=HPèÕ ½æ+8>•ûúºÔŸš¨yãu×m6Ï�Àå>:àHób6Æ…EÐÖUÆKü¾€ÐÍ%ËP­Zµ!ØSããÊ5»€P_Núf¼C3Ê&%…M†œ@zÆp9 aCZè¼Ï>ûHÃéXo6ItòÊœlÇtˆXê˜'árhN¹Nµk&Z°Y .,®µÙo¨¾¦²JÇIÍí±ÇSmfÁ@5×`DK+®¸¢�Iz xÏ �ÂE‰‚Ð2–¶¿ ûËÉÝc;™ûûþvîëüø=EÀö[ñµ»( «¨ŸhóæŒhh�ô˜wQ©!Ñ ×÷l¤êL©µ¦žô¤'9,˜IäÞ/l"´`!º¯sóZ£Â?3^­Zµ!تñ_m±e“š“�k¦R›îh˜®wtZ*ÒþÙ1Û¢àÐp 7§U5’NCTE6¥Ù4§W©8]ïm¦“hHŠåéÙ ÖN$"Í&Ò‰´ 1’…S¼MIšP­JzÏÉ[“+`7'È&¨è^¤`†ûZ7`pú•D^Òu¢¡þ¢œpB‰‚—ë‚(èùæã’Þ¼1‘`Ÿ:ÜH›š(z"¢Wæ5zq0/=uë­›'ÇŸ”(öù\ݨ¤ã¤VS§ðz÷gRN8"¾E¼ZµjCÜÞQä\f(>7)§ùè¿#iR#ÀG�©äÔ™E ²ÀE \êKšª3j²áë ±É™ç¥H¿Oê$÷§·»iZ¥ÿ”ÇÞ:5¡'§f¤Ö2ئp ˆª?ýéOÔ¨Ee==ÇHS¦ô!P™•ô\{ø?â/-iÙee«×�╉ çR>Ñ�´!¨ U¿³0`cmð-ä\#ìBîPóü¤7_S¢ $ò^Èz°8fmµÕV;—¬ÕâÕªU¢mß'>­�’ã¡öÞ«ÿd¤LD"ÏŽá¥~¢H¬X,úñ¾©±8Ñ‹(€ÖÀæL#(Èç<©ˆXR¶ 0òþ"¤×äñ( *Ègþ¦KÝÀ&N_lÏ GÓK¤‡J ¦Wlï½÷nl¢[çtßN ¦oè¾2aw—2�í±Ëˆ¡ùÚÔ�öÊk=E=Ì!c¤ QÏÏË¢¬ðdµ¹Ô€^�ÃÏËžþôæ5YwoFHÈ÷¬™×¦çê5Þ¿úÕÖ…ÆT¬É…‘Z‘•º6i<ÑåËãÕªU[ÌÑÉ?(îZ=C÷ÛÜ�L‡;]¤.£°¯ Ï1ÏÔS¤õó(Î_|ñÅÒ[Ò\‹|<àðÁƳ²Ñòw†ÕDÈòû�7&Õ¢!ñeé¢wÒ,Å\¤ü€�¿®{׆ýæci×ì-½æ4=iÒ¤RÇé^CÝIËaЕڔZѬÜç :€è1Ë 1õ'‰.È:›ï€0ÒL5ÏKÍ Øü4M«çDy{FR§j‰"hï‘[¤véнá%/‰KSñX²žç†©H²ç}KFú¨V­ÚËŠ²öü š™(¥o8©²¢�}ÃXÃä²±Ûà1”4*öÛЗ$ʰy9áþ>I?bÀ»]9É çå”+Ç/Ò’ölÍßUÔWË!C,UšaÀ†ŒŠ+RÛÐEp" <5f²16|×Ò��­}ÔP¹#¸ÙJDtG7ÿð(¦æVŒ2ĉñQF˜áþ�Öˆ)YO�ÔíÌÜsõÃÁü¢�O~â‚uà÷¬ #9 ¯m%Š»=)ãý4€/ÙkX­ZµõŠœË´š›MkÊ0òmôT�Õ~ÚþäzÈÍ & [ãê)Ù$¾Lù:Ñ1ËW¦"²ñ÷üm½5ú…ôðì•t��Qt��Q¾­E_eÆŒŠü@EÛF$jBØgSê0IÛ$©€ØMµ$)$Ñ›kEáVkã° ¯EÑÄøñß! ŒBî5Ö[6îSSôŸOøu´L X}çGù=9‡ @³(ß!rRm6�{Œº�ÎïºÇŒ3+¯£ÜÁ^»ªW­Ú’o Ÿ,=Cs õçÄ|-*òRn¨´ØP™E>¨¬=¥·lü#Rˆ¦p mäJôbF�DíæR¬; âgôè`« Éø: EÿÖ·¾%5§¨ Fè½ÀÉßHØWØTš`»!:ÂÈóš¸N* ú•¨-xW°½¤ÌÄùY|$íyñ�ÒÜù�Ü—H £UcówÚ:…¿HT|]R€¨ü‹¡q¹—¢ ÷Õkˆé¨>ôÓŸþtnÖr[¸”2ýJñjÕª-¡½:þ¯ø½ÑÐí?þñ�ç-ÍŒ!½ré@ �¸M_òpp·ï;ÅÓì’®ñù�IN®„8—×<Ò��_—V.‹]k!]lž_' ºè½zw€¹ «ïa@É÷<öH ô5¹Vi9Ñr hEêçšÒȺ}ü�ñ‘²gú(éÌ+1Ý×Ñ”xjßÿçžÿ-÷‰å„D‰'E¹âü4&Ï Ñ¥€Ô‡~™ëü@¦®Z+añ9ĨaÎ ˆ_^èØø÷–ŽàQ­Zµ ËIxJÝzï4CÛ–ÄP¼¥±òj ]3‘ÇÁ,å5èæ®àÿ›ˆJb¨aª©Ù—õãÃiO…™wVÀøN÷X¹,L4½_èûm­¿7„^�5±.I¥5Öhž™ÃÂ;†û\ˆ _Ìzõ3¼€P+÷szjYmUòK{ÃK†‡]X­Zµ/Å/Š?XôäfˆŒ²™Î±� u8*´£¤ÃD4:ÊE"��›Š«È@o�hAíF4!mB¯�6Ú@ò‚HJC)J5rÀ³ñK­uÎÓ‘×àš\Û¨Í ’TÈöÜ€Œç嚸º˜(‰ä `Y˜z­†›®- À®Ák„€#°låZ&Eíß¹çï¦»ÇØ <ˆËÔŸ zʲ4kÎ {½EÚ;Üëb•„NŸÿ®w½ë¢5ÖXc‡µR;ûNü]׆«V­ÚK‹¤ËPñ{’F›0h)Ì?ܧî!ý#”òŸ–�€¯÷܆_ö¨Zµj+• hê`ÊÝ•�ò!¶— Ý?1—F‘è©iÓ_m¦¨ÐèÊÀg`!ÞïcŽ�(À?¾ �tëE5�º@¥H\F„°€ä뿊x©ëëÆÀU*PaH�¶iðÅô’®Ü r5© éò~~Òr IaÍ¥h€~y||èÔD}“‰¶4"wÛÔZ#1¼NCÖŽÔ[ ôý¹‰P}£¨c¯8²i«U«ö±2quÚ€9C·dà]KzŒ†�VQ´µùKaøçÅJ)Ö/dsµéêÍQ’®óOï^´‚Fì÷†Li–’ó{í4žC4„ö‹‰VmÑõ+iÒ’ž“šã"¢{S¿¹¨¤›ž_{bümñCÒ {z@î†lä-kE£p—Î¥¢€a]cr’iRW´žç„Å÷ïD‰ÒÔO½FÔjÕêx‡?ÅoÅûÚSWST¿_Ô8 ÕUœ"õOè é,ìÝ÷0È€Ž�O­Ä&HqZÄ4´<ŽhA� cNô3hjÅc‹.ª-6I"rHÉ!L¨©õ'UwkîùIñW-f$-]5…ìpsê[-ó˜4w«Y{T%�ƒ”Ã\zÚŽ1ç#Æu×&ÔjÕFÏt}↓DCwµÓr9ÏÅìRÃ�öâ%úÁ†™ Z&¤ ¼DMÀ‹J�hÊHƒNðÑGƒqlf5¿ƒN�Z­�<üÊÞÕÌ2r¨0H­ª°gæó+ECC¬ÿ¬UÆ‚œ]º‰Rp¯~õ«[R³š‡—ÄÚ3œ¦N�Š®.…hܺZŽƒ *¾�iþ©R=WgÔtì÷†¬ä�•é`DŠÉ5g­Ý•çO îÕ£[÷©V­ŽuØ,þÛøY¥‹~jAa õ#¹ôqãÆ >̦ ÚÑäPô©d…NµI|L8°R¸·1Ò;Có–"‘6©6üEzÑJRgÒr"z»€Ñí¥f«EPû_RdŸN ]œâ®0ÉZÒZXhKJ ‘º¥9è`7º&‘wQ7U¼¥Æ!ÅK×MÄ5dQY×µKÄKÕ"t'R[¼=?ù¢íâÕªU…Þ 'Ø7ÇwŠ;ý� €xŸ™"¡Ršo €¨Äãd:Èã‹| â‘zZˆ @0uþ*³|¼ô‚‚�PߊæV$E$êr¢!–[5õÑ�¾&¯£þÔ‡®7ù4¾ZY/�î�zÚ:þƒ²n&$�wV6òéî#Š9QWiØ¥aJ“©ó©÷Yw'?…6íc¤@¤aX=Ô:z¿C‘ÇB­\ûÕ!f|ºÌ9ªV­ÚiÄmÿFjv^üƸüÿ¥ïÔR 8¦è‰M‰ßß¡°}R-Ó(MVûQg@ш~œb�hIãtF?"(À‚%§·GCkÙül†¢!Àd¬C‡ÜMµ‘¢‘c9"Œü2¢Ü@Áùa¸Ý™û~t™Æúìø ã6ëáÓóCÁ¾"‘Ê}~Ÿ8ìpÕç¤Ö̵ùXèÔeÈœƒ�HHjNšv±˜�ÒÇmzÿßøF)¸3²_7²M§ÕªÕ¹A¿ÁZ*àrs|ºh§||bÑŽûYQ9~EÙhN+@4³­®¿.'Ýûl\7 à$úQèöî䊘:�ŠZµš�Ó-z°97úV�tÔçñŒG±§¤Z§ä‘´—hÔÁ )ØÙzÇÊ倒zÛ×ǹw(Ø“1ÉD$êK#Ñ\zÕUW‘6@"kkLTm]-îXê‹Gôg�NL\«U«6‚¶z‰~Poo/àsGüÒøž%%·¶“`h­UºÃ¯0ônzôÇ&ذäߥrÔ†( ž¢(íT-E"Š(¯£!ǯŽlD·ˆ‰¢švM»-C5�ˆÖªÃˆHE¨js-iØ"é4�':š�{vU˜d÷†|Ðo”¸Èv€Ro�â’DÆ„a±à4 ;ìÜ©y²ÕªUûrüà’jÿ|™¹Ü T_{VQ žÞA×v2¾'B�’s8pH¥ÎÉ=¿åó‹sȸ35¾>„¬´^‰ô(RHÃ<ÕƒJ¤§©Zµj£^âCo>Ü'~a¡k·ŠOF^Èiyž<½>¡|LÿL*­3?X4�jbENhÏðDÀ 1¡Ö{ºÈÆŽÛ`3:Pl²É&@H}HïеIÏMÌÆ}ßn»íÖ2ÔMôÓ+vÓM7Iáb[}aÙí]k@Õªu¿¡Ÿ—ÚÐä)Ÿ» ƒîÜôVô£ÍRSP$î$HÅQB ä)Ý|¸Z°RGªÖUtmù¤S�¥m tøÔˆîIÔ;[ÝOôÓC&m,ÚFh�÷'e, ½n¼Zµj]Þ;´riXœP@hN¡‡J­èàôV©áï‚ .Ñt ”Ò‚Ãl²©µG( ÕbŰªÖmZjîWÐ']ª.äcÑnË}–6í13:ºSêOÏÙÍñOt¿ Bµj•M·bü)EŠåäøÍ äýmñ#â»§OãV§M †RÄCÛL&é› ’ŽC¹®Ö}õQ)&£Ã‚ÈÇ=ãœ\“&c“r{ͦô…¼)þÑ2þyR'uл¯‰_Eì9jC ù˜²µÞXû$-]ׅ䃚‚µî˜‰¢î›9CÒpM«ˆ'¤qz\5¼ê/ÕMKCíAY«o¯QPµjÝgkÇW"ž K#âÇã/Š?¹¤äÎŒßï�?X@è�HsyfÓô¥Á±Yýõ1« ãêFÆ4Të.Sý6jtÈ}2þÛá…Þ�û‘F[J¶V)Å3ªBŸÓK„_­Zµ.ªûlYz….,dƒËJÓêf¾_|Õ"ísQé/š1€-××ñ1±S’;˜UN \Qw´«™ù$Ê‘2íŒ|�GPíQ™õ€ZØ“QÐ�Pç†ÙwM‘zK¼Zµj]d«–šÏ1€Î‹ÿb�ÓâÖññ¬&ŧ—ˆè¡Hý� Ä#h©Ù±‰¨eCßKͨZ÷0ÆôùPGÐTHu‘[’ªëAsÝz�Z¡•O-ìαñåãÕªUë"[¡Ô}ÆÄ¿'*ZDÑöñ X]ŸÚ‘–ëB<ºb�þ!‚§Õº‹ ‡V_èØjwêví. Ñë{•h�§43)âÓ³&ÇU�jÕªu%N?Тí %%wd©M(ªÛw LÇEÖ¥�Ü‹"·”Nw 5«Fé€P§ý,˜L €Ü/àdNO�mB}hÒŒW•q¿-k¼Zµj=j��¿¿C¼rlüKñŸ”ôÜ= 4+i¸»S žEÛœ jݧ€r]jA€HZQK0æ §Á�;î¸f̘1ýYƒÇg=îU¢ýjÕªõ¨bÂs㟋ÿ(þ§BÝ^£Ó³JDt]üþ6k.i¸kS[¸ÄðºjÝeT,DAXcê>@aÃaʩޣfŽ‘&i�©³BHøgoFAÕªUÆÜºñ1%•1¾€Ï'ãðaì%eÖÌ�ñ;K}hŠ”]dRöëNM¸Z 2ïI*®Ô…€�ˆH¥—ÓŒzшäÒ(œRÆ“¼>^­Zµ1uŸwÄPhÛÄwŽ¿Ð÷’¢�|:üÚø¯#nyvS­ëS Éb*êÿ)#½ED Rqú†zÕn¼ñÆFÓí‡>ô¡V™¢óíkT­ZoÙñ]âÿ(µŸ¯ÆW¤Ã|“ò3guдøq¹ì΢B�dœ2™”¬’ÈøHÏ©ù™^}njAhåX~sV\qEm¯‹W«V­ÇÆ<<©D>Ë/BÞäñ …®=³Ôƒ&;}f¼óMµ®³Ü@„¤¬ÔM8qOs„zº�ëšk®°zƒ¤oO]²FAÕª=ÂÁJ-hbir�[ä{.ûÚ×¾ö�¦Zצã€Í¯ýkb²RrƈŠ0æ0Ëz¶ñöÄOTëB1Ÿ›‰½—–1õÕªU{„ÚÓNGn^|–úФI“þØTëZC™—²2=UAAdDš§WÍ_sŽ~úÓŸ¶2÷H4~h�‚ªU{dAÓÛ:@èÁRÚ7 ¥ÊËîòÚÉ®»îÚˆ€Ì2æ€>\�šQ ¢ ´lú„÷G@WŠø#ñjÕª=‚Sqã %{²÷c¾×Të‰Ú�ñÜèÌçž{nÓÝ“F�!˜ö‡åw~Öà/ãËÅ«U«öµ5ãÇðAƒýO¡k²|µ©ÖSê½lF5 üã­¤ï #NKÁË»RräßzL|¹øãâužQµjKaÛÅO�O/>£ø9éÓ¨õ j£&RzÞyç5úž~ñ‹_ÌÌ4Ø“»1 *àóäø×âÛÇ60Z²G¬V­Ú[ -{Æ?:i�_5Õª�Ž<�Úй|—F¯pï¬Á×vËÔTÑN|åø'×yÙ�÷{Ê;~?�¯ù’ý+_ûyüYjñ¹Zµj_Œ_Ú>w•Qû†ü˦Zµ¶|°AD@3O4/ãΦÒ_¡‹¢Ÿíâ?Ûâ ¿:êÝcö™úÉ�ÿ/{çWDá­mÛ6SÛ¶m[JÊÔ)SÛHmÛ¶›¤FÌ6Ø�ÞûÎëžmš¤nôæœïl›Ý¨MÞ}3sg~¿ö£ÖûTëºðaÊòæ‹ñ\ƒßˆ´ÐB‹à>p0ÁŒ‘C¢…ÿ88”Êã&*UªdH–,]™—A­Ð�QX@ÙxŇÍ/ÜdÆ™ã·zš¾S:›oRh7rý§ríçmÇkÌ"F&¤E20,ËÁ$Дñ~ë‡V‹1à)ðÎ&KKÛ‰ˆ½h¡Å¿ÝŒÀ9'¥rë7gÓ®�• Q( X Aú*ã6ÖîµÔ⣷Z}B,—‘®›ê5ÑÆ¯Ï¤mÖx]�áÚBœŒÛÁipÜWÁY°ÌÝAzhñs1|àÀ+P_Dn‰ZüÃ2û@µk×VmqhâusGC)„(.h™£¶å®–C׸XX�ÇnËÒ­dʲ£2bö^>k�Ì]úݾS÷ ë´·‘ Œ6à(8¥rÜ÷M¸ ®€ý`H~œk1Ñä;_àìA9ÑUµøgvržOÅ>¨D‰ìCîS@ò „ CöÚ–{Pnódæ³çä]Ùrè†L[qŒ"¤°dëyÇ;�ß4Ñi.#&¨§ŠÏUpçëDÔ¨Q=cÆŒé+V,Á£`–@’'O.)R¤ðÇŸ�Õ�Ë›` (ìº-"�)À ø¨ä ž�¼Ü¦/ZhñìØJ6l˜Ô©S‡[²ýÓ§Oÿµ ×D AJúU€:ŽÙè>cåqÙú¾Ø¾e Ë¥Güg¬:~úúƒWEtZ„»ˆ Kp d¬õA—!3vMá ªN‹p%<™@s`¶#£±‡ ø@d ׌¤M›VpμäÈ‘CòåËÇ“$¹æ�µdÁ�”¤N�š_Ï É›žƒ- °f\R„žƒÏÀÜÁ �TDlE -þbܾ}›n8eÏ]Ù²e%cÆŒî¨jœQOýí "…�¥-!@sÐïq¤�´~ï5é0j]p2jî¾w�¬ìŒ×iÕ•0|Á‹ò�á`¸ž�·À¸àÒ7a„ÆÌ&Mš4’-[6¦íÊrÆuëÖ)kê]]]¹)XYxøöí[¦÷\aÏ×ñ„IE�X²CöÄ‹ëkpÔЄ(ÈLÈü�8 âˆÈMÑB‹¿wïÞåï¯2�š7o^V3|0”ʹ´ó`ˆB” t*ÝfΚ1óödÆÃþÏÊ�—dðŒ]R³Çb3#M€ÂvÄuÁp|>À` ÊnBHôèÑçˆð¼|å�.+++yýú5Ï�ªÎÌsHxÈO•äЛñc¡<§WWÒœM¿1,h"4 ¼2É‚(Ø8" ‡ˆ¼-´ø = û÷ï+¿›-Z´PŽ�?þgÐב€ó UÚ°›aûÁYˆŒß„E‡Œ¥·~S¶+6ìNc6¾ë>nË€°)@ZDEÁx°<ŸƒSâyùbãÁ]´sþôjû9sæð|öŽL?&?—£*‚=•¬L‹HÀØ/à ²ËÅÅ¥²ˆxt4-´ø•ê¶mÛF­ØŠáxOü>:ª�Ë`ˆB«x*b ÏÖ æ{Uï¶HÚŽX'GolE�†ýVðx¿Jç­Ã¦i‘TcTz|F€{@µjÕ’y󿉓““üJðXc ¥w„ …È\£çœ&BÁ3à`²A{;ލ(">@|||”RÊîÝ»i«Uþ®…?Š÷ïßó÷W©L˜™™ ¨ó‡qè½Z•àã%0 D ¡,(°.ÔxúËj] )Ûnžkf%EšÎ4TìhýÏWatîP‹’ /ØO`PÑÓñ†ž�žn7¤åßeA4Œ5JéÿüN\»v�e<ö†XŽ3~l¸ìøùÜÕ�× ~! ð8€×êã®K—.5`ÖãààÀsjx1á‰� 'Nœ-´¦üF·aK½zõXÑ0dΜÙýÙ'jÿÑÜ-@´ ÄÀ2uóË•:Yû—j=G 6š.ùN“�¦IåÎ \Ê·Ÿß)ì �Ñ@#°Ü€ð†ÀxÀlð™iyÅŠ¥dÉ’‚¿SŒB/^<ºgxñcyÍxž>ïÆ‡ "�5âp›Œ=𯄠¸¾qãÆÌª …é“úµ-é#° O€°W�  zŒeMf”ýúõ“îÝ»OïìÛ·¯h¡E`A³Ëo¼¬Zµ*MBŸ`@xe2TþØ3%„( è…2Ü ˆ�'2!ÉZs¢d«9I¢J­½š\e¶H‹Ô` ¸ >>Âõæ€^�7æòN‰Ž6þÀò‡Y‘Q R¥J¥Ô�ííí…ñâÅ Y¼x1›šLëiV €Ñâ©Ì\½z•†…À„ˆŸµh´šfD„Î9_Õž¼ � Î¹®à1ø`r—ú –wϬY³*–øtéÒÑJËì”û½QÒB‹€AãÐСC9|Ê›K\­¯ð{vÓD€.‚e wHšƒ(@ªŒ»T¾ý<¯*](™Oê ’¶ÒXA_ȯV�%–4,è´³4{Á;àÜ`°GãÏÓMÃÍÍ�Öjºe( ÌX8ûÃ�RаÐ~½fÍåN¼X±bR§R%Y8p \†(]ƒÛÆ¢k×ó-[¶|wôèQï žYß A K}¦BÄÏç§nc8zDÀ£„›€à�Q„î^}iìP…›™*oxÖ‹ìØ±C´ÐÂôf�Öë&MšH¹rå8ÏÇÁrWüܼS{Á÷ÁÐ $á•Ë¥ý¨ 6\Û£Ó"LGr` ž/`¯»uëæCÃ@À`“›}^àx±c�ˆ?ÈÛ·oW â¢Ô–«U«&å‹‘ cƈDÅ÷øqÑŸ<)n8زk×Ǹcß…óõ}Y¶ bHŽå$–þ /°zõø‚'ÀÔ±#HfTÜ£›ˆ�Äç;ÓÿíÚ¶m«8ž´Ð‚ÁrøÀ�™ùpKü¹ñS+ OÁ%°  KI£e¬:~c™¶s?m6“â A6Dñ1à˜†«xM�a>Úƒ³À€™ÍLJÒÞ¨¥å9:gx¡ã2ËgŠ‹ïÆ8Ë?E0ÜÖ%¸—66b€C‹Bä´w¯|:rDNÍ�+ !R“'O–çÏŸ+wf�N eY�3D»À\y¾¼ƒªõYÂùVn3pت¸ÿ€ÿ6̈˜‰BèE mûõ“'OdåÊ•R£F É•+×eñgÄ"䡺-σµ Eh©.ðÌŸ"ÍfÚ3ë¡™µ˜ÅìG±d÷ždcÛcü–*´më´Ó‘,öÀ(wE,·Q‚Jå9D£/t\ÍÃÒM7nd–¤d0…±5aÒ}×ýûrܳGì6oV2¢Ó¦)Ï7mÚ”Î- [pÛ{)T<¿„«}¾"“mÒnÀÜ{Á Ðäá©^œœO�ðÆ…äsìØ± ,Ç¡¬Âe{ÅÌ™3•©;6lØÀ5Yü]áçñ èézâçæ…Ú¶Ã@J*‚s><˜®\»yžìQ„�q.ÈÐføº‡ ú.oLÂN‹0ï´êÎ` ø!õ£Ë*¸‹×ÁƒYnã›.\Xq´Mƒ°P€H³fÍ‘™Ú­3E„<ð>„ѬóÉ�" 4Õ1|`FæÏŸ/°Ž2+ JŒôàp/À#p k@P,Œ/HÍ Ž�;_Ýq¸¨¸ñî–¦^dX¾¤Õ–7ZDì hÙ²e¼1QH…¿/þj},eBÙ08E¨¶"ܬÚe¡þ«ª!‡TåÍ»àùØ:-Â|¤k�ðÂM×û‘½�?¦õ—ót_Qt(>Fø÷B¡™={EÈñ9vLV�)™°â‡;ã¶nÝJÁûé†*ûT ,�êÕ«s¸UY€Ê¯ÙTŒœµãÀð¼ÏÀ °4 cC°¹T:^p9îrõü¿ Y„Fîù:sæŒh±ƒ¿[ Sd]Ìd¢‹×Tˆ à,°é@¨ –Øp:êüòæ{s ;â¤dëÙR§×R2 x>£N‹pqúiwpÁ$ ò@G_°`AÅbLp#Ý5t^ñ€+.84 ÐĉvQ؃‡C¨P†£!Á(D,Ï�A³<ÊxœbéwÜ=ÌŒîܹÃC¶¸•ÛԼʾª½2Ù2àEÔ¿_{€5(BsäÇT^ÃFïG“G\† ˜]FŒ- šËÍËÖ䣓‡œ½þ\m>'k ‘(YšIÔí%jžž5w‰’©�DNYÆ1R¢Ü6º˜Ióê¡O„òbÂÜu,9„*" ³ ü½�N‹p™Á&ð è�N]8/@#˜$Bàr£#Že8>rP•дЫW/Å}c†;r+üÙ½ Åè"¬ÜÐOâ $RL‚½ü8˜EÑ•Gñc9Šõn RÀaW·À'\¬iÂp6É=ÁKpGæm`( w U:‰ïýãÌÈòÜ–a´»Ââï¯_?qrõ’Gvääå§²bÇ%ã1Õ]-6 †3¹-Zh[fÞ%«| ¦JÞúSÇH†*úhyº¢æîNú–üœq¡?j�H¡- ÊRc⌬5&úrˆE¨Õ°µ†6#ÖÃóáè¬1- ºüÕYÞ—/_Þ›ÓöïÞ½ ÔÀ£˜µÜ¼y“6lŠ�‘ð§‚­­-Å…YŽÒ¿IN]dLë°-a2¥8º�ç ��uûÇòƒxü«Gc‘§Ì…ûŽŽ=~6dy¡f© Í{£8±NN«·éj"à ¼�xª Ò.0ä ‚”ÌgaÒx‡×(ˆ7d-–èxò•o�e™Íi>xµráÍQÛR°!@•) KŒ4é%NÑ¡»ÈP‰UdˆÄ,ÒåÇì)`_É8€Wภöƒ> øß‘LÑs +‘�Z‚£ió@a(œ=äü [áQÜÍ!LºÀ¸Š&eys#ÉÊŒ–¤eFIâRŠ)$0!ñ͆S”(F�ï‰ô=|Î|À30 DÓ…¢HSÑ¢\pÏTGœRŽ«Ýs‰Gw__›±µ¹ KÀ€+–†ž�2e Ëlßì”bF¡ž¦(ìQDØÀë¥fÍšJÈÎÎ.ÐòìÞ½{Â�Ë�IÚ¸)>œ'â’ÍÿFAzúô)·J³dÅ^¿– ™1ñ >fJKxzàmbnxž‚³`9("ÿ§�âI` úpœ›bvGá䪺is×"t‡—·�ÜyòV9|mèÌÝìqH�î‹…»Ðpñ•ÌÕ'PxB2fCÈ„ŒÂc Ÿ�[l˜ñïQƒ!ò <V u(É‚¢ ûY�íÞê`*Kq˜ Zæ —œ¹N‹pqÁ px=°ƒ›ÍnË–-ß›Û(ÑIOG”¶ÎÑvÃnÖææR _> 3î“ ®‡Äçx‡Îr^¨èUPÙ‡â×Âr 73p¾‰»ï(–&.(LÈU�"g9ÏCu×}ï�-¸6ƒî øQÌ›1+åGÑä�²!Îjq1 œ¤•Üž¾p�q Jëak¥^Ÿe^`9ûbšý°g”Ý(ÆL‡Ä ˆÏfGÌ”R”ó�`E/¬™â^€ÓÀd aJU Ñô£%[Íöc‹½.nG€(Ùâ¹:-ÂEäÀCU„ÜÀ5l·68::/ÒR„èJܵk—òµkzÃÙÍK–l=¯WÏðâš«îd¡ó+{­IÌ~(—@3Bñ¡Àðý˜!¾oª ž€Œ‘¤¥FHœÂý%F�~-__ö‚‚!âœ�-8¬@Q5D¨o±æVQš¤0³¤Þ ?›´èN¨"ô<ƒ�8°c:àÆ’YudAfÈ‚vÀuF’Ó§‰²ñ`Ñ AŠÅº`þüì·„» !¿ û]´¢sÓ0²E^ø9ÛD1bùNc€‹š=÷ÀE° t¹sþ(¨Ö#ÛyÒSxXäÆf ÜNÎó—´½AwÛ¹Ϲb&`¶Ãì…™ Å%xs� 3"U„˜0;àǤ .!"c�!�–LU-$yi˜ ö£Ø¸€ÏÁˆ‘ø®€Å ˆùßšžfÃ/j<ã3E9öÉ8#d¨Òeá!�áÆg ΀ûŽªg(C±GcÌ‚¸5›™@qlÆÎ�lgû„ ߈ç|ႜÆ0p»B8£ ]¹rE¬¬¬8Å>�ÌLØ›¡-܇WêBÕ»àØ&‚F HD†DéÀpœ¯�ˆ�fНñDJ–èxlÊs´Lµ>ȼW…æ5xÉ?«Ï9�7|úM^&âÃ�KÁ£¥]3 „âøìã'˶]¤Ù€e$c©,ˆÌ‡ ¹ëN¡¸ÞýSŒLËl¾Æ(&8n“î0JɈ˜-˜–ûø¾þ™½#ãûµº†¯g‰‹¯aI0¨¯ÍÞàlËéQv¢ðŒÃŰšôÅàk]·®<Ç�jDºþØCkݺ5{EJ‰Že4u£µ€aØO&‚D¼¿þ‚ó»ðüÙóaVÅ ‹¦ÎbÑ]¨Bo¸º{ všÑx@ÃËo€ /ò*ÌF(ˆ­f3«áZ“YÆ£ñBÌ�Ëçùh ‡8ùz 2 ÉPuœÑ5§ áÛ9üJ±böÄrmáüüÁ~}Aô�ì€%Èò7·$ OÖìÆ¯W ö…ôé*�=Ç#tZ„‹~P5pwê9gÂ�\DÜÑ¿]qÔûBq1Û™S ?Qs)-!Hg±ˆT cf¤ÌXmÚ´‰¶ušØÇaVpÖ 8àíN<ÉsI^Üz@Ñ¡øPÄŠà ÀZµj}aï,€¤º–7N\ Ïâ®ÄÝ݉ٳ@ŒÄ]!�áå‘òb1"4f„®�¸à¬kÿûWÕ{êÔ­;ÃÚ%Ìì骯v¹sm†�óÝîþº›öI\ŒbE§ä~|™´/·6™uPűÄ…Äy\ˆÍ:G»îÑôNc;DI±"9©í ®·Û! SÓù _Äþä‘èÊÀµXäÝý¥C„„ªË¯+Ž…@#¤¸ó¤¶ÏàÁ)ñ¡8Høðt›¬¯Ñ,XÆÛÆ– ¯ ït @aµ¦ð à ¿=rýõr‘†çÚkË�koâO?I°x2¢[ó[.¾øbBiÀú#·£ c^�¤]»v2DÉÏg�s£·ùBeˆ�Ïq ? *ž¹Ÿ¸ÅÝ<öÁSÁÓ� ðT '(€\¸Žy1ȽIñµy(ˆˆóD ‘󴜤8/D™6dh /Uµéa÷æéï+-W”£øPq¡bƒ’Ð_©»Ê–ÜòuB�³ôµƒšËxÛJñOÅ�ª¶ZI÷:Եȳöma‚QðK—oD ·ß~;#\,s˜¨?ºúê«]È-£¼žPÿCá)Oìälð>ü›ÅŸ°a5SdÙQ¯ƒÐ™Oa7ˆ†ü ¿£rÃ#À‹¡i)DÄuY¤�§D(ŽÑ¥÷¼…g„' DZŸƒyO�¡/HËÝ¿_ë¿ÎáípZgdÞ‹ü\¬ny¢�@B(ž"¼¡¦©â©iÝÐÄì(T ¶�yBã´çØjB>c֯ݰ`!ts—÷œø€E<*¯¦¯°ûÙ�³ðã-AxL¾:Î÷V ,ö¥36DD¾‡ãœWI‘ "<)Å ´Ç1�ž$È>,ììgã ®é:-DóUl‡MÜÀµ‘ˆó{™¾?(Çé;Å=êIB§ q_5×¨É �¶F«Á2Üv7«$´i1 K  ¬ö¶º°„¾o,’NÁ†§�ªÈ”Å/‚°š-¬!0~ú9ŸTððnËAdNè€G…÷e½Õ€#9뵉á�q�÷ÉëŽ|¬]�»–)é¸'ßKsj€´8÷ –ÿù„‡(l]¥XªøEñ�bóz�Ð¥JÈÝŒ„r=È”÷ñµõ¶ –ᆾÿÅH-PÍ¥æ¢oZ=ÌÍð;v¬tïÞ]^}õU&y†–`Ym…Å¥ô#ŒÅâè$]‘) ;^WHÄ?X} p °‘a9ˆ‚ë@lœ‹}! ÇKb; 8žÄc$ä<¼'Bw´ÂáxËá A‚ܧ#"®…çÆ¹÷cÞ÷d*¾§)|Í1ÁÂJ+n}LѼŽ$ÔMC—ŸBB¼'®!¡­›Ëxû‹â|Å@M‚Ï9çœsªH‚Ó¤”îÒu-Ødx9:0š�uÇ{,y&:gg¥œ8Xè€ýÄ«ƒI9Ϥø… ¾`ò!Wi·Ã‚ï“€4 €'…WåDÛQÌ¡t³Û µS/Å ‚¨gÃ=à™ááàq¹>v(˜Å«BôA°ŸÉÉJ0‚õZÕVG?¸ÊTs«][Õ�„ÞÖÆ¥# 7#H‡íOy˜á{-šËxÛDqŠ¢—bøé§Ÿ>‡Hz¢ 6¬Ö¹!Ôt(¿ŽÑfžûµl)×k×éÿÓ?�ëÀº´ÃÅš ³CZ,X6¢¾Òw4¹¿�¨$Äy?,ú¨ß|1„`²ìXàÍpnó–¡á…àypm .,a@¿«'böa[T‘Çý¥¼†É¡],DÅ}r,`Dàý@vn„ ¼äÉ =¦ÈdÜЉFD¬% �ÕžqßCª"枇¡ËÛ×zÇ�UŒR¥Ö2ôVMׄ¯¿þZ,X°FÉöÔ©SåR­:ŽÁt7Ý$tQ`lw±ö–ëþoUÙl³ E–ÌJ¦º?Xµ!ØZlDúþ׿HÜi•ý‹ïùð;‹gJo b`[”�™ù¹#¶Y7i–Ë‚$8žý  ˜ƒ£B ®Ç¶¨Ç…ÇüÙ=x"~ˆ‘c!C¤ãt[ðsI#Û�†ïÙêñò�µçœGD¿YQëŸkCB*@ø™kCzQâÑá 1&%XXs =јNWÏeÙÁ\vÑEáAFK•Œ¾U©–ˆ!!îúŸÿH;màùÜ=÷ÐOŽ^r®§Ü>ŸRòCFDù&VèJÐHè‹Oïü3÷Áúù1 õ 2±C°,±cCŒ„–)r<,¶±£Õ;8þ|!L™PóB…›Ú·—»57­W/ˆÇ¡L ,K–wV•ÏN;Q[[Zg™*X°ùKò©×AP@NÇ©Æ BWí:÷•®¯–.=¾–ö�û¹pIþ¸p›Õã¤ÙY9õ›?ª�{€ð Ân6ôŽð䄤"b¼‰8Âj½8¯ÎyCœâƒPí¾8¢áu®G~ˆ{"oå䣶<ò�b#¡J“pÿ¤xF±M ÚDñµ¾§¹FÔŽ„ÀŸ�hDv�õ¶�â50G±Ò:>WyÍ6Wkÿ|jWL¾`”ÁµW^)7hϸ_{öt^T¬�µ÷0ZÇL¢-k{ì± N–ñJ¸[º¼ïºQ³èۂ켈ò�ˆ:½0�†¢x ˆœG‚¤xdDÂ?mwÏawÂpä‡,,èBn�"á2ò67=ÖŸ{àß)ƒÅýÛ5Øßyx„Û,,Ç~þ=A:Î[â'a3Èɤۄ !`>£jˆHÃså&ßþ^ñ¬bçÚI1Xïg¥Iûž÷ð�¾NÇ—`Yd$ Ÿ²¹BK�xª#(Õ†š ;tè óæÍHh„’Îß”€þvòÉ2\gABãVhxŽnÛ§h'€Ñ£GK°`™j••Ux7~/¶8�…¹6ÄC( OÄu;€,X Í#�Ĭ4}m‘R ÜÅùñ¼\ µÞa;Ω@Hä���ßÛís&év×&Ì;$Ïf÷8„ˆ¸w#;w^ç„p !)ò�‡â\�?ù̼pcEó£ »dòb´ñ©õŸ¶Ã©—Fðï16å5X–Ù�_Û¼›Õž7@MNé á1?èPõtð†F<ÿ¼L×°Ü UË |òIé¬ígNÕv@�tôwyy¹ –©Öÿ«ŸxÒwê7mûéHðo\Bb„¥c![FÒŒrÌQ·¿FZy·�ëA@œÏu@0Òƒ Ü6Î qì‹gBx\FBÑŽÜçsÞ–Ý ž• Ëáѱ×°cLvD¢íÔt^{ Br|n.”‰é ½*ÝV¤Ó^Ú�¼kŸ2+H1ºùÑVòÙz91ÀyFf7 …ÎÚÇ*z(FZN¨Ø=°óÎ;ç½òÊ+.w¾ŽØe‡d;•bï§*¸Vª’»LG^¡]¹ÙsO¹£ukY€7I¨®é�P°]mŸ(–˜G4«­¶ñâ‹/®X²d‰ÐÙ6’múÎÍVâ©MQ*û@8‹-ZþÜ}÷ÝB¾‰FªŒÆf)Øc�=„‰¯�:u’~ø!R°µÙ˜”<Þ€fsyžÐ q¡˜ƒ€ïþâdÚ,Ö„Æ¢ª7ŽÇƒˆ•k›GÃB ðŒX°c•r\¯†P _pJ»ÈÃÔr./C‹ñÏ€¨bÏ EÞ/×Ç£�l!8Ë?q]7µÕB‹î5ÈbàÈIòÙð ²âíÄ]"wó�ð ñÒμþåJ%åùúú犾�ˆÙÔ†|Æx�|†ß6- 9¢3=S9&ãþ¡yóæŸ 80·°°Pjc¶NŸ>]~ƒ 8àÙzë­E Mô\ò-h¥ÅEÛý)¤›è×=5ǤD¯+,X’öÑ�_ñ¢ )$ÂB�çÂ=û°øãe° ²h’‡aÁd¡Æ»ˆ[ìS¶éaq6I4ä‚G%2Wë× AH÷@ë¼® زì5àÈÅkx_Îë�`ñìhPjãÉ Ãqo�,ç„8ñð8�$ð†\O=y$ò_x5V›Äy]8�ý EˆRy¡×Hù~Â<¼!>riþûåxˆÛ��ÀC³Îœ�ûdÄ<­'Z¢Ÿ[ Ç<öÊW©Aˆx`š+šÐ´H(Ø6ŠëSŒ€&ÍPL€�¶ÝvÛÉ:/G‰¦P›¡VœqÆUš;ª<á„ÊÕƒY¥5F‹Õ³Y¦»Kþüç? Þ�?æ@4†2®eç_nä·Ò~ÎÖc¦êµ ZµjE; ¦�6ê0¾`a${¿/"_Á¢èg“d»Ú##ˆ„Ä;D±@ ÀÏ™L9JFn.� È áYðäÏÂ=ñƒß ²ƒ è݆H€ÅRÄûpònꆙqn#¨TÍVñ€ðè�^s Þ ŸŽóÔ iî ‚å÷»žüH&ÎX"sæq}ÎÁk¶ 7ú ìgê=×]B hùæGÜ—¿÷yÿ®À3ƒ�@XlÃj¬IÚýo*¾‡Œ$*}é¶ý»Â^+6!Cžk”%œÇŽ/2â™�@Á›ÀK¹í‰8–ûLÕ.b#ÏI�G‚ôœìÛºxsîÙÝûþ8y�¬*(‘gßaA^~<×up^ësǵ8·ž§cU‹c„„ª87çµÉ±åZäºJ·}Ü,X“³æ }hÄ0òða…‘Oµ‡J¶§!žœÇ¼ªŸßZÈo¬ýüÎB~?Yª(¦x¶S漢Þ!‡"„ÿÖSÀ,xA„¬\a)Þ DbÝð4Hº¦bÁe1Ɖ~¦ëGÞ¥6Cï¯Q ¸~ì¸pˆ‘û„l M}<jvz~<^|�‡ëâ½t~é Š\VÚâXîO ‚àÜÜ�ÿ9ØØpBqî~È#áiABßþ6—ýyA¬ìOxÏ÷†ø7×@ÁÙ@º[¡zNUÖ”•ÜX•ñ2ý÷ŠY›k²DÔVñ…GB`‘bšm+ðˆ¢ÊóŒJ�DVA>Fd£-Ä×׊cŸPܨø§â4ÅQ6ùµ³âeÅ“]¯Àk-TÂ}h.©JóG‚ØáÌ3ÏFGäääH°`u±¼•EäLXÈ]Ÿ›…×ÜBÊ<¡.r%l÷I¢áp¹¼ç…@")êz tWD Q¾þÁ8ò2lsÞ „ �’èG À}§óÊðöØ2ã½â•XíŽË é6'‡D�h—”ËÒÜÕ�RM¯;'¦ðE ÜÞ%!7ˆˆœÝgw–æGÝ Ar�wÕ¦‡Ý›oã ön¬ÉÚ†ŠÃ­ëö<먰ÔBnK€^Ïj¶ñ TK„ éóbúž �ˆÊO¬Ë³„‚ÛHq­ ~2‰x¡'ïÎQáBÑAD¾ˆî ¡¾(˜SÅ‘Ë`Á´<61AJ,øFL,ðxA,Ö�DÔki0¸¹ëàPÛœ÷Å{òß ÷Ej4„kÚ¡z¼O¼=îÁ'!¼#Hˆº$®ã×#�ü~&á8zëq,dVç÷¼õ‰)ùÜåÑ]^Sð=l�¶�MQ÷³b™W‡JTE·Rɨt—]v‘«t4ù„ ¬ß]°¦jËó x‚w‰zízŠ U%gÞžâ<—4á³Ø®Û„Á¸'Ÿœ�YCj´ðá=§ !ò Žpƒî¬°u ­t�³»× eÄ „á&¼ÒwtJeŸéÆ�ª}þz®M…| Ý)ëo{ßå6Š`ÁÖyÛAq§I»'ãyµF…öïj%¤ê–-[JÇŽ…]°¦i„�]áÕ&b1%üÄâËïä‰Xx¿"c�·�[Ò€xð0�±Ø7©ABxCx*k �ñ:ï�ŸvÈ‹v;›â%AÜ'Þ Ûȱѹ�û¢ ~ú9,~�#!Ú¬#¢ öj#V›x¶"X°Œ¨m:Jñ¼�.Ÿãµʇ„-ƒh#tÊ)§HÏž=Cˆ®i^0‚WÏb ŒåWö£s ðš(²$7d ö¤�×@¬Qˆ²å}Ppëw=0Ôê¼oòbð¬üÑáÖY²ã¾ñ¦Ò5oåz©T�‘SÊ�fëoXCDY>8X°uÞþ¢¸ÝFRŒWÌ…ˆ¢]TÀPµýöÛWµiÓ†©±MD¸láÒ•ÔÓ¸âT‹3BÈòt§&¡P˜‘á8ÎŒž…¬„ã ÙhØÑ÷zâáÂzä—ø¼j–Äóá3ãXˆB$”2ÿƽâ}ntà­²á�·Ëz›o Ñw²ÀêƒP!XFØVŠsí�öSÅDˆ(¦dTºÝvÛ-½í¶Ûò³?DlÀˆ‰äZÜ€¸è¸mBM^Þÿæ Ÿ0“å:2 x#ȳ™ºJnÇ÷Dø½¶Â %®Áj:É7Ä¡ãAA`„<ñvÒŠø¬[¶R¡Â—õ¶ÚtÝ/·Ž+ÍÁ‚e„zn_Å�&çim€* Ÿ*‘vkáëøgŸ}v,ø‚eåønº°(:²nÖ)�Z̼„Œ^Œ‘ž9žhmRmx;u r>?3ÅŒ„ª-¼Þ;SsDÁ‚g´—âFSÑMK!\(³íSµ¾è# ÑÍÎ\¯&ØSo¥¸Òåzüˆ†6>�òlÝ€G¾k̓€l "BR¾lœ{±�`Vƒp¡M�už�OD�:u’vØ¡f:s…Ñ›™¬š š±­xÄj‹–(*c†éåÛû¸‹.º¨O^ž–ÜË´V=Œm€Tüî®�’…�ÅU„D[›/´ÖCX\�0!áAî‹û�”lâj¦‚­ á Q“4wQžD «*Y…Á–-P¼`c`‚ËXI÷ÅŠ×lRIŠÉ±Ë³U*:¤oß¾3Bˆ.slåêbêd ¿NÂÃŽë’À~€í $ò:µ9û ƒ€€?Óˆódz˜­6i]÷mêµæ-Ž/™˜?¾0²¥E‹>ÍV<œé­Á‚í¢¸YñyLxŽßólûbÅ„“N:é7íEWœ*º J ‹{l·#Œh…ºP‰~öOÛÒ‡E�ÆÎ•Ö0¯ I3ù)<3dðŒ÷€L‰X+Òæa€÷¿$g•¤²_~ùEZµj…R®†ˆÊlBs°`?QöHÅÓŠ ‘1æEÖ$5Ǽ¢¹Z[4¹}ûö“—/_Š‹Öa4v*½ØR’Þ %$`BàuB6�Ç›×%«TÄB¿6ò;„ÕÒ½²X³?!AŠ;ÝTR¤Õvß ë‚GÀi 9'¿ mxõ³Ï>“#�<Å\ *FÛ`Í`Á2Þ¶TüÝd 3�tfÙü¢Ã*Ã< Ñ�îÞ½ûìÒÒR ¶îÙç#'"»Ž ‚3â! °ø;â �DŽˆŸùón l:OÈå› �‹l:BD‡8Âc=ë2äßêN„¨–—Θžüâ‹/ŠNVF- U[þ¶6I·ƒÝŠNÖqa¢åŒ–Xh®ÚC1$µß~ûýúÃ?”¯s!ºÐ®‡QÞq5)r¡.Y6‹>­{ø üƦH¼ù�äy$\—^v%"`Òoˆ0ÕâíòC&¤h2ÀóÌ[Y$k²‚‚¹ãŽ;DYÖxCUŠ¥Š'Y3�¢âÚ .\HW|šœF… ]²Mº,Øv6·h°bªÑjEYL/ºü=öØcé£�>Z­]$Øïk“g-•c.{Æ…Àüpá5Èå�¿ý—:"ÂrtÔ†”\áêµÿê�—DGŠ]Ylë½PC6x^\Ô•sÒáRŠæ˜Ø�ÅÜå¢ÖUÕ÷JxïФí A!D<ǺD~þùgap%$d(·‰Ë7e›t;X°�­Ýß[žhqŒ¬”ñšö¾*}ûí·eåÊ•ìwá€0!ÖÓ°® xAtÚ¾é±þ�$ı"…F#<Sãy£ÂÓÃ$ä`­Ú‹z�\—{À+ôCŒÖáÁÏyÕ ´í©«}ðÁ®~HAžh•µè:׺¥ –U¹¢ÝW™œûÜÿ4�Q‹vÞyçÊsÏ=WÞzë-!ŽlíZAQ)ó�Ry0Ž ðrÍQØÚõõÁ ‚Ã[!G”há*ç5åÞ÷ƒ¨‚dA÷k—ðÜœ|Û$ç‰ÃŽÄ!C¼9îÃ/òåó³0ep·D¥ï]z|-u5ºßÓcî�üc UÛ÷ò ÅAÙZC,äŠöS<®«˜¦X�‚ˆª4WT�’2zþùçeõêÕlíY»Î}ñÒæ!)»£ë‡rÛȱ—?ËâʺNä\¸k¶ ­­"VÑyb¾h2´¢Úúy?:A5Ú5¡ß—?I}lÕªUrÕUWÉŸÿüçšs+MPÔO±½"X°¬mÿsœ ~TäÅ ÐHIiK¿ë®»ÊYg�%4e\ºtéZ˜ì¬ãsxšO[›B8©mÇ^Œ@ ·N‹r?’ý¾¶î‚�|âî§AdÈoßÂ뤸¸¾FG¾[[o½5ó‡ªU°0Ý¢/Ùh—`Á²Ö6WüÕÜÿ¹N°�… ˜);œzê©Ò±cG™2eŠTVVJ°d¬ï?Ò� Õ‚Iè a 9D Ïÿ$OF¾w’¡0V„‘6K ±qãÆÉÑG ‘'ª´Þ�YAz°`Yß‹î^ÅxE®¢"ŽˆØxã�…ÎÀ'Ÿ|²Pó0fÌ)..–`�jô"£•°Q¬âÌÚöàeÄ4U ÍAžOB€ßQ×µ¾÷m)-«hpÃÚ^½zÉ�QGTf…¬³­›J~(X°­-_ô­É¹ËRØ|óÍE rÌ1ÇHÛ¶m¥wïÞ¢»¥ªªJ‚5Øø™¬êr6#(caõGüÌ »žùh ¢�ŠŠ†Gh0üøã�Ë>ûìƒ7$m´ß;ð½âXE°`MFI·£¢³b´…éòMIW ùD½"¾0xFÚ}AÎ?ÿ|ÑZ#ùí·ßõO°†Y�þcäÜ›z¸éª¶€¬#$´Û™3÷©±r¤Œ~h×®� Úd“MøŽQÔJ ®^ö½ ¬ÉɺQ ±® K­ébuÔ#‚ˆÀ6Ûl#|�ˆoãõèÑC,XrG É!»¦85M=Kš¤{@’$´í ÷ËÒÜÆU�Κ5K.¸à‚oˆ¼J%¤oô{÷¬bE°`MŽŒö±~tƒ #óŒ*ý0]óæÍ«TÙS¥RÓêBÂ;b¼1ÊŸ»îºKÞÿý®«›)yWI‡g0¯fM°‘ ¯eo)�Ðv_,9¹ùÒØ6xð`9öØc…‡:Ä@p…¬_+®P Ö$� î½M­3ÄÂt¥‘†¨+ð”””ÊUßRP‚¾H‡v˜üío“:_²ÂÂÂZ„1‚õÿê'úÀA0®ÍLŠº~Feʈ­¡h€�aƒMZÈ;ï¼#�mtÜîÖ­›ì¿ÿþs(§ø8t܆5uÏhgŃ֩;×Õ¥õÕ õÂCb®J›6m„üÑwß}Ç<þ@H)lua‰\zÏ[ȱQËQ¹D P�ã‡æ5@\ì“É£Á{x| 8pÒj+^ü^ËùAí|Ž4[oC!üœ„ÑÃñºë®h·Ýv“O<±ÄŸS„¶>Á™šîV#£EymÈÈŠ`«4—T½ýöÛËG!…°'N ‚†ùî­ÿþ€®Ú´œ�Œ\k›nèR@+Í5Ù7ãÈ¢±Á}¼¿£7uQl«ó(qdâG�)õ=ÖgáGíˆÚó‚Ök±7°…pôÌ™3% ûæ›o„‰¬”CœvÚi´øY`Dt„"X°`f-(>U,TÆxG¥iº2¶#—$ÔI\zé¥òì³ÏŠŽ–0AC°�#'¹É¥,¢q�B£‹í W>è™–‰# A¾��#"´šXÃ'"¼D RAÖîÈ™mìÑo"×F7rˆƒŸÌVr#Í!~)s¬ÿ¹øÀéDAcUȇϧ¶´ÕÚ`¯Ö¢ÁGB(D“²Ù³g fÌ Úi§�DGôÓïq”âE°`ÁRˆvT<­˜l-H+r\7†º�ú¤b8¬Ö1%Æ «l‚ù#šd:©¶!VŒÀÂËÈë'— ¬#DÚv?ÞMZñbN¼êy7ŠÁ<¢´ã"ÌŠù Ú’PÕ†Þ^¹þŸ�||�ÃIì�‘*SfÑQÁ&&¿¾æN Á‚Û¦¼¾`�»ç˜WTìdÞuCžÒÜn¸a`ii©4%+.)gf¡)ÿ‰?êå@Jä�˜1Ä"íÂQë*ƒÅ{t©aóþä‡gÄûƒDêâ3p|­ H‘³á�·Nm¶á%¡7ß|sŠ$côiÄBA�8M… ,{¤"X°`µ2ì ø»uï¯XP"*S¬nÑ¢E#&Þ{ï=¤ÞMiì7¡(žê! Ø§~ÂKg\ÿ2 wòA,ÐëzØ ")½Þƒ…Û¡5¶ÕwÄC}¦©B@yН·ëŸóÌ U«œzz’F>ø ´lÙRh&L·m%#œþO,X°:ÚfVsÔVñ®â7ÅBó� ¦Ú¨ë†|•'D¦T6UaH¨yµB’ó XÄ™1D¸êèÖÿs£ šð !¶XBc»¤Æ>µõ‚ £*7TTû$´é¦›.ÆcIÊ4 MŸFÚùг"ZlC*ÿ¤,X=sG-(Ú›âçk º2 ¹¶AÔ‘¬¥MÐÝwß- .”l·œüò=xN G>Ä ¹1žy!'ÍÎ2àùS]©óá3ðÃmü;UŽ©®Å»eŠ™Š+íïv#Å€˜��¹ LÊŠŠŠ¤uëÖ䄨âïÅéOŠkÁ‚k mdó�UܬxÓëæ�v´C÷¶Ýv[¡û0_ÒQ£Fe}áëèg‘á©ßy@¦#DÈŽPÛó6XÌ­ˆ”œ ?¹>ÄÀk ½6ÇG¯Aø ‚I ^74V�nµb¹âeó€0ì6ŬÈße1³¶˜–š \ò:�PÒ`„׿x5X°�?ÚÄÚ×wðê�ÊbHÈyD|)÷Øc9ï¼óäµ×^ãÉ1«Ç<¼ÔgùŸhÿ8¶!]ÆHã4 „ùHÐÁ2öª<^C�w½<î5úš/§FJMm¢j„Zžÿ„O:Üž`’m‰ #ÑQÛü{PtÜ êµiÓ¦IR6tèP9üðÃù[g$8û\ÿ o ,!ÛRÑʼ£ÉŠˆ' rD|9yR¤°�§Æ‚‚ÉRcš'£½Y„Ý¢i ÞH4qÏvȉ¾1&¤HÂïb€„OŒ¼$ÛÑ/%J$Q…÷ÎvÈÎqNWdáÈ$Q¡˜¦hÝ,Þ�־ᕿüòËÉ…bsr„ÂØí¶ÛŽc ì!íE°`Á¶�-þ=\=ŸÂ( ‘°UÕœð;£ÈÉ=óÌ3Y-X˜½0—®„® ™l;Ö»À;9êÒÿ±˜§R—q,^ŠS¢AÎ�§…‡ãzÙA>ÔðX8,]ÃTȆý|q@”0£D„ ;Ú%bmÈΫËÝ©Æk齃Ì!9Ô%`t¡ß"*9¸(’-²ÂÕ§,¤,X°µÐ•áý®Šó†Xü9G¨‰Þxã�¬1⻈\ÍMŠz�‚žitRHY¤ ±ï�?I=u9¯CDœâ�kS䌞v�¡™òzÜDwßÃbÿ¤ ¨P1D±]º¿EÒTøabò5ÌÐJÊú÷ï/'�tºè×8ÕT¦(‚ –°mHXN婿 ¥³ú"êׯ_VÏzëãoYÌ×XƒçrÞM¯¢žcag‘_S·iÎËÂ�Ðòx>xDxJ¯'ýõ­à4 O©YÈÍ�Ÿ‘'ýó’.À­¶0ÜdÅÙÍÖl�¾7D~’ÙYIÍR/ºè"¡ù/3¼ôšó­põE°`Á¶ =©�z@Z§AŽŠrà¼"õšhƒ/#GŽ”lµ²ò ¹í‰R*ÂlA'ÇBøŽBVÂ\õjl 9™r­Ö£!> ç9BÃF4 ¯±äG¸ðoBsœ#Éé±Õ†E'EmZâ­XY"W^ye¢Rí+®¸‚>xBbõu¿)îY;*¹`Á‚'ô�Ž=^eó÷!ÂñÇ/_|± Œ;ôÐC‘lã9‚"‘ËëŒŠÈæ&§­ï};–ˆXÈ­5 ~eÚïYPÛOTŠo*µÕÆ6W|-Øo¿ý$©î „–ï¸ãÙk¯½„ñßœ©xÝ Áƒ –p·…¯õËWT“÷¡kõAcÆŒ‘%K–HÛ¶må�Cˆ’b¿ 6Ø@¨2gH˜vä–lµÜ…ôŒ‹�° ê­�à Aü©á•pËçðàZɵ Jž€>TìÔ¬nÖKQî“òéE‹IRFQ,D‡Ço×]b*¹–Š`Á‚%h»*Æ+ •Ö¨à˜AÔ®];Y¶l™0yòäÉ)1™Å€/j�×ÄÓãm·Ý–ÕýæòV1‰²ˆXî¨>ðE Ìä!´G7kt¹FÌ€j޼y#¬Â:—ÊW|¡@ñVWC±Yì“GŸ~ú©$eÇ´øû÷æu�Pœ¯,X‚v†b‚J _vfÏ=÷„„‚çd¬_~ù¥0‰’§EZœè!,Çþ�SÇŽ³Zº]X\*7wy�¼�ï•8/¦¡‹7^J5Èæ¤¶/9&Dè[®êðrýÃ}å®'?’®¯–‹o{ƒk'Ý3®.dWm-y–˜îÄfõ³ýz¡š¿Ë[o½U’2¼yJApMÛt,X°­�b††×Škò=ä„þñ�ÈW_}åÚõÐ:¥sçÎÂèpH‡Ðœ_CtÐAÉÓO?ÍþY-VøhȯȬ]a'„d-mÜeÀ†Ä¹ H¯ùi] (j’¸>]ÈE5HZͽsÏä²üV>6âRäd-Îww ­P|§ø›býŒ+Yâ‘á_ ¦ûû"ßÄùM˜PmX¨x"ÙCÁ‚…s]sõK^ä˰�:ê(yà�dåÊ•.yûÛo¿Éßÿþwzʱ(à QÐêÚüvØaòúë¯K6ŸÃ¤™KäÆGûs"[¸ùÙ�ÎѾm¾Õ›/ÃŽé �¶/û¢ˆãÜi�ØÂº5Ô®_ã¹FBã7*6j`›©™> þî’O¹1Ê„p³GBËÝM¼,X°„”q¯)i8ΑCÍÄ\ ƒ 3ªÖ4&:�•|�_ÌZ3“Eð”˜I”íFžè�DZP;‰3^„×¹ ÎbÎÁ±Ö0•ZŸ(|r½ßØ‚xMä�L®ÍëìGÎ ¸~txUÔ±çßwíóM��a¿ö²a˶tûÉ ú6Ô&+ª<J¼hUoŒ~®¢§b+E°`ÁjÛÓW±LIdu´eÏÞ{ï-÷Þ{¯Ëõ`3fÌ@ÎêBq>�·RCtä‘GÊÇœõ�·)jýuÚ"¹ðÖ×ðV B[Î+ÂSJ³˜§mÃ90¿aù¡Ó¯{É冸�m¼F§†c.{†}I¥¯#r€„¬5÷Ê5ëJtäsвÁž­e½ûH³õ6¤Cûn�¾š¨¨Œ*ä¾øâ IÊ®¾új!Ìì)óVØ÷c;E°`ÁjÙó±�wXׯ™6†ÑÀ´OŸ>lÇcrûúc "<"º*¾Êv[±ªXº÷¸ðäcžQ�xÈŒ\�Õ"‘"?C[¼¼/¼Ÿ\èÜ€×y¹ö=–_r„áX3Ö†�¤À :ð6Ù`·K¤ÙÆ-ü‰¨½Í µ_¢2mþ“œ/tË-·¯“÷ “iï¥,XBžÐ[�ˆi`ŠrÉ[ ü›ŽÚ�Môˆ‰í„5 ¢wß}W�y7›µ Wîíö "—‚€Xø!–*Ô8'^ ÎI¾¢Š› Dî'‘Ú"óö÷Uo°Ó™ÒlƒM}•6¿ª¡öctÔyÇçž{N’²ûï¿_vØañz×å+¾V¬,XB-{þkO�óñí m{Ž=öXGB$…iq <˜V'®fÈÇþðGD„æºwïîwAÎzáÂ�“æ#§&ÜåÂq w0€���“Âs2aC£‘kUt÷?®øÓ¶»C<1ø¢: |«(õI/ûÙgŸ•¤ŒnÚä�¸ž!O1ÂZ  ,!k¯§˜¥$Tæ÷ŽƒL.½ôRaæŠox6„å ÐÒÇD Q@P.4÷ä“OJII‰4+/¯¤7E®„Æ/,Å3Bh`CðѺk¬i¨._…°áŽ®ʲ¼Õò¯ý«< ÍS\§hˆ�U”DH(Ñp%„ãüñâŠï'(‚ –�f!‡iŠ?ÇS!myòòòb%­«W¯–nݺѾ'š‚Ä\ Ñ6ÛlÃôJ­4�²W¼0ꇙr]§>xx,‰9$kdJN/,2)m¸¤Ý—"ˆ×ÞûFJË*„ÿÿ|0Ž€äñ‰ O¬¯�‰ñ„ Çá±[8®Ú�£˜ 8M,X°„ls[0~Rä×�ä±ûî»3u’ö=)k+͵oß�âñóB™óˆH*ÓèÎ;ïŽiŠ6gažtéñ5íy�5¬À5¾¨Î¢Âu¢$Ľ‘"ÄGGñï'Ìó�Ìááÿ5Ž„ªñ¬­ ”ûf-ç„ÜÐI;Z'4Iñ°"X°` æ…z+Æ+–¨Ìº²†HøBŽ›3gNzuØŠB¡uB¾'…d;ž£ ÒZ¾|¹4UÓøxÈorÉío2CDy8X)ÒcÜyÏ�ÇËÒÜÕ±]èH_7þY¸=*Þo@nhüÚ&¡�òàå¿—FBŸ)‚ – ½¨­˜¡bÚ÷ .£U«V2vìØ5Õü ^ í y$ŽM F@@DW]u•LŸ>]šº•”–K¯?H›ûÞAfM¸îw%#È�B×;»~$ƒÆN%÷³¦žkˆOxàˆQ•œžWÏŽ äb*¢Ãí^~ùeIÈø;§/"}µçÍAB#Á‚KÐî2Ð$}ª]DìÝ<Ôqä}jÕ.…jö3Ï<3®�ÕuÝ&LÇAê£F�²£ƒAô?OY(÷?ý)!;ò:H°‘D#tæTC†ç×/Ž"ê�^è5R Dµ5þ.zè!X⼡•V7´q=¼óýbUÀ5ÞyçIÊ~üñGŠ�Šiú˜«]´Ë }° �ç¡iéÏ?ÿ,µµ)S¦È%—\ÂH—â<�$´Ç{³[N=õT9ûì³…ý¹¹¹¬n^Óò¼ùmúb!löùȉòåèɾ3EÀðogÈ„‹™›hç Ú:]{íµ.'ñ†r¬ º. M÷v L= l™={¶$eŒ«‡„ø›5�lÞЊ`Á‚%¬�#ä0K‘\U<•¢Ž«ICŸ}öY�:ÏŸ?_�wó„Œ×Ó²eK¡xb쳉Î8ã aÑâ'ƒ;+2dÿ¯x¼Q"*³œÊnuœqµ8JBä'éêž” 0@vÝuWŠ´¹^¹ÝÃ4ÅŠ`Á‚%h$‚ßµDlŽæ…Š­øã¾_yå™7ožÔÅX0¨B?묳jbí‚WÄ&²’�\�=2¸˜5Xqq1aXZ⼡ùŠGÔòoñE~ä•7Þx£p�„Œ|ŠPÊŒ„\�Ð9Š`Á‚%lĽ§)rôI°ÀÑ€¢�I«„+êj´ëéÕ«—\~ùån�œ—D3ätÒI'ɸqã2·ëv0Â} =$ÔGBEȵÍãNk&é~‚c"ç(ëСC’Ó{‘CBç¼’Ü;Î7ÿï7p Oõ‰† ïãQGÅi7„ÊLÿTÑþfSÏp{¶}Îe/¼ðB�^'�:uÂý#Y*4eùÑ…#MBˆæŒ`[lÈ›&ö­P'Áº!C†pªâs)×øRr¤[£NCÙ¦gªà ÐÂÏ%‚²‡–E}úô©ŽÓr™‹áÁdµ=×{Òz(ËÔ×t€IQÃìoúË”@á@DCA«V­Ü�?þ×i%”’£�ŸômT*ööP™âXÝtŸizøcm¦,�õòÕ@‘‹áž{îá:O~ïù¦¦}÷¨¢†Ù‚ÔCLÂ#ŒN6‚P\JD hd‰ BhB%f ;úTÜâÐÇ-~ôÑGsæÌqÕ@ž‹�n΀Qž÷ŽëcÚÚ$„¨aÖ7}ÜfÚ˜J¾ŸõÊq'!!è ¤~„.]bRrüÞù~­H~8x}þùçÕÕ”°8¦�†y¸àÀm®ižß<¼©IQìbz×47ê4Ä"Âwß}·‚�H•’ÃŽ‰vü¨ Tä»äššÂlhênšj* }LRqÕÄÂ^NúÁ9§?'ô‰o-BÔOú¹%á Ä"íÔž1+"Ž;î¸#ÎY óèaÚš›–D8.´·®Ì„« ã|ÙyÅ÷ì¿ïe¦Ù>=(„¨%®7�ð¹ûœp jÚ´©Ã)»W¯^q]JB°gˆ6ü¸ ”ez:”’ÛÚô™iŠ©(ÔÚý›éW³Ð±É÷ BKý*‡WMBˆZâ$ÓPoÚ8-Y ˜î½÷ÞŽ2¤/„ˆ‚êBœž#‚P±i€iÏ€cvÝ-�ØIÔÑœ vsÕCÂÅðÆo`+ BK|—^'“¢–ØÞô§"]•–äò /txà W:ãŒ3â MÑlÓǦuý�ô`Óü�k6óúë¯w¶ÏWRÓAèÖ[o Ï7-ö�ÛMBˆZbÃÀÀj¶)+<¼Š±iË–-ݰaÜqÜxã�nË-·Œ BE¾64ЧÛf‡Òp¨ÐVÁÏa{kM“››‹‰.N Áïw¡iHí[ö¡Uß}àÉóA(/„°d9òÈ#«�…ˆ£[·nI¹TíÚ3ü5V¶ù1{§ÒñãÇ»Ú`Ú´iì¶¢£/ø½ÎöëM¶4 !j‘ž¦|Ÿ�Ïò?&P².Ä0¢ :!âøí·ßhé§.„¸~¢TöˆóËëÌ£ÕVóË?ü@çg0•ùTÜ[ÞÅAQ‹<äç#²¼òÃu¡­·ÞÚ1].D4®pbƇ.×OTf)¼F¥¤áj‹Ž;Òù™ Be>0óõ*!D-Ó<И0#ªU Ÿ³Î:KC«"¥Y-µÃÍ7ßœ%q�SF¬Ê¬;­“Ü¥K—ºÚ"//'w¾Ç¤qéBÍ2�bBÔ2Û˜Fù 4ÝŸ†J‚Aˆ®åìS:j qÝu×qjüÃâºJ؉©”�SµÜuÉ×c±"if”ð£ #LŸÕ�]�Zõ=ÀŸùqu¡]wÝÕ=óÌ3Nˆ8Þ|óM@˃Š @›m¶™ãd=tèÐ:YKNŸ¿®IÅ�÷×ÿ‹~ VQ«¾ß6Íó(ÛTNÉ1ØwÉ%—8ÒBDAPÙm·ÝhLˆ B¤¿¸ŽZ´háú÷ï_)Cv7þ–úå{~Ÿ¢Ž¸*�Š+Ž34ÅÂgÔ¨QNˆ(ØÂ{ðÁG!~Îz÷Ã;ŒNË:±�b¡â©§žtÎ.1ez7ù}LBˆ:b7Óˆ°™$ n[%—Þ³gO'D'Ÿ|rd:Žë‡zÎÕ®.`)´û¨Ä4ØgÖ0 !ꈦ¾0»,.!f+pL.))qBDѶmÛ`ºË¡äuÔ¤Iw '°ƒ¨®ðÑBBýMÔ}Bu¡LóR!*t,ËÊÊrBÄ8'à!„Hƒxà�nìØ±uâoܤ0WýÖtBÝ!!Ä ÞÌ´4*aß³ýöÛ»æÍ›»Aƒ9!¢øçŸH»E!D÷\¿~ýêÄ雚¦Ÿ Jxï"o¬ºžIQÇìhúÀ´(*‘báæB:¥k×®‘…e!rrrÜž{îY!¡äà³9e»Úæ³Ï>+÷¶#ø:AŸ6 !êk›:zû’ÒpbœùŽfÍš¹;ï¼Su!—öb·'ŽØ”×O-Ã× ÖƒrM“¼_\k“¢žp¶é» uO »‰[V"3/¤º�ˆ¥M›6tÈE!®£V­ZÕæIšk•áXNóÁµ £MÞ1DQOØÅô‘iª©,ê4´ÝvÛ9† Y�,D]ºt‰í�C̻ڇn擌þzžciz»~¹$!Ö¬^.Œê’#�ÂÊï=z8!bÖ:�úŠ­ Ñœ°dÉ’ZsIh×®¶SI‡„„i²o¹Ç$„¨g­ÚW™2S ®²™y!&Ð…�+6�&6 fΜéj‚écüâJmÙc½_ÜA&!D=ãSßT3C¤5.¼ðÂH ~!hZ9ôÐCSuÈÕÚºøÁƒãÒÀ5›ÄÎñ©¸^¦5MBˆzÆ~Þ=aZT]±™ÿ±±Å�*0 uÈ�wÞyåë³Q¸Ý¿wïÞµ‘Š£>…UOn Ùf�OÅu5 !ê![™^ñ‹îŠâNC¬r~úé§#7b ѹsgY"›øý·ß~»Vf–®¾úê„CmòL¥¾éf é“¢ž6'tô3ùqAhà 7t¸'üúë¯: ‰¨½=¤Ýbg…h¨i~ùåVKLò�'ùëúKÓ–&!D=mN¸Ëo[͉ B,º£ø|ÓM79,ü…’��ÍpslºòÊ+yx©Ñ”`§N�h®ù˧– ¼Èßþ¤¿ŠIQO¹ÔçÍ›q�hÝu×-? jNà„ð!<ãŒ3j4Í›7û þ~(uº÷Š›ç›n®6 !ê1§ù¼yVª DnŸ!À>}ú8!BM 5ǹ&`ÿÄi¥&½âH½ýfãë›YþÇ^¦íLBˆzÌA¦Þ¾“¨$U«6ƒ«~øaä E¨9�SO8‘Ê=ä�CpM¨©STYÑ0ÓŸ‚FûÕ?LÏkmƒõŸmMï›æ„�P0·¿óÎ;s³Q—œà ™vìHë^òòò\M0|øð\¿²{¾¿†G{ýhjkBÔs64=ç º¹qAˆ9¶­2‘N^ˆ ³gÏÆk� SA»ì² ÃÎ5’|øá‡µkôS–D£½;üGþKQÏYÝô˜i¼/è&bv(ŽC£F�rB„;ÔN9å” C«üHp¢ƒ®ºÉÍÍåtN*y’B |Ê4u6­fBÔsV5Ýãsè S!æ…Èï«.$"¡…Ÿ•Úá ´Å[8¶�¦à“ À×dšíƒÐ\„¾3�oB4.0ý`šk*‹ Bk¯½¶£÷†npùùùNˆ ÷Þ{/óB‚¿7a„ê>y‘Š[è†C³¼+ü»êŠ¢a±—_÷=5lß^v·ÕV[9æ>&Ožì„Ò¡CR¶‚NÚC‡­öÔþûïÏ ¨Ð+Ë4Ñ·jwô'|!DaÓ¦¡Qö=aWäÃ?ܽøâ‹Nˆ �?þ¸kÒ¤I… ´ÑF¹~ýú¹êä“O>IXKxrIi 3îKS+“¢±Š©“Zõ6øÑ¢U›n§ë¯¿Þ•––:!î¹çž 5!D`zíµ×ªu6èŠ+®( œÚK|sÂ_¦M[˜„ ŒüÐêܸ,4·lÙÒ�3Æ pÎ9çà�P!±ñ¡‡rÕÅÈ‘#Ýî»ï¬&�óu͇®WœrÔ~É·j§ D %²Èìõ×_wBÝo~­v¹‚×Ë5×\Sm³A]»v¥S3„r|*î]¿¨QÑ@¹Äô{e)9ìYpO¸ï¾ûœZµtëÖ-r�¢«’í¼Õ5ÔªU+,‚’+¼Ëü�꟦n¦�LBˆÊÞÂgz\«v°ã‰.¹¹sç:!SÒ³¤â¢‚¾ƒ\+ÕAÿþýY²Èççs£BßšÝßtŸ_O"„hÀû…n5e†W;„…«6ƒ«Zí ¸Ø7¶ë œœ8m¬è:an»í6·ùæ›'?7ZêëA½LÇ™„ œí¼ï֔ࢻ°pG¦Kîå—_vEEEN¤ï.¡ë®»Ž}SqAˆk%ÁË´iÓVx6èøã�gh:x-Î4 õ]qMLBˆFЮý€ú[�jnˆÁÄk¯½ÖMœ8щôLà 8Ð|ðÁ¤âbƒ2×¶Ÿ®Ð×êÙ³'Ž|­àu8Á�Ü®Tœ�‡=LŸ{…EqA¨iÓ¦¥ç�w7"5(¤¡a)®7ß|³ÛtÓM#ݳÃóe 9ýõ×îÿÀú�6mÚ8»æ‚Ÿ·Ô4¨iB4"gíΦ!Þ%'¢Q�®§|œ“ß{ï=GªD¤Z¥Ù¶K}°² įiÕ¦³mðàÁn9Áö›Ggfh>èWÓ[~Ä@ш8Üô¥ï<ÊŠò”3Ûþ<›*Ã=¡oß¾ª ¥cÇŽe·TÐ+Ѧ�a)Ý“lU�쪼òÊ+14]žS¾t|l8¸åRqP¢‘±Ž_v7Ú¡‚ˆ”\© qÄŽ@ôÅ_øåe¢1³lÙ2Ç)è€`ãnyÂá˜cŽqŒ»îºËí·ß~eV¿) !Z褻û|­ð÷š5kÆ× ¡ÅÞåã“¢r�ékÓL~øánΜ9nEéß¿?›X9e¥ By¦¿M§˜„"–ÍüÓjQTâf†I%Óð¹¹¹NÔßZÐüAº�FRoÿ±èÙb‹-ÜÓO?íª¾.ld� B%¦™¦Ïý(€B¤´ñé„xÚÝyç�]›6m¸ùè4TOÓp³gÏv7ß|3ä¸#”Ûë(8ÍR×c¥C5@§$)ÚTA(Çׂ:U–ŠBˆCM¦n"qÍ ¹Ù¤IZŽ”ŽêCõ'Í›7ÏuèÐ�4\2Wqp âÏ«s5�‘AÈןXÙÐ×t¶I!R6%œìí{J¹�¤7 Ò·Ür 5•ž«cXÇ©„}OlG%ínÀÝ€ZÐÌ™3]u1eʔؓ�|süöÔmLBËj¦V>uR|ÂböäÔSOuï½÷žo÷µ §Ÿï¾ûŽãQîÖ·ìjߌ;kÖ,NBQ� | k‚À¡ý]Óº&!„ˆeUÓ9¦11¶=±bðñˆ#ŽptcÍŸ?߉ÚsƦ6×½{wwÉ%— XV÷QË«î:éXNB¡¨Ôm¾Ÿ±v=uö×—BT:#4©² ×´À>e£G�V�¨a>güøñîÝwßuW^y%'Qvk@VwÓŒÀÇT34AÄ!¾nÂܸžÚ˜„¢RÚ™¦Ä§ãâô#cgÍ'Ÿ|â÷ÓˆêN±‚'„æÍ›;N ;ì°ƒÛ`ƒ b×*0˜Š‹6Ôb¢D‹8Aè“BTJWÓ »�äFÝЖ§NDÓÂW\A+°NEÕ4û3`ÀwÓM7Ñ\ÀrAæ¶x­“ Bâ÷醻îºëøøš ŠqAˆ`Â܆Ù5µ“I!*å-ÓlK£,±›[LJ®êé9†$y ç¦yÍ5×`–‰«³‚ÒÿH¿ýðØ…r² Ë�NäɃ.5AªeË–¸'ÔäÉŒvpRoáï‡ï3a§´~vMmeBˆJùÐ4� d7³@Z±`„oOä ºwÜqÌ©,G�\pšÄŽáSâˆu!R1=å”SÜØ±c]M2}út‚MQ'ℹ)|o×ÔF&!„HÉÞZ%Ën(9ÜDªStmqsÄL“@D�‚'|Q9íÛ·Ç.‰“N•:YÛ@½ˆÓSM3yòdæ’¢êQ<|$N>ù䟪b×#„{™˜²ì†Ršzw˜^îºë®ŽÔÚÞ{ïÍSîrŸˆBˆ›)©%R9¢rŽ=öXN8•¾Æ¼®;í´“;묳Ü7ß|SiO輋ú~Ø3”8ᄾð3hB‘’6Þéx©)n4ØvÛmn¦íŸ}öYÌ1yâN™ 2PrŽëŸ/¾øÂ‰Ôäää¸}÷Ý—‡€”Á‡ZÑQG宺ê*‚{­ÕÝÆŒãpÑŽ1¼MX÷Þ;U™BˆGLãLEá Dð`…ÀŸþéî¾ûnR1 «<ÔJÐònÎU˜Þ ,à5æTA3By+vÐ Ž“HëÖ­]ïÞ½}\í1jÔ(jUQï7ß[âÄOüÀ®§•MB‘Ò²çy?#T B ŠÜ<ð€+,,¬`ÓµkW–0™P×V¼B•Ï®N4Ôw8‰òšñ@ÀÉ”×›´h‹-Üc�=æpO¨ þþûoR€‘ïñV[m•°†Š/+»ø„b Ó¦¦Jv;ÑòKW[Ïž=c�3i½Æ)�›"7Iÿ±)Åý½÷Þ«æ„Ê¡›�”¯-ÃÀ4)�åõö§N_Ãßÿ�aÙ¨÷˜‡—2«O½_ÙÅ'„Çšz›f›Ö¢] /™Bã¦7xðà”Oë¿üò Þq´sSJY+b¦„ÏOÛ1sCuI~~¾ûí·ßü–ÑúIVV–»ÿþû3?Ì[ÝvÛm®G�ŽzQƒƒ:§´¸ Tb� /¥ºð„‚|ý~�ÐS‘Á ’ÝX¤~N:é$‡ee,[¶Ì}õÕWÜ(™ác#˜"‘J¢æQWà@€ó4Á�޲çž{ÎÔW³R=ë´ë•IlFF§³¨ Dp*9þøã_Huñ !ÄꦇL¿™²�ÕòèÆò]l4 ,W°à&É �'wŠÖ|.‚JÎ !»A¹I“&¹º€¯K²9gOëŽï…ÚË›o¾YõÎ2A‡#]pQAˆîÉ› ëžêâBˆ¦¦§L£’AÈN0…¤Ó¸3¿úꫤ­–» >bÄG­ˆ›ü>ûìCšŽVbº¦xJf;«OóÕ>ìß9ì°ÃßÿFÚŒ©i‘N2dˆU‚´ �{QAˆ†…"{y"ÕÅ'„Û™^5MM!k(IvÆqBøøã�ÿwñ›Žº?üÐQHoÖ¬™Ãì’";7}vàdffº:€Î<žài�ÀÅ�ïQÿ¢Û¬Šÿ^ñÌ3Ïà„hhɳkèÞTŸBfúÈ ~´ù“2oµC  ï¿ÂmÆsçÎu=ôíÜÔ˜HձޠNÖ‚óý0pËœ�¯Q± ©<±Š'Q9�<òHœ{Nß\Oך„"–ÓL™¦\ßž�g7�„É‘>»ãŽ;ÜÈ‘#]uÀ©hРA|NêL WÖ™+u§N�8é%ÛЩ}1ׂë7§5ÆU~têÅ +“ÞÌ«l¡�B´ñN %¦ò7fn"´]ã”\�§N?¬¨Ó€.]º­p¨k”7L`GÄÍ•( K Ùìi¬ê뉋ìzjaBˆXnö­Ù‰¤’>oØÁp³f{fc£{÷îüÃ+nØà¤ža´³cDˉ2j£+� ³ìz:Ñ$„±Éc'YÓ@´jcY$b]'â,{¨«Ñòÿ¥ië¸Æ!„8Üô»);„¸¹pƒf—McmU&Õ†�iìÚ Ú¶ñ¾Ã7ODü⪷ڛf—ÏL뛄"v�ÐoÙSB˜Œ^ýõtA5Ú­¥mÛ¶�»‘Ò²�ÍPÄ Á ïUW]YSókȧy;¨uLBÉ)� T Bø©1ÿÁ*†áÇ7æ­¥Ì Å¶Ó%اO'þ�™öl‚PÔëF`o×ÒÛ¦5MBÉé¦?‚ÝqÈÏÌ$Ó*îÇtX´4F† F5 Œ8+<ÑÓ qß}÷…þýâ§Ÿ~âuò'ê™° 4Ö®¥—Rµg !ÄÙ>•…ƒPR{bæ9sæÌÆZÛÀ5›F ‚Pd]è¼óÎs¸€ Ãóúë¯ÓžMaø5ã]jÁi¨]K¯˜V5 !D$šF˜AHDZOˆºPcå� Ž:FÔÐ%­Ú¡å{òŒ£a#b­7)\\ }ëÿG©NBBq©é¯¸ ÄÀ¦­gvl!-..v�•¡C‡’Zb¶%²ÈNͨ]»vZûx½ðÖ£z­x 1€e…ÃϦ>©jBBq¥oÑÎ�ª ÑÇR»�ºF ø¤Ü¨ Å ^:þ<;;Û9�ù+µ²p*ŽVw^CR›´gÿcúÉ»r!D$ׄ¢ºãhOf•Ö6þù§käàNJŽkT·Fê’ó54N…1›@ÄëEšgšnjç˜ „7˜2}* ¦â¶Ùfw 'Ж+ &Mš„k8)Ȩm¤ä¨¥û¶UT˜­ŠÝ¦Š§ê|Ó@Ó‘&!„¨À-¦_}*4%‚7Ý“N:‰/7èt¾¤.6%Çàê¬Y³\:óý÷ß»£Ž:Š„ª¡2zÛÔÄ$„ÿáz Èá“Aˆí¢Ÿï¼óN‡e:ЫW/±ñï�L3í¿ÿþîý÷ßwé ›vmÕG\ !´Ø4ÊÔÞ´®I!ʹÊ4Ø´ œŽ#ݲçž{:Zq'NœèÒ�… ’‚ŒLÉ!N‡W^yeÚîÂCïšk®¡+®ª{o‘¦‡ƒ6>Bq‰i�i~8!æfÚ´iã&L˜�.EwÒ�‘ó/�¹fÍš¹_ýÕ¥!¸Kp:¦{y‚*ó®cM�ˆ„˜øN¦bS"œ‚jÞ¼9A(mlkþúë/VXðo�KÉÑI—–3C=zô a¥òT\¼ M“LÝ”šB@k¿Yu®©Ä”»0Äù÷ß3¬š6îЭ[·ŽmPà&ÌìÔ´iÓÒ®5û†nÀ’gE‚*5Í2½¡f!DK?P8ÇTBx©Ñ ÅIˆÅvé‹ìhP¦�¯ÉG}”nõ NÅÔËV$Ósó¼­Ï–&!DšršéG„(´“S-ÚérÌJ©ýDÎÂ0ÌŠŸÞ½÷ÞëhdHðd`—”du¡¤²M_›v6 !Ò�MßF!æ@°favfüøñéT¡þE�‰­°Þ‚.:Ö¤ _Ú׫3¡Å¦ Ó!&!DšÑÌÔÛ47„سÇ{0'”–›E333éãÉ?ò4´÷Þ{7êÕçá ´�;wæ¡dÅêAñÊñÎ g˜„iÄ‘¦¯¢‚íÙ¤_:vìˆmO:®¯¦ë‘Æúó–-[â«—'Ã[n¹…×¢¦‚ÊõR瘄iÂÁ¦Ï|‘8¾ÉvØaÿ¶w@VWWÇÇ¥wÄ€P)$ì�Fì½’&VÒˆb�!4„Á ,ÁÀÚé- Q$ôÞ…mȲ°°Ô·ýmoÿ|o&Î�7ïí¾-¯ß3óI,ÔŹçÝ{Ï=Ç;v¬³aÃ'ÞBŸþÿýï;\p�Ï5úgúwÚ!Ç|B潘>˜3 I 6ã>ذa#âLÇÔx/²gŸ}¶sÛm·i¼·Q˜?‘ŸŸ¯rlŸWE‹ò�wÞ©·E1_ÇôYÑ; Ivâœ6lÄptÃ'8ˆ_SEyœ©»�¸ª3�¡4Ú¼wïÞ>˵uI¯ÖF:²Ô´ÑX î½ÔÌVLB‘„¤IøEœ%¢–è€6ha“°�xˆöøûÌ$ä]¦­J°””'ƒG©Úíøí—¦®�wzЫ‘™™©ûÁP$ SRñšÅ@r9wâ÷ø;&âü Óñ¦cd†—éøŸa*&ày<„ŸáT4Gl؈šh†±Øã/ éÓoŸ>}<=Õâ1t ùÖ[o©y©ÏÝÿLsˆ´[ŒÉ»!îÆt'¨ÿB�„¤ßãþ(Œ—€æh��†·1K°Û�‚ÝØ‹ ìG!ÊáÂQƒ ¹(„Å(C9* å(E1Ü(@Ž!_ã9œ©»*6NÀ�ä/ ‰&Žþîw¿‹Û�nÛ·o×q”ßÝ»Eu׎ÉwCú3Ÿ7ožsú駇# IÒqZDP²iƒÓp ~‰Q˜ˆÏñ6 ûqÈH,Þ²Q‚H)\�#HA2d6`+ö .ä!Rˆäáöb'æáñH<æ³aãVló—„tï¡GŠÏ>ûlÜŽ0 :L;ÍÑÑÎÐçøo:,ÄdÝ‹Mž<9¸åÙu«Æ>ü­ÊhŽÖè‚sp3F`<>Äl¬À6#éC. ¼v2¥ÆN¦ªŽâ ä0Ò‘Šd_ŒDµ+°ë°˰2pZ†�ˆˆÞXk$!Ÿ3G�mTÈÅ_¬X±Â4h�*âjmlÊл˜ê.Á^hŒE¸�÷”Ö?  ‚¸Ói‰®¸Ãð>Çr¤à ²á6KªQÓD àòrYØç'1mÃ2,Â|,À×XŠ%øS0Cqv$ìŠlØh‡y,&Uðù)ÿâ‹/ÖBd61�ËJ¹'Ÿ|Reë>wCª$T‡…áÇ«ÃDL݉=õÔSê£Î$dra2º 5š5AÒiƒ�xálE&òQ†JÔ„�®C¦‘�¶c–z™Žqx 1Cpq$$"6^áØ­Ì×Ã⣣&£ 9~CÉeðàÁ~w�;wV2ÄLÂæ(RýõF(R’�”a-&â—¸ýp.zãLôÀi8ça ~Š_௘Š%ØŠtA.ŠPf—…C5ràò’ƒ|¸ÍB?÷GÛ°Kñ ñ.¦b"ÆÍ�¾°a#¬q#ŸîÝ,(>G¨s‚î;ô^$Þã½÷ÞÓ™Ž)}ÞŸuëÖÍyà�<½çb!ŠŠŠT‚®ßo$%!sÄz,Å×XˆXˆÅø+± ©È„ …^Çh5¤…pr��X¥�oŸg$¢ ¤c7R°k1‰� ^I¨?ZÆ�°EwŽ“\,(>§‰ª:NŸ†÷îÝëÄ{(?üðÃ~‡»i±V9÷ /¼àäåå9Ñü´»«³{v•£EpCŠŒ$SiÜÕDºr; ·ñëÏ7vDÞ¥ì%pû¸KÊ@2Öc1 �âfÜ€“Ð 6l„-Zqב‚â÷A¦J”7nÜȲd#))IE º/óùõâèJG˜‡õE ÇŽs®¾úêà' «.“Wé¶”"Ũ©…Yæ�…TlÃRcGô n@�ˆH@6l\ýõ³Ùõø}+Ô·o_µ°‰Û·BÞ8çÏŸ¯¯‰«ú};tÏ=÷8ß}÷�ͱgϵn n²*|¼*àÎHÊ}ìŠ }/l2Ž-§aN�˜Rm6nºé¦·¸X¯fañ;ÈMoe²³³ž¦ž*[÷û~FÇW:–ûãÿè¹W‰Öرc‡zç’„ªQ†b‹£U·jã(­Õf¸|͹‘…ÃpA!˱‰‹žՅ† >µÿ½{÷î•þÎþu¢ ¹¬¬,ÇAÉçû¸ ÏDj¬&——]2�òì…x £ /à6t€ ‘ øËÀ�KÛ¶mës±Ñ§áÄÄD{'äã ƒÿ4 Ýo¡G™ª.ŒÊ·Cìâjfçc(]Ž#iÈ JušUéU wÉØ€Ex#ñîALj~”jÃ; ¼ÊóפS�4iÔáp† æœzê©>K™U¼Ð«W/çë¯¿Žº�Þ¸qã´›«oß87ö#QŒ+(ªpÉX‰…x#p#ÚÀ†�¨ˆ§•„ü-8e½iÓ&džïÅúý÷ß÷»‘ÌýË©Z.777ª:hóީΉªâãÎâ’±ÙM²²ª}$ £Øˆx¢W4îzlØø GrE[÷ÜvÛmžKxþ�ä^}õUÝ�ù]¤µRÉöܹs£& ÝqÇúÒ�$”‹=H†d ÆªS‰Ñå`¶a3Vã+L€X+±¶a£>èÒ¥K¡¿644å´I¨ŽPG‰þýû×ÚgM�¶5¥Öív;‘êš~à 7¿Ÿz%¢¯†š9q{w87²°ÃF÷-fã%œ?w=6N@3´DkH+HK´@3$ÄÈÝ1•ûŒR—ê4í¬c¨� Ý÷Pæ^ëÎAG[7ÞxcTôáÓ›°«®º*à$~æáä£*JïbªL:EÈEö6àø±Ô\—Š ˜…gÑÓ&ŸèI­Ñ ÝÐ}0ƒpÆp<�‘ø;ÞÄ{øŸ`šñÂXddd6æ`>ÅTLÆÛ‰ßán Æeø1ÎÅÙ8%Û¦_ŠiÜùœ)Ä;}z¯c¼·�Y³fi'Tç £ ôðWI=âï¹~ò“Ÿ44 U£,Ê«ºQT�)¨éØ€íÈi@ï¸,$aþ‚3";ùØ]J;œ‰›ñLÅB¬Ç.¤ã Žà¸Ñg)ùÆ+ã"£R ƒ9`ê²��4lÇJ|…i˜ŠI�_árôD'4Ót·àkŽ]Ê}-,'�t’¦ŠÚ$TGÌ™3Ç¡ëD�Õd{ªê,â ÒÓÓõû©#ñÄtSÓ¸PÀN¨ÔHF…õè�Wi$°ux%ò’��´Â¹x‹��<”DðÌ� Ù8ŒL¤æ6`9c>Á$¼„?â>\�ÞèŒA:ŽÍ, •p¼�|òÉÎÓO?í)Ùµá;¨TG Í^ªõÅ,ôX½zuDM·mÛ¦B‹xMB¾†Ê5±Àvü çFNò±Ñà1Ì@ŠP–U¨@9ÊŒ�T¾QŽ™‰ÝØŠ•X„éÆ.j,ž1¦#^Œ^èŠNh�hiI€œ`8!>G9Ð @åǶeO-Ud“'OÖR£uOí.»ì2µÄ‰èV>êðpÆgÄc*ÇaìÇA¸ ÅMœàvb1®E3Øs´Ä5ƒEHC®m ïIZå(F>ŽIj6í:¦ãS|„·1#ñÅoðKÜ…_â9|€u(ª- qgwB~J³GŒ¡»“€ÔkN£1"úíCù´ Ž…$T w€÷S¹Ø…MØ‚$ìŇ» ~-)X€aèaŒœ†áø[q ¥u¯YÕ¨@)ŠŒ;©£ÈB&öa/ö )H2Þmó>¿ö×A[ 8].—Í:^1cÆ UÄ÷@�éÙ³§3|øðˆîJþÆo´£2U!.C>ÊkÙíÂ2|‹%X†MF2:ŽªFì€R1=ü»íq&b)2àn²sW«å(E¤RQ[‚÷Ww×]w9)))6ëxÃÑA[…õ^¤O9åçÁtŽ9âDjüõ¯Õ©hMBÕÈÅA££÷ÀqäùHFDZ �ˆ‰ø‹± ‘W«þÜX„{×€ltÂCHÄf¸P†+røjÛ£;ŒÙ³gÛÌã5]õÅ_  Á§ïÝå½÷Þщý©§žÒ�hMBn6¼%R°YÈC1ò�ŠÅHÄ{‰w‘ˆYXˆ¥ØÑÀ·@˜„~°âè€Û‘ˆíÈ‹Ü]�寬˜ŽÊ^] mb¡®×QœùõÔ£Õ+VDì]eäêw­I(ßc=–b%v ¹ëñ�7£ð Ÿ`2빎•âsœ!Šf¸“°Ç"¿ÐÀòÓºÇkÑ´Á™Æ{�SשS'gРAμyó´àGäš—³„"*•£µ,öG° K ˰É^’° ³‘ù#!£1¯a"1«P¨°·‡î8ΖYÁì�5Väó×|³_¿~žÝPvv¶Í@DFF†së­·ÜàÓ»£6u*íŽÈªCýšî¼óN‡Ñ‘–„Êà2ùø÷Ù8ˆ$,õ²Þ8’K†¬Åçx ¯âC$b4Fú0Ó±{Q^Ïú ÿM��K1ûPˆ+ú�䮽öZµ§1>½Ço¤¥¥iÄEƒhv–?”i:‘¼_ÒÎWeç‘–„*� —!n¯~©X‰¥†ÍØ‹ÃF"Z…IÆó…ð¡q$çË,ÁN¸êY1—‡Y¸Ía#HÇpC±2:§*ZþšoöîÝÛÓÌôðáÃN¼ÇæÍ›ëÝàÓüZÒ�@eÚ9,�ÄhNŠ�Ä{¡¸pÇàòá6 h5R��J” ë0Íxü}?Æ!c1Ò0ãðfc;5àƒv1Và–àõ’´qVEo wËß1ÒÅ_ì¼þúëqßKN÷cW_}uƒhoªÁ¯~õ+'555"ï».¿ür%ËHLBUpa¿±›I‡Ëˤb6" Ç�b‚jda¾Å ÃPŒB"äLñjl<ßb;2‘ÓÀ]]þŽÎ°„Џ 8ˆbÔX±q7¤ 7ß|³³jÕ*'^C÷‹/Ö¼�/Òz_¤;%õ�‹´Ø»w¯1–"âvAE^“[e?\>ÃAd!×DZY.²° ßàŒ7J³?ÇBc'µ«Œy?Û°®F¼g:Žù¸6š8Zc ’‘}mÝ- 8u”¤. ‡ŠÛ‡ªêš}Ë-·8ôÍ÷œ¦+®¸ÂùòË/#îŽmûöíÎ\ ß[$&¡\c'”‰£È‡eFCc\è`Þ!ÍÃ,,ÀrlÆ.$va#’py¨i„blÇŸ›¾`ÁÆ F™öZCIt¼²j»XgTµ.ÖãrØ�¦Ì* ÝsÏ=#Ñ|/¤ÙBãÆ�‹¸F¦ÿýïU8©÷A寖ò&®¸;ŒƒØ9ÉÄÂ5FÄHi]da:Ά�&ޏã0 �‰|³æßŠ,u•_rÉ%ΧŸ~wÕr*a^¶l™ó§?ý©Q÷&ݺus† æ;v,âô1–"¼I¨á*�WϤæò!%¨F1\(7þFûŸ¦Q�B¬Ç°„HÀÙ¸c�ˆ¥Øiœ­™£u­ðªëåÿõ×_ï¬\¹Ò‰·(**òìb‹ÞàÅZG›ªBÓìžHŠýë_N÷îÝ£5 •ÀÝ€$”áUö]í£Â[îòn/OÂF£9ºà" ÁhLÂçXŽÈDvx“U[•×i§�æüâ¿P•WÜ'lܸQeë ^¬åôÓOw>ù䓈ÚM¾úê«j²�IÈ·R䣸–$”jÌ*÷3ÈT‚FÄð$ÀFˆî�š£ ~„^¸·c8&`:V"™p¡%(ýà;›ˆt'¢£õQSWèxŠüü|ý¾U1ب#¹Ñ£GGÌÝš’áÈ‘#�®]»FRªDJQÕ€�‘ RQKÊD–1¢»Ì—’——Ò�£8Ž)haNPÍÐq ÎÂ�q5~‰ñ!c»13'ÏØI5° ªëXIm}Æ�ïäååÅÕP»ùó竜¹ÁwCtÔÖÐ@5D�˜û®?þñ�:j�¤$äÝ%¡ ÉÈÀñY®ŸòîÃÈ‚ÂaãqlAˆÇRÌ�ü‚›¤ÐíÐÝÑ—â& ÃX|�ÕHÃAdÃ�RsÞŽU¿D¤>cùðÏþÓXPc?xç£vF nq£™=*wOOO�˜òó¡C‡ªÉj'! ÀDTwÅ Ùpá Ò�ìG*Áâµ…X‰ka#Š#-Ð?Âè�‹q+F`æc öïŠë¾—²stÐÛ—¸)Ý^²d‰’oƒ[Ühf�š…~óÍ7N$n*=oTÁE�.곃|$V ¾÷š=tG°Ɇ£ahÌìÆ<ûí~l¢j†h�Îè�ëñ^Çl¬5.3sP>QÙD¤�Á‚ tyóÇq‰‰‰N¯^½Ü] mÛ¶êAç©H‹„(--Õ#ÜHã`îhràBA 4«ä’!{á2dbä0²Qb…Ø��<¢mØ;*ôÄ5xïb66b?òP�‰H¥Ûút¯;†X l½Ò½Nƒl ;Ô�@£Â#âÑ*Åê†1aW—1‘UÂU‡pÌH+@áçÞó‰lØäÔ?ÂÜ…—‘ˆåHG.ÊP‹IÈLDô˜S£Ï˜=š£½�FhÁnt™¶º“gff:áºzëH5 cÂÎí5 â\u Ó¨šjäc?Và ü¨¶–?6lRj�Óq†` ¦b‹Ñ ¾U±–ˆhÒ©÷41·#"±ª”Yåéf�µß éS]ÂIIIÆW%!ßeØÙ(F%ªP镬$5aPm ðÛ„ è[W"²a#-ÑÖx u-ÅD,Çn‡ÛHLQ�ˆî¸ãÍ߉©ö>ß}÷�îNŒCÃéǸð �?ü0ìÉšj?M}�Ì1⽫ˋÕ¼=ÊEM˜Ã…£HÅdôkh"²aï–Z£+~lTêMÃFdDjçˆ@ÇY«lË–-A+Ð}Ê�œ””“i÷´7KJ¿ÿýïÕ-¢Ñ ÚI©¸Aï…ÂþΊƬº£Ò]U¤&¡â %6³!i~»¤5aRmjÈ~¼‹ž°a£É’ÓI¸·`fc 2�&‹‘žˆto¢»†µk×6é‘XVV–³nÝ:í"ôÈRw4ÚU¨Cµ.Ø=;–¦Ž3fh ƒ±P7žº.Üu×]a}/¤d>yòduÐŽÌ1Ò¢_Šï±Çx’À÷Aà�º‹‚¹2Àp´� A‰fè‚óý$¦ÜpŒË´açyç�§E¼ÁGsÚñìß¿ßÓ4õý÷ßwyäçšk®q àœyæ™ÎÉ'Ÿ¬j5µÃQ2RÕY“æææzÞÑtîܹI’�9änðàÁÎÖ­[Ã6µ–žº^h^T$ßUáÛº�ª)8†ê¦JšAî°P —! q)lØyb:×áYLÁZìCŽÙº(œ‰HïiÔó±ÇsŽ=ê÷D‰C�4jz÷îÝΆ œ¹sç:/¿ü²¦êm�>±kL¶pùéÍ�ÚÍ( ‰'1=ûì³Ú15YÓÒ?ÿùÏNŸ>}š,ù˜Å üžTM¶ŽJ~Ï?ÿ¼¾¦Ñ^”Pä=’!€Ž ™H†¤£¬‰w+î ú“ÃX�§aÃFX£¹Qü0Ã1 °é8Œ e�i]TßËxÝ©há}ðÁ=su†®w7ºoQ+Oû˜_ÿú׺OÒQžî*Tά[ß_ Í×në‡��*óÞzë­&[ÔçÍ›§]—Q9ÖtH :âÓØp½Õ Û°>¾öúÚE{*0v‘àýÎ!ì“&LU!˜,í=é¦Fb�‚ -ŒÄt †âm,À*lÁ.¤`/šÝ‡Ñ�ÇšLjU¯²’;%ÑGíctŸ¤…? ®úöJnJZ/¾ø¢³iÓ&Ï£ÒÆFZZšv`FcϦÅïï‡$äv»�p„Ž:xà�Æ7/ ¿|¸�Ždd¡ºžC튚ð¾©8D�Ž"{16lD|$ º¢7.ãøß`¶#.TšˆBM%ź§Ñ|ÝÙh<·ß~«½Ñíƒ(~ÐÈSéÂ>hIèÊ+¯Ô[!ÏŽ$Á×Jeç�îff3ÒýHÆ”×qg³)8l~ðŠ"Õ8ŠmX�Á†�¨�éÔ=ð3ŒB"V ¥‘”ˆ´xë“»ªáT÷ôÓO«Ÿ›ç©   Ñ HwR¯¿þº¥ê0¨I”��Þé„í=ó tܨ£Áhžªê=Ž!ߣ´Žû£ÝHFrP…ʰKÐ6lDwC;ãfLÁ&”…#i¢¤£"Ý©0Aeت�SU×ôéÓõ(VlJ>MrQ¯J¾Aƒéç j2Õ±äå—_î9> WðÆJ‰0š»%TÂå%ߣ¨Žç$ã€ñm£Mvâ Ø°sÑ]ðÖ…âhÎ,4Ð1›Ž«t\¤"µÌ™2eŠªÉ<%Û………JM¶‹àÇÑãZMèçÚ1œY5¨$´sçN'\±fÍšðwKh¼ äzí†2PXÇ�Pö˜sŠ¢L 20 '†�˜Œ´ÃDM š|ôæGSKo¸áUËyîy>ùäÏQÛ±cÇTz´£+ýø/¼ð‚ŽáŒE9¸IH=ÛRSSÃöPUÕçŸ~ƒÇRH„-Ê…ÈA^Ý®ó�e$D™2d`A¼ÃÙ°ÑÓmcß�;=jÕÏï~÷;çÕW_uV­Zå?~<$½ÕTM7iÒ$%¿²ƒqÔx饗:ÉÉÉN8‚ãK•³«}�~=ñÖ¼´î(M@娇yø)l؈›xGš*±«*K;uP %O…tINs�Tag”*‡IH}ïÂúP•wXÑ“„¬b¤ás\6â*nÃú¦HBz'Ô½{w½íÑ}Ïɇw+!?’Ú·oŸg÷Õ³gOã(4.¾øb5^ Ûh =îÚµkt$!+[ñ..ƒ q·`QmGr�T„éAª 4Ômܸqž墢"'¡â†—^zI—óF…XèðóêØ1lo„n»í6çÄOŒ…$T�šU†ÃXŽ1è 6â2~�eþÞ_Ò¦Fo{Ô…@_}õ•§´š[Ë:Hk¸\“–c×gGØ¿géÒ¥a{#D¹{£ïp?’mŽXˆ1¹Ø�yø:À†�¸‹Ð/#É×N¨®G™ùh±W¯8U»©-¬ït´dÉgÈ�!*ÇnâxêׯŸ§mO8‚&±z#¤Ñž„\p£&Æäa3&áZ$À†�¸‹–è€ßa¹¯7µèS¶J€u÷ J¬={ö˜�JÃVšL_8ÝE™£º­!wcªœ?¾Ž`&“F_]!¢ú�PM ©Â,Âpœ 6â.Z =NÁ8,­Oâ~E}×ÔÕZo}tù¦þh¾çq¨ظ OêÛ·¯3sæL'ÁÏ«ãQíT#' YnìÀÛ¸­`ÃF\&ŸÓ1 Ÿ#É×{ »5Õ1�FW/\¸Ðsù)A"T_8µå1Ž¡Â—„Ôâ³Ï> Ë¡7ß|ÓØ †9 Y¥ÈÀ|<Œ°a#.“O<‡i؈\-M|•]óðQmoÔ^Gì|Ýû„õˆ¾pjdT„5�†–ª+ èkê Ñ|#ž$dU¡YX�Q¸ÍaÃFÜDstÆ ŒÁ,lAvm½â¼Ë®¹ÜWá�:è¾Å×½OØï�£­Ê<Íjò÷@ -Yg<¹F–‡¥2Žyý¹Er*Dªb´êm6b".ŽÇ£76ùtÁ ƒ9HA~ ;i×®�.×uô¦‹n£ÓAdÅ�4ATcº�>iáOB=zôpÞyç��ïÓÓÓ�;î¸CG§µu´ˆ„$äBQŒí~Ža+¦à*´ƒ q èˆk1kq¥Þþêê÷vê©§zv³gÏtœvؤRa>H�˜$Äq˜îfB¾s¤;E�ï£øwÎ$T�ÒÙ UÃ�$$â&t‚ q' 5úá5l ´ØÀüd¬æžj·£OÑ$—““A÷>¾ Øi¨A»6ý>"* 1Ò\G˜!ÿ2ÂA-ƒôçYk¢·Ÿ½jšä“‚/pN °a#n¢9ÎÀýX„¼@Lí´é»ZíèØíË/¿Ôøë»÷ñ]6kÖ,uæjcÒÆôÑÓ#ÞüãN¨ƒnz¿UëÎPÕƒ�:u² ¨aªQ„=˜�_âäx|pjÃFK ÄsXÈîG�U=vöÙg«Á¨ŽÜÔ_ÍÓa //¯£Â7°íÞ{ï5 "/ ©0àå—_y‘»XU2ª¨¤Ö#׎;Ú$T?Õ(Æ>,Æpœ¯o6l´Æ%˜ˆ$_½µ¼�ÚtÏ£»“;ï¼S»õVSU™š]Fð±›ï±ÕTi¡5 "/ i‡ö—¿ü%ä;D½•RQDýþl \)²° £Ð-aÃF\F ÂdìF©¯R ´ÎþU*|Í5ר½Ž.ñ=»žüü|Ï‚m‘‘‘áüéOr.¸à‚ wÆnìX ªÓô^'äï¥è¡)¶�Ì|²I¨nÕÈÁLD/´Šç{6ZaÞó×í@ÉGw=:rÓ@9•/k¦ÏÁƒ=—ùÑz ûâ‹/:—\r‰ñ)>x;eVÇ]Úq†24EöñÇo‚»6 ¡ ßc*.€=v³a‹p&` Pcâ Z}Ý®ºê*çÑGõ¹%%%iqŠ¢#7ß=áÆŽ«{,%Ø0& À“�v¡š(Êp¹\º+kø›„*P„#X€Ÿáذa˰Ñã±ÙÞo(:¨Ð �²?~¼ç¾§   *�ݼÛШK÷õ×_oTÂEGR7ïP84G¨~;E›ˆÊ‘ƒ­øÏá,Édž í1 ‘…J¯#¸Š”i”µú•effzv?ÑŒ×nN»õî&j�(¨€"”¡I®—^zi`#lªF>6##Ð Í`Æ #ša0‘ê«�·>%Zð¦OŸîù4\YYéD{è÷0mÚ4�©Vq….ÒÃ�€ž„¨HÔÑXH? ÐÙ\Eu�p°I¨‡±oàÿ-vlذq Æ`rQã�…§œÒ\MòŒ‰�ŽçÍ›§¤¹Ý EÝT³œŽ•€TdAµ_D& ä÷tú`²ãQ�X8p Ý ÁK%ŽaÆã´† 6j‰Ü�Dì ¤¶.¤ÕaÔ¨QÎúõë=ûÑJœ}ô‘ÓéÓ¼vaL@�OBÒ³gOÏãÚLJ,‰óAÄ(L€MD娇¯ñ Î ¬Û� 6:à,GN   Ž¯N:é$碋.r~ýë_ëžH‹“F2hôAD&&½oáNKeØ‹i ¨ ¥ IBÚ�j¬Â7ß|’¾|ì¸ô W¥ì6 ÅHÅtü'¢lذQGœ€‹ñ¶£¬>‹Ÿ¨BJ�’5žA—Õ—_~¹Ú÷x.Ëï¿ÿ~GåÜûÛßœ>øÀùÏþ£ò^цçnaíÚµÚMé¯Õæ'hÉkß¾}j5ãy`Û­[·H¼*BnC’ÉT÷Zj«‰§!¹OSq‚úê%Ø#¹|lÁ{¸ O@6lØh‰!˜�}¨®oò¾Ÿà~E‰I £*·ôŽEsxÔ�À›ZšÔüTÇ<êÝÖ¤}Δè|ðA-ÔúµDêñ›9õMBf7í›o¾ÙùöÛoCr7D5žz<¿ªÂQ¬À8œŽ6õ{ÿcÆ�N�åÈõ^üÂAÉK¸wíÚÕ$Qõõ†nÐ8†ˆ.@@)òQÓÐÝÐ9眣±Ú …ä^ˆvAª˜ŒÇ#¹ ìÇ"ümë_~mÆ�æè�IØì=¦Á×±»ín‚þž†Ý“Ú58±P;!]Ö«úM ed=Bõ¯¼¡Iº£Q3Ym}7¤ªÈ¹sçên-ÞŽäJ‘†/p{ÃŽÚ°a£%úc:RPîoNG=å¬Ä îÒÆ´ÌàQâP'¥ž9@#GŽôŒã¦Éªg˜w?�WŸ]$]͵ I¥?‡Š<âéHÎ�-˜„+š&Ù°aç-0¨zÏ ÒXn%ƒW_}Up*4ÐÝJ�-[ø÷Ú55ª@ÕwwÜq‡Þôè“·ßKrUämß¾]ÉÇsôvÖYg)qª¥M’Ox’�y7¤Â�mÛ¶Ù*¹&ž÷cÜÿü¤ñw?6lØh‹ÁXŒL_ CË”ô0U÷ ê–­Å^•oµî0ød¬OäJº¼®õÞ€D¥‹OŸ%ÓZàôiûµ×^Óª qÍ,Rµ›ªê<Ÿúõ«_9—]v™’�¾½’O˜ŽßŸ„ô{?÷Üs�™3g†¤\û¿ÿý¯~¾X>’«Â,Â8§é� v't3–â°¯¡uZ؇îlذA À�!Cœ®]»ú]HØ=©M;%0}2W²ñ¹[Ò1ß#�<¢ø*�öùãqü§…NoaôsëXP‰IfUQ§„¨†Þo¡Qß E•Ë?ýôÓNvv¶ìàçP÷qýùÆbªÀ>LÇÍèÔt Ȇ ­0+q5&%•üª+ÂŽ;tì¥#1}i��ªÍüÞKpo¤c-PÚA©ßœ&€z[í€4¡SeÅZ4•hü¡±È©¬[T¸àë80Ì (üIÈÜ=ªÂ055Õ!‚^ À @ídc- •a7>B´mÚÙ?6lØh�k±Ì×qœ’ÉÏ~ö3OrعsçÕVü¿’�ŽêüV¶ééÍ7ßüaç¤D£…Ñ;Y]yå•θqã<‰íßÿþ·sá…6ú~!ü (üIHo¡4ïGÕ…¡í”Õ‰Ýs¬$¡"ìÀ8­`Æ�&Ž\†Å؇*ÔˆèXGóutü5gÎ�¾V²PGý3¿ÉB;ÍRCM% M,}öÙg½“–ŽÐtgäùvŠï¾ûN §ŽälòiD2ß ékŠ Ó…>xh _ïlÆs8'¸ÝlذÑó°×W‰¶>Që®.þÕ¦EEË–-ÓÉoß5%'í”è!Gñ€Žï¼+ßÔÅZwFšM¤6>ºãQ³ ¨ ’�Zêh‡ª˜0a‚>hDó×¼ .¬Æ-8)øPmذÑ“° E¾?’‚J²U妎*ãj=ÿWÒ§â·Þz˳{*..Öý� ¼wB*4Ð}‚î.<ßþ¼óÎÓj“O#“�îË´R�¾P÷†ú­Gr•8‚¥6l„(Ú`(–ã ª|-€¨âMꬂ"i©‰©’‹vMê\ ö.Þ\µXé¸OIMÉM ˜ºAÛäÓDIˆNÁÒtSo’Þ|aH@±‡g÷cÆ=’›†íp7vAÔ!%í†Æ�ï¼óÎ;úkŸÉ‹D¤ä¤c-^6ù4Q"ÒŸ�v™:> eÐ)Ý8vm¸0$ øYxîlذÑ�b 2QÒØQr}ûöõìrîºë.ýµ­xk¤ú–hë/''Ç e°óÒ»¯FÉ…8Ííè6l„)ÎÄËX…ýÈGUcE"%#íttónÛª‡«�¾Û Ñh6îÁY‘óÕ† ›ˆNÅe�9Ø€4ì7’R™™”l≬$¤â¨Z-A÷ õð‹–Ô=ò� 6Z¢=ÎÆ@ Æ0¼Š/ð-6#,Õ6ñ„F ïƒÔƒOï²ôP8±ÿ~çꫯnð½PŽàfáVt† 6"<Ð �q:ÎÅ@Ü�Q˜†MtWÈCµM<áMDz‹¥.Ú�è(.¡·a<ð@ƒg …àèšèkÃcÆ�fh‡ö�è„Þ�ÙHãÓo1 I�M<ÁSÛÛ ûjÞ“A£îu”ÀDH¨¹m^^ž“––æÌ˜1C]ÎU” žæÜ\[ƒèlذqÚáLC&rXL*´'áXþ&ÑÒVI ÐäñCWtµdš4i’ª°@•už.Ï?ÿ¼§�íO<¡QéžF´¿üå/�»ï¾[Ö5¢C?§º¦ë±±Èz:¡÷éÓGI(RÐaÌÃU±7†Á† »;j�q¦ .ñîE'6±! Ý'x“¥¤ð(ô¥K—:¿ÿýï•@ÔËO­”ôhX DUv¢rk­‰îœD».�æÍ›ëçŽÔ�vU8‚ù¸)¶:!ذa£NÂüS°Gà6ªç¬ ñ.Ë¾à‚ œaÆ´ "©3ºÚ)©/ L¬“Táâ¾ØJ@6lØh‰¸cŒwE¡M>–Ù V³˜žzê)ÝÏÔyÃHßVwHÚÍÄZª† ‹ñHlÁÙ°a£ú`(>Äd‡'ùXæNˆ»ÝßÔ™„t§ûíœt”æ³ÀA½üzöì©{¦hL@9øÏÄV/86l4G?<ƒÅ8dÜûXaLDÿ@øàƒzÌù Æ­«©¬Š ”l|öüSrÒhð'Ÿ|Ò¹å–[êLDú¹ÕšII«wïÞó¡{£p%¡\¬À´… 6b¨ î,Œ3»m[ág Ô#ÑE‹9¾B}ä>üðC%Ÿw@ì¦t¤§ä£Aƒº3ÒèuÍ‘òûðTÿ\»¦ë®»ÎSI÷ÒK/©<[ÕqJr¡N@Ø€ è6lÄPœŽ·±¥¨±"/õèÑCID»!³ [ƒ•œ4ñV;Ÿ�tœ§2ì5kÖüp¤GCR;h‡äwv”vUÚ]é˜/99Yßß“èN9åí’B•€J‘ŠÑ?¶Zñذa£-Æ#5²�ß,v&Õ¥ ¹>åååzpªasš\«û"½ãñ¹«éÖ­›v0>Ç�Oœ8Ñ9õÔS}î‚Ô­[Þeáü±vUJn¡H@•ÈÂ'¸+¶£Ú°a#O`ŠQcE6ŽÁ*nºé¦’yóæ©sÂ÷@rסCïdàyt饗zWee¥ãü8>’jw¤£ºéÓ§ûlbzýõ×ëˆ0ï�²ñ F¡lذCÑ q5Vt ìÚÍ@ÁCï¿ÿ~ÉäÉ“µËQ·Ï„ ã9=rݽ{·ã+RRRœ~ýúy?ϸ†Ûn»ÍÉÈÈp¼cË–-ú9U°ìTŽ$¼‹³cëΆ ­1›P†+ªîˆÜŒÚÞÄŽ$“Š·*•m*"P;žãÇ�ûíŠMgnŸÉëá‡v ïX¶l™sß}÷) »3F>¾ÆP´� 6b¨î4Ì@*QcÕ[ÊQ 7 Pˆ’}MËH49¢+ò7úAVUÕ¦»#_±råJÝ%ù¼Gâ>Èg³ÔY³fé8N»¯`m“1=bëQª 6ZàJ,Á÷u>FµªQŽ"äâ0ö`¶b 6advc?òPŽv?*Õ>çœsŽí”„|¶üQ‘�ªî¼“�ªßž}öYçèÑ£Ž*†xë­·T˜ ÄÌ{ÄãXŒ'Ð6l؈¡hƒ[1›‘ƒ»«)A!\8€¬Åט…0ÅsxÃðîÇ=�'ð.c‡Ùô5ØÌ��Œ:h«€Ao‰ÌÐQ›º+téÒÅçqÜ#�<âdff:f¬ZµÊ4hÐv%Ù &W7¶bÄfg6lü—á lÅá8(Ñ®4M²‘…4lÂ×øoc4þ€ßà. À�Ð v!o�Óq3>Ç.ä 5ÁÆQ]�’‰Þ>|Øûq«F<¨k‚ÏÇ­J`?hŒ¸ªêô¶H h3÷G*•ÞŠì & ½˜‚Á±ßÁ† ;ªát ÇblDZ(-T¨F…qGSˆlÂlÁrÌÁLÀ ¸7áJ\äåbôG«FVfµ@üË‘‰òUÒU2Æ¡L3ƒÖ¯_ï©’{ç�w<#ìtïÞ]IÇ'½Ò·›:uê.º.Ô.ÛpAøó+À>LÁ=ñSŒ`Æ­’ëˆs1ó±iÈ óî¨ÊؽH Üȃ ±ÛŒ3ÕØÉ<†›p zãtF4CBˆË~[àb$bWÛ#•“ŒŠ8vË%±j¾P¯^½TÝVk×½Iâ-�îµÖa=i”G9r‘†D\‡Ö°aÃFœDZ¡=zá7˜ˆ/°��£ÈC1ÊêP )�£nH>ràÂaÀ^¤bÖba>Å$ŒÅüƒÐg  Ú 9"|Úc° GÃUàCÓ2éÃHr‘ŠùxýlW6ì1]tÁ¹¸Cð(d8žÃŒÇkxoûñÆb4^Ä ÃPÜ�›q5.Ã…8}p&NÃIè„èè¥ZEq‰ü£˜�ô8k™T¶âsŒÇµè€„&ü·‰î;%6l47´@{t¬C'œˆŽh‡Vh�æMp–€¶èˆ1ðV«#nÀX‹Ü³U†CX�O0—¡Kþ<›Tlذ#qÚyíDZ†á!aË{³ÕÃñ9’cpŠ­»±“ñâ$´Á ‚�„br'dÆMDmÐÁžÝ7ybí‹aø»áFEïz\Ø‚Yx¿Cc'{B|lذaÃF€Ñ §à>¼‡�8Œ¢=ª«DÜÈA:Vb:ÞÃÜ�^f©» 6l؈üh�î¸ïb%2�cV¬Q5ªP�R#ÑB:¶`–`^Ã( Ç`ô‰F‰ 6l؈âúÑ·b$>ÀlÂ.ìÁ~dá\ÈF à†"98Ž#ÈÂ~¤#;±«±Ÿâ=ŒÃïq.Bü�ÑÑK«Ø<³aÆ û¸X:…"'£.Ƶ¸C1/`þù+Fâü¿Âí¸ Ð§w7 õ¨jL@s´„ 6l؈Áh'Á¼_±aãeÏ"´ËôŸ—IEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/rory-flat-xmas.png0000644000175100017510000013754015224505336025505 0ustar runnerrunner‰PNG  IHDR’š%ÑÅ—¿'IDATxÚìÛŒôZ†ásmÛ¶mÛBtm;¾¶mÛ¶-­m]s¶ç3i“¦ÿ �å÷&Ï*V¿œÓÙ…�RJ)¥”š{9´ÃêØÛb=,c”RJ©!Æca,�Mp.ÃKøå(Ä�x§as,j”RJÍéñX«b7œ‡Gñ Ðvý(ŋ؋¥”Rsn@ÖÁ©xYhÃìÕãlh”RJ͉YGãuTùã�p¸QJ)5«O!›âFÀ…�X5.ÀbF)¥Ô¬‘ű7^G lµã^¬h”RJÍŠY'âgôÁN‚^ÃF)¥ÔŒ‘q62`§À»3wL”RJ#²:®@ìqñ"–5J)¥fÌ€,ˆõqJÆøÐ· Ї°�QJ)5#>™µ B=lPó #Kà˜4áD£”RjZ�ÈbØï¢}°ñhLxX'Û¥”RS“µv�>Þ{~@Ÿ�FOêQç©÷4 q�aq¢÷V3J)¥&u@dž8‡`+l„5°¸sØa‹9x$è¿á†GÄ�Úƒ¶5¨òT~®F­7.þ¨$hP\܇ŌRJ©„ÈRØ—à ü„¿ð>À“Öu/íyï½3[Ž>úgÿa<…øR� ”¡%(ö” å¨D�ZIÜ ´ãl£”R*a²$vÁuøÅè„‹`18±ââÊŽÛoïo9öXËÉÄ6F¤Æ‡2£ð�Cl>ò��‡B£ ÁAi@côcRŒ=�RJ©ÈGd9€{‘„N ŸëÚXn®í¸õVësŒm ŒHJPè�E²�‰ŒC�éý=ùþ x×^µŸNœhüˆ5�RJ©ÈFdAìŠ;‘†FcÒϘ´ß~»m<úèøûŽÊÀˆä" HGã‘’†todrP€Rÿº þé$Â1ÀMXÀ(¥”ŠdH–ƹHŘsûûm÷ئ‹/Ž¿)|D⣑âIö$~Nõ%ù`ŒlEâÆ¤;¥”RÏ{/ò+\Œ-×µ I_M�u{ÌVw\ü�Hž7Á Èÿ!IHAš7@ù(�IÄïL^ÁâF)¥Ô„Fdu<�hCÒÛkûǶ~ó�­<ç[ÈC?Ç»ªJGZ`H’B#ò_ÈÿHŒIødRí©¤Ç¥”Rã‘…pR1¾l¬§Çö65ÙÖ¿þ²]dóýÓˆ?"ðO#I¡ù7Ä“$¤Áÿ�ImôW\ßc£”RjÜÿ/r-š'4$Ýݶ§¡Á¶¦¤ØŠ«¯Ž_k1.§‘>†$æIø$òoÀ?žà $# ŒRü”S†êЗ3qý¸Â(¥”×�¬ŒÇÐçÕ'’ø�¤§ÛŠë¯/çÁÿÃñ,ƒð*>`2‘N„F„ñð…Ç$pÍ•�\ÿ}IèTâD# ›¥”Rc’ ð>Ü ‰wµÕ–“ÓSýÖ[—ÿ¶É&óØ;Ëà¶µ- ¯}†8M“2Óe†”ÓÛÇÌÌÌÌÌÌÌÌÌÌL—™™RHCÖ~ËšhžG£ê©©ãÒþf¾‘l7÷ïºû,�£Ž_öõUÑ×7�×¹¿éëÛö äú¿í#Hþ˜˜|×´ÌõOz.åW2•´¸+‰}+õac8†Q„2õi„Á¹XñýQ7}ú öÐz ½–þxXäÅ#"¯.0 ¢4¹&¤¾�­ÓˆÖ›î5¶¥<‚n‚aìW�†Q§{éqÎó ž70º{·‡lnÝ |vPäƒôß ì�œi¶ž(‚ˆU´”©ôiû÷FEÂÄ0ŒÛèÕtãE$V| ’›‡o»m¤?Š|d3 À¯êÀU#"×)¥‚#7L"$�¢"1•l¦wƒaÃ0úéŸéå4–ÄA¢ôv%·øµšÃ¾¹‚^\v)°ÈFS÷Q³"ñUÑr*ôIÅß[bX�†1JÿHJ¯;�©Dœ»�ןEƒƒW \{í(öÍz±Ã0ˆ‚hF h“-d9½ŒÂX�†qýý ½ñz’z•ÊÅàòò§  eÍ™D‘$-Ƨ³©Äp0 £(½�~Ž~œ^…ýç|ˆüЯV/0Œ|<Úãݨî«hO£iE ˜0Τ}0 cAbÆ0½„~‚>Ÿ~ŒžG�?]ŒÒsé'èW\^ ä£@Y€žê4‚ÜG€³‰RaB[M‰>’SI7ŒÂX�†1B/¥_§Ï¢O¡o¥Ÿ£?¦çÒkèMô:úúaú:ú%zõ/¹að–_ýÊ!Ÿé>p†ºŠtšµô5á=IÌŠ£{_‰áÃ0Œñ2JwÑßÐÒž¦É¡L»i�öÓ]ô:ô—Ç>VÄ÷¨"oy«"žêi(€RI}N]é„Q¡�àTò#»aì7$†aD´?¶{o¸Áƒª² ÛW½›¦g–åM½‡¨"Ev¨ˆª˜ ÖÑ•ô'0&[Ú2 ã—Û¶9­×AFh”S<¬wÀŽœ2ýÿ#‚ÕÔ„2!tÑRÆÄaAb†8×t$V6Xà«>F€iY�õBéÀ�ä•�t1Œ‰Ä‚Ä0Œü�¢LTŸ*ÀEATÑŒ4‰ö̺ÆÁÁ‚Ä0ŒQ ª>ÊOP.4�ä„EF˜Ð ÅÑ-´£½X�†q¹çIØàOЙ"ÅæÏ÷BÛ襋`´ Ã0Ž«×� TŸ`Qö~dO#ªH#iUAâk¦’Yt9Œ¶bAbÆœ@õ…Ø¢M"/Dr–²$ós[ðé*ê`L<$†aôôöN)«>Ó €íER!‘6F5ã·¶p*�cb± 1 #Šê£=àI ThB¡rªéPiÿ4’Í:ÆÄbAbFMu�ž¨@EZ2¾å¬T’²tÓy0& Ã0:TOàÉ ,Ôqô"’šF]^¸ mÔè,­ÇÎÚ2 cHD¦Dщž§ÀvêÆµ_$iwˆ¤z"ŒÖb‰aíë+M�¢ÍÞ¦9ûEŠu#)‘2må„‘àOA ÿò}�q`X�†ñ÷¾¾x €÷(ÐGÃŒ)²¤•»œ%ªÙ�¢Š6³0TíùiÂ8liË0ŒOmß.§©žè€§DÀƒh7ÅÿëE”J�¡ù!¢ÚöiD3›Î 7Á76‘†-eUNWÝ&À»#àq!B²ËuA>’¶HwÒÞÓ(»â01ö Ã0¾µc‡cˆ,ö�g)C¤lŽ€€bËYŦ¤hˆ¤) p2ŒýÂÄ0Œ_mßÞ=«^�>/¦ÇQLDˆ¸G¢¸Øö…ˆ‰B�Ý4<ìÁ8âñ²}; ÃÈç§çœäã…ª'”UŸà€‡D@�¦Ã£OgeH6.Õ“´!DÒß-?ctt>€+öî;, ›H Ãpª%Õ­¡êÛÇvª÷¤§�DÝt¨š5e¤Í¦)@ÐÎI_O Tï # Ã0‘†á¾Ìàž>ðŠPõõ�¥›tˆ g‹ß©ž_°;Z¾ýE{z›‘§;`*Œ£8H Ã$ óé=Bà¥eÕ×–Tä‹8Íê DRß5)‚ …ˆý{Õvï�¦6VÒõôÞp0,H ãH$$±ù;„t¶ôUT_Сúj^Ç PV4!÷>�Š�œÉ –6íBÓh*D›º¢€')p*ŒÃ ÃØòÃ:Z¡5ê((Äó0ÒßïFvîôý(*{"µ`Fôv©>®;Š^QS}Tœ$@y<ÝAÉ �ücáó» ´é¥VŠl’ÑT¸DÀ©ôñ ôP˜G–> ãÈxxøä���ËÄ97àJz½•Ö�}êS½¡þþòu?ú‘«íÚ5s’çmðEî«À²:ÐIA3Ññþß¼H‘ë4Eá`j?éI$} ýý„#0l"1,Dªƒƒƒk"OÛ³gÏÓöîÝû`U½;€{Їӻ­ûÎwΜ{Ÿûô.غuÓY;vë¬maoï$Æ ˆspIß�c‚gHF€ä£Mæ�êUÚ‹f5©À ôy£ÀB‡:$†M" �­œF¤ªk�s³¢(ªE¤ ›Î¤ è™®ZÝàwÜ1Ái§…Á™g"\º¥uëžp¼Ž¸ýÙ-^ø ÙïVO£H“_æK¢*&†â¡e‡Êfú´½Àd‡($†…H Àjö"wå²LD¦zžß÷Á{dâûpÝÝð.„·`¼™3áÏ›‡Ro/ÊgŸ�`útøÎÁ’ %VR"uU€¦Ã#1? Ò¦iÿFÄ4Rl§{z* éã<ê\ß÷a‚X�ÆN›è™\Κ9::*A€W¨*ö…„!¼©SÌŸ�€K[Á¤I*å“OFÈ€ñÃ0 “»Í%é°ßñ#ÐÂglµÍ›JD@;Uä)S£hÙ/‚ÀÁ8„° 1ls gÑ@Uãñ<"’_|7ô<¸ÎNÇ/m…==q ”8­”ù™= |øªðÈøÌ¬7+DR¦Kvig?Rpsd:@”&ŸÉB¸?×gÃ8”° 1 U­ MaˆÔ8�Äáàœ‹MÈ ç‡I©/o•׬A¥±ÄÅû'• §“ÒܹñR™Eðs–º‘ù¹8Zà G¤B„¶ IõBšVéò]/PݧžÏ”‡qˆ`AbJnlL" ‘ïE@’ QÕbaâûpA¯« %†Guóft¬\‰Ê¢EèàdR=í4”çÌAX© à�é2º —ûôVaŠw$ªh’ºÏÛ¤˜ÖKJªË˜ãPÁ‚Ä0 73<.‘z$4éGb‹N&n,Lâ餧å“NBmÓ&ÔV¯F�%|�ÓJï+�e°Ær˜çÁ�—šrÌE‹îšÑÞþs¸ˆŠ ¢¤Vz=`&Œ"X�Æ?–xez_úvú$º‚.¡Uê¡xž€ú¾¿„OnÅŠH*…I:“d:)—ã'¸*gœ�ÚªU¨�u:O=�Ë—Ç÷åÙ³†!Õ¤CÉ•ñn`Ì Úæ¢]Š™z7KD›ÅN+g�ëy%° 1ŒûÑ�ÐgÑ÷ÒÑÓïÒÏ2L^M·Ð9t ­Q‡ýcTUïÃpÀó¡·žòÆÛ¯ì{ÓW=�>�¾zëòßÑ·ò¤×¿åU/Xvãõ× ˆ NÀ=%H¦’d©ë€‰;Ÿ�4…�I…“I•…|…E<ƒ%¾/—ËUá'fO(ù�Qt'{ºi÷T’s¯"Ù;ôU¡€ U×ÔTÕ­: F6‘Æ(½ÿŸIôzÝJŸAßI?J?C?çD¾1kJí[¡ï}nì·7�…Ð3EäEüçŸÏüãoyìÞ�ŸŸÁò=žJ@’0i Î!žPjµxŸI…e|Dze¨žx"ª ŠJcJ"2–¼òŸîjFrv°»Œ�’íFR¦^œûB/üPõœÕõ·MŠÈÇ‚Ä0~B÷`ÿñh‰VÇœ>mruñ¼é“¦ H1<<ä]yÙ%®ÿŽÛh$<.%�VIºGñÂ0îOÊ+V ºaâ …%|Çi§¡zú騰?)5E5 ”ØÔ“^™HνÓ;˜!’&1ª™!¢4Á³ªªwïT�# Ãøý)Z€Á‚“1³§†4ªŠ«.¿W\z† ª/o1PâûÄ–2öÈp\È7–¸Os±�¯4z“F¿u+ø”W¼ôUòý8P| &Ù&²Ëu/c/ËÁ@R¡èR“Sâ¾€µ ’Saä`AbwÒwÐëÑBßáøyS0oÚ$8'hæŽÛoÅßÿô;\wõ¨�Žb”2L&.HÒJ­ïŒ/÷ö¢ÔXöZ¼ÕÆ#Ã}}ˆ…ß•J%À>76&²CÄ’Þ%VTq°�&]Æ}˜SVÝöÏ/ÁÈÁ‚Ä0~O¿† ´x8Ža²dV7¼¦0‰êu\yÅ%¸ä‚s±g÷θlçŽ÷8P’ý%M(S§"dg||ÌJeÉt¬]‹ÚÝîO)󥯔䨕¦@I‹Ë‘äßk{ý-¾92ÏŒ¿ó��S4š # ᣗµ*L<×XæêÂ\N&ÍìÙµ ÿøëpÝU—chï@<‘°+‰C…¶z*É? ²§§’øü. $Þ)ß¹c:·oG…›Kåòÿ ùD 6ùì+b“ûDA.íŸJT /×%8ÕʪgF>$†ñúA:ÔºŠB°hæd°„G‚ªâúk®ÂoñcÜtõ` ¾%1îJ’0i+cBúsæ àÒVÈ.¥Äp©p3cmË–xR)³C ƒ ”€Ÿ �Ä õ�GåÐ “X§šñ AnÿS € ç~F$†Ñ�Ð_¢…”'ÎŸŠ… ”�÷:¶“ý²‹/À—\ˆaN# “x‰‹!ÿ¦ªh7â\µ út„óç£DË�%¯uëÐy¯{¡¶y3Ê‹!ðý$Pþ§jsˆ4kˆ�.ÛÓ׬Î'õ4—]Ý­Ñ,Gn�Æ-3¤L§Ð9t>�IKÈþ·B»nÙõÐ ¦Ÿ~�îB‹PZ};·§,œ†ÎJ00°þý¯qõ•—Ž-o å<ÁÕÞ@ñ*ø“|œRJ .qÅ“IçÝïŽ>õUêêB¨Š0Šb“@IúzH�¯%Ù�Nf_³ÿnIEõd‡î&Ì´3ÄÑnz"]N×ÓÓWÓøýýýý1ø]ã7úaúú*úÚ±Éã›ô;ôkôÝ “ûݼ;š^�â¿û6ZŒÐi]UÇ@)¢Hq-ŸÞúÕO¾�Ûo¹)YâJ&“T�´ÄC6¥QºO›†xBaA_cwÒyï{£Ê@)³WI†lÞƒ"MjHsˆä·¯›üæ)j•H�ƒqX Ï{øvG=ÿeïÀ$ɶn× ¤TYvKc_it-ý¶ñlÛ¶mÛÂýqmºgZ¨ê2²RŠ8çío}ç‹—5ÝÕ]È,tÇš»ï‰dgfež{¯ |±±Ób�‰½Eì±I±‚½=!æ¢;hˆ½,öÕ´¯L6¡~B)¤ÑÜXÚÀk7Vh¸Â‹ïÿ(Þ÷ÑïAÿÀ r¹SpÅ8_„­æ ÆÀh †0AÀU·Z0Õ*M//CÏÌ ¼t fv(•¹Þ!ª0$!&hk!Ä”âªa¶ðH\ù’çþf>EŒC‹˜HbäĦm+’ˆ=-6%VÜçÜ ŸR*å)=@¨ Î__Æìr™[W¡8€ïûÑŸÅãϼUˆ$�l6ËžXB24¥Ô�’ID(²Â®C „XÌú:ô�Ð7oÂ,,@ÏÍA˪WV€fsSÃă„¹•LnG$„º½¶òo[ÀO¨"Æ¡CL$1Έ}Rìýb�Š Ñã8@$\…BJõdïSbµf€—®,bµÜà¿qúÜCøîþ)œ8}…BÒé4=’CA&�-D,±l‚%z*««ôTŒ�ˆ^\Dxý:ôü`15Ôoÿ‰#Æ}ƒœ1úûm ÷GSX])�H„Pzöü¥jSÈd åz ®ãàñ§ßŠ}âûpòÌÈ $Çq8ODŒdrH°¥—ÁëmøËÔjÐB,¦R���µ„À„X¸šr¦Ý& Ý¥zê•D„r ™ÜQ¤¿b@"y 15Ô…OýEĸg¡þíül¦´Q;à]Æè�xN,ƒCˆ”¯�Oªž ¿K¥^¾ºŒf; òÌÛÞ…~üûè™d2‰IEt„!=Óhb†ÈH2â­PcYZ¢Ñk©T¨¯@ÈÅ´Z¼?´Þ+oŸÿL§g²i°•¾KE¼cÌš~œ³jbj¨ëŸÿˆq¤á[­ÃdçV§VV7&µ6‰™Ù¥ÁR©úäâré=Ƙi ‡ŽúÒ<=ÅÌR(¾kj#Ͼø¼ûCŸÀøä4C\®Ô(D]>’Кf‚€Ä‘L«ÅЗãõå2�©¹¬¯SkáýJŒ$cÃiöù¸r;�ß‚ã±çAù>ŒëBÊZ§åyµM»6–”Ì–SyÓoûsˆÑuÄDñÙTïû Íªj+¥ÆË•ÚéWÏ_/æó\¹:ï­­WË«P8È&2b½„6Wç×qen�B|.ŸÇóïûÞùüû069MñÝNRdˆK.“LŽ<Œ‰ˆ€dÒlr%i´Û$YI,ôV,™ V‹#—y/[âàcÖ×ù|zm�÷WÃÃp'& ŠE’„°™…&÷ Ä#bØ­Ñ G$ÏEâè ™ä/ø�ˆÑ5ÄD{£bOÛ «mªnh¶ÚøòWÎóÇ[©ÔqíÆ"ÚíG®ô¥®½„ÖçÅ+™YÚ€@Ȥ€w½ø~¼ã…÷crú’ÉÉÄj&Ï„á®{ ‰µÛÔXP¯ó�W#«rò`¿0¸nä��ˆr=Ãg*�æÊ60CC\;4z6ºV#aÉó’@n[X ¦ÃÇÚ¬3…õ¿&¿€FŒ]!&’ʦã>k=�gÄNŠ¥·Þ 5^~õ.]žCj,,­q%b¯äñý›Ðh‘h‘Íæð–w½€gßóaL?I�D)erÙa^wO# iF Ök€Ö$ (Åcë‘Дç‘4„H¸Ò”Š< ®CrªVyÌ�™˜„ÒÎÌ ¼|áÅ‹@¹Ì×âo]÷ý_½s"‰IŒ¤õ<¾ÇÈY±ÄvÅÍ…Åu|åë°´TB½ÑäÙ÷Q‚ïÒ+éi¶ªk4\ž[ÃÌr9šæ—L¥ñØSoÆ{?òI¦§ä²%j&B.{ÍèŠaÌæ™ YñN‚—_FxáĘÎ,Dó7W‡‡Íö‰$FL$1ÆÄÞ%ö]vVù0vK$Ë+xé•«8ÿÚ „¡æuG J‘HH({ ]9ŽÂ�°Qkââì–K5t€zÈÙ‡Åsïùž|ÓÛÐWì‡ òLl½ÉÎu“·òo@»Í¬²ðüy´¿øEHáåßO|üã¿@€wDL$1Ž‹}¿Ø÷Š=.–Âpíú"^zõ*nÌ,Q9ŠHû ¹]¦s¬n ™æ{.ìu‘aìÿUê-|ëÒjÍömº½{8yö¼W²¹{êMÈåû`=ˆÕNx]Œ=Ê·‰&ÃP—„Áþ‹ÿÎwþüÖE‰1b"‰1*öCb?m+̺€ë"°é«°¸¼jE8Ö+ñÜíea9J! ÷^®5Úl#ŸNz¼­Þ ÐB¤ä²ïº¼¯‰Ô”,¡*D¢p+<ÏÇé¢grN<”þÁaŠî©T$ÄÓ3éM�®†µ0Áu•Ëý%ÿPl1"ÄmäcÄ~Jì?‰ýÛ$Q¡KH&)“DŽ*´š¡ÙŽÖA‚ "«r&I; éihmH,®£XѾºÑ@½ Кb{(«Âím¼öÊwð_ÿÝ?Çg~áÉL“W°¾¶ŠZ­Æq½a²%}çøÞ݃ÂV™L¿õÖ¿ÔcÄDCÙ ¬jçp¼UÌC—‘ͦ‘J§pÔ÷µf`šíEI­­vHÏ#Ô†Dº^i¡h! ¥ÀëJrÝòzÕ¦Iƒ÷½[˜lþæ >ýÿ;~áþ|û_ÆâÂ<ªÕj4eQH„d"¤Òí–ô1'k=÷çÅÎ Æ}L$1”õ:þ”Ø¿û˜X=B2á‘LŽ:B !�»lôÚ ÔšÞÇF­…j³EÏÄw†·êÍ6ÄáuÕF‹Çkåʵֶ2ÚŒ1ôD¾)³ß¿øéOá;_ÿ®\| ««+¨×k$1‹J÷=”x�*ŠõÙþpbÜgDñ-Úÿ´Ø±íÆÐ;pÓt]Å <ßÅQG3* &ƪ6Ú·x$B4ñJH Ahо’5’áÊ™$•z›—…pöÚ.š�^ùÎ7ñ¿ÿë¿Bù¿xýÕ—pãÚU¬ É4ä6!ˆ¬ôNìeÖöÄØs(ø±·‰åã>!’®-ü‹–@~+3³ö•j]¬�áÁòYÖ@ÐŽ*Ú¡AK žë¢Ù @ýÃ�,à:Ó­`µ\ÇR©Êc! †½ÊÕ=� Ô4««ìüõAÜœ¹ŽÏýòÿBùwøÚ—>ƒW¾ýuÌIø«¼±AB‰Ì mÏóáã–@Ó3‰qIŒlë?Šý:±)ì‚ ¤Uk�Hti"1bõ¶�6Q6<×Áz¥ ñ>hJ)za"¢3l%^I$CTëmK¥ÑÂÊFÍvhI{Êo¨VÊxõ¥oáSÿý?áSÿã?á•o}7d”ïêÊ2*• ÇøZ/%òP¬†Êî�ëGŒC1º¥Ô€Ÿûób+ìó¿�z½‰�r õF‹&íã±^ªtîÉꈒ 3±|OQo„á)‡ :ÐhÓ!¹„ô8ŒXyÍ6W’ 51ð»ɠѨ£´¾Š›7®ÑZ­&‰¶w­ßiö1‘9޳3’�uƪØgÄ."ƽD$1ÇI+¥ÞàOŠý*±Ñ"2ÔjM,­”( Këx,‹ ¡@n3 Ž$‘°¨p|´ƒÅ4êô,jB kE„T"/Ä€é¾QÚ¯xŠñ?(z#4ta ¼Qbv×ëçE7¹z Z‡H¦3PÊ=‘€RD,ZkšR*²wEUì bßAŒ{…Hb¸®û°­ù=b�äçI"©7±°´ŽF£…™™evÿµ Õ‘õFŒA!ŸÂS�Láøä€¼·6VÖªX,UBQ‘ì,†¹LäŘ[Æc쨡¬­.ãê¥×°!žJ&›c'á0Ô‘Nb‹yl ‚ØC¹»cZI„ÖBŒ£L$1<ÏK8Žó1Uì}‡¥PªZmàÒ•›$�•µ VP>ÚŸµ‹O�à™G§PìKcqµ‚Ùù5¬”êhÛJvfiš—²:8F·„Òþ†du]æådy$�0 Ð é wÑ[Ù’Pb¶²ýKb_k#ÆQ$’¾ï9Žóq¿Û¦òÃÁ‚a› ÔÔGÚíç/ÜÀÆF•gîG=íÔqÞõÌI|à¹�Ϧt³„_þægfa�á,[¹Ž&ÃFšòáé·†¹¥¾þÊ VÖ+ôRÌ!ÕuHÛxŸò¢—²º¼HQþÂËß–*ùoàÂ+߯ÂÜ,šÍ½ضÞÎB¹/õ”@ì¦ØçÅ^GŒÃJ$12™LZõŸð—ÄÞyØ>[ÁaUÊQ$È«ë¬ ‚àhW± “NàÃÏ?„Ñá`€j½Å޽7„HÎ_^ÀòZåêáœð}1—$¨ívˆˆ¥´.„9{—_¿€«_c¥|Ã6†¡P¯dµµ¢l/K*¼,¸I¥!ö²Øÿ¥èã0IŒl6;à�ØŒ¬aR4›m´Z$¬—ª,Л™]Æêê‘÷Fˆ§™ÄÛŸ:ÎþYf4­®W1¿\ÆkW±¸ZF«}8Òš¥`ì¦í:<×aXŽæyð<—a9¥"i˜÷»¬gÁá�Òšd|]Äë^Æ!– ­­®±Ôa´æà-‰ñ†$£I(¡˜áõGœT:u¢’Rê›b_³‰AŒÃD$1r¹Ü €?+ösb NØ F³õ‰ÔŠpp•Ìg§Ø^«5ޏ7bprz}ñaäs©Î�„òF¥�™ùu,­Uè‘4�(¦ƒ{QÝG2á‹yH%ðI$.|1›=Ç�”1†÷ B½£�´^«beqׄT.¼*á¯o}M†l�Á~år)š…Ò¡©ÐKé4;2ÄbŒ!�Ø$-ïo À—Åû²RjùpI 1‰üq±Ÿ8„"5‰ÃNø‹2ÖMñ@ÊÜ Vdc-—kG†D\—á™[6��bzþA34�S0fÓý]‡†„ï‘TÌÁ9_«%¾6�š…‚‰#�ô¡ x›Öv07v é<Àë³ã7"•j•¶8?‡W¥¥}¾¯ˆã§Îbúø)L;�ѱ ôõ÷£Pè³Z àðuz²ºñÙ1Á‘îr˜ÒŒíl—h¥4À $£mC^ã×%‹òŠRJ#FL$‡ˆD26­÷§‰P nD­v€T2AQ}~a�»1‹">¯­ó ‡$c¸ñ['!àq>Ÿ[ûØ{MžœÈðŠ„ÚðýÛÌ-8 Q(g¿ÃX)!‰†m_6’ô¢Í7›JpC¯„¤' Á$ä²cç»oTkL0¦;gì2T‹©Ã¥u¦ç…<†FÆ008Œ‘±q®Ù|™L–­ZÉ?w—a7_Ì#™$|í4;ƒ> ±íWxÌ&tÎpá±tR6B(-yM¯(¥>  Š1‘"ñü±_ÓëÏÁ�6ÏpGÙ?F†™Y܈KUžÕÊmôL<ÏAÝ’Ê! ÃÑ((öå¨!”«už!‡AÈ÷¤�1}÷Ã�GÍAëÛnà¥rƒºP6Ã: n൰Õó×�ŽÔb’ �‰"Ákcà{ß“GñyÙqøØÈcɺ²z.ï“ͤP«·P©5ººSŒo7QZ]–4â+¼ÎO$Ñ?0ˆÁáQLLÃèø$ÒB(…b¿ I…Uö¾Ÿàësz%$K&4ë©D«€«ÜÆ÷¸Wâ°¤Y'‰ØÙø¡¼žE¥Ô¿ÉçóóˆÉ!ÃZ"I¢G°?@Æè(<Ò[àðöÍÅ�>½�õõ ”ýoEDõ©‰!ÑFJÑ™ýa„k…ç½%ðÌ|x°‰ubh£¯�ÅØ`��ƒÖ ·Âê öB.…¾|Š!¾Z£G©»$Kä¶Ð‘+À•¼–è~B<–p–ý;+Ÿ’5�JÂuмž­\š-Œ�ô£ÚhBˆ‘�iwa$¯O=†DÀϦkæ…`æf¯ã²ˆõŽç#—/``hùB…¾"Æ„\úú!á1±~$„|¬gB²§×]v;¾«Šd#YŽ$‹N†!©Ž¾a´ŽÐUDö˜·Ûé’Ñ*×·åß.Ë¿ñ<11b"9$žˆà»ÅþØ~L[ë<«3H$üM­Ä<æ¦ÒÞD4 Y‰pËõEiÆX,dI*RŒÈ–ñA¨3‘�s¹4Åß¾Bããð}—m>—Áäx?Ž %”ï;€ÙzsOø.7eG9CCRé&Tdž—N%`?SnÊÜœÃ�·9bBŽ|o>½Ÿ+n¾Ž…ÝäoLokld'OLð9Ê•DË2r=”㪒\ÖÆàúÌÂ^_; Í†Úø:”£¸†6&Di½"¶&•õ×øz¤à…B©tÅþL�<‹‘ÑI„Xúúx½õÊà�L<(E²äšH&8ÀËê+4K24+òw^wÛY,Ú&¢û�Ü&$â.Êý¾*×ÿmëˆÉ!!‘ ;öö§ÄŠè.¶ò6H&²ò,P\mV W 'PŒµµÑš6m¦õÊÈ\j#ç_Ÿa‡_ kÉcuôøÃ !z™LŠD26ÒÏM¶ äÒ×—C!­Ð— ï.wpÃ1(Uê µ‚°«Ú‡ão«êéY@¡<Ó¯5Z$=c¸y33K@q­—;xx¨ˆ±ÑAœ=}Ìz$¦Õœ›_öFG]£Tøõï¼î]»1¿[ý�ÿŽ6:"8yM$?pa_7É®eôÁ®A»…jy�êJ+ ¬°Ï÷ `pdLÖ"½—bÿ u–¾b?’©4 `‰Ã…W÷àù¾½L jó¯a¿ƒHnñPl(PËý´ÜÊå–oÈñ§´ÖY¼›o#FL$‡„D´Ó ?ØKa½“Ä¢36ãÑBVÌG±2t“Lr4«½ôcˤ“¨¦˜›_eXHÒ|¹Q oÂaÅiyíCC}*ʱ�¬bR®£¶àY_ßÒLI‡š›6'¶¤Ã@ «’H¨5ïÓ]¯Ñf^ù>ÿ.鄨×(»Qk­yŸÎl2¨M!>O&“Äôä(NŸÀà`7uþÍ•/EH¦-óSÌç¿òòž;Ö§|†ŸÐ—Ï@$Ù¤ø-ùužlðzm€TÂg‡¼/>®ÙÌÆÚ¢Z[^”å8œ£28<‚BqÉTŠ¡¯„\7uì$2¹¼'‘J¥é½"!ßu¨}�â}"%(1ðX#÷­ù¾ß+‰‡´(ᬬ�F" ÃU¥ÔÿðO¯Æ©¾1‘9 àoˆ½ÐãÌ“7’‡ÍŒIDž‰Öa$¾'Ú†F(lÒðfDFýÅ<ªµ&ff—�Ï¥±´ä Ñnã�"J{M$¹QÑ#,D›uÂñA&a�p :@‚¬È{•:¶Á¿vsÅBš¤2¿´ÁŠv›5ÕÕnÜtY °_·M¹†,ð(Dscæ}öØ�²DÒ™XQ(äP,æ™` ZI¤Ÿ@�2=sjÚ=ÿúu¬®—÷– ){މ Ž}�0ô®HÆrG „š/`4_g6�$É‘$µQÕzƒ^Œ6†á¼z­ŒÙkeñV^‡muO"/…šJ:“¡§"„bÉÔ�� ï›Ng)æ q6‹è-$�¤\§  a¶Ö¹‡ý¿gÎ=´*¯iNÚ­ ‰äE`¿$'X¯ ™¼ Œ1‘ÉÛßö«צVÚxy‚ñhK 0Ƶ«áËn öXA`õ’v$@ö籸¸Æ�ÖOxloÌ¡Kï%�ÈÆÄ3ùþþ< D2•H*ÜÜ,¬7rû6!�Æòj…àÂJ7—JÈÈó-­–±¼V᫽ƒa!nŠŠd¢…ظá#›âæj+ÃI4¬Á�•·{6„Cb¸uc�´†¾,1ñ¶ÍdëTªu&üÿD ³Sák°ž”"±¥|zI¶uжµ- &=4Zšäè)�× qÊÊc¢Ê|cÀ°ž|Ϙ~í8�ɉïmîæ æoÎv†®¬gf‰*J4�ý®çóäÄ„èØñX )�·?ûžWßô–·^n_Põ÷Kk>%Ç�%�1‘Boäm~‡X½m�bºíDod'õ&ÖøQ¤u”o½t‰g„·ÓFlˆeßu¥¨ Ðó€`DÈcrb�d¡�åæÖ Wiÿ6ÅuÖÓ�¦Œl…¢°¶QÇå™ežYoì¾Qc”~Ë Òó¬~  Bj¼,(ÏÈgs¸ÊáâÈýÁûm!y>žD’ð18ÐGBÙâ{Â67ÃE ôðµµ�Ï¥¨sd,i9·�½®è=ä2I†…øX!R^ßéY)E¢�=fZ3� Çj†ú�j$ìl|å¸|Îí�{·xT•réÜéÕŠ=�˜H3‰< àoŠ=�ýC¤sÈÆÏ\z!z&;½šdÒµÙ]ZÖ64\öÓr”²?ÞHзú Cf —ìgV=%×¥7ÂÐP&‰B>C=‡$rKHÉÀwÌ-ýF…^×ùå †±šÍåJ“׉¼[¢cªnË„P ˜Ä`“—x6„š:‚�‰6dpÝÆó3ÅÙ¦ýRÓ*2+�b÷mÀv)�yðæVyyY�\š"y¹p\†‘¶ áѳÈ[¯OD~ž�oÛü#²äsºnª#ìfÄûr ±6Zmj3åj»‡z÷©³žð*bÄDrI¤Ào'‰l1ɤsÑNÁ�9›C&c0>6ÈÚ É|bI³ÕBjz( ߣ&!#wy½‚Ú�¹$ ž¹¦“é‡@Ö"ŠÅ‰í6 G²ÕGquv•js‹%¶�o´TëM¦:ï $¼èŒš“}n®Vpx–n…t+ ïHãó¤RI ëýÅNŸœB!ŸÅ`x¼ÿÒji»ïƒ(z¶ 2ª¹ƒ·ÕÇc�I‘D·G’›=^�žÏ#B2LDp»À€ØhL$1‘Ö‚Ã_-öC8@X±œ[{n5â886=ŠS²AMN aaqZ1�+WgE“"Á”JWI"ûÒò2ahkp ÀÍ4Ÿ£N‚­à9·†išm¦¬’Hfæ×ðòÅ9†ºö[˜G²ežüe�f ÙνÆ@ °(ûðP?Zí6Ó}%½—Dº³Ù¤ùæ¶S™™‘¥H,R C^H~Ûo[ƒ=CȈ^œ S})ì‹mÕfFY¡^ßzW,ƒGî=L‹ýD/HÒDB· õzëý M¼Ö‡ˆ1äµ—xh ϾóIœ;{�>r�<|ŠÇSS£˜œ¡W Y6›6 ¥zê‘P9~l”$' ÃY¶µúˆÄ`3?S‰»Óyéõ½“ˆ-0Œ2–À3ñCZRÜI�„Uàn¤‡È²;0›†E—c£VÊgïšávlJ>³á~ÔÍíeàD™`ùL Å|–zÃS û ¥hôðHвnáA‘D<›ÏÕ¢…K"ÆA"&’ðO?å‹e7{#ôDN£00X/UØ<Ñ^æ&xØôË€ÝYËå ¹ßž2ޏé��0?:< ¡®!(ñPFøã¬Tk‘Äl#”%ºJ‚HØÍ¤SÌÈ–�3Ÿææ(›Ü]C&JÝz™âs¹Îtß•µêî=7D �PŒf­„¹L2)�HH"Ø HT¾KïK²³H ǧÇù÷¸ûg 0Ø_àw©Z­o‹øÝQàûpìn�q@à{AžŸç- u†ííÑåÎÏlj=’˜H±¡¿÷�ÿO >&ö›{²sìfµ±Q£¨-åخäNž‰ÖššI¹\¦g²Ç3mËe²ifIÉÉÐÈÊj 33‹¶¯ebj&rßn†²¨5ÈæÉØþà`�›§o{‰m$8±7€›ââj…D¸û ìóÚ´Ýhà äÌ�d’“•ÄB2IF"ó`«Ý}yÎ$3´‹ôÌX gtö›âf,vGÒ·éãÔŠl+þ¨Zü à{3Ædåß�‘ˆ&@v$°Î)¾GÎlé$ñ¤�Fª#&’ðDÒbÇ0?QZgä ùü³þ¬1f8Ê2é~×I,œ•× ‹ël_Òh¶8×ÎϾí¿iÛbG“ìv ›±Ã�eddù|A;¤0*—åðlyzj”×t $/ a±Ndzrˆ›æNÞ‹«elu‚é½å:Ø^Ýl¿E=«ÍÂò`ë@XP˜´u7¾\NÛ3wzL$Õ­To« e$¼7Fƒ!-³=�^ aŒáãÏžšdÈíNŸeÊn†¤,â@ lê±­e±$Mí&" PGaÑ£ïF½É<1¹/,‘{bgňÉ>“ˆ²Ù�‹}P6îÁ÷½ûé%»ç/cÎp®�á¶YÏ¡»ÕNƒ"i¸B"kYeC°�묮Èq[V¥nÙxØ[k}}Mî_%±ì1ÔÄ쨉ñ!¶Ý‘ ¾_6³T:‰bÛsð½+t ¶sq±/‹Ó§&((ïtSvsëæ§À gv¡$$Kwƒ=ó¥ÑQÜX¹AÙ��Xt»£¢�J—ÚÀØïŠýy¸Ô�p#Ýf6^ä�<ö�h]§�1 `kPo`æ•£ø¾ùø„%3¶•óùšl¨�Ÿ�o·iʼ-áQß¡%£0(ž–u1b"Ùg$Ä´g3ÏHxåÇ¥³ìŸl4ZOÊ쬮UÐn/�M+åz×&*1!þÒ�—Ó efºX KK(•ª¨TYñk¾¡Í»1Ú†¹6„Œª<æí{hó14Ô�á‘6 T�nNNä{’Vè²’ ì™§ÂØ~ê¯á&y�wÊ@R›4 Š·6¼Â3^Xo„Åw}¹Œ%'šÒ °í‰RÅ^bù,u2¦â*µíMx ˜ÇÐ@§OL0LZÝzæ¾­åàã¬1Ià a+ãÐgk ÛŠ?AT.�Šþ&Y±LŠÆ¿[²xîB2#Æ>#Nÿ͉=ãúÉ ÇqÞnà<#a‘Ù4„<ÚÜ<«µ pB,ÜÌóùt×ÎÂÚAÀ~C· Ó é‘PtL% ›ÅpI‡åFîûÔ0�ͦ6TQÑ¢x%Ü8s¹,'�]‚?ÒÎÇòÒ7z ·ð5Èì‹®‡žeÃäój ¦ïp3cÆ–Úâ ?“Ip50wÚ¸lÊ«‚ï’´[/”Jå�™Ùez×n,B„p_¿¾È�¾Ñä˜Ún¥ÿò/*3·dS%a¬[ÏDþ}zA7f— FR[/Ñ{avŽ<ÎòÑ–L*òØEøÎaJöXÖmi<¶šº§NLb` ÀTTßö~ê"ø~V×ʯŒ Ù¡7绸-˜ŽË sêKwî/åy$mKÞ$“Œ¬r=ÍŽ¸�ºÑvùì�˜d‚Ñ#o˜ŸûäøðNÂ|QV]>—åÉO½~ûŒ¾ÎÎŽ Ó1¼å¨C3NÙrÒËR§B]*•°mg\x~4Ü 6äÅ0äøp#}¦˜Ë Æ~#&_ì˜ÑúŒÑ¡oŒá&¾^ª ‰Ty/Íâõ‹b²Î-¬à¥W.ãòÕ¬9YXZ%á(å`àB‰×s2]¯°¶V¦wÕjoÔ¯k‡ [Ágÿ+µU›vÂzQÃÁ„O±—Ç–Dz­P“MŸ!DI¿Þu˜I�Þ-Åö§?‹I!%!fàYá^ 6ûLuÔ�(BDú”%=zÛülòb!Æ~"&’d¶ï¤Rj0 ¨'0ì"Úƒ˜¦G²°´FÍB†^‚Ç<|ì>SJAHD‹Ã@þÅç‰êU:3µ kL^»4ÇP[¥Vgxknn•#·1ì5;·ÂÛö¦rc /ßÄìì’Üo/^m;`Hk½ÍV†©Àï{ÏÛ�ÎØÚ„.£Ùbš3Äã³!4½-}©ÚR\oGgŽ K¼<¿EʲmÍ4—í6Ĩ‰DÂ:¶ …Ýõ±t`aæ^�ÍÒ A±�ÃSâIžR9srB4—!~¶Û¬�ýÞÀÖ�ìðÏq1ö 1‘ü¯¯•û¡ÔO­3[„]¬nax6¿"›´�½Ï”„æÚ ”纉Û`ŒA'©0ü53³€Õµ ÑlÖ„0Ê ¿‰vC!þµ‹³$I&™\¹6ÏtbI‚Y†x$ôpV„\m߮ƶ ,Çe3J§SèÚö3o‹Ÿ¯õú¶Q‡sûz=å8èËðì[<µÛNL0�4ÉU©Ýgb5ƒ&Öj%®‘.ýÇ“ w«Ížj·tʵÚUtý^ÀJûtŠa®‡Ïga㛟zÐÖeàv¤EËq$X߃xBÒ�sˆÑkÄDòRõA·²2÷k[µòûpwpƒ³Cw¸é ä&Z%´Þv #Öߟ“|E;€ 6Û©…j¥ µq#–MX¬ÆAG6+ ¯^¸n×ÍÕë 8ÿÚuf~IXLH¨DÅ—˜¹sNõl€«éoέ²²_fjl3ýwë3Õ‘Á<Ɔ Q �â|”}[hèÛ–é»ѵÑX®.ãÆÚ n–Äc¬¬`­¾Ž•ªx‹å%´üÂìþçÏã~á;ø¥¯^À¥™E‹í±ÅðÖÂÂj7¼=z5çNOad¸SÚ(2Äubj”…¥6¤×1æ÷žÄè¾4oŒÉ¥¯üïçkëË¿F‡�·­^FšÙOöL¾É‚œáóìßµ¡‚�ðb|` ¸§MÄDäÒs²Z Ä›"éIa£Z[/«/}ù;¢›\ÂõóxõüÌÞ\ŠÚÄ‹€Ç!V¾×ÓêgzR‹K%yMË<£ÞNе¹CÜ«�Ká‰&0<�‹¦Š ´�­Ô÷²›¦„(2‰ ZA¥Æ–*˘+Íc¡¼€R«‚>ôIüü¯ý-›>…—/ÝÄ—_¾‚ÿþ¹ïà¿}öÛX*Q÷¢Ñf5ìJmÒ˜h-Ç&G ¡-†µ•j÷‡8�Ç¥ÃóÈPÔÅxg=ÍT§ñº(;ìð¡(6޽DL$ÿç›õ¾ziù7íÆ(¶†H1Ùðè�À€YFí6«�£0ÅN~XÆPüäL×óÐmذX´´mhëâå|çå‹<áë¾cV–�˜˜H&ä°g¢3uŸ ÐL=ÞŽ‡j¾´-µ‡éñ~LŽ£6ð„B¤ð±{ )9p�ñÓQ±""(>ÿÚÚÖÖ×ê6¼FrŸ]^ÅÌÚæ×$!bßþÎEI’¸Ñ½†˜¶#ÁÔø0Iå­Ï<„Þù>{ Ãr½|ï¶÷ý�ÂJ©ŽK>½ íkbß¾8ƒ×ff0_ZÀliŽž—„ö[mØqÄè%b"Y›¹ø]ÍZùý;Ý•ÓT90$—Uß줆 jÇbëð`Åb~?ÎîH,«+%†�VÖJBŒë …ÉÊ÷±X�Xè+0½œQÏy$2Üj[ŸE Z¡Úêse �|6u`Å´RÑHY¥¢Ò=�H ‘xŽ{í§‰¿ñ7ÿ~å¯ú5øÊW¾ÚAXÌ&ƒ�¸lÂo^ÃÕë7ñò+—™½Öͼ\©{✒s§§ñä£gð¼�‰„½Ø±àn‹þ‘ <÷‘ÂôcïÂ/}ã2þë§¿‰ÿú™oá}áezV+•5”e†ñfÖfå½,¢´6éD„”س¢“8ˆÑmÄDòwÿÁWÿðŸÿâGëå•߯à ¹ÓMNA��ãwñNì™.[ÁSìvì˜H&ÆðØ#§�L%± éµ™)¦e›“,¯;Þ‰Þ+Áã(´Çi…½…ahK>WfÉmGlo…¨ÝŽÝ؉³˜Å¹£8s|ÅB†äÁŠéTBV¶,ïʦ�ð|†¸ n�¿¾¶‚óçϳZ'¬GÄǬU×xV?¿¸‚kvì²Ûgu!v6fû›7=ý0?‹­Èº¶…VpêÉ·á™·?�䣟 ‹ñdÄ{C¨³%�Y•÷1³~S4£l46 ZÉ¥Úªáð ˜�ÝFL$®Ÿü Ñú¯ë0<¹Û3f9[f5¹Ôj0íÖQ K×vÓx0*ü{èÜ$NžœÆ~…œµY xéÒ d¼.kE”ìžM»72¤·^*CÈ·—Y>üoÌ2m™](Hßõ¶rÂ-jõ ñ½ççÌ'ßû¸>==„b>Êii»Á�Tžãt㬙µ'¹dî¶Ï•ÏßY‹PÖ«Y.¯âÛ¯ŸÇK¯^äFÝN^ÄYñN$Áã¶ßµJ³‚Y!ƒ…õE|öó_Ä+¯¼‚ÿõ¿ÿfff6S&åÁqnŸÅ¶T^†„¼¨­ÛŒ¶@FÌAŒ˜HºÜ.þ€?&6½'QÛ(’†[†¸žƒ›ó+̘’énC?‚{ø‡LíOˆË@BZ< ¢ ÎÚ–*¶Ë/WÑN¨£h­Ù�Øq\ô ahÄ4ÄHÖõZ“är§Ï£ª”�‰ƒ-Ðß—U��›ÀØP!"as¿B.ÍTY;·+È%³Hz‰[ƒõÅ2iï ›¶A6ë#á;ÑõÚhÌËæýêµKL†êYŠÛ°<'!®¡¡":!a*nþõvƒ‰ÿúßüüØOü$þÄŸüÓX__'�Xð{¿~ Û�¾T€‚X1b"é"‰x~�ØS{Œá³Pt î2Sœµ%’²ÊÖifÚýsõç8Ÿc?ѶÕäKËkL�:±B¦¯R¡ aŒîeщÃ÷}ØþcÔ¡î&†Î�­2¸œ·?uRK§�3ÁU÷{fù®�BªpK2E:åa|4cGÓXu?9–ex¨Fß’0Ø·/¼Ö;"±áÔ7¿é|ÿw¿'Òæ6eê5�£y%Ù´ƒ……P³$eðò‰môÍré©yŽwP)ÀƒˆIñ´Ø÷¡ ™¦É3¹.]™ãqd¸Ø…8Û�ï[xËØ´%x™ ,"ë"¯[Â|Mªáa€µRjbwZczþšèÝÙ®ÅLUUJÝ1ã·­š¡×م높š¿¿/£zéíõ¥ HyÉ[Èñ䱂4¾ìC.ëcp …GĄɡlŸ·¯¿ô*Lö¼ÉÉã8%•ïkµuÌo,P‰nwÀ×j;o"ÇTÊE6sçï©‚Â`¾Ù¤ÕŽz�7v$ph1b"é¢7ò]Ì+�fªK‘Û¨H{bEí»w;àÉÛg�¤ÍE!Çü ®[–´Ô›(2¿oÎ-Ñë¢ÔC„¡fFYÕvî˜YnÄ*bú¶ÛV¢p“üi„BÔæe0§^|ë'�òÑ#Ð+Ê ñL¼‰„ˆÜgŠxëÓ£xÓ#˜šÈÞ±5É•³¨Tk½ôJ¢©´O" tp›–+>½�ÍßiƒTÒãõwšü9\ìÃSgO²0t¿ªç=ÏëœUÿecp1b"éÞ*ö½Ý†ƲmsE¬ØúK2»ž—Í1 Âl0…žƒ¯_<-!.\xíß—tã�¥R)ÈŒŒÞŸÞLl˜yc–]�m—Þˆ\±öí¼˜Ð8@¢hÉÄsÅK,µ�ÇšÀ[Ÿ8Ž^"ŸÊ¡˜î»­�Éxð©‹Ü9Ãj}c¥J½†C\�ŸE£Ý€‚º%,'Ú=§\6a[è+äs !½ç­^ÿ©ñQ¼ÿ­O �Íð}÷¨U}4pŒcŠmÈ’«£Vü±bÄDÒoÄðb#½)ö3h�íqµÁ�Œï´�xç"+²•Rèãü…mx1{F¥Rƒx�xTFt\º£—‰……¥‚db ¢Î½B´ÛûÙŠëhÚ›�P×®/F31vì«�¡PÈÞÒ|� ”]éÅ𱻘Òçq(Ù´¥ÙãM|û¥‹Ò’~†u_ûæy îr»´VYàûÚˆ%Ò ÖÖªìºlϘ�­ˆD¦Cr˜—vR@¢xi I…^ŠOB‘ÞfxËS'ùYm®³àJÛ#¸1ç‘pý-‹ïcGôJáâ•ë&¬Ç¦0�égHîvºC&EÏ„ r¼e¸w|¨ˆ½ýIdS)Ì.”P®¶Ðl…ütÿå3„Æ—ɉaœ>9…‰ñaòYŸµ³»‹vìm'ŽO°/—T$G^uq••·+¥v›½åŠ¢$%««%!’R_rÓH� ææ–!ÙTô\ökëxùü�è}Z¨­*ö7„H*B( kyYÀqIpÓˆˆÅIH…÷)sêøHàû Å€b^·BwÙdã}cȧòÑí(‡$S�ëÒ‰<Çãå´ŸÒIðv¥RÉ$; À˜ž‘H»Ùj}ó[7RN)y-dž'1Qçk“4æM¤b f¬Ý^a�ÎÇŸ{¦G±²^EµÖâp±f+èñ,—4»�Ë¿û [NMŽˆ�»‰ôSÐáðßú‹¿ÃAŒ˜HöÖr¼¸Õ­œ»Þ¦fBáZ4Æ÷¬€ÿÿ³¾•�yA/Äz <|í[W°¼¼Fïctd€dòÀÙcœâ{çxŸ:9‰3§§„Xú(B ©Ùôñ9 …ÏÈ´6Œ‹7³«&‹ªcc–jl%Þ �FkMªíÍI·Ó&k¬ï�ã‘4GL�—iId õÜÛÁôÔˆ’æ,ù¼ø™w+ä¥6s/Œbª‚õäÀ8NÃÓçÁûßñ.|ÏÞ‡ŸûÁïÁ�|ü£xøø9È}ÅÆH2=3?û…oáò¥Ù´ë8œ‚ù3?þq|øÅwáÁé3˜îŸ¢ ç†àßÙ«"áÂOü<óÀIܘ[�§�V;$‰ÐéÒvª¤xê\G†hÇ��crj"„ã?£ßs4«Ûcx8<{ľˆÖܸeþ‡4�:1ƳèõR IçÕ0 èyØ<~’ˆlx¶f¢ŠÊF££ýH§ ­ß鲟<1É‡Ì ! Yp@‘75*­á¯’HúÅ ùöË—pL®ƒmN8<Ô�f«MÏ∃g�W¯/Ëû†£¶õw`&šëZo„j}À ïÊÇ�OxgæJ¨·z“’Pé^J©n 7ÚB"Ͼx«éû컞ĉcH§Rp­ç1=1Šÿù¾(ÞmC9$‰^y#üîüÂ/5!Ÿ ô±GÎภÉÌw|ñË/Q›Ê$ÒÈ$3l}RoÕê0òª\ÇC>“Æ[=‰w>uÃÅ<–×k¸:»†R¹ŽJµÉôønBYïȱßó3§¦à{Ç 3»OѳF±˜Ó€s À¶(±‰±G²K<°Ö>‚•àË% q1ÃIBC  q0³„! _Ö” Gy y.óùŠ$‡³g¦qJ~$ÃÃò#?�Ç=+ÇýdÈëô©)žy=üàI‹!œ <(dU¯5Ð_ä¦Åûq3<ò0lᲺÆL²mµ~‘5Âñ× îÊ5¼ì¸H¥sxü‘“ôµ6 ‘pó̤¨Ou±ø�Á¥äï2Š'Ÿx@ÚáœB.—åmКDò¸ü�?þágñö·<&ÎcL)‡1=ñòd3[߸vœ0gÒh _Håo}ïï[͉ããà÷­¯HOIÂtâQ�á‘ã§ñâSOàϾY¼�çð=ï}šÚÈŠ�ÈâJÙ®”«Í®¼|Õ1“=›MC<š1àw]~$CÏ÷øÛ)²�Ëžm‘Rscˆ=’·‹e±�àDÅV€VRëqñ«•ŠóÄ£§¢“¬4ßOé´l݈Ë4ÆÉ‰[üå` ?�„º$`ì>“™`‘`jÈY#½¹?¯{Z6§d*ÁlŸ©©Q¶À=#Æ•êèêd¢èQ ¾Î†�±Ô–°K²Eç{àÚÙl²Ùlˆ'¸Ž|>�d" @u|4ÁÈÈ j�–<‹-S$É ÉPãg§�¶ÚÀŽ»DSç€Mã–,"Ö7<òà)Nš$�¼á9E\ç‰�œ ØÁ[>ºÈ4’ì@ê�ÿ½iÑc¨Í©)‚…ÒÚЮÓC†`¨?‹ñ‘;*'|— kuj!$Ž¥•*ÖJ5”Êœ¼Ù�F“ôÌs¹ >|ßÇp_Nþ�u ó¼}x°È•£’ÅLG¹ª Çût«‚1‘ìRI³~ä@`P)W�—^¾lds’�½�fK³U†R$þ Òé Â0àF30P A$¬ø»EÎÉÊ¢÷(ÈçHRÎd’ü\ûå²� æ‹sÚHô%ÅŠ(]ðwÚŠ‡¶Ž‹=ŽDÐn+Ùü0?¿Œz3€ã¸vs÷­6â‘LÇaÃDéL¹K ˆH_‘•?2ÎCñ\>‡ñÔ„�H&òdxžá2$ àÈU+,óß—0ÊažÝÍ�\šdÞ2ÁÑ’`”^m�;«ö9Xª\)ß&a@�¼ë£_<ÀÓÇÇ :ÿ.’À@¯n°¿�M4©·ì|ä…`‚¤1ް çõÀÙãxúÉ!�ç?l™2Î,·$`”-ðô8�*µ&ªbkuÌ-nPÙE­?ù>“håûKíÃQŽx�¦í‡è7 óž81ÉÎb£ÌY±sê“щÍÏæ�(ƒrúQ™©#Æî Id c ©„ÑGlÆ–ËU)gOu ò<{Íe3�‰á´ˆóãñ’ ɘäÖSW †­L´©‰FÀ̰Cê‘�0O%£“a‹è³´ÍÕ’ M@¯¯TZg¨KüÖ¯«b8‘™rÓ“ƒ8.ŸÛäøˆÔ%L2Ü8%&a nrbÛª7!‘8 Å‚9sz:8wæ=HÒ�ƒá6!ʈ7¢úúòÞ�ïœ�VQQ'"Í ’IÙt—gVñ�Wfé�ÜŽÃzÌ”/,¹Èɉ[ÀïñÈP‘ž�|—Eû(²=�xÛ õ ‘ð{+·óXÆ ó„HÞ‹-ÜU4¾ÎDÑ�—IÀK{BĈ‰dCm‡àËMP\q9ö7‘‰l|<Þ%¢6* ’”O²�¬.É·cVû ¡ ñ=x"žòL[4ž%ËÙ=µ–Çejžà° ‰CÎì™@Ð4ãûZ›¨í‰õêøyÚÏ”m]dÝ´±Û‚Ecè„r¬1l‚s§&„Tú!,&&†ùïž==EÒÎd˜$ÁÕ�6ÄMá˜è:c8ûÃA9"bã°Á¶Qt7¶„”ãÚl7,îtôPå0T6»°A­äN'Ië9H(òYò¤F~ü¾Ê÷�ße©›â÷õQ©Žä¡SB¾,0äßur‚Ǭ¡äßH2à ½x(DËd?àçÅë§pâiĈ‰dúH’ÉÁƒŽx 4ÀܲAv öŒœ„Â,I¦ž"gÁ'†M<.Drê½nzò™òlØ÷J£ÑkÒðHì|( ©Ëæé”E/ã¿{!¤¨”[A·�°ÅL7ßx²—Ã^ZùXÛh¡Ñ èti: Õ2$Kò�φޅh2ÌL"±žÆ½g©“â*éÈL}ò±³$ ™áO’ï73êÄ›¦‡êlÌH"‰"IÄâÜn›µÆˆ=’!±‡qðà¦655€֖<¬)Ö]Dz‹üÐHbâ}0œ0*d‘Íf0)?R‰Õ³�K½—1Æ´ ò9zNgÚŠvˆ 7ì�d7hä@’ªËôÙl&Óé�ØÎÉÜØùyG!.BAÓ“£¶Å�€^%3¬ä3ägÓß—g‘­xÉBç¨Û›“ðV?Ot&Ɔž}üáSr<ÈÏ›ßM~'­Eî IDl€¯¯gY”#ÎÚÚeýÈ1´š-ÎroµÔju;DH¡Õ¦ n³¨z6›AÌã�OkÃp–g7X¥À×"B&k (ræ2�l3þðÍ7ÛÝø`´¥„×Q$D«‘ðXÈΆ²<(EòdÈPÀ÷Ñj(åDÚ‰ÜnëD2bYŽÀK+´7\s;BáÀlÖ ƒ€¤ +7@i·Orv”bZª„Áx²Ðh´˜ü gÜâÑ p“”÷±)Mù(Át(^PôD"nƒá±q¼ù0ÛMâ“H�PÌÔ5Ù�]×åf83»ˆÅ¥5VQß¼¹¸ï"·ï{ŒÿŸ9} ²¹I£„dn1|Ð×—ËC~ì<3Ô&*hÜw0ÕÙsx†/‚5½:€ºoë½u®¶�™KÏ%)ä‘Íæ6“ïjÚ%—�3c Ùl Ù0Y¯ \<»Þ“ë×—†lÎ �ŒX�J>G�Œ:„÷ئ„d)^O¶6áwRHÄÆ{¶eIdˆë¨‹]@Œ"öH&Å;ÖDÉY‘m‘ÞÚZ ™•Ä0WzĦ�ì�Ñý ›M»ýç¡v/þ§†h$‘»£$¶‚1‘ì@)P9„Ã�Âci½"‚ö<Ä�Ë'mØÆDmÓ÷}ãö<Ç‘ #óÈçC !A<¶ ™ µÆ�¤¶!ÞœèLŒ¥+ÜUkàí�vW´Ö€æ*Cá&•�à0¬‡ ðó@v ôF”ƒmâ2”»†1‘ìP9‹C cŒ|UfÊèÅË7°¶VF $²±Q`ša¤ý¯2§gâŠXêHo)fpIkæóËkdÈbÿa˜ù$žcìŽãt½u/.n4ÐXdH+‚_rÓöÌ7q4Ãì†Í.�ø(¦§‡�ì$Eu’Èv¡œÏ¨ ÆÉ£b#8¼°üš’­µ™�ÎÚ’R¹Š›óËÐÚpÀÕòÊ:…Ûjé¢:)ƒ ‚�k^2ÙÔ�¸â‘Ì,²Lö¬êždÒ\‚`AI�ùã¤âssÛg˜‚1h5X_\ÂÒ�Ù`mnq5`¦ÄQ‚�h™èrS@j˜ÞâΠªp_AŒ"Ûßõ×:ÜdBñ]6mê&Új2ã�cemg߃zqÌïofRL"a ‹Y÷© Ž¤¡µf+ WLæoGé¾=EØÉÄ ØL '„ ”ëpÞ»òÀðJs ho�|z�V£9¿2;§`0’-T¦/Ï÷ïØN¦º¾�峨¬•¡£=ýð¹òÀÄh?;Ü‹ éy¸É¨Qä®àx—áç^AŒ˜Hv �Lx/Ž‚  n"Ú˜~+gÿ$‘Åå5¶‰€R KȲï @*&Ú=£A!–’¼O ×á(Îð(XkKoaè‘P¬ 3€òèFÔ aƒ!/ òí,P[LÐËyõ­›¯]^^½9ÌKøª¼šEÿøúG‡á%·¾ôv[H¤„Åk3$ G3TY[䱇4 @‘¸é}øùŽP¢ÙËg‡0lý/“�eJwŒm"m=M}äÁC>5thpsnVb‹‰Z•Sì´�2ÁÂ’šxKûµ QÜO§ÙÇŠ-\¤&�:ɾxi& #¬Á†BØT$— èÐ.‰U¸á‘PzêR òQ^Y›VŽSϵ’hk3óX_XF«^‡\' Ýl¡¶QÆÚü¢x"7y¿7¢Q­{a»} $ &6äŽQD'‰fOŸ]½¼„Å‹Ÿ®îô‰bÄDòœXG ÔE®^ŸcˆëõK7èHa S^%{‹Ù\�zËP’1`O$IàëØGo�5c#ƒ,†ôl]Â~½ Ý ìq <†²Ýpkàu‰>Ã,"Ÿd‹nCBT-ñ2Ò�'Íj +³óâuÌbuv«7°<3‡Åë3X‘µ²¾qË/¥‰F´¸I1µ'vëõÐ-ͺ®®Í4‚f¥€qhka­~ê#GJå� ª•3“f‡ÙûJt ê(€aeðþ‚a$Ö�¬ÉÆ4?¿djì¢ZŒÑÑömR¶UʾB7­¸«Au̼Ð!àžQ'A/¥¹õnNØ,cÜNåárÍZ'B‚•äZ‡\·‚Cl¬®cpj‚a˃›Àï£Òm¢4^5Ê‹)¥œbÄD²L‰�Á†Öš5%ßyùF—xF^©Ô‘HxìÛ†ºÛ¡cWµU;úG>Ɇ{33 0FïcÍ�¶£ˆ9¬‹¯ÅQÎþÇöLØ!ô@ƒgΜǡb‘̳jn’õy’J7 DA¹MH”!-±ž©7µŠ"Q’›è‹2ߺ˜Î5ׄX³¾6*ˈ8í7mí<íwGÆz7ç–ðÊ«WðÒË)vü±VÅæ#&’mbZìî-�D*lô8‡•Õ V¾WåòÂâ*kO'î�¦µájaºu&.ž^%|¾Gßî# ˆ[ªÙÜ3!™´£ëH*0ö:‰Ê síÖ31…ÁþÄé§ÃÔƒgÖú†ëB¨»€�'ÇPÚN¸LÑvgvš$B1]9="Ã�–„³M´e´ë%Çè0 †´ˆK;éú#&’'ÄÆpï�!­×^¿Šÿþ¿¿ˆ«×0ss ßúÎëh5Û$­C4›-ο�ËÖ $mØABLìüËARl€¸_�=Îð0ÆDóG–µ�&qDµ& gbÃ\õ[2”¸©&v[�­Rù\aøøÔàÉ'ö'8¸ž×†1[‘O@{Iîfû fôä4FŽOAB[=l¨˜#qrí¡¢u€°ÝDЮ£YYE«º.—�É »~=Û㬭�LCüˆ˜�{ óÌH‘™ïûìËåy[ª‡!Isó+²½¨´6ÂS2CDwc¨�6†æù.‚rˆý@2Éñ«$2шQ5¶%"쬆'©°[°R·¶=OÛ0Wc›»ð„ ×õ'ǃ@ {¤ŽÄ—F Mr”çùA2›©¦óYOˆ?mŒn¹¾ŸHe3*�ÏõŽDßÎ)nÑz¿ûé½RlȵY[2YÅu”ÔÄôNZÇLj=’ÓbÏß³˜µ53»€…¥uŽé•Æ�æZcãG!†¼ªB4ry«ŽÚîÁ ó“Ç'¨WìƒàMòH§bI"2¤Ø`4 »¶@Á=”ÕñnÏÛJYÝ`šñ{øxCSã©©Ncüôq±˜8w SœÁ±GðŽ=|¶(×åFNL;£'�§$œåæú‹$‘ž¶uO ’Dz c¢T_¶P[¿ Øy¼ù+ í×#öH¶‰ĦpŸÀƒj¥ŠËWn²Åû¼è&J)L¹\ãäÅ••Nž˜è$õ½x&½OŸœÏg™#ƒ÷Ão„ÖÖÎNZD&Ê¿{j+u“q  ]ÞeæU…áAÐSQÄ–-ôÜz!bý$�ý‚¤÷‚ºH«Žv« £ƒ­F|YìÓˆ{$Ûk%¼(æÜo“çææ—ð ¿ü5\½1ƒK—g±´¼ÎMXô‘(ÛÉÂÙ«¾\Ú¨°H0ûB–RIÏ$ÔæpO4:ÒM„$¸Þ•|ŸdBj×ßuS­BÝ'5´$b tØ&‘T×fѨ¬À„î€oˆ- FL$Û@¿Ø#¸?!ÞÁõ‘3‹ i‰8Íâ=i­BqÜ’ÊçšÎNoÙa[ûB”vȘ5vèa˜©Å•á-Þ•p#2I àÈÀKó5“D¼ì¾zá؃Z� fh‰¸~7Wñbl1‘œÇ} %¶Qª`öæ2´‰£R%¡ Ñh2ÄU—u¯›:ë8Åþ_¼Ü»�ZôBD!†µ=ŒFTOŠ…M›ØUßž\”ózžábP÷IP áª<ì'”ØÛB"­Z‰é¾wi×S{1¶‰˜HNÒ+¹¯aP. ™Ì.�«—–ÖØ9xm½Œköj2M˜óâwÕA؃V»Íæ�'DpOø^O¼cÀ.¿ÙŒX6ÍÐ7ŠG‚L0%8¨Øµˆa+•-Dx�÷a6¼¡mŠí^Ht¹ã¶µIâ :²“<è•Ø4ß» ²“ú‘1‘œS¸¿aûo5pmf¡"²!mTÌÒ2 …žI³ÙkËqë’ØÎ_«59$hldèQ-‰¡#dÂ_ãcCíËŠö#öáŠ2¸¬‘\ØÀQ‡wà9�#Ñ×KÍAÑîÊ5ʆ°Æ@!É¥ög¦¬16ýú.hÒbÄD² ¡Ý cXRPk«ÙË×_ºðúõ‹^»Î–&B ôNÖK•H/1†Æá»� CZ’†Ë®Ä×g íÀ)¥T·u’žt<&‰LM� ¯�ƒq´È$lYòhEÊÝØ7ª‚ç\øÍÃ�zCc£Éì¤05@ç°Àq<ŠíVÙ‘Ô#&’m`PìbtÀ¸ós Ï|çåË™+׿š’i…ùÅ|ã›03»ˆF³)dpO«Tk(•*Û!êétŠd$…� =cz2Þ·¯�Þ¤˜ Ÿ–8Z0b¡ÕGÄZ²†ÛÛ”•x9/r3O�€—•ÛR1ˆL&ú©Ð쀩Cj$ÆÖíl“b�#FL$Ûœ?r16Á�l·š“¥RÙ—6ôúâÅxéÕËX¯äêÕ9”+U0?¿Âñ¾P*êÕµ˜ &›<ÃN>p¾çõ„Hå@Æú’HrBVJáhÂtÔ–†F×m{ƒ§>1 !�½l'©D¶3‰D�äá÷�Pþ8�e[“¨/Öa«›j7+bU íÛüÚåÅ>„1‘lShÏ!ÆVº‰#•,êr¹Š–úöKYõÞ°©¼‰„£ š­­ãÇKƒ…ç{¼ïÙÓSO�Ù[è�VÒhµèõd2I䬨…£‹([ˉFóîÊ!�Ð;!©·iÃC ÷àe@oÇIp%Ñ(+‡(‡W ›ùy>†¡+’3°ø¸Ãý!k´´%íÆNˆîMŒZÄØ qeû?ü§ŸRv>»�wš{cïÜÂ2=¾zþ älŸu'©Ta’TF†úaAí„i¸ž û<̦OA g8þ×öÁb¯¯.è#ôz´Öl�†š}Å ½”#‹ÈpAáÝIì- ¥\mSU}ØÑ}¸#üC"vmX͉Æ>×à Ãp–Ö&l£Y]ƒ1áNO4ÇïÔo+Fì‘ôóŒ#Æv@²¨Vœ-råêMˆOPPlK¿¼¼ÞY¸ÈŽŽú z-âÅPY]+óº‰ñ!¦w ò\ÔG†‡Šü÷”í¹Å×q”aÇõÚ ¾ËY¼–(Ü"¯ÄËÒû y9z#‘—B˜#âÌi ê )«X«Žæ�ï6Z"FL$OŠ=ŒÛ‚±Íîr¹, _{ý7îå•5ñTV°¸¸Ê¬¬ÎL-¥T§FB3Æð~¶¦<[4]y}ô~2éŠÅ´ê¬!Ù!’b§ãvˆ‰äý3†µ>"VDŒmCkNZ4² Mam½ÂL®¹¹%öµ/„^‰t¾­�.©ÄÑm�=ršóBÍVWt›ŠÌt_Žô¥4à�ì”ÂчÑ6ÄÔ)fk¸Eˆ�a8>7hÖØ%`sÆ»@1b"¹CXë�ˆ±ÍDɆm„0ÌÊj 7o.AH…žÅêZIl/¿z™„²ùq¡�HƒÚÅÞD7ç–yý^á{.µ �Ï`td�šŽ\G�äCíÈ+¡NÁì­hÂbDº ^޶›e´ê%± „­v‰4bl�˜HŽSH‹±k^ôHz°¹1»Hñ\kÍô’ÙÅjxs›ú­ §§G!žu•.€C«Dü§Çã0Å8É'™ðqÏÀ‹µíÃÕ®&¤ñz›TC±M|«¶Ž¶�HЬP#Ù%6c ÄY[göžÖ‹ïR¬¨=Ïu\×Uu;«!ëðp?š²J5û¦¬ªl6ÅýPÄy†´ÖKe^¿çèP(²¬Qé/æ™­%ÕíGˆ!¶H ¢b?É¥†(E8Œ Y3¿A“!.côn2C­õŒ¬ˆ{$[Ígwc/PBŽdrQD—bE­’•£|×7ØNŶï6™¤S H5<3¿Zí{…Rô�øÜS“#˜še=3µî5è¶�ñÞ´=¸Œ%—²õFLL":d  i±vDŽyÝnàû‰`ttìmÓÇOœBŒÛ öH¦£PÆ!� ÛÎ{.µ fuIÕ;½’ãÇÆÁ ± Ÿõ"¢¯Pp¿~c®ã Ðz�sÙ©ƒð¹“‰ ù¬¬îE�8”Âz‡—Ò°N�Ãõ>ïÈÐA"eˆÛ;öFÄö�¤3 ™ûÿsÐz3ÿýç1öü"C{$ÿä_üBÀbtS3¡.R‚0¤ñ#«ß_“Z“k7æ°±QeÈËu]’Œ2F)»Oq]ôŠëÚ"±Ü³¡ˆ¨x°Ù1»¤Io„×Ç1-„A‹“C›îkŒÙqa«�½ÚŽðèGÄ~ ó¿ì#†Eì‘ Å¹á=êgÔnCÚÏÓëà¨Þ|†¡®t*ÅáX2u‘MŃa—ÖzO¬DLg!’‰1†é¾÷4HJÌŠìðÀ�—“ŒÅv¥¬WRß�'b¡„@R<1S{ÑOˆý{±+ˆ{$‚ñÞ í1õ5i;O/$4Çù¾üÊe\¾2‹ËWgÑ ™ ¼K�� BýÅWqöH:ÙY|x{%Që�˜˜D Û¡Ø*öÖ.yHÑË}�^‰ã¸èÀCb?ö·þÒïðÅÛÑ1½Á#G­Q£â™–é<ÞâöCÓ쑤qsn‰âºÖ†‚üÌÍEˆ'Áî¼íV›?Ú04Ø ´Ñ*2KË>?ÆÆ¹ Üû0ˆàø–L{½º/ID›�â:[¢„­]z#`ÆŸRŠv›Éo¶É?AŒûÞ#9}”3¶”ŠÈÃh‡³7WË+¥h(–ŒðeñbµÖÀòj {ƒÂ Òèè€x7}`X+aõ‘û :-lÝ·‰ŸÛj°z�"»1»Ï:ŽCOd ô‰ýÖû|†QL$ÿü_ÿRŠ.êáÀ–‚³ã84¥6y�îwÇu*ºÿ6<›ƒÒO¨™”+u†½®_Ÿ·‰ÙËçÆ8öð`‘méŨ�ÜwÐ-[õî‘PîGoD)E‘ ö([k7^¿ ·Þ�Œû$bÜ×Iö€Ï&zé$�löv5†×Eí=`6‘;Ú:Ši®Œé:ŽÕ”�khJñþÑÆ+v b¼õRè¡´ÛážÂ|bÑ̑ёLN 3¼uAYÍ„S ïÓ~Zmh²’]R¹î�Hl‡hžÇÆ¢‘·‡}é‡ÅFã¾%’ÚjÛºÃCs7êP›Ë¥yÌ/2ÀcÏæ´ËÊ/y*™€„rL.Ÿ¦§FÌñccFŠðôääPxòäTíì¹õÓgއÅþ[†Ø�— �Ãö±EŠšë½6dL$|›¦™@>Ÿekúû�D¢Y%�3Cî/q]Lë6D¡±Š]»i¥Ré(cë.xJì»ã¾Mÿ+´p.!]è…h !nôǦ†±²ZŽCÀûªhŒ�Èý}HO)3>Ú¿ÇigRþ°ë(U«5U­Î ó´´ �¬j½TÅÕk ˜½¹ÂFŠ †Ú„±¡€#ëÉ9üìä=ò ²Ùl³·™ö~†ãYBqîÃ*öÂV �óFvõ›t]�'nÛÔÙ<±_+öY±ï Æ}G$y±ÔAn†A² \š ’æWšÖ§O�©‡}WI¬õz íИ�þävÅÊq—º‰l?e ø|…BŽãD¿�Ay\.›”Ѷlñ~ýÆ¢.aeeƒú`¢Ÿ#@,Ö¡'B=D¼±ŒÖ¼î¾ETWb¸ÞW5#:dCÆfm�Þˆ Û»øM2 m‹ý!±_'6‹÷‘ ˆ%öyd8F<^¶­Íé‘äó)Tªi àMOžq²™Äá=c6¹âîíHê�O‡¦T†ÄòèC'°²^Å�ÙeÓ¨Õ”´áL™XHè0oÈZk6�”(BÀƒh!;þFÖý 刹÷cÚ/‰¤Õ(3¬¥ƒÝ¦üvj$>�·Šî–LʈqßÉ1g?I„ä‘N�@Ê•då(7cüO?õ †óéU-HN&LŽ!¦¬FéÚ+á¯e|óÛ—1¿°z=1DðÚÊñ8Ô$ØûÊ‹Â[÷#‘�0¤evŸ±FMD µïûj‡Œ¬Ä~@ìsb1î}±ý_ÿûÏ8Àþ!Êœ ƒãcý g ôçåx@l�™F'¦‘³$²°Òá-%qîÌ>ðÞgdàÔqzK‡©¸Q^õ#1’a6“¦w'óÞÙgKk�ûŠE‰´ûÑw“Í%í:ŒnG×ï¶Q¼G©]¥5¦Ä~õý3ß(ÎÚJŠ�ô¸$J¹�²¯xH"}…,ú‹9;6‚sg§02ÔgW›.Ì×öü»óïx”ÙcH&ñjÉÉy®G­â!�!!�B.ƒ|.¥Õ™7ðG>�ž}\íBä½ì3è}$b¾ÏÏPÆòR™žeÆ–‰ÄöÖB ˜@ÌÜ 5‘úÆ‚¬e¶CÙ.‚0Œ •R$�L&;Z{„c‰äĸç‰äén»ŸŽ£lhru¥õ=—›q±˜Ãè{BáØô(ÆGø¸Cˆ¨šþôÉ |øýoÆSOœa(n?Â]�^\"é33+/„Ñß—çíÃCýœ¼816„q1!¤¸ÜÏP€ £)¼L˜{vÞˆÛôFšµuÍš\¶ÙY!€Ñ,Š�>;Ÿ'*Ùn%kL‰ý¦Ã×6†‹.áþï¯9~½Ø;º¼ñ’0`˜~ Ñy–,‚:ÉâÁŽ3Åwjr##ýH%½ÃD"Q•¼€«XGqVÇ��âäñQÑr† äºòF�guJ©^�SzÃ�ÙX,2„uìØ8ÄCa£G¹.�)ˆBÎy%¯PˆÃZÊzÁŽ{)@‡ ”s/HDŽÖ¢GÒª­mÛi!u5m a¡=“B!Oo¤‹ßçm •—ÅãžËÚÊ‹=Þmoı½®’)Ÿ{Z>Ç k äù%=ubŒä‰„‡ –4:¼åÖÛÛFêëËbzjWåý|þK¯0e¸Ûd¢µ¶!-Ÿ$& _‡¤Ns„®ñDª™lº§jѦa ðÒˆFïºiÜk0œ»Î1º$�p›5#J�…«Ë+PPÔÿXȪôõµ�ÍåºôÄ>*ÖgÛ¨Ì Æ=Úʈ ÷@ŽÄõ¾¾N�ÇÈpǧGE™@2Á ™v˜ ý¬gÂ4H{,æò²)ù^Ïž™Ä›ž>G²4¦û¢«ÕfKDt¦øŽ�Aô›é6´. Æ(ÄèȔ۸ ®Já^‚RB!�V½,¶A‘}ûŇ Õj¯_œÅÕë ˜›_Áõ™%Z«ÂØV=]Ƴb¿G,‹÷”G’Ëtk.ÉÞú>…až9OŒ Â(Ž¥·rØô­uTÉAʲ3Ø" C¶‹° ¼,�£¦ olÔðõo]dÈ@u±=~£Ù‚„Ñdmsê¡üÛ,@TÊ $œÚŠþ0 ­g¼­(Ôuo%Ð#YF«Æyì;ù^q ôìÜ “ad4×�þ"‡^¿±À“À„ÏQÍè(¾ßûÓbbD§>ì¨0È™°qN…õÓ´†ãWà^‡r®bö‡•HÚÞA‚0 A³ë.Idx¨�º'ǹlŠ÷9l°Ò9¼§#[Ë£9|?ÉhJœ1` ¹ÑhÊcH*ÈdxË›D¨µ1^b¬¹k0$56bô™íæjñBPìË9òz7'boÄžÑØ`1Ü‹]€´nSdo³Mü¶¿GB"rÜ„ã€Þ‰1�F`¾ù­ JºGãä‰ jp !›n–ü±E±¿‹ûfêõÇL»úŒÓX~a댂9 öƒu+Dؘ…1_@ñÑÿ˜Oó³;d¡-OLuéÌ>jIÒߟÃèH?[™K�!‹ûH"‡oüd;�&áû¼Î®ŽEF,‹t:CrbáæþÿW¶xÁ#R¼xæÔº>ÒflIÒBpúÔ亇¢›8[�H cI.ÅõÊÞ24­Cz[Ž—€ë'ï¾3ÔÜ =[!ê#¶ÍPÆ’.êÛ/‹WÝnÛîÉ^¤OÄþ˜Øá>…™üà�zÛ�¡6ÿ¯Tsí?;aý/© úãÊ´ß¡L0¬`<þ¶�NB·OÁ?˜ à?‹ýY±76�ÄÇÞ�­Kß'zc£ý’Ö;bÄ#¡‡r¸Ry™âh ¯®QEí”XRŽYØKÑšÍf!÷±k’Çö¹ †–aZu¡‘eÔZÞh¤S)ñ@ò� -“Ëgòr»�·�‰b74c5’hJb‡~r¤¡ ‰t™¾1ä‡N"•†—ÈÐüT®—„²Y„l?$�®޽9·Âcµ™xx[½ÞälØÚ¥aDì/‰ýî#è©»fôùO¨ÆÒ¿âø»J7? lóû˜{«Øo±ÞÜ#‡…HÄÒ»£iÍQ�p3‹¤âš.±T¨+Ïs\4ï<¶WvX�Oò£þa[BDÇb‘¸~»BËTŠž ½¹ŽžÌÀ@«ÐWœI¥S{@RËfkÙ�˜*�Ïg|Ä$²�Rm£ÅÚ¨“lÁ uOˆí®ŸF¦8Žlÿ$ɤ0|Jާ�<.3.ä2/™�6@£Ñ"y¬®–±°°ÆË[e ÎÌ.b£\íuZþ°ØŸûîL}8ï´Ö'Zkaãy�Ø[×ñ3‡�HúĻà(‡‡s㳇ÔGºM‡�§­6ÛÊHFá(ëiø°ÞŠ5G×mõC¶: C^…Br¹\ê©ÇϨgž~tYžs¯�M-ùºDð$qeÒ)®1¶I&$û“ °1g®÷Ræ–ãú4?™%y䦑.Œ";8�¾±ôÀ䣦8z ¡vP*U1·°Š•µ2šÍÛ ½cŒÜ·B/¦Çµ™\ø‡Ñ~aH7VÿÚÕ߯L8€îà=bÿXìãbîAIv/¡-’‰£àù¤á¸ }ÅëFGè�Ë€ ]1de=�„MQîô@"¯£³1â·GX. ‚b_fò-oyôf"�»´‡ �M�‰—Ó·õçe#©è8†-ÊÚjеA2 ›ÔKîI(ÅP–è%4?™ƒëgååÐFV¯n„�$‚àük7¾ÒÚ` ð¶vÐkÙ¼MìÇp¯bâ½Ç¼ ôWT»ü+a‚º‹‡Åþ¶Ø÷$‘ÄÔî«¿`=ž5;ŽÄð ð°Snôº¶ÏWDrl=ŽçµÄâYÒÙLöx· ëŒ &^xþÍÿÇñ7vÓ‚B>‡w½ã |Ï'_”júñ­‹ðÑì %kTçÝ—v¯Âhº%€ï¹]¶sJ¼ûfÈU³ÑÀ׿ü%õÏþþ?TŸú¿Ÿq—ÖÙånßÁr¹Š$'–†¨QW1½îÖñÙaã^Ãø{Gký8¦ýƒJAõЫûy±ÂAIÿ^ÛV»ž £yÌN¾žëØÛÔ~j ‘÷a�—mØÉ†°¢EÚ¯c{^o­{‚1#ϽíÁ÷¾ã�oúoÊõ—·(Üd¸ª$c)6d×áá¡"§ 12Ü+�œ¹¡krì¸�ÞIt[D"ÊÞ_©èú{³ÎBÐ<&Â&à2³é¾ ï¥3<úä“xDLí “g«@t³²Za~y?" ÓbRìî ¾éY4Wÿ>‚Æ»÷!œ:,–Úw"ùåOGQlßÂP[‘Ùƒ,ÔEÒÌv¢8¼ßè$ˆÎc@ÉJá* é–X¸ÚPØ&2é Œ9óÑ÷=óægŸ}ËŒçûoôÒxÙõÖ‡ð3S ¡Öì‚|lz §OO±ÏÖmÓ/ìæ/æ$¹Aru€ò£~S‘‘<àX¢�¢ñþ ¡ÐîµÖêÃZUë‰9¸ß�+ôá}úˆúÄ÷ý@ÙO$×¶{¢xåêMóò«—ù�Ü'<+öÏÄ^ÄQFñaOj>~aãB·žÙ'Mn]¬u‰ËtcÞºœ9³WÏôÔd8³·öVç —!ˆ2©'jkbI#"‘NO¦7$bá(<óþžxü¹gߌD2ÕI&ÌŽQP$ßT2ÁP‚xu<”4_ê#RÁ~;"1€²„á€ÄàÄrlPHrpeU.�°—¼�Pö>êÞ ucMZÌñ£÷z¿%¸¾‡§Þòv<üÔ[*Ƙm� •6*Žx%lѳº¶�Jµ}¬=Ä›ÄþIå(¢øÈ1@ýÀü±SØ?,‹5ŠHr{´Ä~ZYn‚Ò…é¿û\Phµ�DD6‹f…ð¨gVD"û!Vç=Ï?…�~äy S ±ú‹1 '"ŸŸXŽ#†�‹'29>̶B*·­nJCùn"šHOÃË�0x½Ÿ³ÇɈPxÙñؼ�«²¤¢¼H_±¸G¼“¨f¤#(s_æ8®Ê¿í]ïÏŠf;d¢53·ðí.Ï}á[XXX¡Hßj·Ñc<`Ó‚ßv„ÄûaÿQìwS6ØW¨’XkßÛȯ®l$ü´Øé½x#Žã0K+Á™áï#¢•‰õL1^w‹Ȱ'åÐsp•ÆôÄFF‡1·°f$õR…aÈ×;91b;$û”¹'“¤—wËk6bŽï 9 ŒãC¹¾ÕEhÖë0`˜Ë„ÖqÀÂ�¹ù%¬,Îmëwa›�2ñ£Ý Q«×ö2ë=D¿%#öÅÃH&fäÙ 6þ,€_)–ÆÁ i‹/î;‘èV8àgÅ÷‚¶ÅrÔFd“d¬¿6mKô@„Lh6mw“8V²º ¤O–ŸC³ /ŸF>ÇÐ+åyè†GXÙ~ÇLByi@ p#O$æÄÕëò¤;ŽMôZ@"!Y€ÇÐö8Éc‚ÞIhW�ÛCá°""Tþ Ò|÷1Ø?ktb7®]Cy}u{dÒ ZkK–ÖP/E¼gŽäí1V7YûÖa‰Kš‰÷�˜Äà�«°þÇôº<—~§^sÓ¶L¹qÝûh»oO ‚€d"ñ|¶‰ßÇBDÒJÚ”Þ‰å0Z"_ÌnÞÆ`zzßûÉÐjÔ +�…PxL˜;ìÑA¨Ï¹c–8 `œ[öõˆ\¼œ½N±¶‚«ÇÛ@s"ý@·Á�W؈¼>&ê_evA"ê÷cIÑX"�«þ§Äþø÷ÿ þí?ýGXž›¡g{7Üá=_JƒZ£ëbffJ)ö€“¥S[évW‹‚ØŸ°©Á_켘9@/ä4W¿6žŒ×å“mtè„Íæº6梗H<ì¸^üÝa»É·îx>‹OmB‰kŒödÔnB9Oø 1o7Db�Ä!á,†µ¤ã/¯Û�1¸Æ˜¨5‰è3<>è¬×ˆô Y½l‡ŽÞÏs�dÒ·mP\¤3)¨»�ì+kP€›j ѧ¡”‘ ›uÕiŽ5Ø .1Aô:#�% 5“TH$®%-¶a^);©�›ùݽåöŽÔ© 9Q»ùûÒC£ãæÊåK¦^­(î'Ö›l¡âù. £^k “M¡Ùh#ÙRîÓà‰e—‘±‹;mÉÅ·méÛ´£5þá„J�ü¼Vÿ¢ÒÍÇ»�KÞnµfW.¾\Z¹ôR~þ寚ùW¾Š°Õ,äF&½Ve«WÏcþ¥¯`íú먭.0Hfó€Ró:h Ábá ²¶žKí¥Ñ÷=fkÉ™4�Õî¼î.;õD6·¯Wب;oX<“Ov„˜ÄH.iDæå�Ô�ž‰j‹šÃ[ÉŠ$•((9n*¼õ�o §S# çaW×ÞîFš _‹R|�›êMx]G̰݉»4C#™XlY}Ïçõ{á‘� i¡´Xì™<øÐêßÿÃ�ÁÑñú6§–rÐÚòò:––ÖÄKYÀÅK3øÎ+—8 köæÖKØnô.ã¤Ø¯³=¦>eíߊý>±Ç{ÙpÑo-ý^´Kº5ÙíЫ�ÈkW>÷¿.Í|ã3#+WÎ;ÍòzÆÁñÒÍkɕ˯âêþf¿ùy”gÅnbéâK¸ö¥OaõÚë0Z¿¤uxþ Z¤œØÃû¶0ÌÞÐÚ ›Ùõàªí2AÔLÑ6Œ´úÈAéÔ!î�‡²E‚ ›r›è8NFµLÓM�ì4�™ØÞ&j´X„õ$t+ ˜mt"T EÕï wVºÛL.¾FÂÏ’°xÙ>†"?_#‰!:¶Ïu‹§]Ö-Ó-YS–¤"8Éè9»½i2I¡]ÂPQjðýN&�>öHê§å¯VϽç}:_èkïF(åj³sKXX\Å¥Ë3´—_½„r¹†µµ Ü”Û^¿x�õ'�çZ“¬º¥� XïäCbXì?ÙY?bu•"º=òücº¶ø·•nüN&Ûíò…�›×Ú—?óßtuáú3J©”âoIÑZÕ !�Ï¡¶¶tK4(hÔQY˜Accµvå³ÿ#Üw"É:<ý�ÚËFÊ~Zö‹Q(dʪµîuÍHg;^JPç¦ Ø„êØp½ ³ª� i�|ДKBá Õ÷O�òñÛ"“ÖÐ\õT½�Ÿb+Zä±ÀzÆlÞÔÝ”%=zWö}¢÷Éû¨„õ°lÑã&�EE¯—P̘ŠÈ‹ÏËÇEáºf†ùò£Š´ (Äž‰vFÇÇRù®ïv~ú×üú�w<ÿ•t:}G2QbU!ÑknÌ,ñPpýú¼TÃÏâ’x(^¿.Í!¯AйG4[-ê©B2½ì(|ÂÎêø‡bÿ]ì?Øf�É]z!I>õª]ú·Ž®ÿ ŒöÑeÐÛøâÿñjK7„R·%)£õ–'ÖB0fõÊù/Ø"±L}r/º6†a-©zåDß'±ô$+Û?ËŠþú€ˆÄD:7`®öìÞ„¶ �Ht»ý]G{#}@jh{œ €V h,ù*ΞϮ­çy ee@ø9DY`‰øÝI€Ur€DHb ‰XRº‹¬7fSp•-ŽÔaD®¼Þ½ EC‚#‰ðß1ˆ!àIšƒ©ãÇ>ñ}ß?õÉïÿA]ìï¿ëÉ¡üþÕz©¬ÅÂÒFÕ\»>‡k7æ$ĵÈÛ76ªB6«� y®k¥r$È‹õŠT<Û{걿ÎìÔÂŒ¿gÊÔ—ÿ¬Öþš2ís½È$lÕªíÅ×¾͆‚Ú:,¼%”j5J«{þ•¯ý½ƒšG2*6„݃1P©peyj¦ý:Žêù`*[5b4ûO$äK Ñ™;7M^×A[xî�5/†• ÁÜ�Ltâ�(fYí™Í­³ò=Zý|äUÐ+qS4’ =‹ŒXÖL‘ȧÓóŠtš(Ìg‚HÇ!t»‡8ûÂ&‰-Æ-gjJ)'õô[ßæïÿ¨™œž†1únð®ìN[HeeUDa1ñN ž f…@¾ö�ó\× µ¶Ë*•Êb=FNì·Š=µÍê¾~çÇÄÓÿ—*¬ýZ˜0‹ž@™ÚúÊÑBjJ©Û»ë‰l¡v‡hÃ?ƒR¿ ÀòAÉY±<ˆ½‘I¨5‰Ä÷Y¿Ñ‹ ­[új ¨•ø~T7r =œ"¡¥…�¼üÞ2�œLbØ@¹Ø™h±6½T"2é¡8ÖËØÔàQEÉ$MKŽ$ÁDI%"S’L6"YÖŠÒ¡ nðµÞz Ê]êö]únÅxà±ÇÕþÄOáô¹¢”ÿ;@1ÅDC¡ð.$b^~å’¹zmNDù¸zuç/\Åw^¾„…¥Uz$û€¶M}òÎ^È{§Q_ú“*(ÿ#¥[ïì­·j”è»%h]¹õ&³–È|qì±w^=þ¶÷V½DªI2ßüùIìO‰•rBâI±ö†h*" %Ôè2"Q}3¡Û­÷ £0•gÉCÌñ톙æñÞ`…ìdàîdbtt¯x¦Ý0]Þ|o­œ�Îîa@R�¼†ÅtG°¤X:ÒNxÔ­ ŸŒªöï ³Évå“H¶Õ 8öN0>5�úÉŸ†è'!~Û:¨B”ᥖWJJºãßz _ÿæ|ñK’qtcëëd3iîb$•Î1ú.±÷m¡…ô…CoûY4–ÿ“ ë¿Y™°?NF³Ã¹‘©KFëÆX/Ä3Åã^{ðC?œ}ð‰cÙÁÑ�чžN䆯‘ꀛH.æ/ÚöVö¦òîO�¹~’®ÞÞÀÍÜOx8qlc£ý=i—Ð9þÖz#v†ˆ³©“ï~ "¨ˆ8¢:‹ˆT’›ZµïJ ϧŒnJ·6ÇÁÔdë™5åkå&œî(×N�è6÷é4æ‹>W¾^Šù¾@»Ñ«ÛvYXßYíÏ¿›ŽšXƸ[%|ÇOžd ´¶† h£ÝnïôwG’¨7š gU+u’’t¹fµ<#›BÕÖ âÚ$ÅbÿmÓ7iü½�èÆòŸqtC$œÚß’3§??:YkÕÊ«& üôÀˆ?ñøÛ1òÀý®çNA‡®r½038ŠþéÓNqútCˆäwü›_ùëÿ+ý÷õÛ~üƒØR6›áƒÝ ’þbNHdO?yFŽ3ÝÜo7©0ê�•L¦¸Ú¶ð½õ:L°IŒŽ õÜ$u¦µö¢‡“Ñí–ª\שímš ¶J™)Y“Iô  aÓx[k!"oF· ùô: Óp#‚6¼ÍUçýísl&‘Ý‘ITlI}'G‚㺃dì !€2n\½‚ÿýßþ foÜØí&O‚H$|v»–¬O3=5¦d ’©$‚v@ÍUJ :Ç<ˆ9{ÕaYìb%‘ÞëêêŸAP{¥Œ ÃJШAy^ÆK¦ß˜µdìfæƘŸðmtö_¬Ø-© Ý±Q®u­=I§ . ylg­wL5tÄx¼UêQªj$¬G�u…{hP=x ~Â$ŠV©¶0ÛØÜÃFÂԔѡéqÖÁæÂÙÔì1ÒIàt$x6–‰R£-©D‹»:'…¤æÙä„Ö.Â�1ØÅ¢¯?ù¾÷G~ gx0"…]üÎʺ(õ'/‰^ò…/}»-)ÃÁòÒ––×$uø*®ˆ®"éìE)Wª,|¶°í™BÚPBXo‰Á ¿í{Tkõo“DÆ(å8y?›Ï‹r»ÔWzRæ?øÊu_C¬+D’é’†Áùžç ™`U×tK VT÷h$^׳޾·–“+g“·ek‘P6wÝíT¢àssÝ”ˆ† ÓXÞ§Ž½ ‘Ñ4ÆNr‰n�>ÇΪC=Å£³ÖÆ`;0w+Hlså¿c…�ZÒ„Oàû~ôÇñÌ[߯ ʽh­v „<Ô׿yþüW¿ñêÍ—_¹ÌL¯o¿ô:.]™ÅÂâ 3Á6ÊÕhZc;à,y†É$\¶Íß|³uü{`Æßÿ›UXÿ“‡ésÝFìÓv ñ/A붺aNzÖ$Ñð+ûìtVÿp9ìI)GŽ£Öð‘«Û}�6“ /#Ú숨ÕH’Ç´‚ÏŸì§@Á`[P­U¥[åýv×iÖû0 ÑÞRC‹n3†Ê&o$ÑÏŠ“ïQ›f ®b·t ˆ±³�¯hŸü�ÄÇ¿÷û1<:º'21ž�Åñ ®…¯^¸Z—YñFRˆñº6JÑ#ëR®^» ©œ'�Hª1 dye•J�‘ƒ;ÁÔà÷Íøõ¥?�Vé�CÃ8X´�*¿ÞítFw�¡£!+¶÷wgÏpØxpx¸¿[C­H2*·#|e`gšÛ8i‡T)ÇÆòfÁ®*Úèh^6ªø&zL&L�Õí‰Ì F7aÜ Ô¾ Ë�dÁUÝÙ“Ðá/€Ÿ­_äû–×­T¶W‰®¶ArÖÓ‰ˆ,¬ïÒ«Œáù>¦ŽÉÓgP«U±¼´­w]$œÔÆ´6Ê£ àJ8 �z RðÉòb5½TÉ“< �Šzz*Jqdõ]‡Ù„ô g§”iý ‚öqtð¯lQe·à`Åö‡Äþ—Ø4ºñ8Ôꡎ‹MïZ ³¨¢ã\.…°´é¡ÃÛŒŒô8­7 ÕGðrØÓr£¬­ö­u z€ÙawƒÐ+ÂÉNð3=0�KGGamð›P_K öD$^T÷ÃKå°ÿ–x|{ yÅpÔ*e|úSÿŸý…O¡Õlv£»2Ë \�‘ ðB¼|üØ8F†û!�#™õ%3~˜ôsòÄD™Ø^nuñl—áe&`´‹£ƒoÛíßDàtK!íRz.[F·¶�Þ³'â8‘�.ÍZï$Ý=Q�:ȦŸgj/-Úx˜ÆJÛwp*bnWïÓ Ê0í*�níPLÂŽÖÍPÛϰ2¸ ´­jƒ‰ZåïÑÑ�¡52ÙÞûÁãmÏ>×­ýÜW�jµ:…÷Åå5ÎAùÖw^�þ;ÔP®\½)zÊE¼zá "Až'ƒƒD¿ñদމ¼$ö»µ6ßC/ÌÁá³&Äͤ۹ºV�6f×Z‰„™Y>³´¬.Âë”r`‰ÆízVVäy°âšÄÁË–Lh¶q!ÍñnÖIjØÀM;ß¶C°òÝh=DŸ=LwžÏXm$¬G-òcì : ¼ëÅwcúø‰®6qµ}¹H¢Ç²ù£´`¡Ýœ[f/¯µµre£Ú^ï²ÍÔ÷Üq 3®„DŽÚlå‹¶­ËseÄÐ;T ¿,B$¾J¥*ÿØöúŠolG-͆�¼Z¯ ;ÓLylÉÅIE„Ò‘™E;PxieÒc!7ØÙ_RY�¥…# vGÎîý«k‰4j“[aOÄØ;™ñþ�~ ý²ö¨Rý�]-¸ï4µR˜x¤Šô„K�M¹êž!|Cì׋ýô{&’´X]D†Œ]J÷O!•]Ñj­¹£¶)–8ìè\o³‹HQ×ÚAýÐÖ(?ç‹Ëb‡P¦ Ó®ÑþRÜ%"WÌÚb“KBw9s+&“~Ÿüþ2 ™ôº#x:“ÁSo~ût&W8v„{ß|Uì׊ý1}؉¤(–ënx‹½rÄB¬¯—1¿¸&"Ø:Ï”ÚQýˆ�¿¾OmOŒ!YðEÂfa›òëçó†ë˜D¿#ä×ÜÙöi€vùÞo©í†¶BIT ¨›]®·‰ñˆ.¾ï#3rrhzI&Ò¡ÒöO¿å­ŒQ8š¸`g©|‡�Hrb~·›+ �0Ãbyu‹B"•j.‡_í¦²ÝžÀ„bšÕÕ\•‹(œ¥œCéIzH�6ì„J”®Ã„­£é•ðïÓ�ð–MQKyŠîJÁ"š/coŸó“oz3ÞþüóÆåÈÓÕ=Çu]#‘øñŸû•�ü8¢¨ê/­Úg@ "™óÐeqpÎ@­ÖD¹\ §Å�=n)ÎÍQ¶–nG�7Œ‚_H?×ÜɆ§LÕ^çû<šá­ÌÖ$¯vÚ#LÊë˜M¢Ä ­;õbø‰„z߇?â¼ðÞ÷1lÝ-ÉdÒûîïYúäü�ÁÑÓ“ÿ¥Nôÿsæ(Éé^H "�GB{"á�3Ü]g‡âWÚ¾Á±¡Ç�ê ŽÐÙzÒøEpôŽ;6V€úÜÔŒ-u»ó\PôD¢I‰$píÞÞØ+I¦Òx�¤¿åïÜ3�­1448óý?ö“³ïxñ=Åd*åÝ�$¿ýRƒdsE°icWÑ9ÔìØ™Jú(äÓ€á(θ´jkŒFD"p¢z^>0á=aÜ´v–!Ç"¼àè ïѬx˜ÛsÃN¡Û6ƒ«eõ’�JwOc2ñS)¼å�ïÒ}Å"gÁo{¤·Ö6 'úúšo~Ç;þ{wÔXÒµqüúZB èØúçîîîîîîîîîîîîîîî¶6;.@ôÞî>tuUªv`f`è_UW'™Þw%ÏžÖ›[÷Ê»—z…W|aTKT9®¼¯åÒ'O· Ôʉáh‚c4nc Çaõ–ˆÜï?4¡?ØÃ™ì^Cw�×ŶÃóãD$ˤXöD} åX‰§ L…z@ÁÇ¥Ànn@hJ “iòžs’W½7ZÏó¢w·íñuVVWý«¾ÆkŽßé=ÞË}àG~týáÿ‰ýw~¯÷�?sþÂãx_pŒyÌÿZ)?!î^ç8‰ãXçÃüÈÍ[›8ç©kËöýÍ÷{•¦ªâœ=”¥¾“/¤v �ÉùYǑ棒){¢`G žã%.ÏF¦&Ô�íÂÕ[@A•dzŒyý7~ãÎû|ȇ÷_æ^ñöBwqhŒñBÅ£1\Z­¶{ùWzåÑûøGºw~ï÷-^åµ_Ç¼à‹½x{+@VŒ1%ªk¦¸iÊî§’ç¿p\ƒÄ2eªÊxÜÐŒÄ ds³ÏÆFŸx�æýV5¡õ“› %›\ýŠÄÆq]+jÚ€î¡0ðµC�ã¸É[Sœ'‰4�↠Ô‡>™®ÌHõ’/÷²çÞûƒ>¤½UaØ­£èÇÛC^[ÕŠžø‘ñë¼á�?è£>ÚmÝw’]xä±ÂlÁ{Áûìb�TßD¹ø Ç9HžÜ m…0q·£ž‡£°‚+¼¶—s»Îdw:¦„¬5¹G\ Ç™HØ÷" ì‰o;�p|(˜ ò]‚SÙ?�ó#¾?w [®žÈª³.,¼Òk¼fûßã½äÃ>îÙ –âmÞé�‹'^äE˲ª Ôs¢ˆQò¹ÈÚË_Øã$<ËÁó$½þ�̬uŒëf«·UeêDÀ”“‹“ŠîdO y•‚½ñ9õmo9Nâß?0<8Õ;«;œT'ÉÁP%†Š,,.JgaÁˆˆÁ{NO>÷ôÖ>X8îAò,p…Ò4Žº±á¸”ÁpŽ~î.ÞvˆsUå@¨ßLî '…dšÏí}xËö ì >fUIœÛ*™aBÁ×qe[ï0ŽOITC;‰”¬ïMëk©V?¸ p‚Ä}ˆªÒï�¸vmç< m€ðXUïz„¼*¨*F¡™rr®ÖI!òyÞ¨+ß<~;ðL+JeêTA&ǨJ’ìq³áHÙýpÛ:ûÙÀM€“$5p‘ƒC£7!B¨Hnßî…êd·aì¸�<Ïá�F39áäÐ0¼…)öV•4½\ípÈ1£L‹‚z&¼\‡¹Æ{˜/Iã4«~Q«Õ÷~œ¤ ±ÀS0ï=@˜#¹xéz<ÀQP…¦¶¨ă!2F8(q³Y¬HrNÉ (ºŠ²÷ªÄö7Aü1ŠÍ)'Œ‚:@&½ZhnÂÄ×w¼/Iî`ŠK” Ÿ¯åÚÇŒ2<¸ U aÑŒøŸÿ½ÄÕkëd™Á{e8‡×E&aR–U¼VW™.˜ð5¨‘“ZJ{À£ì…‘f³…ºšcBPÇ´Åù�ØÇæêÉ0W<>åÎê$Idà¥úi-—Þ…réK�ë'9H6ϪËå+·øçý¿°»}4jhš°I‘Éîuˆ=Þ;ò¼ >gê· ÂóÎiN"ÉJ‹¿çá-7ZÀ„ãB�ÃÕ CAch¨EãIØ>x;™oCI’¸´÷ëLçüûx€“$W�Ña\ûÙrýÆöµ˜××MXÍU9 1TLöò±W¦&·„†zõœL Y« ÞÙ�rÿÔç4›†ãB½ „¦ ±›Ðô-î‚O;&Ñɾ“ÖÚ×¢Ó$�>‡¤ßrõúzØœ("´Û%MãBÐLª‘c ªÊt)¡™rrÆVÞâÄ’L(<ˆ²7‚í|£˜C .P�QÙ„zãŽ9•äTº|š*ŸÜ„‰ÓR‘Üâ�ˆÀÆæ€�Þ�3k‹¨Âíõ>um!.ýõÞ¯Ûeš&—TyÈJ+› N0!ïbtÏSH®Î°}@˜uª ªÂn”ƒ¡êõxÿ}‡¹üéæJúÀß lœ¶ i%‡¨·9àég®„;Ý/]¾†º¼÷T�êAr�\=ÙÙ~b)˜"'†%Êx¨×uÌ:A�¶P£«h}›Éä¼Þ± ,¹?^•cf |ð€8�A2ÌqH¼Wêºáɧ®0ÖlnÂÜI‘g€b­¥®k@b˜(Ó£àÇ`r0¨„ÍäBÖ”½‘Éž‰Ygû‚wæH¯¶ñ 2¾ ¶ª€��=Þi’LJ"Ï©Êç¨_ŒNk�\;Ì¥iªŠóžÁ`Äõ›½°šË{O^ä9çhšEQ„©19qURæ*8ùÂ!Ž€Q”û'’gžo=úo%µhÓ =v ¨¿—{ñaæI¶IÞù1ÍæÞ½gÎ °Á.NÓÉßqȼ÷üß“—Âà…ù¹P‘Ä›ßÅ(ÓcääSÈÛBVîcÖ@ã¢gv ˜üèC$7B·v(hê@ ÉÉàU.úlî£h­}@<øÖœö ©�ߊýa ÷”d™aiižxÔ< ñb+O L‘Ÿ!Ÿ·Á”œ ¦ŠEP@ïõåï1w#@˜]3Ã×H½~ã-˜’ãÏ‘=éLû£€Æ¥ ~ø7Ž@]ÛpþÖåË7Ž&WñªzÁ«ŸÞÄ»j܈( €pJäs‚ÙóŠ&u0ºj™YñªäÙ àGàã¹\¦"9²ößHµú¡À/°£$Ï¿Âá G¤\¿¹6(^¾r“x„øwî)ÉßAÚÆ0 ÷¹c]¸+žl0f»…Êdz暬õ?mŠ®�Ïío)°)và@˜=åâ�U‰rd&¡«àP%9fÄ É羈jõ+�!÷%Éønà‡ÌZ˳ÏÝà©§¯ÒÔ–ºq4V'C]Ó 9H\ᓵ9•Ä@µ ’í½*Q‡Ö=ªÌ �å3CýäJg__;.ÕI"™%Ÿß¾Éð뀆Î0UògÀ¯r/"S_7~óÖÿõ¿—øçÿxŽßøÝà/ÿö?®w™ÞqàR€*d-0§R>÷•({›/Û3êe& TK`rfÉäÞ ÚƒcçEÐlîÇ©–¿¨9r¦k |ð¦Àv£ÊÔ©rùò�ÐÚ­’¹* “ñUUNažDA-0YÝûÓG2(—âɵž»@™ð�Á <Ù ³9dÊÙ9‰×7@Nµ=B¯òÉŒ£Žò§TÚŸ^ÀmN Ãôý9ð#ï=Î9²,Ãd¯žxõ.F@¼�Ì•œZ ùÜ݇÷”]x‘¦§ :³!™µ™‘y?9–ÇŽ@uÖ/RKs"Å·dsç>¸ÈIAâbUò›ÿTÊ¥O®pÊÆ_$"WŒ1Ä ­ªŠ»M™L%hŒææZäy¾ÏÏîd +™„ˆpª PÌï½*ñµ¡éYf�B6c'¨grÄ|&”dV˜1ùÜO°}%.|30`jR�0Œ©U?¸º²à:�ËKót:¡ï̵ †Fh„>MQä!„z {JŒ‘ý­Ü1“fòX�@ ‘H2h­‚É÷˜ËjÄnzð–Ydò;R9Z¾™—âgféy"ù%ÊÅO÷åê‡Ã)f88µˆüv–e½…ù6EQpáü k+]yx�VU„V–9y‘aŒ°´¸2­V¹¯J"ϳÐë@À;¿¯ÏA= “~ãmˆ“pH ï@µ²ÇªDÀö[Œn(³GA"†Ù¡ >ΕôÀÛ# “DЬõw>ë¼?ðõÀ&& ›½ág™yzûPųgYìα²ÒåñÇÎráü*?¼Æ >qž³kK,v;¬,χ!©ùN{«¯aOAà½Òn·¶Z…zÅ«²dr•.¦‚¼ ’A2Q­ì}ˆKÕ0º–Q¯ÏìðÖ/•#æA¸1ø1¨=ª=%IVý®T«ïü&ÉÁ‰”ó7:�ö•¥nÒÂÃ|§*�~¡‡x‘|˜‡ZãñÇÏñØ£g9sf‰¥Å«+]ºós€Pä÷¿3Ý{Ïx\³º²ˆÉLx¾ÿ#QrPGè‹.LŽH -‰$‡ÖÚ.{÷ ŠŒo…~æˆ�ryÆÎߊA’•€€úX™8’Cuô3€"9œ Ùh¿Xommå÷¶ª�º»0·]‘„Š£(òk«]–—BEòèÃgXY^ËùsËÌuZ̵«ð¾ªÌã| wÙW…‹Ýyæçâ)0´•UàF“qód÷åÀewïC\n nF/m* }Ö!™eVx„גCõÓØÁß“^�î‘GÏÛââü7u:­ÞVOYæa"|~¾M«]f±Ãòò|˜39³Ú �²=¯rá ««Ýð^h·«.»Q¯t:íð>€<Ïp3�µÁ޲ $Ï'P­€)Ø•î¾"i6 ‹SÓ¢÷l÷ àâõŶoê4$‡æ§€/j’à ’èÖV€|qU•?[yØuÞn—¡oU[mûyhUXÕµ´´°a(ì‰GÏqöÌóós!@yèLX>¬ª»^ú¿±9ÀZÇÂB‡<Ë@Ø#1 €OÎ×Rw·Í`IÖÚçYUž™%"Ù”Âãþ(wçm�ÉY\9É¡øà“Žh�H ’èv§ÓþáVU^ Ë{��È!Ï3¶^¡RYèÏŸ[áÌ™ÅP±´·ŸŸ_aue€"Ïîz€ã“O=‡1&ÊÞˆLVl‘B Ù½6"&ÕdÕÞ¶ç Ì.¦t¿r'¹gÛ�2Ù˜ˆLZr�úqƒõGÏ�i��þ4ÏÍOæyfÙAœÿ¡RYÚŠK¨N{ä,ahk®Ek•1€²0\Y¶ýz�þˆ!0 `J@îõÅ—˜Êev¥»ì×™U�—7¥ÂƒTSúÎ&a¢þ`«‘D�ßÞøxà"É®Ì!'û׿ÏîâÆÄTU†´^àñóœY[ �Ï�]æì™åíý(aÉðêr7„ L”Uå–—»žýPÉ@ ¤Jä~•]ÈZÜ'�dõ¬ïà×ֹĠûªB¦G ^} ÅÉ�ùÅ"? Hf#H¢g�ïnq"B˜SiW,tçB²¶¶Vt�;»ÄÒÒ|è·69†�ò±:!ltlU®Ó®<ªì�ä­Étœ7Á$÷` h­Ü_ðªz|3b¦ "Rf_sÂA8Øë�“ß‹ó!—If/H¢?þe/çfy¶(!0Ο]�²¶Ú ó(sa_J‡ª,ÂRâ°2¬ÝiDU`ÅVÜ7‚u`â÷äÞÊ%(—¹7oT½‚(³L5D÷^muPÓ5,o÷JrP!ò±Àÿ�Ìt�\~ pì�ÄPiµJά.òè#gXÛêæB t:-ž“ªàæõkí��žyðûGLÙ $÷ËĹ…œ{0âÆð–Y&ˆ°+¹£MËøwà×�¯ \üF:�Ý µ¦’iº]ü àŸIf;H¢ŸŒc�ÊÞm‡EX*¼°Ðá±µDx»…='umqMmnÞ¼-ì—d >V!yF0$÷K!oA>Ͻ¨æŠÔÌ,ÝeiÏ¡1þøcà×€þ ø{à/bXü ðƒñ í3�wÞäÝA>“ùøtLñiÀŸ7€1ɃRàO€÷>ø?’c$OŸü2àÙ³X�TåöÜIh/þ¢�„0iUEè{ýuÝìÿøx1 nßË?“¸âI2v'ˆÖ¾0ËL¦t?scà7€÷Þx[à��7Þ8öo_{Ïø¾�¾ ø•8IJx`ˆwß ¼+ðÞÀ üðÀw?Ãêß�!ÉÝ< |ðÀ¯ ɾÈÓúm¡‰�òn@ÉýQ@`Â9�÷ž¦q<{ñ <|a�³g×XZê’eù},÷�TÁÄóµ² ŠÅýV#‰z\„zƒ»htþ‰ )æWA™Yõ-\u Ü˸ü;ðS±Ò¸ÍAYz©0@@t�—^xà€óÀ*ð(Przýð#qdä_Gr|ƒ$:|ðáÀ2÷¦;Þr¡e4®C t:mæ;t» ´Zí{®4 ÔC P-C¹ü +µ;€þEð5;SÕêÌUæÎ¯ š1³Ýô2º" Âîþørào€«@�#ææcÀ¼N¬‚^(€uàI ŠasèNXà\ŽÕÛ÷ÿBrr‚$*�·>x)€ª¢ Y–±°°@»îBADØUœ AˆB%Ò:3�•ZI}—@=;ÊZ=íðóÀ·8’$D/ |ðŽÀY@ž,--!"Tñþö]Iy|=™d¯Vã†/!y@ê¡ÿ,4€°£¢«t’™îúVŸá¥ªÇÕÿxøàIÀr‚Äa³ó±zyIàÅãó qØì쌇Ëß_‡‡$'?H¢¹8)ù±ŸgïâY[s!D:�Î}‡Rk\qT­…×’ih6·ÃDQ'»¾§}ZkÌ6ÙÐzý3¥ÿä_ Ù¿N‰,”À\ —׉•ËãÀ9`èÂÑ?|éÁ¯ÄJ ³güðþÀÄñÌÿ,{à½g4ÞûR,Éu“çq3b2- E-ºž])Œn@ÓóÌ4íjëü  8}·â<Ð×ï ¼%ðfqˆúc�_Ö¹SøC˜LÿTàãO[ˆ¤ŠdwóÀKo ¼G,¯3°¼¼r�Š$�áá!kM®Ô-º$Ó¥vè¤ÿŒâ›|÷ÀY¨é<’#™aVåí?¥uö-�u’�õžZ^=V++@\ž:±šyÙXɬ] ÅþYà"ðsÀ÷¦ S�ì¦^xgàÝ�s€áæçèv»dY¶{E’U“=#ÙTKU‘$ãÛ› ž+A+v$^ç.¨T˳ÊTOi¹ü–À¿’워.·�Eàaà,ðB1\f2³ T€0á�¾ªþ¯ªþ«ˆü›ˆü ðïÀŽ$É=ÌÇ@y3àÍcµR±‹²,Y\\¢Õj±3�¼"�µARÔo¢ª5ý‹›Ò¬¯ÂNò¶§ó¨ÁÌ*Í:?(íµ�ú$Ó®fª0g€'bë2Ù+³üûx<þ�¿û«¿¼4$)Hö©^x»X¡¼�ï4é¾²²–ï$s L'Ùs’ƒ£M$ý§ Ôgì¦Z%L¾‹a&I6òÙÜ'ßB’¤ÉöckÇA¿&Nªý °¹ó¾½ãñ„N–üª#9x’·Zdsz÷�äõ:3K]KÜèS%«^�$IArì �ß�÷|dœl»M¤ MÓ`­Å9‡ªrµ€ÖrßD2h­ ¦Tv£Æ·À7Ì*Ñæ1lÿ³MkåI’‚äØóÀEà‡ã’áÏ®(MSCd—åÀjÁä $‡¡˜Úç<’yvãGà†€0«Ä�ßBG7¿]óù"IR�œëÀ·‡¾üœ³;‰êÁ;�Œä�”ÝL«e”�¨‚ÊÌRoÄöÞÞØÍOuÕJA’¤ 91jàÇ�÷¾Ë{¹išÝ«â5º"$‡I�jÙ�µ•Ý4›à-³åùsm nø–Y½ù¢$I ’E�¿UÕÏTÕÏ0&ûGPvdJBËý&ÄÄ”ÂÜ9�)ÆìÄ¡¾=áá]ì=ê-ªPPïV7x’$ɉt³Õjýp«U}‚1ÙŸ°u Gv,J’w2_�Ùiv==Ø×5U�ªâ�%ôÞ9¼w ^}³¦\)I’$'’~×ó!ÀwÏQ‡·½£û²JÄT‹]ò¶ån ãÛªw<ß+õUÏýÔ{œ­q®Á¹uÛ ãgxÄÛ7,lïCLk%'IR�œXÿ|ðñÀo5j¡Ù;”äHViµf‘|ÌNÆ·”¦ïˆœk°õ"ï-ÎÖ Jh�âãpDªÄðï÷®ÕE´{@‰ÐŒz¸fH=Ü¿ÓÖ!HðÞ#¸®±›Ÿ­vøšOÿíOg[Í�$)HNœuàgã1õ_\@¸¨’EÊn©­3WËó©†W ±j4YN=êƒ�ð¥^�{¨úÐl=ÂÛ1 ‚% &CUn‡ŸGPB8Ø:„…z‡kF4ãÎŽ©G}ƃõÐ\3F½ ã�Eíè¶„çÍxžƒ„ð.T"ÄÇÄ¡,T=®©1Y~ÎÙ:|¶w6QSBH�Ãïۨqõpû}á÷†ðª‡ÛAúºcÙŽÖ_¨€<s IrB’!ðK¨ÿòöÇ’U? ü7P“�¬ZÖÖ™Äñ|ê`t W÷BX8[3Ú¼�÷ã—|¾Ìð“«qv„�|éãm¨^B@¨ ó*4u?¼f›q¨.šq¥íÏ}3„Ï6YÂÅÛðÞ8Ç ;l~xßõgþiu<¸õŠ"ÒVI’S$‰žó3˜âSóñÀ·ÿX’Ã¥>£\ìRÌ�AwÞ[2¾íêѦ†!¦Ámâ—>qˆ* y¹fÌpóªžf´Éx¸†¿¶Ú­ðs®ÇØ�PéÐŒz!0l3 Ÿ3ܼªC˜ÔwÍdÅ…!0¬ã]~_=Ú\õ¶Y9ÌC $É© ’(Ï¿|Qœ�ÿÚÔDD Ú焬5àJ¦#kG›Ív8Ø8Œåc…0êßÁaëA˜Çð¶ÞšÐ B¨N†ë Ö¯2ܸJÝ_áÒŒûÛÃSὃ­×Õ‡° NꇀQUÔ5€âÕ£êâûâ„<ú‚ÀK›$É —‘ìD�!ð´ˆùcà÷�1ðR@Er8L‘cÊõF˜!G5(jE2¯ÞgkP‡ª÷‰B`ÜÏ CYá}BÂÙqx_è�ù&üL¨@Âc‹÷.4ÝîÕ) €€  á½ �ψÈ/¤ÿIR�$¸"Yñ‡¨¿¼°ä$M³àÃß0!"EÎh4¦©Çµz×Õ ÂC�¢qr½ ÏQÅ…Éô$UÙõ&Gž ÞY:D]¦Þ×á�ªT!<ª^Õ @¬ <I)H+Â߃ü"Ž˜èÃ@IrpÄòjD½! ÏÓë°Î VõNUàÀ£ª¢±¢ n TgA=‚09ˆQ™ ~?rä ��@%´˜R`�` X°õà—K’D)H’[À¿j¹òÛ ·ÿ’ ]’ƒcò\íØ‹ç0á¼g³×Ç{/€2Ô¢jU½UÂc¯¨# ^Qu!6ÐX~ LB$¼FL  �Å&LLÈ.¶ü p•$yž$ÉÀÌ�ÿ Uý¯(ð°À<É#yÛaû5Ú xUíõc�@[È6À ¸ØxP|l!h˜ð€Äf ÈžWi;›üÌDø[àïH’¤ I¸H{í×Áü®1ù Ðó¨_%™.É3Ej±=2ñ£ñøRÓØ¹ç 7展@." HìÍÿ·w@ÎliÆŸ“Œ×¶mÛ¶mÛæ-­…ÒÚ¶mÛ–FJf"tÒ}ð^MmÍç|Îôü1ÊxêmÀö CÜöÙöz_ì I[]«°ïï`쫈””³˜#ÇÔeñ½‡"…+�U�c%0Üè0X?ûV(¬Ûëol4çáà‡èœKÛ&ŽÄ–maÙz¿Ï÷ í¡í€™MŽ}ý¸3°ŽH ¹ä3丸$¾gÅ#¶‚â�cÀrµÃͳƒMzï‹ÕZ½cœ`4i+(p€±o4*lÙk’‰Û¢’¶O4fV9HÌ2àÀ§)!gaˆW—1ß½?±¸¿#] ‹ÓÈÑÊÉÝÓ'K±ºÙlöº½þÙ[FÊÖƒ‰mAp{…çœmûolëÿÓ#žØûàñ@D¤d\ rB\ˆ˜ÝÔÂàÞÒM±xa,U�#ä‚ 7 7¬M{ŸÖ뛓Þûiö1RP"€™M,FÛ&™bëÿ3@•Ñü¸°†HÉ83CN¨iâàrÄü¦¤â¶¤pC,^HQ9vß .oäY·á7›çˆ1U82¸Ó›Y˜bt àÎÀÏ9¶E%¿qpÓ†7w–®çˆÃl �™e.VkÿtíÍÕ‹ƒM‚ã82Îä]¼‘’qÉ  “É.Z1—òëcv]RqY° ai98W‰§i5×þ9›5—§bÈ›Çyð8 rh"‘³²‹Zô×µ�Ý×Y¼…#� 90³D‘ò|Ð]íÔþ3ë‡�ó‚UÁ1þܾl‡‹¸� {s®èÞ€4¸“³psR¼’#�Ù?çCðýîæ’ë5gBÞ›ÇÃèû\Ö€[C¤D\ CdG9�…Á5ˆÅ­°pgé*gFÅ�ýqVäýÔ©Ï[¿½ê’Ï«8w²B2|‘q1xdÇ:·ùÞÕ�…[¸tzXâµv60d;‡Y²‘³XÞºqø —Šë€9JÄ€v}>¶VÿîÌRåWQ Àc�÷#R.E�Èqp1Ë[�u)Œ³pJÄR´Æú[�ú¿g1›áð-wþŠÈAi"qÉ÷oèBÿÄá�6II$£±òÏŸâ³Ö¹qŽÃôàq@�H TÌ =ô8Nss?eæ|OÕ³¾˜Êä%QqîÓ³g�€qxÀûË‘ "Ç_§:{î72yÖ§&ªÿ¥ œsSg=/8ŒÑð^àGˆ6…D$�•³<:¹é?±ó9‹ál€ct_ÞptÓˆˆB"ò½T=ËS"Õ°³¥ò†�x‘Æ·ÏV9j ‰È¨Î>W]ܹῃn½Ž£ô€u  t¶>/?Þ<x&ð?DŽ…DäëLžý9;o¼ƒÊÔ¿ÜÔYŸ_dÍç�{ÁV(îܸpàŽ[ß=ø0DDDŽ-3sqÐ|AêÎÖýŸ�ùchý•¿„~íÍ)ä×FDDƃ÷þl6¨¿Ëz c’Ô[ü±ÅürfVADDÆ‹™],fµo¤qžHúËß5³³#""ã)ÏšWõ�ÅŸ¦ñ ÉOÌì܈ˆÈøòYãj±»ø³ñÜ´µô�dvVDDd¼¥¼uíÔ[ú©uçÇ*$1«½ ÙBÑ¿Rl|Öºóq,BÒ[lÄaó&ˆˆÈÎafç‰ýµ7¥îBvR7iuç- 6ßkfsˆˆÈÎRø8›òöSoiþäL" EÊêŸóyvDDdç ƒÖ�Sí«©;Žc4†–­þ=õ—,[ûSì¯~4 ›�2³ó""";Ÿ/òó¥¬ö ëÎÿûøÞ»Ò°èïúµK›Ù…“Ù""R>Ö�¿Îi�ÏÛ1žNRw±Ÿ÷7oŒˆˆ”Ÿ ê粬ö¾cyˆpêü¯ð�¥;#""»CðùÅBwù›Çn"ùŸ�½Õ{!""»GÌ;W²þÊ�ŽÑ™ë}?hß Ù]Ì÷®fý•ïõf®þò¢E%DDd÷ EvÉ”Õßžº ½#žH²µ›Ù,""²;ùd3qؼ¿ekßµÃ=¾¿üë”w®�ˆˆˆ™�'6ïž²õ·YùÖ_jYo>ZwÞN¤ÿo›·Ô[짬öÉ”w®�ˆˆÈvѬb–.dEç¦V´Ÿûk/ ýÚ+ŠîÊ+Soé�–­½)æ�{ÆdgCDDäp÷årEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDN¦óÄrh¡œIEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/teawie-xmas.png0000644000175100017510000051051315224505336025037 0ustar runnerrunner‰PNG  IHDR›¢1ík�‘IDATxÚì™”ûx…;j©H=ü[u‡õpmÛ¶mÛ¶mÛ¶mƒm“·7=k›¿ïœ/¶n^Òhc0 ƒÁ`üµÌir:æ4:¼h«P€ ”kÝvεsMQ§Ð�Òýu6ƒÁ`0~ 1^¬GÈdàaðx?¼>†w‹áÙpOØ Õ„êf¡Ã`07Y«·ý @p„á¡)I}1ÉIŸ¤ùS8w X…FÌÁ[Vcvn"Úä|ÕŽFCP�òÒw÷“Á`0Ö§ ë! §ÁÙ°¦á,ØŠ©˜Ð~Û[{§ä²§eÑ/ï™â¤]’XTÜÁsqQ©ÿ‡�îÚ§ÞHK %8‘Ò’JKJ‰2aû �SždêT\FFq�u©îÑ”¨Ü‚ñûaþ¨õÎÆ`0,`já²<Þ_�ïÄÂËÖÛ:¨ ý†]wC÷vp=8O´‰‹ã­>œà¤†Ÿ¬fe¾4/¿Ø­yG»×§ ‡ðt¬Éy–1Œe¶ 1Q©ÃþÆPÍÜ�ä¥*„$Tnn¥¬?L ÙCq^¡8'Qãz=>*†[h ¥�ZÛÍrsÛD·[ûx:–3B‡·1þ•ô¸ôº.Isf]~¡OÕà §0¹WñFJz$УxBQaç–Áø!¢œh…L߆<«Ð@x¸™Lƒþ« «£béFõîÕuß®l´í þðÇåp+åý!êóú«¥HëG¨^ºO…Ùbe+pzãNñÑ�Ö³ŠÐ¼ÉnZyÝÝié�¢Eר…†–¤ÞöÙT 5ÓpÇ„Q;•:V( µO2@ãØÏwqlχŸ¬ý�ÿtݪ§a ÐÂõjºüCãQyû2‚2»WÕç+x‚;”}‘c³šïÚ„C¼ãîɈês]’ëå�@ëÃ9Wà–nÙ}EÎå?£ß×|@§¨­�ëe*–m·1ÿwp3´À5áËŠ*4¿�,”dBÀÂÆúM7ŠzØÀ5lä½Ac(Ô^¶Vçi�V µU1¾‚eŒ¢òy+ëòÝ�ŠçL³–¶1ÿ7P9HxÀÍ/Ãö_üÜ&t×Äð¯º1¼fÜ)Xáó…!X(óÅ „ º>èþ¢miâæ5Ò‚2–æå’œôª¤³pSߨ­yF{\:å!Z8ž¢ùæDkÿ@dÖòM¬÷#kÛ`úÐ:ŽÇÙY=PéC`¢YÚ䨛h�Û*´çí÷¸Ý Ýn§.{�ÖÚç"™{Ê"T@Å3ØÚAKc¨mÒD>yÕÎõxø-upPEǘcwÚqõÁÍ3‚vIÞx ×â[¸Æ>è”]/ý‘WF:¦¾ºH"óòJå¡;Q¹œ—wûoħ°G2/õÐû½š·‚ÊÄDÈP—⦌¨–Ai´3Ë!\ãÔ…v�ê© Kò2õ¹túœ}s€ŽlÙÂðãuœN'­¤ã â7IiÆÖض®16¯m󮯦mÛAÿï¯ZçÚV×Z{Õ±êT}çß{]*âë‹"âN¢�pýš,ßÀA–µ÷ÃÂ[þúE¤âr°´Ð`'ÜJh\díQó™q^*ºÊ>ƒ‰ÿ`¨b¨HTŸY3vÈtß n+¡—™iÙê0�M­©å6§³Cµì=d̃1�2WÚ”¶Cvt’<&¡&AÆ}ž×1‹×û»º/»ò2q â?1Ÿ¬Ï0‹‰[„{1#@…‚¼¶¸åµí=Ÿ�¡ ØŒ£Éz~=FN;…~ÎA~iw˜Td‡éldL'?*F¨� T|[Dju¦OPBò×xªþ …@ø7ÕDÁÑ?岫D:ú *�K¦€�K.Mx�ScðT4JBQT|½U­ñÐÅ »Æ€Üðh�5(ô­Ö¡Ö5XƒÃ�­ ƒ‰mkg{åj"à 5HÐÐmºÕ�åÌyöåCLyoMþ* ;mYLcFÆIË1D6EÆŸ¤ËíWˆ®–à —À)Œ‰G®1º!?*î<¿’ÅÏ¢]h�ß|…ÞböW]M·Wö3YŸÍÕëí!ZÆ ¥[W¨ �[á±Å&zªmù =jZIÅÂñ^I5Âö•€q„…£ :åt‹–šŠÐ©ÓõxìÎû1ùúÑÒdAf JîÇv‘±J TK(�N@«ïÞ~L›‡ Kסnå&Ԯ؈ãs—bÙ³¯bb·>¨ tr ÌNÔQפžjg—;Ôp}Utâ¥6ño"€�¶L|í)pçe�ÎÎcÓ…£Oa)^ž0Kž ëßxg/Âùpnù:œcÚúÞ4Ì~ìYÈ(÷7yQ&ѤÒQl‚„ްzŒžv‡²MÍ2sM+XÍZã¡kí,�³2åß±}xÓÔ à¿­k-ÍÇÿ_L-vÒeö1ÓŒO0»¬>ý*?P!€µTéij#ÜyíÐuò{òÑ ŒšS‹�O¬@nv¥LA¡È"pÍòÐáæGÐï�¹¸õÝC>«Ãg^Dï{g!ßÙVñë#aSkÅ;-]aÌ6CIT<–cæƒOâØì%hX½E�Êö/CF.“Atfá*,}ú% æ¾"nTÛ•�“=e±Mµm’9¹cŠÕçÏÜ6ÞâU4ñ´ â¿%£Lš%Tƒ»߈=ÓæÂ£@„�Qì« ‘°ù²­—ðÁùåë±øÙW0´E;ôrå¡E³,éRshô2FãˆKE‹Î·ać”`yƒ¬åôgöy|c’20�À™ø&8¾�ÒuA#p®:Æûé,ƒõ¿Þ?Gù¬ŽPÕ40=YºN8-âWG¹nÒWaÇeQ´µÓ%•]qyŒšv’jFFQ4J܆µ´qÒ”ûVÖ�œS‡ë_Ú‚ÒÖ7ÀžÀ6ÒÁ"SÃY‡ÕZµ†Ã6cÔ¬Ä_VÕäÿ¾ìo¥d®öáGÓe¡Ìäº�]Û©h.R]x3“I,>l'•e‡¢ÿãË ŽK>‡îÊÙuè5å]ä¤:aå35E4F^QWtŸø6n~sFέÇ(åÙßúÆntt7ò˜®Î A�Ì%lJäÏ5Ñ6-óÿì]˜W“Ý_“@‚Û 3øÀãöÆÝ]apwwÜ5JâA"BCâBBÜ!„†©=]{?j{oîß‘µ×ßW_¿×¯Ÿ2ÜÓ§êÔ)š×k½ñØ&úåÕ7XÃ8/Ü|' KL§î!‘TÚ)„J:Wö ‹ÿ¶[§ði=ü"Ü ¡îí׆²Åhâocá¼xE-À�–Éé°‹ï~,à!a`5ò¸°¹ÿ#rÓëç-¡ÑÙùÔÝ…œw_43zrý'ÚË›òËÆÒ¬m?bá•úƒ�ÜÇâÌL€¤Ãæ>JqÂXå¦F%¾�µè�ô¯^ÃhoÃ.ÌPŒ©Ñ*¹XNÍÖùæï­¾!ÕCÄ@‡½õþ-ɳ�V›\£Öw¨·TÆzµ¡�Xà¨Ð©Ù(¥öRÏáû³öÊãsvŸ§¡·ï¤ä$Ô ŸEc(µ�v«ßð¡°º Š|¯«ö·ÿ“2ýê7ý ]ú®ÈšõC�ÖQȑϫº %@Åèªß”rŠGÒÈ;_¦/�g&3{/ÿ†4 LeÜ#ÐàåÏÓ„�ŸÐ¬�çÔß™üÎ0 »ëUô@õ¡¼àx* ‰¦Ômðû6æ”YÒ\S‘&~eÍ£têµ·ÀNl©2ÙÛŽ;‹Ž<ý=1g)�Ï.¦±E4:5ŸÆe—\î“üqnsŸeCÿWü›º77Ð4Gl·�RÏPR å½ûÓO ÿœ*c€ÐÂX«A˜˜�vÞ—/ì¡[Gާ¡,“æf8,ÜQmhøm/q�bžÎ9"°¥Õ8ý´û ÀUi´w…¬ÔÀà²ï?®{Ó*œÿ £¹3¢^ÃoQ÷¸p²Òn÷ù9,ÈVZçmF�æ~3î oH�‡.¦Ä�¤+}Fx̲å)ûµ×Ž�„Qgš6ÃzÑøÞ°@!õšÙêª{³ ¨óãåÚÌí¿0Ø&‡gPl³V,¿Ep='Ó§ã©x¯–ð¹'3{û?´¡¯å(ÍF£øüögÀ2+,¶ °I“ÉNïEWmK9Í¿'‚Á†ÙÊž š¶õ•?ÿ~_þÍ9æ*�±Î�úÜOÔmÌ­”Ò¶•ÞÍ’~Jƒœ9B� E�éåûȼM:¸|ª�¤Ë*±¯tžÊw-†³‡6Î\LåE=PÇ)¤�1©42-ïÒjogyzg¹W2÷ö?½—æz,>÷hÎs�CE¯ø$úf×+¿Zü×YŽ^›á�ã¿Hï (�Úÿ­™2“Š:úSÄÉh Šñ‹¡é›>%^\•0`!ß–Z†0a?s÷\ ~ó6"¥ÛœúœÖ0œc/58P«z‹•Î0øÁ€Õ‘%ŠÀ±}ÿ…¨Àÿ?,hãy?P*!»f—„²±·Ò¼�g¨ËÀ¹l°ÉNÏH¯%xµØòÄCWÿ�~“Ú²M¥ÕCÓeø2¤Ä.\>̰áý|©S ó‘¦P^q>úö”›Ý‡bÛú3Ѐ1r µœ xÏC1ž-VuüËßÚþŸ0º¬Q¯%€f�«Výãøw¾d½ i³¤fm(Eý~s7Ò”g�2°Ìä` Q¿mê4p`8øþŒ�i ØNNá0JjÙŽJÃ,;v²H†W_¿ø$Ú~Û=\[0á0ŒX*UPUâÇ]¯Ó‹+ï¢ YÅÔ50‚òÛûQ‰_È…´¦­væ¶èÐÞ½¢¹·ÿÉʳ)šãÖâŽ`ÅYqH8½ýÄfR@ãÀ`>Ð0��:ûÁ¹§^›n5ž‹«Á7ªÏƒþ‡¬â4ë…¼˜ª+xQl1è ^ R ‡í_Îß@žÞXhy±µê7ßã;÷ ¨Yç/ŠÑ¸^DX@C–0Âbyˆºÿ‚ z#¾˜T(&HÉhºœ¾ùVÑõ‚÷YpƒfWFD7ó:ûäºõ%‡ßyǖ³&vâý¶Áá\d4ð…¦ÐlÔ©”�f«OúmìbI¹ñqµHJƒèŒg¾¢Ò¾å”‘Ä©=K’Í õ]Ì÷<šáÓq/Þ?ú¯œ¹Ú?à¢�6³�ú§`úÊJÀH”£À2ûN]C“ÿŒ‹ú\“Ù#Œ†œæ ðØcXâ Å›)7rf¯Ö,dÉhÝŽeû%éŽ1“XÂÌÿ.ø0«fã l�#¥Ö',–z†ÄPQ‡ÀÊŒæÞÇãë5½­¨­¿»~ãÞþG¦Ï"o[‹&‚ÇT4gZÿU~ �‹„*:ØØHO©ÉslL‰έ£& »Ú“Ùƒ`Ý&Ôwæƒè½¹`‰J§ à,{]I åjŸxAÌØòegõ¥hHP¹¥v½‹+�Öq¾ó9¶¼Qo‡6Ÿ!ÂÿÅX…ûZç ¤ô]ŒX½û #™ñøaJ KÐS,�‰}ùž¥ËlB«~„ךZ³ö1Kº޾›±÷½fLÄ"a2J8`ÿ " ` ‘˜ñüqXþ¥†§¡fÑØ\ø�+Ûìû÷ëƒÿ× ZªÕ¸�ÿÓÐ'ó dÆè´çÚ‰ �úé�1�ô®±¨T«PöÈ^˜£°+Æ>ò>u0—|B(µ˜¬¶>Ø·§RÿP)ò¶›ï¢3ûß•4Bg3z*Mg74ÀÑŸ‡8�tÝ£&BVÝžòÐÝ3,¦¢´Cð[�Û‡¹W6÷ö?g“ZÅÃXÌQžeX�›ÑŽ» ËÂTtæò¡Š„Ìj5û1Äq(ÕFdäBÝÃ~®»DxÓ¸µTºHਾ[ŠM-0¬0JŒçyK|ÀòfᜰÒgª'Æ*êsÚ ¿ËQD¢ Ó?àüJ§Ë {MµÒgªG±ç vO!µ×sþc–ÍiW£Æ)¿òzq¼ïPW©´Äh(D_z‹Ô÷Z¤7xÒB ÛñÅrŒØu`¯Þ]g©ÏìÇ(=:—í~8�V¯3€ï·�Õ8˜/ô¿¥ 4c( 4[SÏ㇌Þ—óÐiŸŠÚI4dÊÝÆ¯¦ÉO§žÔ»H\ÛáPb*Gḭ̈{öSZbЇkE¢jšÍm߉ºX@“]@ïmÜÌ*1³lÙvßIPi{\{¾„>Û²ƒ†%¤³ŸZWõ Ž>žÓ¼Ý=¥Þ�ÿ£êqîÍÍj²¬Y2ð+³fϰëòÚò¹Ü¨i(èë·uÅ™ÜÖ{mÌ=8zê�ÞÙø u qq­�Í,+.@ïNb+ 3BCå•;QpèMà:QM‚dÖªÛœÇïÓÿW~·Îˆ£H1ˆ4ð¤+·€Q\R‹¾€ÞŒg¿¥¼¢áœã~„%(°Òf�u¤£ß÷?¢_Ä{_Xžpóvü¬^GÀÄNX„ºBgf·H�«(ÓDX`/¸jq�ƒúÄØÞ¤ü’Q”èI`S¬þËïàGe®(kõºÀ›ja@[­ÿqŽP•ýŒ&#Â_Ãïyµ'VF6nEEeãhêS_§P™ÁH k6b®0þfì¾Âf„Ñ >Ó}ÜjJ†ÏYç@u g°éMƒã’éÙe·²w™¹óß™‘èáp¾sðgz=8£’h§GHô‰,϶;!‡þïŸíäÞÜ£œÃë5˜^§Þ)•â¡x,´ï=õÑG,Gå€Ûβg½c@é�áüÓ¨Û¼½áiú`ÓVZ4t$ûá œ­ö‹ûΠy»Ïaqµ)‡d¡ç�N|¹òG:í–¨$.ƒ²Úu°“)ÑÌ›ŠºO¤dÿ^¼”á¢6£~…Õ¼€>Í@iv—á‹iñÞ³¶®ëö2ŪFß±“R"2l€bÖ„×¶Æfgÿ{ �] ·æõøÇÒ¬Q "}E$"7uŽ8 dÁUWõª–UA“P4/Eê/Ù7‚{q0R^ÔëÚ/‹Ž= Ð)õ¯S÷à ÅøØÊTCÚ,+¤z�C°ü¿huêGÀ59̱ϔûhÚ³?ðw©¸^‹ÑSfòØ”§¿¥nƒæS^P,•BmÖQÁušqYüw‹üÍjlFGÑXÐ5‚»”u£¾®xê™XYÒ)äçL϶sûÅ¢‰Ù½¹·ÿ>Vã‹âö™ËÖÂ�Tʽ�WpúL@Æ8Øk †o‹*M ¬ä|pŽ¿|�FdåQ: $Á”ëÄvíVº$ ct»ß·Û×0ñÎ2%X�°Òjsvž†Bé^JŠ¡äNaÕ¼=• _“23Ó{PzeÔ˜ l¦ÿ—ß-ßr °úuB¬!XžÞ˜®yÀz]~ýå*Ô{ª4Þ%ê1®ÔM¸Y•ÔkWXi3DÀòJ[ç�ÔzuøûM~üˆ8@-švÖ€+à‚s…í,`¤Òiü¸<6íÉϨ¨ËÊ Š²¬ì¹†“ìÕÊr뾜ҦÝë°¸éì_ý¦ÿ¶Å*#µý¡6ƒ\½6ܺÂöÿ <Ç.Áa.ÜG4òîW,o2a2œ“ßcŽ(ËlÌFÄh„#*éSN™¾Ìf`vDù�ßw…õÿÊ�#èÛíût梫ʹ˜~žþ˜s ÍpŽh´µÖ¾¡1TâB½Ãã.¶õ?TÔÆÏ۽⹷ÿ.çÛ`a» 5‡S\xGdùt¢¯wìP°‡A®¬õ j5í\‡ôÚçôöã›)ÒR¯9õ„b>3eCaõ0©‹†‹)©³íŠŸƒ{QD­&J5nzüópž üÒQÔ{âí´à¥“´dï*(ÊlE©ò¬^›­~7TçÅV�ÍCèû9ŎΨ­t³Šì9Ï‹÷r4™¸¹Ä:¦îÏ~þtŸ„T�[ñ(1ÆE5ä­µzíÙxß3V %«=:Ü÷’ê^WÌLÔT"…`³N©ÝˆÅ�Üf€6D“ŸøŒ P#JB£b € ŠœZCÓâ ¸H<ÓÊ ?,úªÊ u°¿ê™º›¦HkŽÇí/Á:+ m¦XkvLT�yè] ¸*òi’f›2 ²ç±x�že£(¾E;ÊÛí 6S†Ô™%8X1x$eY³¦,CT­—FÂ�ÅH­Çð>Žlµ¤7hZAdÐì]Ò1øÇœæí—•uù“�Ü›{“«ó£–}‹µ�‡!6,ZA—ñl[ÝÆT—Ñë-ržˆŒ‚õ|¶¯) å~UXçÛ(è“ ¼±6u¹Œ¦bÒ%¡¥.c¿Âç”À@ús„ Ђ}—hé«x.«µÎ¡7æ6r5kËsqð¾ؼ…¨£~·øÝÞØU„¡®âÂüœ)�¾£Æè&¡‹8ä±éÏýHi™}1–¡ÅÁúC.Z‚e[ó0þMÎY êjÒš,|Wå6¦2W›qcwÐZ ìÔs%õ&�¢ê¼)O}Iy¨á¤yûQ:lsòÚûrÓbj«¶ŸB± ÇÇ€?¥ïëÆš°X¿G7¿¿_7b€ÂkÖû²f0ô �}eÄ# ø©J rØ¿÷Ë:ëa€b7ð@Û™¬”®øÞ, €MŽwGêG—Ï£sßw– ¿kƒÚQàpÜ,¥fuÜn¸J�LÌ‚j©@ÿÐóÍÚÌoٱş¾Ð¸7w­F9œ¶¤¯áˆ‘¹…<{Fc0ºÒLrÕzF;æØÈ)¯­ÕwÎx—–ajaг^œaÇÏWÜ p ƒ©��Lú+Ru )œK )†#ƒÉ¤)Rê!èß²h¥%u&Õ‡r¿Õwˆ`6K'qœ=ÛºŒZ�†Ñ Jn-zy?ÞÛ€bú>R±¸…²º¦ú7�·†­áßäY¤Ù.p J±�‹Ÿf¯3ÅÊD�fa5²è »³Ž‹Çš Å’E½Þø ŸRvÞ ¤’ ÂjåmfËl×áXxý†!oò‡+Ín¬ýO¤ËÒ`S´´ØõhqpØ�Ä&^gÓš·¾œeX $Ûùeãiܺ�ÙqY};s!‘;Û‡Ÿ;ìöÝ”•K¹�‚yøj3ܬYäãKÛn½K7ÏvBúq'ј‹°è67òÄ$N8�¦è¥½SŸð8êAym:~™×¢ÃX¸C»k7îíOe5þÖäH,˜\<ç¿=ƒ©‚ŠÅèé.ý{=uö¹`t°1ƒߦým@*£OôD#&×nòB¢ %¥عG ‡¢vjH2 [±•½Ó$mrå*Þ&…u—í¶j„¬€½ËK4 ×(® !Ýu)œïUOMKİš‹,níOyG¥èTˆ(ÀÖhºb¿u\ÒWÓQ'IM)£pÌ«WÑ–úí”Õƒƒ×¶•AtøŠ­`]ü\I…)†&`³ÐÞÈÊ!@$À+¯ÃŸQÒr4ñ±át<šb[wâÏá3Œã>‚¿›Ñþ7Üø‡-XHëÖ©VsÆ%¿>8=çëQ%—¸‰�„”j Üš‹1|ÚæïC±7®ªºöÒ°)¿ƒ�ù±‹sf\å šœ6+B�¦;Rh�ÌœO0üÏÌRÎìÅP·ÑYŠ„vžé3Ø•ÄÁáT�–Gý£“¨wDüé,¯v/´êÔü�_aÜ›{yíÝHC�ŒnЈçnôˆŽ££{ö;Ôhìu=4°01†AÁ&ƶ5³»õ†·Us®sÄ!ý”ŸÕ‹Æ¬9@ƒÁ@ºšK)QÙ”“Ó�†/ÛÌM‹ªñ‘æJíBÊê†Sƒx®ME£hÀœGiþ>Nr0{7BÀBÀÇV³ BàÈâ«+ùPÃÀ$ÔÜÌ^”ŠT€žG'4õª«]ï-tëç…Ôªó×?`Ls­Øz«°àÿÜÝs¹8(Œâ­¹ÿž-˜qD¢FS Ë�©[Ž,l©1¹Í{ûcÂåØx°¢\¸ E¢>i 8…Ö+4ŠÖÏ]Lç½ï s8°ûm¹ˆÓX��Ý84Œ*+›Å½hb1z:©²¨cÐǹ-Ú�/nà®Ý¸·?…Õ4F|ˆ:Í%–Cõø’UT©ØŒ ø˜°�4 =§7†êì¦ÒjêÜð u ‹Ø4ä^�XÌ é7ëaö[€ÑÐS6}N#ÑãRk˜Eès‘+|™ù€ðº}ú%B-Ìv–3í‰OèŽ[ÖÒ”¾ýqÕë‡æÏ–GQ¯x¿ÛýHå�ÁïG€F4dås¤j%üú+÷ók*%šXËÈçásE¤€˜¼ñÒV)Ö*¡J”À`ƒ©�)>!0òœÇfš6§»á&/ž"ã•ô Øùˆ‚MIHýG¥‡­ÚF¹É¥”Ô¢­5 ‡%çp^8�×N̉iû;[ÎÔ »©ö (áŽeyûTZWNiecOJëŽTØw&j^ÇU]Fc+8&!€£¹0X•`Œsfû@*ö ¦’€PôùC¹Mk§Ìâ~3083 ç4˜„sÊÍñ=fyõÎ;ÖÐøÜ’ÊQÙ…Çž÷S^«ŽO¶ö­õg¬5îÍ 6%XÌNXF”V$i©ow¾"€¢ öãp# Tìª4y?3Rç_|ë0=¾`š:[[LƒRPGHv¥ÓćޤyWD—¬B?nK—¸ˆäJߦH³)³kP“>gÞÿM;‹ !PèSQæz ©®c1j9 5¤Xt–ÏÛ~Üz>ƒËªý¼WòjŽ€�ÔŽ8Hj(°´æ[*è5ö1¨MÕ÷PQo¤¸€8‰Ahóð½” €¢�RÀ^€DM¡†ÐUió%$¹æ¥ §ŒØ|Šm‚ï  g«þ†�O@~ü0âwEŒ¿Ç6HÏ=vwVý•–Ò,À y3ƒ@A`ué? Þn?ñ÷—¢¿=-¨Ì5¥¦¥$Ð6ãÍí'Ùç,­­/hRøoª• }¶aî:¯ÄÆ\˜�ÎdÞ7ޤ›04ÑCÎsPÂë7œ \;mNå¸üÎ4±¸ëå²×Ë%mþèuƽ¹=ÐjXn–Ü–;ñ‘ Z>rU(š�•è‹¿4„•Ãy–�I*­±¤Ó0:\1t%5õ¢tÌIïDÅ�ÏÀ•®¡,u+ÔB3¹ýÁ«^ áwî¡‘H ê[~?òõoÒ´§>(#á×Q¯5ç)±ì*ËëCýSÒ¨_jš5å2ìä¹Ç'-.ŸeÒê|Ž×|ðº|_ºÿµz‘(Â^:Cƒ�LO§è6‰zTÿkÐtÖ&ð”EU“0$•¤ÙÛ(¥œÔ–D1g¦9»ÎA0ñ$å&–X‹?÷�áT‚�|íÿ�¦û__ý￱FÓ4ªVƒ'Ó<[ÿˆºLe:XTT�†°ìoÁòk¤MyR<Õç’ï$À#¿‡ø é^håÛN ×iåù…ó 3°&®w ¤§¯Ò ‡¹¯Æ™õúkä¹ZêA×îH€ç¾¼ö±ÊIÅÝÎOíÜ“†§æ~T⸨¤]à:ܽ¹Y�® ¿³VÖ¢éî«oК5CØŠì%ô �:ˆi¡Õ†ôóè'4y®2Š ‘ÚêIÙXðGÜ¼Íæv¼€Û Ë«”š7˜"‰'eZCØ�š «k¥ÀêcÄõuäÂ|›‚ž“h¤Ë°™±yâöì͘+�FÌ’Ø4êkõúðUp\£ûL_Ë H˜ŠMêlóg“×Õ˜…ª3ðk03›ºñ#´ð šðà4gÇ/WS6ã´‡ôóðmËÑIn‹©§|VH0›Úq†úM¾‡2BS¸ñ7³õ1ñò˜Í{¡ÕjEýFãeßVËr¬‘Ñ”Š!oq±�‰y1iVê b€›¿± l`«É›E €P,��&zŒ\Aé탨 cG¬yú¸biýìEÌhœ¥ÍøTQEæ ÐÍ7��¡f4 >yv�Î,<=&»èâÈŒ‚“�Û=SâÐî÷_aÜ›{“Y÷³!­=iåÞ£�ª˜Ñ³/]@ŠJS¡ 11­öb¨¿è!ÏsdEï|L_½°›Þ\÷zl~úyúzû^f8ýâ©KZ ÄÕÿBÔmÔ•9<¦n˜] Yr0À4¤&L&o¼‰" øŠÄB_»Û›„ÖA K?ÀÑsÊݘùò5×�]‘H_¢Éë?¤ÎýfQ Þ ¾qlåâjäE—<ƒ…Lœ¥ؘ‹€�˜bÚj7úˆaUüD2- + ß·©É³°Q@c¿ÂE–¦B›cÿ ØËóÕ ñ{ê>x!%¶îÄ �èáI ÍN†V¯µj½úWÍhþY­ö3�V_ˆ¬ËÞt\§JõlMÙm;€ut \ )›ºù»+„]à ê0Ò73ßö½åwqÛJE , ¯�çãG9–ÀCO.\Aç¼g–-ëÇÌòc ¢£TÚ @3rô÷¾Œ¾›5SfŸí™p|Tfaeÿè”÷‹ÛúÏêÜ.èqövoîš5[ÿ5°šóÖ‚‡üûºKI—8Ò_6‡ùqg{¨¨ç¼ §çù½Pߨ8*ÀÂ�‰:MÍàä Z5v"-›8‰–M-§9‹×ÒT0Ëï¬ü¹£”UZH] ±.ÁDŠ{Ð]á´-5†^L‹¥ƒ9É´%9šøw ôºu)ÚêÔ·äÇõS4ŒûaÀÚü]g$Ý…×�µõ*ê9™¤â<È®Çbl³0a ,¶…_“XËc6‡féÕáÅTë«à�T1p!lõ[Q\bqŒæóÄùX-ÌâRÀaküœµ,ÌÜ�lД¢ÑT CÌJ°›O8“®Æ°ì:MšÏBÊ|&«�×gàÚLQ+�V›•œÔn4aã§6é2ÿ4¶t·äÕoÅÇÇ>üåÇh:R.JüC¨dÎOŸ+}4æÂ½–þrp �çØÃ iÖÃÞZ`JËîjKïoÚzixZÞ÷ãrK/�Ë)9ÛÍ7ü™2ŸÐ?¤gʽ¹ShƒÐ‘ýäΕV¿JA'úæ¥WôÚŠnªi�6kfÇC¿Žî@¯£·fhB*e£É.óC’Q�OkÕ†÷ ¤·Æ‚DƒsriìÐI4pöC4íñ�)9­;ƒ† à‘R¯>Ýã ¤ŸºÐùÞÅt¡O‰-ÎáØ×]rèñxu Á¦žÂ4åFÍünãá‘ö-ÒV"‡î<|)ÅC~ pzz¦<ù…RDÙÓbâ 5]ÚÓX¶4›&\°5�ζ™m (a¯Í²AÈ1ó¤“^—×µû…Ék¸Ù•^#ïz…â;Z.ÑM¸†¾¶¼)±èªŠ4[ðÜ3E�+áÀ ƒÞ"FDÂ;¤£ÖR ÉŸGŸA£‡:Ç^×™ f”•Ñ LÆušÖ`5¾Ô}Z�̘¯Õh”bf¢± ³¼YÂ$Ðë;Z›# Êm¤ ß¸<¡¨ìÓÁqé?�J˫잰¿{§ðœßy™qon4î­ÙO©³qMšqÏÊüþCèÒT†Ú‰ÖЩ±g 3c2÷ß°ùæäÂR®Ë s�ÕgPq¯TP¤æçóXL´äÇh~ ÃÈ�@¤bÂ]šxЛy)tN�ŒS|W–KË‚:QvÃF� ã=�&KŠÎÁ€¶ý4wïEš°é“Θfø-ÛRrXMßz”~)¬ XHºŒC—fRw±)Ó@,ZÐ@„KÄMÀö:¶é¥ê\ûk)ë™jªBw¹­îóŒ˜n£VQB KÍRô˜�¿…úÎô¦­ªý«¿Ã¤F^žI�›ß»™ ‰˜ZÙ‚Ýà}õ¯wBª×*ô½®Ú¯6 FÔ¬W'±¡çê¼¶�~ÈFê `Ą̃ S�áPV+ôÓÆP!”gå/œ´}¼·Ôd(Õç•°ûÁUPÿe[ÉåÑ‚§kBÁÈ2ÿÁñ©ôÉæíNVþæB¿ž¢r²¤‘ÛÂPŒ*4çÛv–ƒÐ^Ô^ûàÉçŽ Oz³¼¸×¥Ù%}��‹Ï[Ó'0¦Úï¹Ö¸77ØtCŠâG h¬ÈD7ögÏïtT� Hh®Íz°hìÇÌdìõ¢“¯½Ec2ó�[o iKI>ÁÔ}ì-4vÍëTÜc%tа@‡S.ÊOÌ Íõùš÷ Ó背ĹퟕfÒ„v­¸Ž‚ˆ N¢î“璉äÎááFÕˆâ;EÒx ƒñ§€…t÷ó}å òfq Æcšƒ�zŽ�0 ÄÝÚæhl‡ýŸÁDê/ ˆli)½?Ga wã�‚ur@,C ž�çÑìzƒvâo-æ¿þý…ßTû:¸L‹ù‚€J‹É$#™‹”V·°(*ðñ§¸–ØuzÂú�ùóÚê.üôϋЛ6ÕsƯ;L‘-;QŒGSVâ}©‡+b“'ù*ßl%ã(y6`šë%.†ZŽY&m>¦+Ø ByÎ/ûßùyFI¯·gô>½´ûð 32»½4)¹¤ýï¹Ö¸7·éæÃ`§ù*©‹É�»Óù7?´ˆY=¦×Vz=‡�C{cJŽ÷væÃ>h�ý‚°€y¡¿Â Þg°¾ÿœxA†âlèÊ­°ÿLñ(ÐG€u„Õ¬‹<5š0›çS¢X~C|ßiµÀŽw¦wV¿‰‚oªI±�=É¥§c›·ãŒ6PàÀ =zk8d–�È�m¶2XLµqÖv·ha> &v°�áûòºòÄßÚD£©ßð/OPäªYïXÏT˜¬¿ÁKƒò Î�Ì+¤>qI”Q@Zb)�X½�½é‰©¨Ö/£÷ÕØŽ!Åù#eBÉÓõ°šxì üùïK€¦ªüWŸ^Ó{³‡š<¿Š2l‡ï OÅ=c§¿º¬Ûˆ“w �N·öd^^ß~£âr~'o÷æV¡Ý`©Ð"êÖ¿ÀÌ`³ãε61B³‡Þé¯îË(#ã1;<Ëû®_°”Ugœêk °Áð°Û~"‘ëVÐl(ÎúL[C¹ƒùÊM�œ>ÚÊ‹ŽuË»zpéÍ{-~éYHk#‚(¡&ÆT»i;kž”h豉iÕ‰Š1Ýr.O •4šî9h97ŒrhRcµàksgŠ%IÍFØŽÝŠF¥ÞÔÂ,2j±ÄÑäÏ$.Â’ôÅ\©Ôt Âç�HâKÊH ñ’ÙrV»Ž§‚nªµ o>"q®ÖÍŸ/ƒÕœCÚ̺@¨ÈvÅ]<÷æS#ËШ¼ʋͣQ«÷°-�(Íl<ÚHq°)Òxd@ç±·Q¤7ÿ²  ³o0=cÍJ‡ºLÕ3ƒ³ã³�™Ø‹xØŸS‰Ç»Ò€ð¹U÷¼³¢ûÈŸî9“Ö Ÿùó˜ø¼{JÛºSiîíwI¡e¢žñ-:ß/'£à^Œ+¼ÏŸßeË,M™v²� å‡�‰Ù˜�¤Õcg`|8©¨3E[Ó®FžTÜo&Œ.ϩڄ½? ók¢0 8“2ƒ bz*!‚Aâ÷Œ= hrûÖ vdNÝÆP¢¥p�|ú“ŸÛúi”Û3…ôÏ 4ö¢ÍÛQ©7u[‹ÅZ©¤æ8l‚Û{ X¸ ˆé5U`“IKo öþs×s:­8râð(Êóü0¾�÷Øÿ¸Ð¹®.þþ@ªó4€ÆªÓT$xµ:6íŽm'FÝú"õè9‘útNC—ïÐÕ{)ʳ-‹JJByøÙÄÜb5üìÓ«�©˜Uiæt™±/Ç~[Ôš`“×6†ý è?ûâó ]ÞkTå¬â~³š·ßUв£»ÁÓ½ý>’gÌ€9‘ܼ%%#Õ16²_,ìd®Ï8Õdh¬ê9v‰´ª/MM¦�‰nýo6äÔÓjF–c¯÷ȱ/ XÈõöá‘‘³Áà°™[¾•Ô”\Õcqú…Â=ÛQ˜eÖˆôÖ;ùi•™N5]¹Ö·„ÞÊO¥ÒÆ�ÐãeS`Mâc þœj�E˜(Å4 23›0@^S=µ‚¿€…™¤ÉÜ$e&,AÞßÞ‡3}ÇYýÈ4â�·¨ß’Í”Ñy¥æ¦ØÀxŠô¢¤ä2ÜN 8ØÖ$%uÁcC(ƒÖÒp<ÖAe®( ³ �¨?x]õIáµë¿�Ô¬EJB¯ó݆/91oÏ…‹³vž£ièSwßë0!ýF&mrH㦳�ñÉw´ý¨Ó ÁP½†,™/„Ê-Þj›—ßf.¦ë)0#88Ë�ÍÊ1 M8 1yÜ‘±H˜çëèàuäëo‡åÊmÓ±¢ãÒ=[ŸãÕ~Üo]kÜ››Õ4DÃÝ{H)\Øp=äÃg¶ (8‰8„ÅÈm�­H^Û\Ú{¾„:R\Ã&ì*l�[NA-`ööŸ[°/>7| —ôÄ@þ<­C[:+ý4¿kœï«··$GQb�º¨Ù´¢’ÁóiѾ Òí�ö¡x^e¿Lzf”—™^· [ÝGæäHŽÚkÆ›ê˜Ü–fN+l¬€k`“{�F®ÙOÝË¢„䮆ïrC5ô+ÝH ‡ÜÕ ™uÑ€›fÃ.üþ¡�‰7D4BCfck¢'õŒOüµ™ÐGsÞR�Ap1->ïûéÏÿt‰kH›[Žâbâ{š·ç">�Q`kœ]`k�U`¬~› kP*ÔŠ‰`ï)Í[Q:Œ6W Mß<| V0J3£dYc:‘€8îä"­=O{Žö~öÉ·*M~qõœ�lNu ‰Ä^§ïÓ›´¾§Ì'ÄmÌéÞ~ØdÃäð˜ê¼§IiôËëo뢳jÌ. ÐY‰q®�.Ð@Íö~Ϭ¸Íêâçþ™àu©ó˜[ØP“X)v³Z Þg¼èaA£ÛCýé"ƒÃ'{Òßöp&ÀP3/oš´î}1íä¾1ç”°± )ðkÍŸËd¼´ˆ ì}5x=c“¦’UKMG€O>Óì—N£è *DM,Ü«±Q)‚¥¡'EÂ�!¾fMJ®]›RëÔ¡²&�idk¸0×­CYõë[6?|;·!8‰àXÞs!ÕkàµjP4œ±1p­2«u{ô¶xWº4>Q~ïKÇa)Ti}Ïr{NÝü=I•éJ;‘]shÀ©X;fç ¡8˜xàÀ¢S—ÐújÛîßâœlXèŸoî£yŸÃA  �“#�Æ–´°ƒOåÁ­/<ŸÞ¬õ…>~—sÛt8™Þ´õ†ÒvAµËZãÞÜ`³2Æ£ÉùDÏæ,�Ûg U:öËhJ1é¬Åœš�� äžXºŠ\ªYý-•=ÑÛ²P·|�ñiŠô ã«é8È�e0üñQqå5¨O žm(Ñ•N wóˆÍzÆd!³H@Â2¶Zoð”ÉžvûI�Éë ³)4÷ù0H_Âÿ-õ�¿‘’‘s5oOaõ›p„[¾d�ygã» jÙœ¶BB¾3=Ž~èšG¿ô*DÑ)ÔÄ ÐãÛ…t Àg“¢¸6ñ`ô=B°ÐI‚̳iN�·èè‚]g.sú  °úk=ñǭد€ÆB›A)GŒ@ˆlÒ’ÇúàÚÒÖ›ï„ú숖fÒÃDŽŠ2‡†N[ÁßÀn €eê­‘×ÒÎ3ƒ™Ü—é·ï~:×Çÿƒ¼v¾ç‹|.¤5iµ+¿eG¿ß²Ö¸7·äù(».XMm è5Ø~ç�™èÞhÚ¬1µ¦ß®‚-�ÆŽ€¯ŒÙåÞÓîÃbYI¼˜ ØÐàÛvm‚ÕLlßæOqx&)’Òš¶¤˜¶þð!ܤ¸Ï“@m^g69³ê»Ñ‡š±Z 95Öb7é@QÁ‹±¨Ñ¤·Ù×m†#T o·Èö!`„iDœ “)hܘÊ;¶cp9 09Ó«˜ôtb)¢DÓî[@´;#žº7m °©Fˆ ªWfu 8½tË—€ð|œ)è�šµí¸Ö/ÄÁÀ©‹)�í5°òç~¤ì´”ÒÒ›½Ï:„ÁÀu �Åü#Y��@Ä‘�˜†ùÛ…–¹ðot0ï ¡³­§íò‘/¿”žûR¡OÀÏ}"â+Àrgyzg_ëZãÞܬ&’ÓÏÐlWi)ÑŠÑ,ùñ³Û»ø%Ì€#Ï1‘6"�–½€Ú¥·?¢›GO 0¸4#¥ƒ¨…g`q­TVÿ*]²‡Shpt®ÏçÞðG¦ÐÔBj�ÏJ³¨k‹Vh, B¿Ï½Xø.Ñ2½p- $-g¦bh&µ a7cÒ ý6 ±©Ó&®?L=¦ÞK.Ë;®v™0™œ¬à£½Y ôäÝì¸Ð×üH”Ú€Gì¿-Ë¡‰�‹ÿàT¦†W¢öv…‰Í8LX÷•÷LÒfº¨²ÈÞ•¼\& ¾VA¦ßG ͽ)¦­™mÚó˜ç϶¾¤�‰¹þâxžSíÆÑ2F½æ"LȘ3B?nþÿŒÿ{î˜5ïÁ Ï6?”øWf6oûYNóö½¯y±qoîñÏ›cPæTZ’çAÉét æƒfû½¾¢«ej1¶Ûúy}|4âô�w¨ ƒÏ‹�Ñ&äÌ iÒ=`6²«TP÷Éw±•LR­Zôv~êŸÅjdaEÜLIB)18�æl;¦Šø"O3©¹€õجmæÚÆ8Ûë3‹¤aSZï�mœ€4—â¼O~B}ç­£4Ÿ†B�‰ß�©'ì ú Þqü�œÀÅ6†óû³ùé1¤ß&¶kM�=W†C.ÞkúZ–«ã÷à�på[~àÚ›¸hŽ`kº e„7È*ÑÏJ M¦"¤ÎúF'&}ÒBÌ^~çc(8��C÷¾±ãßxä\ ht0‘0œ£e%´ãÚsµŒEå‹÷?úvvóvßw ªÌ÷öý"�•׺ָ77Ø,A¿Ê/¨×°Ëó}ÓfÑå÷4Wç*÷ÖpÈm‡çÝ£5 á†ÎƒïQYˆ‹ÂkÖa5Z#ïÜ«€F�ÅûÎSŒoF4âÁñîù>Øàö»i”×ʇâ1Œkð’M,»Ui.Ûà1)ÌÛ&wÚα§âÄxS޼–^Ãa‘®SðÈot…Í +”FY³+ùvŸ¥.çR>ä»�ø�³&©lÚêÐuï (¦î~7Ý,S—YëYÃ1@ÃÎf éluŒçÛl~ápŸ�¸c]üÃ(Ï»ÓÙøM6^õ"ãÞÜ[`íºØì�KòÅ$ l DÛsßÃΠ‚Ðs8ÇÑÀÓqÚ'˜Í»ŒÆÌWÃ0›)�¢,ü¥™q6ún\­}¹‘stÛVÐè�ºêíyÄpFJ,ž¥à¯/š’“P€!j¶Yâm& "ßß&}ÖF Ð>eÃaÊ*Œ ÖŒ™F¬+iìDŸ–dÑ 4 ãÈtX“ÑíÔ1+Uf) z½Ëï·Ä \¤Ö$M¦âÄÀâ Åì¬QÄøߢL¿(€LYÎɸ Z3¹œ.¿ý±³g™Ù€S$�±˜åѺ/š©ÿÆÄ\†ç¼¯ƒ’t$Lé´O¶î<6§¨ï±Ù�ûÑ øô3)-žëÜ>¸ÞÕ®5îÍÍj: …öz.§A¡ƒz š&wÙûbHL5g0‘çýÕìñë½6§^‡ˆøÌ\€Æ4yãk1i1 ÝݧÜCA#Kñ´62ø*ê.¿¼’•Hý[µ¢hŠqÈgÇ^j)*Í&u’1Ò¶AiªÆ#’iiî´�ožŽùüEC±ªŒe˘Ñ×›"x8ÜïÇêJ9p_ pTcìç¥Ù”ïáÁì&>*‡•„2&@³Äác2–ڦʳÉÌçaÆP¯Q+)×7œ›7-‰uÿPŒßc[èÀPõ:ÍÕÏŸy×¹®ã,0€†¤ÇŒ¡×ë“-/�[P<àèâÃhBNçŠÂöûºø„Խڵƽ¹Á&…öÏQ¯¹l¥Ð$¤ÐEá@ ù:p8J£õЇµ~ÇçŸDïORS/žC�`°™ºñ#kqy0†ieeñ@|Ê^ÉNüCÁ„Ã\×€ä7ŸÆ‚]‡ÄÑðÛ^"±•[Vª1PJñ[%•&rùl/ø[ç‹�¿.„ y›Ò³ú\)þGaôˆ6-9Íg`+r¼ê€lO«YÁSdc1Z¨çŒjÛ’\V@8üîWlê:õ»( »Ó4Ânp*KSñ½;º(©ykÊ@�&& wŽ›BÌjœS`ú#Èžg¨9ØÒ˜ÜÅ~Iƒi÷Õùöãάç³çv]^Õkô/w ™LóÊ\—õäÏW»Ö¸77Ø ù Xõ_NACç”’®¨×|¢§µtÛg¯4=tp9ü…hfGéOY¸S߃û3VH9dŪτæ)°)±š!yŽ­Q“'qþ¹¨ÅÕ» ÜâOEÁ±TÒ·œ%Æ"àÀ}»õŒÈšåª]oø´ƒ’ôìÈí9˜–ÙgÞŠhíO‘e�´Y�fMèј08_ÛêXΩ3ðÂfþ¥�¼Æ“‰�þ›0ÈÕ»O½�'•FSìOi®Ä¢ÄS¶:/�a/¶Ô6-Gg–;÷rÅÐÑ]¯ÉBîX1 C3§À 5§&Pƒ·™þ~F€.Ì1‹ôZ�À~~õ­ÊÕC¦ü¼zØTº}ÐäËå9=>êÞÉyÕ‹�{s‹\uëÿfCi›õ —S%ã,jÀEÜ9ô×ÕGœBÍ&ÌF� &dzÑŒg¾²Ï­ºH|D&×$º4kÊ ‡×ê€/Ìs|Û±ÝÍŒŽÞ\ÛØ—™ÀÆ›Ÿ5íØ�ÜdêžL…%#hÁÎS´Ô^ÈXòbió4³{“!˜DD ¯¡@Fö3�6+ì3�\�[ðH儚µ¹ßȹ.ãÀdœê5" py}V¾¥Ô­ÇÖ6‰1ù4|þmVíöÇ¿‘‡rL�Ù>¸=xÙJéÁµš‚ŽþÌjÖL™©X�Š³ÍŒ3pèå�J“÷v´�á½pLÎæÔ·.úùlãsÏÐé§WõK·œtyE·‘Gffv«ò"ãÞÜ›ß?oø;Àf{\ãfRpÕ—†TÃË®»@ÐCo#a4†ºŒÑ Z÷S;5Zú$0Þ™Óh¡-iìÚ6kþy;~¦HßH ƒs@o¯ftªgÑÕ*Éø9O'DÒ]á�4)°,Ø®$a^M ,YRQçH�=K74%ÞÀ²jÔ"þU ŠGŒ §¢ü4ã‰#´LI•ÅÀ6o{Mò‹ïgP�ÙͤM_PFî@ŠhÚš¢69°ey:1’~îY¨�ƒ&Q6¿šô™ˆŠ8´šÈì[7 …jJ�)Ò?†æí:Ë@só~Ä„œER�R*4[ú�c.Øm4ûæù…SY°‹àZL}#âèíuOüÈÒa†ÇtřӜÈ8Lú4‡sFŽKZM'SzÍ8"wç Éné7îòê¡SiUÏÑß.)”|5k�{s³š¦0ßÜÉs,jxôî7/îk“ÒLX‡c�E¯Ñè£BèçÑÙ7> þqI 4¨5QHýf4hé³WðüšO(}#1ŒAiÍéü5H–¹é°K®UÓàÿ“(¢ÏƒßYg8:§p²êÕ£<ø}e„ò6 Ù�Ú1{Á‚ù«é¥s¸o¤·‘i4dñ&MŽ,㟵ž±cAØ›29ì³k0~zÌoPBd6¹`Ë Ðҋݨ¯¡_æªAšCØŒØh1¢M ж =ÛRùÓ_2¸,G(Q…J“ÙƉѨȣÇã7ÈO(¤®¡ÑV �E«GM¤óßÿÕÝÜ £ÕWœAEŸ'c�1ë¯iV£I˜ÿ¿ÙßÇÜ@-·å¾1“Á�Ô·‚Õ¬ê7¶ò60›9ù}>›‘Þ5÷jÖ÷æ›È ê5¿LiÑŠÅè_ai±½¶bsc§„¹ÇæCý¹vV£=_K«�ë0M-ëÁ`„üçuè~ÿ‹Œ¤RÆÜû*…5òb¯á­›ÿ>Å–1³ç3™m)1´Àχòá– ÐÉøä`o�ŽÔBìq¤8“Ƶ÷¦.9}!í½„Y7gh,d¹a«3è¶�4ôŽ�4 1âNìWó}»hðí»°ß ™÷v‘Fª‹ì%²Kœ+hÄ­Û)²m…[õ0„é°—a¥Y_'AõɛÍ`#a¬×è5 šïïÃ"�0‹±ÞH )„2ØJ‹÷Úˆl Ñ+0›²;Rg°É|4w»9üÔó¶âäÔÌ{‡eÎÍ›*Æ=�žÍ¢½NcP�~®3¾®|N;ÞÒ4-*L‹» ©œ�×ëó))¥®f­qon°™R£Ö×°¨©´zæôèËW1ZŸŒ¹Y“÷ÎEÇ*ίP3>ÿÀffϾÜÔbÙÕÔ¬�¾Œ¹˜<÷Å4á�CÔÆœ¡ šñ ›y„?®‡¦Èš�Ã�ö%�Øù8¹Vm¤‚ŃѡtR¥Í´pd2æãò˜ÉÀ s–ûÆ÷[Ð�½ìBxÒ¨{_çÔ¡rU�@¢FÃ! >¦éëß§ÜøBÊòîH%HŸåûøQyçîtâ��8Z:ѵ;Bkê2¹íä`èü—ûÚqG)´pl {i´Û�£©Ye4«°wåÔôÎ_MN.x5k�{s×kŒ¨ßè;kþz*f‰<¹üªÔ�C/ 0\UÈsE‰fgO¶´€üç`¥Ü]SÊyJ'Œ8y"f)ºÃÃÍ·¸ÛDJ�+ D¿ââΰ:ÿG;ôf—gšÛ0D¶Ö €sV®àÙWìé„îÒ€ü¦€8u«›(Ô«=åWhà‚�Û1œ¢<šS:ß=Âu"�}8¥ÏSeÎ)4çrÌÆÏ0©J¨¹¹s8œ!ÄûM˜‹ŒØ±€J12Û2e‡¥°³s".¢ ÚûÑc³ÁÙYRR¦Þý˜“ŒÙ9œ‡™9˜Ù:��9ÌJO³M3½ Gìõå‹hRF)¢s帄üÇÄd�­êZãÞÜ`ó€Í¦ˆú OXÿ)3Z¶eçA€c}Æ~ÛAm.ãˆiåú<ŸÁ&ÜJ· â¡8JN¤Â°* ‰ 4¤O‚n¬‰¨Íµ–ó’Ëó)0™½ <Ï¥¨aC*D¬� Çq(ÊM¡ðê7^�àªSä�7Q4lûã ÓPÇ-7dWõ›¬sùvh5Þ[�s„â{ÆàûÇxR|³Ö\KÚžKçM@!A:Ûq`?`sÎ TD0 aºA¾<«(µ¦Ñ÷¼"5-Û8l/Ác£3â )Þ³ÛÒûRÿèzïñͤ;";Ì�1 œåÑò¸³k³Î�lÿwÌFš÷�²ïI?Úqû›˜GS²ºTŽO*8>:&{yU×÷æ+P`³%¾‰çÉ̶í)3Ø_h½¦ÓUbŸkîÏÚÞ!]¦Mð49èïßeË-wRlýF|uФX×Àn‡[ûPVb1edöâ6áu=Øæÿ϶¨�$µ›öÔµ±ÛèoŒw¡ÖÀÃöüA*]€€à€{pvÂr7äÔ‹:2È„„n õ硃yÉt07™gÁ €Îé:H¿¯¤×BÁîâP§é Eáë9ItÎ.tú»å³Ø¯ÃŽëbÃ8³Òî‡bBéQulòe¸<ïÉŒ§÷ Ó軲\–�«×3ƒ ³ 8 ƒÒXïVlCäjãO“ŸøL³âaÙ·HÄUH“ë0Ô¶: NÓ1€º¢f³qÞRªD*Hmquê½qò@Ó@È™5™k4ÚmÃcºÚÌpÜ)ìïQ‰ÏµuÅj›EÒŠit\Ή‘QY·Vu­qoîz�/â•ä­Îfzû°ßØWÛ÷Œ2õ޽¨oJÈ9:cÑÁDÎçãZT"~zå ºoêLó„Z‡?À]ñ¨UDQω«ÙM`ÔêÝ|eŠt zb®ÝàÚƒSX÷¢–Ó×j¹æžP0lI‰¾ÂJ|ÿy=íH‹Ãû ‹ø´4ËJ³1ƒI®S›>ëœÍÇUpZn«Ó?®¿ò:Q šP‰§íX�WŒäg€Ì48–v´Ä R)ˆ‹Eaºfn'Â;¡f-fU˜¸É²îdu< õ'°.vaß®5ƒÑþœdú©{«îli�Õ˜únø|£°_‹fHÖ"W»`šöÌ7Š¹Øœ±E¥·ÏÖsÃÆ�Q¿Ë Œµj5#’2èÓ-/ê…yY´�¥Îúq³±¦³Ž£BM�åÛŸïlAãü>f¥Ÿÿâí÷Q¿Ðx“A£b³•y[U×÷æ›`¨¹Þ€­"«�u �`Y±6ÜÌTL`0æƒÍ®Ðæ1v&%ïqý5Ûn»›úŧPlžÒ fé²´¤R}ûš÷âOœba5šGsN±­ öuH£ëÇ~ŸóY °59šÓjSÚ·áÅ ~ MrÔz´tÜL(ÚÀz8fá6q¾†âó÷ëˆ_G±£} hÐÀjÕŠ­(.h0x$«ÛQ8îBz.uc Žh£h@ˆÏÇžåÞE�0Î}K·�‰½�Ë“7Ï)ðÀÞÀ^t 77�²e Åàý"‘œõÂOª¯È>«F5¿ª�øÃMúk*ÍèI™Þ¾¬@ëN zô£Ÿq�âà-¦Œ£ôÙ<ÉÓ1Ì Æ©*�›Ù�6v µýâÏ�rÓA{î}˜zFÑÀˆäÊ¡‰Ç†F¤ÍªêZãÞÜ`“°9’Ú²õe«f3¡°J/Q¢i``(:ê âhÄiÿ¬÷޼ςú¦‰Ë†Œ„ÕJCK…Ƶj6®Æ-a?‡f>ñ1-Aa˜»òÕì’ˆV¾8§/Ü6n(&©¨µè±y+/•ò6¸6eM=èûnyZ:éµì$Š©QƒÁ&£^]ú®k.Éãv°ñÃë¼�TÛ1ŒPxÀ6~g�=dxŽÇx¤¦Vto ó‡p ”>(J§#>j÷ÑÍ�׋¢åA�ø7°^{0š.gBN}D¿ïWèE:®˜Ž0u›!NsS§'Eáß6©°ÙÏUõ‰%¶z�4ÁÎÝ[Aƒæ?AYÁ‰Êݹ%õ�¦'æ/uèÑBÎu 8;F;Ôx$tÐp²šq4íˆFfæÂn¥!«ËàöÌ5šh¯vÔ{ì Z¾ó8­ä ‰9[¿£hK™U«O‚ü3æ¢ |Õ%‡ç´`<°x3ÓÉx(°¡q`ûþª¤šÝ%@Ýɶ(?žàâ^‡ ú~0‰4�†·jÎÌ£�gS¶ì_NgrJ�ßqñ*S‡C pÝçÔoðÝ8݃Q¹/qk«©z_ÏᢠJƒŠ/ CÜb‚hᾋWÒfj/s}Ä„“ÁfæÖ£Ô½Ë(ÊíD]P§éV3.=�>Ä̳g™.æ†L´´ûUL¿9¤² Rfƒ±¦v¾áuMÂÓ{²Šï¹•«ÁjhH\Æåþa‰G†&F;.2îͽù]Wíï!µêÞ“èÕâGë 0±q3zhöfÚ¡bfcN“,Ú4*œÆ \D?Íë°Îœ”Æý2žaƒ&T¾êºø š¯<ÆdÔ2ïÁr.RT`E‚ néu-̆íj~AÝ…qܶ¢ˆë Ÿc‘ý©ª£*>.ÎàñÏ(¼sñ¡ž<ÙµIc®“4hÀ Ÿ ð¦¡À†¦ø´5ÜŸF�B|~ïójáGãã•ØVg¾¯ÍèàMk"‚9t,A€Å)®Í-À\“±…Ó¼»Rï&«wªeuŸLs`9³Ä6~A‰Ôm™ØYAƒ?MeÉy”Ý®§Ðz‚ÕÜ lÖÍZDCbRihbæå^�Ñõò‹,p\hÜ›{ ¸©Ö_Ð ¸:¦qÓÒÛxS:TLO.½Ù’ÆhlGÂf5c:WÈ}y ôÝðcçÞø´ý.‹Ñ0‹ ÅInà„J).$™&Þ÷²5SÀ"©å5ƱpÇOÑ1œ»èûc¡VÍ•Wìðí ÒÁNRèC0“ ` “$£‘N‚×íëÕ”2Ø®¦¾Uëàúˆªqp¡½5Ž~`+À2>,Ê`f€Ú‰4³}Û+°Ñê<!¼Ú� 6(äãùé¬*;\œÁ ¤œBC<bÞæÄ(î+ºÏ¤™„Êë_kèÀa—÷_ù¼r…�Ñ sö"ÄíZl|ö\¢ÒÞÓ© ÀE=]±Ô 1:3Ÿ™1ÏA æÀnœÀB'u™¦‚“½!ÕÌ�;ôÍðãN¯å$>03› ŒÎžÙ¹'õ�J¢i¹•½‚£�tëÖÍq¡qoî-°N½¿Dx4¹#µUÛ0µLCíÙ·É(hó°3óÈg½Ôìq& ¥nž9ðmÅg––EáuêA[‹ÅaH‰åÇð±OÁ\*ÄRž÷ˆ×dšåâ—/Rr|¹<¼¨´Y3Nû\kïÌq<ÒxñÞ�¢ø<_®[HR,É¥Üâ<š9Ùú)È‹¿(ÍfåÀŽÙÐ(¹bkÖTµ–èþ¨aöàtÕ(€Z$Ðy ês-ø\˜W½8Ôu+U§>/Ûä¬� ãϸЯm;ú€ÉïQe¹zÿ48²™RG‡i¤ýX¬Z§ ºy»’3K öÊ=@†©Y1sûI*ÍHém}©Ä/½5A4`óÎÆÍf�ÐA@g?Ue™&0õüèJ9çy3šBͨBÓǃhÏ•�×?�‹¿Q©9Ô'2žúÇ$Si§à/J½{Ue­qoî›Ú¾ûçƒqMš…�R<[ЦE+8�¦‹œ2FÊÎa+­§Î¶.¿�F�ÑD7ð`‡6ܬQ—Šá0ó™/2¶9óÒa.óaxn}^—1äBz\­:œž¹å™<¡\‰ÁxØãìyH˜�£G¢CÙ®Òf¾2GíB˜”ZLvV †=ÔÅ’†R[zÒƒ`'ϧƠÏ&=5)\óAŒGÀ8%Õj/…,AVϧtÔx|‚¿÷¦û´åt‚Áf¢üÙÏ;*Â4p©Š´¸ÀHÂ�JÃ~²O vkÈVC#1wè—B=­ßÖW‰±Ð_e4mS•µÆ½¹ÁÆ/¼~Ã7ÓÚx_ÄxÊ„UÍs7¯æ_Bc `ãlq#i3=N bå�QQ»M˜Mt“”[:óiN^Q"ñ^ŧ̡Mk×äPkÖ>ØÍÚÈ` þ¬€{×pܧ·hnÖd†£e×Ì– 3¦½À�Xž¼<È—ëK½›¡†…Ó!H>ï⟙å80�é`MÁÏ}òðH+]Š:[rrÍÜu^1WûŒ=³™r„��Ç+hšy;Çd°cÆ °äyçê56°¹:¹°óäMÃçuq¦+>�i/½UÎ3¨Õ*Õq«í¸qÂç7höV3€ƒ¸\àí÷QzÓÖáUYkÜ›l"5yj´‹q�›Rפ�·3³¹ÚÉšÎ`ã&°Ä¹ “?q��®øœü�4½ÒEnŸçÂsMDБ†¿ÏcË“ôäŒlÓ‚åÄærmlǹÞÃý*`Üó’Û >3,°ž-ÓÂ�À몱ãsP­úäÿ·¿’ÿßÿF¼ÿë_ˆ÷f0â®~ø¦ñ@43@Hÿ£’LfGc1®?Þ{XËæ–\škI:‹)uP”ýîÁ³€ Bã „ÒaKiŠþ2O@g–¤Ïˆïcôsq× ”Üʇ¤u«—WŒzÍ3¿ºÐšgÿëu �½è};æ…\¿m›ª÷Ú8¿Ÿº|[Z½^ûá“ÏÑ�ؤ'#ÑüU‘ØÈó¥ô¦­š;.4îͽuü·¿ és<§£/YCÓº…EÒ¶›ïØ|bnØ|ß 6æ¹4šŸšfwS‰ý׸rZ1xÅí8ÌÆU·Eû„ÑÔuïY`"óù_�aš°>n¡¼÷<%EçàµRIc.ðk)¢?.¸®SÖÄCÙÀÔâb½ª—p]gØE8@5º=ŠÙ·o§q÷ ±ˆÑk÷Ó˜û÷Ó°Û^ÄçÏ¥@¤Öb­TZ},ĸÝó �Mˆ¨Î.Ô=5ŽlƒëGÏà³LCMgy`'®5}Ö9‹ëQº*Îuc835ÇÂ®× ¿ œR§áÛ6Ú^¹ø˜üøg”Ÿ?˜’¼ZQ:z7êUe=éÛûLòb­'¦jÒe09„ΘÌÃÎ$ &š8 ƒqí¹UÿÌÚH}`rÛ/6™z„ÇœNjè¹¾¤mÀuUYkÜ›lúlއ’•FëOÏ.½….AâèäÈ,¡ùÍÎæ1Ï_nßCÓK»‘5O'º¡Åx4¡ÈÆÍ©ÿÌû-U™ê�‘À}±š—É–J™$óû­+ãncVÏ߯ӀUU;pˆâß\Ó� �ÓVÑ7ÝÄN÷GÛs,øÜƒ ÀHŒÌ¤/½2¯e™VË*?;wû„˜,MnÞ<Þ£€Óc 0÷õ¶¤¹=,€_ß<àL_Ü¿-Ëe'‚ç’£9µ÷>¤Ý¿ûs¬ÏèE‡Z�Ä8ØÏ]·…yzÓÔm'‰ˆrÅb,àëñD›†ñ×Eùƒ(£u;*‚ ­{D Ý3aUŠÄ؆c¨ËHè©4ðSfæ>gÕ™1k æTÚëH˜Ó‰;ï~€z†Fa�H¤² ˆ£©-î©ÒBãÞÜ[§¿ý³<ÝÛçÓÿ@ž÷1*+�^_û˜LÆ0vVÿ�¦³šŠ÷>aaBBãfœ>‹Ф{w Ìôî4{Ûqe¸(9{åø+uë¶œ£®~År~Ò£ïY†œ\tU–s¿MqåoôH㩘CZy1£óP ™¶sà—f ØH4#¥3Íßv M€†ëO�ww2Ÿçò@(`ƒí5ÐDÊ@ƒ÷aéõ~ܯú˜g¹},ë‹ÎÙüú�fƒ%åÚÎugi³ógz@[«ÿ;¥fô¢é;Î1ÈÌ@”KÊÌ õo.ý636}F…±yTäÌ�þQ tðÑ'L- 3g Ï׼Ӝًp0j&NÇ(ä<“$Z¿LŸUþk¬£Á.žä›Û®Ówñõ›¸ÁƽUlþú�±™í;~›ãÓ‰à�FSJºÒ»ž–áRzÞÖ\§1Ós]õ"ŽÁ¤VmÕhØTÓ…± Þ4©¤…Xlˆ@@Fíå˜XÎ#l–&óvŸ¥øÈ,r¡I0ý˜ªé4föâÌz¸gT›Wznú5oÇ{s%X {‹ñ|€M^ÑPš¿ã€FêNØ3°Yö4ÏåAmÆbfš�Øú8Òi 8âšÁá4R€M+•òŒÓh` ÚyV*,�ŸA–c5Š{M¡ù»Ï©´ Ç žq÷ì¥à: ù\¸?k 8Ò^\¿AªQ“gÁÀM@ÂÈPL÷¥—È fvcNãƒq°Üak¡ F?ô.• £±±Uì+0¢RÛs�úL¹›Š]ÔuÇÁ'° ç:ùê›ôåó»é…[îÂ~U‚±kõ½'Æ\<çûúyβeí¢Ë¡¯Åì�æÌ@ä9fI´¹†£¦ìwÝy? 8ã©WT|ejóÖïÇÖm<Ùq‘qoî-¼^ÿl–‡Õ®ûYró–”ÙÊ›– NGw½°ù5“>SýÆ¡ÏÆô|™WÃà¶óÎ5TìÄ›.BFZ¸ýþ(@#ÞX "J ê5r{ßGÈ<úò玲œÖÕÈ“:7mJßtÉqš«Œï³+@SÚ¸›JþšBíyÔGÀzXQÏ·cohV*0UŒM}ß™[¿g¸È¤»@læ¡¥¦>†Ê,¿Q‹ÝðŒš;Ã5Û=®.Ý¥‹*œÀ¤êÏC-ŠSŠaÕ¨Ž4}û)VœIO�J•²X€�‰sÀŽ3ÔúZJõ âfÎ~±I´nÉJÚ;ü1™y4<%“î;…ŽïÞÿë2h6cro6³SÊKspï,`h ÇÉ�Aœµ�Pýoëf. �1ITQ‘ܤÅ�ÄÍJª´Ø¸7·/ÀæÖðº >E vì!ôÈì…ìGæ(S°1ÝÖÄöc:»ùèém4,%“SiQõ=( £Fßû-WÕ;cûQ©(aBHI±È tø «�†Aˆ ‘bú5M±Í¡öý¬KÐ@�ÆÅöóúóXöüpt(×Z ûúeŒb½^# £Tgœ*»ôî¾-�Iœ:ðMC/IJ¥±È_» žO öœS9z)þ#Tª‰¯tUªMK¥©Þ O}Ná�šsÏMj�:l�Ï‹�lŠ«¢Bcçar�òfž°‰É›ÆÅ>eÜŧ�¤¸š æ…ÏɱLƒ�Š¡«ž³Ò‹ N«jòzöé�÷FñP4°fAŸ”d¿“ô¹ê ¢ß¢ÆTèшk50ßÄoñÍáÆM»Œ]À†Ã9ÜcSÔs ‡ÅQYx$•EDS—ð* §9ù¨ãäa$ôªÔÓdZ÷¿„sï̵‡bSLÉ´YÆŒp®íèqu°QR3;÷ ó±—ã5=˜âáåã¸Ð¸7÷Èø__ý¾D¯–Ç�Fc_´Gç,Äà´Ãæ®Î` c NË�óªØØÍ7ÅŒÓòcÛ´x%EC€TŽË³Mzìý+ŽË´«~ìÅM ø<Õwc,Þ° G±¯�ÚF-Z Ù0 õW4v‘ã1ršÓW±Xà 5à™5 c?dÆ vt›p-~¥ÂÆÞ”úÊw�ùì7‚ߎ¬lã®}Açã£1à ¬‰YVÿž nò9®Íö_g$¥†óœîë^hû²¸&ZAßr‹©(F#cŸç"p8®œ3åéo¨°`(•F$Pi¨‹2Ú¶§’ PêþqpÏ. #ϼàX°×ë(æé›Î…u“¬Yžo°ˆ1°çz�„ 8�@OgPz-–YáøìB*ƒ `CémÚ¿ �@ã*-6îÍ 6auê? ÚEx£Q*Àæ¡s…Ùp¦gj³1do¾*:}à]ºðæ‡|ßß¾ô u `°á@oIqÿY´h÷™+Wû«öK(™°M…¦j;JÉe& ]')-»üÖêp1¦™•pQvèÇqû+þ1ý’göÃ¥˜¢áõþ¥³2Æ0„(VÞ¬ MZÇ€jF|~“ï·iÀ.Ãlª³q'jA#LvŠf¿4€ �uÐÒ“'�š…ú1ý¶‘Ù8§âÌ@ÆãzÁÉ ¼)ÎQkÐ飡¹{ÿöâ$0ùɯ6Chh�:+ ¥¿@JÃ@À�h@¼sÔD:wð=€ÑÙ‹¹6âÔ§Seµ˜¼¶�½8¸HG;ßà `k¾6Öqp›•}cÒrÑø�‘Љé•Ùí}ez¶­á¼Ò¸77Ø\_ý¯!5ë<œèÙâ´eU“�Y6�/X&5›÷«\³`ÑŘÍ|óâ>zý�uØïÕÔ$•Ƭê¾I3(¾q36ß„I(…ÔoJ#–=E+^»Ä`qó;à¬D,¶+¸Äi@Žaá’žœ1¼AaMZQ8R6)uêTî@qœË—=ÑÅ�"?§ÏÒëÖ¡-â`ŒCy©l¨ ãM›‚nãð½*,£ÔuŒ¬F{]ÒH£nÛÎ  bË™ æ">)ͤ"�†œRàX;y¢¦sªëZeÌWÿÔ¼¸y3«É†C7Rbø¾R�°ˆ5 ƒ�˜­²‹€r{.-Aã»ô¤Ò 0XÕD¡J=#âh\NFB/£Ëo¬-öÚ¯5hj…vÇù3º›3™Â!Uf8ã8C=F5``Röïùö†gh$$å¥�a”ÞªÝQW�º�´óûK–÷æ/pÝM1M<·¦·iw9ªA#JoÞš^Z½,ä#}¼€~[Ï/ëÖ4 4Þȵ˜‘(þoµ|×Þ9bnE¼½þ)PõpN/ù_W�'rÆúFÒ”‡aPÚ%Y„Å M5? £Aඤ£˜¬øöà%O²:-¢^#JFJkC\¸XÙ˜ƒ'vâ\f1ñ5y¶?ƒ¤ÌN@Ã5••¨· ·†�&Ô£MÂ÷²>×RGõ½šåO|LahN «^Œª5£%Œjø´êGqŠy݃ŸYÎlŠ*úÓdάûŸ{aTBû6�.|¨÷œÇÀd*msk”U ïÅÏÎf'<ü.ué>‰Fw£B¸€g·õ¡2È ‡¥fÑöÛî¡JYÐptï4ý|}ÁwPx9†¶Ð_5ûržÚiIÛ1GÇ�—¡ìëc�m¨Œ®çñET­c«ºÖ¸7w��gÀuÕÞ‹¨SÿRú[â6¦­«Vó4>ù`phÖé¸&@ †;ŽûÃ'ÝyÞ>üŸžÞ×­j谼ǙCïÑ=§QBO l¸¾ŽÂq¬ M}ôm[ÍÁÀ#LÇíæœ+ö[{¾Í£…ÑèÙoîcP‚yP`5V…Q±GC®·Øíú‹Yi†ÑϨµDÁ-¹§§bÀ°çþ%£vŒ· ÒX%†÷bãͼ²1¬’S)ê:N!é²î寜§âÞSx<6ØV­¾$ ;KŸw¤ÇQÀŸ•çIvž5ÍÕ³ó¹Îã¦÷f&0È×nPëʤéÏý¨¼Ð„ÁØwE ÂÇùwª  �}H]ÆQ.úlr:t¢ôÖÞØÃá:1…¦v¦ï‘žÕ�B—K*̸P#®­S¯Ñh’dËr®éੱ2ƒ«´ÙvJŽq|íÔY”ß®tô¯Œ÷höuT톪ºÖ¸77Ø4Âôדš5¯°Òhi^-é©e·�ôØèãä¸ÚëÇÄ!ö©çhDj&åâ?} €, ÀÓÖëó¹8r[ÀÌÊ�LË&!AÂ��Ó—ÊŸúŒí䥘.raq�Û"°� X°ï"Œ.RÒ7èuageß¹þ2–6�Å„qZl3çE ]Æ5žXèØg¹®³õ�4I.Äëq­¦…CÚ=ñÑw”<[˜˜`l‚žL:lÅfþ-\x lSé?#0›z,6ŒÁ1�´ß@5�ÈUͱ¹jfô„àƒ  ¾þ�!i,ôPà" D^»Ó·:†ySžøœz£Oih×A405ƒ…ƒÓ²hlA)Ý;qº¹‘SRaŒAŽé€e|LS–é áZÌI{/<œ�ÐYtpâåCHG ûцr¼;VB‰ömHõšU]kÜ›{Jç�®ú _LiÑšŸ“šxÑúùK9Ý`h5v†sjÿ;´ ÷Œå $¨Ý˜™d¡itå°ÑP¥}hè Æé·-«î _®Ý€á0àD5jF™½hòºl Âc‘E“\ËíeœFãàbû°›·Q˜Gs€Y  -c°�Š‹@$àÂ=+ñì­Å S‹¦ õóF^²­OÇ)>(Jçt üüžEýfc¡¬–&M«;`.PeK›¸€X‹Ýp* ' 3‹ÖD3à(†Ã@iLÃ9ú Š4'À’tâ�áU£K� ÎC­æ,‰+€’&ójÔ¿£p8pÑ0só74dî:Z4}!�È+¤n�>÷ˆŽ¥Þ�éîhƒ6€ÃÌtÌõ„3»±…vÌÑ]Y»ª‰œçñ8Ô–ÌCÓ?³�J‘žÐ`VPàåè: ?D- J �{soA5jý%´vÝG¡D»d™p&x4£5ÓgËxƒk€ý¸ö8_Ý6j<¥£o'^§že®Éc~óÚu¤/·í¶=ŸY�þš JO.YÅê4<Ÿé¤Ô¢6¡¤ðtyëv€ÆÅhì}8 $Í‚�ÞîËÙ…nóù(¤.ÒgÕkrm(f3:xc ©�\Ò¸ω‰T~c`l;s�+�U` ª.(¸=Ìß4®ÕÄ…¥Ñä�GÀƒaÉö~Þ 3ãÆ³ì• ÔsÌr …ËBk€7‰:1ŠSœû"ƒ ÏÆ¨‹™TѤ¦“3³Y‘æ¤j“8\œÁu¤p¤Ð"}chü¦¯¨H�F HS’gq@èX«šiOM‹î}‘6Ü÷ ÎÊ¥îšÉi˜öšÇ⃚Î6ôEZŽ!´Ô›|œE º¡¦’’6ÔQÍŽÑS\ÔÎC�mâÁŠ M½(Å«õ…Ðê5w†T«éq5ë�{s§ÒîÃbûcd½†Üßrˈ±tñí� Ç!i\§yvÅí”›÷8(ÊRZ´"€a8E[7ñ>�/^A—ñZzNOXRiô�I 8€¡ 3n¬ÏšàÕšRB“iÄ-ÛhÑÞsWšm~iºa'ÍÞñ u¹ÀuqzŽ�¦÷É@©uT)§Å0.™>¡8ÂjͤášÍÅk´±ÙŸ“„ŵ[ze†¯Ü©@®1Iã&×›V²ÊŽo+ù³îx=ùá7Ø9ìf úh¾DÍÈ,a¶›v>‚ô R¨ñxê{ù»çXˆ dªêþüë㱇¶nNaH†àý»—?  ©´É›ÅjHBj9öÇæî¹H!˜}Û3´vÕ­Ô7)…Æ”Ðp8˜ÏîÞ‡.áïÙ<¶Ù�¡ØÁåÚ ùf/2CÑëFæ‹s轆d\{Ÿ»ÆO¥dM<ê¨øÿwÎÿŸÕ‹©ëážcãÞª4×ßð×ÐêµVW¯ùe4Ôhñ�šÒ-#Ç Øh¬Å0>€ ü_ð®"Ù~ÀÅ ‰´WšO¥´jCQ¸�Î}ž™s =xŽ.>ÐÇöÒ%'÷Þó �Ë*  Ær¡Žãª[Ÿ¢0Á3ªµ/uŸ@“Tíq¥Ge™}<4Í{ñ'ê:f%üãŸÐð0²>^ÍØNÿ_ÈžÆ9>-Éb�´ ¤ÎÁÌzM¾yÞVk’ÛŠ�½"{ ÄŒM‰æ¾ø3ÅùG[Ö.œê[N竨.;ƒïº1ÎÅM¥¨OqÚpRû6à8 ª¢hÓzŒ¼CkÖÅg¯O©Yýà�vZÉšmŽ<ZÔhJ�& –jç9}ûNºíîM´júLêF3 ì¦wl= û¥Jƒ\XO©}f4átN•™ë8 Žsnœë::UU…f9íjÞø�&AM‹ºkL£&ø·«sÖÿŸ7Ì kèá–=»·ªo�×ݸWÛß nS™Û¶-0ÔRž i~¡ñ*íµ ï`šÄf-8Ý•U0˜JúL£XÏ6V Œ#tüÀ#�ÛAåõõÿ´•ïakó! ©«ÌÞÁ±¼Žý�ÿÌëïÐí£&P>1 <ø9øœè b�A*†gÕGxµ§®°q)ü0-Þg¥§¤ïfêº÷(6(Þz>MvƒzôBZ,�ÓŠÙ¿Àï‹åÒüÞøÞ]G¯°Òg•‘ó“˪ÕÖÈ)}6¶¾!õ}ä83#Ĉ¥›`óÒ„ íÝ›6¦÷¬)ž6bêÃy*¹Ü†õ-°aœ®x볟¯bƒ¦ù~)Ç9ܶz{b�þD-Gn¯T¾ó‚šº)u h �|[Ô†’Nã˜úøg4á~HsïÚK] So8>w �¤]z¨ž1�E8°sã¥Ùí¹ê~cÎï­³ó¤QçÑÒÎìGaňÚ"lP3½ä÷�v@ÉzÓÕ®5îÍÍlbÑ¡ÿU’W‹Š8�¦4¿ï I£s¾¶Û\ã¹ M˜± 3ûHðlE…EC¯Œ8^è:tE4mm1N)%4ö¤GgΧ oÖ®6­W›®@A²9§Ý.ÈØTƒ’ª•/77]úM[÷.Í{éÍ‡Ê é¥+‹óœmÇ(9¥ õ8ʪ_�»üÏi©žß?°è³ã2 ¾s:F>ÏÝòM%Àâ"âò2‰ Ó|– Ø�:G5©Êc ¶£ŒŒž ùp ØÍ”öméžu㥶ÏûØ Ì=Y‡è@HJœšs`2,ذ á1¸-DÖ²ìhšRxC/*±œflÿEc)öáh¶Z Â&`PR�×:eß'•#nÙN… ù4$3‡úÅ%ÒÓKovL™™ZmR]ɦ7hêLÄÙ&ÆY´  Ìý96Úmûg:‹ ÏÑyŬ°ièaÕÁš1Ùïºj½ÚµÆ½¹ÁÆ…Åü‹¬v.%Áˆs@)’ñ÷Yshd1�öøGÝÚŸ=°àwÆ}7}¢1#V6•2Š­lÞZ÷$u çZ„²¬!? dªC>ÝÑEE}ʹÏfþÞ x}[}FROHŸu» é+X‹'§ŒÎÿñŒ†ÕV<>€ jJ^íhüšWié«—ðÙ˜Ñ,Q`²Pê3¤Žã¶�íà¾,ÄÒô©õ š·Ž\�ã½7§§“£5¨ÊµcÝóéVȳ£¸¿ˆûqXØðBj [Ü\�Sôy�¹7Eh,™v¬yºN¾—¦mûY±a1ª&cc4 x$�&¡G˜Ï<ô+õå…ÅR/úGÅ3sv6°tT|]{EOË9ŸÙ·Íà‡æ†ÎÇÍ�æê-/òPE¸Â[êÒJ¤Ü¿G -èZÖ÷æ0�ƒ³IhÒŒ ‘Þ‚¢Ì`DAvüåƒ42Ó$°XÞðÈåžSiîÎÓÊbDØM—!óy– äJÁ¼OL�¡&ÅnÞ…Ù߀ØD¦ìáX¤P›á.±® ê5îšüÈ›V_…m�À‘Exšü¢ƒ“,VÁ3c�wÙÔZÎ`c`�QÌl_Ãsj¡p2î;c-Æ\È0£Û“^šÅ¢ýÐB}& k> F<‡[©ÎIwÓ¬Ý $}&·•Ù&-¶#�[�Ûò\{ gþÞ‹”ße,eù‡Sq@0õvEÓç[wØx�¡kà`ëyMíg!‚ù¸³êÍ ¯6¦ÙªÚ*5 ‡0™3Ù³÷ËAö\p]õ·�Fs§ÐÜÛ5¹ÔàìÂÒ…ØF�‰›._ØmÎY ãáæÏ ó–R®wbý="1Öö>V …Ý&fÁsßPZÅšõlüUíeåÐÑtbïAºŒt܃q� ‘ŽsD6iIy]Æ`<ô1Z¬Rd2MŸÆÉuŽW.bžÿTNŸ±Íu7°´÷™ÄHžœyªç" à×]%&›—“Ó�æ½pÌT* ±n¨ûòý–1(IZ ÷ÅÆ�Ûüz+_»H£V=K1Þ�<Œ�­{œš„ó3Ÿe�¿8v^¶¥ÕG“Ú öÿ‡ù3Îæ ›¶ônËý™{|ހВBÛ…/«×‘û4#²ÒzP>ìï‹1ÇfBn‘5¿†S´{î}�ÆC™vÏ„©t G3p(˜•eWaY£÷ò8 ÀäœVÓ_OÂèצƒ™ð>{n'¥5oŬQãÑô(ÖŠàkZhÜ›{C¡ïo0ãܘèÙò,zm0Ó¦ ½xç»I¦n¸ÉNëæ-¦¢þœ¶Èõ ¡ÎƒçA v ¦­�EQÚe¨1ô+¿Ÿ"·¤ˆÚõyaF°áæê±“éÈSÏÓ‚>)2é°šu˜Ý„7†²mþF\Ý^’ô™0eÏog#ïØE`˜£Ž2ÝdõÕrÌxyÇa4€1Ð<™Á>_jàE·  aã �¨T B¼€#ãhÕ¾�@RéH¾/ã¢U=C¼àhö¦�(Å•ÆÌ1Ÿ¥G³&ì‚ ÀRì 6z0CÚ„ï7}8˜BÊ€€á›r¿æ›Aú°šò˜«'g4ö¶  â~3ÁL�ªÚ ³ÅZ$˜é ÄóL1 9F|®¤v°¡é›¿£ OËò ¥"øñÍìÚ‹•h‡ñ÷4<9�F ÷f.ŒöÜó€ÆÌlÇÑ(S\@J_à?tð93*ۀƠ s;`w пǞ5S¼GBŠÝjæ< åê.w ͽýֺʹ°Zu�£È ™,3NCoÍ7;^¦!Iiè§éD¹ý)Á'„fn|_€HsŽW¬þíü™:cúf ÆÜU¯j8p‘¥yèóyeÝ4¶O_ U€[“°¡iO¥ çRßà£J[ dÎöÔkâ”[2œÂ1(-à/ÿ¦:¯Gå@¥†¢þµ¤ÎØ# 1+¸4ìÛ!œ†C’ à­´>/ w„ü6bU#àbK³­ÛŽ Xe©Ô«ø±×/Ñp ™‹lÙ‘‚nªÅ,k�úEÒiNÞefR2é/ N;€t±qš­üâæùûXÁÀ}sˆ4l@±5­ßƒ/$¸¯&¯t$ÍÜþ³=•&!5ªØ »Ñ÷ü\Ál ³ûRI(jE`6pzæA|ëæ.¦!iy4¤¨Íí7Œ6ÎYÂjGMà4Þ\.´œÀÈèð¬[æ Œ`agñ€CšPcMÚsXa:½p �›1Øà¢ïúkæùßXÃ�Bso¿ lòqÕò ÷JKÖ76‘iºK€84o¹y5Ä~TèD ÍÛR°šå¯\P‹¡¸+/“&DåùÅ÷?Qi¿YÓ¬ @…Ug!èG鑚FK'O¡dXÜl8�æÂ¸àìâáü¼%*…¦òú6£ÆbS³ôõJZ²ç4ÍBÃçØ{_¦²Ñ+©xð|JIéÂÀ°á”F_ÐÜ‚Å5 ‹:/¬øìQ-;P�ÑËiéË*ÓÂw¶±0ŽU*(½4Âj“ çßTž«ÎåtZ÷QËÐ{äÍ5L!å®}8˜äÞNµýö• €-n¬ã¥ü>oä¥Ò\_N«a�²jg\¿*¾”æ¼tÚ6:@¤Ìö†Î9j?Sœx¯‚ÏãçªçLÜø e§÷¤|ü=æt ¢Õðò:´îiœ« ´2*ÍíGKûÒýSf*i¿ ¼CC¦³RLŸƒ£Ÿëlc1'¿5gE�óçâûßí|•rQ¿MkÕÑ©Ùz'°Nôý­k�{sƒM"â:ü/»°°§Cêxlï; –ƒñïÒœî}©Ô?„21-¶­?•o†ë®L˜”QÌ%ù¸H}»~¡îXü£ÛøQ$£Ñ§“ˆ1 ˆLŸN”Ø¢÷Ù°ctý†œzëe×¢},yV’`IÕ!”’IŽCœŸq¼’–Z}?ÃcäúÝ 9;›r. ìȵ f4`eáÍQ7™KËöžâ÷Æ!Å}>®BîÛ]„åa¯À[s¬òšó^ø‘òKFð˜åP5†à5(É. :�Ùéb‰§#¹©•{«” ¼ÏÜõ´¤vGk»: �\¤Æ31S1šy¢J³©Ù¦où�òòqͦ00„Æ�œGÓŒ¥´àDÊ„{DaVoê•RˆFáItΆÞ1k&Çî{�91]¾lV‘éŸÏÉ›ÍÄ´Þ¯ýÒ]÷#¥Þ–2P�̓{Û? ºþ¦–¿u­qon°i‚ø‹B˜ OÈühó úütµ?øèÔ=0œò;úQ^§@ê<`67nòU¸\‰ ðð(f¾­úa¤Ïdþîs4xùJ�- ¤6¾”Ö²�%¡f�ÁçaÅXïƒÀ€b:EP Éš±å(ñB%��¶fH¾âù®ôOóì^ˆÕâ¿pÛwÛÉe1®ÛìH‹u�?ÛŽ±°`Žo{ ¯n±±êü¹¢[wBÍj.-Üq\}O„òmb ©5áq ‹±Oe¦(–<20á<~œ¥ßÉ‘Ù\?‚kC%Ò]œÓ¿““‹³y§9åÆÁ,ç•ìD+Ũԇ7PxûP¨?$N“‰³³b,,¸]ÎÇ\Ôï¤öâ7ëÅ_¨3ú¯J¢©wrõì9‘r’J)/±” ’»PAF/ê–Þ•ævëG', }ÖÌ`��3Mi, ³bLÂY.-#«µTœ €èàḛ̀.¼ñÍë7˜ò||lRš·:ƒÙI÷ºjÕûûoZhÜ›{ëô÷Ö ¾á¦—`·‹<Ô`^jb§>ƒãü[Òܾ­î~ÑÈ‹ÆÞ³Of­ðÕµ>!S®Àv%OZœúħhÊ»�rÒºQ\?,àuøj5ÄMì"€4_©G4kK}§¯¥…;Qu y­»NÓœg¿¢°®¶è êŠôÞÀYÒˆ•Ïј;wÓ„Ò°•[yXÄ,íÅP3ÃUº. ~Îã¼[ñ|€>ÓMèòB j&-ÚsF}_,þÂîd0š,²Kœµ)�âƒÆ‚�óRûa0pgÀQžkã~‡R3zVZŒ"ŸwL݆, t\¹FÁõ9¬QsžÍ¨˜�2íä@ßRsŠ%‰ õâ)—*~¥¦Áþ`›à@Ð �¡`\ðÆÂmÉzGßñ-Hªº” tÅ<Ó¦˜Ã1=†ç ³±÷äàq©ç¨cja‘é9’y8~=U~Œñ ½´C�&¶q0/™ɯ¦Êj9gñ8Ëjå4dðõ³¡Dô>eøFÐÈ”,:¥¥)åä\/ÑŸcÓ"_õú‹îà ™¾j“QyŸ‹`5‹�`5jT¦œK± ›¼|C�šîÕѽý®þ¨R"k5ø,¦¾G…U»ÉlÛžÞê¹+©´ —Sz‹¶üÇwá^Óî£%Zˆ½é�[Yù¸€Šž:R #Wê/_b£É²áK(º}(/–PW)9-3˜Ö¢¤Ð–Ô}P,M½¯;-Ý>’îýh&=ôÍBzàèZs|Ýz>Ý{lr]›qíZÁ×+Ÿdñ,¢_àñõV^ -‡¬9ÅšEÃu hnâzÏÐÛv Åw^ÙÇØUw’F“Z «ÈX•\ÒŠòÛ ˜à8ƒ�ØÚè.Óa«Ó0ÀÌÇEÀT¤ L¾›:÷˜BéA‰ƒ&ÚÐk#jQRŒ�lßS�²1­sl›–ô úq¾Czìl/;øðm „ 8Ȧ<˜v¼¸†Õñ34Gat¦Ülã¤ð¯ ÄÎÆ qÐL:™)í:KÁz“ÂÓ)=7SžøŒYqŸò)É7ŠÛQ÷�úø™môãžýT©-äÎõšª»F;‡™a8‹Q6­ß7;èiÁ£`|½\1”ÑÚ›…°‘:‰ Ðîî•ѽýî]o©Vsš;ÏY61(Èß6fÿ§<{èšÖµr¸ (Òb<ÛÒøû*Vb—;#¤“Ÿ÷r•/óYä\餷�dÑgiè’§(5®€\(ô‡ü‡Œ—SfÁ¨„Ô¸‰rŠhÎiõ;ÓiýÉôø™›iã¹Uôø¹›iù•ôȹ%ôйEôè¹¥tÏw³à>\—�¦#Rh¯çrï jªž,�.‡%~6úR°«Úƒ % -3î�CW¾�H›¥°/5é�U™ `¤—!žh"$°a)�‘½˜�Bâ�4ÕsGi8Tx½Ð¸ZõU*Øpt*„ iM06M†°#²Ž•þbFbÕ[˜å6h@ƒQè_Jo¢¦sÀ«ÀEÀGö|ü,nßg±Õ šJcùàPqð^f `¬½ûÅ�!L‡C5u XÉo1Oê;̦¦AÕ8smFÞ¹àsŽ•Ž}¦­¡ø.¸\„RÏÈXº Ÿëf-„*m¿¶p#œ 0�j3}§#Cºªpf.†Ïçì$À··`ÒnšW+N¡%5oy9ø¦Zobœ€[àÞþ°É�ÝQü�û•Vƒg:²?ß¶‹>}~'G³O$šScriÖó?^IeˆHZGRv`WZÉ•ºÔ*¨|ËwÔ}ÂH‰uâ Ÿèÿ± üœ ­U“r ¡FHü0Ÿ6�½•`ÎÞLO�»ƒž>·šž8w ) áXnÍ}nˆÅ†xQL€�Ê{…©ô]×Ú–Mc½[rƒ&3f>�˜¿ãòlO×ofÖ µ»{�(ï8$Å( *>nò˜4kÊóĬSÀZ<Ĥ¯H)ù0CèUê_&& c¸sZw¤h¸@‡‚q„¨ãxh<)²õìVí(¹usr5¨ ¶h ,» ê<Ç&ö=}‘>\ š=Yñ–K�Ðhg9ß—åBÕ×ôÊž–ñ÷¾Æ�–ó÷U\a"º´Y7äœ-)3b§¹�½°aL"�æÛ;NQùóÇy §á�Òë1îvJ„)j Òh�]1Ô-< ónhûí÷ð…”­ö¡�NÕš.¥Öâà̬×ZŠü²i݈S«ÝØ”C›_^›†¦dR*Räém¼-Vó‹ÿ?nXÕñ†êùÃ÷æžo†óÀælæÅÇÀ+m><’,Ÿg¥_Ø<ÓU¯1õw3ÕK $�£Ž‹ØÆ´QÈÖsTmŒée¤t%WCO0™šÖ8çÃa½’МF-)¢µ_Ì¥�gWѺsËé±sË8Ö!«¡Mçn%Ånx¿þì š¾¾/ÀŠ•h|5�Îzv2Vu‹1ñ¢ðo×Qà¿]OÌlÐPŠÐÕ¦\�¾}§ö‹!3ÈmGØ,‘t¡ˆË‡ +ö$ÝfëÑy76ÌG¡w(36ŸGeÇáb n1u1‹Ék—‡ú诇ájM©8 &¡“0B`}€Áê:2’â|Cðñ@­›(©Éˆš5(Õ£•´lLC ^»-ÔÏšrÊi¶Ó= µÚ�%´S@nEvj&Lĸæ9;ÏÚj.Rü×öò˜VË‘cºI§ý<0±=—¨÷Œ)Ñ?†Ðœ ¿œà˜uѽ`8ß<¬1;à˜ë:Îá˜Zs®éNÍh ar)0L'•ØuïƒìÜÁŽý*¡Zük�ÿ½Þ¸77à”»j×ÿ.Ù³e¥5Ä,ã]C#þ�½«Ž"ºŸþÝ 7‹›wwWÜ�ÃÄÝÝw×sã”sw7Ãí ä}/]S35µÅŸ[¶ªë7;;3$©»ßÛ×ýú5˜Â’ô‹ÿxK,ÌÝÂ�°Ÿ›¢Ö/à UçU¦mS•USpi5œÒ'.a1{eÂÙ'ã?Ñ ìX3NgÊ��™2KIuAÍ«pø²°F£0–ݽUا CXÍñž62œvðX®Ù{¥³V¨œRïá7ygŒÓoxÌÕõ—¿GêOaLEG#Âà >´Éq£d˜ìîô÷Š�†Â¾…n6�ž�ñ½¾ž£©Ïz“MþíôréFÝß“¡³ÒÛÚ{¤©izËcÜÜ£É\FÁŸ_BFŒFCçl¦Bÿ*NÞIôp[².9»f¡ö•Õèþ!;{XÏê-FÛTmí›�„x;Ú��Û0*ï(¶pùóïÉdÿFÕx±Cþ*‚ Úô°ÇÆŸ¦Fâ3Æé¨@ìõq�T°aÇ9¢­œË/ «ZžDÁ©I‡é†¦i ¡"mÕŠ�-Q©&PÒmjªMV!°ÿfnöf±fÊZNS‹©1ó° ,-Ë×J·¼áæ¯óL» `q_füüõ?Ï\ ÏÔgtqïæ úàØqÅN´u”:mà˜ 7™>>B…ê¯þ³÷šÁ×  zÿc»ŸìæG‚ üFŽ–T‹òÒè9Öœ)¥“¨°Ñ¤Ìй¢6ºoô -MT§@Ó€&ï8E‹K:Ùþl*¥N€Æ•þ>“†cYi º?ÎÇ¡�8ÖÓ*©3®& »P!©ìæX/?ïi´÷F òÎç ü?Ãáïþ Ž 'FìGúuE`U{2ÞÝ€í—Êäþ£×[Ðtj=¦Fº"d"ÓMd ®¿ù#œél0mI)J¿ K‘Uj º#½ˆ@BR (Ï¥™þ3M8qE�Á}ý�xý™à2Püçèlï¿ %HEð˜qˆ™l‰yËiѳÛÏ—`ç�rlé-ÂæÞBlë-ÁöÞR‰m7JÐòÅF¬Ùœ„”ÙÎ:¾¦ó=â/ è¨i6W†åãÁ¦Ü|È ©jƒk>d‹*Ø8ÿš×Œ‹hkL‹ž�UtpÈ⺪B“ÕP K¹)À¡��z�Æ K ÔkšÉ'™Í¢òÈ L%óKD´OÂYKóŽDnò4ÎØÿÞ¥Í÷ÚÁo¬Ñ’!×éÏ1bisoÓ=õ×êÁHýw%�x}+’l1Ef9Ãoøè3“ýû°ÿªýfð5Èn¢™Nû„2ç;þ£ÆH÷7GìY¼&Ù!÷ȧ¨Ñ¾�sÕ Á•z«v¸k)´ÛÈâp­)óòàob+’fWš ÕZ�ë8¤oNÅžóU0 ˜HVV…ÕHêŒ!àC¶#+ÁFRi{Y¿Ùsµ �å¢óý¦�ø­®Y–iO-,+h ëÄ9›8wæ{µQS€F«½€Ž¡üY;V þ£YÛh µfË«ˆ‹š‹Ö’¢É´‚ü˜J �º ­YîÑÜòçgÍ/êëæBÝ s¹'Ôö¯\}éMÌñ @„‰¹ H 5î:1Ûlñ«Á&ÎÁ×]“§Ã/~Ó6Éì •)ýÒÓ˜ÌTš¿¥«È‘ &ºYøZc§ëmYô¡l²·‘½÷m$%/‡ßK/ C·¸ºù "¢,±¬;Å/.Â�ëõ8ÑÓ¡‚Í‚f²*àC&Ó&Ák„å�ÑH­†B9>Là9¦0"®8ØÓ‚‹òY“€Ù® pä¾m?–"ÿä\„ºM¤o¼˜Ú 6Ql\ßñ” ƒS$4ÆV¥k´Ô”c:ö£“¬[2>G ßyÞgJqB)ˆmŸi(ì� ÞkØ$úÚaeAZ_ÏÀÞ«Õü=ëùw ˆöÔp*°³·œQ�DWo:úr°©·]}¹ØÔW€m}¥è¾–�æo6"çØlnØ)H™éÌZš9ÂLáCða}ìm’pöGùN@Ê*wD…YQ? QLßÑcKÄ!´Šwòà šefìû@j)6Ö3: ‘0´§ÑÎUh²j”k朚<šç³†9+k‘B ø”%åð3u€Ï8K$Y;à•]ï2pLfa´Øn„‘…q_3ãM ÆR ½Û�Âjv”"`Äh™Y4v|?¿LžáM¿ÿêýfð5Èn†plôa¦ÎÎÑzL£É ³˜�)Tvý Û8ÚF+›¬^•U«ŸR)�÷™»ßBLÄLx�/LÆSÜÈœÆ�@|Š#fÖùaÑîpä??{¬Q`¿4 (d2x §[ GV©ç(é3‚ï«‘• CÀjPág|V•<‡¡¦àx¿Ô{x�ü;;¸1o¿VŠìý3h>VØ ½¢˜âû‹Œ«.8ö9ê Š–Ó€H šošµ:ÃRý±lªtQXÓò8R—ps0SfL�ýEV»þ%ܘ"LKq6³ã»2¼Ñ229ÞÛS 2¾/RYL­ ØtxÚû²ÑÙ› Þ#À$ ÔÃg\*F×…|t\ÈAÛù,´œËdd ùL*^_Žö Ùh¾ºů/Ĭ x�e}oØH„ÑK/rœ Ílg5iIËèe÷ŠŸéӥδzŽÖôiØà©¬š"O_Ë‘U§V£*î<Ön{«;Oc'¸z 8púkäDs<Ù¾YFGÚ¿q¸— ÞСùë:ƇŸ²í¼ñ&PC�ûô�Ç‘ìàŒskiÔö1º—_0�Ùýâ×ÿøß²á ¾ÕiŒGll:pfÊ&mv&ÊŸ¼¬›£ÏI(“) Æ!+ï£yñ Kدc*i323÷ÑÃ�’ž„ù‰HmpÃÜÍ�¨~s%ö÷Ô‘µ¨5‚ˆ*8ÙÛ)@BÀPUj»Eö\!€¡€ ADê80R«`ìä5ŠÈ@®!Øpé´Ü+ ‡õŽ1ˆ21EÄèI’ o�iü[P ¦4·çz†¯Î^F4ðª)3-´FW­‡fÝ–W˜‚@ö8… gzŠiª€!£…ÙøŒ†…é!hz-»®U‹!£p!�H8$6#©32®rŽ #àÓI†Ãó*ñ½\¯“C>ã}dDyêg|�º¯Ö`ay(‚-(µ¦ý‰/•�þÃG‘í˜Síä‚´ˆ™X×ù¼h* ¡K�i)2Mú¬g9†nZ�¾é³è©‘Cç܇?G„3Å”‡Ç[ØãHyîp|´ñ�á€4ã’cý}zÀ1ÆÂ0µfD‰öóʸ^ZOU/Y�°q“D�–`ãpÇ{Èð�½þ8Äó¿e£| ¾ÆÀl iõ‘bxpƒ›·¾UÏÞÐIjl%}¦ï+©Ó¤Ñ¬7|Œºñ†YM¦w(ÄsŒfgJm£7&`ñsňkñAj«¦uy¢åÓ TÁBX ß7а¯Fâ$Sl ’c(-’b#è¨Ä{åså½€�DžEÙ´lÎJ]HÀˆ@%ׄä¸û‹|Ìœá-F¤”ûÑ�:€6(·œÖ‰´ßWÇ`Tö§Kè¦NÏ%ªèl�M&·�ã,$•çIeœ?ÿ}¦ôn9 Ë ¢ÐúnÙL½¨ôø»à°%Œ†¿‡ÁFa0ù²¶õe¡¹o£¬5�Æs hÈqk_¦œç=*øð3ðzY[ú2ÐÊàçàV¼¾å|ÖnI¢)&S�tð¦h!`ëI-‘lëŽiI˱qÇ›ǃӆi3 C©ÇhólÒܸªÀ%«€ c€A=y�Џ^ä>ö#"ã—‡u·�q¦hYµž–LnÈÆAÄ8ƒÑ_k$fı٨‚Íð9ÆSx†©¼—™>Œ˜h†p*Mc-lèâ>ö¼ûïÿRJaÎ`­fðõß6¿ýýØäE˜Z\Œ6³‚'7·¥Å»QwºG©9è&srÕBiôÔ¥ÙJž¸‚éT�Eغ�ߤ$ŸïÊp>1Ù)Xþ^¼��Ø:$6:cæ_l;WªÖeÈ@ÔTABe, žP�ô?“P þ’:ây¥ö#çù^U·1tŸ1@Б š«(ÚjŸ]�H/s�h¦²b(j˜==å��×±;�gDéñOQûôUµVU¯¦Í4P©×›õÊ6Fõ ·�K ™É#ÖÊYX•ÿ_G°cÿ÷ ÛmX&byi4¶~[D¥^ƒ Cj4û{jUvCÀ‘߃` #�‘ä h ,ÈPT&£0YÉrÔk¸òÞ\´(ÃçHð:5eÇû€ê/¬¥£@2•`LÓŒ P�‚•†>LG†M°ÀL¦Ô6t¿€Â§ût 4½@;/ñ¼–2Ó÷èèFM«QøT¯(áŠX'âçHZPH•ÝD�7Ç Júo¼úž.…¦ã*1ã¬ÃˆÙÝú^î] ý3×ݽ.ôÝãÏayx4h,m@a@/Õ–Ïzüá¯&ÿ};Íàkl~ÿÇ¿g -ÏëoÃÏ3§ wöq,ÈÛŽòg®ëTUŠ0@+pëRB²Èÿô‹Š÷!�)ŒPZÞxRyæF“K÷Û“ŠÕïµ#ý«­˜ºo¢K_逥£iCSGöÒ†ƒÒS#›¦ 8ºÔ× *Ó~$8\bÿÈ9Ö(¨`Ûy¦ûyÿö+L—]%S¹^…7ê)8 Xõ Ëá±KÒs|¶Zó!HÉ¿KÀ‘çdµNG´…9‚(íM6˜Vê�U¥ûÁfBÅN…ÁæÔ¼=o¢pï[(;uM\ê^b,'yŸøU§.óïv[?¿FoJ*ç²|ˆ™37°ÿÁ´›€óþÙ •�Ñ(îZ€½gªqôF³”ß…«+µ)‚Œ®’ãJ¦Ò§1% M}ëU@á9<ä¾.… ‘áH� ñ:I�É{²9§€’<—Ï‘h;›…¬=ÓlKeÚŠ˜‚¤` ÎÄš›½fÆ/ÄÚË>Õ§õÜh}3 �ÊQóCÓI¨uM ú�k´­!»QjCýâ•æEfL¡À g|ùð)]íã_Ù3s÷e†i8£ a<gäž{Aé©©£ÙfØø‰b¶ÉôY?¿ì}ËÿžîûoÝh_ƒ/§?ÿõïÉlÒ9%óœsîn”Ö.«8Ì©”=:énµ„:ò™¡5Š ‹£™—•ìE„k‚˜'&]gà ‹ÛìâLôÂòWê±ú›N¬ú¨ Ñõˆ*³FL… 2ž�#ÜHY�Q™ 7}•áìº^Iùrš?Ø€Âç ýÑ4Ìß‚™]tlÞ€Y›}1»ÃKwE1"±ñÁ©È}j.²ž™‰æ/7bË¥"Ö8*…á(,IHa< qå¦-`´íÛÌ�ñ¦ÂpBǘ ™¢‰Üƒ‰ñcÑÃg1ŸÕCÒ�ÞüØ�$Œ&oï›H‰œŽ)1Èè~•Oœ‡Î§^qЮ}ñ–Zë*}ø ¦ÍÉB¸©=RÌÍZ ›6YÿŽSdmž�ß– ‹!ƒP XJÔ´~a2Zí%GW“aHtpmêÛ€ú¾uà{¹VŽb(i7p¬”Z�¢f“û V¹žÏQŸÝz5OAr„¢LMEÿ­0NôD ¤9{2¥¶k;ž•/$Âh’Bc¨&œª+C èüâ´4› R"¬(yî¶ J|?K7±BJ²™ŒÚѯlÎw80°��žTj6Lþ|ƒ¦Ñ1Ì÷žÓRrÆçÛí^åø§·>FWz&¦RÀ,B'˜P�6ñÍY�P>ÿ·ÿ ûÍàkP$�Æ>› L{ÑÆâ/´ly5/ü¤IšµB¶ö^•g»Þ�‘Ï!¦6  Ž€�×ðaðó´ÅÂG ±ê›¤½K_.Gx‘"+­YbÉ4ÒjPm&5ÊšE @6#õˆŽor�ujfmõEj‡+â[¸!W3-PÊ�¬„ùè*sDÔY ¬Œï ù¾ÈQ¥Vˆ­±EB£#kB.˜·3餢áótlºZ 5>_XÔEBM–#«Ò‡#Ñx:iÁΡ°Ã'ZcîÚ&²Ñrʼ\D;z"ØÒ Kò·*)3ºolƒï¨ñ¤;ÂÊÚã"²à©ÍXV†¨ dްÞ!ïK»€¥ˆc“kŸ=†¿ÃÈ ü·Æ!ÅÍ‘@3».”ƒ ¨Ög*òó|$ö3Ø_$ì„`£²•îóÑxe=.§£îòZT^^�¼Ïç¢úÌ*9×~9-W2ÐuMe.�L(²!HÈ‚tL‡!¥Ô~<|~ßZ!²Ëë�yd’baÊÞ› Œ±T©YØa�ÎÞ žj4ÐT{d0´¥ÑÖ%äXíïR˜�*=מAÁ@씵lZuD4kGE3æáÖ[ À¼}à86­ËÄã­›påô`¢¨ 3#®ÌÆY‰fƘ“<ÆŸõÅÝýÚ”÷ï:Iÿ³Èþ k¦dmÆüÿúmþÿû?e¯| ‚� ûn¾ãœ›Û ±û¯zAóESB›ù‚¾I±ôѳHN¾�Ê$G°NCsM¦Ï&Œ@jÍl¬þ¢SXMúWÛ1ëÄ}ˆ¬·At³­€BÇG¹Tšu€gÕ¢fÿ•:T¾¼ÓÛ½]Ap)¢:+oBKMRh‚àüI!|VK�©!è”tÊy] ?Ï™ˆÐªÊ-ɈmµCÚw,{(•¬ÀNª¹.jZŠºÊxÈd#ßÃtÜòÜ(DMäóGOdLBœO,fÌÍF”•“äÃ} ³×ÖKú¬ìÉ+ˆ¢+œÍs›Y—ªçû˜KúA4‰Œ±²GðsÎN,6FŽ7§òl+õ™JtßÈ—ÚIùg”¿:é§R1ïdf=ÄÖ#¾˜ö„Ã3óÁÜG±èd8–>‰u�§ ë•™Øøætä¾7U߯DÃ¥t´^#sQj; ™ ša:\`X×Ñ1¨æ‹°v{b‚­à?r$‚Ø0gb…42Œž�˜–r6î|Sê+%tòhk¤ ê�+šZu!Rëú1?gü¬Ü`ã�ÕQIøâá§qæ©Q[„ý7D„ ÖnÈ„-P˜DÝ3k�ìãHà¿G0!à„�3ËÚ´SRj…Ô©kPqê 2·¿L2_H_ƒ•"ý @—¥±u}çӢРeà3lÃ}‘â‡ÈI–p¦cCSg^ìAòÁæÆHì¹T%?ÁFM¡qUA†é4ÎïaS&{bj>Y�uÏ¥böa6TîñAÊW$uNFÂG$ퟌ¤=“·Ç±{í‘pÈ ñ‡�¸Ã »�×e�¨6k$ngoÌwÌ8ÀÔä‘,{2 ï,@×™|6½–x „ùxÔ¾E8 Q¸ª2ë¦s°z[<¢¼È4MÍÁú]àð1#h'»böò*l<ô9Š%õ¥�ŒÖ¸©�Ã`j…6[½_aJªÕME1á3âŽ)¡©¨Z²…Óçb]¡Ó&»!ÑÆM+Öáö;Ÿ‚Ƚ7vêšD d÷è`\.­=ÿ.ϽÀaqåsaª³›ü÷ÅZ ÿî–×( 8áö»?[ÿ�Ùh_ƒ/û_ÿî 6µt¸áñ—!pþå¯�søc¥D+h+v,ºé‘yG?GTøL‘LÓ@ÀÆmÀ½y² +"›ÙF°ÙеßlÆÚ�ºßä�èn¸ù˜Ýí�Ý«d£gP•V‚…{™ ³FHY … EÀ%(spŠ&!ˆÌ%¤�ŸUš"¬ÆLØMh�ë¸V›‚猂ry¯/3Gh¹)«ÍYk…¤¦¿ND áÓuØsƒÅ÷›LÝÑÎæøÍVìVÒTd:Ò�?oMÓ‚C¤vC{±‹ 7Q˜M¤ ûœý°dm¦$,Â|ÿP, Ço?Ä9ya)E9#'6~BM¬Ä1Š÷3Õ6Vzyà÷ײ±H0·Á’•áØL›Ö�„udTAß ní)B íf²NÏÄâÜH·»#¡Û1dŠQ�Vˆn³A|‡¶9B@e§#â 6Ñ�ÖˆÞfƒØýöHÜ儘-¶ˆÞbƒˆfKDw0íÒi‡¸­öˆéâùv2A7Ì;„UÏ4?_†ÎkŠz�);†3�&A QÀH€HXPíw«°´1Áöã˜N"}8^ü2@ЉtôÆ¼Õ È{è¼R·Ñ�ˆÖB7›Ç:�îúR íúzñÍXPH†5EL:}™M ¦jÎÁIŽ®´² À#M�z�Ð÷ÛMcoÆÔ=Ë8ô¥¿^ÍÏ n¼öZ×eÜ™æêugêdwù²6Ád CñýϦ{18B`ðõ?ç5ùoCDit¸@ï²~6vb^îV­aS³§Ñ™QVÓ;lîúvkOdÃæï¤qÓ…CÏ\Ç Ejźýzñ=nô…ëø/Ü>¤ªá¸Ù£éôZ±Á’›d0}ӢƚÊ4ÓDk;¤Ú;!ÍÉ�j5WÌððÇ|Ÿ4e¦ccj øíY,züXüOæ5)N.˜åé‡9n¾H³š,Ï¢€ý<#=Þ §¡ã�lé£á¿« ²©'m½RŒ‚·‚1u·b»íÑJÖÖB kµâ±"Ú,„­Äm¶C4×èM6ˆÙm‡˜¶ˆé´AÔf 36Û %lf´9ð9¶r.n»=b·òZ¾gú1­¶XÓ8OE¡è‹EhïÕ©Ñä˜�¢jÏ�lÚz³Póù*,) G”Ó›cYû"È´ãÌf‰Xœ¿[úb4ï3}êLÀFTjJhcô}ÚM>/:u�)Îf¤M]‹@?ºVX"Â;†ÂWÌ OAÉô9T©=u·þ£a_¬wuaÖ§ÞÔUÂpü�Ñy9·Ø?t´¾µ¶o`?™ ¦læûQ`ý‰Ý?ý"“©ñ_üOÜo_ƒ©´ Œ÷6·Üé*胊'/ª=#š»³6:oßÛõˆ€›iñ"ÌÆuøß2ÍË^®!Èì (`;c2¾Úƒy�¬Cx¹ ë+ÜȪ¨D{bºHnáXçù‡BQm! $´Ø„«€‹–.«fº¬Þ\Î ˜HHíFRm6yJ4 ñ=™MM=E U–ˆo¶çÏ¿û¾-Çqq>ss'ÎáMô〳¸‰/ñFÿÂ"¤¹Éq“Ù‰=�€36fbé¿£¶ ïÝÌtŒ� ¤ g1¼bí}¸ôÕÛx繇0/0“ÌD4ÃÍ Ù³ç¡%¯ÉÖŽˆ›`…ø‰Ö"sŽ ‰eŠ‹jž¤‹Â�µJM“ôа>#±åFj>[�•�&�Éx"ªÝQ]¼ŸàÙD aDlb6±ÖAÐh'ÐtÛ€!©²¸Ý”™v$›±@IØç„ÄLµu:JÄuØÉó"Û-剈 0„%î�Œ©G<±îåT4_ËPûs0ºô™Ž¼0êÉGí'«0{¥/™Ec& °JÓ§5�:r6ÒÙƒCI¹&Ð�àÊкsEš1'?×…˜ ߢ%%Ö ©0£@!AŠçË� ,¢>`)2‘kä¸XVQ¬•³–SɨàÆYi�U'â±÷"Ç œcúì\¸Ð‚çnî"̼à<ãîðÝS·ö@:ô †k«’4�Ìfä8éèOušŒ·Ÿy}>ÃK�Æ–š6“=7VŠÊŒ*²R±>˜I6‘5AΈ„õçNBX7ê|2Ÿ\2ž\çȽÂptÊL˜bjm„A‘1‰€`íñdì¡[Á±Y|¿Â8C°¹Ò‚Ç(½~ãÎ1\Ç' à\ÄMœÁKwŽH½¤ëõ< °wDTiÉ”òn¯.AϹOXn^dJãÒÆ•/¹2xÜ{þüøý8ûñ«8Ø\‹Ùìf`Ý'œ‚q' QdJ˜º^΀aá_úg6Û¯•!ûÅY˜w()›] b›¹é7ò÷j·‘tYüÖg¶²>³“leƒ@’¼Ë Û!,e�-X§àIÞâÌú ÿd0’JÛçˆØ�kZù<+þŽ[ V»[„=±¦c�XVÔ9½�B‡£®H-Mz’vߪTAFñhS€§HS­]]�ŒS‘êë„dæù{‹ig*…‹bfbIÖf=~Z}F«ÙHèY Á„`¤“;ó>-Çc�T!‹€Ù²úG��¸ +’ça{v!îhu™Ÿ—&k£�ÄFTh†œúÑÒÆë=wcP4ÇÚú³fÎé[-l†µP}vÇoô¸W9ò=Áé74Ú|ý� °›F¦Ò®Œ o63ÎÉèK²ÍL’ï—ífÑÕFRgTT‰%�Ÿ;‹Ü�T\„Õ€+YÍ9^þQ ;Ã) °DD¶’j]°tOÒjݤø/ÒæbS„› `ˆ´™Z(i3I¥IªL•@“½dN”-¹³ŸC©´)¯aTÈ J¢E°á¡4%ÐV"“5ø ¼ðêW‹Ÿú韪ßûÚëù`g›ñxLAõÆâÍÿ]\ø_ÝÝÚá@Q†ý‚–�ê½§þÎîöÎb'ÏÊ) Þþ/—áú2!ÀöÂì|h¡.’,£óËŽsæ×<ƒd,{2X( ¾a:Içkv;Teµ“¸ßdÍq"D Ò7ÐoâxD €�P)ê­ (¯DÓHKesínè¨å°$Éè�¯`(«‚[õuvØ kÜfS7°b9ûMç9öè zý‡æ�PÜpíò5DLëˆTAi¡£p÷ö2?ýý?A7íqhUCæ.â±_÷8a* ÇÝÑ2_}û+\»u…Áh§€ƒ0òP êA‚ú›ØhIš ·U•D¬&ÐêP#Œ$1ìJ¥®jÜÈ!Ö`�¨"SÉ 3ý9¾ý[¾•s'ÏB¬ Ž[Ü`�5èÒႜç›í7Ó±Œ Q¡–šÞÉ>ç¾ë³'æHó”N·Ãtoš¾:Æ—_aëý¯aÕcBU¢{Ú‘•B†ö>ChŽ€v´ÎÜ!žºxŽO=ñ8çÏŸ³ŸI �V÷ƒ´@Ж4È> #åãÔÎ ã¢àëï¾5þ÷ÿñß-ܾu³[ŒG”Ř<Ϲôàû�´óúIçϾ°¹²Î�~±D6Z�8"æJ¿Ó÷ŠŸ?4??ºs+ÝX½Ëá‡>�&9 Ïý «/ÿ$Õ`€tªÏ±ßøEŽ|ë#[a4%%ÇI‰JÀjFµ³ÍàÆUÂxŒ�P{ê²Õ V°=áaÕ¢ÐêcŒ@B#””0ˆ4Û’ ¶oI;i3Æ)ÇŽçâ£èvzˆÇ#,*EÑ 8&QqŒ+þ=6ýFãÂE¸÷Áë—×#|‚Î=t‘{c  4jïrË¢à�—¿Î«Ï~ ¨b¬¥tЧþÔç8õÍg"(]Åå7ÞcéêB„e�€B<—Á ¬5ˆ1 ª�„#&B:8ß«@b“AšI$AbD¦©ÆñfÔ€ ¸€N"K+jP¶kAÀbùîïýnŽ;ŒX‰ï% "î±Æ,3(ŠÇsRNrÞžçj¸_8qlÙ|N¹PP.�ã\Ÿ`¦ßÅ;O÷ÄY:³‡ð*¸©…Ô´Š´L�ÇÛ�`ŒarÍš5´LI5ð}‡j.œ<ÎÌÌ ÆØûû~¹LÄ|#›tbrŸ±Ü'ÚÕ½}í±Xß8wYWüÄsÏŽÿñ?úûK«·—ÎdI’¥Y6‰P“ѵkWêöµkéKÛk›èl´¦ÁR}c<>”öºgççfæ‹õ»loÜ‹Ž`ñÅftýmž$Í —sö�ÝS‡ K‚Š‹ûÔy¶–ßC´Æ{‡¹´Y‰‘�z�@ò…IÓ›8Ù hP@@’ÆÑ" `Lׯ1(„: eb GŽa¦;ƒI-š+VŽgg)ü( cÆÌ™y*S±æV££cX¿±Î­®‚BÌšåÓO–4mé×J¹·²Î÷˜>»uù`8Fø}úþöGHçS �cÇïðî»ïrëÊu\YC€àÁ{ö>‰ �ÓÏbTӀʾöˆ²4'›Êâ1B]W1:òÞEÐi\&×#—8?Ð8^¯h Æb 8xø‘ÇxêÉ'E c"`PUT@€“œÁÄÅÒaоt¹®!"ì�(í&„mÏÎÛ»EdUUôºRœÉ蟛¤ìA"($¦Y»^[˜˜Öo£´’Id¤MÅ‘dÌkÍ™Ž�Q•iéµ�- ´…À~é}%²ïõý€$í¼ûÔþ=Âp4ä~âGÇÿå?ýÇ�b0833=›+JUU:57»±9Øùþõ[ úMW,q _¬°9Њwþ¸IÞ/ÇÅÅ€vç¦{=[�ÒÕ+ïP.¾Ïpm ïý©izŸ;˱_ÿ²nŸ „ƒEÊí vo]Çx‡ó¤Ú!¢ˆ‚Q‹/\óÚ’NRG™A€àBã|­4N¬Ôf­ŠÖ L§�VŠ–`¥qŽ®®q#ÏÅ/’ô0‚s5ózœ“ÉEVÝíXŸ)((͈cr‚5·ŽÕ+–Ý囯¬1Ý™¥®J’^Æç¿ùóô¦ú½pÎsõÝ+¼ð£Ï²¼t‡q1fº?ÍÔôüžG9öä \ê(¥¤’Ško^僷ޣt%A6·ˆsgŽrþÁ‡ØÙÜDB¦Q¤rдR¼ó" ©â ¡%˜’¤›bRC’§$’`Ô@ ÖÄ6u0cMŒ}HLÂ÷~ï÷Ðët1„Éuö ¢ˆ4�„’’>ÓÌ2‡Çá©™–inq‹-ÝB ,™Í ÖßZega‹Ý�ALãyïI­aX{òIf�’X!(Ѽ‚ ´Š¬Øƒ íº±B~)á°ñ<ØudiB+ýhı_r¿Z û¢“}@Ú¢´¹Ï|û¶�áC�ð?ô‡v_|á…r´³s¤*«¤¬Ætº=l'_¼òÖ[ÿ~{uíÏ¿éÊtP³ùÅ®7]y³.Ë?±º°ø÷–nß~¾'ŹóN=Z¾�¯kBìz_¼D~t/‚p|câPñÈlJ2×#x…Ç�Q¡�AJ‚ÖSk‘*é$ª±‚E0~’" AÑBmRcV@!™NÉgH·‰†B® ün“š‡8Ž–ªëÌÊæ³cØ`P¸µu“W7_Ák“Z�C"ëä v·ñ!°uw�•å•}N+€Âîö€×¿üõ¸bº?Cšf±ãO¡ǎ @6W6Y\¸E*Ô‚ZEƒrúÜ%>óÍßÄööš „$$º llšÄ†Ÿxju¸‡) ÖYl’@‚ ¸ª¦*KBá'ŠÁ+~ì©w«øÝñ:PA–$Ô¡¦¦F ± äÕAÐç+µâ ïSá�É’‘sRNM@e¨¨IÏeÌ=s˜©ÃÓôûÓˆãá�ñÖ:aå&›¯?�©GXi�‚5�âþD 5­ßöDhưÑ4&´uŸ2ía’”Vû >üPhS_÷Õ}°×Ž�û§ã‚*ï}ð¾þ«ÿôï‡_}ñKº¶tg®,K£M¤”NvsckóÇ\UÿÍ7]¹Á�~ÉÀæ8u1›üÓ­åå?ÿåý±¿¹øÁû/{W9H�Îräó� š X”ã¥ÆI‰—‚€ÃÚ;•¬GE ê�J .PSC ÓЦ0ÂÈ#¥LRX¾I­MYì|]P ’B"´Œ±LS¤6¦‰’�x'}oe“r§§¨*e³âor>}(6;¸¡çúÕ¼øÖó €€§¦&ÉvËFL´ºª(ÆÅ>çB„ÙúÊ+·–ÙØØÀËÜÌ,ݼǥßðù©NŒhü(põÍغ·‰¤‚íŸ=Å#Ÿ}’¥7YY_ÄÏgÄÊÍ‹¢ÔE¡¨A›”Yåp¦&¨FpŠ—èPð¡úJ˜±>&FÀJ˜8Áb!ýüØ,‰óŠ ª“´¥1M£G"ÍœTcF,rP5sf‹�'*¦�ñ$: >8¬µdIJ¨=ÕÖ*÷®¾AØÝ@¨B“)mÁa xRÓÂ$´µ¬´ÑP'pS»¬ø.´ ò�Mì‡OK>PÚ×÷“î§”¶Ûªã#òÁsùÊúŸþÇ­_yñE³póÚôp80UU¢Êh°³óòû/¿ú7V®]ÿÓoÔãUôÿUÂ�~Ñé�rè�WžJòÛÅîî§´Û}2I’ŸÀì·=ÌÌ¥“MúKj¬fPP ñµ¥ƒÛ. °3–P+ààÓ·h¥1¨ ŒªÉ{,èdéH[« €ÖÍmÕ(±!d:ñP—_}ù+<ýíOs4=ŠÛºÁE£§3¼»ôo¾ö6OxèÒ#twÑf‰]Tõ Ž^Åã¼ßŸì�°Y^¼Ãòòrl@aÊNqäôQæ<! D\¿|•�µ-,Ap•göÈaÿügX[]åýw^k>Ÿc-ÒdJ¨š*M}k'D¤y†ÒÀÁ$í6pAˆQcœË÷yë›è.ì¤&#¨‰oj3UÓD€€I �&)ÌDPmn|âØ�,ÓÌ`I¹À%^æeª¸ÔädäL!„Óßvž…;×o�b:smc�|<$ϧ¹wëG�ƈ!J „o,m„É:1-+Ú–ç6©µÙF„+®Ç™tŒèÇu˜Ýo¿¶«ý Ñû·:ß/_Öî—�p¨ªK^øòKúßþ÷ÿ(—M¾+Æ�e¹ÛÚÜX)‹òMBøGXóo•EÍ�`óKYFL/I’ù<ït|hRdóO]‚Ü"XŒÃh‰ðI4G}�ß-?´*àh ¡Ù¶g1œSD„0r`oÈ4Ñû�»ËüÐ�ÿhxîÿül¨«*cmÂÜì|µuïÞ×´ö/VEñ?5M^~³ô H£(ñÞÍúà“�äØ,ó�^ŒÎMT&Q‚¨A ( ”*?Â3Ý{.FÁ¦M瘈�¦)ÆK¬¸º@5`zT'è^D¤€SP.4sÇßm#Iã85(TŠdÀ¤©`°±Ã»o½Åk/¿Ê�ýÄ�ñýßÿ?ø©åŸâ¶Y¡¨÷Z²•…;7ðΑ’Òét‘¾‰í¨J I’˜. {ž�¶yo“õ¥UzY�~·�žQ5¢>0󆚚áî�ë\akk‹ª®p¡Fsxèɧ8|ükË«¬--¼B*`¤ÈÐã7Àäµ#@Ð@ðMçYH˜Ú`‡© Šâ|�1 ˆÅ (5„ÊÇTå^מ±E‘ŽidA õè¤eZ}€ FX°Ã«ì²IÍ.˺?[G;d4Q—ÇaŒ¡{¡ÇÑgNàóÀ¨‚*©/)n]f´r‹¶}™ÖÓ½t™6Ã÷¤´MF “´é¶WݯT‡Ó¾ùþÅi"­™¸þèþýRö¥éZô¡Ä”è­…þÕ¿û×üí¿÷wô¥—^P#Æv»=cŒèp¸;¸yíê‹ë«+MÄüù7êñWÞ ú$D6 ÁŸL³ì¸ˆ¬Ð}ä$Ù©ij),�€™@Gâb¨1X½�/Ëè É �Œ•"(µºæxÔ)’K4%š°WÇ ¨€‰`ÔƒI b¤Iÿ¸ üTÐL›9kP%àÔ!^ÀÓ]ëË+?{’s>į|‰à Æ£»÷82´c|œ¯×éGÛ¾·‰wk Î9nß\dáÊ Š¢À»:¤s´ÃñÏG¦ Njn/.°½³E™–ˆkNž¸À/{ôaʺàòÛobæl£Á@¥Ô£š¬“"I‚™µÏgÓ¤‰ŽÒ©Œ¡ðM¹h„�Q‹«=Š& �$D(y ˆ€”àM ô‹ K<ðÀ¥fDísL¦IÓ¡Äùñ V¨©â²ÎÇ8ÎjXÅbñxAð(#‘Ï9ùÌÖ^½‹ÛuÍßl ¡®Ù]Y`óêÛôÎ>ŒÚk P†¶åÀÚg\ åP·`¢Rá�ºÏé6ˆÞ§¶B ˆVó,LÜqÿý ûêBÊÆæ=ž{áyž{ögY߸G휔eiœ«ÝÖææR9½è\ý—“©þ•W7V”}RZŸôé©YQç~[’¦ß+ÖôÕ@ÿ‰sý®Ç›šˆZ¬Xœ)°š EUܸÆh}“º ˆ�à|„^›ÔŽH¥I�% D•茨š±Úx� °L³� ÖXð“š�WLbâ¶ j@¤qœ&|@«Ðœ_•¹ãG9}ñL™±²º FììhQŒüöÖÖöÿK›Ãá¿VÕ¿õz5¾ug<â@Ÿ¨Èæà:CQ<#b~kðþP]VH7eê�“Ø4#àñR£â5“Ȧ‘`°.e¼q?¬À*$¢¨ŸDi›Œ× T¡�ˆ 4àÈ,‰µÔ»5’H%ާTl7A­Bì^'[ÓmMŒ�T•º®Gé8ÅËä„©PŽÇ¸PÓ=Þ£{¤ÏhwÀh8dqñ§8ÁÕíwéæ]jWScвlºU–w–â¸í�Mú½):Ý.ýÓSLÍO#*\¿~•ÍÍ ‚ÄK„Ú¥ÇÇölŒ&n/ÜlÒ¥¢e@¡)ö‡€4ð1*0epÏ©ÀWíAÒM�$‰À2´¾jP¼k¢3Œ65'¯ 2ƒÍ Aø Üټͻo_æÓŸ{jê*Š6`T›§(Õ@IÁHF\s×(|�Á��PR2f¯�«uZÓ9ÑeþÁ#¬¿¼Š–MýËãé‰0^ºJµ½FÖ룀5´ÔFÀ¶¾#-\Z~ÐŒ•¶ÆƒUx£žâL2"7ͱFí„Qò1 íÐ�k2ï=£Ñˆ»ÌøÊ«/óåçŸ�mÞ¡vNv‡ƒÝ�ÁöÖšA~4M³ÿäS½òúx7p OlôTڙѺþ"ò‡òS+’ ‚@Ö¤ldÆÐÉ»œ9y†n¿Çòâ2hÙ@ Ul’ hÓž«�:Ô à‚"¡�÷�@ (v ¬·t“™t–;�ÂÎx‡Q5$Or2ɘêO㜧ÓÍ¡u. ›ë¬.ß§ôº}j_Gp>ôÅ'Éçloo³±~‡‹OúW¾¢ŸÎòð£�“HÂp4`uq‘½¨-± ÁD0©(YžÆ}š) �ÚŒ0nÀá£Ó˜`‘IÓ€óì$4�L¨©Ñ4à®^Áæ–  ¸²æëo¿ÆÜ±9Î_8‹1ƒ àQíÕí¼znì^çÆÆ­8N*©Pšë^Tãí1øŒÎÌ Ùã �òÎõ¸"KsÊbˆ]]dwy�Þ‰óˆÍýÁ:4mm&(-|¤mt¤ýߊ íO߈À•ÐåÅò0ßiî‘|c‘¿•Ü/MÖr¥”R×.>ùkq��Á¯¾ö*o|ýk¬,/3îÒíõI’D«º*-öÎp¼ý’U¾f�ŸÎ{½w¿¼¹8Ð'6ÿ¾ ªMD¾5Ͳ,¨Úáh7¶×J¿[xß: x)1XÅjÚü2tYƒk-®rH*˜Ô6…i¤#H&›*ŠVMÇA +dyÆÓŸyšÇŽƒÀÑc‡yþù®)r‹�<‹“Ljðñî݃Bçj‚(ªl[?*Ý8:ÿ$IIl£Í`8Ø¥ÖôgûXIb#CЧY–Úü§ÊÚÝ5†;ĺÝ.upt溾xÛ·¬½¿Æp8Ä5Z…x=N_¸#3�g{iÝn¢‡ÜfÔ�:nK-hÑ€×çMÌx‹© F!¨GrÀbQÚ §H%1=J£B#&�6üÄCèjSë2ŠVMT)üÌK?ÍcwŸäég>Gj&Q ‚Jë}Çõ˜n_EsaV "Ø:tHF)ÕVÅÊ `½†®�Æ�ÉH.¦¾²FY8ïÉ;¨ v—®3÷ø3˜$CÈlÛöì´ÑM›ùrÚ¬EÀ4UúL\q>ã-óÖcöÃdæLõ¾™´Ú9ª¢ˆ]eÿûÿ/nÞ¸Nðží�mªº&IR¦gfAŒ;Î×õvUWUõØ$ùñ©©éͯ•Ã@9ä@Ÿ8ؤʹ®¿ø—bÌFÄxï!’,‹ÆÑiòÍK 0�K@€T{x©& ’&Í3®¡Ö&2I@rÛ°‡ ¥"`Íꔉ"˜ÈÑf§f9zòY'ÃÉéœZ:ÍÒõEÄ4µrÙKc´?e#D€1$‰¥Ö›YBN•+¨|AÚɘ><ËÚúmÄ ®¬© ‡æ°³¸I1*¨ª §I<”HLÑmmnQUuü4k„$MÈwèë0.Ǭ¯®Dø™`ð¥�QÄ‘ÇQ£º·oÞ¤®jÔ…8Ÿ$‚f 6‚&ÂÀ j° HW|å"\ÅHœÓªAŒÁ'“XÔ\á°Ý&Å(ÒÀ aRÇQŒ1ñ|(q¬t&ÕÈóî•7©“ŠO?úTü ›ÄXŒµ{×9FO;ÃÿçÅçb“Ãá“G8ò¡!D%$1í8þÐ*/¤•ƒ`H÷˜yæ×n¢ã:Î5î`v·¨·×ÁÕ�d` Q.ÐþFš�@»_µ± ´íÒÒ²d=$¼Tâóù6G“ŠÔ ûþo¤B5I›nnmrkákܸ~•»KK,-Ýfck“ªªH³Ä�ÛKH ’$€V¡ª@”mLÔàK‡ÁÄã6±“çSR:ÒÃXFEKÀ)�æ<b„à�‹N§ßIIÒ Ä€6Oòß»»J–fäYFQ�©ëšù#GÈ�wØÚÚ¤W°¦9d‡ºÌŸ#à)Çå‡ã—#4Ñ2$5ˆ(eÒ@Pa{ y¿ƒ¦Ä¿-L"Bq VšTâ¸y U¥¯ÁzÇ`ÀªE%5í9tÒ4`2áêÂ\yÿ2Ç��âøÌqTÁã�‡lìn°3ÚBD0I¡[�k¦ºSišÖvWTcÔ'„ºUÄXìt—ƒÇ“æ9ÖX „º$Tcl·�¢U¾�LRÛýU¡nk9q ÍZ¡ÝV€öyœ·}�«ã.¿µ»Ê©¤ 1íýëëëܹ{—ÝÝï¾ý6£Ñ�ÅÅEÖ×V'5&��‡i'G�!Ф-kWÕãÑx·ªªº+¨>—eù?ïOO¿õ3wn)ËôI…ÍA}fZCø+Àïcú"bD,sçΓú’Áæ&�='•à¤$àA@THÈ1j©¥ *¶ñãrRXnáv: Ð5˜¬)ö«‚Ùû=´èY{ xua}u•zPC  W‡›>Ê\žm¿…d Šê×biRUÎãÕlÓ‡*8@&ð.=Ž?…5Q‰ ®y~…B v¨\ ©DÇ#UY±½¹Ó„Ó8ðl6�N±ZÄÆçêμ›séâãtû}”À½õUj*Lß‚B�nmHpàñØé EÄ@Ö@§–ä�œt*Å%ƒÅ8i"˜Ä·ÑœjÀ�V,6µ“´Y 0©�Š C„?Añ5&³Ü]ZІmÎß><+Í|…Øy×ïõI5eXÛŒ†#œÏH§!"ÑòóÓØ¹.ÉêWÕñ³ÿ—½³’ãȺõw3«ª¹‡H#4ÓÊ Ù^/ã^¦Ÿ™™™yïÏÌÌL´o/yÑ$ËX¶dáŒf4DMU•ùôndLF‡3L):º»ªº¦§íÉÓçÞsÎÝZgýìIëKÔÇ&ô<�<1= ôs`H<…~¸ ¦çAj`„¿Úhss9`|áiæÏŸau}ñž�~𬬭’¦6·6Y[]Vpé÷û$iªÿýªY…Â)k¦=2æÖ×V¯,/,œ®¤Ù±"ÏŸ.Ëò/I“ÓÙXvl,³³ý 6;@ƒ÷ÿÜÚäKlbSç½Á{H*̾æí¸÷<óÇ?++cHRƒ7%‚5Ú@ÁE¼Á�¢ÞqxJ|QàËBû1”€G›Ñ¾T&Æ„ò™a( Úå.ÚJ¶Íšý�>�~ô1>ám¯!M2ºë=- Ýsèn>ð±÷և˂ ‘0¹”ÍX‹Nßáðh�$« 3ÚÜ7ƪarPôUÚKîÙXÚdck#ô¦Ïž8ÅßÿÅß‘e™övçè÷ªXC<G}¦¡^›Õ…eúë}¼øídì©ÝÓ¨¡ÕWè¯öÐ~’–ý ȦeÀ¢ š�ËQ¦`20F4³­”*F°*…`Ûóà5ŽNÍD4>Ï lfµì˜52ÚFpÆkÃØã±Î"Óûö365�–"=¬Í_Q ǃO‰^ÁB °B5VF/ç tŸ.èaК£DÙ†É,¥ú�3xa{²©$ÚR¢¢‰ªŠ4ðx”Ù =Ÿ¨‚÷A¼ÑCEÖXJ HDA^ ®¢Ÿ1y9Ðc‚�û\k/¨Á×»0ÚWªÚÍæ‘‹P–ù€n·Ã`cqI’ År™A·¾BY-â Øð ÔãÄ8¾¤ôQáˆ+u¸_±µÊTšû¦)¶ÖÉ×úœ_œC™tžã�×óó~O•ŒSÓ ö[ý.µZƒzµ®¬1/òJ«Ý~ûU»gk}ã7^R–ðõì¹Ï]ôìlÿƒÍŽâlÏÏc>9Qi¤Y�J{‚—þÀ¯1yã�ä¥ÓK“Tt³bÐÞ…)ðBèÙTÑRšxï·*㬫à‹Ó4àÁJeM‹f™å·^M�àz±:}3¸"¤ R×&7�>ú˜¦)ûÄS­TY¼¼Hg}‹²pˆUP 2àP+ ‹th± œÆyÝ—�dLì�F¯lÁ{0‰f‰éb£×ëZ2©UëÚÀ/½`“ŒT+j ÕûÎÆ&Y½¢2íöäÝNW^“jìŒÊœgfg‘D��„„�Íuü ¦üH ¼"Z¢ ½+¼ík¹rÛ„iR � #h´L¯Àƒ×ß_ÙŠÇÚL`©bÕl©å»TÂïîCVš–íB(¨®òHî·‡¯QÄ BĪäœ�ÎmFHÐ-«•šF�2eÀÔÀŽVq!æ9KSM w½-›kš­'i‚ó‘Á”þZã¦0 o>‚LÜL‚IÁÑǤ •‘1ú­QÖÖF¸´º‚[ÏÉWVñ•qªc #ûnCªutÊišÑ__Æ]¹Dgñ"Õ´‚/ e¼e‘P¯7ªÿ [Iï#—3ýõ­¿¸;­þ£ùÈ“ƒî€�íÿ3°ÙšEä'³jõ€TDÄy¨ßý Þ·0qh¼±Fϸm抧èvÉ[¤Ò@ð^ïKr�B�!� 1Æe§j²,=ôÜvùÌ'‚n*Óë¡=ŸœÀˆu^o¬òÑ�|œû_z¯ÖÏÏ�?ÏfwÄë"˜vĆþ !U@@P†D i+#kW©ŒTÑTjuö;]°Þx0�Jªygv ï¼6“4« 5¡Ž¯�³´¢Ï3ªÐ†ÎÆÝAg=ä�Ô2Z£#Ûñ1›ýuºƒú3SpEk1Pª 9(í@ÐÑÙ¸Ä)�ºŽÇ$zLKƒ¥+µ7E ¦F0‰X)¡è¨973� Ÿ™;;•d7R4ÅÁ¢rrŸ£ klè¥Yð/ ˜Ê@(È)0Ö �‚n¿ƒÅP–š†O�‡ «) 8œ€Ïp†0+©$M5_Xžã¡xHŽnw Œ–x‚" $÷Zz#’Ê\ÙLX‘ÏP"Ós·¼‚t2’bÄhˆ§)@E!׌9“iŠ4ÆH˜À 䀪¯1ËxNÀ� iÚ®ô ~Ú³‘¨Ì|… U-sv]-óaØØG3ˆ¥qó ÙLS{Râ (á(»8“Ÿ]ìÑè�ðØÆ†?p±”‡¬é1¢H@„Dw.©ÒØ}Ý5hìðtœ‡jc„Úìõ öS`ï+ßÂâ‰ÇX<úaÖ�}Œµ3ÇiS5–Ažñ-òÊO|%õ±6�}à#ÊhŒµÔjudÒrÝoäòúez�.ˆªµØ·ÿf®;p“²%ƒUFsñÌóô·ºjjOIÁ8ÁxõÎàc—7PÖ˜%q ˜ƒ 9¨zPåΨǧ¤Tæá5Ží2>¨ ¯~%�̶}K ú»àõ^KxzžS@T¿O£ÑdrzRÇb_¸p^7Kï‘JJsßMZ ž’2ï³öÞ3øåž–(m’’6ƽõ>Fo<içÐ-NqŽ��häŒÏ‡ÇGG€ºfž �˜È–ôzEÀè8T0Ö’ÖjÔw]ÏäUàÙuß«©ï¾ž•¥E–/>OÞëÒ¬7I¬c$Ë*Ù¸óê÷{¯£,g'<Ïïk4×. zž�íÿ1f³£:û¡4Í>½Þh´Ë¢Ò*S|÷ù÷S›¦tÃtÖ@¥5‚ͪ¸$¡f ý…�+‹dšÑT_<�nizo õˆµø�^@BÙ#(ÔͶ`€ð­ 8â(�7À“ ÞÁØ(ˆîÇoLbn*`Ä€QÖ¤¬HA*®{AÍwS{g©4*aˆY0@f�5+úºõó �i�Œ2½kšz£N¦å ablŒý7\ÏÂÜe6;›9Óã{¨MÖ0sQa'¥PͪxñäŒTQPF2!©ªë‚T܇ŭÜÔI¥:Ÿ¦È#P˜̦„t$Õ²šÁBXЉ½/�Ôâ­î‡h f0ßê”O1$Í šöàñzRý>K�õê?Röƒ1zMT$ …š ‹^€KG %ˆ—àÍJÈÒµtŽÌclF™0â1€˜Èh ´ä¥> b¶h"Kq±ô6Tn“mpÒ[dLzàZùtÈ�EµÔÄ[­’VöqÃ'û^þFO>Áùþ ½#ïGz› ºÖšdtt¬]¯7jW®,|­1æ3ËÁàŸîJ*+Æüƒ©U;�¯­xv¶ÿ[Áfg;<6!ÅÆæw¥Yö¹Õz} 0^ £÷¾†~Å*Єhök: “¦ú­ÛIl?%õUU�õeCª�#Þl;K�†å!Ó²aîŒ#QNÊa8WX À÷ÊÂø§ÇÑ^BÅ„U½ž¯ jÁ4}íÀA/\[Mà$ÖZj fg÷“eÕmÐ\¼8¯N~jÍe?çÈ{ÕòÒèȸ2ƒ[î¼�j­  µÿZ½~õV¥Ý¡›w0ÆÐœnQ–¥æ�•ƒ2L õ:nYKŒôg¦Ž´ž"�À¸ªú;S–NYžö’Rí¹`RM ïúØLYI™h uá HAž„”ƒ2=9͇þ뛫ú¾ÊŽ& ( …™?”¾ÀL ž Pˆýrgmþû-§ ˆˆJ¨UéV€’��‚=‰µ*�î ºAjRš©Œ! `#˜jF¥YSßJ¯4êU²jI*”Á„™Æ‘ÐÛå5«¤* a3œöãA$–Ò à"3ŠÀ¡7¢ 0Ku [¢ÀÁGÂ+†¬9Êîïbæî—sù©�ròŠÎ‘`Åé—–V³•ÎÌ̦Ý^§¶¹±ñ™ù ÿ`Yº§Ê­Îoª4þztrrí¡‹;}�ÿ Áfg» 4ŠÈg'i:Vä¹1i•é×~*÷|þw�ŽL#• 7n(&­�Tx�š%Ð;µ„¿ïÈ<‚𢽠©kQ«:¤¬ F …�2 n+L‹Ô² èãÀã;P¢ÇTªÛ6::w!_íãÊè'Q1� l„npÑX�ÄÄ•AC>-•¬ÆÔÄ.RRÃæÖ:—/\ÐÅU“ž³�•½…MõÏx卑G!lz®Þr0&F'(ÊBE KëàAûá�7&šaÆ‹ö]TâëÐ-‡-4FGKQe¯DøÌƒA=2âK- t‘×~ÊÀi _(0Nצ¹aßõš5öÒ—¼„�=ù1V/¯@æ°¥n‚¡$­ePC¥âV¬~Ž¥„¾‚$±ne°`ÕW}p•:£Fݪ*’£ Šƒîå.ÅV�4(�Ò+«*ÌÎ 0.æU’é––OÓ$¡Ès .��RÐòB8ydß(øp/«‰F÷Ç7ñ¼Ä ÷‚¥ ô˜GMÛxî�aT�a¸[ò§î|‰–¯ûÏýãï³öè?±¶¾®†×F½™¤i–ôzÝjY–Îùþ,?‹~ñ[¯�Þ÷WïY8¿ÉÎöQÏfGpø‰¬Z½ÁZ›`S·ÝÇ ¿úŸÓœÜš§h†Fqèͱtê(›çN`ƒ§&Û5ÂÈáëI*€Fû ÁüV¡¿¾Eçò0•Àb õz@`6„^ar' F�™ªÁ$±Ì&^{=¸A¡‹!MP¾^¿¯½ôuÖëÂm4ß㨠¥eÏõ7p÷’¦Çܹ‹œ<úΗÊ"ö_w=gÿì ›ÇÖ´ÿÑÏûì¾aŸôÆORé5Þ«ëýÔÓ'9úÈ“ z�o±‰¥±»ÅÄ˦´Œ¤Rq©IxÁ]÷©K~À€ŒŒ¢›³pù"ÝÍ-d òèmpÔ$ƒÀˆô³À`s (€!¥QÃQB ‚ +²×‘»ìfyy‰V½ÉüÂe-ÙYcQ¦QxýÙ(¸ùÐ]Çx¼€~v)áKˆÿÝ}/P‚Ñã³�¯&"dY…}�™Œ3ÇžÕŸçÚJsšêô.¼-0>S&»ñ±3tŽ^F�Œê¥ÂôUæÝÜ n{ä8±”~ÕÔ¢[Hè [d.–»â¾Xb‹à`Ìp9 ˆ E|­Þ"ðÖ5üÜôóÝ{»ï}“W§­~��s§ØX_Å{G¥R•jµš4ÍzbíÞn¯ûÊþ ÿ’Ýi%Ý•$çŠA‡�íÿpf³Ó§iâý÷¥•Ê­éÕ c±{¸ï«ÞIkrVÀ ”.Öµ­ˆukêMêÓ{Ñz¿~@iU%ж]Åú4L²ì¡ #ŸF¶“8Fo¼‰•�áéãCóÞTÂöJ!%`T, @#"¸Ž*³P£æz�%§Æ„�è½ï{0 À“ÈöµÀcjºpÃÀÓn�qÛ¡»ÔS�«ÄøüéguñÆ@ZϨ'56N®P”ú3µ\vëUpšƒ²DUè5k5D`yå ê3ÁÑ/ú¸Ü)Ä‚A°X ŸÓ£‹ Ó-QVa$H®Ã šÒ c²ûN÷ÙšA¯UÛ²˜~�ƒtœf¤)[IÓÓ×1p}.ž½ÈÔè8«Ë«Œ×ÆYèÌkŃ.ð^ص¤Æ Ô«ðhh) Zf+ÖHÀrçJ§Ï•‰)Ý@ß·/d¨Ÿh}y gõyH‹¶T[3(H¹ ‚æ0 ïö)\I*¢)•jJ÷ʼ– Å$”@Q‚!2’RA0€Žf4>–Æ"@ɵrh½†\[NÓ}\Ë’Œ�e6ˆ’l|ìù(¾ŒÌHm¦ï¸�©[îbõü3œýëßdñá¿¡×Û@'¯‘JV­ÙÔV _~bQ”/Ãóå/�zèêñß?ñçŸóìlÿÇ�ÍÎæýKŒ1TjÕªCîàŽÏúF&n¸D†Ô9…ö~£Ì÷†úØ$Y{Œb°©RØõç.1XÞ¢23‚Ãã¥Ø®ó `I¯×¯�OS™˜ ·2ZóJ°8ܰbB&4²û @¨¹Sÿ¥Ä²Õvá+=ÃA¹îÐÅ.þþø áuA€`0Œìš`|Ï9}¾¹¶ÁüóçAÈ­=â MR�޹Z‡§¾9¢½çÔ`Š.èÖÓ+:´h>”Ï ™­€G{3úû»Ôég-™€ŒÓ œøVíyJÓËUe†ñFoÊî<Ý‚úî ‹ :“§Dû@ˆ18È‚§Æ@îµ\ç¼æ�E)uO›#øzèµyÁ‡4›%”¾Œ‰Ý¡¼F•p¯o­ë=AÜ`k5ÒvK™pM� Ú”vtþŽ#I2½N¥=†›ö¡¼e%2ñ",êgÚ -"§Ç"H0òþZ0ñQB­Ç=D|¸FåÕÛ›ó\„HÐ{‰Ç 21<�eLÞx�±/ÿ^ý6žÿÀ_³üO„”"B£Ñ’Z­¬$Išz篯¯Þ‘ù§­¯®üþ‹Ûï)ŠüÃï¬ØÙþ)£í°šqàGª�ÆÁ,ÍRõ¼úúô¯Ö†rœÏÝ×" KD=#:—fáȇ�Þj tŽÖ öÓ¸i*h‹A!Ñ…D°€àq$’�X6.žF*¢Å"º>.â)®ß‹±ö‹Ó,1ÆH‰áî¯x'íÙëq±¶Œ‘Øð ×¶½ôìStçÏQô»Zò�©ã‡oB2´t†ÄAj^r�ÀIªíqݽ¥ùíº‡ eíÃTMT¨%Ak$¶0©¨@_·»�½ðHüšŠ„ôh=· J51dÕ ûo¿‰[¤–5t¿xæÇž~”"Í•]´l‹éö gÿð9Šå\ýEi–òš7|"‡8LÈ1Ûþ¯ðäcGÔø™¦‰†sjÒÀmÖº«ú;å ðÞÑšg|f‚<äÊeT¸ôüYºƒ-t”µ €ª ƒPZÓ�} £¿;Ö¬³ÛIÍÆ†ù4™!©h঺÷7×·Xí¬2H`zš•†Õ¹4z¯€"N´üeªV[‰ãŸ•±”`zÁ¯”*Pm׊ôZ¥ í‘QœÅK‹hiO,.1Ô&wSß"zMO @ÞÛbõ¡Óø3 Þâ µ]7²÷e¯§>9Ù€ �þbƒÞÊ13 0@<A ô+%zlˆ§GS¨Dåüð4iOÁdXˆ@œ¯3tÍ(Ù6–æôÆïx€É¿^üØ4ÝóÇ]wmU_R–%ÕJÕ´ZíŠÇµ:Ý­ë777?9ÏóOÚ›U¯ßW©ûµæÂù~'ggû_ 6;ÛT^¾¯Ú¨ßj�IKûÞþ•ÜüÚw`¬ å‡k™Œ«¿™eŠA—…§>N¹µ†ää›=Fî½�ÊLe0d!¦†ÀnPit)’X²ö(›—ŸÇ—eK$A5Ö�«ƒŠª(¯@eDX§À É so<¬|éÐÇeÓûöpè%/a|t’„Tâc¿—•ÕE¼Õ<2nš¹™µ§×8õçÇU-#“£¼í3ÞÆÌ®éíÇÕã²±±ÉS�acu�^¿G·×Q“hûà(y« Ûéèu]át¶Ìôž=Ê:t¹¥ Öi²påΕ ”TB°i”l�T#¡/‚î7¡4œýÞx¨Š2�<¨ªk½·F¿×Gq>ÈÓ¥TÆ‹× ³€ÿ -ʼneT:‡‡7„¡o×+¶Ó ]hŽLLMijyc ±‚Îò1 ͽ7’6šÛp‚Aá©4l=q‰Î“s4ë ½îØM‡™}Ñ'“6GPЀX*‹}�áÞŒ>6‘‘�VªA4€Fµ‰Äã>Þ† ŽœŽ ß_üùx}<Ô»‰–‚p¾! ŒU_ÛÄÍw1ú‚—HubJ½Ž7EŸ­�5ézÔª A$m�ŒebdÆ‹ÜoDÞè=¯Ý�dì­Ö¸©=¶r¶³Yîôlv¶ÿ©ÛÝ•zâËò­Íѱýõf½20±—;ÞøùšF,MÃÕ¬èÂ&üÁöÙDóÒÒö½Ëçò¹UV=ÍèÁªB+}ºye7qSùéhƒ‰;_ÈÂcï¥ÜìjÓŸ(R©0¹Ç4‚™³ÌŸžmŽë–áúDEŽ›°ÏÓ£C ¿¯@662ÍÝ÷½ˆÉÉiR2…£„Å‹—tŒX&ê“´›-NüÕShïÄ:Gýê« ´›î¸o â=OXUÔ)¿{Ïž?yZ¥Ì6M(9ùü€úž+‹Ëè‚múš÷åˆ +˜=°û\JÞYG[.è¿ €pš%çƒQrr½žÑŸ>[7H!ZÊ£PFb…D§Gún‰ï‰àe/ƒ/)�ÃùRÌ–6$M‡˜5Û=!_¬µql·Épb‚œ'IRen—/ÎQ¬ˆ^ב´3²ÖxÁ`‚zqš¿7Ð ÐÁ �Ï ía«W(=¼÷x°C2æ:¥‹³j’hƯϽ$ÆÝ„óÌ5Li8 G"“ÁøÈp"èÄ>g|},Í ÃPˆçy�E››¨\zäÀ-ìý¤Ï•åc�0÷þ¿àÊÓaii#‡W[š¦IQäµ²t“e¿¼ßˆYõÎ?ü’‘É÷áyºÙl=õ�—άî0›�íø6#f7ðõµFãÎÊÕ­ áà/»î¸/&65}œ„?Ü8é8Þ â�z“�ó§ØºtZ§( B¿Ûcâå·“ÕCÃ\|ÈÌR‘.B¨óëÞ„¬ÙÔF{wq¿Ý0ÉL�%Z¢ éá_ã@|�G{çc-½ðz®­é¨b¼÷z^¸€^jj7/|Å+™9° Ê6Ž~øž}æ(e¥@ŒÐ¨6¹e×-\üûó,><¯éÈ#­Qvß°—ÏýÒÏÕ±CE}Ø œ;s–玟ÒX˜J¦CÓHÆSj‡štº›ÛLD¼PmÕi�k Ì‚3ÏŸTµ- y/GUXÖÿŒ×x‡9mñ¥J—)»N�¥’)8) ¸¾CmÆHT*íJ|ˆœ¯…ø`½FVÍTé¦àâ4]@?WB>Ñ8£��xÑžj½FRO¸²º EáiNÝLcj·ªç¼è PvC±Üaõ¯NP\ÜÐØ——4v`æÞW©ÀD€2zc`˜ÉÄx¢kKT�”„zõ0ñáüm@‰b‚,zK zs€/–Ç®D ‚a€1‘Q!qŸKZkÐÚww¿œÑ[ïÅŒïbkiÉ \‘cŒ¡Z­KµZ5•«›ˆ´1r›1æÕYVym­R}ñ­£S»ïÛ{ƒ{`ÿM믺ýPþ wÜ+ï?utlv¶ÿöíÐȘø<k¥^{£ÕÜU8¤vã=ÜùïEõF †³ðÇ“Egtô"ÄÒ@,UX«±5Ëω[퓈ÅJBóƃì~ñëHm|ì§ •Ñ Š[�øXÂc†¥Ï^ï<¦t×€Ó5¹jöÁ!ˆ¢'-Rd[ñz±¤‰xô°±}<{>N8Ñtêæìfïz1³÷¾’±[ïQ†¼±r£32úý.Î{QReLÅ&Éx¥R½ùêý+«Yõ­Yš~¾ˆ¼¥^­|ǯÙó©/þDûà�ìÿ„Ûï­Î&Y¯™8×Ýt;`³³ýg·iGø¦z³qH ’;à oùbfïz„4A‰3ôÍ,w±tPFe Þ‹–ÑÖ.žfýô1e78OoyƒÉÝNÖjà<Ž2Ì·°>�пÑE�Í‹ÏRö6öqS²Àhô"¢`†mš«�Ãoá:ˆ ¶÷«ÐÀº°&ŒŒMpç]pÏK_ÄØäëK«<öÞqêé£ú\´g°¯½�Úr…g~ëÝÅ.€‚Ưz ïø¬·“5jšŒ„Á*ÆyH®\Yæ©G�`¼ÑR’óN†íyå>®l\Ñþ‰ó^? ÏØì$•zC`ÇÒòeõçà”™…(v—m˜©ùeÁ:/Ÿ�É *·¶ï4UAúà(¡ €‚)9èÜJ¼¹CvPŸQBÞÏq.¨Ô2ð‰C ѹFúÞÑÒÆ©*މÙ]l̯Ò]ÚÄdFÁ´2±‹úî=Z’,ˆÞ Þ¬['çXÿûçè\YgÐèç>rûý̾øA|ZÁ7ú�Š rÆ©œÄ,4Û|HçŒ÷ˆî$‚R¼N�¡Ñ :T°&‹ ´x®ÞÇ*ï ò>–m' õSs÷iÐëè$c×ßÊì‹>‰¯~+®5Áæâý΃^Äè0¿JZ‘²,l{d4«T«ÍJ¥2Ž1û¯Þ¬7¯©dÕ·xç�¿tö3¯¬,~ÅÖÖæ'O"̓ͰÙÙþ“Ûî´2f¬ýªj£¾×{luß­þÜoÒo‡.Ö½8ÀW„ 7좢ÑSTª©Þ‡õ3Çè-Íéb–¯law�P¿}J]á¹éPš`0Û�S�Áâñä[kl^02_WÁæ(›k›øÐ¸¿w’Êž*ëƒu¥*ð‰°kfïv¨Ö¬³º´ÌÖÖº²o‚/HIT’ªÁVõ· Íyí© !Þ_B’깤õí]á��'‰AËTg»APÓ¦ ’iã´$ƖΩ yme Þh©Ð Š0® ˜K*~ 2Z¡Ô¸E¿ è9š7Ü¢>-ãS”ÑøÀ ±P Ý'.³õ‘Kø­�¾§j¥JëΗ2uÇý¤•Zƒ(y¦b‡åÏV†ïqá'*§�l'•ª:3ˆçÆ2×5èøØCj#`Xª˜8~íëí0"<�›î¿VÝ–†¾(êëL’`ëmÆn9ÄÞ—¿�±»`P:ÔOŠ‚Ô&´[#¨1Øéú’¤‰m¶GjÍV»‘eÙÕ[e¬;è�öúݱ¥å+#ÝîÖS—]qllv¶ÿäv Ö|m’¥o®5êSÞXö¾îsØsï«ñb�P 6>‡þ`õ6üGǰ§AÔà9è÷X;õ$¦Ìõ„�g.�]7Fåº6F’0ßFeЪ®…Ê¢MN¾¹Ag©ßˆkõšÖèµC”.û`Lj)6KAG !2!ÒZ…‰Ý»¸ùö;¹ë¥÷sû‹îb÷�ý´*mý¦¿º²Âþ(O|ôC¬¯.£ 51XhØUÛÅÑ_8BïØxôšã|öWwß{ÏvB à�‰áYÊ.¼ªÏ.Î]âÔÑ }U¥%X²‰ 3÷Îreî …ä¸Ä+@”ƒ\gÛ$õ”R–`mÊæ•UòN®ç`PµFÓHx!ßÍzJ�Ú#ØÒê= Aï´Ì廤Ô×+û ›Xt.NYèµ”V2e›Ûãt$³r” ÆK‚R�í}¶šÒ¨4é¬o"AjN¥N놛t4$x)ãÿÞ⻎Í�_`ëc`à´üX úìÓÑÚ{3’d±‰{,Ã="s ²ò¡^‰5Ûû�Æ«—ÅùÈ^®!è-Æâ ƒ$ñõ‘q UJË‚Ä^0 bQ&ª ÕËíŸ!ØJ•‘]û¹î¥2ó’×ÑzÁ‹°“{éôû8/:î@’”±±qï�++•Џ¢L2›Jš¤¦p¥Ÿ»<·ÚélüåeW>ÃÿÅ›°³ýÏ6qfxÿ#õVë‹j�ÆÄÀ[^õ®¿`ò†;†|eœÙ®�côZ�‘xÄšygiŽÇëGYxïŸâóž. öŽnýþwк~7‚)ÉxRRJÉug~Ž þ|¾¦�íû^ô« Ëœzî$ƒr€ àÙÎ=«4ëÌìÙÃ̾=E®ók½•‰:cõ)FÇÆhMŒàÒ‚^ÑE¿…w`cn�3Ïž`îÜÙ ‰Ó9-ˆ²F…›n»•ÙÑŽüÔc,ÆÏþ‹ŸbùòzÝŽö;Æï›â¾ñbN_8ÃüüEMgöâ$ff÷qÇKîQf—�*£9ùÈ.ž{žþfc…²©Ï"ˆm°cƒo¤¨à$0m,ˆ‚K)…ž/>r§@.uAœ£â�&m+@ xT¡§òôŽÇ9�5H_�®à¬S¥%=ñ`„‰=3~À`½¯}�Ây7ÜÌÈM·°4ú+åÅ.çþÍ{Ùx÷)œ8稶ÆyÅOþ=mÍE6*&£)rl†òÃe¶"Ce/}ÁËW�IJ˜n†˜Và}dA0‘ä^“Ɇ�=!ÏÝp[üH†ÎÓ›‹) C¬Ç Íñ¹ÜJí/y•îo]¹Ì`}·²@rå<¸rfåøÓõjZ�i�Ðl¶äýËÇO>ýg�>ýè7½oá|w‡ÙìlÿÑmÆØà;“,;à<ÉØ ?‰Û>éÓ°Y€d¬aXîÌp˜¡÷ �ÐÕ× —*�ã×߯•sÏÑ_¼€õ�/¬Ñ¹¼ÎÈ ö‘µ›DßM�“ƒÅx«¾�¹çðyW«Üw'ûoÜËÌ®\ϵ”Ã;m¦“ç}Ö–ÙZÖZ4í±6»÷^=v##£T³†šLçÏ]äüÉ3œ>r‚g�ãÔñ£œ½p’åÅò"§Ì ph�gbvšƒ÷ÞÉH{„“¿ð4óÿx�Ä%±ªÀzí;äŸý)4ê5HmðÔ¼5Û+� xŸX°‚x¨V2Î=žKg/j親ºÖ ‡FØup–+—TÑÕ-éu:x“»fÔL™K­Ù`}uM'xýò°Ð‡Òž³¯ìÈ£M|«Œf[þ­u¢žÛVŒ©©Òo݇ªÝ”ÛÙq hó_߇·>$K ~P¢&ÏLpy4�R5–¦:ö�fu„éÙ=,Í_Öß¼~6ã×ßMV<Æëh*¾�x xòKæÿðQúsky¡)ÏÍ[îáºW¿SZH!f Ïp¾™ ³’XjÛƒxjœaeÿ0tž˜P¸aO�5(Å¿Dô\˼’(™û†ÀÃ¥íèå‰V…xcx œx#iŒ�ŒÍ�ÌÞ€ý÷‚¡éìÊܣ͒,mdÕªÔ�Õ¼Û¸×ë~ßo>ñÁEþ3Û³ÙIwÞ'"VoµîÆ$Éî·~9/ùÒïAD0y F†Mpök�Û@îâhÜ¢‚-¨q…÷\xô}<ù‹?€¬\D¬Ð¡Gëe·qÝW|"Ùþ"�„ø¯ÿƒåÎô¯)ý ©Oyù«_ÅÞ©]Û〗–9~ô$.�'w¹–Á|ø'Iü›�¢óQR]±AöìjNs¸È=Ô�VÙõ*箃w2±gBΑŸ~Œ•÷,�Q¡ÕhÑiñÒ7¼’×½ýõŒ��¢ÆÇŠ¥ÞšÔ‚}®ÃÐ>ʘ4Æç¯þà/ø�Ÿù|-ãã/Ÿâð�¼ˆó—ÏsúØ©`¾“ •F�;ï!»ƒÄ—’ÕËK<ù¡�²¾¸¢ý—ÄYÊT}CÊ$ð€GËh:§p*÷@¹XL"àÑf{^�Ì„|8§�1Á‹cŒ‚œsz}(ÁITÚZ‚Ž“îúÚ�. Û VkÕ¸ýp朾wiŠŽ7É(ã/x¡ª­Kñ€e¸zŸ6Xÿûsœ{çCT;†þ ¯ïcäj èÞoþI&÷_Oî /–é0œaæÜ°2̱óay³•aæp -ÆÐ¸kØPd6Dpª�%q–ŽßªxýÄ åº¶3 ”ŽsÑêü0š¡ÞN¼–�côØ @÷óç°ü/ó¬ØJZͶ¤IÚ‘‚§«Yõ‹¿üWÿʼnz‡Ùì0›äÕÆÚ×5Ú­‰Òanzãç2yã ð€–ÿ#½ÏpßFb¨!@”n9½�5í×›«t/âú]¶æÎâ×ð¥ÓE«{i‰�ç¨í› ©C+Ñ‚¡·²ÄÊsGÕ¼gœpÝ�7P©k?Ȩ¡rï¾½ìÛ»ñ–^·«J®`îÄB™(ñè¢èz¬¯¦Dðh/ÄT YR¹z½ºï0·ÝÕv�•cËùW�pù¡‹¤d´ê--á½þÓÞÈ[>ã­´Ú-ð ±O à#Žpï"Û œA¨V*œ<~’Kç/ õZ]“ªë×7ÙwèK Wô3tÖ)° ú}Ö–—™W� @µQ#‘ìêþ%TQ6PÏ eøf "kÐLq¡¬†Ç‹WN xflš(0X4–F*1‰’˵¬§�•U&ÉW&”†)™[¡¯&@�n‚ʧ¹ñ¶[©5ªœþy3PŠEs÷Õý£³&ôi ÀÄrírÁÒo¥ûôe-æyN–dL½äAf½©4(£ 8« 0CnÿÈ2Üp~åp™ G,‰AdûIüOž…t„ë{#½‹ÒjJOüÛ#<×e8ã�Ä ‹ˆý£¡Í3Üßñ‘!]#é†øÙ ®Ì±ü;ï¤ù´IÓTDL)%g ò_ó[?úðN‚ÀÎöŸÝîi´�ë÷€ŒZ›kZ³×á4Ewxgl:Fçó�¢†FÎÑYž§sežù£¡¿¾ÂÊñGñ"ôçÏ2XY1Ôªâ :JÙmÅÒäyž¿´Æø[îaöu‡IGëÛ££óîå ‹ˆÇÔ2Ue.Åa<!Ë,S»¦hMµ¸iã&¶–·¸tþ ó—Yë¬n³$_(@P©4ÕzS1šÎÜl4iŒ7˜ž˜f´9BÕg¬žYå‘?øÏ¿û9Kꕚúk0žOx˃¼öu¯¡ž¦�—x‘í´€ø¯h­ûô˜sàÌ�9iß xÍë>�+èll1(rXpö÷Ÿ£~{“ëﻉ'?øÅæ“ nàé®nòô‡åð+_J»=ª¾—ý·ÜÈÀôyþÉ“löÖÕ(™52|ß+ ¸Ô©(Àb5ïTD%ïô˜z–@ p….è© R‚I4MZËj¤‚(ž9ÈPÖ¨Œ±p¸ªCêãôuˆ¨¯†±ñ &÷LrúäsjJ•¾Š'p>¡2: aL€õíÝyq Å|—ÁÅ5íÕ¤I¦iã·ÝKÚ ‘Jô Û]} véˆòáhÄ2WÆÁf‘)äe\„!$7G°ò×FáHì‘)2‘}È00¸È\ô>wáúŽá]qŸÞ†y÷ˆ¿3Ãý†º°<Ïâïþ ²¥óêÅÙØØpnP¬äiõ§Ó$ýþK¶�2ÚÎvht\ÊÍ­¯°IòC£““~|¯~çïМއ\Óøþ†'DaUoc•+gŸåòÑ«zeõÄc¬Ÿ9Î`u— ”ät‘JSU“‘Uªê)Y[ZB÷5ªLd†ú ¯g×k/Gš WN<ÁåÇߣ¬¦>Öàuo~�z�‚¼§R¤ �…”�1|�.Š«ë+\¾<ÏêÖšžÓªµmŒÐh5!…z¥N-«ªŠÊ„Ëó+,_àÄ=ÍÆsë¸õ’rP蜚J–ñ–«ý™7êi�´�ìL4nŠ÷ñsv<úHØf9—ÎÏñ?ô.N{v»UmÖØýÙû¹þ‹oæòÅž>rd;\ÓKbRF&'¹ï%/§Yiáñô}�õÅÕ«@ô8—/ŸÓ@N,z,\BP Þ«¹SEFŒ²0¢ ¡òo’0ÊÀ©4Y¯#eøŒqˆ¢³èÏ(´õÂe‰™AÁfrjš;î?H¯ìñäG×ßUÌgÔöÞÀØÍw“P rø>Æed®…÷Plv¹òçG8÷ÑYX ߆lf?/üÖŸb÷½¯¤ôf(ö"KÀ ¹óc‰7œwmL Ñ� α´•;®mÌÈôØpNCQF0‹%<" g¤áüµ¬J¯å‡ß³'nqRh”‚ûk<=Ãiº@gñ'~å�ØóÇiµÐÿ' ×3ž_mÔšßøoú³ÿ%Û³ÙÙ|Y4’­Í.cwÞH¥5åžn˜Vã †7OçÊ%6ÏŸbîɳqñ9®<ñ°–Ìèw(zÝm—ŒÙÐEÁ'Ö–õzݶÆÇDDýŒMO±py‰´ÞÀõ7‘­.[ï9ÉÉ�œ¢vß>v½õݵy]ÜHP@  ã‚$Å£&<Do©Í(ê%SµIƧÇÐ…�¨PÁ óú·†66¸øñ œèy:‹ºk=ÊN‰ñºP3³wš·}þ§ñÊW¾  ¸°²…Õ'�×þ ÞRxm€k߯‡ç¡î23;Í‹^ó2NŸxNgÜè÷ démš_{£ç3'�AeI-am~‘ÇßýaÞ{­Ù¶‚Äèô8÷?ø2.œ<ËÉ£OÐ+;š÷f±:ÆÙù0óÇ“gUô˜+I3Á(°xý½­³ú^ò~ŽÍŒªØt]&!E:Å$VåÐRŠÎË�²?K(%ª"œÞ5Ë­‡nƒNxÏÏ\+‡•PÁÒûkûT"žÕSG9õ;?†™{VK¼ƒ<Çå èþÁ{ÿƒ?÷‘wÿ—ÍØìlãRW–dÍ6IV¡p ;’:©qsáË§Ž²~áWžþ§Ž(À Ö—¯µzTò›¥AYîêV”eQ–e﻾,/zå+Æj•Ê Ï<¯~�´Ödöæ“Uê\9òAÊ¥K¤Îa×Kxø§?øIsw‹ýwìg+ßdîÌJW*#ñÀââ>ðîn?|˜]×ïÕÙ9Y®»óF¦÷íâÔÕk^>wVÅeQ* I$ÏÁWÁ”‚M‚ñsPè1“œSpÇKX\£7)PÉ2¥Ç´_¤¬¦ô8’ Ii5#îæC·¨romq�� «¨Ê.K)½ÇVdµ1Jéá)5sVPÀF… ž]¢<·J"–^1À{¯Îñ[î!�úfÏ)ñL¤ì×H‹e¸ÔåÑ>‚~˜…äŽá¾ qá¸È®¼ •ð†çê ¿—xœ@B£ŠM·^ IÂÄ 3€Ì‚_kb&²¤pœÈØôœÍ‹§9þ›ÿŠ­S�3ÞÁãèu;…+Êç�˜oÿ�'^à¿aÛ)£íxl~Þ&Ég'•j6ó‰ŸÎ«¾ñ]jˆƒ µ_¼ÄÆüYN<Î܇þŽ­ Ïâ=ÜÕÞ�÷ˆhB0ΕyJ~|ß9·áÊò2°òwà—ñþd{lÕjo¨Wk£6ËèÙ&·ù;=p —yÿé�ÙO\ÂÑÇŸäì‰Ó è{4• #7ÜM{÷-⋌«àE#qtòK\üͲòÇ�«Òn«³ÞS»é.ý»¿ån+1ÁÂyO™ktŽ~‘2"ÄÅûZuZÜ7,ޱ±‚'^?/#‹âyàñ4®QªÅ70\:óž +A£:-^+J ¯-§•×ô…®û Ï=g�óä/¿“þ©ÇµV«ÕÉ’Ì•y1oÅ|ÑŸ:ò_ß§Ùa6;›ˆT¼÷­4ËÊ$Ëðƒ>½ÕEÖ.žaëÊ—�|˜Åc�л|ž²·…”…ŠÑ‘»‚óÀFõK¥Û*ò¼ï½_.<„Ès•F£ÓßÜœEäÇéõ^nkõ‘²,ÙÓ/{-7߉I2ö¿ìõŒßt�sÿ«Ç>F1wšÞÅçhÖšÚ+é¬nâN xä‡?€Ï<ÉD…‘Ý£Œß1Amoƒ¬–jÿÅ`U,‰ƒµ «äÝœÁJŸþjOÜN§£%£J¥J½Þ -R&ª“´“Þz¼Ž{_úBÜx�ö™DG\”27€÷@(< ³lðÃN>EÙîiÜ~çm¼ís?•ßÿÕßaþì²´¢£.½ç<­#Üòé·qÇÝw0µkšSÇžaiy Å @±´°ÀÇ/=ÄÄ®vßx=#³£4ÚMªSuî|ÙaÜr#Ï<ñ4Ë«ó8_¨ŸÈô-~ ¹hàÁ‰´¯¡~ëô8àCÜŽGœPšŸ€4ÂØçÔƒT�&èçzë�·³ï~r›““³¾¹ÎâÅtªj åÀ“´šd#cª>K]=*¢\yÑ¡szŽÁ—”�‰�v«�˜„‘«¬¦={@Åâ‰M󲄲&Éb)m¸lå€4²y ‘] ÷z"X™J,­�‰’"›ˆÏc.Úö±èƒ�Ȉì"(Ä>T|_±Á?<Šº¼V™N.Óy<Ë'ãįÿsòs'ÈK·€õõU—H²œ$ÉW³ê»ùoÙv˜ÍÎv¨R¯9çþM£Ýþ,1f´GJuf½K§ÑòNgƒ(Àø.¡¤â�Ò;_ˆ‘Â9wï?â½x‘c¶^_|mÙßSo7¼BD¾£Þn¿¸Õnµ²JU$Œ~-÷Ñ·«©ôÄye¡:çŸü0Ç~ÿ§X{æˆö[ÀÓl·uQ̲ ­V›$1lu7Ñ}ÕŒF³‰BG‚TòkU9…÷:�c‚+^P…V+iQ•Œ‘ÑQ®»í^øÒ˜Ý=«½ŽèØó1 fÍ#¡„$Ñ ®7‰yöøh߯‡ÐJ‰«z‚H]JÀÃ,ÿý1Ö~ûQ–�_P±B«ÑVhû¾×rëç|+í=7 ²jU¾õQ& *mǤ•¡rÙ�3ª*‡DÃÙdʲxÎ0ó¢ñ2,ä™6a*9N5ˆ,™lDivL;ˆû†ãwb™/†ÙŽDÀ¿¢tl=óÏþæ¹KÇžÔÓ´oçœK“l® ~4MÓŸþÛóÏøoÞvÀf‡Ù´F¤ìöÞücí@X<¡Áï½/´¹ïý:ð”µÉÃeYc.¥£íâ‘…ËžkÓ öÛ$ù®z«õ™ÍV«Vyª‘2i�=WÃ>ïú”¯Òñ½Ãîe0q sG?rl>›²³A³^ã~àÛ¸óÐ�,,,rúÔsò8 sój2,…F¹„¾Æ®‚›ŠJ§%"•[‹û¯?ÀØøc£#ìÙ¿‡éÙÆÇFI+Ùµc"úØÆgÍ} JB8Ÿ:>€ÐÁÛÐ1¢=³=ô0ð ¿Ãåó—”¹t‹.TáÀƒ7]ís¤¶¿ d¾¢CÑN?wš³Î(kñ¥ÃH}†$²=§°…ʘ­·€Ðž§Qm#©J–uÁö¥°±±ÂúÊbDA ¦’µ� N ªæsÊhhŽµÙ·{?­ñ6Õfk –ƒáòùË{䨆‹3�µw3vû½$ÕÆ' 2‚Q¡@AŸÎ/påסûØ%ò^®žçí›îæî/û>&ïz N,[—žgåÙ'hî½™Ú䳤ÖTÀ)= ‹Ï¯-‘y÷9?0ÃýŽø0óÃþ–È*®�u¯5<>€8²cˆqÁ°oÇÅÞU ƒ0Eq(1õÃÆ—\~ä!áÏ~¦˜;ñ”5W7Atˆ_¯×[è÷º¿|çC‹ÿ‡0š°Ù 4ðþ»Œ1_î½1Æ8ïsà ° œþxD¬=~¤ß)ÿ bp‘wµFGoj4[âé÷õqn»:”í¶Oút’ZaXãâ¨\],OýÝorüWßIÕxöÞx€wþè35» ŒöŒX_[gkc‹A¿¯%¡å+Kê…ñΡ3à,ðOÀ#³l«Õâñõÿ_^Yµb0_aû-µF}¦V«+›)J£»¸ç«ßÉ�û^Mªnñ!9æðl�ÕKçøð¿úÌâFê5Þò9ïàŸöVu¬'ˆ}“"dÛFpÍÆøUTÏZMb<¼^ÂcÝíá¢Ñ�)„ŸˆA¶õ¶‹Ãå=‚—aÍ«8 ìŒ(’w\™_ä·~õ·ù»?üsõºÔëML"dµŒÆ -ný’°ûe{Ôsãñaþ>*æØ\ÝäÜüV×Vèõ»ÊþеŸªéM0Ž´‘*,(äL�ûƒ„ümÑ9} YRe¤9B¥Qe|v’Öh“j£Š‹#z–Œ·�¥ˆî_]Zå‘|DÕ€bŒÞ*í)F¯?D}|�Fó8í×€Ç÷<Ëø(‹¿öüf‰w^•‰’f̼ä ÿ޽«’ãH‚Ùƒ‹Ç –¥³_2ó3333333û™üDffÙbfYŒ§c\êþùŠ wttøüŒ[ŽñîìÐJº›œªÊÊļ—¼™öIèÝz/Ž_û=4ð¢t3ìù`nl5�jhLJXªº1…âÚ©þ¨p¡–¬Ì¤ŸÃåÍ\ng-ZâÔf%e(Äé>¡2Ò’ASUUš%=—”©êD9ÔìŒú•¥aqæ–ûnúUT¢TŸÊÌ�Ï#?ècÀOÃ(üΦÒX/þIQ›z–“ÚÔjã±.„˜à=¦e?-›ÏåÒ™Œ•ÍäPõˆŽÅçŒÉ_Z©¬+u©’×SëoÇþ8«±«ÃX¸dÞùþ·cÒÔÉt€�\N�ÓÉ8©0j5EÃCE{G>öÊb¼¬mhÒÀl蜌†Iz„Ê&¢m´/-”í(w*¥I¾ª�äµûôüä»?Æ–{×£\*¥óYØ™éYÌ~Î<ÌxÔ ¤ZÓD$°…EÔãÈŒµ F–…ñq �ÂÐÈ"@òÿ&1Ë�fp(+äѾ©Ÿ•oi¤Œ%�ËÄ™e;r9*O%�âÁYŽpÀ“Ï}æÁ6Ž?pGbÊvDVÑ6XÊ‚ÛÜAù–™£ì+É%©oT¸ý0†¼Õã#4ôIŠ © ò³—bêS^�ö£w×:ÿÃw`› †_@ÍÎcÁKÞ‹Î%ç'`­€DrG@†É {\fB¨dir&InQK_¦!3ƒ)Ï6²y/ÁL¡O'sµð¤è­r†!÷—e30ÕÞ»2ùÁ«§÷nuÆm×M1Ò¿³¬Ð¯y'|Ïûºx?\?6èã?)ê`S�ùFÆ«µEÞn;ÎMËjqR®Ëâ0M  ­�|–<ûMÈOšF”iÇ”ÍYù‹¨*är!гm5ö~ÿã`Å>¼ä /Çóžý 0Sú9yª=;à$Ú!ØJ D™Ú“€#ÁL5‘!r� P$+€':§j–¢“7ª·ËzËððn»î6\÷ë«1Ô×!ZÛÛ@%«ŒÀ´ f`ö³ç¢yQ3ì´ƒÀ áÁƒ7p½³" űF‡Q¨h6ŠqŽíÐ ÝNÙt<)5Ûi;7ãÒv�dPôÞ @å¶*«B0 ¥^’¨UkØ´zŠ…xH 3—CÃŒ…hž¹„Ìó8ÈV< QÚp½ß¼c»ºÑ”oJÈ6œés0ý1ÏÇ䋟„�«qüúŸÀ1-ÊâD`êÓ^‡™—ÅÙ¯ã$MxéûÏ¡Ýä)ôI}½ÇqpLE ]î/ %b ÷Ó˜hr¼J–ð$P!yøR3&7Ÿ@&íð"ÚWŸ’4lÚî°ÕÓ‡üñuèÞqõ/Ó&3; Ó0lÛAGL,éö½Ú9ç_Y76XÃJÔÁ¦«òMfœ-B<™1öbÛugd²ÙT*�6¢(„F€“�Ÿ¸ß€EO~ìL.aÁÈr€>™­Î Ý�uŸ}=œò`,xù8¼öõ¯D6�–w‹d ºj¨`�`¢ˆGÑq�! å èÙ Ô;�ܦö’,†²Cψäã²¼�OYâcBÑ¢¥P,aí=kp{ÜÇ!m9/ð¨Ÿg“$zÙü¨6LüYhZÐ –øùG‘B*ºÙm™ä{ 22#¡Í$!&@Y’Ï|ú4-ÒôÞ&Ï"c9˜Ü�“E@B 0@ªÑÇæû7À‹úÓw:’·,«)›É3—È0,,Œ�ž*– Wºnê»kFú+øOŠ:ØÔ<ÓnêY¦m½Û/–&ÛŽ3Õ ,¨¡,„³e2Î}çW0iÑJÀ´å|ƒLëå @J±+eÏ °ëwß‚Ø~ ̨†ç¾úExüã “K‘RÖ‚ì‹Èì@É;�½žAÀ¤d5 EI($½Y$ú>†!9h€ y³�(+¿ƒŠC2Õƒ4V©VªØ½}7n»öfšW©”+0’¬«zðx ÎY)Ì{þbLºx ŒVƒú96XÂDÈ~`¤WFÒ? "¤uú:0À“íˆñæ>(³ ÏEòý"„D,°8•Ò¿Ð|‹Áˆ¹†[ÀñSGÁ჌Ñ2mSмì\€;Cˆ²ÀÐû1ðÛM°NWÖ²ÉvÝ4Ò�m˜ü¤—cò¥OÅÐÁíØÿÃ�Ã/@3Eœ£ié…X«d[ÚDD`S&­�qH~™ý¨ÀÃdùK™¶g2 R^S›ñŒ¶ÉžLDàLgÐGÊ|ŽjtæGr]ôùÁyߺû\÷£!ïôá…•RÁN2X˜¦U1`œö¼ê·üèîþÓþ“¢6õ87×¼tÊœÙ×äóùY½§N^­Ê'3^ÊA„™OziœÍ¼ SÆ”_T¦Q3S*YJHÖîÀ–�½¹”‰ÆÖF|à³Æü®Ù� µ9BÁtä #Ðû1* @øQ‡2¥!Šš¥:¿U®K@‘Y�ìPKÆ ¿»PE¬h›’öQ©é䱓¸ïöÕØ±y;ƆG©ÔT­VIÿ,Ó�ÒnW­v sÕd´/î$�Ï ÉTPA€²ÊP,XT£Ë�®O`"h]öU˜e9„xlnÑvÊhLÜÍþìZ³¥ñ˜iR6Åœ,ÏZ‚ÜÌÙFÑ` cw<€Ñëv#ê)ÁŒ²“±â8œÆVt=þèzê+0Ö߃Í_z3üá>É,K Zç}àh›³¦!ÙdP²Í�Â2ý1…É%uâzKO%j£ŠÈ§ìïȬÈdr߈Ëy@~ɶ—= . C­)‰0ŠƒµØJýØ®ï},eÓ×qèïž1DaXöjÞ1м×uSkîî ñŸu°©Çª†&ÃqÜ�LŸ5ë=…±±lµ\B„€“%1Äy/x+&/9¦e+ô΄%£ü2¤­�2𦤇¢ZÇšO¿Áñ]tc¸èq—ã5oz5ÚZšÁ¤È“Ö�ÕJe’ת:CI`�Jĵr£Ý¡x kå6ÆäÍX6øåuãx)+,è­Ìrô’ 4 �H`€ š§Ù±m'î¹ãìß¹c#£H»P¦Ã8ѺÍV­çw í‚N´/êD¦#8É0)e3>ó©tÊ| ›²!8"&v‰ 'e2à#€-(…m3ˆA`ÓsªG7D­è�e ¢b»�hœ{6L'�ê®î<‚`}Q�±Þl*ź�Ó1íÑÏŠÙvO!&àî} cû6ƒ7 €aÙ›¿€Y±Ì‘Sæ\d-;PÙd¡'XLyðÑüi¸lЃsåáH™u‰„tºPËÇjuUž—B¥IËAQÙ^Ô”ä(‘$GNnùéçOußsC{*•™î…ža™�Ú¯ÕŠ®ín``ï¾½÷ø^Ôã?lêqiÛ³R)} ßÚüjp>Ó0L˜¹&,}Óç‰if¤ó0 &Ÿ …6˜&ñ!™B�Û“EŠžX{ ö}JÃp\Ï|ùóð¬ç=�� ` ¥Hﳨw}_$ôeXfÒð—ÍyU)Ò-ä`:w6!t, "ùqIRLÕ {:jD¯2Û‘w$&�yÐó8û±~õ:l¸g-úNõÀ6Êr˱á5˜-ÌN+¾qOGÛ#:Ð4·Nʆ` “ LxÌ‹ŸJk”¹° HæeB$ý°Kh2Ó‘ ¢6›”-ˆ�æÌÉ“à\<ØÏq�d磰å4ªë»až «d{Q(á¦óÈLŸ‹®g½SV]†jiÛ¾ñ”ï"ÑWÛI‘g×+>„®x¦¦¥ôJ]�)î™òŸAiÜs¥ñ¯+È})8½Wl $ËL‚”eÈëI™Ì2y¼Ì®^‹òýÔç§äz¦àáÈŽ{Nîúñ§‡ŽX÷Q³ žWC…Übæ(€ß›†ùáÕC=#¨Ç2ØÔc¹�z©aZoohj\‘ÎeY¾cæ¾îsh_¸Jógg˜ÎòQl  L^ë³ ‘WÅž_wÿµÒ(²Í xý{Þ„GÆýÇ0’Kò�Ø.CMêz R³=!0õOë�:­ÜE×”‘lc’˜F’õÄ´®~,QF~_¹[òM@#uÚè½°h¥BûwïǶu›qìÀšÏ ¼€7C2±�H¶=‹ô´ ZÏëDÛò68�.2�Y8® 6"Æ Tx"ñÈÂG”õˆ$C#ª4h�À‡%ÿÁ`6Œؽnº{N‚³ Ìn¼d£rt ©Ð�ÉÉd …Jfº �³af<+Ó²p8öþæëè¾ég °ëR‰nÚ³^�1Úq]­½¥—· YgÐff´ù…E¦øÁ%¡VKÁ2CWéËœ#Á`Õ3¤ý•ê¬B#Ð>Ž Z Kr‰¶™<®Ô{Â;ò‡ož:¹úFæòhºåØ®ïû´- £šà¼?,×ÊßÚZ.TQ%LÔã?.&Yö€àüñÌ4§¦aµ*ÊGçò ©|Æ$}†¡H‚Iú©ØfRv`*Yë'.ƒ¦�LÇTºýw¨��À+W0R(b΂9h›Òù`VBC ƒ<—dyÁH–d¿äËiÒ#𬠓‹²U~®a†0˜¬k€ÉíÒMNè´,ÚŸŽW:Ë4T*Uäf‚Î%A•1ºéNš: ³çÏÁ´Y3ÒŽ37N×v‰’,jÅÞz6ŸFyo‰\H‡O £6X M•Çïé» ‚#a¯ÑЦ’ù™tÎL�Þ‡dëP,Å™Í>”÷�ƒÞÖ Œ#€;l![°‘A È< ÜÉ"7{)¦Ä™ÊÌG?-óWàØþ³/àô­W‘æ�e;”ÙLzü‹°ði¯‚�Jƒ«M}Yµd€©µÖ”ºÜO�‡*~©&˜ZßD%¹6¢Êc˜ü� P{D�ß\Õ>£Wµ )|£Â©ƒÃ~ðY³çžkšY´G‚[Œ‘ôŒàœWLfîK§2o5lû7k†zÔã¿!³©Ç#ZÛ/–ßë¸î[SÙÌ»©—|æ7h™1GÚã P˜ eSÊsØò]n“U&¥Ñ+B/f¦}‡¯ú22)’eÁ² Wám~fœ5CöBÔyzUiDδ�Ç‚BÍv˜<©ä¼Ae\Ë­* [Ö`4mµ|”Z#uâ•ñrrý”**—‚Q–0ØÛ�=Û÷àø�£8uäúº{Q*—h Vа_øÈä2d�iÏ"3=g’�ì´´Lk�ÕlÃmp‘²]Rà'º5R(#ã>LÓÂÐÁA=>J§‹èÛy ÇÏZ@ž �Ípl—¨Û•j¢¡ÍË/�ÙfOCnze/ÃÉûoˆK¨†_«+Í2M4Ä̳G¼íK°IîHm‹ÉžŠú¯¬ÿTÓ3• Š\2Å@•À“•YÕòYJÜH»ÆÔ™2Yú’ŽÄ$Ø8¦Zž3tÆ>DµP=ò‡¯wï»öç,¬”§ÂeŒ!•JÁuÓ¼V­ Å�¾ï¿§_Ù‰zü·�M=ÎÉ4®4LóǦc-¶Û‚•Âô'¿+_òNºQD\¥n %lSÕlb�”ÎHH!N!ͨÈkgë·>€âîµ< ¥âg¼ìyxÙ_‰\C.A4!§õ…„8¨¤5 ²9¯‡PDÞ×åfyn¢hk`&!—1u›z>)Ô)5ëUDSÖéEfAÉk†ìÀѸ¬vhßœ:z='Ï�m�eÛHg²(ÇÀlF”e?ôÀ-ABÃ�Ž îqp‡ÃjsÀR¢¡FÚ‚ÒWƒ‘7Q,�9D•±À2‰Ñ†È‹P)”à{>DÂ=Î56Sé/�Ë£¡¥ ™™KÍ9­‹Î�ÛØ$ ½ñ`ëWÞ£\€›J£¿²É³±ê­_B~òLa`’ñ¥›©ɇ��±d)K&œB(í`!Tï‹A‚”6g£‹y†r6Æ� c“€£\[ÎæÈï`U>üÀ–ñcwü¦tóÝ­)Ûm©ù5³\)ÑÎT}ö¼¡”“ºÊ4Œ/Þ7Ü;ˆzü7‚M=–;iGpñÞt6û–Øÿ¥=ä«ùá�4[#‡Ù”§M µÆÍåL…ú ª>)ö؉Mß|?JGv!å:È4æðƽ�yÒcÀ I÷ăs/ÂÐû/�ŠËjf „žù¨ý™‡IƒÖL_×GB&éÛ€P24½9YSJúQ”É EÆÆ‰µ¶wÛnœ:q •b9^JDOv®“†úðÂ`€LëXÄ(áB t7b¢H%Bp²°¦ €³M‡2¿æ‘"7CÍ«Rc?Ó1Ù)³Ð2wf�Ì)óÀ3M3,wUGú±õÊ� ´o#,ËYHØ.¿íËh‹A ‰ô)°`²T¦„ɤ�ƒ>BŘÊH“$TTqo%I•¥`•i&·«)––yiþ6ò»©å4úžœ£xxg0´áºÁ�¿ÿ!· «ÅqÝLøð}Q Ë4‹ÕjõD…c_ÚåW=Ôã¿lêAÊγcW·´w,°m;í…š.x.ÛçÀRYÅjW(Œɬñ"9e�ì+Km*Ý4¹s¸ù*ìŽelà•éI~r× |ä+ŸÄ’e‹‘\E§Â鉀ŒA%»Jf@z9-Ù$?‡DFY®Óêxê;&¯§žUÏl ä:#tV�ìe­EU7Йy$ìIYFµ\Áèð(ºOžÆ‰¸Ä6Ð×�îã§0ØßD ¦a’£¦ešdRäÒŠj­Ûv ˜ m£’çˆ"R“† Z{ˆt:C`S.ÈÞš@£±3âáÌ)±kzjì\,ÃP@ñÀÕ?ÀÑk¾‡È¯!ôjÔ¯YøÊ£ë /"ž I ±^-Õ`4EÉNÓ~.TZd6"3• È©"”ëKú´•¤Àe Д § Ó’¹Ÿ�h¤‡í¼¿ºãç_ôKƒƒ6K3¹$fÔ\Û9S(Žß)¾g§Ü=F9êñß6õXÑÐÄD¥öôT.û½ÎI“;ŠÅ"ÍF¬zÏ70ãâ§’4¿)›±TaÀ€ËÚ9iX©–·ôczyÁ/±ýª¯àd,W½2Ré4f/ž{ݼ‹—.NJIÊ]‚‚A ¡Òš%hhÌ3p´,I*åÈaG¡Þ5ô™ úû Kr²§#»Ôrð”ÑL›>”!" ¯R£¬gpp±-�ÏÐÀ aŽ�Œ" CpΉj¿'`Éd2H9)Ô^͇kÙ0m‹öÉ57¢±¡‘²‘c‡Ž¡÷L7(+À”³/ÃÙïýÒ­“¦ÞxMZØ· {¿ùn P-�Ã+��»ìç°ó-h_ÁØp"¡Ê̪ó&—ÃŽJù´¿F&�8®P:péÓü@(³zU{M)‘=´¨�8J݇áo»%8~ßµÅXn†1Ëj¬UÊ3 ¤SiÊ.…c…ñ±�¶eÿ4ƒk·V EÔã lêñˆT®ÍqÜ_ä.õ/k˜xËT\òÑŸ¡iz oš²-GS ¥9’Ï” jUDm܆¨÷Q™e ö1ɹ6Ð6­“gBP øÉVÉ$�€¡V³*‘6JÑKÍ^d–@Ùl˜ìõhð#‘L†>Ø©#IrXHÍ/�f§:†j×Ã�LÛÊå2F†FQ*–0ƒqÔ‘‘!TË5²ÎvÒ.Íð¸™4F‡GȌβm467Á´ ô÷öãG_¹ýÝ}†I³3->K_ó1Xn\Õ£(v�ÅW?‚ ï$e±¶Á Z¦bÑ>ƒ¦sìç€þ1+›PùÙ‘2Hê@§ZÙŒ¸Ú¨7 ôp(„j#-?£ž"- ©EÊìi#Ú?…Éä1�2~ ø1’ã Žíˆ^õY¿:4$Ɔ‡í ­ ðYÊM“ël*•æÅâøIÏó7øµê×·{å-¨Çÿ0ØÔÉW˜–ye:—í2mËâÌ‚;w%Î{ë‘m›‹´¯˜Rš0  Beü€"»®IÞ Þ›«ýÅM·"Ÿva˜ ³¦à­zfΞ©��z© Ú|�^>ƒÞëgª¼LÁ$(�ÐKkr` ƒ “ÄC•à¤�gÄåLñ66ÀW(\‚Ém€�—HÊc `y:Ì4èø•$hBºù%Y%ý[¬¾ó>üø«ßGÿ©^ÀpÀü—¿³Ÿø2M+�1�úô‰›~‚¾;‡Ñ�n‹Ì|BM@÷åÒR@I¦©˜ÆÏÇÐÎ5¨ÛÍËÇwññÞÓÌ+—YàùŒÌ¼¹ ¶X¶åY†u8ôƒý~àßƽ™Ý¡7Œzü‚M��2Mó«íS¦¾8�róÕjô‹?c–>ÿ­hŸµAié–N¸ -P³uÈ.âPK-Ðé¨A$·×Ʊï·ßŠÍ´~L �“°è‚xe,Ú9{îl0}ðB«�0hÔþˆÊ@ÓUÔOT`ÑéÐ2Û¡u=S™¸„ €(&FK!ä)M-„DwåîªTì$)Ò@ dƒðá7½÷@È ÅÜö)xÔ×nD¦u²BŽ|ûc9¢‘õ7’4Q>e–‹öç¼³®x"®jŸ†œÂÓ€™ÌdÉó©™sª(,|0‘" S™ÐçBÍ>Ô¾�nR&“B�|à˜Š;' §†Õ2zwo@q×jˆÁ¢2Ø�ô÷‰(-E̱:«m;Ô' £Ð‹£;ëfv…ax±Tøu*›Y?2¡ÿÇ`S§Cw1ÃøRCsÓe¹|C³eY,ˆ8Ì\Úãº{ãÔ™Hå�‹3�̤0Rù¤v®ß¸ô`—R!\! (B‡a¥€]¼‡®ú2ÙGa€…ç,Ç[?ðvÌ[4_ÔÌ@•E¦ßÇ%H�gMPS½†ŠcÓpC‚�ze |jèŸë‡>ÌR‡vU¯ ¨'A¹½Z®âcïø0vmÚ�ªï#ˆ8¦Ç峕qÏNç”ÃÆŽ?€mßz?ª½Çà ¢Ì·ùÜ'`É+?'Û®Y0KÅðh¨~ýºä?í/%�ÂQµ xàÌ À�éPÿÉum"‚ƒA´lƒö1“k…\µ(�ઽ38*ÅenþH/zï»V¤Â2«õ ZCqt¡ï'?EÔ·¢kZ¶ Ó°ü ðû¼Zí°à|scCÓÕéTf×-§‡@=P›zœ›kš$„xŸ›É<3Û�ï?fÀçÒMÍXxÑ#Ñ:{1XºF×J¤š;`Z ¦6wM 4ÊS**U�ëÕÂ(6þøs艛Ì68LÓÀ²KÏÃÛÞ÷6LŸ5 JèÍ$&��b –ìô i`£lÓ€tà‘;( &CG<X&Ú�¶M+90Ñ"Áê5J¥¾ô¡ÏaG 6^À!` ý‘ω{x‚é¸fQµ„íßy?F÷mó+hnÈÁo˜‚¯û4Zf/” Ͳ´*¥ú¡8f*�x.3Oeß„l¤ÃÒ¼‘x¥q0;…ÚÐx…QñºaÛÊEê55v-‚ÛØFàӯɻ †)Ëfœƒ AvÅÞ“ðÇQ‹et÷¸a†WˆFûΈÑ�CnÄ ¿ZÕ‡l6Oƒ±�„e[‘m:}ã£#ãmëLÓüc¼íÀöj)@=Ô¨ƒM=.nîHGQôÞªW}V&—›ã¦SéL6 šrB4O™Šy+V`4×…†¹+Ð6g12Í­à00á�ó5Ž!YDúíR2}Šýg°÷×ßÀé{¯AT+!—ÏcöÒùxÃ{ÞŒ…KÂHê#zÿC/QÉ÷s-é·€þeˆÁ GZ¶-7*Ó†¸iÚø:[MG?==š8&NÙ ±ê “œó‘+=¯b©„�¾ùd!)@wÅ¥ÔU¯ø „i'%(�¾í÷a÷•Fut“Zšà �O~%æ?íÕ` ‚p™Á&<uþJ%—èŽÞ†jM$¶$“¨öŸDáÄAÐwp|°±”{Ð}ªU?BÍóEÂ4‘kŸ DZa„>àæa¨UŠ({Ò¦ Ù¥´c"( Bˆ¥ñqŒö ¤ó�y-H²,a˜fo+„AÐ…áX¼}€ßq!vÄ !êñÐQ›z\ÐÒÁâÂŒ±Ï¦óùeS¦Nkä‚^­F¿ä<â0Ü š�‡ôôy˜qé“Ñm2^ÿ¾·àüKÎG*�Ò~Ä–&ô¾ ÷MjÕÖÜu??p1YW<ᑘ4u2˜$È jçÓÊXB%èJ†¼€úw€žMêš@_O?~õ“_ẟü†,H9Àu1å)¯ÂŠW|,^' (�aÇw>€‘ý[�ÐCƱµÏƹïù:rÓ”w©~, ›÷’'¢TýyÚO¤TKµÕbÅÃ;h¹p†ƒÁžnqìÀ6ÔÓ‹R¡ß÷ „ å/Á‘I¥áGéT v6‹±þ^ ¥œ¤|Â0$±Ñ0 †�ï]œ1& ÃàqTþ¼Äs:^ßòg�‰Ag¯iY»wÔÊêñWElêþ7 ðΆ֖Kã,g�¢”e™Ì«Ö „ ¯úH �óÑÛL^yùÓ;©4±×Dr“½vµyÔªðƆзw#ârÌȶÕÈg]4ÅËP?†ãÅqãõ¦fÔ‚*.yòcñ‚—¿ÓgNO-Rºz&¡•ÙTkj&pâÈq|ñƒŸE߉nd3Yt-[€'>ûÉXröRdrY Ð4AÎ ïñº‚†€ê¶ ²X&¾¢4Èäµ+•jìz/î�çk¶Þ½†” \7E‡…Ί�ÐV¼ì}`–MçêÛ¹‡úYTÇa#@ÀlÌ}õ'pÖÅOÒ$ÿé\“?RD,¹] RmÜP[sT2;µúdGŽ£Ý©–8çWw;Ö[žŸÅ8šy¥jµªF™� Î)cMÞÓÏP&“£2),į!‘.Ç¥í–e¡R­ˆ(.„ϘQ‹Â`D±×2­»âsocûsS˜¿3ê`S§F§ Ä3<ÏvÝenš¢1•N§¢ˆƒÓ/*GÐКÕ܉ŽX?«é¬ùÈ5µ"×Ò8iÀNA0Ó9ª‰WºQ8¶#¶#( Ãæ5 H8’¦á½:gÎÄh_h0fVÊe¬X„—¾)~Ò>çÈd3›  ¢‹nÉ]´�M`|l?¿ò§Ø|ûš„Ä�èØ�½/zÍ‹iÒ^¹y+YŠœÔBCEèûI~ïÃ}B^s¢ù@W”d:¡adpW~ãûXã�ˆ¸ s³r¡êe&,7…Ïx-V¾ô=`¢VÇŽX’ftÿfÒF³-éYK±òm_„ÛÔ$@¢Œ,AïÑYä�'•…Й‹ôªÒ¢åô?=¬Ô°/6gëôz‘3¼¡];w^~lÛ–ÇÍ]á¸î2?ð ƒÍ4MÓˆI/éTœj¹,„à¦mÙÌqRôï+„€W«RË(Ãó=‘Îf�0+`øó1c�çÐ à¤âzÃ07ÆÒÿUÔãu°©ƒNÞdÆs-Ë:Û´¬å�­­³cði ƒÀ©UÊV¾¡¾ =ªó†·±¦›ÁØ`=ç›ZH„3¬•à3¡W…_C6�¢2šmÙ’’EûôéXtöòêŽ 7G5¿óMÓ´×…åXxô3Ÿ€Ç?ãI˜6c*•7‚RüP�*†Éd£ 8^Äu¿¾÷\{+*å Lâ§Þ‹žúh¼ü /'yUœS©ÙÆ4jµl¢2àVJ()¥>¬QÏÑ‘19t¸ê�Øuïz˜¦EÛ›Z¨Wa˜&ªa?0ùÑÏÅÙ¯ýe¬C{Öc÷w>„°Z‚W)¡¹£“Ÿözt=öyàBªMèb &\Òãi ÎmAÎȘL‘±ä3†D“¯VÆž_}­ÞMolëæMϼeïî{`…“IsÎÏÄóÜ\®Ë`l ÷Û2­”aM©l6]/„éLZTŠå2cm×å~­6b§S#"ˆÆjÕÊ=�¶8fF Ž]ÞY*ÔãŸu°©Ç­�,ªyç¡YEsMÓº,×�ï4m;íº®F¡©ýš¦Iøa!Š—|>�|®�nÞÕj¶íÐûL&‹BaŒ”‰S™l¼¤ýU—\>tðþ7-—ÍO5ëyn:sA¼O#�Ig0uîL<ÿÕ/Â#Î_IYŽF3f˜ÈpM£J#‰j¥†›þxnúÅÕ¨–˰L›äF÷¢gàůz1²–ÔÄ 1<|LØÿÑ@g‚KLT2“pÎÑÛÓ‡u÷¯ÇWß„¡=¡òQ*•¦²Ñì8c¬Å@TF©â¡\.bÒ£žƒsßg.®…ý¿ú*zW_¿V�xhY° ç¿ï;0³M’Ö.@a’öNUOHú³HfYlCµ«P††%±CÊÚH‰Î)³agöarFT¶¬_ûú¨æÿòþÞÓB)§s†ÃfÑÑ�ol ÃpR¥Z^ ÀbŒU„ÛŒ†YÉfrÝ…Jqp�_�P�ÿˆ¨ƒM�*íx^mzÊM_*2‡x„lÛ6LËb`L˜¦™ÉçL˜–iÂ4-júÇ=Ê€jµªˆ¢ÐP yè¦Y¨Å%‹¦ŽŽ¾S{öÜÂ-óšæ|“i0ã"ŸGo9kv×ù¡çOŠÁ‰Y¦7ŸÆù±êÀžù$tÍëB|uxQg¦Ml”–D­RÅ]7߉Û~wJcE�OòüOˆç©Ï}R™”z^ý&?1=šéûé›zèLgŪT+ó1R8qì$õeî¼öû‡á¤R´{ܯ¡’e¾£¯ùȇpùc‹«ö \÷½`¼T¡Su\þL¬|ãçà �ÁÖ¯¾ …“ ˜œ|3ºâ~ÎÜ'¿œ¾³%UšÁ +ýIÈY+! ŒÖ©“ðÌ—>OxêÐØÜš–Ù„Fh2ùÇlÛ°w\s Μ8…ÒxùÆF¼ü¯Åy—žOTlí@!´sêΞI°‰û:ò#m“.==íéïÀ7ß�£‡Žbï¦í†í¸”u–ÊEÄ$¦�5—<í)¸âÉOÄŒY³¨$yóïÿ€ï|à#”�x¾�TײØ&à§8±öfüég`2F®� g-Ä’7f¾5E UÏ ¸œÌ7™´Q–ÖÐê\ŽÌ’$Ъ÷=ì¹ê‹(ØŠ®ŽŒØ±yã•ÅÁ¡·ïò*êñ¿u°©Ç9 ͆¹•rS© ð§Ç¥¯¹L¾Ùinh/Ž�Ù.Œ’ë¤8¶}bxtøv˶ªQFÌ0J–iùëLJÊ-ÚÚ)¸x}:“Y3ä.hmiëaãã#˜:}V^|.xôÅX|öb*wMÔãг½YϹ@Ïél¼w- …â, +Î]�®s‚'4jå�€º•ÔÐÏ'Cýph`ûöìǦ5qèÀ!ô8†L6�l6Gö¡ïSÉŒ¹–^|!žñÒ—bá²¥ ™¢äú7ÿñj|ùíï33 ê¯aàÂ/^‡SwÿCkn@È·MBëù�Çœg¾‚Šl‘Šëª;¦œ¹‘å6)1#ÁÇ6!…¯õÖ½FQ„½W} ŃÛ0£Ù»·¬û~q`ð­ufØÿ9ØÔ£+óMF†pÝ”hJç²®›ön<ñ@ðW—ïòMi&°J0ö‚|SÓEMM¹i‹ ®ë¢¡¹³—ÍÃå�–¯ZN†a*"è …^†Rw�±�ZŒ1R7`ÌPt>µò™²‹®9!‹mbà¡éw;r Û6mÃÚÛW£;¦p{žŸôÍ,8¶Cý³ˆs¤²xòË_æ9˜³p!ØAÁY�Õ·ÝŽ¯¿çý‡ã¤¨„¸â}WâÔM?C0x¥Ò8r­“17¦CO>ûRi¦&e÷‘šh¦”i‹×¥¢“Ù‹L ·Â”ƒ ´8(|¯†Ý¿øÆcFcgÖG÷l¿º2:öŠíµR õøGDlêQ�Uí�Ìë`Œ½°¥­ý‚ÉS¦]x~G††í8àÈ5e±<&<æiOÀlêçЩÇÇÓ2“ bZCBù]æ€M M£žKAxâèq\÷‡ë±6Vi. �Q™¬¹±~ â!Í‰Ä &ÜÖf<úOÇ%�y4¦ÍœIl3¯ZÅ@o/�k°c##éïÇ©ÃG°æ†›Q«zà̤îþì¸7Ó·ñŒ÷ž$Ùšü¤™XñޝÒ§ÁôR˜,£©æ�Ë^ Tœ—âÞ�YR¢¥&Kj€,¿…>öþòËÞ³i^õN?°ó¾ Z}v,Û_D=þ‘Q›zÔãܸ:—qÓ+½À \žÏ7�ÛÞÖžŽYp†mÛHçÒ1…z).zÌ¥8kÞ,´´µÀvÊP¤�Œ®•¢ÌD`¡öedL`’ö°¡^O*¹¢·§{víÅ�7ÞŽáî>øÅ ü0¤IwÆéÕlÈbβex\ 2ó-ÂàÀŠÅömÛŽƒ{ö`dhc}}°M …h��Ë5ÐuŠ££ðƒ€\ ^¹i]¨ŒS–”Î5"¿ø<Ì}á»ÀLK³Mf´HËh‘O2‹©¶jßF•°‰„ÒúRb{bñÖÑ}›áF•JÏ��÷‡ÕÚsc×Ë:Øüã£6õ¨ÇE­�F©0>C1?•J_Á…xZ¬´Ûò5dó9Ó6-°´�eçž�óbÉ›K"Û�ƒa$å0itPašDÁ“ùr³¶¿ú^7Á!CZ=ald§NžÁ¶�[±}ífTŠ%”Ç Úx6Ë´h*>X¶×qáf2¸â™Og [Ö¬!}¯±þT‹%RÕN§2âWß÷èï!þ«" �àhÉåp¦¯Ãcã€a Øñ¶¦8‹!Št|쌧¿í+/'P“nº™T\–ë–¡‘j}i¸'?3T°!pÜS±{Ö߆œÍE¬h°cìLï6—ÇP�VÔÁ¦õXÕÚÎ,?šlYöœ´›z3�'745u…œçã>ƒÑÐØ„†ö&<öéOÀ¹�‡ÖöV¤³Y*¯=”[¦ l&5ÓÑ÷›8j5'ãòØÚX:fÓ½ë064JM„Ä‹Aˆ€ÆqSHÅ‹O/@&�%E‡B©Œ…ú5Ù>“ÖW†Š…1ú̈Dz`3†æt 3››0½µƒÕ¶õ Àc&H�“ÈtN£Ùš¦ÙK°ò_s3°¤ˆ)—¦g\¨’4ŠÊsŠX§^¥œÀÿTp<ÏÙ l½YÇUOÙ¹ë‘Ûj¥c¨Ç¿"ê`S�z<õ¬…­až„Á3SùÜe�ïÍlnmµc bµZ3æÎÂE�½ ‹W,ÁÔSI¶„ŒnªúíM—�ÑC%ý>¼ž™šÁÆ‹èÀáƒG°}ÃìZ³åB‘”<Ï#U…L&G`Â�~Î#ÊN f �NsŒ€‡(–Æ!„ �³\.O@–iH;6¦4·��fg.‹éM ˜™Ïà¬æt65b]ß0®?чÁ‘Ôü�(›§Ï‚Èw`ò¥OÅÌØ�ÃHØf ›þ§Y+«7ú\ç”õ@ªBsÕÆ9HÖi-°X%ükèß|ÒƒUzŽìÞóÚ^ùfÔãÿ&,Ô£ÿƸáÄÃn½¨©}ëXaôY™TæI–a.hjk›Å¹0íÞ�áîA¬i»Ë/Y…Xl“ú9M­MH§30mÊÔr—^6ƒ:ó,Áƒ@…GQüQïdh`8¦'FO÷¬»áD!§Æ~­\E¥R!ÕÓ ã¨Lö'öÎH’ÜZן¤„"jš\{×Lt××ô˜™™™™™™™™™/3ùÙ ¶—q¸§{ “$½�ŠRÔÄú1ßümEfe§”YµúçÐÊ¢RìwûM�s“$"$ÙXKª´¤%'‰dŸ! ÝMƒ¯k!¦N¢¹xiŸ‡Æ}Þ¹»ÇÕ� C£f)ã<[w¸SưßÉØêw¹~§ )+²,—Nœ‹Á˜áÃï£cŒF…ä1�²@nIxb« Ö×¢zs8×*’І¼\8±ñ:*ZFÈ¥±eAÚK)W•QJå´øoEK6-Z|¨?ÔÏ,fþƒÃ­ ݼ›õ{ÙH�NŽÒª*ÍÖxK%&­S“¨e±ôišÍ•RIYËNÞ�þ›k/þ3Ýþ7¯‹ïWTÅ�×iò!�Òóåèùæõµ|îë¾�­sÛ\¹úûÏ3¹pù"[{Ûä�œ4I…|”FRŠÃöг6\×ÈQ…@þb%ÉjµâúkטϼöÊëÔ«‚Õ|É7ÿÛ¯f9�còLÖÆ#–ɪ(˜OÏÐZ �ÿ¦Rù‰¨$y†j*ŒF\iƒ$‘~CE±"1©¤&_}à ïxðÇ.ìlqyw—^š±lU+t±45Ê;P¯eÏã»[¼cZpg4áðôDÜv‡÷NØ}û§^zh#{Ì(¢¢7±`S«0´Ü³Ó"ÑD+Hæ*ÐâœÈù‚ÆÑh-Kë¼ÛGë+Å-Z²iÑâÝ~vq÷âZ–«O}ª?±eU>öægiž(¥&ý^ØíôÒÄ$¶iêUš$.Ër­PgÖ5ÚZ{VVåÝpéѪ¶�ñÎá=J£SÛ4e8¼s‹CÀ˜DµqýåkLv¶¤ ôôä§ÛçöØ}s˜,%ÍRºýžJ]7Ë�hLš€”Ïk/¿Æ«Ï¾Àìàç¼X)y¯K§Ó”(LgYNS5��%©�…· ‰Ñ’ÆŒR¸ ‡_7µœ×e!÷¥iŽñžŽV¼ýá˼píÖ$\]à|ð}ü°�~€n(ÖôJá•FŽ'Š8G½Vx�!OOGi®t [Ú±èõPʈb7Ê£Qh /}3h|Œ¿è�’¥øÙAL$Ÿ7-—p®ãÜ@h‰�ÞQiÈ´1Ú6•X¢UYÖߡŠZ²iÑâcÃI:ÚÞý-ʘ1ìmuScÒ³³Ó±µ6G“,«¥êut»]5›M½J Þ(ÿæ5_ו¯V•×iâ³EE´jçĹñþ˜Z�FîOåêR~óZ9–³Y™çÝšjE‹ÿ"Z²iÑbÐþˆTëÙnš¦ó&TQ¬„²¼Cæ=Eµ"I ãÑDÍçg›XÚ…Ä/ºyGb:Iò4IqÞ‰\‹1ZÔ¦Ñí=“Á�ÌhÊÅœÝÁ@¬•¢±ŸžQàtB'I°ÊP­V¤yo=µ­Y•%F+ÊÐ r8Š0¤sŽª,Dµàìì yç,C%dalè×çâÞÏm3êwd—¶·¸¸³Mn ~1ÇT%‡ÓÔ5‰w Ò„^–ÈwKòœ´ÛÃdÊ9|’@§Ã;®\滞™eQðüµÜ=9ek …_wÕM ¶‰=q‚*‚ó.BÂq˜§|ÿ÷¾“Õ“_â…Ó)Î-YÝyÿêdµý ƒ+WI´Z“Et“Åóûu³ÖJ‹qœ·"ð.Ä€ð`ˆdå�{硳ÿ ’)×MN¶ºÕb¥iñåÐ’M‹ÜÙUn¾4i’�_ËŸ‘ö;vûýnUäiÆh0dá½ôÀ©ëŠÉpΑâÑMCb £­ðžL+!ŽÓÅBjO¤ëdY°³µE£Bv{œŸŒõ:L²ÕëÒëäÌV%™­é)8:=ãÎtÎk'ž"U4Þc•b†¦ñå¶\ñàù äiÊÉlJ¯ßÁw3z½.ngÂG'äZóøß$ƒK\ÜšðŽË—褉XK&è«iXëñ«Ô@±âR¯#¡êj­Óï•FÈÅYœ—¦hçðZq~kÌÞÞ>+瘞qóäŒ÷¢!ô×QD OÐ(<ë¿88¢™á¸8èñßó»·�xýø”áîÜ ëŸÃZ‡Q&f�yd9-!¬H0�\B< ñAåÀ…Ç+Qà@†Ö`Øl+ kî4Êûño²¬Š’bµôeQôhñåÐ’M‹n¶Lœ³¦´«ÝJ©wõݸÓï ˜„wÇtü6ËÓc¶²ŒÇÎíÒÏ3ʦ–‘a·#A{ßí‘î�“¸ˆ³–§^y�òÍßÁ¼¬øÄ;ÞÆúÈûÅJÐÞ‹Ec�“ù’ö¬…ž�¡ª¿·-ë41@6úi’s§¬ÐJ É]ÞÝÒ+Akœòã_¾Y„y\<Ë IyÿCò?ò­„ð²+ヅát€ò ÚÛOL»RáBÎ3BB֢ĬЀ¢“¥Þ»ËÊyRïxåÖÕ{òD¯Í /ÿJ,"�w±L¿aݨºÆ§íír顇øì /ñÅ—Ÿbîûd�-†—¡Öƒ5Ù¤!ÎâXsã†@gãbº³¾¯4ñ-ä‚Ò�ÿîã4±•�ªË’~¢Ä’T(ÝØ:ÿØÎ9õ�÷îzZÜ�–lZ´xªZÖ@ýÄhçý£ÝÝd{kKŸÞ½ÅGßö?æS§³˜¡ÎN0ÎbP„|Û¥ð™Áæ)¤ «¦‘ôâƒÃ#劻#FÝœ®Ž-²b-6|˜ª@•%aƒâðJ¡µÆCwóÐËÆhn}=ÆuŒŠÊÎÓ¦}L0¡�ÓÆbO�⺠W•B²ËÅœÙ|–7¶㽢ŗCK6-ZÔUy¾¶ÍE±Ló9u±ä±GÆ'©X^kŒµh<*ìJ>ްYÅÂD­ 'g§Òšúd6ÂqJãu*Y��¾®q�bœÃE‹C¬¿&©@ÁRŠí%WuÍÙìŒAÀÎxGþ,•�&VŸAÙHȺȹ�U�xT =P��ƽ®d©eY&fË¢äd±ŒY`„ù!Z“#’- ßÑyÔ㕖ߣÛí0ŽP(¶LéΗ7^j{¢ˆ–L �Àù‘$LLyÞ¨¹ .¶ØçFCüšq¾¢…ªyÕÝ>Gž±:Ó<3xæÖ9O‹/‡–lZ´HL2éöz{J)­¼cw2¦ßé€Ö�$¨àþñÄ‚ í,*Ä0r'YV8çðx¤bµ¢±BDk‚‰$á#áh DkIþæ=Ä�®K‰é(<ë¨ËšIbÂ(� wMÏdã�ÖŒ  ‘ðQù$  É8±Êp>*ñ™a×V@‚goÐÇ9+J·îrëø$ª8‡q„XÃú¨µuèaÓ„ð›jØý,ÃâE�Í7¶ùý¯|Ñ.Ͼ-Õ�èXÈ™™H::ò¯œß§“c7>ÌqÈ1rvœ È8?L˜Û�$$&µI’6y–ÓâË¢�Ù´háámMÓôWË¥öuÍÕÇ!7±6œ_Ö…P¼…µeA¸N^:RÎ q¡õz}ºYŽWá)„yÊÄ ×9!�[»é"w(çÚF,™£B\IJ H“„^Þáðø]®8™Í%ØD·„w ñE8S¯|$€M÷!A iðyGÿx'YzÃÁ�²±äÞSÔ5Áö ÷xdmbÊó}q'‰íÄΡÖ1èäìtsž½þKç’›o¼þöìôÕ7Ïw ¥XO\cCM`3Žã‰®3ç£\�õ›‚œZ#ˆ¼·¹NqzD¦=Zi–óYí¼ùºÎ�%-Þ ­eÓ¢Å|6}ŸÉÓ¾ISµ, ƃ~¨Üs7Á{ l¾!¨­�‰ÁYT°Ý®Ô×dYNYV’½ž¡5NG‹Â‡ -ÄJ’c ‚�™ˆÝiÃEK(ÆZ<ÓÅŒƒ£‰'4MCc›ø¾1VëAÌ@‹DÉ&ÆOä9�Â9 4ç¶¶ðÖʳ¼R�M×,—ðY6ƒN1!\ ÏŠd§´fÔïQ¬–h­U·?Øþ×õÏN{‰у ÑÈHupu­šFbqáܺ˜¡VYhl$%+×ÃÑÅy÷'d¬Þ×P"´²MS5¶ihñVhɦE‹÷äµ5ÙîÚ¦iÎÎN°UI?ÏÑ¡Ņ̃€"â.¶´GëG)©Ú_+P‡ Áùè:’yëuפÈâ­%lìkrÃIz2>X!„gy“`Ó\žåÑtó.Ãá˜^·Gã<µs8±¦ÖVPލЬiºhÁ@Œx¥âûj³þþ·ÆHÍÑh,j_xã¦<›@8Ñ’ÑÈ•ãâ»xâµ�¬ ´|'8·G¢4ÃÁHíììvî~ý¿ÞÏTóë2Í"9XÏZ½u�¹&Þ‡¾/îÃfã5NF$!‡(>ˆfÜ`4fkw·èot{ý†o…–lZ´õÆÉ|9¿pzrœy縴‘K»;xÛ ëjÃEÝ=D«Æ‡ /q‹¢,)B((š`�€�q/g¨¨´¬-‹uL&ZN®é@Lk‹&ZFJIKæÔHñ&ÝN—Ú:Ñ‚ð:¬ÞgQuµN‹–'»7^ùð.Ñâ ÌÁ£ó¤Z¡É6ÖqtvʲXEë …oš (àÖäãN›ßSÞ#$1(Ý4¡“e,æ3êºê(m®Þüª¿øïókS�½_ ;¼–Àn“L‚«-Æt\ !E´€ð`T$ÖV’—)ç5ÎYF£á¥$I.h”¦Å[¡%›-f³Óí²,Þ�¦i†w$Z³3à„ì°àêŠþ9ÇG1.bf• ÑåíÉ[“mFƒÚPÑJpZËý1>ãѰÎVÛìÈ©Ö×´Ä‘!ia}ÍØ9Ç ÙÕV�I쨪Cz3D„L4AÈ”ÃÉ{Äç Q°†÷~ÃÂqƬ Ÿ^žK“4ïÖ;NgKNæËµ§À˜MƆ5ù¢‹–MÓDW¢÷¢|0[̘ÍÎ(V«¬iš ÿü7þÒ‹�ï¨ïè$,„´ŒèóqøxNíâXG$¢å­¡HP:¸ê´khÊBÚ/4uM±*–¶nœÇÓâ-Ñ&´h¡µ™äy§?O˜Ï¦Ü9¸E®Xî•�•‚±¯IˆÝDËòN‡ó;»TϾ"™Yåj† k ØH,ðZ�2ˆË¨©�g*âßQD‰—˜< pk7šÓ&ÌS¸ ½–$š²®ÈMBU×k —¸¡j Æœ„ø”ڈɱD‰ýhÍ)µA >�U§Û¥“çÌf ¹gjΦâþZ›&¹Ïr‰ß ODtõ¨˜NšñÐ…ó<ýú5¼R‰µvÇÁ{¨eíûuY ½Cç]y7¥¢äŒÇp.Ðܯ‹8Ó@*6ÎA‹>=W—¡G¤y6®q[¶¬iñeÐZ6-Zø�IÌÈ$.�?'}dÆD÷TÈ¥õ°NIDr@{‹ÂË|r$Òý§§g,Š"Z,JE«Bî&º­ ÆtbþY°d‚Í$8�X/�¢Â#ŽŒºi„8Ë9E]áBŠux‡¸nð ɼªDyî"‹u2kkÎÅ÷lj"ƒ:FyÊ;._Æ(�V cRÞ¸{D ´X;b0(�ЉøMÙš�L“%:yƤ?`oççÎ�WÃÑ8ÛÞÚ´ñꕼ8=›¾ú%^ùªÀKÿê¯sûÉo¦šO‰ j‘8¢�ÇZp:X0Žhý(µI:�t‚NRÆ£ÛÛ»xç+ommm£iñVhɦE‹,Íß]–EÒØZ ûØ?·––Ñó™¸sâ?�ÝÚ²À;|(¨´i&.%� ®«¥”9æ�žd¤ aâŠñ�V‘“R½vÇÅì0!€µu`š:ºÒ¼Û¨÷Ñ!擃2cmèHå¾Ü+ƒ˜]‡× ÄBòÆlh¾Ä7‘HH¯ÓÁõZ¥R“¦»ã1òl-–§‹%ÖGF–Õ´Y“êýPžx¯÷1pï½èÐ º9e±âääX•uÙ­šæ]ŸýêöÂ;‹ß¼Ýœ,/~ž›ÿî¯óôŸþõ<ýW~Ó¯àœ§¶Ä`< 䳋 �M 3Ý”ŠŠÑ®®°Å‚Å|Î|>•lCç}¦”r´x+´dÓ¢E¯ÛwišÙÕb)ÿ“Ñ#µ1j- ì‘ÿEY_J)@@@j4Ê5¤i*u6»“‰Ôœ¬µÆˆDc~#L†ü 7kæ„4jk �‰Ö–Kb4Úi3½³µKãNR®ÍúY!03Òd]¹pÑU‰‰À9ñÝAéyÇéb&­„€�yYâiy4 A ê¥"¡î�)ÝQ'Î{ÉðÓJÓít­Uÿoýò_š>¶UÿÅËöÆoœwê¡iÈë)'ßõïùÜû\ÿÖ‹mêu¦Zã6‡V2Öð©Èý©‰í£ƒ{M ³ Ÿdhc¤#jšeI–K»"Z¼%Z²iÑb¶š=˜vó!xœs¬ÊBŽÄz„áe6A/C6y\’ �$¸Ñâp2ˆ{ÝÈçûã6Fƒ]Í)�P® q?íÓ$](¥iñ–hɦE £tÞëô‹ÁpŒ÷^²ª¢¬þfAΣ\K„'f¨9ÇÁ½#nÜæäô„ébÎÙb� ˜wV°n¢KÍÚµµ@Ð#Û°p‚�u›ïã6¥gVÁ’’® ½uU®-<˜¦BÞ'ÊÊZlô-¡P‘È‚•!÷bGbF)Í}˜~¯Ïd¼E’eLg‹%!6Ó¬Aà£6Lèt›MϬEõÕöǶGc!yë\š¦Ùù,ËøWÏ}aVΗ¿z~zúO­mæÆ¤…X9óïø—<ÿ'‡ßõUâOÀ¹ÄFl‘„ “ÜÔ«%j~*]O�1 ´SŠN·Ó¡Å[£%›-<ß;¬�µb…Œd©=‰m\ŒexO@ìæ(<“á@´Õ”’ŽšœÎæ8dS–ûœI1D/%±‡lº°¾¾jdBœ'`ÔZŲ’ž7ÔA1:Ó&¼7!Þt_ʶ6±ž'aÄwaý\š‚‹Ë7Ý»+I ÇgSIPð€veÔÚe¦Öñ H<¬cY…@épŸââî¶t<-Ë’åj¡“$É�6ï&à[O¯OONÖ/½ø‡¯¿ñÚÝéÙ©«êŠQ/g0½ÁÑßûݼôg~%Ç_üvlUàî‹Ù¸ K1�JÅ6�‡$ËÉúCQh¨«Šéñ½E¹Xž®V+E‹ûÑ’M‹ŒMc›- -Ê‚ª*1€öNtYBD$‡¨�TXoô(Í…�!œý½}†£ g‹9uÝk²ŠG¢œ~´œÂRÄê}Ù¤=ëd€¨·FX/ªEŽ»])$ÕÚ�˜„ÊyP„ /ðZÇŒ�‚ ›³N{¾Ï|‹„¬£Î™ÖŒ†#.îìImOcqI.Ëj­"íÓDÈVùè¦$ŒMxÁs!–Ô8/$êÃ;Vu™:gÏCÄwÌŽýÞð÷4EùËÓÙ‹Åbµ\.D9ºk,öåïàÙ?ñKxêOü2–×ð¶Ž%äãAŽÖ�õA '1U嫪�öÛ÷ë¢X‘K‹ûÑ’M‹¾±Éh²=8wþB/I²àÁƒ¤×UT$VˆAïX3b’õÚË2²ÄH5½Vгù\â !Vƒßáècü:fB I D0ëIŠqŽ0 ¨*!ç¬è¤U¶Á£"É­¤3‰±¨«¯Š&İ¢r�n*´ÌAHa2²=aŒ‘ß²vžùj…Ü/óˆ QE`ƒ`¢ Oø>M½–³ÙôEE Ë;lmm“¦Y¯nš!÷áo6ßvvô÷]c¢«ì߯s0�ͬsŽñ Ç¹ž¡|æëøü¯ÿ!<÷þ8·?ÿ ³©X)krA>GW›s�tä[{ä�®7I‚­k•˜ä ;xZÜ�–lZ´0iÚØ¦š–e1O³ŒA·ƒ ›]ŒSø3‰1d„ÏÑÂXoúÚ;2£9;;f¶œSÖ–Æù°1ËÅòŒ¨·†Ç‡çÅK�* 1†â�HJ“~ áx/4kkÆS  ¾"ä=uTX“ņ0§ü_‡ 7%ï2HާStHÏ{}nŸjƒbñ(jÃǦ\Mp³…T Yí,ÝDÓëv‘–ÚIB–çi–¦���w'¼Þ$œ/�ã_{xëÖo~õùg¿íèèp:_,šNžsno—�~Êêëþ&/ýÉ_ÁþÐÏç•ðÇ8xò›°Å\ᬼ«‰Irù‰Ðø4WŪh¤a\¯—{ïëo»sÓÓâ~´dӢŮ8å˜'&�çy‡ñpLj g×Jr¼Ï�¦Ö @ø7í~ž ÙTU%™a½NNXWî‰ä7\³Z EU¼wÍg.Æ]¼“ÍÛ™$ÆL¢uĨß®¦0G«X¼éN ñÄ®™ÈÜõzžøn*�a°DÖñ“ÈXk»Y�z�`*±ìަ3üFf1#OŘS$œ˜Z]–Èý:IEoÍ9GQ”¤Ij<Œ³4;Ï—Á¿|íÙÓ7]kÙ{ÿŽnÝúÝ7^{õó·Oʲl´RÒrz”ƒ¾ý<Óoþ‡¼ú—+ßõg~#_øŠW¿æ2»õ˳ci€×8¨l¨Ù©+OZ–UY¦ÖÙ-Þ -Ù´hñúÉ]f‹Y’di_Ú+\´{9Æ}PÝ—àƒWtIÅ€žwÙÝÚ’Éi–3],¥ÖÆ+� É 6²´T,¶„ðbÙ;°®± ذN”‹­©u’�g9yÖÁ®ÛRG 55QÙÀÛ5o¶›Þí„õgŒ‰–™GÜgã~ëœ<Ó¤)‡³M] ÉIs6£ƒ �‹É÷u�jÓñ(Øßšpaw—^§‹Öš¢\%ÖÙþ|1ð_À3MyS÷zØY÷Ó'§öεë/E1e }ŠRíé±Â<ÿ-Üùžçþêïæ[~Ó�ç™7‹DŸû‡Š›Ÿý¦·^gqïIšaŒF+�Ñ:F´x+´dÓ¢Åçïx@��œTÖÚPSá×m¼^WîDZ�Íx޲v½ ïM¶è$)Ö;¬ÜllX"f—¤E7ãߨ¤‚‹®0åíFFšÈÕ%ýÁPîwÎ’(½!Š ®‰å¢kËÂ{n$0Äù:F)ɲËóŽ;98¼ÓŽ{ü7àkî¼Q¿Ïy¹;èÿ~ר’%Ù/­VÅßžŸ>yvzz­*ëEU–¥ÑÚæYF]Ì饊abéS’¾Œ™Jk]Õt‡}`ËRÑâ­Ð’M‹ÎÚibR›wº®Ûí 98Û ’±u±@ÅÍ9ß ˜b!—FòÔ0™l±½½ÍªªÃF®7Ä.•wÑ*ÚˆYø/›ìƒ+ÎéèRƒ¸aÖ¢´‘^6û;çÀ«0SoôÐr‰K¯‰FI$Æ•TŒ%S·=jC»íîéÊ#½t”ÖÜ9ºÇ¢\�ŠÉ(!¦ð&jí:“õB– âÓŒ!IDˆNÞ!Ïr¥W÷:=ϾåèŽûÎùÉé¿»ñò?G©ß�˜äÇß=øC‹Ó³¿º8�~û½ÃÃgoݸ~ëôøøìøÞѲ,Šª×é¸,1(¤§ y–/šUu¸…Ñžo…–lZ´0Iú%ÀwòŽíöúÜ=�R[Ç1I@†r~ÝZâÞèHHJ±=ì³3I+ã¹µ¼q÷pcxb[è`­l$ (â³T !­­H ²©ÉÖ †¤YÆîh„FÝ—|à6Ò¯7:pFÕiÔ}†Õýnµ�)¶^{g8 ŸgLgg,VK–Eɪ±±eÀº\ßàu$.Páorº&dEÌ„Kµïh¼—Þ6óùLUMeæ‹Ù;øïEL—^|Óñ�ëOU«?Óét~Mžw~Öýäjµú�årù·ç§gŸ»wp÷©[×®½8?9{ÅÕö¶F¾IDŸÅ¹?¥“ä=yvâiñVhɦE‹ª*ï̧SuëÖupxÀ�{ÇR�OUALQ&º¾<Ïވŭ éïÒcg2‘9³³3ONÐMb- q‘ˆ„�ÏÙĦå[)oÖ½¨>S•ËåB†ÒÄm† 6% ºekÒ#º Ux~£¦Æ™dM\NÅk“áP …¥N 1u dJCОSr=XX1lƒ¬¤Ö ßU)E·ÓÒéu{ŒG&ãí|8*þ'â›îÝ)¿åäàîçŠù3Ï4å_@é_i›æ‡¿9~¢‚ßQWÕz“dþx±ZýÞD'?šDÿã§ŠEE‹ÿ Z²iѺÑJ­•3Ú¸7ÁáÉ)³Ù mëGÙ°4TÄ‚NmPø É•NšòžGb2²»{tBéD¦§c±�Œ3ü—!–øÙÃ[�àfœ^š0ŽDöQ¬X–%6Ö ñTE¨+ŠV•|–±© ­™±ˆ6~ Åd0/jÚ¢$�$ MÓD-8¢•†µNG.ÆVŠðnQ[-ëõ1&áì쟛sÓÙtô‘ÁDó¿O•‹êÉjyü¦Ëí•o<ºý÷+åÿ¢3ê¯êòO¾IJ÷¾ýøÐÑâ?‡–lZ´h¬µoŽÆ¤‰ÙÚÞ¡ßP4b1X®ca¤ " øÍMWî5 ¶&¬– ò^�EQ3í™!Z#1û ¼R± Ñ¢·!M£CâB$·!z8[ QH åb…të´�´A�9±†'@$ˆqnª>³™=¶A´Q´Ç¯]nZ+¬khêJ\yÝnOjmÔý¤­…PPZƒQrÔJËÑ›Œü1&Þa{8¤\.E—¬ßëS¯ŠpÎZ›ò¿ ßvxÛnvêžZÎ-þKhɦE‹O_zPõº½Ú$æNo0$íthŒæµ;wqu ëMŸµô~�ŒÉ ¢ó%^¼y ¯�Ř´b¾˜Q—%y"1$q·å�ŽÈ”«%U±B©Ø¦Y\\ò\�*k×nIšQ6 :�nc=Øg4íßë«¿£ÊCn´ÔeÆÐxHÄ=‰ÍÖpȼ±R#dÓ”<í ½q¬%maì1óŽЫ§�JL¯(V;Jépoÿ›>xàiÑâ-Zü7à=øö¤®ë8gÿàî… O F£Ûðƒ.ìð©‹;ôÂF¨¼ßÈúò„€yšQF¬zC´ÑœÌç4JS5 Çó%ÿò[?˱u8çÈë’Ž14Îa‰}öûý§«ySÓÛÚåÞ|†·�耡4“јn–K�æÝ{G$i* !Ž/®ª»wo£µÁÚ†ÅbÆÙñ ÎZq¥míîÒëÀ{!Ÿ$�Àb9'1)ÆyÇ4Ë0ZËõ¦iÖq Û4(£Á{y^]Wr?Σµt(ҦoÎ{¹?IR²<“Dò~UUR–+!®¾úSéhª´¡nj‰õ„÷�÷ô΋¤¿÷¹ž�Ücqv*šsÛ{{è~Ÿùtzóöõÿø ß1½7£E‹ÿÛ,›-”RÎZ›uý³<ÏÍÙñ±lÐßpÓc‹_qi€Ó…�ÑŠçO(¬5åYÓ°P†{EC’^¼~ ’Œ!Ës)­ÊгÓc†ã £q¹µÿh’e¿`ûÜÞÏnªzlëZ6нN‡z™Ä–UͲ±ÔÎK€~e-™ÖT¶‘Œ¯ÒZ©lN¶¤w�Ö“$²¡×u%n¡ÑhÌÖö§§',ƒåÐïö$Mxµ˜�RbÌgg¤(zYνÓc!Ÿ­Éãý‹,¦§ÔË9ýnŸé|J–¤âv“Þ9@’e¡ïŠ'KS�ć�ñPAÝWöÏQÛ†º±rïüø„í^N7IImCQ–(…ÄG:FÓÍ3!ÍÆÃíù‚›'Sé×Ó›L(k+9=Jøl±b¡à�—xK¿ß¥…ü²,•~; €2)*´ÃNóÃÁˆNžQ— Óc°ž�=v•'Þöû\�f™ÌùÜë×øS_û-œyÅééé¬ZÏzûKž©‹Š-þo³lZ´ðŠ7ŠÕòèøî¡ ¸P¼x¸\±ÔZ´¾ºƒI’°Z.YU%xȆCRï±IJ?Ë1ZIìÄzX+(k”Ä'|Qpzxí,=ç@kÒ¦fì*FÞ’ ŽÉ¹mÉÂÚ ˜\ÜæÆÉ);£•¶\yô ÉÖÝ<ÇyG71 û±ŒðŽQ¯‡RJªøcÖM×e´$—šÄœüáF)4`V |]˵u…ü͇Öˆ¨¨¸¶.?„3 eUq<›ñ׿êëxþ茳EïêGÁ[´Np×�Ü;0†nhSí‹)^%¨¼/î?¥ 3×°Xžbo¿ „›%g>võaÆ€jj¼kÅÛ/œçÒÖ˜“;÷ŽF½“ªúýNÿœ_G‹ÿ7Y6-Z|ÿK�t«ºþu½ÑàggY~q1›©åb&Á}­ :5r>OŪXrïÞÞ¶&‰Gt󌦩ƒŒ�#UŠ�A�Ë»æe-©À�<£›$ º]ƽÊ{Ò$‘Þ7c�{‡$J…؆ ™cŠDjgÆf{ö/ 6tÊbk5×pZƒW Þ:›N²ÏîÞÁë�‚½Z€mPžMxÚY 7jìå‡ Mäã ·nóþÍWq€áV5 ûØ'PÆ ‰J…�X;ž”µ«ªY7+kí?bš•Ì9?òkøä±í±� ¡…¶óðuÏ¿Èýªo†<§“å§«ùâ«´V?óëo-hÑâÿ˦E‹Ol�{ÛÙlú;{ƒþ÷õ°µ\-T/5|êñG˜.ÜÉz,}Â�ã9óYC§?Æcè\¹ŒlSÚ†F)gwxÿÕ+<Þ1|â=邏&tµBHC!ŒF…�7Döc}Îj�YNñ6*?«ØÂ™Ä¤ëëNâ'bs.&Ê­Õšuìz)Ph! ·Ñ¾@Ë$� ÷ýºÏÌ}P²^¬³1ot /Çõ»‡ÎæT�>bÍ4­5¼KãRužÐfZ~+”’DÜô™r˜$—ã­ª`å……ñD=5íთ.ðÈÞ6w(Êr°,–_é�ÿÀÿñØM‹–lZ´øŠó•^UïþˆSîãE¹Ê˺P¶ªùä»ÞÎOøÀ»ùúç_ÁûŒ'�+òÇ>ŽN»è4%étq*¥ÑÎ᧨å1b8Çv¯K’¦k…�Ùð‰"—˜EqOï!BáέëytU‚mðZo‹òb¼ò Õ�érMÞI+œ1`’PœiÃ�Dc(¨4G¨õs”“¿QyxöÆM*`¹\aÆ�ì6OàÕ@²(D¾ÆZ<�&ë�mÂ÷÷øÕŒæè) Ö:Q À5,Ê´^+¨¦�¢Ù?t‰þÅ—QÚ$Ý^\«Ÿû‘þä»>»8­hÑâÿ„‚@‹Û9§ìlñi­õŸO¶>yéÂåÎ¥‹¨�ÉŸºú?ó‰³ôpTÔœ(CÑ8±bt — ¨½YWë'Ôém.Nr:Ú€2Ä~/>º¸6tÇ@;»Ñ‘ÓW%÷#²Ï¢ê@¼'öžñ^†¶ Bf¬�¶–Ð�SFX#´†vk ïUÞJ® ¢ªp6_pëÞ �ó,ç ToJêqIJQ!aÁ:'Ã{/Ã:KÓÁÅ.Ó§·èú‚ñp$„•¥™t>UJá“E$P¼'ñž]¾ˆ±VÜ’{çöû;{ûß»“åï¡E‹ÿ–M‹ßóº;¦<9û Iš~Hò$åʰÏÏúè{é% _sý§:iü¤ÓG%J+tØ =g=ÕjF}çUzW/p|tD·›1^‚W]/½Ö„×<±Cf¸�û‰D먣–uVM´\¢ú2 ÜlÄ÷�g&àcc6�’#Þ"d@€F>(Äw�¨‹¶&yŽ‚;ÇÇâF³h¼³ØÕRHD)„’ÄÈgkÝúšÇÓÔ­Ä…†÷ …C/ŽåP+±˺AQ’$¹Å{Ö„úÈö„÷]9Ïçn`’D5®ÙZ®–?þýÑ“O.¦Ž-þwY6-Z|¯óèT™¼5Ùúƒ£ñÖ'óN'uÖrzzLguÊÏûÈ»¹2p0�ñ¥Ûw96†ÙlŽî1Æ„˜BÜ{­u”G7ñµTÚKºr7OQ:6G �zT°dPJF„Èü£lƒ�äÿª ’—G�¦°& ûÁøp¿ŒØ3vã Ö�|–kFN´µÑÍUœeMª3±�œ5ž~í ŠÆJ]&ÃôÇá÷Òk+Ƚ¶t¼wxçQZÉð€óŽfqFyó9I ŸNO±ÎJm�¤�ŸœË»±Ò°¶—œO?òyÖ m#ªÄ¤ÉdÝ=Z´øßF6-Z‹æÜå-œÿ¥EYüêÛ¯¿þnï]ÞëöqÎÑךñžÇ¹²5­ùŽë·¸¹*ñ&¥.kt>@×ÖɈÙU`œe4�âJzY— »»B6Áâ�#Á2ða(E8…”"—îïÓ�'ÂËÕ¨·¬t ‘à‹í–7\kÑÒ‘ÏñÙ±ƒ±µ@8¢ÂuÊÇ ýlVUÍ‹7o‹å´Z­p:à w$ûν–̱�ÃZÝXœ÷h¥ÿ*Šæä¶t7ív»TU%„ÔÉ;RCT×™JÐ&þ¾AWîCÏóö� ØÙÞM¶w÷ÐZÿÄ�lï*Z´ø_M6-Z|<oUuõ«WuñÓVÕêcUUåÓÙ”“Ócr­ø^o{ˆO޵5‚ 5Êü¦\œæ‰ó#iÉ*ÏE@±|ìU£cöš_3d$™õI ÔÓùœ›G÷èGÈï”d¨¬GU7(…�s>œ{¬µ²œs^î��ÛškOc›Z¤r„à�•cj ‰ YZ­ S…FYÊ'¼ �QÛ�$IgY¬~ŽªšmZ´ø_I6-Z<Ñ�{ìý‹»/üän·ûÞáp’moïJ ¡ZÎyç΀õî·‘ ÀˇGÂ"dÎKãÕÛ·9<=wd±*PÝ1J¬5�J‰ZÒ´ÎÉrYšÈ}I9e”(rQ@Ð4M#¤1��É<í]øn>Ä­Lx×ЇÇ;>ùàeuX-æ¥õÖöΕ^§÷ChÑâÙ´hñ‘Þè¡ýw¼ý·¦YþC³ùýîPïmïŠøã¤Óãû?~•_üćÙëvÀ{œÖ|ñð„Þî>çÎ_¤,+üÖytbBöH@[‹kVg¤Ê³5Þ¢) IÊÖ �Z[ ±N&~Ž6ņ°g]½eˆ],½BàC f¯T8šp®q&fšA Ö.½Øœ-Öé@ÞAGHdM>l"|�PyJíO¿vMÔžmcÅRÑÃ]!�¸&Äi�‚M-Do´^§Dû`%•‡×˜ÏND‰º¬JÒ<k©Ûë‡,¶à:Ô ÒTž/äûðˆuó±+—XÌfåŠ,ϺÓÅìW¼7í iÑâ24-ÚÍ®<öØï²Eù}_zò©ÔV5MS‹ëìàÎ >²?â§|à�ìö»ëMýx¶à³oÜàÆí|é OrûÎùd6³,Kdãtaã$ž‡ا×ëŠL’h­‰pŒî²p-4Y[g™9!�ˆMkdÓ'÷ ó7ZQ‡ú<¡ó' 97zÓõ¦âó‰±jýÂDøðlb�çÉlÆso\tÎâuJ:ÞE…g£ƒ´#@> #ëÄņ+W”×¾@S—¸Ϻtñ"{»çèu{tòœ4Mp¡·OR|–Çx“�?ÕW\¹Èn7e¹\R…O&oëd�G‹ÿ3Ѧ>·øøhçC—Þùöß¿µµýÉgŸ|2WZÓÍ»,Vsiöƒßu•ŸþÁwÓϲ(Çóí×nòÒñ)I@3[Ð{à½ø¤+÷Ȇh]tnÙšã;7°Åƃ.®ªNFh“ÊzÚY™§!Z8^Σz€\`݇Ÿ€‡ˆÖ !5د3ÊÀ­&ƒ<“ÍøŠR>\ŠÏ؈¡¡i6,0-ëm¶¥–'¯ç:Ÿ_¸q“kwî"ª•Eww0�>>�JH¨ËÆ(�‰Ù8ç…ˆ døMIš² €­ñ˜ýÌðìá!%Ö㢨Ö>*IĺñIÈØ �çáñ�üøUþÙk¢ßf’´ÓÔÍ/y_UþÛ§ëÕ-Z´øBkÙ´$sá¢ú^ç®|òñ�|øúÃO?óÙÏæ®ªEù8ëtô<ñà%~ÆßË MPD—Õͳÿî…WÈCÆã-vwÎÑ»ð0h-›bž§›¤P—<¼ß#KàììT´ÒÎïN¤:>’Dìy³Yt‰Dy‹jáU˜½‘º,ó¢…ã>r?ŽÎEKY'¸ù‚Ee ÔÑJŠï>Äl4n¿QëJÿ²±|çó/âC�q¡í\�ÎÚZ!¦�l>¥4*¬£µ¸Ö°‡¯£‚^§Ëްϧß÷’�½¶*V�žâœ�Æ,ë†kRÎãC\è‰/c ±l˜ž�ª4Méu{?æ.+Z´øAK6-êÙò=ûWþÝãñ䓯>ûlZ­V¸ÐA²«5Øñ+¿òÃluò ÿ�¾xçPZ/– šº¡4]üpï �n¬ТÔ\pt÷”^g€³Vb6�œßÇ#79qGNKD(¿NÙ� ¶�ˆ˜UÉ*’R‘XbеŒˆ�í¦Ø´PÂÑ£‚ûL…[â:ļé([(ÜyrYI—ÑϽv]ÖòH;˜ñ>‘� IL¨­QâfLSù,׃ªÚ[Ôô6à°¶‘ßóñ‡®ðÀÞ£þ�ñxBãóÕ ÞÛ£˦? üVQxTiî|ÿþ6àIÒ�ÏQê'-ŽO.Ò¢ÅZ²iñþN?ùð`üszãÑFóßþ _—‹£áX|þû[;¼wgÌ/úèûD·LÃ(ÌV+¾î•7ðy�<ï0›/Ð{� ’ ëÚJ*¯³iV6»Çòìe¹”6ÅÕ‹çÁ{"”�Œ‚�¢L¯b†WدUŒ#ÁD*Œ×ƒ5Ñb!& ÄD±BÂ5 €ö!‹‹˜Q¦�Å4uLöÄØÍšA9 äú+>}ûóºÁV½m©I e€_§�­c��óÁºñèÙ!‰kȳœ~Hž¦\ÙÙaº*Å –¦)½~_Ú(è5á‚7)>Ëð‰|ïX ëý4áG¼ó*ÍbŽ¸Þ´N꺺 |?Z´øoEK6-Þ›vrœÿ¹—¯>úk³nçï~ñÙ¬“døPH¨µâ±Üò“ßý0ã<�– äƒçÉÛw9ó)»»çØžì�f]²Ý‹dY²Î˜JC'O±Î㚆ĖŒz:yWº[žßÞ!76Š-ÉÄXI”zq.Z8®Y_'núž(U“Ö“GF”oQ^G>[‹vMtÛɽ‘tÀòñè¦ïåoºmœÛp§­QZ‰¥ñâÑ UÈ¢³t‚ê�×êÍa ŒQÔ�[VŠu¬8¼Æj5ÅãöœŸLøà£3[-9›ÏX®–¢ °* ¼sè@´òýÒ 7šÄtsôÚj}×þ_ñðÔ«YÞQ[;»}£ÍOøh|™-þÐ’MK4½$ÏÃÞ—™ÑúáÕÉÙVš¤ä�.ãјÄ;Þß7üŒ¼‹�^7ÔyDÕ`€¹ó|Õ«7XJÐÚ²X­Xöv©’uÝ„˜8çĺISƒÂAS0èe8酪Úö¨O²Ö/#Jøk �(6uÉd³oÖïCˆ�@,ÂTlwF}0Y%¦Uk'Ö á¾Ÿ1FÞC†Z�[ �N™uOâS£\Κ áh¹â;_¿�ó¶/^"ï�æ�u|Æã%ƒL‡ß @þ®C ´j êÃ×ñÎRU•¤=?¸¿%²A¶ªhB�çÉé=îMgxçâo¾Y ÆD’$žjÍ÷|ôé>*m œK•ÖïóÞÿ¨ïuéAE‹ÿ%´dÓâ+'{ÙÖîÞïýàÇ?þ‹ÃáÛn½öºéäÀ3_L±«ßëáËüÜ�¼�~–n¸B€œç…£W5Nk–«‹¢&¿ð(:M£+ÌÉ\ThðUÏϘžÜãèÎmÙ4;y.m–Ó4]ë’içÖ–EÔ:‹JŽa}!^²‹¹¢ˆðàuT–Fžeƒe£°&Á&Iˆï(œNÀy¢u$ˆ–’,áÖääM²©…&ûgýÎÏ“·8]8d*ó•Çf£�`˲Npž@Ø��¶µVþ¦Vgô•¥ßëËçbµàÂhˆK ‘ªô‡lMvÐ:ád¹Š1,„dźñ�ùDxßse2�,Ä¢Xª¼Û™dYþC¯ßC‹ÿ9´dÓâ{ï]åÝîÏzìÝïþ±F›Ý›¯¾¦ª²}²ápÄÎpÌW.ðÓÞÿzYJ”ÄwxïÖñ’¥‡o|å‹ q_–%MÚÅLΑ&fÜ6F£@®Iœ¡˜3ì¥h­X,f¸bÅÅ�í�À^éh­D‰É¨p)’±€s]½ ˆ÷æŠjcmh1àâÀEK&&SoXy„K& nCõï9]•|õK¯Ñi�dl]¸ˆêMð ä“ F+ŒÑâ–ÌÒD~'b,«ƒ×XÎÏHÓLblÃN‡�<ò *ÈÔ­Å�VÕ5Ç÷8^,ÙDÕk©mã!{Pë_C꨾ϣrn0’µò,O‡£ñ»ÛüÄ÷&y‡-Þ m�M‹'º£á#ï{ÏŸÞÝßÿÌsÏ~aÿôÖºYÝé3Þš �çѮ槼ïqY†÷qSàÓŒçxêö!+Æ{”Nè\¸ŠN;x‹¦¬› ž^âxÛ£pçÕÑá*J&ÝãN…Í.Zq£&ùQa8ÛxÂÜxï¦ø&ëôã˜à # ùXØ©¼‹âŸ1ZU¤£\�“SšÀÈql4q‹õ@_<8ä ·±(‰ �/^æ¬É1ÝÚèÈU�Ptp Ê'"ÕQ.pGoàê’ª*ÉÅŠé1êõ$;/7ë½h¥YkÅÕ9].¢eMUÈsüx }ï.ÞF‘S|èâÿøé/I¬­pž²*v—óÅ'´6¾Z´ ¢µlZ|´?\xÇc¿sÿâ…wûÖ�+w¯]Ó�m@ÁÖÎFifüÒ'>Äþ /qpQÚ?�…OR ¥ùæ×o@Þ¥ÛÁ™~¸OY[Š¢B„$aMXuc1 |±b9_¡•&5)à97ãbˆé¸1ÈÎÆµµK̇ϑŒdÈÚÄ5ÄgBü—½Ök7“Š-âÚ^â;ëMYG}µûynÃ:«­ã™Û²†±¬Ã“’º³�2ITÄÖ ï¡ªEÂFzÙ”U#¤a…  R,Ïl>£. ®žÛf?5¨ª€�á�n·OYtº]Šª&&-(|d]IƒöI FÇÿÖÎs®ßã#^B;'ïÖïÌxkû‘Ô$ßÿ‰ñNZ´hɦŇÓîîù«�üöý ~òk¯¼œ½qƒ~§�VJÄç§Ç|hÒãç~è=ìö:xB¬Ä†GF”?{û€'¯ß¡ö�e&Éñ; †;ë ¶1&º‚éÅ"ÄP.gÝ•~+Ï Óa<èGëa#/†[»×”'¦ëZ{¹Äƒx-Œ¨{ã.J�Pð¸ A®y¢)ĆLŽÓ‰¬¹™ùF€b�ð¦eÅ7½r�FiòNIuîŒIÛ(¥bN+êÎ�ãÕºxS~Ó ùãŽo ê‰ I¶æ+._$k*°cƒNN–åRh»3ÙÂÛHÔQÝ ²Vø<ˆD¤5‰Ö|Ÿ«sa<¦›Ëz ƒýÇ>ôÁïUÍO@‹@K6-Þ û{�>üG}ôê/ž�œîÞxùeÎfgˆðâd‹^–ñÁý�7³ÎÞÁ~¿åX¢¼þZšÞe9•ó|ýË×ñI.uZj�a¶. “­TØõF@9Ÿâê [­ÄíS¬VdF“*%¤á£22QFoZ!>¡òlÊý£6óñZ’jƒÈœ‰ kÕg�vns µi9ymÖÖŽ¬å¢Õ³ñ|ßçéÛÌ«šÆ{QeÞ¾|EZ0ø$ V�&$ D•çç= „XßPßÄY+ñš<ËHð<¼=OÈN+9>5iŽ��˜/ܹwŒ·þ{geËu�éïœÂKÍøõ@h±%+f'¶“ØqÆA'“L˜™‡13aff"3Ä [Ìðôˆú5_.8gÚ{�UgÝÕ#[²Œ“·½ÊÕ}»ºnÝÒëó×Þûßÿøûæ,·Q˜F³’ò=Š“ãì«S ¢F�¹>òäÞ©{¿þJôn.ÇÇ<ž×Ó×%µ+·¶—lmßæ¶ÝÚ^w]ZŸº 6—ãÍ:kî¿âÊo;vå5Ÿµtþ|ráÔiqcœ›`lb‚$¹u÷®- š«˜ˆc|(ðSúÈbŠnï;}ŽG–×É„A(eža:ŽiLâ“OQ–#6Æea¤ $¥Ÿ0�ÅÐC«Þ`fbœH{1LŒ/S¹ÅÊg4P5éíÿÓRÀg>-q%AüÑþ=*I¿¡ð¡F±§,PÃ�“çͨ|ø’]?/DÒ§W”ˆsi³Ü±˜æI£�’³ÅVÒ4YVÈÞXë~†|=ìuÐù€8ޤD–çØ39ÆÈì�5†(Ih¶ÆD>H§5  TŸ7Äê¶z›Ö‘°®Ô„ >ûØ!f'&´¦Ón“DÑܵ×ßøÒ}W^ýÙtqår|Ìb Pælžÿð§Àï? ü4ð½Àï`í;¶Žùí­íy—Áær|Üãê0®×ÆÆ¾5IÓW?õØ£­“�?F{s]æ1ÐJæ(Ëç_y�=cMPøð„9–@Ë¢Ù+ zÿÜY]&/ ºý.ëÃ=6‡Nê„�“TÆ”S-”³„¦ÐHCšiMÜ|ØgÏü¬”g”Ë´,ºZøGÁDùÅ}8F4ÏFdbp4hy= GD•ò°e*û€ÀãßÛ;s¢üÌNž»«Í€ÔH_DŠ¥n�/®PÊk0$F5§±Q]€ÙéeiéÙXÙk—%J9Ím¶»F-rò4ÆÔBMâ( ±Ö¤iŠF‰5t˜Ô¹´ÙM¶Å¸ëÕ Ì´qhd3`¥Wõ‹ \9=ÉT½ÉÄø¤X"dƒÁÄ‘ãÇÿÍ`³}ˆËñ±š�_^ ìP0¦•Š"­ãn�Rpø\à·ŽÕe°¹·¸©1^?vó-?Ub^ßö®\[º*«¨×›Ôš B ÍA›/»î(û'ZÕ‚:ÒãÀíãéQ„ï[^cÕh‚(’é÷Þ`È0¬cÆæpä0 ­UEuV@Y €`Ú˘rÀf{SÊhqЪ×ÐAèeî•w‘µ„Vl ¥!ÏGyZ¾w²Íµst@S1:©ñ@ãË_ÛKc>#‹§hã0= º�EûÞ‹Ë4ë É4â(æ–;nejv-ôf-÷K;PÃ�( ¸Á«?[wnVNÒëlJ)N;‹çÙz ´Æ0d`Jr§»†µôË51å²/¼£œ§�. 9IŠMk¨QSjQȧÜM™ °ÖŠÞÃ÷ÝG‡×ÎÜ÷¢['§5ÿŸÄ S3êúñ ýÁ톩iõ š[€_Ž“ âfESií›h꯼ö 5‘ÆA¨U¤ ö?#õqˆË`s¹t–ÎìÞõƒó33_k‹âªÞÚ†R uý´V¬Ç—]%Gç¦jò¶Þˆë³D1ÆùŸ¬¦ þòÞ‡‰ëMâ(‘éôÆÔ<Éø,A}ŒÒ nJsÛY‡¹ì¥¬VÄaLh‘™ža6¥˜k�'ŒÌ€0Z@Û^¦2U^|Sá�EU›=GfjüÌŽ ^)ZDA=ðøK@éóxIÈzDD`3Ëy×™%r¥¥ Öšœ mŽÑ3‰|_¸�›¡©æ“¢PšÿÕì�|ÝÛDwW)D! @S­g§ýÜŒ€W$ºsQ’P«Õå|ÃnWèèžÆ�û/N*Ø45Zã´O�Ñ ÄšBI2ìö'÷>üeÚ¨£| Çõ-鉼|kûº²Ûý3Þ»µÝSv{²õÚ÷omŸþAðù8ÍmÀ&�¹Hëp2M—ïÛ¡ÿó‹nä—_~;_zì ¿ýªOã»æu†;€ßù`ç2Ø\Ž�Y¼xvWsÏÑ£ÿ~a~ñuÏœM#êVc¥4…5Ã>ûê)_wÛ�\·8‡ò3$Û#d"^öSÓ¼ã‘ÇYíèu:Hùj)zz'¡,ŠªbT•Ž®«”ÎCÅC·½B1ì‘$ Ãá€é™9Æëud� BŒ¼g _{o3ÂÃ…ò–ÆîX|(ðǹÍXC×*ä¼A™û'úÑNŽg¬ùóy°sÊ >KrY±»¬ï{Üi™‡—.‘»¦/·¼ç®Ç)ãNžF@ç]£µ¼&ôqWN@7Û+è2cj|Rtåâ8& CöLŒã5ÙàŠùÔú*ýÎ&—–—ho¶E¹áâú:à{däG‡hÓ6ŠÝ·Þ´­E|Ε‡¨Õj’Ýœ|ôQÆÆÇ®ÛwìØg}úÎÁ§ÈŒM|d›,ÿkàOÜÿJà pØ•¯~ø3~0kØ÷qšyà§\¦2«•b2�Ãkæ&õ¿=~ˆãS¤BÎQL¥ _yõŒ'‘J‚@1°ø©-â@ý2Ø\Ž�zÜÜšŠ³"ûæõKKŸ¶|þÜ¡•¥%z½.ìÏÄÔ»¹~n†o½ýF®žŸA¹Åc±xS/WÇA›(‚™9.¶»üÓ]÷²º±F·Ó!Mk$cÓlš:AkcŒÛ,ZD<-G�v’ø‘2´šcHs\dÃçNŸ`²Õ@èËÆ •¯æ¨Í†Ñp 0ßxyÚð´g-›�7c3ºÊn¶•Íœ‘›\ƒ',85—ѸL ™C) p׊ò¬(xÏést:�6Ýn‡v¦)Çw ãº€‹µV€¥4Òòµ#Y€µ•ÂΊ<¢0–žL¤5SõÔ3 Ý i¿?@rGqRDÄ: Âú·ûLô#=ªz¥?«êâ†Å&mIGB_ï¶Û[û�Z©í«V6V�ó)f8übà½ÀK�H� ” ¶¶0Ö:ŠE[{…jÿxã3 ë[ãúc£Â^o?ìsY�jFaùªƒ»ôÝz- �0‡&Æø®›¯¢¥VÊ!ð"¬}Ùe°¹Õ¸!iŽçÙà‚4þ¼$M¯Íà DËâ^o6Åzù`«!¢š{'ZøE�ÐÅÙ&)fv^2�·?ø0'/\Ä–VÊq¥‚«®¿žtÇË›Ê5²dâ0D2ž^›];çÙ1?Ãä䤸¬ÌOMÓHbßçðÞ0Õ¤¿oÊ›‘>‰U’ aGen|ãà rº�Q/8ãhÐø^‘. ÉxÚÛ=Ëq~&¹F*•gåz*ØQÀz|u�wŸº@¦Š<§´Òª>�RÊa{àå|3.‰ä¾UC�B‡ÆÐo¯²¹q‰�΃áhZqÄxšø÷5–z 1i�(M%óÜX_£;è(…rŸSÂz%mãÈÍ–sòe<€NÖS^~`­´ÆÄäqœòذs×®çï:røK®OѧPóýy.ƒhh¥Ôxů8°³öƒÏ¿6úŸ/½%ü¯/¹Y¿þÈ>u`²¥„¡Øü©É²oºabJñÑk¿¸˜ÖJÅcIÖ¡ÝÍ/;~˜¦d“ÛCǦ&˜®'A#  üøxM\›ËñQ‰«ƒøª­�ÍŸ™˜›ûìéÉé«ú�nÐno²ÙmCÐHšý/žcïDÓ�W<®¾&Œ�Œ¦VÇLÍ ðœßhsç§Xع‡Åù"W6'yàä:ª1á¼?¾„±†¢ô>6y6àä‰Çxüч9{þ q²wažzûlÃøéðŒ)íddü¹©L•e`Ù6Ô©,Õ&¡ðR4JûÉSú÷WJL6o Ö›;;iEÛ»tj²G¾ÇÑ”ßÿÁÙÇ6“û“Ž¡'v ’:J«*Tn CÑC“{Jˆq"`žõIÍ€<Ï%C2Ö0;3Ëâø˜d8à]G×{=ÆÆ§¨7šBæ0Ö¢£„~žãd=YBU},òQŒ�œGªÀ™nÛµÈD21=M¿×!2¦4Ñüü‹&§g¯úš]Žå5§ÀÎ7ÒðGn{žþ‘[¯ãUûwqÓü 7omßô¼ãüÌ‹oán½VíŸlÅZ1ð#e¿ÿÕ×�Oª�â5í¾¨)HÆâ(825~ù•‡u- øP1“&|öÁ=* "ßoÛ‡µ¯º 6—ã9Ç�IkÇäŽÅïOãøµZqÝ… ç‚N{S”Óz3ìQï®óe×\ÁM»ÁŽ [:íßJ·«LRÊñ Ìä„�0—Þt×½ô­¡ÍK+KA}5µ t ¸'p)¥!6A( î0Р�òÙÌôŒØhg•<Þj�D1(?™?²Wà~V•ª|&aF…ÏàéuTL7Ù|fàÅF€³|ö³F£� Ÿa•Åh?NÝÀe:ô†CÞwú<â�ÏÄ ªÖ"–>W0*OcìȬ¯¥.�uªÏe>¤iæff™›žczbšT‡ŒÕjè ±>˜n¶È×VPJÔŸeÖflÇ.zYŽAÉg÷ÆožX`”;�V”­qˆ½ª€kg‰åÄU��ÁÚªX†[,ËK—XرãºÉóoØbC†ŸÜ})�}?pT�šoÖŸ¸ýzuÛâ�RÛ²†ñ$úo/¼™WÜ(€:ðSf0¸í£2´Y“Œð»\é,NÃÀÎÕSõµ×•ÌõÃ…nZ˜!ÐJ§L@üÛ­sÇ—Áær|ÄqS}üøìþ=ÿ3@ÝÚ^[kæÃ eÒôì<‡æÈÌ _~ã5\37ƒðëÅH(€(‚4EµÆª~ÎcçÎó¦ÜÃéóg¤ “D"‰Ñ‘d5yQ0H¿Á=‰'‰<‰c­Hâc�@+égY޲JÊ{Ã�uiÔjd¯¾åBgíí¢…¹,±xGLe¬lî{¿l ñ 2÷^fÛûŽÌôh[2ªxk¬G¼tôH¶ðÞ³9»ÙAE�æIs¢:±3H³/µräŠã¬J— á¬lwS h·7XÛ\£Ÿõéw7 ”ËJ M5¤»÷`cc�öÚ g—W(]I÷; À1ë´×�C2¹f U¹£z©¼«æ¦E-J:íMÎ<ö«+ËÑ3í³jãcÇ?¹û4ÙQà •’ °ø›¯?®¯™™D)>d,6j|ãuǸy笚ÀÏoe$ <ǰÆsN¨c@P�Bõú£û¹rz‚g õ7/Ϊ8ÐV«ŠÍr�5æðe°¹Q\Ÿ4—¶üÏÅ`ðò4NöG*T‘Ž�…ICH —Îóú£û¸nÇú$„îL¿ß£kbÂ…Ã&¸·v~5Ö-›‘MB!`­•!_=M·Ó)œÕ›gìlÕQ^u¬ ½¼„hvúr߇N²Æ8¡Mh}Ï |ïI˜i¬R6tplvš«¦Çk¶h4›B9ú µ4ÝÕj/½eü“sîææ…E|�‚zÁk�ìáŽóXžYL&1ßwÓ5š ¾çyõfÈG[¿_4Ä‘ÖÃïY ^±wŠg�(Ü*¥í&TWJÓ@¼ü2Ø\Žg·4'�·ÆÇljvöÖ"ËÆ‡ý¾4ž‹2§9ÖbarŠCµ”{õܰ8‡¼˜¥_H<ÐHŸ39-ÍfܼÈ}§ÎðȹKJ9/ü‚2Jѵq¢™]`}ÉÇZëÊ=V$VúÃâ¿b�¥0%¶è“çò2§ÝÞD‰\MÝ•È4Fù¬åedFYSTªÐ 7c3"Âé8=øàäX4bn†1¥ð{ÏV3Z(زÇ<{MAQxJ¶r äå{.\ä‘K«¨8–}V¨MŽÏ‰¦œk¹aM7øêT´Ö&ÞIÁÊ|�m/QKc„Qf­,ðãiÊd½†Â“¡)Ï…ÓíR«7‚Pz=Æâ€RU îÍÔÜæ>£UnÖªÞ”ïT½�zðʃ{�ev~�<²tê´0Ó”âöA¯»À'adkcÀ+­ƒ¹fM}þû˜ŸMìhÔ¶z9ÇT- C�øj[–Ÿ%@ö„-Ë#ÀË€fѡɱúç]±?Hƒ€gRò µÒI Á—Ò^)€ö,â2Ø\šc¥-,mÔ_Q¯7¦BŠ­²4­ñ vîßÏBòÚ#{¹aǼ@J±Mê+‰1iŠ™šAÝBÔéõxïƒ� #Í%�iMO 1@M,R‰Pz‹¢tbÐ ã%`´®ÀG¨ÎÀX=`l¢% ¤ÖšåóghÖR°ÆÑ›G�ÆÜŒ�Ë2JtYVý™“1–maaû| ¾vè™ €ÛPnó½"Ô(‰Âe†þ5@åC·Àã ZÐÉ þ鉓 @ýR.«O¢'ÑqŠÜ¯Ò P\d/÷MQÍÔÈ&Ù¡ËxR�ofE.‚˜Æ–L¶ZüçCäjZ"¤È³Lú?*©!w¥+—™‘ž”Å÷õÐ ;9Ž2íàŠéIš½u6ÖWÐa(ÙÓÙS§õ¡£ÇŽ¥ÍÖK^º{¿â“.ìgÒÀ¼zv’½­Ï6,pÃÜ °ÔJE@ü‡lmýÀGЫ‰�ÎÙ„V*мlïb´w¬ÁG;›u®�ŸRq Q A¶+­1­Ë`s9žQÜ19xסC?µsϾ—h¥¦óÁP&òçæ“„V#e²³Éë¯ØÃUsSàÃ/(¿¸„1¶1†�]ÀÖê«0–w>ð0O.-³!’(¹?Îî=;ɳ”¥ªn‡€Ð)JY=®)%›¼k6�§ñ@+6Ûôzêµ:cõš£9�* ”oàëÀ/~£^4Û2Û Ú½¬5à�9Õ¨•´gé¹p¥¥ÊïÆYG{‘ʼðj[bñªÐ'×7¸ïÜ…E¦üóÂ`êSS»¼•øÌÆÝ7¼�¶©,µ:ë¡PY.ç¬5š¤i�±é™jÇÓe!JÍ(E«Ñ"@‘^!+›Ç\”û̥φÃü| c§)¡[¿êð~«+è@£´fýÂE 8Ôk½bcyeŠO¢p ûW(­EÊTJ)>’µâ Dz%Jn�ò«>8$ú,{57×�8Ðñ Ó|Ρ½|¤*E= ‰� ’‹:ÖÞzl.LJ�ëâú ¹2ÿ#©%¯TŠ©|8$fdE†U0>5A³Èù·ÇHãV¡Faåkð•]@96N9=‹Ss®&÷—ÛÞýУ\ÜÜ ×é2 hNNPkŒq¶$©Ã%-l5·z'äÑp�ð‚N»K{yE¬§§fç9|è0Iœ80-›Yÿ„Xïã2ˆ*ãÈ3¼´?¸M^ó�o÷r”xó4Ùüâ9ºØú²®wŒŒ,5^S @Ü87C™­‰êÔŠD�4þK#aìÃ[^s`TÑ •ËÈÊî*¡*„ =ÞgbbRŸWW–h„Á£¨Ç!Ócã4MÚ�MÖÖVD”³pTò£9 ¾sc½µ„#C˜zCÀ™‹º}ÏnÚ¹Èôô¬<”dƒk—–ãO{ŧ¿rfçŽÏæ“(¬1ãÀõl3ŽÌ®fƒç{Z >÷ð^´V$>ß ‡»Ÿ�Dð…Àx T˜�úœÃ{¥÷ò‘†^¾¢¥©¬ñ·-^›Ëñ!ãÚ¸ÖûÕsÝùS§ÒÎÆ&¦´tzmЊæxKì|¿øø®˜�ª$h¼Q˜ í^ORÌØ¢hð*ÅRÓÿû÷€'—Wˆ’š”^¢8fqï^Nž[¦ß˜­Œ¼\ïÇ•Ê4€¼n-²•ÎÙÀ”cc (­˜o�6c4“hÔ‚Ù!–6R6sKŸòÀ |vbŠ ¤~®Fž‘†ë=Ø0Ä&ðàåÑÈ÷;¬í½_*€ÒnBkï³F¼•óƒËk¼å©s$ã“b�„±”ÐTkƽ�ÙûMÈP'AàÕžÝa²—Ì'’åFJUy‘Ñh4hF15kÄMSYª ÚJij�µ†z£A%¬µ7égÊÝguÞÃÆgs¾/ej éí)‹¥D6åÐD“þÚ*RJ³†¥óçØÜXŸšžŸå µ±„Ožx5ö¦Å½<×xÁÎyL´´+YM_yÃÄ3›½1Y>ïš÷qèp뚸zz’òdu0Ä~¢€±Þ�Ü.ƒÍåxº´?i¶’8ù‘ÖäÔgïܵwQY%¬¦N·-s4{d²,øú›®áÆ�‹¾a€ï'¸�¾Œi�Ãø P¥ëµ¼9}–÷=ø:©ÑïÉû7d„œÉ[Ø(­æh”®ÞË5·e… yQÈÏ( 6V×P@Yt»mÊ2' ªLÃ7ç}£Ú—º\YÇ'?óDßS°zDŒÓ¯¥î®Dç÷xý5Ÿžù¹l¾‰Õ�£v�vmJ,PXË�gϳ>Ì@¬BÉz±ÂZˉk•¹œ›Ÿ©Ê�Xlè ëîµUƒ!E>” e0`¥›Q CtÕß’  7‹cMÊ<6ÚúÆ:ý"Ç”–Kkk#ZoÖKö`ýÌ�'M ¥4Ls¬zhñHˆXT¤EÆÜü�f‹Þf›ÇxÀ._8ŸÖÓÚm|òÄ<`J¿x÷úÔРcŸÝøØÔ8¡Ö!�¯)‡YÌ3‹×‹�Rz,ŽÕ;ð!³c-+ý!'6:•€®µ  •D„�_#¸rKHT]›ËáÃg4qYæ?¥ý%aÌ_ºtQõzÄ›$Ž˜Ÿ›ebØç«�àšùYcœŽWÕl÷å´0ÂÖ؉Ig­lG�Ï.-ñ;o| ëeÉf{St¯Œ²ä:åÑs=ÔøBÅ”0’¹0òºÌÁ餡PXWªŸgyåëk(in+ߨ®šíŽ–ì652[ƒ_âr 7.Êm>ªó»ûâKEØme<”9¶º§H�e̽s( %È{Ï^`=/È Q¶Æ6¦‰aps5 ”Ôtºg•ƒl¦Ê°\YqØ£¿ô$ZÂ@2Nš�õ$©î£l Ö0Ýj ã„={öIFd­•ßä…oö+*�RZ1Z²cy=pZcîsûÏ~ÕÂ7îZd²5ÆÜÂêl¯¯ë,îÙìl¼ðºzK28]ÇÕˆÂ`÷3ÈjÚYΰ4>]:°›4 ­”ÈÙ`Ì—>³!NÑZ‹µRñ«îâÈä8OYi¸ki•VÖ¨…�°Îž.Æ’˜©Z¢¥Á?n·e©/ƒÍå‰ÎîÔiœ~íâî½_<37?ßkwÔ ×&ÒÜŽì?p€ÙaŸ¯?¾�+f&Qàë¾®îûQDÙhbœt¼“ÜwaÅPíOßñn–º}¢8%Ò¡ôSö¾‚¹�»±S»@è¶TZ^NúÞ sêÊlKk%‹a‡ÃÊ–9«Ëë`Y6ÉʼnIB…Ÿ«1ÆÛ.ƒ§k;P¨¤j\‰� d›J¤b;ãNW÷E6ð}Ÿ ø2™lîFøk“Á �+Ïy‚Ò<¼´Ì™Í.µFKÊ…6¨ÌƤã•vž‰È&€gñ)Ù»¯‘Ü=VJÎ-¶ÖJ©S2F…¢Ä5x™­+°Œâ„fWïxkœúüNP^ˆ 0ÚKØ€Ò~n‹QºhŠ|¥ðàiП~hýÓ'(ŠLþûÖ’šJâdw%3&Ëfø‡-Ëx¡VJÏ6Ò`2Iøp(8Ýî2(J>T\11Æñ™ êÀ×?¯Ñ >Lÿèv`¯VJ5âP¿`ÇÜh¦åržÚìðg�Ÿä­§/[@‰âé#ÖšDX hds¨—Áær¼bç~]‹ÓWÍÌÌ}íX³5®KÔÔä´¸#îÞw€ƒW\Á±Xóu7_Çb«‰/ul³3®Ê’ÑŒM €�=ŧÏsq³qB67jì;t”Õ,E7§°@Q˜jÑ4Žš«\IÈ•…d+Œ¡Èý1qsóõGXXØÉìì‚ô&ZcrNîT£ÍŽˆrVß?¡e�* ß§Q€Åoø½3Nó`â‡6ý½pçäçޗó¬ñŠJWYbg8ä�O�fSì ²,ÇÖ&ˆæöÃ(Ó¨háÎb@,´=1Ìߤ¤¦�|rbJþ`áâÒ9ÊÒ Fý8À1O[rþÔA —‰ÿ�õUY6Bn0:ÄŒh«ù™,ãX�Õ=h´ÀéÙ)_¢”Ù“kæ¦(6Ö„Ÿç¦4caUJ]Å':¬�€`,‰dûpш#6‡'6ÚR}ºHÃ`+»ÙE¤µV8ì´Eqó‡s‘öŠÖÉgÜÍñíJäÆð–3çùå{-·²;Y‹¹i~úÖÿRÉ|"â :Ò—çl.‡�«›cjéÒ…Ïégƒÿ6>5uxÐí�^GJOF&'ÇÙ9èñ﮾‚ƒSVü�bŒcñ”—9‰4Åϲx#²µv‡w>ü=4"¨Hi “ó‹<ðØiºµJ#³äEIéJd pƒ‡ò3¥¼Ÿ�ôåµ–Î…œ>u†å•%V¶¶\k’X0Ê ªi#ó^qÙerÝžÐPè,ƒm&kŒÈÕ`Áe'þü¾D†#g�™�ð�ÜÔ”a$×\�£V¥xó#Opÿ¹‹ �¡Óí0È µ]ÇÐi½"ÄQHj$îž�8›ÊÏœf¹tk ï·iwÖ¥,·±¹!,D@6uU*õz2nhSÊlÍ:¡Bʯ+«ËŒ�Àç;೯=¸ hþᩳüîCO²2ª™Z¢^wh/¡ÖÏðãBi-ø/ƒÍå�8Öl©† ^'µÿZRî9wöt8k�31=ÉŽF�ç•]¾ðà&ê5¬VÛh¨ ߣiŽQÎ-`“^~ÅTòùý,ãïî¼KžÜzyN¯ÛAkÅìâT2Π¶€ª�Ue37°9R"ÓZãH^Ù?"3µ„¥óg±¥! "Ú«+h¥…eå´¸d�+y¾™ò™ŠW„®Êi–QÕU-”…=àcD�…§P»ëw¥7W^óC§Ažá²1Wг¬2¼p‘va�¡U â:LîtësPÍÉ÷¡–}á¼{À›Ì *ä ËΪ¼ÞÞ\§, G‹Ž˜Ý½‡©f t027Sõä´fj|‚±F³¦E ³‹„3‘+ÀúR(Oùö4éJõÚ¤5Pž†š£Ó“b†iÍäô ­V‹8Œ§Pê&U–øÄÆ ÕJéøYLæÏ×SQÀÉv—_½ÿ1)«ùð1‘Ä2$ªª¦_ÌÓ„›y™ µÖûÆ›zqÔ£F˜f¿÷È þè‘§„#2•&úKŽ”>Í3 %›Â…2>É"är|B"ÍËÏ®OOþ‡$©-fÃAÜï÷Äü,nÕ945ÃU�˜×?L͹MŽÄÈS©•�ЛÇ'dÁÁ–È¢jýB›Ã_¿çNÞ÷Ø<µ¼L·Ý‘Eqbz£—63ƒ»ÑNZň’³ôfd±rr5N&Œ£@+" é<9²s Û¶¬ ¥L7ù‹w€GÏ]�sŽ¥)i³gvŠz’8Åè€Ð©X·pŠâ€U–†RJë?›o�P’‰¼´¾k~[ëíðâŸxÆ™gl)—j8¬´Õ,Š3ëœÙh§uК’�áÌ~QZ‰Âú¹#'bPb­B’ÕP:;­4A( Näî;¦D)Éxp÷B¬Œ)I…¶%Ê(Üu @¢5ûægÅ5Кf½‰±pveMΣr?>ñ¨+lƒcðÔB…Ö¥Áš—%Êbøâ]saâƒÌ݃^›í±àZBúûvâ‡/‘rÝ/Ý÷(¯lˆÚ\# ¾êš+Äï™Fi­dFZ)߸…Jkû¯l.Çç¾FŸ8õäg$ÍÚOYk÷]¼p6ŽãDžDçwîd÷Ü,×ê’×;€ ¾ü�e´^ï2Uo½™ÐIŒ¸cÜW2Ô÷ÞGŸàέíôÚ:½n€¤V£5>ÉÙÕ!Á¾+‘Z~)ÙŠÛ@9Z®B� nR1ÐÜТRÕ$ûƒ>ÎT”±|ñÆñ¾¥‹v+ÃXCZsÖœ°yÖZh²øŸXï�¯oš:qVì^wx/�R(Ý8?ÍŸ<ò””±\h vÛ„m­UÇg&‰u@'Ïù½‡Ÿäl»g´Rú3íâ3÷ïþ€Æ+ t²‚¬,•µÖÉDðæ´7Ì¿N°¹,¬9áŒOL½|u}yJ¹Iíù;847Ç ÇRnÞ½P™‹UáË>Â3¿ùãw‹ ^_¬zò=»²Ê_½÷.–çΟ! B)×Åõ:ë½;w5»�Òâ<ïA9Ðq�c Él\³Z -/(Y) .Ó £}è&Ø{-v°Imívý<º·N§ß&” gœ8)e1Méí ³;� ÎâØÈ9¡�¯�±DaDÚ“r‘4ÁÛÉbúqŒ\ç©3BµZ�ƒÊ3¦› 9ÿÛî}ˆ¹É vLMGZJOÓcMym×Ì45§ˆ-a ÖÄ‰Õ î>sžRà›õì±|)‡I&ƒrêΨJÿlÔ<ÍÏ=ZW=2ëJ“yf Ócjf–öêšM¯Ó¡wþ<§ËIÌ>´J^oÓ J“ı›Õ‰ÝlN “$qsOð$‹/Þ XÜ1à')•š*â àõWf¹Ûã‰ÜRÔ똼@)­U¤÷é8üB[d{tøíÀi>>¡qÕÐ^^Èߌ¢g-ßÿõ×å?ßy¿Þš§âïNœ¡GRbÓî¾o1žÆôD¨u[( ¢^N$‘Œ üãSç8ÓîÙ±$Ò[¢¼tÏ¢ØG~üÀX\.ÚÞÁ'Y\Ž�ƒ°fm¢(òÿXk4^×ßZãfT†4’„YJ^±8Íí»œ¥²B�å_�bìü¦.¬$PŒ†{ážOñkÿÏœßèÒíg"Ùíu¨µZ”ƹÃȦ4¾LfÁÑv+:®?ƒÆ‚ •ìÖÖ›ò¹ßóø“\lwåžÄJsýž�\½c9.©aµ±@áký„‘0õ¶á[ƒêv‘À“ êqÈT=eøÁûÜí£‚Œ¥^o¨4Iš¥µóEžOï©5O,Œ�-ŸïuùXÆB]¼T+•.¶jÁËöìøH2‘“¹zf’Ç66Yí9¹ÙÀÙÓj ”’²Ú½Ë«œÞìbÁÊà�RÞàÂ…>÷Š}ƒ ½Ax÷Òª5Ö²Õ£QBxnå.yÿwž]?‹ëiýмü×”Ù\š¤YS~sc|ìµ*Ð3EY(hšqÂ,9¯=v€›v.Œ*7oÇë2šéYL£¨áD×Ü–>Çý'Oñ§ïº““k›bÕ‡±MR¯aTDÞX@ï8BÅ^jF©AH\SÛ"vÅî{Mé@ÅÊï€ôÜ“|$‚€«l¥ŸÆ÷}‚�°1†1MÀiaºAÎÂ5Õ3cЀRÛÛ$´%¦·A·È‰�õÑäkF”2:XB’0p äšiþ(Še椞ÖÙØXÓ¯(Že1¼°|I'³ÂP1g7»\lŸ@=uЬÛ%�B¦Ò˜ã s\¹c‘“«›äJ‹L�!$™Ú+æh*Є&@”wµB[ (O´pŸÚ¬ØjžÙ­PNÎ&ŠÊÖ ç¸�zdÈò!Ö"Y[Ì4jhŒc;(Ÿùê´&ʆX§ 7è÷éeCæÇ›(€²DšüiÍ µhWv<õÙx%åÍ„^¯ÚmT·ã5æH›™ÂDÅà_Ö»¬uzôÖZÕ¨7§­±_œ ûêEü�7NΆ¹•2¦µ>ÓBMYŠÒVr?81S, äuGìÃ`åû¢,ÉKåznr-„ΨlX*V{ŒUhïYÊ ©&)¥Ò¨(b8ˆïQ(ªÕ"j*Ç�ç�¤xÙÀâç�|?Ç tâhàÊýÂìAvŠb¤w;Ǭü4Æy˜·=ø(+AÄÚÊ Y–)­u«Þj¾xØüç-Ðù‰ëÇ'ßý��5ËÇ&>ôJ-†²?—Ø;Ö䛯;Æ{.,s¦Ýe«$ ´«Õà ´ƒiF‘¾ea–ÙZÊÕ3ܹuüroˆkw­8p:8>æä, ¼á‡ÉÎOmv‘~ X‘ uÓ–æ3Ÿ×l¼]ÇÁÀôË\×Òüý«+öÿS°¹Y»}�1ö‚(ÜÛëuCé ‡Ì7?Xã>ÄÑ™I?Xè}]¶{)…Ìј‰i”1ž�ä'Áefãﺇ?ÛšÒXâ,—~K"n�š¤ÞbS�º† 1†(Û‚@kþIð=¯éU•Ú¢(”ß·rœl9@šÞÆg6Ú5ŵ[È´Âù·nà1ÐZÙ,+0惛FU=(r8Ps^üÆ*ˆCÄ�²1E  X<„5%¡)¥¤`†]t1¤±µ‘÷éÚ’4žE û˜A›!Š�~ŽQ‘(+´ê-’4k©Õbâ$–Œk+ËR"J¢„¢ÝšÅL,€µo²[1—³•u€1ÆéÉcå{—ÕII\GùRe\oR4&ÑÅaŠ��væX­$B©‘”×`ƒ¢`½Ó¥QoØè `¼^7 jË)¹Õj8O/¤FÁ@™Âg_îuS«£›cèõU—Q{VÛd-!~äš½Wä·îy˜¢–²nJq-ò,û¥èÝþ·gù(Äp)3¦XÛ>¸ ØS“\êöYéøH-rc„^üÐêÍ8dËƒÆæÆ¨ZT?/aæÐdK€`2IØ=ÖàRo@¨´º~~Š=­¦�U cøË'N›-°f¥ EçÆjeOŒ©½ãM9�HÑÄbý ÛC®éäF‡Üý‘‘5ö%JóB…-M޶÷˜Aÿ=×¥µ§”VoÓip²ì™®ÒÊÞÕë|œBq9>&ñü±é…¼È¯Þh܆Òqž±eÉñ¹i¾ú¦kE¹Y)¶M¹oÓ½ÒÊÑ›ç…Úʨƒ%E{ÐçÞö.Þzÿ#ôó‚-`s22i³EXkÒ¯Íc� EWjÍaµø¹…QãmÀ` ç±Ödqô âz>¸ESƒû]í�Áܹ½˜u -7(:ªk†EK${Gö׈­æQ\ �êÚµÖ¼xÑ‘ëÀ5â)3²Î”¶È%Cж6ÓY'ver?PP:â%£Ê!½î¦|^ÖI®zᔹt>_Žt@-ÇãBk_ªô¤BíéÑZSº™žþ£ÿB£sš2ëÓ^ß`vq�ƒ;wrmhyÃõW¹ÌÆøY*çYtréßõË¿…ª5$ ³?ùšWph~NE(òQÍ!T4g?Øêˆž…&%7o³ ?ë÷ÑçÏ@Y‚1þ>`9³Ù¡=Ìd‘<µ±ÉÖdËú0+×ÃòT»o�J©N+I¬®™�€<8ÑÚ.[ãÎñßwo|ê¼é乆 ¯!É«2*P™5ö�X:RÌÀ÷:^ÚE2¬`T ÜÏ”cÌùs¸ûR}.w?ªS6Ìу Ìæ%tk[Ÿ�Åß³Ô¡ôâˆiÊ/ªÏº qä¿‹#d烧î¤ì­’†¨@ 5û¶…¾ýEÏGWE”¦Ÿb06ίüÓ›øÓ·þ ô¥áöƒ{øÉW½\ë±m£ ­qÿwô¿¸û·i¼�� ¯¼  Û&¼xÄ;g¤¯#콕^Ÿ@kB—1ýÇ·¿—§†!v9ƒÁ`¸±¾ö`Y?tO>ø›§±œÂÚàyÀg/sࢅ �ê@nÑsÑáÙÝ2Â5s“ÁOß~“iÌӄЗ·pyp{Ó©óÜ·¼FVéŒÞ°0­¶,¡Ù?Þà±µM¶² »Ò°™å¦›Z+e]Ã_+®,jKkQ(Zmé©ñ÷^«5žKX௟<Í{ßôË¢$Ò::RSÁDÄðmÌj X7õ£ž9ø¿µ‚Òæ@ÇѨ/ç�7+­ß½•ýäŸx°¹Ls¾øí8M¯Ã0(Ë’8ù¶Û®ç•WìÇ‚¾vŽû¾VGfiœ¼;lzƒoyàaþ|‹°1(ÂØ5Û3iv×&&›žã|/Bï¼’°1îú&~ô߃پHÊf½×Jy ñ *|i…´� cª~Lá¨ÓJù¾D…H®„6V8Ú4ø’œE®ÇŒò 8г0ð˜û?O,¢Pc îžâ]–Æ| S2,wdï@ͱï<ᨎq æ@[ºã˜T¥6—,ȵ;wžAÎÖ¡½Lvÿ?—]†Ý®HþL�µ¸a~†|ù§à2a#, !�<ÑÏøÞ_ýV6ÛLŒO‘õ{|ÎÕWðe7_+4u1Ú+� icgæ Ž±(¯Ja=9`;�ø'ÉqúÂ9)©ÙÒ‘ðàÔË2:YŽÖZÊN'76ùÝ­Làáö€µn‡~¿� º[N‘ï½ÅðÏÀ¤X{xð5ÀQ Þ..JvŤ×�ÓØÛ¢~¨Ek‚zÈÉÿ -žêw'tTÿÉ;®××ÏMó¡¢_”Òƒype�¶¶4ÐlÝs™§‰ó­g.ð‡¢äc“üÁÝ÷ó;o~§ûLÒÓáß^œ/¼î8J€¯x]–B0³óÃ*V GJ�>|!µü¹ Ã/—òÚÇl^4³c> Ã}«k+·köcjÉØXb� ã4-)ŠnwcÃ8é„sÀ; \.±�ß3ì•ü·�ϼÐóÛA-&ISrh¢Å|ÚÍ,¸ÆàvB€ŸÔúêì‚”9”ûáêf›7ßÿà–jóû8{q)é jÞÏìØÉÀ&ôt�`çqâ‰9ßãp Qõ5üBí2ÿÄîläu¥GGÊPÆ÷\f¡ÀO åµjþ¦(Ë*;�c°øÉw=Ö]9Ë¿çh¯‡PÜ¢î³ä…!ÔHMÞÙ&T™ ÖV,9Çv«¨ÉÖ7ã¢Ü÷v亵V8@Ñ,°c«lÇ7ÿ€ºk•c]9Sˆ ºÎ6ºóè{áÌ=ج/ì²fsõô?ð’ç8›œ*Ài«ù¾ßþCN]Z­,¡›iÂWÜv¯¿ê(Ú[ø¾ŸVؤ&³[Jë›mUØò;ÚQ§q6 •ât6tÙM×ß`Oü°/–ÈÙ(Ü}i…ÿü/w±•`÷WŠ,fÙ ‚mVxMémcÄ´ŽNÀèP�E#3r79û^I4g“4ùŸvƒd)Ï"$[b£ÍŸ=vŠwn�M'/ln õPt÷H�PÙ^^”ÆqàÝç(C­‚Zª$DV&+¥Ó)ý�ñ4æõGöñŠ=;Ÿ‘øfi-o;s‘ß~ø º´Ž±¶Ti [/ŸU‹Ÿ¿�Ϻ¦esCÑ.ÈÛ9XK÷ñ6ÕÝ·.S.ån¶ƒÎ›Pê5[*×½�‰‚À5arõ¼¿a˜ ¿Féàs÷í{u6>Oúx­^?^KÒ#aMâäêÒ–Çêõæ± Šo1eñ™J©™²|Ù¼õÖÖ¹hÊ þ?‹]IúõÆšYkDÉ9B¾êúcŸ�EuŸ xv`ã„rqÔë(k�ñ/ïãÏÞõî-¡£û¥H—4š-ÆšcÄqÊäÎ=L.îbÐØA´ç*tmÌ-`¾ï˜i(¥ð‹¢ï8b€+ ùÅß•º|¯Å-ü"éz>(É\†bWZò 2Ò_Pàý÷ý{+îZ}ŸFKïÅgŒË˜pó9²PË<‹›p9§”¦¶PVl0)›)ª>�ëóx¢€ÏŽFʉ~-‘s8àªÌÇ•«Ïá‡eå}¤4ÊgkPýùžî ƒ3£L. ƒ|H+xñÁ½ÂŒ×WŠÞúÄSüËÃ�K™5Ð�F‹éf“çí˜çŠÙiÏ^ÔÊ;‰ZçRêr*O¥FÌë´>Ev#Mf�Ê3¡“»¨”¨åÞà>¿RU?n~¬É{wsï…%y)ÿ=Ë2�1ŠÑ0(V[¯œ[ßóýGÇ&_0ÏĵS¤ 5ÂzH� ø�ŽEô/õ’á…aÐëå™Å[Ô’‘<ʳ(üÉ㧤7òÈê&ý¢°Öâ÷ŠÜÉÄ ËÒú&U ØXL^]#å4¥‘¬Ï~à ÿraI�%±¼µ"GmßNh‘²ÞO¼ûNmt1Öý±JÕ¯W鞆Ü�g¢ê^  °ä˽'Û”í‚üô ·}ótI‰ à­ÊâüG1³�Ù|¥RêÕAŽâ8™�_H,�ö&�FS&³’k÷‡’ Ý´¶Rf8˜,–e^t·¶óJ«_)ËòW¶ê´›|ʇܣƒaþc³9¶/Š"Y…ŽŽ×ø±—ÞN#ŠFåëQ#upE”;v£Ãˆ~žóÖæ-÷=ÈO>%}ܤ±"fIA€jL¢'ˆbêS(­«2P‡äE9Âä—œü\®aíz9ž‰V‚r „Sº߇p:e>kñD…[ˆ¨”ÒrNßòÏ“´Á½ÿH¶à€Â¡a„¹¬JBáË„ PHÈZ7DéPa”vìKtþUÙOéz(.;ÁŽè�(FçÿãÙTÍ1)EžIYq„´à@e$)ÜP«)2º÷þ3ŹQ ÛîÉ1þëk>ƒ�c-”sÒ<_ZþÏÛÞÍ{;A·ßÉÆ9öën¿‰ìÙ‰rö N—Í_½Ò˜(–ršŠ"ÀË#�¸Y¯(0J�5è£.ž‡|®,‰W'/d.»›¦ÜS(~âþ”•N—²”9‹FÎl�-P Â…˜ÖKf[“·Î‘Χ Ï&6î\áüÿz¼ÏÀd³iÚüœ+öÿæð> ^®ÿL§ÇýËkÜyq™-0�ò™|ti¨É$–̦´V¤ý³ÒX·øƒBéZ ÂéX›niŠõ¬Ä -=KWz�´b(UÉŸQ½k¬Áñ™ ®›�âÈÔ8”¤?óž —xß¹eEiJ+ãÕó•Jt�oª‰Î1ví$a+âÃ…-,ý =úOuè?±µÝ¿I¹šc¤*æ¤=\ŒÌC˜jŠ÷ð-wûùÜÁÆ/¢{�ŸÂðyÍññ©ZZ‹²<«êïe)^Xcɲ!Ö×Ä]s˜*LiÂÀfìt{�²,Nj¥ÿg'ôÞîz÷Sl~2Šâïn��G½~—DüèKoãù{vŽ0Ͷ—ÑdE¢\ØE?Œ¹ç‰'yãÝ÷qjeC¨ÊQÓívXß\“†ja,Éø2È8�pjZdg¤�rY‡s¹²�K¥И ÜB'4cåèÇx“4¼¸#rŒètæ_¦q™t%3ðe*,èHøŒf¤oâéÕÛçTPÞ€Ì÷xŒLÕ»ãGèÏ@u>ãÌÿ­T½(^ΰÏ0´ªæŠ”d!^˜TûÅ—I�”-ë¹kó,@åŽó ?’@•Óì=ä�¿…dKLÖSþÏ•yÿä8ViŠ(æÍ'Nó»ïý�ž>G†e)jÖ¯»ý¾üš£Ô1X4º, ò¿A^CkL`k ñ¬Q ¯&à˼`œ�,°¶B°µ©²¨^òp옎Öب  œ[äíç—øÏ¿÷Çlöú¢Ðø´)ÊvÎð�.N7ß:Øï¯3vý4ã×MIÖò´a!ßÈX{ç%6Þq‰ò‰¾�µ6@{ ,ÆNŽ©ZTÿÖ –AQ²‘ålQ‰•ò#Öš­:/Ú½h7³Œ7�óebêŶZ¸«5›I�´{üÃûïáɋ˴77%Ãi5Ç(­o·7!L©Ïí%š?3»!ˆAi\o¡’’)¤_Q�&1 OÍÅ…RžÖ(í¨¹¾)Ÿ»¡Hë2—àY_´ª¦;HÚeVEY¢ðöÈr^xpÁõT\ÏÈ�nQ6õ½0ÕÏOd¤lçKr®DèK…Á@U÷ÅÙ6‡a€Ï< ¬ˆÀÈ€¨¿§œP•ôÀ3δvé€Ï�B~Ik?ÈŠ#Jd+gé}à¯ Šž[ 5ÿûß|Gæ0:�§ðŸÙ"Üwæ<ýáPhðZÁgÜz#_öâ;Øè�u(KWJóŠ#€‰­8Œ�yÖ™/‰U¯É÷ŽQ�?¦ß¯$»1¾•ìc»›jR£?¿“¿»ë~ãßÄÒÚ3_µ�©ÛgÙ¼g�Õ¿9Oq¦�雑3†‡jŒ½`–Æ¡�½M߯°08ÓcùÏѲCñdߪ5×Hyí¡=üá#OõzYQ†²”©´´6ÈJSkEî?P„¥ìVZ1žÄê‹>¨_8?ÃŽf�÷_\áßõç»}!8 ) ”MŽ6â™­…~ìšIYèó�œ¥?:Iï¦Xͬ-lÅ\ µ…€ï]T xëé ,÷‡rH!Á–Z©ÐZ«Œô¾•�®Åá®<È‹v-2SKd&çÍ[¿÷O�ek.È­X]ÓAëÓçÛd(z�mÒ¹�r%£<•1ª*¿=ƒQ5M|EƒÖ�Ó´®'žI¥D×>±Ééo¿"ÁÃ5ໞ3ظ'õÉ 5©×^œÖÒñ8NÅ ËHZã,ì?Äü¡c¹åf¦ç™Ùì‘ltHë ÆöÎO×¹tñ<›,Ÿ_âïþøOX^[fíÒy E‘á; ó"Ë–Š<ÿU¥õÿØœ5>…â–ÖäÍÖò·(¦’$Q­$å ¯>ÌëŽDSŶùšÂZÞ»´Ê_Årn±ÖcV¨ö¥LöN’ÉD€fíïΓŸ Ph‹=2=®¶˜_On´ùó'NÙ7ŸTCÌÆ4oœV[moê‘ÏÒ~b“ÓßQ�MøÖ-°ùµç6ׯµk¨9>ñ-Q�S*PŒÏídßu7qã«>‡ƒW_/z[: ‰ò‚™SIº}Pï� šJ«©ã¼=äÒéuV#ÅÝ'åç—8ÿûQE.‡”E `ýþz™çáÿJêõ»ß¹|Áò)¯Ú}øuJ©_*l9‰µ*±%ßù‚ë¹iÇüÓÈ`”<²ºÁ{Î^àŸŸ<Ëz¿G-m'‰Èyôû=1Ûèö)¢:áìÒ�G‰Çg¥ïב&¿[@ÉóR~Ç¡§áZ ò³W(F)· \V!Çy:°+#]br~÷T>2€éÙQj²/¡ºRœ¿›Ñ ä½»�½ûä{I¢Pü}ÎLòÓ¯y%v߃üÞ»îĸì6Š"B¥ùüßΗ¾ø雊¼L–¡——PEá(ÎÊ„ñ½˜ @diâ;3‹râž•(§ ¼†Ú¶!O‹¨ Hv3è{´ e,õÁZæÉÊ�{ÅáÛ~æç Íîÿx%­ã¸¨Oçz´ï[gãí—(Îp l޶kÀæÖ*`¦‘ªWïßÅ¿¹bŸd¸Xd"óòþ Ëö»çÕËÅW¾Ù!¶ÍîßUÈ„ÿ÷¼íNNlt¬ zj´J´Š¯¨3ÿûhl�b{8Æ×Ú»—YþÝS”ë¹µ…Ep^z7Zß¾kŽï¾éj&“ØõQ}Ü{im‹p7»ƒÂZ[›™ˆ¾æš#:PŠÍ<ç|§'s?�­nÚ^^x%$PøØ0‘"˜�ˆ÷7h^5A}o“ÚŽ::ÑÛ{bÛÁ,—ÙüüsÉjŽ*­ÿxrzúh†A6ÒšßÉË¿üyþg½ž¤VÀ™Rïõ™>³D˜å éþqÂñ�E‡bcH{i@grŒödK†Í¾ë=üí¯þ gï|—ˆ1Ê€1v뽆ù`øDiÊï" ÿáž~ÇðI/žÛ…:üÕ¢Ì?§Óï6°%Çæçù¾ݾ‰1ïæìêÿ—z}þä¡'yçɳ Œe ZQЛ ^kÈbui³M¦S¢©Eôì>¢É‚0ªJAø²Q•mh-›*:³ï¥Œ4¸…Ë@”?É.›Ú 4Eî4Ý{šQ€åX[N\Óõ`<0º¯ÃlDÍ«¸9�(p�{ëãÒ;\º~”Ï<•ÚBh(œKõ9«n¤¤åzMn/�|ïõÍŽ:í@ÃPŒÎÎHfåÚ#e#‘A{•llu=^y?\»q�á] E 2ÜyëžEÞwú—66\™®$ £­òÙ |ÕË^LKÄ7AæaP¨•K¨^¼*€Ÿ¥Ù9Ñ×ñIpe3 #e5/Òið¥aGX[AomÖ”ø¨4ÿ<¸¹y±Í˜[à÷x„ÿõG†üüõÔvÖŸ¶St ú§;¬¼éÃG:”2°Ûìyùþ�b 0WOE« �žço>ô8¿~ßã`«!¯ÿû‹nþP³7ÒSù“ÇNòË÷>J¿(� PÑ�º¿c–ÉÛf Ç£gäp¶yÏ:kÿpžþCmL¯´›†ApËŽY¾çÆ«˜LÀG¯(ø?w?Ìß>y†aQæ@8Ü\x H¾(ðõ¡Â ‚C z<¢qdŒÖÕ“¤s5¢‰øSÅ:Onrê;ôØüŸç–ÙDéO$õú·Öõ¦5–´Þâ3¾åxÁg>Aùa>­µMÆ/®f6Ô¤ûÆ [I5Ä�]êÒ_°ºkŽ~³æå±/œãÏ~ñ¿qß_ý‘ôÄw>-¨bØï?b­ý^”ú‡­²ZÎ'iÜ1µ0¥•úý _XÚ2€ëfùáß*TF‹ÊòƒK+üÃã'yï©ót‹«4õºxñ �X2™Š ç`&Ñ­)Ð�ô‹.#®z#Œ²SGT—Ý× ü@¦ ?ïážÒÁçD?Œ¨•Òij»Œõóˆ<À]·[ÌGJo¾¯2ríž!‡g­ù¾Žg9iO+FkJõ÷Å“ dó ¡pY Ë¶À«\ûÌÃÉÚŒÈä(ü±OzÁ³ÈdÂ…§–ú{îï¿g¡ùYiî»’b%õ#àÖ_gpß?a:—hÔ›òÚX«)=“ÍÕ5òÁ€±zÊko»‰×Þr­ZZ)7+œEx¯‡Þ\‡<�p½Œ3à“ת>b@9=‡i6 L9"Æ)¿£4xÅ‹êçYFxî4d®w#€d©Â°Å tâ ÿõ¯ÿ^Àæà/^OºXçÃ…--Ã¥kïZbýÎa� Û¡ªP+®œ�ä³î–×rÀw¿õN6†YZ¿æÐÞà[®?¦"­?$ÐüæýOØÍ,+l¬‚pwªw}ë¡`?ÛÈ–‡¬üí9Úï[¥\ʬ5Öj”Iܾk^}ÛõWʨRT±™å’½üâ½�°Ü:„A—Ñùco"Ön›PS/_¤¾«!¤ ¥Ï:lÚœúÎû ,Cà;ž Ø„lW+³šMÍÌ4Â0&Ծ翈Û^õZâ8ÂZFžÖ±–¸7@%ÂvQ�ËT¨ ¡L'CKÉdtÆdza_ü?Æ»Ž^ÍíÓ¿xVŽ�’8Œ¢èP¿Ûýñ<Ï\›6þ�(Ã~†1åbF;’$Q¥-‰¬e¾Q—…µ�e<´¼Æß>üoßš0Nè DIB ¨�^VзÁÌ™݄³»+bô`X  Òü \Ü-R®±]5²­¥²ÀÕ{Ÿ�(å7꤆a£“ôà%V¼p$àæTJ÷”o±ÎÏ&� Ä�VÕ@/åZCòÂõ6dþ¼®IîÅ@Ëj85Ô�S:pMkøžÚT½/Œ*øÌÊeh¦"SxÀ�ë¤ÌMrŽñ'ŸÇõ›ÐH6%’uŸ_E Ôlj²Ý^W®›@³¸°@-ÏH&ÇøÒ—¾�[®8X™q™ ðjB  U=¼ˆ�¨1¸©¿Ç¢7×PZ‰Ê€×C“ßAU¥5%`¥ªlE€C2£`õRõÞ¾aãÞº—‰æk—.âËaÏ$T HwÔ˜T¤lX(n\˜ è{.®*)‘ËÖײ"9Sk”R:ÒÚ~æ�]vk¯>”,ÌoÜÿ8í,/L€‰&âhþ‹÷‘Î×øH"žI˜þôEâ©„þ™.ƒ§ºä'úzXšòí§/†­lðK¯¸McqÄV9PÊl?s÷CêäzÇÚÑ•+* Çg'949Æl-‘ëÎ-vê… jüªÉ�¶t³Š�ìW½ˆäaè¿oŽ�í�“$¨×[¼æÿ7¾øÓ]}ÝmHH¿fîÄ9âþm º‘žFÅr\V2xd…ÒX–öï ï&é•Uñ½û�oâwäÛ¬^"ŠCçéžÙ`xnÐëýüNÔÊSÐß�²f«ÙäÚ];xÁ•G8¸0Ï®Ùi”S��ÁÏÆ¬¯¢}T6}bP®÷¢ðˆøÖˆm� #÷²ïÙHFNÚë©Iä9Áù3 ïc}*¬pǺo­©(ù¿óàVmK¨“�åà¯ßH2�<ãlùŸÎ³üKO‘*ͯ¿ò&ÓD´ÍÞqî¢Þsö¡Wض€LðÑñ|í5GŸÖ+fm0ä‡ßu—ÙꛨaiŠ`<Œ&>w³¯Ø!@÷¬C�闔ÒޓÖß±d‹õÜïiû“YÔ7ÝxŒÏ;¼¿²±Vøxǹ%~òÝ÷°ÚZ�RßrÓqn˜›F+ÅLš¹àáÕ ¾á�ï¦[L}Ñ.æ_³›ç’Ù|×} KßÍÙüÒGœÙ„A˜ UC [§`c}�W=ÏÉ�à4Iˆ€Œ{�4òÃ^à8ì`¢W;g4;RJqõ­/ä³¾éûxûïþ2ýKçév7ÑJ‡õzcwYß`KÓ¹¹9ñ ïí¬RN»»9 ´¾³Óï‚`R«€»V×yÇOQ ež VoÛ@XfQÚD§ã³ûhî=ŽN”ÖÓ�„?N(S2Jã&Ó-ÒŒ–¬D)Ÿ�8M±Êý/Ž‚‘…ÛMÕ£¥_ºÁ?íKl^„Ó•Â@�P¥©zEaFÄ#(ø,h´é/ûHJŠÛçl\vãšæ"S3Òxw2VA ül�“ÚL•!è@1Ìr¬õƒ–ÖØJú?ÇB3ã{;Ú�¥׳‘c¼œƒ*u`-nÀÊt8äý.Å ':fE>Ä8«e2äý†=9¯CTÉVÍ4ÅuQPQ$6AÚ@¡€³) Ñø–Ÿ,H“„2Òîô¸¸¾Î�ùY°Tò2ÊàB2¬è¨Q,š{RîÎ2¬ª|”JG,°€ÁvÛÇ�¤˜ ÃÍ”¥])®QT@kÌÄ$z0@™^5“5Ò«õñ‘Ï B gfP’Ÿ—LMfê7†þÓ‹¬ŒlÆ›¢@ý'í^èkç§‚-GLHnÊd†åO=I'Ïår•Ö:Ýß š­A¦Àf†luHïd‡�÷¯Òï:v¹€TQE²h°8òš±¿|ï#j:�ù»§Î²eŽV€5¥qÈ!Îô6Ð*xÿÒ Ç[X|,Ôk„ZK­·lç�ÉÍs›zˆž¬ÀÆýç6aµ‡ý~„ÚXcõ¾[î�^ƒIEœÂÊ #i÷=£ÑMhûdUQ®öPe‰(ÓÆÖ÷ Gf¬Büç�ß|á÷þñß‚³Þ…”%¢(Zá‘~§û=ׄIçÞbø^>¡!b¤QߺÙÙø’<Ï^Ú^[›MÒA2éž�4ÿË¡%©�“N,,d¼5‹ŽSYdQ3Û ½üBì)Í€[,�—eŒ,4^Ì•£ði–$ «�KÐò{a¸lÉ…ÅK¬¸,7µ£™”z—ª|_¤,¬,èTe*…Ö>[•­*™Ò±ª»­Êļ¾ÞÏÆõ]vå›é�®JZNqÚ’   x�*¯ÒìÒn-@¤- :ëLo�òâãìjlnvX9›u ñ32a”JVE±X?( C«$IÝ UNKú{¡€z‘çNUÓ_{ ݉è<Ù¥VoíÜ…m%Ôǚ䃌Öä$ãµ”µÍÌÏÔ¨4¯#8G]&­�Í4…sûtå1Ž©r¯²Œ²ßƒ0Bî³Rd”¨Ð¡ÓRsD� ÍÖëØ|ˆÊ v´„æ×kd9ï|ò´S°†Õ7^`ü–j{6Ë)%ùÉ`|¯c42cx|½�µÖ„Z«WìÝɉ�6ï:¿ÄRo€(Î`õkîáš™)þñä9ζ»B€2½Â^úŸ'Ôúñ‹4¯ŸD7•"Ù]GÇš¢�“md2HÚyp�þ;Ö1�"�HGÍçd?]HnhQ»j ú†Í¿½¨ÊÍ"ÛÊ`ô�¼óî¼WqaÌPi¥ UhKÀZ�ÿ\2d�XT¶5 ¥Š3CÊ~!ä€çA3BË9(€îscÊçk­tY” àâ£ÊE'¾yênZ­ÝóÿP�£GÆتP�éeòs(l ¬/£mc!¹Eëðµ[¬šoû!~ûG¿��3'@‘ ‰Óäöápð[€ó[€s†O@–¢êázÐ+ ”qï¥,.“ÁbJï�~ê]v(Tàû�Þ*�@ãz-ºbËe2"Ív×°÷²:~ȱteF�SÎõe¾`d:_2!y= ÐÆŽ¨X x†~ÅÓJ#&lNùÚ÷Ëʳ~‘âÒ †Ë§iÄ£ÆQ½MR•a“Ðõʼò�µÒÿ’r´q#è†bEަßwu¹^÷¹ ÕjÐët¨Å!ÍZHûì 6Œ�EãÈQfæäw VJn8¼=%80pý–± éß(M0Þ†É{q =t{ÑOk6qƒœ¾£]) 0Z�Ú 4Ç ½ ªð´g”».'Éãzbûg&9³Ñ–cÖþôí÷¬ÒxÞcÏŸ=´°±=œ�Ðzé3™$†í!Λw]\Kk³Ÿ~÷½"£Àh�jÅÑàl»W»c×¼úãGNØn^ŒLÖÛÀ’=Òeí±Qb‹þLÿt—Î]«dwv!w #\xÚ|t°FýÆ Â‰˜±+' ›!:Ò’uØÒŒE¬þΙ8ëÃ|˜ÅNŽ"h~Ö\O§ oÓ}ÛªØ7Ñ ¿ìÊÃ<�G�r;³RPt ž[¨P‘̦d´-0.<·2ZYì�£0T[a,ŒÍ/J:æe�|8 !(\ 8Ôè4ôClí!vX‚RQäý4üyF@pÇ(nΗ|÷Oó[?ð�¬m\"©§aT ·Yö}×DéwÝ›zgIš7”eñyJ©ó²h”Ö|°§¤Š¢�§ÖB܇&$‡o%šÛO2>Ká² ²¿ÄYü¹0µd?Òð—°n SUÍù,+*�0?-ïAÜõ8Ü{¹áKåäZä{ßÓ—uø+úŒ¡»†ê­#×Õ˜@Å Š ÆºÁ·ŠÙ„ë©‹Ÿ $lNÖ÷(P"*l3UIݸìÃ÷‚¤W¥UÅxªÞÒÞp íû,~!•¨”RîÜTY”òMûWÐjÀ³Ì¨]zˆ¢³J8lCtiÉû’Ò”H–Š@L-)=×âÂ@49c3óL.ìd|q'+Kô7×io¬Ó¹tA¤œòáPÀ¬œ£×íI¹MÊkE^)(¢uÀÚ¥K¼·ÈQY� ³L4}3‹ªØdÖaAˆ˜©××ñ$Ð`Q¾ŠQè�5Ê(‚Ô|¬_|¶¢¼¡KÙʲÒL w­IñÅ×纅9Þuê,÷œ[¢85`ýÔy6þâÉõ-„º»¯)l. ;ðÙ¹WsÞ.°éBk•A(ŽÚ:¤ÜÝÍóàb×f¿ùÀôó"³�èF%W5I4é¼s™òb†-¡\Ëé¼mÅvß¹æ)Œ5÷À*µµ.&Ô¯Ÿ ^¬‘Înm»j¨@£c½­ïã*Ò2½‚µ?8—˜AYJÅ*ÙÒZ£uÅ8ëï[¦÷Ž5…±bêöÒ=‹ÛûNw�5„ê-ý¬�œç’½%;ëtB-7 ~=‚@ጮP(ZS2µî)Ï£C`U“O‹nDèZTýáË=\=†¬YC!1úû°ýÜîÉýš+®âu¯ùRþøÏ~�NªÞh´Z¿~Øïo\Õ~âž¼ßçc2w´; ¢¯µ˜¯œœ˜žL’Z¸´|^@¦VkPKk,¯.S‘î¿–dÏ•Â,‹ãØIÎ{É{§‚Œ ×Çpƒ’hò¼iÚK™Iù,Çâ2¨žˆQ2lçŽñ*Ä¡�ghÙj¦Ã÷ ´ö%8×q†ê“R&É—NR^:�(„ Ñü‰9, œ¦—7=s”h-”èQù<Û»W"Óñ(¼^˜g®yqMïÇ#{_ùWk¬(ʪ<§•ÐTn¶ÇÉýû  Gg�lÖ']y”=°Ô²9Ød¬Ù$ cúƒžÌ…MOωº9Bpû­/åø�ãìYØM’Ôã… D12ó£ ;Þ¤‘fXKÜ»‚§º�‹gNrç_ÿÝÓÆª˜äÕÒ”n·M– èu;\( Þ�e¨#^p`ûf‰£ìèt¿Õ²GUFyjul‘SlíU6�~+r¨Ò–ò�}� ±kbélë@9™”Fpe×é ),4›°¹îYi ? àH×ÎÍrõÜ ¯;~xkÀóQ~ù=w#PXØäü=mÑëšzå"µ½MÒÅr®ÒJ^!ò/OWu?ÿP) k• U¢ÆÕ¼cZ2‰Í¿¼¸uÍ07frc˜ÛÒš²´6B+.$åâ“…û™»È. é=ÑféמÂtKÂ] é‘&ÑŽ” ¶bÒé”x.@Q¡Ë³’üŸ¼}ŽîÝë ïïhk¬QÍ@…µDã±|f¥ç»=�b½ý< Æœ vKÝY�_?–Öõ8—R¦ÌÖ†tîY§ÿd›âä[DÀð9��…¢VošÈ1žf÷ì#tY‰{¸ôH\©.Ý$Ðþ ¢Äv2çð‡ôk l'øptH¿XEVñ²;>ƒ•µeÞüÖ¿¢09ÆU«7f6ÖÖ¾4ë÷ßü5ø}bvÎZû¥�Fó J©Ù,ÏÔ¥•‹H}=Œ(-¬v{õ)Æ�ÝF2»%½;2ÁîJ?nñ«èÍîiPº'n'áâ{ pÇû¬A[M6¯[VVO‹`+¯™ÜQVñ³3#c›§”cqÀ#¢Ž: jNŠõ`é)ìò ®?¶ƒÇNª”Ò&”ÅšÎì8;·Î3¼ö^úªÏåÄãqþÔ“<ð¦¿cùÔ L·#ji’…1÷?uŠ“ç/°ojœ×Üv ÓÍŽ%昡F6¯e6­¡{]0%hQu1ñõ¥pw¿/ÀQNL�#(wNGð"œî5'¨´ŽíuPe9ÊÉöû ¼Ò äèôWìžç‰t“¢ŸSž"œu¹ðÈãèÙˆÉ×î`ò†(-ª®¡W’†!O²(;…ÕxÙ, ¯Û#“ÿno­üö©¾Ú´]°5cØÕ‘RÍçOÙtGÍ) ¤»êÒÿ°¹P[¥>¶ŒËPAUC~®!38[³<œû¯�R¶‹?xï{ç?Ñ]=ÇÚò%6ÖVˆ¢Xæ·j­«Ý.½·¼mKí…Œ5ꮯbA¹¬S�€€E£�^_C¡P~ˆ®¯‚/—¹”u Ñx'Oïx*÷ÜÍGea&§²¶:¿§³ƒò%9×ãZéö8Õ^£~Ó³¯ÞÉÆÝ+¬þÍ9ò'úXƒ”²–ù$o\büŽ›``84ÙzZ*ó¥þ€ÂŠ~2­k&T4ƒ‹éO›WÑXçÿôž殇? ÊÏ÷Ž/ÚW³–Z5¿æ2=ˆ›æÀýÝ<]$ÂHSdF©b-ãÒÏ“·36ÿiI²25lxE¢øeà­À›u¯|`sÃàâ#‰1¥ ‚HþëŠ�âB!ÇÕÚ]Ƀ+÷åÏþþ0œ=ý”�À¡�ÁÄ,oàQî¸òµ(@»Ìt”þé,$ÆÇ1¦D©! ܦ•Í7þÓԺ׳!ªßÃÆNÃË™ò�xß(U© ¨@#¶Óý>Öѱ‘Àñý… .¤×©W2\OGJ^³/Z`òúiÖÞ¿ÌÚ?_ x¨�ëcù£‡Opj£#¾þǦƩ‡a•mvò¢z� šly�©ÛæÀ‚<ý¤-,.Dàc~˜z£}5ÊûÚ¢¹hñQZ+ßÃŒûWÖøÀÒ ½¬�×ÛÿtI¶ma�¢"Â5âñ„Æ5â�Óý‡̰,�;·”þgÿýœÁ†¢TZév µ•9Œ(FÙ‘E¢ªëÆý PÕD±Šý›í aXTOÃÂÆ ôh¯fT½Å袪°„yŽ.ŒóA¯Ãtò¶y†ò”Aí8F×G¼f‚P22\É¬Ò ÜÌ˶ž�‚B®i „¡b{¨¡È׋(åÀB²p&v„QUôâ–xQ?#„rsK®/VÍ" /…ÕP4[ByñUG¼¶™€€7AChìø¬®€Ëz0vÄdÍâ˃¸rY†íu±­1mK—­€Rvd6 ”dA›PòšÚöäé3¯¡³|Pu'BëCfEf_¼(ýšÕ÷^¢}Ï*÷oB$}Þ~úï:»ÄdóyG÷ó¢] ,6êò3@É:õáÒÅÓ ¾oâ¾Q|C2žôÊ1÷µ±Àr úmz=.vüñ#O1È <œ³\2Kx¼F´kkO¿eš¨‘Ì×*2ƒí•tÊK H�½øøè€Í°×‹±v>Ë2Æ‘ü±;ÀØNU6†h8D¹^@0–¢Bg}¾-ª’~SÆ!Ã4�Q@M¿‘×=mוLTtÊ �xñ«^E:Ñä?~ï·“h­Us¬5Ù^ßø‰îêÚ)àxÖ!šp ‹�DXc® â¨Qo6T¤#¦gçd8»Ê…ÞŽÞwQk Çxuòþ`'wâ™VNV&Ú7®* ·à{UgåËKîëQ }[õ�|CÞͳT R”o0j½¬‘ÒàÊGJ!@àæ\&`*&!~NÀ7ûE¯�Ù}%ž|?{ZšÅ™„åÕsô]#8p#ª5�C_ê0ôª(ƒvƒ«r̓º`Û«½u¢î2ù OE4›5:Ý!"@šué ¹ŠHÓqLck¥ÄæéÌ(,¸Ïd«ì‘pl=,2¥º«Øs�0VÓŒ�K^2¬�{÷ñ²Ïy/|åg09;#Àm]Æí2)—iªÐ JWª Ê—å÷UÔB“€r¿ëŒ]ö#™OÙÍŒì°ÀôrLázu¹AYع°‹/xåóËôs$µºÜ£A¯Ç‰‡†£G(O–ÌOŽql÷® 2v[oF^%[ƒÍýp‰üÿ;vZJyÌ:‘òÙ©W’vì3“Ö!I–ZiÜyÁß@HÊ (°@±–¹ûªñáAgîe;˜yák·,³ñ�eo[ÇHûÓp©7P?w×ÃlY ðšÃ»¹wy 5æÀæ9„Ј÷0Ã’rPú*ƒ‚/ÿ»wŒòü�ƒ"·è™�èHƒh1%LCZ×O‘Τb/ð´<Î]l LòQŨ�ÍŒRêO›ãã7k­c,/ø’¯ãußø½BßPn˜söÄy’^_@@ŠøÚET=Âl È\BÀFB±º{ŽK[Ûžg¶?º�õ6³'/xþ¿VD)éþ )üâÏþ/þøçþ7: @ú E‘³ÇµR_ÿÞîÆ›qñ¼Z]žÕ‹<ÛaËr!ŽU˜r>ŽÓ^³ÞØÛéu&†ƒþlD{-¶¡µNÓF#ŸžÎ;vmÓªT,�;Kš¦¢kuòÔIrÝ 8x ñôŸÁ¸ÔézU®�eéYdʳ¢œ4 ŽU£úh€¯qV™ˆa¡´Æ›y@¨JrQT”`3:Gâ6ü´¾Ÿþ¯Á ¹LÎ�†S �ó›Þ&Á‰÷’ÚŽÐrµØ@pè6Ôø<Ö©@[ãtʼnD€(úmʵs¨ö2våScMƒÝ^›lØ'­5�ϧdA=±@°µ…Í)T\ƒ Ä÷3pZŸ-ú¾½o9M±¹Bùè¿�äëÔë5â8»‰©¾ä+¾‘Ûïx!ÂȘ­ c@(�Ýua@¤vBK0–½4�uàîy ¤ì¦ê2" bµzkSnˆÀó�%J‹ñ%8Ɇ( ùjÓV¯?úäÃüÂïýOòÄrîÄ ò,“¬ìæÜÁÔæ_ù²º�jœyš×N‰AOÈVigCàÝ6=•ÚgoàI"hf|²2[“õÉœ <ÞRºÓF;×Ðís7žÂþ?Þó~ÿîQ©fïO^%®‘&ÄjàÌ~1UË­›³ « ˜‰Ùý=GÅ,ÌQ½Ÿu,¿ñK?÷$(ˆ�68ø£×¸Œà£ )Õå›ÖB¾2¤óèƒ'»ôÿy«-ø÷ch\*5ñú�lõ¢„T6C‚Fèúðïñβúk§±¹)€_ß*£}ÕG5³´u�Ø0 ) äÙ°þÄ8r@áéÎÔBp‹I¹6@’APÉe ê)åÏ�ë{ªhŸæ2¬–zµW v iÇüÛ/úJç6ù›¿þYŒëÍfX¤ÅÁ|˜ýê�¦õKa½#ŽÓcqM†ý…žÞ¿sÿæÊjdY+ŒãHGa0¿¸£¡‚ ¬ÕjJiE­ÖP: T’¦A-­…©N™ŸXäŠý×ñÎ{ÞÉ¥S��1L¤†µGÞA¹÷:ÂÙ½è0ªÔˆq†ƒaAjÿ™Ýë 1¹ïÙ8–˜[Œ})) }V" ¼¢êëxXtœ˜DE•V™Óýr= ¯ hÇZsÓàËhÖY6{™îw,ÎeÓ‰RµùŽ«X¹ëïhÕ4q2YOÙ|üm çŽ.fh£ ¼†¹AaPý6ÅÒ ú§ï'ï¬F¡\÷Ʀd¬XSPXEÙZÜÚægvQ¯�Ic\é~˜ÕâU®)@(÷ÔgˆÎA¹û-³A½6ýGÞEØ9OÔj‰RÀÚê ³S |Ùk¿†v=�üÉM —ÍŒ„ö¢—na–½u‹­+$Q¢Ð®1.Ùzà²u�€�ô~øè4D7bÑß°Ò½ÁF@0‘ ØIo´“q¸vœÏûÜ/ã�ïú²Á�KgΈÇÍã<À®ZÂr»ÃÂäÖõ^”?û¢zr:€Áte94à�Øp‰Ê29VG]ÞTàçJ­P¼Gm"ãX>“÷‹ÞN±ÆÑâ�_�€õ3Ç&T4‚p«|¶�Âñæ?×îU½O,”—2Nÿ—‡¨ß>MmwƒÖ‘q’™ôYe*ÉΫ,—ç 6J,¤E´sý®U² }ºÿ´ŒÝ(½–šbÀ*÷î �ñ4fË^šÝ­ið»=©z "©Sß×ÄÅ3Vë|IØ—¸¨ÔËhîûz'j||œµµuZ3sèÀ×(kH;=¢Ì¥Ã ÂÅIÐ ƒ]ë9Ž?ÕüÀ UG�öi|Œd8¾_“vúUƒTöÂFTiª$D¼þ3>Ÿ3gOñðƒï2—Õ½(¾_[µ†Ú¨@«F³™ÖZ­V¢Ãz­––elj‚@•ÖP‹S,Èáp0¤Ñj¡J… -ûîçØ‘kXÜ{�é—¼”ßþñï¤}á”�ò¬ »p‚ú±;˜8z ¥…¼4•–VølÄÛ3Si}�(TÏ•d½£0û{o¼’1¨Qåvx9mˆJþÞV”aoªäK΃Å{¿X0#¢šÕ°§›À7ë~‚ûl…ÁÖ§‰ÞJçÑw0;]#IS¦Â�µ¥ûôÖ‰÷^‹®·�즻Áàâ$«'™jÆèùI–ƒÌ1t†Vä}â�{iM.Õ[ÈÄ;Z‘HiYˆÕvÙ£Ði�eYQ1ÑJïª)ß[§*’�ÖÏ×búýA±oÇÞð¹_Å5Çž¥Å ºÏzÄRYY5:¬¨©RU:ŸBz–î¬ú}•¤4Ö/ -3iièÔBt3F…BD‘׈¥`k£NÖˆæ¼hï«èé!åÛ Ê,cmeYTÊÙ9î|ü¯¾áZ”åù¾¤5ŒO ³%?‹åÀÕ*wxï9G ƒ�€'[›JS°¸y|X�L)°iŠ¡È}Í·²ÒP‰iZ+fcéÎ:.La1í’¬4e¨Uð%Çóú+öñÈÚ¦4Ëÿô‘“¬Ëy±åÅ\µÿôí.E𯫧ÞŒ Š&³)&D:GO‡˜5G¦Ð¡Ý@O&éŸëÓ~x�üÜ€áÛ7 V ½¦ÚP¸rçÞñ7-ÌpåÔ×ÎM1™$ÔÂ@H¿õÀØÒÊö…•‹ÁáYòQ›@ʘ2̆ÛnËbEgyI$Ï�”½ÌÖDƒ å†ÓT¤Ñ-W{îfØ^îY”b0V§ŒÃmýÔ6êsAQ”¥œ—Î+a3–EU†×L�OñÕ_ðuüêÜÿÐ�2±®¶¢V«5ʲ¬• jõTÒlÄÎZh7Ùn­ãœ÷nÁ˜]ÜÁîƒWpËMw°»±ƒùÙ$µ6 9|ø_ôÃÿ™ßû¡o!ïn� ôû}y2îµ&©í<‚qÔe7((SúΖXöUÛXù™£ïŽR‚±€Àˬ¸©j"Õ¼\S¼ê�˜ÂÍáß*Õ‚aÜŽ9­”[4œ²¦*?9½/'::Í/"LëôÎÜG¾Þ! 3S”¦Gûô=äS{(ºëDë§HË.Q€-Kâ¸EW)Â…ƒ4f÷ŠÜ~ÅHf" h$ã‹Ü¢‚ê~)mFÔ¤å:°jˆÚ” Çk$Ó[=�9÷0q¨Ä-5 ëL�Íð™[1W»åŸÌ�;¡rÓõÞæxù´®´Â�M¹¯=0ÙÀóKåGZ#à–”ýœr]IÖ£ë‘ÓÏŠP‘+ÍÕcT¢4òº®…¼ìõ¯åñS�±²²D€–Ï´Þér×É3¼ôšãRöÚeï°iÝÜOˆi4Ñ›¢…¦¥ªáâðs7Æe0A Å^’Jù¡R4´&Pà ™5* ßÒªZ�RŒH¸<³°RV,±ÁrH¨3µT¶ÛvÌñ¹‡öŠ̯Ýÿ˜ºÐé[ŠPœ«h%ÖÙ}x“þÙ.åjÆð=›¢M¬FÀeä½´"Ú“’^5…¥|Óšzþâ_íQ¡7[| Kƒ p%ÙãY‡±˜n~uæ£ 6"çDÂW¿’´ÙrôP$jÝ>Q–WV¯z"• Š•v”µÆ`¬‰qƒ‡Œ�5úeX8:µU¸)ì$©õ;*é°cÙ½°›¯üîŸâ7ÿò7¹û/ŸZZöRQæz sÈsybfssCÄDƒ¤.L¨(­1Ý—9‰Ã‡¯bßÞC4'ZºùJvìÚCR žXÆR‹…¯¹�×|ÏOòWÿíÇhAaMÉ�‡ÞE�¶Pã # ¼ï’ Xp‹¢¿WyQà%ªÔ¶{T͑Ȧ¼Ž˜·/ã=Þ�5#6Ä¡–E€[(ð=$åm~%\9ÍS•�©ü‰„ìà ®…’½FÏìFMÌÑâýk'i4“cDÝ>˧ï¢U‹™˜iQ5Î_\bm½C8½—ðÀAZÍ)ùï„! Üg”€%åˆ.;ôR6~ÀUöÒ« 4Æ´¶X×ï’´I—eq×J[ÐioÒL[¼â3^Ï­·¾¸ÔćIv"e2¥(£€"‰åûp˜I_†�ÌÅhé߸,+¥ËíãŽJŒ—}±Ç6a9Ù~Ž Q@�èPÖ ÉtT¨]Ñoqãm·³zñ…œ ¥\¦Êr¼J!ÚiÃ>fsC˜mÊ[H£¼t޳€Öƒ-ÏPCSŒÀ+pËRN|¦2/ÄòwÁ#«B÷ ýš" ó™ûwóÄF›?=q*On›ˆU#Øš¥ªLUžÏ蜿Dç/—�Öˆvט¼mŽÖáq‘¢ÁE4¡§c>˜!©H¡#Åöf¾Ø Pô 6ï\ax¾G±”‘?ÐƒÐøˆÕè爵ÆI5Iæj4·�ùš(m´å7­äg;½(3F$zðá[…¥ì|$a�P¿]T¶ÞGl”Ò%Ø IšîŸžž¥Ýë“÷zˆ´dãçWð³5ÁB ,cf}Pý1²o�7«LÓnWÂ@±�•º�€YQÊyäÉÎ)J›Ì`´’Ÿ×¦gyÍ×~'®¹ž»þîÏ8sßûIëcÔÇ' ÓšÐz'ãtÀѼ„ù=hµÆ8^›d2ƒ(‰ÂHhéþIT¨1ýjÈJ@Ô ¤´WL´¸æö‹¡Ùo|ï7ÐítÐ šaA|ú>L”`›S”°�dŒ÷¼w@áæL¼d¾#X¿à{;e ß¼G�본sJÑ;‹*‚€cªUÅ¥ëŸBŸ|'ŸS –º÷tÀá];}ûÖËíW³Ú5Ãåuœ\¿ŽößÍ)ÖWž¢Û_!Тd,Éz»O·×gØÜIxè°¸•š(v÷Ëë£ â4Õ´¼î<ÿ=È`ñ€ˆy.´n\�K€Ç±â|ïáÜC$¦C£1�qÌ»É=‡9øé¯f}n�(—ƒ ƒFM²ÿÄN%ÑâRJ§‡ @¾µ#B³I§�Â8Õ�h˜ÓZ^Gå%6Pà{•ƒ€JÙ(Pn! P�ˆ ¤!6É$ºéù·óȽ÷²¹¾.Ze°b ÷>y‚›îCáÕ–q`(`®¼�m¶Dxg¬¦L!{Ð(ÿ°ë�׬•þM :˜0Ä´ZàÍÜèXù^ö�¶½!ó:V•(€9GÞ-tXòÌB”¿+išÑðø�b²×µ˜yé"Ùgì¤s¢MïÑ6�w,c7K@Q<1�­ÿ–5žtOƒÉÌÓØ×$¨„c!9^tÏi‰‰‡Mûžu²K}¯ò\>"¶—¬ZádÌø+ç‰'’¹ThÈ:TÛJt¦0Ep©? sÖ-ø¨hãXD÷í# -(ÁGþ1iÉœ)òÜn¶70h.>þ0[ß‹Šm£Û“Z•¹4cT=FÅ—zØ¡€PU×-â�¬–<]26*Äé_‹ieÝœ�kÂJ !p ¬AîÄè4&€<ŠH› n~Ågsí/ãÿRw@–ÇÖþª./‘X–%ƒLÏ2…3ýÌÌÌÌÌÌÌÌô˜Ñ ²üÄî®–f.7TÕ¿/#£+::&¼oᇌè¸3—ºçÎí:�™'Ïyý¹§)V ÆKÒLŽoçô9z£1IšË‚kŒw^¾N°ZÚ*µo�œÀ¶—â\<²DiÂËÃïx‚÷ýì_Âü“¿‰s›£1åü³ç>Âøí_!Ç*Æ)·¦e{{)q¨RHÁ·è�ȶýÞãøŸw?³ç'|ä*˧ñ{\ ~nÉìÆ6ÿáë$÷õY{bGÊ™€qJ^û[Ï!`óòw¥¢ ­0 f”0xd�ÁcëRª[{xÛK°Yk>îhK…­.Õ‹¼t>¥Q$*o¹•°†d#0ºm¿s0ê=¹œw l| w#fÓ ëÛ;XQ¬J'Û®Æf¢ $§Æè áPhÐ@<1¦'·‘º5ñnZêέ4¹â[–ªCeâk)Á«\þ¼Ž%kˆê†þpÄ#ï~âH~wÌ¢ Nš¶£Ôä ”Öd˜‚0 Ë«ÍबšãO{}¾ö—ü:&‡û|î¿ý[D¾^Q\gò˜­Ç¿d��þùòZ’VÖÑØðmö‹ì Fa (FïеÃùXÆ2 Ñ¡¦ü¦{ ¸¨'†~¾F¤[âì‹12�yE5ZS‘NßHòc<²ŸÆ~ÚbT¤Ñ®mccP‡K›÷ÁèÔ¾�èéÓÞ—Z_Gc4k›R_ìÍÐôÃjïÐßåµ1ëÐŒ€»˜P<ÿÖsKUUò9nŸ<Ç—ý²ßÀ½o~˜ÀtŽMïïfê­çÅïy£�J[LYîÓš»Oö=Ì1¹X#­júó%£É\9ÖIv©Ë(ð¥#ì.°»s’AÊ=k§xÇÛ¾„ï|í?@˜’®^ãÒÞ�9ÝôS0mÅ˜Æ ÇŒ†PRÕH\* þ_ÐrÜŒTm‡d8“¼ÙDÜ=­iLÕ¬s8Î&b¬f§5 ¡™ôñÚž–ô9n²ô#êÏòš¥sì­ 6ÛÞ6Â(ò�“¡ñB!2�o±þÖMYc¯ÏX¼6g¢¢”aß…ú…¥Ùá KÆÂ±üè�œ¢ÑÔ,†=—K?gü®mú�ž"6 zÞbnaX%:‰tbZ–91±¡ò·Þ³‰{”¿íN³ÑVÀîp¼VomoƒµÒÛÀZÆ“™”$L�Þn 0Y�9X6 Ïä$šm®}exT)-ˆh㉡Cg2éj,‡~Y6WaÞ&Ô"¥Ý}?èÜÙšë©“o $*i¶=tO©–…Ô,.– ‡k|ã/û�\|êÓì½òÅjIb 8ÿ4Õ=o&Ý>‹”r2‹õ&*ATþ?Ñ!Äq¦Æ6�ki í§È<‰±Í¸�…H !j­%FžlÀšFS²W AÇ€ÐÌÅÄ ò˜ˆm&«Òø¯*ðŽ¶µ³A3¸Ø{)Vò˜üöòÚAçp¬×òØÚ,âèiK¯€# °¸Ê¢áo~ÁlT (ó¬‘üâÁTlºY’ �®¿mK ÎN~ÃYVìj×Ì>~¿W¿çŒ^±y»“¹d#ÍízBðȹ6xëƒÇŒî7â™î@ÈûéÁÒŽì­Jœþâ«À­„w�ò¥Ä·­LbË; 6ž&>¸Z½eq°Þ3:œ¬ÊˆcHά£©$þò„àâ†ÙÎËñ =ÖÚ)A´GÉ6\–6ÆN¢L`m£3ÅÊ5Wu6xéÛ�§qøŒvâ�&&_¸^†OÙÉ`²Šiy÷+�WN‹q¤bž8sŽŸõ{þ$ÿêCmyþé›ä6áðÉïeø®o&]ÛBæS…ç((Ôu2~z5®÷úCmC¯TÕ‹3õ«Ñl&>'� ùÀz¥c[ƒ·`£eʦà�dÁ4Î’QDͰôy*Ùƒ#*@£‰f vU�MçÛýc¢-Ajâüzè Þ7},¢Öšk UfÐlÒÚX¢ãà2k«}²^Î|>¡( v{þ¹¿L2â@dº«U‹£@¦ $ÝL'íçøVÕ<_ˆÇs&Ç6Næ$µ“Û¾^ü Ò64:eðÀéxÿ»>̧žþ¾?àÐ9ž|ùÞûЃb©ÉÁØö1% ~}»’4»ŠÐ1„(CS–kdˆ3lˆ}+=3™5Œ0UmQôd²ZÅÒu?á¦CÿvÝ:Qyϳ’)÷-ÆÞäÚnŒÌä Î�p_{–r¯0ÅõËg&Ì>²g{Œò­/;ÁÚ{¶;¯½Ó!¬QO²¬�©»$‹ªÖÖ¢” o¹gã.W(:àdp~ Þ9°1ÆcÌÌÊ{Ïpm, å`ÿ�õ+X­©›µv{¨ÒwXhºŒ­¦;L«Ó% €i3HI4s°ÎÉ-6‘Ò€U`Ã-Å‚¸ØMÓeµqéMmi…’-%¸Šf6ÈŽP¸d”Cž@éåqCPFQ �á�·½‹÷ý´ŸÇ�ÿÛLâ=ÓÙ!} –Ïÿ8ãw~•OÛŸ´zã³=^¼f!1‹‰ýcp &6±D� *b#_fÚ3R)ß”×ÒÄD"C´h Ûʪ@“ýYCÒËðÒ/‰}'kM4Àk¦Ói2ô˜£:üÚØ!èÓ[å¿h9 ¯iì­­’ ‘Œ-–+kµ,�J¯�÷'¾"\}‘ÄTxW±XÌñ¾ôgÿ26�Ÿ&U6kÓòõ±£/šŽÖ•m?¯«¤ÞÝ¿o_¿áM¬Lv6èëÂxo"¥6Ï l€·¾éíüØ'¿�´—1ŽyþÂeæEÁ°? ¡ ±‘O,õÅêõ Ë¡X6Ÿ'tTÚ4o„óO( a�Iv#N2ª`5�a>Eæ�œ*i4b´Ñ÷þfBDë æ ò¢Ùx|d{ƒg¯zco¥„•ˆn^Ê ð%ê×/®®……;*o0ü cÉ*ï-tÁÆFÇUÒã½[|Cöè€ò €˜žÛ 1Ä Úû‹½ÁÀ¤Y|]ãWk¯\@bÊ„JNŒ›R‡Ÿ°¬0ÒËA@§ôX¬�èV͈>÷ÝM"[Í—VÓ|ùÂ)�W¤:È~К·KìÑMÜÎ"Ïo,åh W:õí ÀÁfVB"¹AÒeº¥ú½_÷Ó9óÈ[Cžõöû¤Ó+W^i´Éjïp:ŸP{/™D];Ùyž±�ۦܯÆ”µ˜ƒ!â™zÒËk]- ¬kÜ8Q:3ò|í ©%�ÄL/ÏôqÛPšÑ°M¶ⱈxg4t ‘«/ú\ïcÆR©àfš$ô{9YÖÊ@µ7•°ÄÙ"žÑs'‚¯ÒV-qÙF$SC�@Ž;®¦±<¨œk¼t¼ ø8Ú­db][Ÿ/~8í•2Ðò«i L[—-ä~Ô®Yî�Wðâ/£ºmDz¸1IT�çˆR�,W%>:ŽÊ{è�l£ 'l6eé%ÁÁ•—¸÷ì6Ö7X­–ä£u¾âçüré»Aw1ïªÓÄ…_‹?·û0†Nø¶æ¤DSN;â)ž#­Œ§=¯FÜçlk�7Þ|/Óã›Tƒ¾ìtk´Ašõ˜N'ìïrõÆöôK/ªJYžF Á7è‚MqIŠï ¨�ŸÂ †ø\²”n¥(jÎaÈL]c¦ÌRTE�¬úõèøD¯GÈsDÔÓ6†ýÖ‡œçfÂè@.Æ6óœ‘ p°*!àë þvÁÆÉ, ¢pyEð�»�µï»J­A?Ò=n%¤%RÆô@ÁÆÝQ°ùôôÀ÷;wvœX›»º¦oS|Ya¼²°N¯EÉöE%ÖÏšeHÔYÆá‰-H ¶U«nyKÅ�¶í@âÛNÒ“É0F8æ"¿î‚Îá@ÕË�bíW§ÍÏñ£[°¶õB¹Â^UÍB–¨UHŒ<7­jé+Ñ®íËöæ÷|�÷~ËωïÅr!¥š¬^â.½(‹|´*n¨Î²%Ö l.õxAn3émDÖšöBZCŒÚÁXƒö(šŒFÅ?#a =øØdš)Ä}(øH•&ò?`ùÊ“°<ˆå,´A/å¯hÇl½´ �yY|+½üÕMäJ¾Q‹ÎS†Æ[µªˆì7#÷ÛÆNYKb62ÝT§M�%RŸ«ªVp Í ©–ÒTeÀP£4 Ò6ñýxˆJÒ`ú÷ XIöS ˜ qÖ"µÛCQVTe­YŒfWòù{Ë‹×>Ç @5i³±Å»¿æ›ÉzjþÕ‚m—·8¢,K�Ðù®Å÷¢@qÝì'nGѬéS·Wé­eïÄ{�>HïÄq†ýƒÁHlÊÚs¸XbsÉp@û•˜hmˆÊ!ZZ‡^òLÿp½?xhÄPÕ:”d9v9ÃLa1'€žK‰ª„XDÄS.˜lÌXŠr563Ø�b¼�°nŶdK-ìo3ÒA6&Ы§•–ßï~b“@îCHB#x}2£òA°Þö’[QB,g¨Å@M/ï_^Nç«Ãƒ}Ó)©MšþLrz­IÿÅ_ãúB<ªQ_Òx'™ÐÎ^ô磣¿\ÉpV�=åÐaÇú`…©]ëªk5–’^§þm �èÜ èåRë6�Hß4H…A¦²�Ê”Xç£kaãßÑ.gœ~àM¼ç뾕¤?¤vµÐ¡M½„ëç‰ý Ó,ÖVH�ro•¹¤tä<™–Ô¬²ªêXækFµ%1<3†¦÷ãÅFZÙoš9Ze¯¦™žè¬ŠÎ¿ôòŒÙ— e6¢=AÔn‹fqÍß èqø–ºA#ª¯WàÒßoJph�šô�l«¤×d4UåäçòêyñÄq®bw÷*{û{¼ýk¾…ãgî‰ßǸ«N„£†�ãk» ºÄ3¢VÓß´Øi0“ stO(>ÜíÿT£!ÕpÀt1g6Ÿ²?9Tús?šdrØšŸ  ¬L'›qe¬5un}KÔ|Þ�|î$Ñs°}u3¤c'P×(.BëpI&YÓÓYëó¹Y1IQ™»&SÔì¨SF»ºXAP‡ÍÛ-{©ñk¨¼©•ãÝs#´rÑù²>ymO0$F”nñýINe1¢ê;6U]®CÚïõÁZ«Î9Xïa6q8óâ!T®iâ»4Y�åH³šŽ•@ç¶Ó<]Û=Œ}¯ÿI)c¡ÊNÅ ÑÙp"KÒÉl:Y‡öu:%RK1î£C¤�IšØZjž4%2Ô•ü ªH€’ôï14Ù�¾F�5�hé $©dDôû¹”½‚§–%ɲT6«âšÑ.àꊵùUÎ�9CAôñF÷>Èû¿ágêâØ™÷ê6ôã}±?stï–Ätfl:¡ïÑ齺Yyè29å¹Ý­ÝKăgçôYvŽŸb41¯C€Éb‰ÈàT•Г1ªBM$ Dµ<ÓXT›È\ ë¢ò.ößè9¡ƒ<'VB«v.jÏÓIb&¨TNšá76 Ë8ÐÁéªôžr3aR‹(éÙHïè–Ѳ ¦zi�¿ÝLÄèæÁíW:€z÷#è%‰ž»] ÐÓîÀ­„M Ùƒˆ‘Ýñ2Ú—Ÿ»ß»ç܃ÇOŸ:m¬!O3†ƒƒáˆäžMP7J¸Äí-Pùt@;<¹-31»h—&’8¶¢ÏiŸÄ"ðIˆ©¶5Rº“ÞBåð³B÷ÙxjÈ~[Ò½¸j—=èžØ>MH늺ñº7@ÒO””à„’�¥þ]]–Î=ôfÞqƒétâäYŸårN5ÝeõÆdÔX²‹¢¨›7I¬°ÄdQ�úiZÞ 9î¬*×ÈÛèó›¬Ç6+g”¯‘l ÓLþkÄ…‚Èóºo��iXnšaéce¾i„F,4h¶¥ ¶!D Š@C³¿Tz=i#·ãj�”‹òÆV+@>?Íò$ƒ‰,¹µè˜\ûèµñ˜í­Nž:Ëû¿ég³súLÌbfÑèôeºC›�¿Î>4Z™wçÂèhpÒýwˆ ­ ©Ö÷#Ú€³ª ¨ 666Y{àa^¹|ãE÷ Ê3=C»”f-‘iæÄšÝº @lÂö1BšÆy› `U­›98£›œ?‹9,DßPí †,úCÎÏW¬êº•±ÜLˆ¹ÜÊcBTû¦Û<»h@ž·&–ùüa-å÷ÿáAª�¿ÑÀFžA@‡U ·òÚH›&Õg£“i^,W}W׋$Ͱ}à1†[k$ã\%Cî¥ë§u`VkC1Ik7þ‰'I«Äй•™�´PeC“–K¿Ô©Ðk-94“Öµµú¾_dŽÚW~!�kIÁηÈv�êМÑÁVßa%oEÊæÝ_õM¤[;¸º¯�µÑwA²ô*K²µLˆÔgÉ6T>ÆÊc²ù8£¢ñ’! £’Ä�‰ó-V0£Ñ¯€]QVòx î;ÎüĬ#hÖûH±¯£n1SQ²Bž§Ú�‘Ÿ…Y¦™Tó>4L6CíbæÖv�¥1çôôûASŸçz|]ÂÕ—¨Š™°ÏŠ¢ Œ7yÏW~ƒ.r]ˆ?wú,1Ž„î,—n´2“nÏ'Fç9²qX™&m×ß;e;#<ëÛ “Ù„rrȤ(ïÚaëJØba®ýcñ6‘­%i£ Ô¨H†³!Lµ&¬�j×Z)˜OÜÔ¥XPWÍû;xæâ%þêü(?ðìˆÍï›Lm±µ€Tå’Z¡^9oÌ–À ¥Ì|Ûáµ4,þ1�»"#ãÀùºî›û¤Ýv«d v”€^{ÆÚ›Åjá½sežõúýþ@T�Ç[[ä÷mƒ6¶Ãd¢0@•gr…49¶‰³6‚Lkî¦øÆÄ[kM5c1J5öÒôKòD½rª–.é ù4‰'Y§Ôo�Æy¨zy˘(x…ThC–4¦W¶r_ô‹uêþ‡xðñ÷pìÔ9v¶OP¯úd{)IAh“œ×~C]ËÏ1-–�ƦÉ/¯%4ÍóКS Í€§ÊÏ´lÈÕGN�µ‘E_2 ¢P¨.бOBˆ�9ÉΕ€W�–·ä±áGŸ}ž?õoþ#?òÔsÔ-Y&¨n²4eR¹‰æG”9{Z†¿C6ÎRÚ7˜¨ºz#ˆÌN˜t£V¦É„ö«£<ÂîCU£Ç{6Ö&i½Xäƒþ€^ÞƒÃ{vd¶q¬_Ù'ÕbJê^&¤€Õ°ËT´)Ïö(Êhˆ¢Ùõv] ImSo¨WUì©ètül{om|oZûèÒ«»Ç ?×JŸÖLçQB“q$ýT©ÏòÉÀZ¬ Ó¶$ÍøÒŸ÷ËY?{—._ÁÎýë—™¾ú9Üb*€¢}ÀŒi½2Εc£ è ´ :Õ�.üšñ4å/$¤ì3 h¼^ôƒh kÚÆ`Í7�|ÍZ”ØÐ”Éôq}mŒ!‚f ÖcUº¶‚H›”€ö°ô3oH Ö˜fzr5 ¹UQQkïF‡M Р¯*æ¯üUØ$éP‰ @w6+FG#-¾^oÚ÷ñ}÷¡û\�#[g_Ý2Y‡ÕÙa«Åì)Ï9\¬°ênz¸Z±¸±%è8‡™O¥‰¯hb ,È­ NE)¤dÆ ÙMÞWê2È󼃿Dñ^ƒÜ/jÏË«Wø¶�~‚¿÷?¾�K»{TÞuR=s“éG=«©¯Z˜hŒ‚ÀµÅ ˜ìÎh•õïÆ/ÏÝ f”|8òУw`ï2˜;wømÝ�ÜÕ~s:9 {{»¬æs¶Žï4_·;Ç/kÕ3bàÒ”ùÆ82´ „ÖlMçDé0u‚z}´Td Ø5é×H ÍÍ«8\©RëÚgéfKíÁ¸¡=ð)¡‚œå°×Z‚‹O²yBÚOUuÀȬM^”ZW—�àÔƒoâì#oÅ$Y“•$õp¥-ÆçZ3"±¤ÕÌ‘´|€Ú ÷5³(�º@)Ù˜—ß«ÚÅ’UdŒµÄ/#»‰D ÉM5Àj©LJyš}XkâઈÇfˆœÒ¾Uš&ÄÌ+–^¶'‡5r,ÊšË$Ó‘ß�˜\Ã\Ôù¤DÔÞòUßÈÉûì|#Àtû6ÝL¼•5w{9íÞ`Ìp [¦3 ë<‡v•@¢»?}N[ð3^<|ÿ9„ùG�Z¿Hˆ_O Ðøç˜¢€ôpô¤Õ*C£è®ý—æàe s{[†™C¢=ˆÙ�„Ñ,'J¿zu—¿þ=?Ì¿ùþãúÁ¡|/‹b%¤CŒä& 65$SÖ‹Ú!÷�U´7½§�M-·¦Ÿ(;×Ü]Îlê@ùÚ«ç㢮iEˆŽº:NÍ-‡Ñz=äÖØ‡‚ó¿åYÿ+O{ÇÞÑ%· 6Þ»~U—§–ÅÒ$iJ’¥äƒ>&±Ò4w¯@Í|ŠË3®ßsB@'6ÝÁpôÔ>¦ ²dñ†xDB5îk�ßÂJ̬¢†åÚ€.Èèq´'..Ý^ž¥>¬Š¯<Á£µX¡@Ë ¬b"—Ô.î»C‘�ÙÍÛ¾âëxð‘·plç$'ŽŸdsm ñi(:`˜È–éŒ ½b”Š¢I˜�tH26é£7¡á!�öÔ{¦™÷‰¾?>ödý tà´Ñy“!TÉõWæ Ãþ�Íõ-Î<ôf>øM?‹4ïu‰»‹yÌN½'­k™«ÌÌ—"H;üÉŸ§ !´Èãe%ßßÑþT•ýé‚dU¢ÙzóþÖtÀ¬Óø7戬ɴoé–‰ÛdÙ'2[“ 6Ä�ŠùÎ ’µM1Þ€Q… Ðì¥,‘ìf"çz3wt *?£�)!ë a€,o¥k T@hÞß;ÇÇ^½À_ýá�ó© —1¹ÊíH½§dñ‹zRÞ|³>µ Œ³îB‹”Ðî[�?«o{â_¾Ïã´“–Þíð&£J ­û �Ô�±·yuêáS÷Üû»�üx¨ë?t>üžþøäm˜§¤ÆHѱNÞ9>àÎ@%i®”±ÊQŸÃSÛB h—͈²4tO†níò¢ÒaNT HÍ0]ë�5„e%‹¿IcÓÒÈ‚hZ×&î›¶DN{–!*¢vÍ“Œ€[=TRú6\[àS™¹‘…¨+ñݵLxäÝïçÕ¯þf~âý'ó RRZîf×1½¡,Ú±Q‡)Õe²‘}‘ãjú¾¥ˆWNæà%ÍPt¨ @�8dÙÊHõý„ÊÜÈùWΡ"Ÿ&!HyK315× &ªġцV�–ñ@A­E-öó·Ú¹V91I ®ö�Etˆl69Æz²ËüµÏ³µ>–ÁÚÙrÁWþÌ_Äñ³÷ÒÕÈÓÒªéÃhÖº*è­JòE!%Ó´¬HÔ�¶5UOh\\­‚kH¬êì‰F u?c)ó 5”-ÞX,�*M1Î#Ù@—ÉÀÑêpÄý.´Ë|6I¸¶+Öå G#Ö×ÖØ?Ügá<¾XW7CË& Çb§È·¶°!ÈÉV°A³š™ISü`Hìj%ÀÒ„Ó¼ªùá@ó¯?ó4“:¤<¾ÂbØN,?ýô1ÞX•üðÞ!%úÿÜdfÓS2l@hÁ†n$Æ0Tm¾ú�òö3y£¹½ë� äÜÒþS ‹¡’ó�`Ö$ýä–q„Üôö·?nˢةÊ"LŽå6ùõ~aåÜ+7@çG�ϧŒ±WžªV‡76ÖÚ¡µÉÐL]× ‡k¬�ÖpûKüµEóWýž4Õ§ëcLg¡mÿbè4R»‹²ÿøü$Á H-ÁêY �F À©DKëkí#þn:Ì´Nˆ@œ€Æ#aLü2£å†`@ ÞÖFGÚ[ë~dJýÞÇßÍÇþó¿¤*Ké…ùjÅò³Œ6OQGV•ÜÆ+aßbIÉ)Ĭ%ÎÃøFZF‡>›yýâi?&‘\,5sò˜`c]< ]bTsLT 0´‡e¢œâû[Ù‡1Dp ±DûRA…<ƒ£Öó èóI™¡IÓ&cS�7õàQšw#qårÁü Ÿb,‹(ò77¶yâI灰“Ì‚”,¥(ªpY p€¨‘\l:ÒrðIt¹$ÑTYroY´êÉQ‹ ‚/&¹]lŽIŠšÕÚ€ÙÖ:e¿GH­’‰ÀØÉÜÛši1›�}ž¢,YÎ¥Då�ã`o—-ËØè?7Á˜õ,ËEª¦v5yH¨_R Y Å Çî¹H–s„T\è;©§©šWUs¶åq&ƒV-pQ»i  TŒÇH–A+“éj±½Ðƒöó•Q¢dM_¹hÆ”"úlIßR/Tú¾®¡ûwA»F�ÁðÈ»ÞG²¶ÎµKçE6ÄËK_ ÿЗ�®í�˜8WU®ÝZÝ-[ú_ YKì›ÈcÐô>R“4"—QÑ9ÄY� ¯‹Ë¦‚”fAÍ¢O$mÊ\2×Rúf}-�èÕc!UÙ�潈VÆ@QºVI/Z Hh)®ÆÅÛ(à�'ÈOJënÉñ„bûY;¾#Wd«UÁ=o}'[ÇO!‹eåÈëšµý‰Lo¶”’¨4Âõ³Vd/…4wX›ª›k XÓø¸£¿+ …U�°kõFª=€Üš²ÆÔ*"[Rjzó†ÀÚÞ!;o\“ ¹b45óåÚHôÿŠaŸ*Ki»D·3wC·'¹±½#V!ëë’éùº¢0AÎmjvècV/YY”.†Z]Ãæ¤©üÝ^•È m•“¤RNsó™”ál±bQ9>~ñ 9™|á%ymž÷XÌgd!ð[c~ѹSœêçòÞ±„FãBz3µí8»& ëgsy¾”Úk ·ngòîzø ã„ ¼�ôˆãϬÍLn1™¹uŠõÊA ¬æ ž}ö™º,Š$é妗å\¹rÉ|ð}ï7ïïûz?ÿgýÜãÏ=ÿ,ßùÝßÉÓO}ng¶\¼m>™þÊ|4|¶7¾ñöW^ùûŸwճЛ5 ”ea�›wm‰3CŒ.h‹­5NoãT ˜£Œ¤Ž’aïʈ”‰ IÀT¹ ?+ã›YÆHfÓQìî³ ‚G[yÖ2Y¬P£�¦a˵H0^sÉŠJQ¥{ÕÙöû€^¿Ï‡ntýÏ?ÿ‡q®&Ë3Æ)¸ýK˜ÑVÃä2@š6�ò(°é5PC2›D…äÄX½j—y”È⊳2 Àè,�Lù'JU÷ª•Ö ‘í–5ëjÏxƒU ÉRˆ¥7�Õ}SÐnúMAîÓ}Èë„fmL\<‘}éߨþ>ÂPJš4Þ6 é}´k Ý{ƒÑ ÇÚx]Lìjf¼û+¿‘�²f¸;!_¬è-WR“ž œ±3êcÇ=L?Åd‰~^A~&Oˆ ¦‚JbDk1‰�²û¤¦ï|ˆÿ4Ÿ¹x•êf¸±‰÷ŽÕtʦ üì³Çùºã[¬'©¾—~7}ø);�ù‰û¦ °Ýï9ÔylÐÂJÛÜ‹�cyÀ…:¸�…p÷uÑl€<±-IžŽ…ûª&¸pëë©–… õl:yŠÀ0¬V÷,�™ÿ»þÏÜwÿÏÿ±õ�¯ü*{ÏÙsé›z8ùÀû>ÀóOÿD¶µµ}l<oÝÿ蛌Çû?|}ïsìïvÁ&±iA©»! ib3ñ¢öër¢³\Šo†.Ì­9„£¾]Sªv%©j©�#ÆÕ2BÂBsšÒÕ=Å/'ÞÓ�ÄŽ»èþçF›ÌG®½� jþe2£VN|w²¢’×Ѭmî4IÂ_û-¼ôc?ÈÕç>f‹ó«/Ó?÷f’¬�,œ.DY±hˆš`@Ó¯1QáT�Ãë½½4ÆÖ ßçÁ� >¶ÃΰÏZžc÷vñÌx ¼�;Á´4w\Uð‘×.ð?>ñ$—&3Âx�µ$e¹¿O^W¼k˜ñ ÎÝÃ}Ã>ÓaŒ…x¥xÓó06·$›²fÐÓrnŒøÞƒ4Űe�çÞV$.27©¸«a€Ì`1ò7ºp¤™’UíÄ[¶p»ZNpƘÿå½ÿûÀƒ!„ÍåbqújUŸùŸÿþßߟ$É£Áà^çýFc­—ÃÀ{Ošeï××O?Æþ.°Y¯…U±Zæyß�IGÃ5èå¢?Vç™M•¥š�pTt{áè ì¬#'|jT Ó`R+`c0¸y žÖPXÕOÅÊÀB‡rݶ-ù³ŽømÎZ’¢’Ÿ…   "H é¸G•®šaÕÄ9J²í9¾mŒ��Üóć8ÿùϰ\ÎdÀ.)õ|Š7©:døƒNÕp.â‰@-–cÏEËehÆÑd(Iª‹cbdFåe”–6' öWÔãÇ5–´.hssC,Äf¿ƒ@�Ì)ÚD"€‰¯‰Ó‹�xTM .B-*x x�R^ð,®_ Y’oo‰½ƒsžw¿óýœ¯“'�ôz$=“<ÁŽò&“!1rËdql_Á;ò¤+O¤öí&I\�5©�Ù-˜@Ð÷’ïYm1yŠI a=–$ñÙϢ¬*X”˜R²#²iE¾€Y»¶OÝËål¾>’ Ýßñ3÷Ò¯‘d9Y/a4QÕ¾?(ë4Íë²”~Î~U“VWIY‘QÞãS.±ÞËy`{ƒ·Ÿ<Æ[NgXÕdàèÌ�‘ÍyØ;“ŒæèF{GØ�Ž¢n€„Jjä—øb3H1Z&r2×'6Fp>±m]2Ú–‰[·¼Ö5s“ÕÕxȨšÍ/YТ4yb¡— ×ùâoÝ} ˆZËÛžø0ý§G²‘­Í-qê\\|�ü¡÷kbs=zÌÈcdoY«%Åh£KTMjÉLhÔ‚ÚDß¹U*ˆ¢ñÕ•ö�"Z¢kè( ûÌ4”g‚f‘•R¡�³ËæwWû&SqN9&;T…ÜbŒd6Ô˽½×é ¬Š«å‚Sgïå-ï|œÑ‰‘€ŒX|÷S¡ñ£%9x›2Wåš¡Aé0pJ'àd5ãw(Ñž�Êâ£Æ{€€i"û“ uSM­Ÿô„Ðrc–Èq¯(×—ï0À–’ýD š¤ûK‚ TEEVˆE´œ«Ëµ!E¿G¹Z�¦›Ç�S/dƒ‰ËÊÉþÁù­cÇ6вÈ�IÖëcL*>LË•X}pþpÊ W®ñc_x…GpNŸäÄÅ+œ<¶Ãp{‡Yí˜+¾pá—¯_gºX±»\²[–¬”iæ÷®óĨÏ/|à§z9iC?7¥�ÜZRc( Ô…»Ù…ø‹ù·hF`Ñ9ÓÛ�nZ…P¸¹±f£¸ºâ.F¤3'Æc:e´ÊyJ¹HÄÄÑó–”‚ó¸Ý RPÏA7ž*—5°§ÛÓïoþ˪(>ørÿõóC Ì67æGÞßÛ«£�ɲœg éði"N€G–¥$ÚµâxÑ«÷1¸&zh!´Ô”²§6à '£÷ñJÝÚ–›iƒ Dšs§ŸbŽíGÉ …”Êœ“ÅÆôÒ†�f3+WC¡¨c™$FGæ$Ðn^�}èÞô�?�Ïÿ×-%¬åjÎêÕÏÐ;÷fìhKWÑÝ�ç�JÊè0#AîoyÜxegE–˜oz?Qm9±4e !òÄ›þNÚÎH!DEêÚëãJNˆR6dYÒ¼Îxäø‚�s5ˆÓôzLηÉ!’T!¤"ŸÙá2_°6³\-¥ýðC�ñà›Á®õ1à r‹UB^ø{ ü¢D¤�&nZ ߳ƜOVlJf^€[!$†h�˜Ò¬iËFšäIXDŠts0iª ÔˆVÆé| íY…@K¥¹O2(²4JÊçiB’&j% Ì1£1h6b£¸©›€‹mhÍh†¥ Î�l�5€¥é]É­d½jW€#öwÉÍ[[;¬mlòøûßÇÆ©-l®ýÅÚK–ìgawF¸>Ç,*âìU#ÃbZô\œ! ÂAÔk¨ô\Æm£>RaºIör-ë 3!,�HïGžCb É`Ü“ÿu(4ûɸ‰ Ø¢&]V”KѬGÎÛÛÌvNrýúUÖÖ7Ó{|èÞzîù>‹ûè;zÃ�ók.Tçê¢ø2›$¸º~«5æ�ÇÖ··{7š¾}ŒIŠbiö®]£ôžkËBÊn½^Ÿ~–‹Ø©§ÕÌϧ“j´¶–/K“UW¥çH€Ð¹à‹s^¯.W”1ë¾i}/;HH¶3œ±\š/äX’vãFÊÔ"Bé¡,sÛa°É8Ýö¢ /ˆïBHH|xt'GƒÎp²2[1¹‘ÏäVÂM+H›Kõ>à¹ýhƒM¿7X�ÇË$IÉû}£$ ³ÍµVs¿›MtUœ»Wû]é�ÞbEZFZs�R2ÈeW:AçÈT‘ɉ�P0G“4: Át;M3û`ü F®µ·iêø¼‘ã·qÆá¢D›”pæáGémÞ$M UÍüÕ§HŽßO’÷ã‹TŠ ÄÒ‰d¨v›‡FÈÒ-§Ñ²Œd5­FhYŠÈgc¯ŒfBÚÒ‰ßrÙTP FäbÐáPÉlÊH"Hô”0�´M, †hŽÖ„¨¢€‘÷N·O]4BPåç˜ ,¦äÅ”³'ÏPU%“É['ŽóУ�bÓD¾?þ`I5/ñW瘢VöTwa:!ßÎXƒlÞ}jˆà£ ª÷*›L©Á¨GL;½EfTŠ1P:!0‰CÍò=L­€Žé¥Øõ¾”õ¤ç$ÙÑ0Çh¶…s0+2)i/%YóôŠšÞ%ËîÅKìî]:ø©ÓgÈú½¾…àsÅÂÀÁ»FëÏø²ÌƒÑöx8zGUU_5Ýß§q~|ìÄ©��övª²J\]§?y›ç¹³Æ†Jh…¦.–KWšÕ…Ä$Ïø ÷þÜ!Æ|Çî!ŸÝŸò5'·ùÐÖgú9¶]8§ žÚÝç?^¼•‹oZõY %©²Ã²v„ڬiÊž·†‰2w>bI9¸B:�F¦(±¹”k~ªQí—˜ÌÈë�°wl–«Å~ÒÏ6ã&À|>e¶6�ÉgÓŽ6¢þ%w4¤Ád�‰e2“Ë‚ L4íì7üÿr8ˆ´ënDü"=£Hh/6>IHu–Çk3Ø@Lß­Ý[ïÑè0îL§—$›Lqß÷Î÷rxù"OiKÊÃËTW_!9÷˜‚C4L‹ š¨†«zax{*¡i¬ÆF”mäûŨ-¦þ*‡hOî«’ÎëD s];"6PBƒ>O¬P¦XÔ<‹Ç¤À Û!Tz¦/ ¥¯�ƒ«òs’¼§Éš\Uã/>Ïòêk ´9<ÚØà�‡Þ̽'ïey~B˜–°¬‘U«ïi½•#²Í ’Qʵþï�ú)9 ø–òq�% M^nÁkÆh ÎE�ÀØÔ× �ü¾Üçu0!^Ê Vø«BƒÀ#¿Íf{DH­Ü'À³Þ‡ÊƒP«k˜—$ÃŒd`9~ïi—ûË¥dÓ“Ù¤ª=hÇg笨Vo0¹þÆ×Ÿ{軫Ezpppòà`ÿ ïÝ“ŽFãûB÷õ{ƒÁt2˜cvƒwû`þ½IÌË7@ê7c~cš¦Ç’ÑÈîcøŸ³šÏ–¾asÀ{Æ}F:é?¯kþÕÅË|÷õËúsógé‹Ú¤ïcŽPD¾:_bŒQ°á¶Ã$&Bd~R·p"as7" ç„’aZV­59Ó8‹q¡Â˜Þ­R»ËÓB¨ãÕÖ›ÄÚWóÞÀ�×Ö)‹’UžJ#§­žl ëéD_„ø{ð‡‡s¬–^‚^Q¦kyS¦q³2¾£¢�ËE�¹;Gº ÓÕh£-YcÚÏq©¨X“”)ãuþV¹OÓ,k©�ÇjU÷o Ð ã Fk<ôî÷ñô�}?õä€�õM§3üµWq'€¼ß,ô¹:P†Ø+Ñ>ŠÅG–HÙ¬‘×�s/ºÄkYPÖY�4’FÓZAÒÐÒ7«œ�Ÿ ’©4Þ:¹Ú.D;k4[‚:Êù4™ âKi’5¡¡ûU›ë&S2*ÃA³-m’ï½Îðàñªº0ˆƒ¿ñŸ$W·ÎÈ-ªHᓘuÔY3`É„¢Q ÏóiªÓ÷>Ò¡¡y\€HÁ×HßJï‹–²rŸO ¸5€¿Ÿ¾)Òcfaa ç!³HYíØ»ÖƒQOKnX#=)»¨p“»×®Òëõlꢜž8y.ãÊëÀÑñ]^ò@ œ—MãkÖÎçjö÷Ö\]åYš“çù$ɲºªJó‘Éu÷Ähó¯íï^[·Öþ¢þ`x,Ž’Â>»»Ë³»�·­�øª� Žç?°»h©­•ùžÚÕñ¥òÜtXÄÐM2ºáC`VÕ×�ÛŽ´ŸÛOr·ò0îZ„Ò7frAeyB[ŽGXÐë9sk� ±©ºÊ;6Ö&»®,§>„õU±äùÏ~Tä.ò!­�y–#�¦“ÎFX ©ë� Ý(W™9OÀ€¨:Ž›- ÁÐ¥(¶2�¶KhüY{3Áƒ–JÎbV`vQãôøçda¢#“Ó<W^}âÃ|÷hÌ|÷ŠÐPêÃË„óÏ0xà]bcB£àK[xÌL«ZID*±>ß+ƒK+uÚß5ì40$ú¸ô[bŸ¨­/'¯O ‚‡Á)訦Uì¯(u9*Dy}\³b‰�b–¥?[½*æ�–­_Qï¾J†Ç{&àøÖI»ÿ-M¹TÂ(¨äÂ-—‚‚‘u ¤—%B#.Fƒ¦'ƒµMéu5èËû櫹-+œ’í$¿Þ£.k¡7§™¨�»Ë—߸d¬]p‹ñ½ç_ 1 ZÁ’V|b~0y<ëÿïý³³éä·×νiÐäI–A–ñÙù’§§œ¸XÖ¬¼¥íÇ0àÂÍ3à GpN$iB·y‚‘9›„te ñ ð6¢Õ§9pÔóŠd-ån„X_§ci.ʺGp„°òB‚°yþS&!¤›b±½’Åæecí•;6Æ&sW»ér±pÖÚ¤^®˜OXÛÚÚ„€îô~ë±#%Ôq¾&©ê¸8ksÚ ÍÓH/#”.ösŒ•«ÊÕpp$«Ì˜®&t{)˜h¶Í¯¬_Ôj© #ÔìyIPeàNYQ7B·§äbàõ–/ûZ>þþ®*I’”A?£Z€¯ð’Ãz’$Qùš(}³•V]øÕ5ÓZœ—AK-SÑd>ÖûÆ!Ôi3|K)ÚèOòze%Mt‚öTšì#˜ØSi|oÀ{â~T¤œ<€Ò„U©Ã±¯ãU� ŽŸ^Á+,¤õŠý½k¬�×ɲœÍí<úè;8µsªY¤|¢ÅÌ‘>ŠgÊ_%eS™#Ë™%“Ÿ“DhÃ!±] ·ü*ö!@ÕÏ;–ÒÍpå±M¹M+¹@ÁH«J²û¼…h²U)÷¡™MVTph$ú5ƒÒ~Pˆ}=.Lð» ØbO¯c6äãƒ�±0ôª²`6›Ö岘ìOw9žªV àßPþîjµúýÁ¹ŸŸ÷ú›Ö&¶ �8œ/Ñßc/2fPÝQ?›^žï—«ÕO,Â!¤?óⓟ JÜw·hºÑ}®u^À&6\=Ò|怒%´àBlèOÝåܘ…D�è8J…ï ´¶øxèÚð.7FxÑÃJ£²¥k4¾p£=… ò$Ù² mM8½m©ôʸ±}œ“gÎ {êØö1NŸ8ÉzæY]yµYœk皆º2sdCiÇD šÆšÙÇL¢¹òɳThÈrð¨£fce`âû5Fgâ/RV�=A–&ÄõÃWMBˆ`ìЈbtYÔ8çÚSƒž”¤Wòs;É�ˆBžZž««¿œ²±¶AY•LçSQjX;yŠúØ‹­1óÍ5f?y;–ŒØ#€²\qxb‹k÷žâê}§¸rãöúéãîl2]K6Ûù®·yGÍnÏNï7ñ³¢ß“㙯�¹vÏI.<|çß|?罟‹7ng;›ƒ>‹µ¡KÕËÕ4°ùîA@åÿ#è¬*Âüëûø—vñû rTA»Ì4éçiQ,'üŠO¯fç?SÌSáç̧“ßßÛ]™ê^¯zy�@ðUQ^ªVų�¾ë]¯Œ66T§î¦ÍÓ"àª,ÍQZEí�‘ŠI% ðÛ ¥µ ïéü]•«Á Í»NŒí ¯ö“„žœ�ÔòÙYn)²õ\×j`uW•«]]WWƒ÷U–fBwyéÉOñ¾¯û¢^B»_èD'S0G°Òz‹J!m˜9É8kþq~YÓNDd¡ Ó÷4G�\Là(¥ —$ñŠÑ�/ji´Ùi”XÑ?TJ(æ(º·é°ð£§ÇÞ÷a>û?þ�è¦]»r™ëû×mn2²eyzÃÖ‰Ö¶�Up ’è`f�Oi\7‰¥ÿ8d òÚhÑlH f&ûÐÑy’ÄjÅ7À¦ó@r iª çižç<$HF¦Š¾!/$ÖD`ŽuׯÁ4JÖq7Ъ¢ÕU•¬BÅt:‘}L8öè[)z96„ $Ф,GœZUºúyæ(­¿øV1‘~WÊ)�©›ñ"Yd?—ìh1Hæ?:˜ŠJ@¶h˜mÖyí9Òª–ßãÛÇýL DþèêŒzYèÅEBYõµ ÷¬µûüŽOÎàñ¬ÿ©<É~¾Áþ÷xçL]U«Ÿ>ñù�}ì—ø•F6OÈ7rn6òµœYðQˆ´K§ÎDÖOPËíDº‘Aj”Ðä¨öJ÷ŒîÞPg4 »„@Œ~šÈ}‰inÜÊ“òS�üx“ÙÂ$¦ >\5Æ”wÜ©óÓýÂÿoæÓéÁr9÷7‚×?ùã\zå…®mèô%¢·û¡#›LæÍ]Ð'ÚqÐ�´‘«–Íé×´³”®A[ç„�[ç×­å=ŸX@¹Èñù_Œfpô’�aZ™UûØâÁµ½éïyøQξë}˜$•ƶsµ(ònöüî«$q¶T-{åèç/÷ë¿Ú P;}® ÅÊmÓŒWÉþ¦W¢¥«Æf¹ÒM5ÕPí³¨oæã`¦Š~’ªk¦*-8©m5Ò3ŠYWÓ›ñ!4¬8=ΖB�ôe¢¶dG2Ôš¥òºž/È+Tç4ËÔj`0^�x‰¥êç2�<9¶!~íÙt}mhûu%ý%Ú�™ŽkfçÿM¼¿Cjá;hg,Ežsýä<Ë…Gïgïì ‘¤q‰•­ìç’õ”ƒžH7©lL4,[Õ„IAX”\½t‰bµ¤®*÷ö§‹ÃÃ9Yöl´´6ùÔrú“$ý&ø³eYþÓÂÏ~ª.þî�íSUUþUïêW0;NÈwzÜdHÏÆ#}‹£õ‰1 Æ5¸ÀíFT!Dƒ»*À¬T $Fö4–ÔOkÜ¢¾µÝô,¸`LjÆÀ×ï¼Ó™�D¿?x)O²§ê²'iµ‘š%ŸþÞoãìÃ�ŠìmáÍ.ó¬Õ£iÝ6‘W5’Ù˜øj“e¢^djZ \o�4jÛû<úçбiîˆo¶@C,sÔ½\&ÒÁœ�ýåŒ5Ä“;4 è ïÌú}NÝÿ0/ì‡e±¬ªJš›Ó½+”~JvêAl>ŒüÆ´/ƒ ø†‰¥„–5€Ú8ki-ÎÅx•ñ0yƒÆ­v:TÚè_)€$’íèP&Ô „Rj3† jÊ F:›#ïÕøã(s®µÐëÕ7@µY@€GJñøÅ¡d/kã VÅ’ÑpÄÛ>ø• wŽ‹¿°É¤ÜiºÒß]&#Ý9±ö÷¤]Öëd²m�2�L¿;ì'é;r=†H‰ñZy|K´ÏF“¹d<½Ù²™93€­q …«ÿ{jÇb9g2=Öc]×ìî^;0Ƽðä|ø¿?~põ à¯v{ ½×CU}{¨ÃCõõªwá½h7¿ì$ãGÖé�ptÄžr/±G=,VÊ*/h·QBæ«3C}XqWCÓ¶*ø0ÌRÓ6Î ”’ùâéÙäV†V‹k+>r�j¯LCåp˜ß°É‡Ã«‹ÃÉ�ÔøÇó~Ý„`žþžÿÉo7ïø²¯Á`‰çGS›�9bÐÓÀp2Ã:ßz±¤j n}@Ðy‹ùöb?ß&†w2C�æ9i–°œMYLÙ:yZ$Öç‡ûäýY¯�÷Nîë0æBügê¬DC5”_epÌ6Þ'^è³jBeÒ–oÎ{7IÊcø2>ý¿þ=ýÔÊb ª‹)u]RNI7û‘ Ð ÊâÞ¨ D‹g-Ÿ^ËYÚ»iôÊL"÷µÅ6}³Ð ˆD34ñïož[”uT¦ÑPÕªÌQséúe‚5Hfh­/çK—&éç¨ø2>;›øwöxg¨ý‡Ëgý«ŸÙ^?™3þÐ6O£b ³,Ýf½·õÙ…À‘a±aåÀ‡;¤WÖÈÄ� îjdf/1fӀ鷵Ñâ [bÛOnZ-Tžù+3æ¯N™|ßUÜù¢•/€ï~ï“Åòâ]›»úFx<íý »2ßlmr¼×ëå‹åŒïûÇ�þh�‡ßù%º w{™å´Ï‹R©�qè…öäGx½8 ^NIñ\¼x‰¼×p®¤XÌP¹iÇ#¬M„õÅå×^zÂZrA¤]H{^üä�’ÏÚxÄìùÏpíú.³EÁzÒ§,l¬mñ%ïþ�LßÁÆÖ;ÇOP9% Óºµd7¡KTè¿¶Jùcü Üä:7F®)ŠR¬¤yþúç¨ò>é±{1âw 0¦17SG€–»eÐEÝÕ^ÊtjߌÚGGUf”(k�-kUêaƒGhz>À9éd?dë)½ºQ”64ÊÒ‹sN{5±4§* Ö (u&Hi5u± <¼Î°^�ØDf•²L²W-E�Dt•ÁÛaº„€îct·DÀñ1c:2© ¦s$¯t{D�Å3hö=ÛKy-++Ét†‡s$ƒØ‡ÍRܪ€`jc̬ªË]þŽ'WÂú軆ãOçþ�ßBøeÀ8ÔÁ,tßœÿä¡É±ù•'YÛé(cHr[ÔÞgæ.Vž$Xh²ó;ÑG½g‹Ãó…°ÛÊk+|áøî­Þ+ÁbR WKL[1[¥§‚QQ×î,Ò¼fq~Îþ]fùù)~âÀ¯™ÌapỀ?Ÿ ú/|ú`?p#åˆX.ÿvU¬ÞW¹úëûƒÞÚúƦa¹àãÿéŸñô�~oÿò¯ã¡w¾—�­ãlŸ>+ ?IÒ*w âT:¹óØÅ’€‰Ù �:q\yõ^|þ9.þe^~ñyÎ_x™².YÕ+fó i’ÈâR+f»+Y` AŸÂZR­¯ÃkZê scKჃ=¼øõgÄAS^U�¹( )Åö¬o¬±=Úæé ôâkÏ�f)¥¯èçŠùŠGßö.:ù '¶O‘Ì焵a³(v°%€9bá{Ï×|3—_zžåô�˯¾„Ø/—¬ SÜ•çdØ““ÁÚqL*”ÄVï&Q:³d εÐL#Ê™¦š=„€ÎäĹ$cÒ¡L%x°ÆÈß”œ^Ébäñ²oB´¬ŽßaªAšsÂ(S²AcK€¥)Ñ%,†š%F·Ï€€¥ü¿}µ’,pÐëË Ižë—.FñÏ.(4ïqd„¶jøÑ ÓîãÄŒ)^tµ††9¢§s¤ÄR—=é8âõÆïÒgÙÜ=`ëòua«á®®å¼�ÆdIºð«òuŒy�Éÿ¯Çg³øÂ;£ß�÷ßü:à˃ðtvõ™‰¹üÙ ûïXãøO;†�U…»w‘¹ሠÍŽëâ™iêK'ÍðÛÕ+ËOöÁ"oî.—ÆÍjìv~W´Ñ’ã9Õù•†Î�OÊŠeU㽌êí¶ ˜z^3{iÂâùÛ“Ôo„¥)„ŠÀðð‡M’|ôÆç^Q,Ѹû`ódµ¼üxÚû�³ýoÙúp=¬OººJ3k™½ñŸüOÿ‚ý·ÿ˜µc'XÛ>ΙGßΉß$ J±˜SÎgÔ®’òÖáµ+ìœ:ËbrÀ™¡ú‰§Ù9v’“ÇOÉ÷ádŸ/\y‘ë»W8ܽF1]J_¥¨ Ö·6Å_£®*ŠÇôÒ+å‚@ š\]‡$MMUUº f¡—÷d-˜¯”…Ð?åøJ¥ñ«¥üž¦Î;|í(ªŠÅá!³Ã}ö¯íb­‘fþÚÖ–”¹¼«¹péU¾gQ3ŒÙzëÛ8þ–·Ó�yøFok¸¶�MR:câÂ×1oyÏ|ëÏÃKéÕ\zåeÄX-ËØÜQ¹=¯âÒáØ}0ØÀ®ÃUn¬ž!�…IJ# ƒ †²¬µ×¢ÖЉmÜ0Ñ�¼o$wHÖHæbµ¿b’¤É””X`­xæ4”j¢Õ³�(9[�â‚Wx–÷Õ4!ŒÁ´šR:ì�XÛ>C:yCôûò¼'û~ã…gXN'Œ66»í0=”L+ï�^ÚàÑ)±u$’Ú¿èבå³vií‹fK†ƒã[Tƒœ�óWyãÕ/p}ÿ ÃÑ�ÇW®È²ÞKßöÚsŽÿ�âF)§zâô™ï¨'ßœûðç¼è xrj.>ÿ<¡ �ì¤ùz ¤µô’#ôÅ ©».4zîDøRÕ¹1$›963Ü•0`‡ �`kïÃÞªhí¨§ N"ÜØÿ±«f~aÆÁÿº„»ZÓXsO .ÿøç&I^p¿‹aø"ñ%Ãõó“,ýEýá`{0­gyžÖU% j±Z�÷Èb­³åjE’¦�W½d¹NWK6â•>›Ødý£ñ˜ª,eñHÒƒÀɲ\dÌ‹•ˆ†²,Cžç ×ÖVׯ^Ý;~ò”ËGÃÞb1/Ž?±séü뫲¬Šª(£µµù¹ûï`4^ëÛqé�‹ó /½´»uâÄN–gëÁnÜšb¹òÖØ¤(W UQÈTzš¤\ß»†(')E±’…mÐ2[LAã›ÇN’ös¼°¶,u@ÈO|óÏa¼sLg´¾Á¹7=†Ì˜¤±Vo\>óƒßÅ·ÿ­?ƒ].¤_d¬eûÄqéCå½ÎÁþá”Ùª$¤=zÇïîŸÄnœ„4U¦`\!½–ºB õÀ‰*¹ÈýJ ²Å†|�× ÃL#ZX¢ŽšþäÍ�'éñ¨¶YkáJuU7€ƒf,±Ä'¯A2Wâ\PðêD<럢?½Ìtº¯sB)˺ä×ý“ÿÆ}�½½c…¡ÑUv8йxs ¥¯¿ùÇé[€:¡R|G]´tÉ'€õžÉü?òíÿžéjÊb1w7ì^œÍ&ô¾úÜâÿãx×`”ï¿ømÀ—½øï–²¯ùy�=È—ž=ÉÛv6I­m&ìÓ÷ŒW§!¤Ö<ø÷ß%Œ·{ŸÜåê_IÖ¿Á£kœûm��måÜáP{ýo=Ï꓇dÁð«ßñ¿ì±‡@íW—K~ß�|š—&ÔIšl(€ËÀ'€ÿˆ1ß›­¯M?yõJ€»†›ˆÇ³þ!|io0üE'Î�}Ÿ¯ª~–e;zm—Ë…�֨늲” ‚þ`Ø cöz=qPÌÒLl‘¼'®®}OK!u]³Z.‚µVY¸>”EYeYfœ÷KB˜úª>pSŵº(®úÃÝÚ¹‹eYL­µ;ç*c[kÏ÷{ƒ«‹åü$˜ûN¿é¡áôà€ÕÞáÁøøö ÛÎù�áÚØ•³ù|íÄñ­A¿wâØ±kY–'‹Ù,½òÆE3»q+ªÉJaªÊR¦þ1PW•€�5–é|"�•e%YÉ©“gƒ4ñÃâZ¸.å¼’ª*…ÄPyÏ`}“Õ|J9ÿßí��$ÙÚ†Ÿï$Ë­íî�žÙYá®æÎoÛ¶mÛ¶mÛ¶y¥å˜;šåÄÉsþú32º¢£¢cz®0›O#³ZgXo||ûxbЩÎ#µ ZVku/×÷)¶-ÓïH3Á«µ˜Y\àÊzBV›Cšó(ÏGåfjã¾ûBp ¡±ãaZ;~¼)6lIûØ¢ÝØníðoŸ‹M!XHaA`즠+p6·Yç^tÚʼnެ'§ç÷:ËŠH,¼y¦`©_?Ž·~ ¥ã¼þÓ�úô‡}ÞïK¾��üâ¯G”Ú¦5~[&gg÷;rk�|<)|Û³ý¹v‹RnýÄá?ø-–�=�K§Ýno¬®þïFgíÓþøèÓCîö•÷¾x' N�€®Ã'�f”Þq~Ž'ç¦ÑÖð�ÿû4Ç×ÛØÐᾟß�×ôxCÙxv•ë?ÿfhv…ìþöG æÃ7‰-ôÕ?;O÷oàásŸ|�÷Ù½Èé�'×ÚüÕ© 6Î v,4¶›Àë€?Fä¿G5±6o„Û`ŸxÖšÏQï·°wï;._ºŒrœ)Çu*µVÓU¢|kŒMÓ4i4›j$&b2“aèc|kó'k­ušk3?ƒn/Ë´:®kÍèNÇhmF�odZod&ëpÑqÜec²c£ë k̺±fuôá8ÉáxhØ!/ ªÆÎ[kt§×jL9ínÛ×:­(xdf×Ò“ÍVsÁñü{¦gg^b2Ývû«&3¦1=u_Åá(ªJ²,sƒ ðÀnF>žàz¹µn.¼i’P¯7ðý ËŸDuÆúÆ#‘Æ“eù½ï{´¦fŒ5ˆúý3I’œ²Ø‡*ÕÚL£ÕœÇ©ÔkuÇQNžbÌ}‡<ŸÕv�H×,hbý*në.œZ ëxXlµ‚«bD~Ëf'˜ë¨<Í6ià㢿à"fÉEisöÏU›� ‚l ã9›M;�¢…»p$)k=öÔ�B4uF¸qçò$îå©ÕN¯“×»ª‹»øôù%ï{˜‚‰ÍÛíñS“è;œ�bb%l2q>vb„`Û³vrc˜¸Ýæäïÿ:ƒö ÖVW–×ûÝî¯ÿÄýõ·r‡1ªéóŽÀ÷ïø€*†8ÅUƒÓM>æÁ{øÛÓ9Ùí Z÷ýØ>Ü7‚Ø´¬qí§Oc¢Œpo�¥ozäM#6Ærí¯.Ø�¿¸¦‹+#RcwæcU\�)²“À«ÄÃë¼…n›<Ò l–=â¸î¼5Ö³Ø÷¬ žÅ*I|ÏŸJÒ¤ã:îåT§­µ #f@ÖEð­µÕBy׊kT�• #­u?3Ùu¬­!Ò×Õ‡=Û�woÍÍ(äž�öʼãxMOÍÖ©4w-,TkõÙ�«×*QM/Þ»÷Þa§k×m ‡Ã`´V=ó<Ï­Tªh�J¦µ2ƈç¢%A23=kÛí ÅCeµIÄ ™É”çzÆQÎr–e¯QJ~¸7è½€å%•°ò®žë½�ãºY‘ÅÖôÔ”Aª5µZ�ÑÙôGoív‡$³X7Dü*ÒZ@s¨Z ãd¨¢€n ñ(æ]`l½\‡d…› ;‹k!*s2ª˜å�bÅ�΢HµŒ­©}�qîa£‹§ÔælJ D¹gP†‹¥BÍßbm ã6Õ›ÇÐë׃�<Åf3zÑ€wû¬/áÿðkóè¶Žq kÇv“l©ólÉL2Yb‡Ñ޵u£â~,š«Ï?ÃÉüK._¿€ïx6êö¯ÕÂêÇ}ß?ýáSÜ¡Œº×\›eï|0ºâ€»5X”àβ÷{ËW³¼¡ôOu¸ø}ǰ±±îŒß^úƇ§j7yƒ°‰!í¦ùÃë:ÿ|³¦‹KŽà4ðJàYDþaÁtx+BxÃÉ#…ÂñïŽg_XSÊØf½Vß›eÙ ×y`vÏ={«ÕjÅÑÌÍ‹5¢QoÙú("éûS@%3FÒ,m�T[­©¾NÒD”ÊÒhxÕÛÍLVñ=UDþH”üëÿ._�a̧<ú‹®ãŽÎÔäyþcKÜ÷ö½õ¶ïú^«Ûï6â$rMfÄCEy”…8XÇG[¨6fI¼*¶vT[å‚RãzHaƒ¬ÅÁ‹Õéf Î:.ä(kPä�W„¼q¢h8èبMÔkã9¡ëàæÑUáçÖrïúùÖ4.†ÌZ”Í0™Æ-†Ac�'1ù"P ¡ï¸F.�Åô{mkW鬯’e:ºÑ5O¥Õîæ‹é�¹û¾‡vô®&†:wd�1é‰[™Ø³¶ÃˆÉ²Í™ “ß—®¯qä÷ƒ§^÷_\½q ×ñô(2ÿ·‡ï{äcé•ÿ¢¹ÃÙVkXû.E¤óN€@ À"Á;7™z÷»¨¿¤…?¼Þs7ñ�ˆ³ßt;0x󳟼›™÷[Ëmc3‹î¤yô¾ñô ñÕ!ƒYÅæ8YÔóEäò/ÀÿŠã›(òßibS÷‚Ü7¿ä·;í@g¬±�çîôÔŒç:+ë˵8ŽBßóSÏóL–ÁZœ$­ÐÏEý!·à«ßë#f3�>Åñû ãá>‹]B1oFWßó=×q•ˆÊEÀ’/°Ìkhžø×'ÑŠÌ‚µ† Á:8>™(T–�Ń\ªõ:™ò°Å"H×h\l^« |% ¯R£Óírcí&—¯œË×£Xkòù›Àuð=�ªïƒµLOÏmFMK ‹8Ê'v‹Æ¡ßï‘ïŠKuÑâm©Õê,ݽ›F½ Jˆ¢�^›«W.qãÚUÖ×W1Æ»ã2PŠûßãýøøoù¡|È×ÞF:ÌÞ²9`ûzÍ­¿gò¬�3-)™Üz`z=:¯}5Ç_û?üûËÿ‘82;í•Õ/>¤ã?ãEC.:M¬ý4àë�½€G;ÚŠ„Õš¥µ†ê}uü™€Û Ÿ­9ó ¹Ø B5l~ìÝf×'í­a¹5&1Ä+QnǼþš›Ä‡»VŸ‰ÀA0Œÿ±Xàð ðïÀˆ‡×xSSŠMÉg<ñ. k—DÉV>¸Ò¨¿w£Þ¸?�âiDšÊUÞ`8pÂJE*•jþ¤­P~JåsE:MóNÀjµ�‡á`€ÍtîÂéx.ˆ³1NÞàQ +¸J‘Ïo¸.#åÂåó¤YÆõå«ù“[á§ƒÉ²ÍÆƒQAkÍüìÝ^'·ˆÓxìÈ) „<-æH>|ËüÜb¾Êgfj6Ÿ—jÕ[ˆ…(‰òŸqåò%ÖV–m»»!Xr�MÒ·Ùä?ùsØÿ¡‹rÝmS_9²u��l÷µ[-æ'k?Ûoúc™lcÞ�@ ·Æƒ>r„K¯yÿñòâ…sÇHâ$îwºÏc>òPmð"dP]û^À·OŒ#�ü:î”I­¸‡ÔÞy–éw™#˜ wbñœOâŸû–ƒØÈZ*S}ÇiYú¢”Ss�I²aF6йÀôOwœíýG1@ˆeë¬p< üð"7Fé±UÞR”bSò­ô‰’¤éB%¨Üc±ÅÑûZl%NâÏuƒÖ(ºÚµ´GAb ³™#J< Ø8àæÂSˆC.$I’wÎå�«Õz.VaæMÆš\`Nœ>Êé³'ˆ’¼Ý�üë‹Î3�$P4`,~¢³”f£EµRK¯]¿l×ó´NÍè¬Ø÷}ðtšc­þ\Çñ§¦f�z½™î]Ú+u¯²¶0»¨/�ĤSkkÑhX7ŽÒ¨1ôƒ$É»Óò_ËÜý²ç=ÞŸÇ>ð#PŽÃv‹ãì¤ÀlÛèµm¨s{Ø sÁ�ÎM¦ú´&¹t÷ÜiŽz†¿øÇ?Î;;;í+ñpðcn£þKÏ­¯Z^Ä‘Îg_P§²TÛvïY÷h›K?p ´µâ+©� (,v‹ëþuà�Wÿ‚ȵ‘À yk£›’ïû¨Ïv“4™QJÝ%¢P"4­ùj¥6Á^“™ùÌd-×÷k™Íj£!Ÿ®œ�½ËXðF€ˆ“)QÊÏ´v­1ºR­Šï‡ÉÍ›×âÕ�ÕäÜÕóæì…b¥T±nš¤Ã4Žc­ulŒ±J¤âWÂfE©ÉLªDÅÆZQ®«}Ï‹zí�š„Ƙ,ÖQl•ë:¢$Á˜Ë&3‰1ÙM:®ãš,k/,. öÌï>úÀãƒÅ»îÎ罺½ÎÕQ„tIgú›’4~ç�öº ä³Põ©�¥=,�\QÛ‡ 0Ùý¥&›°LâÙ�ÎX&™Œ€vÚí6±0tâŒd}�áÑÃxÝuV¯\æO¿Œ£'ž'Ž“å�å忱ð�£¨¦ %…è4°ö}€O> r\@ÆK Á;6h½ç<õ‡š !¢ Ò�„¾ö9lßd³ŸuÏS�}Sï¦ãŒîÁu†g»è˜õâ‡)€-“¯*Þþä² ü•ç:†·%J±)ùÉOùrñ=ß ‚üyÛéõ»SƒaWšéª �Dñpzôùûõ&qOk­�Åêf½Ù˜ß½Û‹ã¨ÖY^ZP:Ó¢hÈ©s'ã£'­¦:Yî¼âí =È  d@ œf�›À @D3î°I±"’A�£ÌÁAÏÀ$?ÿÙ_¯ä£k³­ï­wîK’ØÕZ“·B7[Twía×û|­{ïß²B&Gn½mû¡Í v”.›O–™¨³¶ö¬NÓ¯?¤ã§(™àíj ÇjýÀ{ŸTðwKš­@Í{x»CZïòÿ�MÂÅ &5œúªg1ë,™4U[™1ë9²E\ Оžþx¥Ú†ý˜;˜RlJøþ�ùÜ\Œ*AEÑ@-¯ÞÄ##±ñFä×*µi`ØôÌ`8ØPJ™£gŽÙã§qC–9Pà8™ò=ó|{ÝòfäW?盥9=õq:M¿Ã û’8v�<èUª„{ö²ð>H0;‡–mgW¶~ÜNø!娉èhÛ¨dò¼í ÿn‘– ¢%8÷¿ÿ7¯Q÷=<Ë+ËüÍ¿ý9ëk«Qg}ýéLë?×ý̓Q?£äVá‹ÅŒÎ7�0þkó áñ¶ˆ�€3ïÑú E�¶¸ögHÎ-vâ¯Kâîòš¢ÀÿߢÔM§VMŸYY¶¼mQŠMIÉ_}ÓOK ?%ßì7Ƹäsaˆ×"sïûþóó€L¬‹ÉÙζ¹¸··ÝQ¶Í ��ŒZ¶ ÓdäeLF2rõ™×08wâÒ=Hh4›<üŽ<…[­½päè�(ÏûÓÛI(Sl¬ý0à€·g­OOS€ÏØ­©9BbíV—NRàÀ¯#òO¢Tœ·%ß”bSRòç_÷cŽ1æs ö •£ÞÞx®GX© ÄÁ­7i¾ç{2ê–À*¶ªåWì¶EùIn³µÙZ&Ø>ÕV`3:×®råÙ‘È\¾H«êS ¢v/_;êÌ7(œº|Ú4¦Z+Ξû�ö�ß{HÇ%¯oßšÇ!Öî.ÞN3À}ÀG÷UÀ0®÷Xàð£(õûwpj¬›’’ßøÂo¯DqôQVøÊJXÙ?j›®×ª5Q…EÊAví"›žÆ]X hµ@)/@9ãZÉd½D&g[°ìˆI‹ô™\£Ö2l·¹ùÂq¢• Î�¤^ óÙ%›jº«ëˆ†Ñ€ë«×É\ôÉs'�ž9tø¿M–ýðHhV(ycn'p¬1Öî‘¿: z””bSòâaŸ6°öÃ�¯žž]x‘pØk;âˆRŽ›Û7¸ž‡1–bÓ8^XEDáø!¾çû£<�¹=÷›ªÕ¦íÅž‡Ë#!åz„Õjîë…D9ˆ™ˆŒ ““öºdÑ€ÁÅó W—\¹�Ï ™¸�ƒà¢ÐI Ö¢³ŒZ£A}fŠv¯mþãeÿzie}õÎ<óÌÏÈ’ƒ””¼å(Ŧ¤dŸì>ø$yP)å8®ë•Šª5êJ)kͦÙ]–éMÿc-Åv‚üÞd7¬à¸Êuqý€ÚÔ,/Ý÷Îì{é;£]‡ÔœFƒÆ®Ý(Qà8ˆRH±Y¡sõ2ƒÕe°}ã:Ó¡Kàûxé ¦ßë †Xkðý€LgXeiÌMÓœ�¥ÝÙˆþý¿þåØÿý_ý’8î_”s4%o]”bSRŠÎ|ÑeôeÀ~å8¡_ %¬T|Çq}q=ÏsÔÉÍýŒÉ6í¶3­ñü °@Èòˆ£°´¦Þœá=Þñ½ó4œÑùÇ üD1ö0Z£1¸Ž—G."€1´êÍÑ[Gœ|6H”"Mt¦©Vk8�‡úhe¸qóÚú•«—;<÷—^û²ß8'))y«¥›’2½VÁÚ=ÀãÀÛEÞ‡E¤éxžT*S#RÑ`�Iî ªD§©¸¾—㺮”É·Mg¹€Ü·÷!ö,îÆÁ¡Ùl‘¤ iªÑ:ÉŪÞh"(*• ÕjŒE§šjX% C<×%yzϯV¢Ä¤éF¯Ý¿zýêò•ëW^wõüù:úÔËN$âœ9j³”’’·)±)))ŧ†µ ö kí´ˆÌSÀ40 < ”jŠr| •£´Rª"JUý pGåÜ5· LflœÄV€»çwù­æTÖ-ÜOiÖ›¶Uoik1§/œvûƒ¾uÍõ‘�©ùÙ»êÍ©v»Ó~ê…N>{þÜéã×/�=hàÚ èSRRŠMIÉ�ÉþZS™4õ0¦¸@x UÑ£Àn7 ¼úôt³ss,â8Ž©·ZsIšžïUj•º´ÛºVãùù]Ë7Woön\»¨­µ±Àºç‡'š‹ n÷êõW¢Îѹù¥‹/»yÅPR’SŠMII™–kC¬ 1�£€% ŒˆÔÙ«”º¨uz ‘^ñùDÚ"2D)S®˜))))))))¹MþOÀQ­±ï«IEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/teawie-bday.png0000644000175100017510000047266315224505336025023 0ustar runnerrunner‰PNG  IHDRš¢Þ/£uzIDATxÚì˜�,ˆÿ,tõt�v÷Ú<ƳmÛ¶íÀ³mÛ¶mëÚ>æb„ªÊ|5{ðlôñEbhddTTTTTTTTTüo¸êÌ¡´Æ¸©äô›7’Ídkwž¯ÍjÙZW˜+7Ú”Deeåßéᩬ¬Üåt0L~'ùóä�’¿Hž“<=ù­äg“ïßý½ãÓà±ITVVþu5**þ‹”e]™Üa¿@ävÚÚGÙ,[C £R”¼û%E­p¤´ÎŒmvz �Ì:_ÔÊ¡÷c5ò£>€9TüÕ ©¨AäÖ•¼nÒ@»ÐZÃ:E¼çBà…Y#!"  O M œ™âÇH© ŠÔÅæ±›¹ü—iȬTF›A¯‡¼,lj~wvnsü$€é²¼ €`"öû%D^М™yDž»zdÁñ×¹Â1«C@ë:ÌŸ¦KPä Æ‰ÆZ(­ãüìÜpÔë]ÎÌ‘7˜GÅÿ…¢ÞPE–ÛÀ±ÖáñüŸÞoz0žRJ–<.wîªZëãB'÷ú½ ÛÌØ‹ÍF«¼ŸMC¦£µîº,_7Öuû�oÕòâ}TüUhm­ŽŠ%Þ¯ð`"šX Ew£”‚2ZéÅC3˜T§IpŒÑDé§ÞåJ©Ÿø@¿ŠY˜”Õò•.wyy-GŒŒ^§ýx<Ï1žà úá>÷�Še¬ýL£Ýº™Lg'¬ÅÕÿ´Ž_  †=„×ýöGX÷ƒ/bá´ïÃ90Ãfv÷_HäÈÝ�[·ž–ò_øiýÛÿÞ&WÁ1LieN6Ú,'"+"Ј™B­¾©µ4mJKö´µö£ñèºi`¬æcµ±y£¬“žò<—…NÎå(j5�Öf9R¯×…VÊ ¤Ï,ë©_¥ï\Ðíu¾xÂq'Ÿ@Pñ'�q¨8`9*y‡ä#I©eDä B"‚‰RoQ$÷ÔÁîZëEù¾¿dÒæ "cˆt˜ù’”ŸGZÝÄåµc@Ðe£�™#Çæ Л�3#†SÜ࣠zé¾tau �;çEñ¦ZYé™pìí‚Õ÷|ls @’,@tÑÛr.ÿÉ×°î ï†÷`�†NÆSnCÚäFƒÁpÓ¨Ûý½í?s¤V2MÒNîë+¥¯×¬7—Ç£Ã#‡"ÕƒÜå>­"ÚØ,}Ò‰ �vúEaìËá>ø©"/ŠÀ¡%£µ!æˆáh"•$pŒ0Ö‚™¡ˆ’ 1õ"GØÔoÔ›(Ê:{?ˆÈp~an'ûðImÌ»\ŠŠüè¬ÂAäNî­”ºˆf„Ù !:0”‚±voýGÃ&EfƄ̹Å'cdC)­UŠ$ZaCey^rŒ:x�Æ¡G`zõuÑßx1¶_v³Û "°ÖJaGðþ] z>€áÿs‹uYl ±?(•¢ºZñh¹ž:ËîûTœt«û‚”†",BI™zÛ7Ჟ~—|ðeÐa„ÌYhmÈ$òÜÎ�³ƒn÷œ”¿ÉšìKÿü&WQ¶›4XèVËk7Zè,<`ìGW3Ú´óZ!.Ï•@L«=EÂÂó�9Ä)…4T0ì÷G12Æf&ˆPˆF¸Ì"˜ïÎC‘ZDÁÃ{�E "ä.3#síÖ"G€i;b—¹�ŒÎŒ!¼CýI]T€ME;yÏäÕR+E$ÿKÛ ÐÆì~ °÷…¨RÏ$c{‡ŒÒõV Çžz hæxŒŽX�ù�—aû¯¾t!Ã$ØÌÀ( ¥ Öf ¥°°Ð�;a5®þ´7¢¿m#.þÔ›0wþ¯0œÝ¾w¸Åæ™ù¥PêµÆø_"ÒN>‹ˆV%#3TeÙ'0�ǧ©�äEíʤ”.—\ +ò¾òê)LPP2ð®œ#caýEX÷³¯aý—ß3Z€ÕaˆÄ{?öÛ‡½þ"ü>Òú#*þ&J)mH_ËeÙ­¼÷÷õÁ™3JkȹJkXcA|èõ:ˆ)šÔ;ôÐÃÃBê噓qŠ‘#Y—"fŒ‡#0GdÖ¡,ËÅÿdÝÎBêÙß±sÀm$Yþ^÷Œ,c†u˜—™é˜™™™™™™/—cfZÈò†™ÙlYdIÝ×¥s<åŠ�1»þ«þaO¤‰Z_?(Ñèî3}æL2é:\¤JWg'û÷糧»‹JµzzL•l¥r\¬üXiýfà8 �F4£Š`íóDäάµ*€Œ ”ªYû>.�E:•Æ�„Aië,” \ ¥œH*á¶ËôvuÖl(F%êIÎ_Çä‹n&Ñ8†R.K÷Ñýtî¼—vW³è8Š´‚ª±H}+³]$0nååX ½¶±í+ï®Aªšï;awÿN·ýTQ|ï¿™ã®æò�Ôž÷zÒ–R©”-ö÷ïn:p”ƒÍãÜù·Fãñ1¡dÉ9kXô¬·Í4‚¥&`Á `«Uòݧ8|ëO8ð½O!}'ñ5k­-–J¯S¶»ëÀçÄ÷ï £¦d4Kr…ü³Cžy<‘L#¢³Ù^©=KÝPqÎŒŸ`'Nž ÷ÿì§R,•p¡‹dª,„cI"‰:pëÐØ‰\|Árú�ìcÏÎl¾}#Å\®­5±X­îþ$ëê¸æºX½|M��„üùB�ÃG�pËm·òËŸÿ”¾¾^Œ1¸Ë8µ—úó_MÆ“ox 5„ŒF4£ºø‚(Õ„µÌ5Z+Z¦Lañ¢Å¬[³ŽæÆ&"á0---nÁ¥0UþÝ]Üs÷]µmñÙl–®Sí¸Y8µÚ&ÙÈÄKÆ”s¯!ÚЊ(E9Ÿ¥sÿv:vÜK±»�Ô9óhj›Oݸ© jˆ½Gö²ã{Ÿáø¯¾É@O`ŒK¡ž¤"‘_ý7`cŠÅ«´Öo�&3ªå²oŒé/‹[€«‡Ú·EXû>wÍM.U’1‘4K^ø^Ư¸c©Y X@ F6t¬¡÷øAöü쫜úÍ·ÐÅ^j׃›™Û|6×ïžý!·ÿqD>«cÑŒ“M¡ŠGc³”ROntQHÃ@y@¹ÈEŒ5µÈÅ¥½¬j�Ê”+ÍäåI¸®‘=¿ü¿ôª•JåLîž_áSa TFxžWéïËž(ÿ�hnúQï±#ßxªaü$!_js©±—(¥®*–ŠMÆ�©«—B©H>—Eùa Ö2ùâ‡3nÉùÄÇ�k?ÎÎo~ˆ£?ý"ž,‚nžÈ„ o"3c © çÉ4#JÓs`ûüÚý â‘p-ÁSµô­“&ríµ×³láâZãyˆ,�$€N>ŸçБÃ|sý7Ø´i450‰¥'¢Âßwº—'FA3ª³27UJeÄ-NCcÜ´i<ùÉOã|Á¤“©  0x>ÐHûC Ú;;¹ýÎ�üúç?£PàèÑ£ä³ÙZduˆPËιééLZw-xþðñ7¸m,5) ¾€s}½lýê{9òó¯QîéÀZãlÝm+wºõüLzçbv_îî|ÉE(«<ß;¾�nN¨r¹-Ƙ§w0(ñ}eËåW¹k_”ijJ—ñYø²�3nñyˆ0 &¸Ãß?]jåB?‡oû)\‡Zñð<[A° ʸTOzLK×±�»¾j­ýˆ_—>þ`Šn*Ùþd:™¾Ìý¼až…ˆ1F¹È��ª}Þ2çßÌÔËEËܨP²‡¶³íÓo wpåR�ð9 ™°îZæ¯&1fJk°PÉõpbã/8ìꄅΣ sD£1¢‘®�O]*ÍÔéÓ¹úªk˜ÙÖF}}Âß),‹E6mÙÄúõëiïh§�˃ÉX²k _üªRêu@׃4¡(£:kå[cž ¼N{^# ÂÑ(W\#¿éf¦ŸÓ–3%2œÖÛÁ±¡ëŒ1ìÝ·—ÿì'ìØ¶•.‰~g*U‹J60óI¯eò¹×‚ò0d*ÆBÕûz0Åd, ôœbÓ—ÞÅÉ?ü€�ÞöÓQ¥\*mrëÇ[øËÌöëxl×ßó¥ìºÉ¢Xûíy�uÑ^øt«1\}ªÝ�î±ÀO‡sØN¾QßÒÆ•YW<øÕ÷�;¼[ÊÑß×�©—pÿ)T%O±Ð�§=2õ,Z¶‚g<çy¼ô%/㢠. ‘H‚0|-Róߣúúz,XÈÅç^à"£YÔ74b¬ÁÍOZÆŽy¸K >[+qæAâÑf€³X—q�™¦´RÖÚZ=æy/}7]{¾ïƒ±Ã£–šd„™µgBÈÚ`_dð~¶V4½û¾{ùþ¾ë¢›m;zc,ViV]É’g¼…Hý´"£@Å@Õ‚–àvJÀZ(vŸdóWÞÇñ_¯Çä{QZ3àô•òÀ'y%�#x]+>ì:Ŧ‹îéè< <ø]ç‰ÈÏ÷ëE)AM.ÏOÖqä§_¦ZÈXS©ÜS*äÖ¤¼Ðxíyë[Ç,ÎË¡1—=†%Oz5Ú =¦b…AïÓZÐjð=° zpÑ{p;»¾ýqz·ÞŽé>P{ÆJéBã¸ÖîýÛ¶ÿÂAðC:½ç�’JkI5¨B!wa.Ÿ{¯…É~È� â׺òBQbã§ÓxÉ#¿úJ© 4]T,l¹èÎÕcE ~–ÐWJßø3oø Ò{‚œƒÆEÐ"Llkãúk®eù¢¥444Dó2B¡%�X°2t=#«³«‹�·ot�5÷З立§›hžç=ãÙµn²@#A‚)wÐŒ ;|ð8t�Ï|öSܹñvzÜ ªT* }¦>ôÙ,~ìËjE[ TOÆ-€†�ã\w›¿øv:nû •ÞvÜýŒK¡uZkŸª¯[Ø�®žVà ªk­µQg\ª« xT�îA"�Xû-÷w«”Ö!wo"grÞ›¿†TøãGÿ�í(¥p0ÉÏ=wí#€ïC »ùkeÊåO7¶¶>Bû¡H9ÚÀÊ×‘Ôøih¡¦² ø®ÅY�:Ý‘<¡ëÍ`„Wî:Æ�Ÿ•£¿ÿåŽÃT 9¬µˆˆUžwL¡۲'Û¿‹’¯Ÿí©´D$žže…§VMu¬±Æ§TºMÒrùc¿îzb-±" Uð¼0f(ÂЖ@céÙ»•#¿û6ÝØ@&¡ì ÓÛÓ…¯}Ré4ä£9ß5Æ454R“h$¸HpnX¯Ùß!ױɿM›øå¯Íî=»( hß«öw÷nò”~.ðGRŒêì’H x›Öz¡v°Æp¡›�=ñ1�#‰œ9H€ r´ù+cÆâlG‘(g˜ÿÅÌq9éX<ŽÁäÙñ•÷ÐwhZ†ÍÞköµ³ Ž[‚´½N40éüˆMh«55Dã1?ŽÌžé`3x¶çû ¬k Z{ˆR>° ‘0#ÈŠó€Çj­ÿCóy×3qÕeˆÒ$šÇ2é¼ëð’D@n]¿aÍ×>þ‰ 9SwûsŒ*žNÞ÷{ª•2Z‚Ž:gB:xÕáAdÍC'~˜Ö—1ý1/'=w-¡ú1T+ÕL;Ž�бSëš�µ�?Û`ã{žï+W¸å—’uuMÕ7dBá�§ü± 3Xúú/3ã/$5u.Ú󇞥 8€µ¢†à¢‚gG±§�ý?ú<[?þ Š6ÒRë(‹EL›4…‡=âQ<÷YÏeªÛ B”š­a_@FÌãDYÂ0E¢V¯^Å%—_NOW'ÅRW·I¶´Ž[èRyë’©´ð—fTg�L±Ø&"¯…Ãç x‚жp!¯yÙ+™0v�F¨± F¯µÃi#æ3gu¨«K3fL+·þñ¸b6"B5ÛM¾ÐϘ…ëp¤6ûÔ–`Ö/AáÜYgšq5²û·¢©J(ö-´UÊ勵çµ9{Ô$x!ãÈa­éF©/©HXl¥ú!¥Ô¥µoŒ!Ô2‰ÅO~ éqSwD8vç¯q9�Ú¾5FÞ¿¯îä‘#_‡àž…l¶ßZ{‘v¯Ãó=o Tf²£ŽuÔY°*œUð…hm-µë…á?c#JÕ~q!:¾�|çIÌ@‘R_'žöp�”¸TbRiÝj*•Œ5fÐÏÿ¹Bž·Ö>:‹½!‘Jµ%“©�Öž*†Æ Ƽ§¼�ƋѾæGv„«%8f¬¥ïÄav¬ÿ§~ù5${‚r¹„ãÉx’ºTŠG<ê1\vÑ%$â7›ƒPþòÙ ~ù·aãi�ææ&öØOç©v"‘˜„B!/Û×Û`�ù��hFõ?—ò} ¨¤„Ê¿û êcCVÌ“À*ˆBcOeì`‹_ngñ©ëP­³�ÊCJ)´§•�©=²å6”V$B-S‹FJíiR©v©”@f|<ËO¹å€­•ä:f1û�SÈu/B*€T2¼póÝ÷ºõŸ_FQFah¢Z…ÚèJ˜&qžàIØï+ÏZZ©s Ôj< v ò3{XzáÓé9ùRZWŠ—Í£”BĨÑÁ¡NÏ÷Ö™ÌÅÄñ¿%ÙøJÏÍfrï¨EõõH: F ,Ú—e׿–¹‡œ‚ŸÍ’Dº±3GÂ>7æ3 °«¨ÎŽßÿ€{ßõl¼þÍtut&D\,”˜¹kû¨cŽáÊK¯ «³ )““¦�€qB$7�ÌTH¿w˜úý‹.äÈc�AÚóDk[[[ÇLŽ�¯˜&šiüK!¤,ÛÛn 2™^@ÆQćÎqGƒÒ í,ÍiË·°yÀ ûSj9¢¹±ÓÂ4EM&6lÛ¾�?ox„£Ž8Š%{í•\—§#÷ý‘�ܹû´‘�Z‘�ÍÛAˆm1=QsÍ:èxZ:»ð‚ ©Ùh? ;o%‡ž…Ì–v…>à�…‰ã„Ë´Öž” ¯³‡}Ö=‹L¹£é« @KÁì}cÖ."J¬JD²D‚Þ…•ÀKîûÕoÚ±2�‘QC­a£K�À°9MX‹ 9%1l* lí&(¶°ð”µÌ~ÂJeÕ„R„Q�-6094¼tÅ>{¿¸£gî+å¿Id#”’žÒÙb¶°ÔÀ›t&8í #L!¥Ù2½§¯gÕ5¯ }ù¾(%w÷T…¶¨ŸÀX9¼�CJB\gË�¾À¶/¼•òäN´H¥Ø-‘ñ4¯]Ǻ‹×R,íoxÊß´Ó¨,H‡L Áÿ )$Gq$‹—.%®×ÑZ --KÂ8zV!WÐÓD3� ‚D`N™[DZ�ñ®¸x-m¥2Ħ™8ìÔ�4Ø6“â€%IŸýaw›÷úÃíâú®áO¿…—¾â%ìx|;±I„ ˆz… ?þR"1¥1Ë÷H«È"Š!Œm૆` W ÷„‹1s÷@:På.ü™óÙç†WÓ{ä„(¢8N¢@Y쀧€õZ{3¤R2Š {BKïr„_A Á“6S˜)–YxܹøÝ bSJ%Q‘âà}�nñ±¨%rncß¶1™M7ˆÃF,qCä`¬;BL3 ®x ÆB Èe3,8ê,:לLËâ½ñ2¹DJ®”ÎŽõv´µ¶��T/%Ž[ùÁ/—„@(mDFI½W=¬½"›ÏŸ &Ÿ¸.´v³âÚW±øÜ›)õ,mËV�§lÐAlš‰W;?ª��²é'_bË×ßO¶:HÖ1±a||Œý÷ÙŸo¸™#=Ì¦Ê öiºOÜI™“b"­±yjr1)ÇÜs@¢Ð<ö¸ãð”fxdˆL6“ËåòU«Õƒ¦‰fÿÔ‡Z�ë0¦+I Á!'œÈ^«÷á�G�®@{,�PRŠÿîÝbÿt›Ø,oÅÜqÏ]Œ%kr <þ8Ã;ûku*““„• únûÛþôÓ„xl É4öí¬¶‘<»ç1ÿ©7ÓvÄSiÙëpö¿õ�Ì^µ?~�A加!qnVÓ€‰¢=�åÚ÷¥=‚î¬>ïFbé;É­ÇÍNùY Xyñ3ðº—äòøAðÄ#PJ_lâø†Fšn‹b@Ji”„ÑÍ&ÒÚÄPÆX¥™’–€¼Æ³i:ÙÙ{`UyÖGMÌ=ò:�>�8ßF®P$›É&*¦¡þ¾Îb[ËÁZéµJÊ ÿdH!‚hd¬,Œé”RZ«Wß�’§Åq”�œÑÃâË_ÄÜÃÏÄ/–w“ph�¯H@:·’¶kr°�­_y7Û¾öždÙ0øA@E,\¸ˆ /¼ˆU+W"Rw0öac)uÆî4†TR³é“IM«-Y¸ˆEK—`¢˜jµ"•¯»ªõê ¹lÞŸ&šiüSá x¶TjµÒÚ@¡½�óÎ>‡l.×äë2õŒÊÎìÒûc„}v£!áÎöÜ舤? ›Ë$«Ö*´—ÔU{šÊ¶ ÜùáW²õ×ßNÖ Îi�3ñ´ÅÙ YqÞÍtó똹ú j±$Ò™Ä9Zj¡”�Ri`)€ô= œ¬´ž‰*20óÀci�³`÷À] IÈÌ`ù$�%]{ÎÒuÏE–:JcŒJ«v!åóL]t!D‹RJ` µ‘Aªãv¬²“a¤pû^w7i&�ZãQ�A ð%ÓÜcTo¼^zæýfŸq ªm6F@©ÔBàù²X,-Ï §Gax‰�”�m&Ž�‹Âú9J©›â�m3–g²ÙÀ�nŸËêk^Iïa§"”lÚÔJ˜Áà–Jì÷�b{l}î­lùÉð«£IÄ2::‚ˆc9äPžqëÓY¶x©=ÑTu!RzÍ„}¤›ÌZØ”µœô¾ÕÜ ôÎíE �Jä£8Z]©Lî=M4Óø§"Ÿ(Ç)¥2R)„\xñZö\µ‡K6Ûá›8v^g°uÀ8³.‘Vµ7TZ A8`ßý˜3g.A µ&Š"À�Éfñ‚ R *îáÁϼ• ?úbÒwb…mvðµý¶F RŒ¢ àç‹”ç,DçKH¥�Zà@¡”ŒkõyÀÙÚór‰?c‹N^‹Ñ>¾²ç»m{\’³÷;†yçÜÙBJ‰?J›ÒúYÀEžïG‘��ª¯AIK’"eR+…å{a¿#"e¿’¶ÁSÒ8¦}zŽ8‹Îã/�ŽyD&&!£jµP,—ÖtÍë]/à,]È þ�(”Ë"ëgöPB^3{ù²³öX³æ¢–Žë[;:–å ù¬òòKögÅ�o`Æž‡!„ÄW�_à¶«¸=¶m#÷}ê Tîø1~4¹{Bã)ÅáGÃUW¬O|Ë�¥ÝpD1©Äaï‘æ_0Žáì³æÎ8lJ{®åË—±dÙÒd¢&•Ò™|®Ý˜ø©¥BYMÍ4þy0æ4)e§çûZ)E©µ•C>!Dê�ÛŒ±ÇLš>Ùiîp�òrç¦k>0¯§—=÷Ù7!šl>G¹­u×£�ò¼¥dfÍ';cA¡ Ã�Ów篘ØAlUVV¾Ú¤i°Í�Êxt®LKΧ}nZ'Rï(›( €3¤R=JkitðD4³ K0ÉCÐLf�-ÀÓŠå§®eåÕ/K>K{>R)¡´îöƒàÏ÷»¡´‡ò4¾'Q¦’Í5;°5}/Kp€°®Ö X•š’–Œ¤°ðøó˜yôS©µÎexx(YM²^­erAv…dÎGFÛøA*)«#c§dòù7,ÝgïËW­Xu~mbò¨À÷ÛèZ“í]ÉW¿œÎÕ�ö147Y‚�Vš"JÚŸse¨�G¾ò^¢‡~OV„‰ª,—/PÈå9xÍ¡œsÆY”Š%·ÿÅÉïºé.ƒûÛžº“9=Kà*/]‹&W}æžSkÍÑG�¸d³9v‘t‹66>º×tÃæ4þ)x‚`€7ù™Ì‚ ›ñD Ç�z:§wJk;b¦ôÎ4M…SôÄÙ6N?@jã¦tò?Øg™(~’¾€»î¼#ôêµ:"SdÖ‰—²ðŒ+)-XŽrT«5:V@®£;Y¬Jí&.ë fg³Í3þȉ m¹‡¹m�>p?~6OŒŽ�¿Îö‚`O¥µ¡X±öY´Ì]h›&2pšESTOR)Úz—"ںع«ÆdÂ:q W«T€òÅî^Æû¶£ê“„cƒ` C%ÚãZت•þ5Påïˆ|6ïå‚ìSÛgv½iÖœ¹«Ãz½cÃÃ{£CƒA†Éz9w9«¯…ÞxJ8"/kÓÓx¤Ùê%¨��ðÐÞNtÏ/(z¥TÒìk�ý8 ‘/·¶´’ !R;>íS[ɸ|ãj¯ÓÏ›¢hLq}P,ydÃ#lÙº!„Fɸ:1)²ÙìO�hšh¦ñ�&š5Bˆ+ý (%ÊÐB‘¯¿‘îYÝÍ¡¸î½yfeûkrrò 2õqÕ9v§CX-å2‘1ÜöÇ?&E…LŒŽÐsÄéÌÙ÷ÚW¯¡cõÁI3fûÂH?CLóåH,ÙXo0+u+’‰¾m,Ÿáóøæ�˜0Õz}4¬ÕF„”§fs¹c�ù%{³âÜ^@ �B¼�˜"Û»ƒ¡)"I!ií]Šß9—áMb*c`kˆ¤9Ðk›EiÉ>dZ»l߈Ú}6ZÁf0íw4"lô"ìBq��Ù^§ö3ç,bxûâ±AêãÃÔjU”RY)e»ˆÍD¶¥å�ü� ´VÄñHñ¢L>ß;<<è ÷õ£¤LÄ Õ³Š=®{ ŹK�R4¤ãX¡‡k’ öÙº&$j²-ßÿ }?ú >5‚Jµ’¤™–¯XÁÕW\Mkkë“•Oa7˜´h…ôóšÿ¯ã�ópL)…ølÜ´ÉÈÀ…bQ� ×Lÿš&šiüÃàçrò‰¢³ö¼5J){í<õ\”T)¹gãjC§^?C7m·]"éw±kO£”¤³³‹í};Ù²i#ÚטÊ8ãããt¬<™+Shï$?k"ÈÆ‚(næC„v¶KŒU¥Iìe¨FžI·tQë<é)ÑQEvÏëõ*a]k!WesÙ@(Íâóo¦sù~ ¯é¿Ç¦Ò°i¬úîå¦íÌ[(E¹w ™î… mÝ€¨Ž�‰ë5Â8fæ¡§Ò}àq¨ Óp°Ë!ØÀÏqtvx=nlìçKgÎàca,ðò%ò³zÙ¾…‰‘A¼z…z­&„’%¡Ô¼ÚøøÀ£ü�0qT–†k#c^d3½õZEOŒŽ#¥J<îrÅ6L×V]ýròs#ÎÊ8ʻȬ:=Ãö˜1[þ56þm}˜L,öPR±fÍ¡¬¿|=m­­Îî@ç>°3DJߌ}mzÊM�{w¿EZ½È~fâP ël~ôQ‚ ã…õúX8YÛÜ=M4Óø‡AV/÷3™6!wAHÖ_sK.¶w%Æ>#\°£”Û?à¾Î­V»é8\Y³»:§}>—gîœ9ÜûÐ ø“ƒý¨Ö.Zz'd$¤Ä €fn¸Åñæ>“@ƒÍ5XåûŒ�¡ňþMä²™LEó*£c�‚ºðè=ê¬ÄâD¤4q a &²³ìäáfU"…îyÌÚÿh*ãÄI_�bæa§±äôËÉvαÄdÏ‹q¸ßS i^“&v^ZÝÊI3!š¯?Sn'7k>LŽ3þØ#„ÕI¢0R~ä*óðþË¿qvì²Å§‹åkÛ;:gg³YÖêIª,“Í$ «µ–Y,¿âÅ´Î_�’ûÝ›ÕöîjâÍ©CaB¶ÿæ;lÝE2ª:Âää8ÈYŽ;î._w-å8&W’ŸB ¤½Öe7²wA®ü_8ç~Ò5/âØpÏ}÷Õ”RZk/¬H©~ T¦‰fÿ(©N“R�éù~Ýóæqé%ëH:�-yØGú=âD%8ïI›Ö0Îߤ‘ãSc‘äÌÌ_Ȧ͛©T& r9&ÆÆi_ºA¡h—=ͽŸX÷cG‘Ý, ¦iÀø¹"&yü‘‡“ÃŒ öË'ˆ )ÞÛYzîMx¹2B@ V6l·-Ñ9ëžšS^žÉÒÍ]«¢Ð»”YÏücÏ%Û9—ÈH|ZÚ&P @ »„³–ö3Msú¬ñùöz¤eÕh–„b›n‹!�.µãZ¨ö%Rà@k|Ï÷c·#ðøË¡¥Ê˜8~ŽôÔÓ*ÕÉNcPJ$® ™ By¨¹ËYµþÅ´/Ý»q‘öû œèÍ©¹ ÀŠ3 �ÿáÇ<ò‰×¡&“:”’*©cvÈá\táE6]æ"ÝÙ?Â9&Òš”èÃùR©÷_Š[(â—¿úU½Z™ŒAd&ÇÆ'â0ºxtšh¦ñwGàe2ÀKµï/òƒÀÓBpÅÕ×%+Št)¥ü§ ñÍw¥�Â;9jgŠÀÀ¨V+<²qCú÷õ÷'ƚ˗¯`Ó† µ DaÎò3f»‘ŒWnÆ`¥ÀÎÇ;mCFJ¼ryFúvP¤24€A ­Ì?õ ð”ãî6¢°ÎÀ6�cg倒ö:=ß§4k¥9 ÈŠH!“ãu[€ !“´I4ˆÃ®.ÚD$ÆX‚QÛ¼Š j ºAœö½¯µ#õþíÔú·¡”ZéÀ@¾^«= là/€cní˜9ëA´T+E!Z{I—ò}–Y,»òet,Û)…�H±di�»­˜7²”0¾c ÷}蕈�Í�²¢DIɇÁ…ç_HG[{úo\@:Ò-—ìÕ¥D#ì¶ý;52Îq ç~²µQÒS|‰˜¦ÖÍÿû‘L6[ÖžÏÄȨ¯”úPŸ^aso(„øt¾TêÑZ«l.Çk_ÿfö\¹ÊµûwÔb)dÒ�c¦ŠRHÓý§¢R­òãŸþ˜ÿèlÝE2c££ ØÚšôÕÌ]¾„ZXÇd[(-&·ê0t±  lÁßÑ/X ³´û¯µ©3gÀŠêuºƒß¼òJë#B1óø‹ØûŠ%½<¶ÐÜ|^a¹‹’5GSJZ‚¶XßxØHC 1iáˆLš—©}?–t›ÜŠíÂiÖ9@ë‚Ýt-µ ¿û)ùì›ãCÄ&& ÃÑ8Œ¾[­Uo¶ðäÐ"๹bqÝŒ®™�;·oq¢”Jˆ¶½s&“:OÏÅÏ¥kÿcQB eóx¬¬\;5}-†cC<ð‰×R½ý‡ˆ¨†Ò*Q—íµ×Þ\yù•´·µO]œ8!òÔŽ® 9ÝQÖ>9p¬Ò¯#�™ì“›#Þºm[ôº7¾¡ß÷¼6„0;·<ö‹údå¦D­fšh¦ñ!?“9'—Ï—”Rì±ï~¼þ•¯ÅÓ^s—Tpˆgê»;º[¤3œ·jõ:ŸùügøÁw¾ÃÎþ�Ôêµd2@T@�ÏQli¡gÙrúƒnÚ:…Âì…h?›x‰!±=; )ÙüUÂFç¼q$É�igöŒ÷ïàÇ×�O�º ˜wÎõ,:ã*¤ÒHaIÀŽ/6¢ÀŠˆb[”WÂQIÙæÒFÝĺ Øúƒ�H\µUlÒ”µöœX‚jjb%�M¯I,¡a¯'A81Âmïx6ƒ¿û.2®c0T&'7Ô*•esù×!S Z©äâ™^àß0köœ–zª��;О—¬½¯ü€BçfŸ}ËN»!¥%üØÍàºó"G»‰]ÑC_zCßûÊÔ’†ßÖr+«WïÁÕ믦µ¥åÉû‹¹:iSËÌ\÷f‘îkfX8ŸåjRIÏÝ/ìödµbÞú¾÷lýóÝ÷yÅR©klxäžÊèø[€L7lNãï‰`ŸL6›•Záyšc�9­tJ´aGÉÔE83¨”ˆ%ÝlÓ ýeJ‡4‚»î¹›ŸÿäÇô ÆƘäÖjLNL0:2BÿöÇÙºq#¿þö·¹çKäwo}6¿{óÓùý®AðÏ?ü|"OŽêubgÊ0‚ÐYٓبÂðÄKDÉ?Ùp ð‘™RªÝ„»‘‰# ÐÒΰí{°�†š a�[SÌfw%IÉ÷p¢&»j¤ýÛSMËX!DãZ} ž´QœÛ£ã);Æy¹‹Ï¾šÌÌy�Eç Ù\~V¡P:VK½×Ô©Û ß5cæÚ\±pm¾PlPƒý;É‹äryŠ¥ �=t1‹O8?!å(äìw´ÿß6òrzcÃÖ_‹¡}–°:J=¬“ 2ôôö²ní:K2OÖÂߘ”ÒßèZ-¹“¬Ñ‹==®øÿß@cïU¸• 2âàÔ"2;‹…b<{noOK[ûâÀÏü‡CuIÍ4þm°¯TêÊL.›» ¥bÝÅ—’Øk¸¾efŠ ¨£„I•€¦Öó›Š S¤Ì*|ê3Ÿäᤅ <ê2KÝH‹·'¤Çqݘ8Nšk�3¾õÏŒ‰AfzýÃ>‹t%¥�ì³û~·.Ó9U3�GÚýbo(»ß ‹“GªtÚó<ó³ý¤/W(´yÚ‹¢Éê`G?¬ÕkãÓ©³iüÍP¹œˆ&&ÞärWŠ…ÄZ}ÉŠU¼uW}&›Í2%Œiùmcf:¹`e^i«hN�W6Ü~×�¼éõ¯eç`?(E=heå5¯ œgó>ËØ†{‰FvR#Ž¢¦A2VŠ(Š’mƒ¥ñËH? )jÊd»ç“ë~B@Ð…jX˜ Bjâ° B¢´†($¬V^Àäà6~é=¨p#<æœ~+Ï^O®Ôº»&äz‹ÙZ‚%†@�û?Ånï‹Üí˜fVO)ª9-!£íz4‘clSrMç·×Ž%§©©¦Tîçž½‚�_%âgbbü¡‰ÑÑ� ¥^¶Èœ/•D}br B¼¹½cÆ>€2� cfõJ•þþ~ü…{1ÿô«™·ÖªÙ£KÈnf×]Å"šã÷o&£¿ý&ž–x^�4d®»ì N=ñ$´ÒiÙß)‹�&åõ¤ÖGÜtÙ“ïÔL_5Í>¥¶ ˜æÜ­ÝGµV�|×ûÞÿÐàÖ�=å–ÖâØàÐm•Jå¦áÑ¡_OÍ4þè¾Q(—öÔžç‹®¹éÎ=ë¤)½+i䤎ó÷ÅQîÍg@Å|è£â_ý2c“ã„Ò§íðsØwýK?Yç~ÓÏ¿Îö_~ƒ±-�ŽôÕ« ác�k°{;! @H™Ôy”’ÄFP­GH)ÑJaHÞ‹©$&Š²ÒžF+í{` £Cƒ‰ï—Ò:ñÛZ±ö9ÌÜ÷¨Ý]ê¾i;õ°MBX²P6Š@:³rI³  A¶AÓØèE`_ã«f«›zÔä�–ɱƒwJ#»±ŸßøÛFiýÝÉ]o¹5º#13­T+CC}}ß7ÆÜì AÒ§Ò¼'[È­´â82m½=“ó{çå&GFÙºi3#ºHÏÉëXpÂEd2û �±S¤'²’Îÿ‡¾ûI6¼ÿx¾F�0på5×sΩgàyÞ$‘6Þ?ù}öï°ÑO*YÙÈ$Õå|êÒ¨‹t©ùú·¾ýÇûþp÷ü¶íµÉêÖ‘¡¡·oݶù�@8-ožÆß!D¯”òêL.WVJÉÀ÷¹îšëim4§Må±äh1�p<ÝÐra»'Ó|žêõ:ûØGرc;õ0"šòê5ÌÚû$‹•ÍXº7Åù«�…rB.ÒÏ$$"ã! RIŽɳ‰ã„`H¶ R‚$ÆS­J‚¯UB4JT‰¤Ž•���ŽçS� *“Ç(�îCO%ß9)±ëúï–[¥—V–8"'@ŒŒãG¶#­xA:%-%­õ¿”6Ú›â¤÷ÒbÏᤞ¬3´MZr³N�ÚO±•ÉÊdB8ÊÔ)•Ë~$D½:>Þ/|ÿ.Àxž¯Ã°öl¡äÙl._©LR�Âñ#οb¬ÊB`B&BA×±1çè§àçò lêWÆí8É%ߎûÿÈݯ»†B1O!_¤”/±|Õj®]u2a™â7úàn:mê÷Ùè"Ý�Ù©¦ŸHI«‰´ÐÇžËã£#�>¼9“/ò�ï{�mß:¾uû£?&¦#šiü­x±·”ZÊ-Z{,[±’×¾òµ$i3¸ÅC)í¶M�9�'Î1ƒÝ—¥+Ùšö?øðC<÷¹ÏdÇöí ’uaØÿùï§{ߣh|”Uw™˜±�ñØm?K\›ëïBhMÉ‹¨W+IÿMFLŒŒ ¤¢^«ÇI�;kL,ø30ì>n�»ë31?È`t�DN"[$;{!=G�CÏAÇ ´ß˜ñÛþ“0jVŽ' Œ�¦J-°…zì¶ aߟ:þ„ ,qˆæ™Ó‡bm[ûƒXR1®/\óJã�màî÷½ñè=tt´1<2<\Wò»o¿íºLÇŒ�úÐðUJ©frùYù\N„µÚØò o˜h;ð´Ž-¿üŽò6v-fÎqçã·ÌH „Ý@ÙNÐ%n7‰zûšõDïbFGG’ÒËžþÌg'K`üÅH·èŸ"åe—,RÏ�%U*ˆûš)Ò¹¦´k6lëëÛô¡7¿_d[ sw‘~ü‡Û~{»©Õo~3ÑLão�hÖåK¥=3Ù¬gˆ“N=�}öØ!¥[7Iï�Sä ®…LJêÌýŒôŠë†Mùî·¿IµZÅ™%û°ê¬õÙB` æB#�Ù- VÒ¹×a´í}$^Ë "t’úÊJè|ù¶fõÌeæœnt¹“(×N±µ=YaR“¾̵Ð>§‡0×…ˆÒ ‚Y‹ÈÏ[IËêChÛ÷hf­9‰çßÄ‚“.¤û “h[¸ Ok¬�‹%—ØŽ’X—ëLмB¦Ý¯.¬�¿AЬY`{Jh’d'ÛöÙn[?3pûvì>„Sq2Ÿ:WD(Åðæ‡�µQÚ:fdâ(Ú<>0´µ61±BiýìL.7PÕÉJ}ÉS®ÜÙsæ�3êÂó¢Z…š˜¹ædò�³�us,á5ö51Ž¡¨dXáÁo}Œá_~•\6“¤?G¸ñÖ§sÐ~Øß»C SB0uJÍM;›´s;vK)…1 áwÅ‚T“êîìFAžï|á{_Û¼ááÛw>¦ÆGFC ÷•[[oŸŽh¦ñ· KJù�R[ëj­µRFðš7¾…}öÜkŠæ1¹�Ù8bûž' çÜ`Œá[ßû6oxÍ«ã(‰&ºN¹œƒ¯yIjLØYwÜlíbe¯ꕉdEJ¢:Ûdbx€¸ZAš¡B{DqŒ’°V/“øw�OR³Éäi[´*WB¨¤®“¤Ì@4z{=öëXóL×%@'ïµõaÓ[ö»¹¶*)~¦¶fá–¾ìqiI°Ç",¬\ÚJ½Cç¬øÀmµßß“PîãÏ_yѽ?EÔ'èïÿÃÀcÛ¾ÅñA-ííÇ(¥òa= õ½ê‡>÷=5QšQG¨öo푟½¡t“„Y%+ßÖ”´×i'ù6õøÈϾÆm/¿Œ|©D¹ÜB†¬9ün½öF‚Là2�+N�XÌDã¾ÎísÁ>[8"p3öøÔ‘Jêý”~}Ø?=oèuïyÇý¿üÊ×÷ËærzltdPÄò½™Læ…K�fšh¦qX�Ë}±¥­uFX«ÓÝ=›w¿ûý‰Ç¸y»�ã`·í3îyÒ¢c�Ùs%)ŽW¾îÕ|çë_Abf¹üÚW³òÌ+‘ÜÂÿcï*€$G’¤g Ššaxapgq–y�™™Ÿ™‰î™™™ïñˆþn™™™yz˜¨¹ %eþnšlÜ–¦�{† 3Y«T*)¥®JWDxxä‰÷B´/3dEe<�-�ü3ZíåoŒq¯•µ´‚¦Œ³ÔMc.ƒçë;Š ÷$c] 'dÒ’© @O$dvÛ�CÖá$™'‚B‚‚ð¦±`á%•�ÙN@ÇÃ[QOLÕ�ˆúmÖb~ÛSØ{Õß@í{õz¥·cÛöçš³³ÇUkµQ­´mÌ_ð‹ŸÕc›Ï‚5®í€ÊR—wSq EÓ‚ˆÀ{Y¬} óüTwa�üÎ÷cá¹ûsI…ãO\‹_ý…_ÅÄø8à—wñLØbÇ’VʾƒúÃb|QÚ0PšTkU(ù’+<ŸŸ9§•¹î¾»?÷§¿ø+ïlµš³í¹Å[êõÆ7XèMßþ­ö‹##?RoÔY’â}þ¾ó›¿a·_XJióvY9Ðxó92n�f)~ô“?Šûî¼Ã½cU€KÿèF,ßtF!?`¬�Ù'›*§ sÉØ/Ÿˆs ág5‡™{-Œ\ÉXj¨ñÒ)åâÂ`âò!Ô”Á–Ò"$D@bÈ‹Ÿ§tŒO<’çŒ4ß³<ABP§ÙŸ‡€j²ö21Z~®ÛÆŽ?‡î#×a$J°gÛvÛm¶ThuÚí“Þóõók?úÓ“&Œƒ$I‘,-ÀIÁ4†`  ÷ÜŠk^þ¿$àc°íæ/aÛ§~¦Û¬A…_|)yÙ…—¨-—3Ïä$í’r šò�Ïf>Jt¹Ò€”)—&³s~æöo{ç{× NŽ­2Æt»Kí»��o¶Æìélöík7¥ƒ ü±�áÁõ аo{û;°yÓI€Ò%zcŒ Kã¶òÜMùXA2Õ2“áêë®Áþ={À±É6¼ë›] LjŠ!‰adQ‰Î–࿬ WÌ÷ÂáŰœ‘rú ›"8üµVÈ»ˆ0>Û3S:EŠF’X@@%æCiè9Ešù÷רn$ ˆº?É2ËŒpD…×£ƒI»…¥Ã{Ñ9¸3û÷©0ŒÐn·Ò¡u'Íoù–_Îj£±;G¯¥àŠ(mq  ÉÈ­-¸5€Ô²ïÿ£ü£ˆzKCÅxÃ[ߎ÷½ó]y½ŒB©±¸±Üää.�«¤÷"ó‘2Ä\Î.ãUù™ŸXdµFµ¶ïº;o]JZíÉ(Šã^·3¯¡ÖZïèMßþ-v¢Òê‡ãJeÐéfßúí߉×¢ÖŠÙ‰G©¨ ×ýdá•ÿ—à"½©^/Áç¾üÌ:„ Ь}Û×#'¿ PœÈ¶Æœ¤Š¡M)ùDÏÅæÛÁk¯PäRe™û꣯©â̧nΜ$‹@Ù‚™a¬Ôr߀b Œ¨øsÁ¬š§ǰœâ˜ÞCÞ—b®šá6ÈQ°ârïP!Œ+nÒoM=‰ö¼«Iʬm]ò]?W·kÏ­*¥`­Ega*M6†¥ÅÜÈ{-çÑXó>yp“¥ØqÃçÑ~ú÷™8Œ]§ØïüÖowªÇFUü^…œ¨½Ÿá>~Ê2„ ðç‘a/¹Ÿ Ä~b,rLŠ‚(¬½°{çŽýSÛ—�Œ�Õ[ÍfÏdÙSa>Úš¾ý[ìmÕFãÝ��F]k�ͧžŽw¾ã�¨Äñ×Ò˜I¬“ë/êØ¼#­|Å N¿ì+W~ 33°Æ@5†±þ­GcdL0£òðÃg¬!Í'LˆÈŸv Š5òɹ(¿o@л׵ ¹Cæ<èÉÀb¨³€•‡§�©*I�Y›ÿðµV`øˆy¦�@Ên³ðDB]œ´éͰ%�!€‘¼¾ïî¸_¤)ÿ¢Ex G¶÷Ñ;qðáÛ¡”…ÉLzÒëß¡V¼ñëcTêJ+¸}{æ Tµ]€iØ(ŠzæÆZ$÷�Þý�Ÿz Ïýݯ ÈÚÃi§ƒo| dÎ>ãLÏäN+ |m½g$3Œ‹-9¡·-_ I7eÜþõLÑèf×ÓÞ½÷ÜxӉЪ‘¥IP�×ë�{³,íMߎÝÂ�ez½�Eq|^¥Z«À\öÚ×áÂó.€I|_”Þ‡ QC#ŽSÞy<Œ¬Ž~öÅçñÕ/~YæjYM¬Æñ¯ÿ�k¸ÅbÄ„S g«â¶�E”ÃE4Caœä8ÉjŤ3•�‹…�P`h‹íèÕ€×7ÐYÄæm÷áÂ[ÿý<Ö¾p;NzölØvVOïÆú=O¢Öka¼yÉ ÄYÈûáh¥dxŒÅ› JJ2´Ê�Š9"z RÑÙ Y�Lh6&­˜¤‹ Š1ýÜ£˜úòŸ­ya„hl¹=å{7Tc« S°5ªC£PA�×ïq*”Û! $³ëFúôg~ØÿV._ k,ÇGñMÿzÔkuxŒV>AûÍï-øÍÇÞ�‰|ÿyý‘‡jçy£q»È©f·Ý~Í • ‚ ̺íÎb–¦—gÆØ—ü/Zúd€ÿVSª¡”úÒÐèèk*ÕJÕôüÊoü6.:ÿÂÖ¤4)åëy"£ñ=̇�P”U~ü©'ñƒßÿÝè´Û€Ö¨Ÿr1.{©X3lQL‘9†Ò˜�KÖZ’AÆcê|{/'< ‰§Ìë8PâDŸeµvëw?†“û2†=« ïïd>ˆ4@¯6£,LlÀý#fGVAó–Cæ�2CO%Ö@ƱÀX. Ü ½Œ¹¥Ô´rOJHó`“$ÃágÂÔuŸFm|%6¿”OÛ}çåØõ…?„6]×êzíGëÞþÍN•�9k m2¨0t瓤c¹�ĆCó®¢î8{î¿ Sý³ŒœtZKKøÄ7~>ðÎ÷¼²Ò…µ¥¤ÿgQÚ†Fw¶¤Þ¥ŒÇmÜÉËt“€âa“Å¢{�Úõó?úÉ �vWµ;­d~zú¾Z­ñ†¯µZŸuÖ·åA<229±B+¥cà¯þö°fõx­TóÏ–¨Ô–��:æ"æG ?ôÐt»]7Q/{ÃGpé�ü�kçË$¿“”̱"Ï{x"†Œ¿”?Hàzfè¹(ð¯ë;,8¹×[ ØüÂÝØðäWј›‚4eé%øManåyxôü�bÛªS`u€HóI²¾Æyjžú?# 1Y€IÒ@V 9°WŽP°–¹œÌXzê~<ø›ß‰dz?ª“k°å¥vË;nÿ*zÏß§®½z3^õÉ?E0²@QqYóØt4èÁäÅÖÓ™èPš¶ñèï~?ª‡¦0?w½N[Î9?÷ÉŸÁðÐ�?l¤|õ3%Œ.z"~+& rðWÒ ß ã=¬{y‘õÁ#GæÿæOÿºe5Vš>Ô{áé'ù€�ø_f!úößi—ĵj%Œ"¥-pêYgc„Úf‚º%HÖ§e&[ox~@ŠŸguã1‘’4E·ÓAš¦GÏg­Í= :²žF°�4ÐÉŠ=õ3µ0ÁYãQÒ‘á%C�¡'Ä%Pd>°ïÌbÓöqÜÓ×aèà“<š˜2�1:‹g#8ãíHꈸ1ô i̧_{­¡ZbÜB*´ØFš4f&úIq¶ù¹’¼—Oëð><ýÅ?A6{À]{U§˜yþQD‡·cbÕ*^Jpüû¾zx´·ºŸÀèÀÆ C�œ‚ny~r–(äÅ'�؉$k;–Y�·¿õmο_eÞ·Ïã–“µÛËjÎ<Ÿ•¯e¼�Çõiÿ½r‘³¹-ãèXo=[-¢.Bmã¡ñ‰!ÕËlŸ зc¶ QW6IÞV©Ö.‰â¨¡ŒÅ%¯~-.fØÌÿ£S )o‚¿¤šìe.­L’ؽo/n¸öjØ`ÖŸŽµ¿*™§÷A}±â“7eâá^{Yc)';¹àä�!†üunlÜË,†—¦qúÖ»pþÍ„5Ï^…Jó�\J­5t"¹ìûñð©oD/®½&zÌ�D,V-ÈÏeœ³ Å›SHQþ_ç`b ^Ï'êmœ'1uÅ_ãð­_Dë–·q=U‹÷À1ÌÄ 8þíß„ Rc[lO.Ìçl¤|0à3  ×åóÙÏü·?“%ÐJcí†�øè‡>â›Ñ¼òþe­˜iJ|¿�©g ¼ !ÆO¥–,7¹]ÉÂKO¡æ±“Z­¶~áÙl­^«XcB-ÍÎMß«•ÞÖ÷húvLf“TN“$'NYŒJÙEØÀ§WVÎñ—µ³•¨ øäÒµRNÓd ,â(DèÖàÃÐMa=1âtì cèu(äû3¹OÐ2�¢�,´ô\MŠLf0ÜšÃÛÂÆÇ¿‚ÆÌó°JÃæQ2™ 6ãºÂ��oÁý| W¸“å¥üU±î¥›òuXôFè�i^K/#“,1¼î¢�'ïsjåW‚÷õÀãwcÏ•e2´›Mq„´ÝÆìâN$½3ÍiwÙGQi ¢òÆÐÓ$%¨Gn|…Fn‘ó"sQnÙóÈ�˜{ú~ 4œxªð±�~Œjä(0¢ù€‡Þº(RæqJ{ÑØcôJ”�¤ÍúŠ6åy­_ÓÌBîïÍ)  `xtÄ4[M;24ªÒV7œiL´ÚM“õ�¦o&I*J©sª�ú ‚’›6nòh!É�™:¶¼Šòm”ºhåOô2ö­´Kè:Ö,\) *P´{ŸT×HãõŠž‹OÌí0—Ñ3PT �53ÔPcN&1ÀPsg<+Ž{æzÔæ¶å ¢�)<&÷É¢¼pÞ7á‘ÓÞˆ´RG…Oý…ÆeL‘ñº*,ÄÌ'q¾g ÐË$#¶ØŒM‚�H+.ô !Ù yxv]õ)¬Çâ¼F 5Vw<²^‚¸#ªÖ0vâIXÙÛ\[cæa¹PNÐ`«êÔšžV®‚]è•㺥î¼òS­W lŠ8ŠqÜ 'âäM›=ItaJÂWaU|ÆS�²r×�¿ Y˜å÷¬¤÷"Ã~2l&·É/Šp5¯±R‰UÅñlwÖÕ­ «µkÖ¦8fëM߬VApjW*JkÔ††°lrð›ïËY‹¶"TF¦™ï B+ߟàt‚–Qär4� Ð]˜AgiñÀ0óùdÙËò‰¥i2SœÐн`€¢™fÍŠ5©Ð` †ç¹$ÿñ/ÞŒ�™X†ñUY²ß@‹“gàþ˾Û–mp0ÜUÈ9kp,™!ð†öà€Åœ9ØX¥w2Îa 92ÑSÔ˜$/¿1»ï½G¹Kqk�£0§i‚4I±î¸ãÐE„èôKP_!#<Î,›³ÉB[·€!@Ø|܆¦9‡pi•(D– —^|©{B/Òó%áD&îí+a?ÃÏIB�g_Þ S~j%ó.¾œ©-®Z –ò…ÿâ´Ö0& •… Ã@[­kõZ}ÿ M£oÿ]¶1Œ"×Ã+{Ù›9ý Œ 3^\ÒA¶¼’¹ü}š¨¥1œ i¶àaÕ+UTëu(:‚Söíµ–ø›³BƒÌ2Á�ÜÂb1bΤb- zD±æå*¡lEËöZ¯‰óü46=öi ̼ÀFN¹Øuú‡qÍÛÛWl„Qú(Hö²4HÒHÛ&õ8Ó¸�•ÿW( Æ}A�)o]ÀãwçcñÞkœF^–&€k§1;}½^Jk¨ÚÆ6�‰Œós…ƒŒ½ÿÏ•wÐ$€R§ŽÛY³$‹³ØuÃgQ€^¯ëŠ?מ¸—½4Ê'Ë/c|[°AŽ]ûÌ R–ÉrÒÓQ(.šÔJa‚“ONTžÙUl˜f½ÈÀê,5v~a¾µÔl6ú@Ó·¯ÅN 㸮u`rrÕJ…OY‘œ š•ø"‹pƒñþ(<R±ü|£Ñ@X­Œ1ˆ'× 6:  ßXd//™…F†È¦HÚm´›/-­¶ëŸtÚ°™›æ òqÏ„Î3‚á~p ÷Mj¸÷ ߇ÛÞ÷ÛØvÞ7biò@G�`£,×iܧW_†GÞðIÜyÉס=8†@«£Ôí@“‚ hj^Bz4©ÀŠJÌ Š´Ow=ÕÄ›˜³²Ð ǰØuÿ ˜™za Qm `Ýig:9~åZ\ÏÌÍ!ZŽáã7åj­‹Œ¸ž¡Pgš¯óÃÿ‰Ÿ#Ï>„é®G`S¤Yêvºä²WaùòåBíØ°rn—yÉ’°°W„“ëÂýçâk³ìÛ_ñ#4n§ÿó0¼N¿N›ôÄŠ”?ËCuZ-Õl.(kM˜féä®»U¿ñYß^Ñtİö+µêº8Žë6ÍðÆ7½§ž|ª§Q™õ¸äÊ£@ó³a¬Ø^F5ó3r––qÍu× ÓlÂZ‹¬µˆU½Õ�!tçanê)Ìm {¦Ð�;GHd�¦ë�¢´‚Š*yXÌ@éêèo+ä„ P TP`u ÈîʬBêcسæ4ìØü̯ڂ@×Pm͸†[J^È�èÈqã¶7»Ž?Ã%Ðó¶…ÝCͼ=Nüp¯™°çxA‰s¨ât¨…“É¡JæÇÐ6¨2±Lu ªÆJŠfJ+o~æ?¯d” Ð(?‹òȃ4 N4á¹Qáã™§ŸÁî»Ü–^§³d3óˆ Ôí} éÛ+šF|×ÀÈèò(Šª¦ÛÃǿᛰlbR¤†ùÇCKæûå&�|„•Çá¯ËS§×Üx=gg¡u€¨ÚÀàºÓpà±»°óæ/âà=× Ýñºû§�¨Áà8tmQ­Ž ®8@Š¢*�‚Œ3Å:¶ ‘@Š\ }4™åþtt„Ù‘•صî<ìßx²Æ2„™EÜ™‡6IaîÈâ¶Ÿý Üqé7a¦1éNÅçbEO€ü B�žºc4Šc¦ �ÇÔ(µï!,)°Prÿ£w¢õä]¨Ýx§|ãOc`Å 82õ tgÁ7²å'aÝ;¾•¡10DZq yÅ\"]¼ÿւפ�Å}Û±÷–/#›Ý‡fs6ËpâÚux×;ß…(Œ¤`+_ó¨àR"ˆiq¬&÷e™²Ó%Ç굕 eò84¾.»É¾Û¿?¶OmCšeèuzi’öžN³ôÆ> oDz´ÆÄÐÂðà ¿ R"Ã/]+%g¸Ý¯ö šË’ÞŽòVH‡Aˆ8ŠŽWº4ƒí—ÿ T{#õAf�5VƒË #®ÕQB¥¹‰Æ¨Ü{1rXŘ¿#̤) 9Ì…Ú%ËΖLØk�ù‘xô¼÷Ály'Vî{ëv<ˆÛïGua'ÚÃ'à‘W7¦ÖœŽLˆro£“”¡U^;’ñé>àÝDfØé2÷ʘ»ÈÇÈP< -e�JΊ gaŠÀex�¾vsOÜ•uÑîô°æ­ßŒ5ç¼ �™;ãbÌw14ºƒç¾�ÉU 5»llÇ�=9_kŽ%5»¸3;žÁä@ ƒ „Jãâ‹/F%®”3mI?ßó5)+­ü÷š*™ØÝE1ŽZÂõ�¿¸šÆ'?©€×J›X¶ ½n•z :Ô:K±ê�èMß–)­M%Žu®ß~„ y~ð[^òƒR|I€ðTy=·YR?ãJ+—¯ÀögŸ…Ó;³-¨­#Š«˜[Ž�MçbâÌWaxÝi\¾µ�h­%é�…Ø2 †Äd:I[=Ô^Üä±ù9Ro•BÄØsüØwü騟ó^·ëIì]¹³ÃË¡øá‚70÷b †dÈyž¬*–uPÛ�9—@q|t: n–Û(¹“D ŠÂšÓÏ=ŒÎîç0T ÐY�5ç¿Þy0º>‚±MgC«ÝÅ9 ®;:Š(G/cÈ/3’¢]!%ØŠyÝèÌNãÀ}×c(ÝÀmf°ñä“ñæ7¼Zº¤Ú×®B¡\ÃÏKz‘`P2Ã+Ogø• l kÌŠÏ{•šy½ž:8žç•›¼9BE’$Óøå¿�1&LÒä˜üº> o[tØ—–Pk�ãÖ­sÚOòI‰ EOÂ’3›—fI�òtùb,š? oì˜ûeY†]»w!5Æ­+ërM°:Fõ”‹qÒ·þÎüÖŸÃú7|ãëOAœ÷1±`Mæ6ødî+ë餔b‰Ös¹p'—"g‚Ÿ¼ ËóTBäŠBs` OžôjÌ -?êih.@� H߆l|Í\ ÷u×ly@m3žó6ÔiŒ¯P 0ÎÌ<ÿ&‡ëXyÂÿÆ�`ð¸“Üþ6n [‰úÚÓ1zÆ%\½¬W¢RƒÛ2'”ŸÃiVìbJÏ&¿nô¦÷£fRÔªU„aäÚ�_r饨ժ2÷!M|G}€"&|2�رÜ�±¾×žæc’&£ Çà%)qm¥¦|á>—[k·[.?j­5V‡qd¡úMß^Ù*•jµ 0Æ`ù²åŽÑåõË­]´èxd-,¿°†@ m)¯~š7Ai�ÅÁƒpýÍ7âKŸÿ,æççÐ@kD£ËpÜ[>Ž�oþV­s2ô<¥TCg�H¨é10$Tœl­åveTIÒ¹³^3ß‚liݤ…ZµŽ¡ÁA¬^½J&Û­¬Œ— QíÏ}¼Uø4…c6‚€t«e-Yan.Ñâ•È,#ÐR«G*¥û¼1Wd›šÚ¤H[‰‰ÂèˆVº:ë[¹ECƒ*[j.³@’n7/ÔTà,í“Û�lM#öñIÖXh�q)OÎu ' ùÌóÏâoÿáSxôÞ{\B2C—{‰WoÄ)þÁ› Ž(Ò_ƒ"c).ßËø;f~EЀ'¼œbÌs0�Q<.(øhŠÒü�´Øf€€ÅzÍß<¨”,T ”h� HJ�Œ5è€àjÜÂÉ[n¨,¯?f¿Èò³b‚KÍË�ÁV£Ö¤ð¯GÀý­„n=¯�áB)›‚WÂ~/kb*A±¼(`e.|8æº*´·æ9é*ðy,ž‹úq¢î&é­L,›€]ƒ‰Ó.DÆ ¢3óIÖ�%߇y!ð>{ÛÀ0êJ!Ow‚s�݆(kÊbltܱ'·œyVN úû,Jh÷~pá FV†¶DØ™ç+Ç”fùžô€äqx.¹]ÞXURœÍ×išAk… Žl¥Vk:ØÛ— éÛ+š5¦Ö]…A#Iz0½V¬XQÚ÷_*¿²Ù[ÕìãÖ–5:“ñaײù†›nÄç¾ðYìÞ¶Íåb”Ö0VaâÒwbËG¿C'œ‚4ùûa˜Ë™Ô¥R@±%À¿!'[Pv†›Iz·ŠŠî³¼UݔŇ2æ+ÈÀ*›ÎÃQУ*Š…Ò»q×ÎPš[,™%娭/ÎÏ`,›ÅÀø†ND}ùš‚G¦Åþ:o#ÐL`°¤“g%præk‚2 ` ,Þ‡é‡n„nϺÄuDÆ©§ž­µŒÑ–wË´Vv‡õR¾üž Š^¹RÇ&¤i…‘ÏT‰z­¤wŽó¡F°aÚ¦Ñ;[\\p¿?ht“Ę^º¬4}{eS*éu:ÆÔª&Ž+î ´Z©'ü¹~!E¢’OK¦´7MyZ�]vèð!Üxã ¸üê+±{j : `�� "ÿö¯Ç9Ÿøa×­1e2¡ã=5'T¶$f²ß2ìæŽ•dôˆ| qaý zš�5Éè"pÅcÆ«ßãPùñ"Ö™ÀŠl¬Ž'h0mvTûŒa®,¯ÀÏÁ&5£VJoF+Nò¡ÎÇä^3ßÓN-Z‹ hëQŒuŒ¬? ºÒï==¥‚'Êçdùý—d©Pñ¾eù0ïDZ3°´w éÌÔ¬ÅÄØ$Â(r=•ÆÇÇý¬/Iã’^»Ÿæ+'xÏd ?«Lnò�útË?¯|Ý3åg•ð¤Ä1y^a|zzaë‹H² õ0T”N²´תÊöRû¿«ÃfTEßþK­6ºrŵÕ0º@ºZ¯Ôðó?ÿK8ý´3ü‚‚JIÆ™|¯¼Ž@Á@(f¸-,8€k®½×Þt=vm}1ÿý+èJ'}ËÏâ¤7 Q­ÁrOþ“’i`˜GƒZf– "['LŠ#Î'ÝÄoBÉN»œø-¤LKÜ4£�ôbÄ8 ÖÏô8<×@�δHô+‘1l�Àê{ä Fu¨ uêÔ€ µh9€öô( ­;:ŒjIÜ¥wݽ“üø¼ÿþÖ)ÌåX‚Œ²{nø v_þW¨™–ózÇ'&ðÆ7¾ ï}Ç»A�¦ ¬ÏcùÚL©r’ë¡ÇZÇÌ(�`)XfÞ/®øÝúÇY–á7~û7±më”ójš‹‹ÓVï·¿Wš§é+ôMºÍÖ›j�úñÖ˜(Žb\rñ¥X¹r%`Ë›9•ûõ¯ 2”hss³¸âНà–Ûoî©)Xk`�j8ó‡~_ÿÕ:©¸lrVH€Þ 'Q·Ð(…¢yNô<ž_Ô·˜ûˆƒbþ#eÇOŽ‹â|s1©a‰ s6S �Çñj]�q¡ç¤ý ¥Š@§Žáz=Ò;TGÑ m7Q_�¨1¶Ç$%›tkðÿp?êÖ‘AŸpP `𴇽w\ŽöîçaÓ.â8v=•^÷š×ºïs©Ò%í•2UÉâ Wq]ÈÖxeg|¿‰�ñMš u[XÐ%áøüå=òy.§xË­·¢Ûë!ŠcXcšíÅ¥«<Š2ëMß”R¯®Ôjo­T+k¢(V•(Â%—^†U«Vɇ§öEqñë?•W_ûÉî‹|Ã�×㺮ÇÔ Ï» *€Á¹Ÿü ¬¿ìíˆ t!_BÐa¸&•ŒT1Ù*= éÑ„ùdšÒc µ™1s³¬1a%ÇÆêzáAdd]å C�©e+ÐRKoJ��ÜÏÍ9 šäȲŒPÃsðâ5p{f¬™ P™tI‡tyi ¯¡XTª ï�¤ ]8‚Åû¯F{ßVŒ�M8m³ÉÑq¼ímï@µZ• ÂÁ—)5s_1±+qc‰ELê<$R”KÕÐ|ÅÏ%í `st±Ö#gSË×íUw?ï¸çnLOAš$xYº¹Ýl~À3}ÖYß^Éõ‘¡J†NZ"Œ"Ôj5!Θ®G«©DÃóä !ÀŠûìÚµ7ßv+¶>ÿ¬µ®¢?™Ä–ïùuœpÁa alQÎ^ëbNA vUÄ¢FDAq`2Ç�é¨"Û;ÒœŒ9á.‹áŸ ¬^Ï?[uì±brEhæoò šŸ«„Ep³–�ŒÀ€Ô‚Þ==¾fó4ô 80ü¬`Óñ+¥]³:2�JÑËR@$®+$Pò(d|2“«4;°/§Vø@Qx€hÚƒ´ÛE½VÇÒÒºÝ6Öœp‚Ò”Ô5·X”({=~ˆ½cè"û¿ÿ,¬Ò²ë¦È•ˆ¼ó¡2¬%AOþó+(€íD« Á• &.�……t›-ç)Vªû’u,l¯OèÛ+›R£Zéðe�±Öº¤©É2À–P-%•Óÿ€V_¶>ë%=Üy÷]xò±G‘¥©«“Ibó'~ë_óXhÁÍÙK¶Xo-ÃN …¡"]™T]&ÿ¡�óàRœÈ(SžÚ8ÙYZ³¾EÔ‰ynC¡LæQÀbMæ$ŠÎ!" 4A¶X‡È|krØ�‡ûåç}jDš!à˜Vjp÷¼6Ößæ„��[�Éf؛ͱNH(VgÆbáÐ>Üù"†�A½>€ÑÉ Œ½´DqT.iK$‘d¿~+5ý(UvîkKD¼Û)ÁDã hEl·°¯x�ÇU|Y(Õj·‘t:h¶štˆ¹¹™#Ô”Yߣé[Ô¨« ŽO:aݺ*ÕšzÉÜÄî)>+ yIÆM0ÕDGºûIšâ™gŸÅO<æ@&ŒcX¥1yé;±áõïwÞVž—)ÈÈeaÈhj%pKfèálfÀóËdt%ðKº€µ(Ìßr"vûUB÷·ð0JæZ!?KÁ) 2Æ-náÈ€ç€;7kgܶ^F bž™žCóSjò³QÀ =àõÆLoˆD],Û^gÌ3!3żŠÖ0$È‚ÀÇöÙ­ÜKbx2KÑÚ;ÕœÃRsÍv•Z«V¬€Ò’‘àez•ЖKd[,ÊÔ•eûî'�ukò<Â]•á.É*ó]Ï/9õ2šH/�Ä�V«…ÅÅEÔœbÐ…±sÕ¸¶½4};=£®R*‹kU(­Ý—È TÊ/¬®7_là¾\/éæg­Å>€?ûó?Æã�=êhÌqµŠÊªõ8å]ß„ >”+eù-8q»%YM(ÐaÙçÞ3ÉjUL<','mz%¡"(Äî/AÅŠÞQFÌôœr ±ÀRèf@'å«#-ÌÝäã‚%ušóÃ[ 8Qw³B¡¤{Mbïc+eHÍ7/kæTP 32ÄU Yò~I«{þ»kbN¬°€ž;• ¬ÁÀÀ *Õ*TfpÚ)§&d‰¾þ:._ß}?®{Z(k6–Ç Énr„ü ¨PNÔ±â¸þBM™ø'ê=¼v·ƒnÏE>�¥iW+½ØîµÛ} é[©Y“)“$«æ¦§›0ÖŒŽŽ½¼x:fÚ’ bQ°QÖæÂ/þ‡Å¥EÜ|ëÍxöé§Ð\ZB­ÞpB˜'¾íëœú²›dóN‹š‰{ÈîŠ`•½L%qra(Nª¥ÌæOàܧjñ†Éxpæõ¦$€òÉ‘ÀeH÷á~vô4ù_Ž;Í„àY§4¨,ß'x-½ H2^SLÕ�ü>qB˜ßb›k°ûfJÀ7 uÁ­3L˜¿ÏRÎ@ Ôø5+ö¢Â�Eoa£#£HÓ±pÊɧbxh˜ßߢ'Í¥Ôüˆ¿å³ÿúkUüÞUTEîE˜y+»ÓúuÁ×þèƒìhç¿ÐJ#Ð�v ÍæRÖKè Üú@Ó7¥¬Rªi�­¸VÈQŒ��T*¯kï %�å¨"5ô�qŸé´;عsÚ­– ›uSƒÊÆs°áõ„Uú¨4>¥L•>�9ùYa­Äñ, µÆäÄ”VÈ’ ­VëÏ~ÂÁ1ÊSgÆß<‹.ù”]˜ì©JÌ +³EuwC™k-T (8­ÙT‘ Àµ¥´‹È×dXds5Ò“Á$<Á‚û… Tð19ph~Þ±Ï 2;Ìsh‚ 2Þ7(zž¹…û+ÅëR¦î©É�� Ø@à¥'ÂQž×-šÛØ&€¦ zœêTÆxtçáðãw «ˆ¢�J6lÀìì ž}î9×àôSOs-üÒ2>åq®—¸ãþbO~AýrN4‚‚/WTúÌøëw¸ð­<¡3í£O{�š$Iò0«A§Ýî&i2­mûMßJM%Yd­­u»]k�A%Ž19>‰jµæë ä×q2åOO|“‡)röX{ÂZ Ã)2WëH[‹”-ìNGˆù%žºÃ@Ô]0!Ÿ×ÄPÒ%µ¤3rÁ'q›ŸC¶q×lÖE5gÈD{±WJ’žîFB‘p èèåI0Ë2ØåSŠh:ËÃbÌ ™�ã“Ù32öø7ßÏåuzn;ë“:¼.äs7ï (@šäŸá>8ðž¤CÑëS©˜—s�Í�ªsósè%]Œ®X†Ôš—r~ŠÏ|þ3¸æê«0•þÂßÈ_$ÖKTšEXLx-²§’/ç"ÃU~àâñ¹¿'±_y�ëR€¾>:Ê[StàÀ~DÕ z½®5iÚ ‚`[c`°4}+·Ìd @„‘­ ¢ñrÏŽ5kP­U}5.Ü&Myµu!ë,üJ¹7ÿÄ×bp(ïãŸõpø©ûОŸE~zÊɈVÄž^&¹lLž¡ôLÞ9%0�@•Ïy¹÷�çO¨§ef3"XO]«Û©{Ö3Åfb©È±(‘›¥r1XXªŠþi¡†“º•û*毜zt~]Õ¨ly�ƒaÎTœÖJ#ø/ y …¢E¦Y”:ï¹ÔœcÈPA¦-Z©hȦµóˆ]gØÆÀ ‚(k¸ö®Û°cßn$Ö`ÿ¾½¢.Æ–ô��ÞèñxŠ4½ à#I™Öâór•ÅYÜ_•ýî¸ê÷Ð<à&ßã¹!�öðáÃPJcpx­N»c¬™Ðš¾¡Ü¬m NLTk/Y¯ÓÁüÜ,Xçåí:(…¾Ø°ÉO¿$ȸjbc­·ìXi…µk×bppÐí×k.aîéû0;õxþ4ÌÉ:1 ½�‰EfSj˜ÈW@¡…2Ÿêã �w°ÆB‰Šw �ÏKc^ s-Æ#¹/:)ðÖ±ðÔ²Us¤1ÓcaD�¨íþ’ü@AQ:9,%µQEÕ~àÀIµ’Zíhå�„�Ì’ ¨ûY‹Ä¼(¿cÝ{,�•%&�bèpáð>dIa"I<}ÿƒ¸ÿ–[œ‚D£VÇŠ—s7kŽƒâQxúteZû"&—j3<Û… I1by�§Í³ò�×_Gc!› ùiÎ4W[7¿°€ÅæÚ­–ŠÂp( Â]8ëMß²N³ “eª×ëBk­4”VòË]žÛ}ýÌ­u_Tc ÃÂuWX³z ÖoØ­5¬5Hg±õ†/ m-!ȓٕ€Ì' Næ «Þ(NV†õ°GŸÒ •`ØŒ—” J0s'TW®²¯ 2î-zÃDLšó\n�”gا §†×`AME®ÅîËAŽ å[˜ËJ- (ã€â¡Z´p÷ÝS`®òý˜û¡÷eøðÎp¤-¶³V¢¬# 'tAÛ‘GÚv¢R©Âƒ¥…Etš-Da„¬— iµpþù V­�æóL|µ0^0ŒL±MœÃÂ’yùÐfK™Lte‚Ò§#(Õ=£Ô>ÁRë´;®›m’¤€Sëld&›îMߎÅT½Ñ²°5“e.d•t»"Î,¼áò 7\¬³F&é%H“ &3ðÇ’­“¾yýëÞˆ�¡!÷›&8tו8ðäÝ•=E¨@=Ê'Æ€O8t1שÃ*­‘ušxúŸ~‹{·B.Öi¯€Ðߨ¿$3G“ÿ�ŒÉmVÖCñé™´]€!+g¬+AäÆ@Uå(`gÈXõˆì6kIPŠù…ZTéUÙ`¤ �üš8y‹6pë²°œ^–bhÆpâ¦'Ø€°HÔ€Þa(dq8}’˜¡@‡Ìò8Z±®GüïÂ|º–Ûé©e T– Ñ¨£Ùl" ChhLŒObllܵ–<†vþÖO^aß\§À½xT™¥§Ñ| VjúCu\H:�ùÑÅAà!Êkcõ^ívÛ�úÞ]»æîÞõ|«ÓšéMß^Ù¬]>8Ÿn¥& {ï31]�õÑvÊÕÀMîÌ]pâ+4 Dm „4�zô”(�ÓN�$ã­r È|.C~@AkŒ¡t� ŒREG³%@Aú&e^ iA½€û(�Îiæd­ñ!šã³Eê5sI9à*�é¦ÙYÔ› !(âna^Ç-ƽGAN*qS]Àf°ÖÀ1ÚͦÓËk5—Ðî¶‘Ú q�PtÓ@S²h±¤‹¦_- w‰bJQ'ãÏíè"˜¡¤þFÁDzSüpyžÈßñS é..,Â�xaiq¡¥•~¼^o$} é۱؆éûk­¥%dYæ¾Hi–B+]�_6ƒ?DIÃìv;¸âê+ðÙþ'ìÚ½ûöïCµVʶÅpœÖ.ºèbœ{Þy¨äõ<�VØ{ýgñÐßÿÚ syÑ%A"µ É8€{_։䧢wÐKóI˜…—œÌÁ…çâdÈœžD'ºA©GF(¥Co…m (ÅÒM!8%�ž ½-Oñ*‹1�!xTâ�‡¡è…n�÷W¶w”ì™Åc^ææ[nÂå_ýž�vŒ¶ÁÁ!\zée˜Ÿàq=O‘�zÃyW[_|s³³0ÆÂf š{¶B côÄS ¢˜r+dFÉЫí-‡œ’Ö7…i7Ñ1‡ÁVÑù6Ëš€½b˜ïá¹ã°Øà‹‡¼­ÛuaŒb�sõ¥D ÁÒ�¤#²¶ø&óQ,àä5Så (šoc>šŒ·‚7ü 8ÿÙœz®ò¡¸ý€âÿˆÉÈgdI‚îÌ!Ô�`²® ¡1:1�‘ÑQ Ôêx÷»ÞƒjËî°|-j»<=i<vÁøòe¸ÝÛ½ìÓ'`{YÈå?—Bñ¯7J4Õ¼rgW\u¶MMÁÂn¶[Íæµì{4};;V*Z)­´Ë¥ôz‰÷‹L+a‚ò‰ ;vîÄUW]‰ù¥E,-,ÀZë»vïÇS’…ã,�Þ¸è’Ë0:>‹œµÖmaÛgÿO_þ)$K³…&bݬP_á Q+²Á$q(Íp‘1`(ŠÃ!Ûb·ÎP‹âB€ë¹g@6ÇšÙ"cKª¤¼–œ'ëizÜÒIsÏ EM7]lmÍ÷ !RÖÒd†çÈ,ï¯RÅÜ Àk`±*A+dMÇ…|?J)w �ŠÍLõ‰t ß ”EctËV­ÆÄÊ•H³ÄmÎ<€�7a ^'J±ë¥LÒóµ—o›c.ÒQþP3›ús64™ò˜( ³bᶤ)*Oó3\YX\ijO> †®˜;íöÚ•¨òŽÅú@Ó7¥õ�´×mZc�Ê�¦Óíø¼vQ¡\%˜Ú¶ý©¿Æ‹[_Àâ‚cþ$½9ŒÛïºÓåmøCöpð‰�}oyû;19¹ Jid®¾f;?÷»xôS¿Š¥½S�jæ`HŠ=óY�E$™ÏzdŠìª‚±ó-F´®8�‡'©Ôr¢/&ÁQH€óL�yÊ4ØŠŠ‚"c> J´ªV‚ú¤GRPTÃîd“VrJÓh껹×ò _UV„±{ Z䞊 ×¼·Q\Á™§œˆJ£ŽÌ—“©ÖX·v�óŽi²þ ~L%E)yÂÒ ~%À+ Y(þÒ~å¾öU @ã"@…ÛäÍý^ææÐj5±°0—v:íén¯³ ÇbýÐYßÂ0TÝVëë³,¶°q…8ûœs±qÃFÌ«¥äï³ÞévñéÏ}·ÝtŒÛî�KìO9‚ó/¼“Ë–üšQ®SâI›6Á*å¤E:�6´²éý˜=¸#'ž„êÐ(B­ u.ÚS󖙢éÀ*?ó˜:^p¯5iÂ"¢^WÈÈ—à±µ Q1äT¨ÃaˆJ¦ ¸ÍmwDP�·‘àBRßÊA‘@Æð^Qb”è¡'§I `Ú 8™�Ÿac¸âÜoXDš�™ôkkyü‘,Í£u`;ÖTÛxæþ{P¯ÖǤ­ÞûÞ÷ahpùÚþ°’”‡Û,a¥Mÿ”PÌó&èùâXT8Ê�? î*Ygâ¼¶x_î¼ëNÜ{Ï�лÝnÚj.=_‰kŸÐí{4}{EÓ:+•Ú´Rº«µB sž/J~XR¬��ç° xôñÇpÃ5W¡Ûí¢Ûî cBÔ‡F-ÒZ‹Åù9Ü~÷�°žJk†7hã£cøÈ?Œ|àCØ´i3\­O¯‡Îü4Ýq9îûýÆãŸù}t›‹ùÄÎ$6) _QÈ’“©B¡æ…2.à>©áûŠ Kb{¦˜�àQmGlŒÐöê9o�õ1)‰"¯DïùÛŽ)ÆÐV@ûè°É[¨y'wA4à5’ T4,R(”‹ÉelBÞ×vʆpü÷Ë6 ô�Ò $öÁÉØ¢37�¡ AÒ\Dg© ë¤ì'V,w54þ<Ãh¢†¦,©î7ÉF£ùó*ÊÃÙtj¾'K€E©YOØN^3 <à_Z–¤Ø¾k§Ô̲ I’Z;R“4û¡³¾“Å•J7MV � WÑéõøåö‰ÊüŠbŽåà�øÔ?|ÊwÕHàÄ~/:õIè—ÁÇ-�=ò0Ž:$…L æœØgÇå�ºî©*éöÐZ\BÒiaþ‰{°ó«‹­×üSÎXQ öÈ8pàþésŸÆÏýê/áÓÿø÷Ø:µÕ ”ÖÐ9¢h­`{MÌïÛŽ4ÍH?&5–—–l(-ṣ䂜ܮPÔõ 䂇ADR{ù”�Ä]2H{ÎXŒÉ�ÏÅä¹`„±>( XkÂ1òØlò&Bq ‡eQdñQM�ù#¥È dï&齫T«gÝç?‚‘?…¼aÒñÀ0ž®àþ©,.5áŠ�{=l9ítÁü²¾P­O¾Úë1HÒ€TB–´ý¢yÛ&Ëœ�Çá:_On_À«‡æS{‹xûðôÜwÛ­hwÚî·«,º xBœ¬Ôú@Ó7«”Ú–&Içð‘CvqiÁéY_"î#TnŸzö\Õ•h A)�ù,À–Oü —­Æ²3/…Utš›ŸŸÇ 7\‹Ù¹9ÎtÂvíÙ�ßýƒßÁ>óϸ떛pøÐAtó¦h&Ë ƒ*ˆ€JÁ²ã±úœW£5s4ƒ¢¤‹l‘ž6çbøˆÍ¹,ÃS ]ñ·_ðHÁe·IcÉ#ã ib` ,B»Kô~Šõ'Jj©)j²õØÆº iÆkEì¢å eÅFi|_tôñHƒ€Æ÷¢W£E~‰JÛ±b2òŠ †2óõàot¥�ovÐNŒ¥FFGÈ�¬'á-ÀBß( M ×´´]†_O­Œâ,Cse,3ˆyûÛóçkžxêI(­s¼²Y’$;3c¶ák±>ÐôÍZ›Æ•Jœ$‰Jz=Œ�O@)-;vy ½Š˜��Åç¿ø9Ç0[ZZ„Q!6¾ïÛ1±q  5Vœ~!Ö� ]€µNÅ{wïÁÔ¶)÷Z<½¹§§¿ù»¿Áý÷݃ýû÷9Ö‹%µ ªRGõ„“qêwþ".ü¹¿ÅëýóXqî\“4ö«/VÈ[N¤LÖÓÓ(¶^fIöû§žš(�`gLz °òÝB�H+J³h�Ì”…T �L]·�ôââûFP¦­¶(\§G‹Œ[ïóš-ÜJp9�’­‡ü/Éšy4Q Î×9Дkƒ‡Æm­–îF¤­Sß°é$—߃-«¤/ɧ{e]|a'Ih)öP`…)@`‚«å ªd»- ãåºÔnãgòFgO?ÿRk ´BÒëõŒÉî€Â4¾ë³Îú¦¬=G‡á¥ci’¨«×àÂó/,„Ê ½Ò4ÅU×\…ûî¼s sÈŒEuýœñõ?� >7IUªPµ=x+ÒN*µu’N=ù×>:ÿ¹<ÌÕ×]‹«¯¼Üy>&Ër�Qª;÷uØðþïÂÙßúóX±åŒ®Y�¨>è<«Ã‚ÌSœf†“=@�!�˜=cØM²Ø õ9(öÐgΕx‘…¦à6+…–œ‹ÄH0àÜDš5�¥„WEìATÉ¢K¾—ql0lªVPŸ¶¢»&¬œì¹N‡�‹&¸ºü€¢®Ãt\§”�Tƒ.†4µVˆ†UóÀ¼û*TmY·‹3Ï<çœuNi ÃBã…ú)Í 4_» Œ<¡ÿµõ© ”…>KÂeef�m¿Ùù9üéþ>“!¯ƒkø-Ïõ=š¾}­ö¬E»ÕT°oß>Ç—w¦Ê…4­¦vlÃå_ù29ä¶Û¨†µoûDC㜄 °òŒ‹1°öd„õAcœTÈ ×^�øì?aaa!OÍd¸êÚkð¹Ïþ³#h—‹ ŽLbõ›?Šó~ìðªŸølzû7 :<çy‘6LÖ˜!KŠ’22tTÐ cxÆSb�¯k0±NT¡¶…!"N’™…5Æ­�‚ŠY!lfdÒ› Ú 0´¥À’bÌV `þı׸ž-Á‡tbæ`؉”=s´.mÃB–½E·ðžÒûcŽ‹ ¤ ¶&Ëó†�Bköz­%´�ÊpîÍÐýö©Šû%ø5_Ãy‘¸çû4@I±;o8¯<üæoT&r9(.¾ÓI“û+…Çž|ÜIÎä!o£”š±Ö>‰ÿ¦Î+èÛ©mÖApÍø²e'¤½ž>ç‹ñ‹?óóE¶”—XÓl5ñÇöǸó¶[Ñê´Ðéö°êíß‚-_ÿPе)‡žywüÂ7£sdLšÂZ‹áÑQüä'¯}Õk�¦ ~üg>‰'}Ô…Ï‚0D84�Íßü3Xÿº8A?­É˜ ”ðXÀ‰“Õüdœ…¬Af‹µ|Ï€sH$ÀE<‘çì3ž;ax�ûåX¥ýÄÜ£ X˜'h"Ïå¸u]kgèY�Fm4¯3ÁdK…Ç€É9!úΊõ.©)2‚CMà ò6¬ïQùµC†ÅÜ:5å½,8!j ¦®ûgÌÜö%Œ…]´ffñ?ù³Ørú°Ö —÷º¯V*(3)¢Yî%”µy†Ç<*Ë^¶7û¯˜šFÜ׳ÝZn÷ž²ÜÛ±Öâï?÷i|ñÓÿ«œzoqzöSCƒCß Åÿr –ë}û/ÏÓ|K…ƒÕj-€5¸ôÒWahhXR,‹¦€gŸ{×_{5ææ]=Cíä‹pηü4†Æ'¡Ql— �±åèé3O>m�“³h.-áùmSÆuëñØ“O`ÛÖa­…Öâ‘ œ°ËN=¡VG'1í«¯£d dˆ,äþ|:·˜È'wNxmXÿB‹t±Þ&�-y¡T†“”’ Qlø•�‰}­è¥0ÜG"ÈslŽ%öÛZkŠãµVz`4k!óGLï1EæñXCY$*Ùï¼FùHÓ »nþ"z¶#k/ VÞð¦7»ïÔž½{ðçõç¸í¶[±aà ʼ(O¿¥’—¬ i¶47$�ÍøZN—˜ßkóGï_x×�¹ùyüæoü ’,àò5Í…Ù™ß4Ö>×íuñ¿} зÿRSZÅ�á‘ol ŒC©@‹×½áM ü±c— ÿûþlß6…jc:ª`ÃÇ~+N>ZÉIˆ •#'lÆüÜ4Z;ž‡Í—ãYœ›ÃSÏ=ƒÕkÖàœ-gâ¹­/âÈÁƒlº ³®=Àä–K Ž{Z¨³“&r“Ip&¼iaPHÔ¦)ÙJ<•Ó«aŽ'µ"lݾnkXèw#òØ›… Â1ì y𺼹"©ý¨x^IŽ`8�¢œ¢•×uQ©ÅXnGAU ç¥'W†;yߊã,š{§°0õbÛsuWoyËÛÜ{û©äÀáC0 ˜›™Á–3¶@+A¥Ã10Æäºô(øÏ‚4æSÄû^�ñDy²…JYmÊ/{C äuìØ½_ýò—rR@¯;{èÐ J©ß°€¯Õú9𾩠\l-Ì?Õj.õº�ŽSYžŸ_ð÷RgO=û4ž}ò —(ìö„ë¶àø-"àÓ�Ü3pVÄ™ú. Ÿy)TÍŠÜ»¿öë¿âhÒßð}ß‹å›6æØ¤°´í)Ô¬O¡â3—Œ‹hïÌÛP êŠŠö|Bµ¹—¥, VÈÉ6?Æè4[Aû&z¶* ÈtÎô¢e´�Û‘s‘VE K2‚Ÿ±ôYàYzVšç5¢é‹4E�-!_“0¿Ä°‡¬ß1éôÄÚºpëØä¤ëÖº}×.ìßݰŽ�±˜>tÄrÒ·±i™p?EBÏ[‡Ã'š§âÔZp‘= JªúUIÓAoÍ�·fˆ ¬„ŒÉpßC½�v»…ñ™5ûño±>ÐôMWbc­ÝÚœ_T�V ív wÞ{W¡“ì;“fîºû.t“Äi˜-vRÿÚ÷£28 &zŢɈO¦h¬Z‡³¾ã—0zökœ<�Êëkfgñ§ü¸á–›pÆÙg¹cÃy‡®ùì¸í+€Ió°si&©ºE�$dß~ª'g2AÎÏ'Ož‹Å‹öÆ‘l*·®(ÒYET 9nJ¯6…þÃ�ÒK¡‰‡oÑñ“"¢†…§Åžþ‚^ ŽÇ§U©Áׯˆœ:ÇÉû-?5žÙ hÑR¥`’.zIŠ®ŽLÏà+_ý fL³¶†éÅ.’¤‡V·ãoÑ +ž=KI¢½ˆ¶"Ç"M†¶ü =�6ý‡,÷‚”/äçQ&à>íN7]-âZ a¾¼„½^g€¬4}û7Yà¤X°Ö˜\Ñûp T¯²-€ýû÷ã…§Ÿq9”N§ƒ¡³^�•ç¼½Ì"3ÅÉË­¨§ÎUš£ÇoĹßû«Õ{P]ŽÚÀ‡Gœç×^�'î*Ю°ÐÚâÅ/ü1f·=}ôØ U1×À¼Å P-€`A)~‚ƒŸIÅ0—]¹uÅüL%tûY8�ç-wÝìzÉKc9ÎĘœG£¸€e¨ŒjÒ¢Å{fɾ£V¯;_Èè"`‘)硳�2½©¼H„ ©@] é‘QT”^%¯�c´V€lj\ù€@¥á¼Þêä*d¢(Äb<Œ«z¿°Gæ`2ƒCsóhu{Nb©˜Ü³2^*s¼0”å,Å>ÒüÝ5¥—TZ“ì9_±%×Ë›ŸùÆÅ×ÛwlÇÂô,‚ pyT­ƒfW¯Ç¿Çúu4}³i¦|Hk]”Ák^õÔ*U~QIÀ 7߈G~) ² 8áÝ߆Éõ§zšr‘Ì!­�êàÆN¹¨¡ud"XÀ41äÒ^‚´×Cš¸¿ÈÚM,ÍA…ÁÕàD@5Ã(l% gY¯ k2è 8¢‹YO‚”‘ ¡˜,¢…²X�Þ Ô¦ e¥òúô û°@ä�µÃE4‘!3­ Fo�Ò8šc™]6Ÿ+•A±�ª ndÅñu T ´æ~t xx?d¸�µD€5è¨çQ´Vüþ�i¨Ð9²‹/>Ž‘Á*ôØqغ{Ÿ;F¥>àvVA„At±yóf,›œô´9VeŠËeUõ4>U”{!V¾.i„Æ÷…çcýžd"ÎúrK4ñ^f2\}ãõغõ§ÖÜétZí{­ÿèßÅ6ë{4}ÓZŒ£xξl°ØýìsØ»oŸ·OÇ‘Ùi\~ÅW^ú{ss³h ¯Àäæsá&`VÖ³Š-<½ç7¥Y*CcØüîoÆYßÿ;{Íw2‚ÁÑ£“TE.îîFÖkcú¾ëðÌßÿ:æ¶>†@T¨`ì@*YœÃâ�]˜žz Ï_ùwxöËŽw\‰=Ü‚O܇[ŸÃì¾ÝhÍF�õ aòÚѶ]„‰¥^�dà„lH'ÎkNÜz-dx,Òô V¬ÿ)zCG™ìÓŤy!\×3ŰYÂ0œÒ€b'L9¿ðDe-‹Oi…Å“�}nÜ"÷ë1´XCj@ ¨8-jšHê ×Gõ…¼®hpµÁ!Œ-_‰¤—@â*zIÖZÇ4ju:È2ëË]^HÑ$ÅÎ,‚úÈ>z$­�û±Òãñ%æ|5A~� DþÂO”%€¶[m\sùW]K÷,K±´¸˜.-Ìß �ÿC¢oÿåFñö,MìªZ­^ ãŠk^vÚ©§¶øxÿÜ‹/àо}Èl†žÑØðº÷cprEþ¤]¨oÉ·yŠ%5C'.¼£#,?匯?½æ>q7vÜy5މŽÛ�,- ™=›¥Žt6†`­� BD�!ÔFÆ1tÂI\½“›ÏFcʼnˆPJçcåÓ¶f‚šüº 9ã– ì¿�-ƹ…9”ØøJF>Šõ(,X§Öº˜|¾Ìø2°oLfŠ‚£,.¥ €²€U@âÎKF�±”öQ6· 90r]Ñ£’♬gbîÇ}Žý€Š9ó@µb�{¡ÜÿÊÞ9@IÒdQø‹ˆÌr÷4¦{lýÛZÛ¶mÛ¶mÛ¶5ÿØîAÛ¥DÄîÆÙÓ±•'Oýkötªxªß­ïÝ{…­°ù®ȶ9ÛóLì=F¡{!2“#[XÌðÁ˜8fT…„Q€¸iIz·ß¤pbÜõŽldÕHâvé†i¢9GpS•‰+]¸24ÍœÍ}è¦�ï>°ø~†z½ÇA8|‡PÌ6gâ)Bˆx¾ß"¥äªnÎ žý<«eC»„õ‚—½˜_ýäGÄ@˜kãÊ—|”Žå¸„aHp?Rôü"í’ $­‘ q½J}|˜‰þÓŒögª÷ƒ»~‚çgÙrÉ%lÝ´–_÷V8y„±£{¨öŸ!$®U1:ÆXÿ§õâù>`0:ñ{ìgñ;çÒ¹¶‡%WÝŽy=×�)µ5Lg…ÚC¤Ý¾/“†enÂÊ©7.{)áŽÇwØ}™˜js|WiHá€!É/‰MZNsÉ@¢í_ÕŠU@ë5@`Œ¶ ÚËdQ™¬íy =ð|¤ÑXp² b�«dµå’ãâ&ÍcŒuǧs£@á@ƒ3Ÿ“n"ÎZ{ŸþÚ0g÷súÈ!f-^MqÑ*æ\x=ûßó"‚ês3š'>áI\¸½÷M>‘œ“IܽÒ¹.iÞ.)@�¶ï" FÍ®I€™Hžø+‰§î:­5üÄGøÔÇ?‚TŠòÔÔTm²ü£HÇwùG™œÍÍL,~¦”êVž'sBò¡Ï~žåK–M_pæÜYžôÔ'16¢”º½ŸÉd‚§=ïÜé¶wÀ†€ÏùK¼ûío!Ôõ bù}ŸÉ–»?ºÁXËS.é5|s¸%wÞ%n©FžA¥Ìà�ßqü›el÷O©�ôÛþ\D2ã!3’\w‰lg•Qä; ´Ìo¡}I'Ç)OVCÊ}STNŒU#täÖí‹…hëbù]ɪëîL®£ !¤ÓÝrÓ[˜¤, �•"5A6&‹@7¯¾‚0vžû6 8®�»N$\BÝýcûÖ�tb”‰ãûèÿÝ÷©�ØOý >ñÑ�àùQézµ¶krtìöÀþ13u6~k‹öQË ùâ6å{E#“µ*—_z©%¿i­ùÈ'>ÆÐ@¿M¼uá³ö�§¥k>$x€óiûS¸o²NfÞM %™âŽÝ ÝÍî�¼†#Ÿy+‡vLŽbŒaPYE¶%ËÜK—°ú¶éyØel¿Ï\xßKØr·í8¶†¥×.gî…óXvõr–ßj%ëo·‘µ7l`ѥˉ2‚úd•h2 �/`jeúvüˆþƒ;P¹¥yKAz8ÎGCbtÕœK¢ |簙䷸†wŒÓ+s#Ç�ºhÜspï­sþÕÚjÆÕGÚõ3NÿèKœÿÁç9ñ¥÷0º÷—Cç1Q� ¨‚Ñ#•  ž”4åʵúT,XetŒÖ:r##»~Æø¡ÔGú~™Éâåò¶jt-ˆ„¦vpý+"E—Ò—®çDÂ’›d^ •`äØŒŽYxÝݘ½|=õ±Aú~öuë©Â›·l¡˜/�Íe-8¦…«@hÍÔ\HV)‰Þ�îš´iµä8dš©Ys4�<_©Vùħ>ÁèÈ�åÕªµÊÈàÀ{€¯ò?â‚B+3ño‹%Bˆ/ )7 ð|!yék^Ï5W^Åù¾>žøô'qþÔ)ËyÑsWråóÞG¶­«Q‚?…)nØS€qK)€ãoèFŽJelˆãßú§¾ý)êç�Õk`´‰ñ<¯ÃgÕ7³õÎÛèXÚA©½„ôZ$’��ˆö�AÛ? �ŒÉàŸZµÊÙÝgùå‡~Ή/îÃ#c�T Al ¹Ùó™wó{²üº»PZ°¢qµ�€rUJ4�0]µáú $G}IX$ƒ»p~ýN˜ÒàÀÌVŽQÈè‘=ŒÙÅø¾_œ?Á@ï1¼8$ëgÿTAjûOy %±}O3ßqñ…­f¢Zˆ§|â0$ëåû#Ðx¤�dü ÙL„ÄÏñ:çám¸„®-WRZ¸Öl|_‘œlÓ<à%ž®lðÆ:E•eúµºsÉ>W01ÂdïaÛwj[µ…B['#G÷²ã5�¶ïM›Š¹úª+i+µrñ%—°nÍÚ4I–&}Œ&ý›ôÞMâÍŽ5�æ•LêˆtÊ@òµ¸Êë77îàE/x1†8ŽÌÄøøÉz¹z'`ÿȘ©hf"ÂLê0\¥”·AH™RQ×±õ§Ù¹w7_øÄDZÆe(º/¹ .½±ÓßÀ“IS ÷Ï铹äâøNmYƒuÑë=Êî�¿�Þ¯~€p¤t„Ö‘M’ÙRŽõ¾�ë_|Kzî¾�®…]¨’“¾ð‘(" 3 :øø$öàá¡…Æd ma;+®XIiAãgǨ L¡ulû7ºVfh÷/˜KnXÉ¢ë–о¾“YË;è\1›¶Õ�È‚¤-׆ÊN…D:& ëÔƒ*Y2~èF¦ßH}¤£C¤òl5¨<…÷/ÑKw\$>CÉ<ž¢WÑ °‰ù-ó—áZí¹rß)†~÷} ¦9ßcll„ãG[?¤m[·Y°m¨œ!™ÛNX™§Ëº$g¼ÝùÔm‘梖éÕ‘kd¥‹ßºç“^Å8  £�÷䃜>yÂR ªÕJ­2>ñ�y; g€f&þ BκÜÎÏø¥”ì;}šö9ÝüòW¿âØáCøž�ñsl¸÷™µ`©[�„F™0ätç׿“ROae’Ó¿ø6»ÞþlÆwý„pjÜ�A“+æ™{å®yÞ-¸øÑ>¯£ ‘ˆ0,p`ÿ ‘(”eF<ã�°çA€…¤½½ÈKlžÏ«—RÖuw�·ý‹ ^GÇ Ÿ£2>Bçú¼\ ž�`0 ¸^“IU!�p¤vUܶ}vU�Œ#pÀ\é;ʼno|Œ=åÞ#x@½V¡^¯1»³‹L&C¤# ¥îÝ—ÌcÙ�V³â~kY~çUÌ»j ®XÄœÍs)®oaVO;]uÓvq's¯™O×¶9dæfÉÎÎbÔË5ZK­ä29 0H£¡:IåÔAKœ¬œÁV<­í?‡Á}!Iæd§;™žÃ¥ã !À½'ØmÇM?“/3}>˜§ÿWß V)ÉŒ)d3lÞ´Ùö§¸HIØ.칿n¹MÜ”­²Hë÷ܴЦpå™4û×t‹–²ðþ¼!ù|�ñ±Ñ‰ V{á?Óàlhf�fH븧PjY”ËçsAròä úûÎ[ÁM?“µ’1‹®¹#­Ýó§™éà*M’cæÄö§“°wŸ}«0ÐË¡/½ŸÞo|„`¨]¯d;òl¼o×=ñæ,¾h1ÊW@""lÅ2ýGâ)$11Zh 6a·%Ê‚Ž½o¤bQì(²ä¢¥_1yt Oø`ÀhÍÄ©Cäæ-¡så|O5€¬qÜ'!“¨@¤S4ž:®lûÄ��Hpð1õþÓœúΧ8ýµ!ke«MP©–-Ë[£)µ·Ðµmëî¿…Õ÷ÛÀ²Û¯`Áå‹)-lA•�0’JYж&Wž@d%^Ñ'—ÏÓÒÕJÛ¦º/œÇâ+—2ﺅømj#Uˆ¬’·­„”uu’Zß)†ï"4†Ò¼eȬëß8UWé5X¸ŸŽL›8.Ã]Ç©÷9«ö2°ãÇH/K6W eáj”òè.fÙ²e+­­­ Ds»g“‹¿¢ï®OhÞ÷I—ÜNzLˆT�iSh8‡Ü|ü# ôõ†J¥MŒŒî5˜—ÿ3Gšg€f&B!ÄÉ0¨_e ])åU*úÏ�CJ«=†ß½�e×ß…\[çôïdèDù"¸HpH\/Bköÿ–CŸ'#¿ûõÁ3S“ B foŸËϼ�žû^@ëüY@…JX¢ $\@»‡1v[£§ÅÞE`«ö<`oŸÏæY²e)~)ÃÔ‰qtqXg¢¯—–…+i™·û~¸e³¤ú±“šÁ†³Y·|–hxN®w‘&ŒÛÃÑ/¼‡s?þå¡óvÀ2ߥÆËxÌšÝNë†ÖÜo›¶�%×,¥¸¤„šå��û`0Óïc† ¶DÙ÷[I…—óÈtdÉ-ÌÓ±q6ó®ZdÁJTÀ�|ŒÖL•§ìgDiCTE�ökM®{!™b )„Ë{‰\)ê Nê¦AqÀÚ%|phXIš®hlÏJJ :&j„µ*Ù¨FÏbí0’ahÖ¨o~P¤…H.«%Ï'A+œš6éó¤ÝGŠê´û0þfÇoùÜg>m«^OùTÊåñ Z}ð þ1ã™‰çæŠÅÇ´wtÎ-—'™G*BÒ}ù­¹ú¹ïÁËxA ·<’”‰r~,Ž{…}{~ɱ/¿�±=?³ ]­5`PE�îËæsùã¯aÅ«�JbÐ "v„„dÉÚcM@ ÓËf¡ÑÄHlUc�Ä""&v�ßd¨NVùõ{~ÎÁ�í" ÑAD&[À_±‰ ÷rf-]ƒ6b`™öSqà Lj— …CÒl¬á˜;‘Èõ©I÷üœÓßøý;~DubÔÞ¨P,Ùj¤ØY¢4·Ä¢[-gõ 똵¢ “1Ä"&"Â`ðð°ïƒ€€€˜‰äOç¦û\這�?]7 Üq-¦6XåäWŽqêËG™<9NT‹Q( NZ D©ƒö‹nÁâëïJÇÊ� §t¾Œø« lúâ×¼‚õK,(Au|xøgAܨðÏŽ™Šf&„½¿gï,€$¹’4ýù{‘XDͤ©Å¬iˆvv–™™y�ö˜™™vo™hv˜4£‘Z¬fîbH x~}naaUiuÚ=Zšò²g‘˜Ýùþp÷ß÷IòÐØäÄ®á`PKËЈ‹Æ߯á×5ˆ¯hËlnTå¶²ˆ|ÅÚJÝ yÊÕÇßËé›zd«OŒþÊ"EQ $3uö}éŽ}ÍqfŽÍ1=5�y+P  '0à(Ã_Q $¢`^�:-�À§üSÔÎå5"""&¶¥ €`yŸZ`âä$ÝÁ€…S×lR-Šé¬X¼æäýVÔ¨`æØªã5ÚÍrTõÙ® mÝ>ÊdÅÀJÉû]®~ì·xé—þ Ù¹g‰Tézؽˆ­Ýmvß³—»¿çAnýÒ“Œ˜ÀE‡Cì{<™}WJSóø" H*ð { È%@++ÑxÌÔ]ÿ3Ÿs€´—á–À©E�Î:¡ß!^�§{íÔ[Ôf÷#>ªrLf#�ê6åg*«€©<.ÚäÙøJÖhkºÂkN¾±Bš¦4öeãÒKô—®C6dff†xp4^µ½6Xµ¹Z± ˆlJú;&y1z¸ŒäÜoïméöDàýù?÷“ÿ•€¢X_^í¤Ãáß>Ãï’íÍŽ­Yö\w£s‘峄;çˆcì¾ÿÕìä�¨8Ø\D8BÌñnÓÓæÖ'Q£Í^þÄ»xögþ©ÕÆd½ŽQ–‰Íì}Ë!Z÷·quaj×4íÉ1†2ØüDmà���hÍ&L¥b–�¢4C¾:6“”¢ôŽT  b±oxvݶ‹îJ�å§ ÀÆÆ*�ë‰&çì =‰mÒ„J.ë½�Q@{ ÛTöC¥c¦Ø¨ò_ƒ¥\zï/põ?ÍúÙ§m7Xžƒƒ©Ûf¹ë›ïãþ›Œ¼2�0•”� PQì{‰˜§RÑ&Ð xA†2¬ˆ@¹OLiÕqÁ•Æd›J˜|`v ó/^gãÚº…!›�&…õVN?C}j†ÖÜ>LÖF'[¿k$›Af«gcžÐv�A77¸ T UEjM»0}ì.®î£ æ/CÈ9tð �<ôλ½±W“mY /Û•sû¼l»¹ú2‚íkfFnCX�m?ÿüâ<ÿêßý†ƒÆ$Íò¬×í< üY Ûšû]³Úìôõ¼Ó}2Åyï'�÷B­Á¡›ÞÌÜm÷¡Èöµd@¼¹P�­ŠÄ!ϸöøûxñWþ=ݳ§Hû]c–‰jGæ8þ=¯‡]]Òb@£ÙàÐñ[lY£^æ`r­¼œ:utS&—‚T,Ôcë=fæ­”6¤Õ1*aôißö„z³Áøþ Ö^Z…•@½Ö¤æÄT¢kû�ÑÜ}Ù6c„Mç6ËÐl­níýÛ´Bl¬pþý¿Ê…›Iÿ°p…¨¬gA NfîÚÍcòµÜúUw0¶ ñRÞ›œœÜ^k™«Ê©BhæÍRlÉsEDæ –£½Îɪ{^åÇ€ò~WÇ%�„é[§iž`õì a-§È Ö»kH€¤Ò9ÿYh9Ã}?üLy‘Y|»wå,ço2¾vª§ò¬Ø”ÌgS«�Y6‡GfË{8šÇI;ë\ýÄ;¹öžŸ§X¸D·³Î`8@Q»7{_w�Gâ5ÿ‚[™œ˜,s,ŽœÌ¾SCØ÷U yUa05ÐÉ+fžŠ˜DDæÝX •åäÕ>¹ä¤¤dRåsˆ’˜ƒ¯?ÌcóuL<6cç@ Ÿö,䨫7XýÈo0ÿé÷ ƒT`ËhKçчô*<¦›î¥@å%ºÑÎÈ"øÈ›z·$MZ·=H¿×1VÞüâ½AŸQÛd¨Vlßv¹JØ¿ŒlÿËè¢�ÖÂ[M·™êÍˈŒæ�.^¾Ä/ýÜÏ Þ[ac}}cØé¾SUßÎï‰íx4;–ðÕ�Ür"ò>΢GÞðULì=H3w•–Õ:©j=Ê sí/üÂ?gpî�¥yTÕ@&Ú7α?óU̾á$Ï�¯ÍS)»öíåðñ#xñä’Ù§¢hØ_ŠMªÚv yIEg¶Ip(D0Pq8[ïpD‚ãÊ'u««)_€Il öÌ®|â"�¨A³ÞÀ û$Ó{ìé<²I¥YÉ׌Ü5Ú(o€3òL\Ñu·¶0®’ãÃW>ô›Üxï/â.aJËyf@"eÏkðÀM<ñÚ“Ôj5(-¶„ U»/øÍÌ<{ŸhR1ø ¬Å€¸º/v¿7!�ÃÙ}F0ðªØi¸êߥpñ\ÌämÓô— ®õp…c0ìÓÙX7¡|þ²õŒ‰fMÒ¿çd›y×ËVŠ[Ì�4‘ T’<ÆÄsQdd–¼·Æ•þˆc¸¾Ê£¯~ s³³£á¨—¯…a´ne�íûg�ÈËü¶d€í�G·¿}aèV€\]Yáßÿ·ÿ̹³gŒÖžçyÖYY;£è_Îó{d;ýhv,8çüêÊr¸i„±&iš‘àÝÖBlï·j�QMžXøçôoý$Ï?ƒŽMâ~ºÎñ¿ðuì~õݦ¼»qþ,2Hñ5arj’šÔªÂ@5h U’º Z(¹&+“» ÉÅá’8BH}†š·à@mi¹Uʧv%0¬&S§ĶÙU}3âÖ/¹�Õ'¹òþ Hzë\¹ íã÷PÛs1°Å&±þ¥34ö¦Ÿ“d‰BÆ`Õ”¯iOÏA”l 1¢“–\þØ;¹üîŸ#»r” j…—µvƒÛ¿ønýc¯fâø”=™äÏá©Z=K^Iò” Z$b[öô‰%¶œ×æ™W_…ÐDÅÎk„'ª æÙDDv^ó<}DýÖÉÕx¶öWÞ}?,éÒ.&é-±þ¡_d˜fìyÍ—áÛSäJ¥p-#-¤ª/ÚZ[FÄ9a+`9/¸8²m�‰YâÉY{@ˆÇå+—¸ãäIÌô·ïvYYØFÚe”U6ÊcÄ ©Ààå6_^/M·Y?rq¡b™½íÝïàSý0>ЬçÌÒÂÂÚ`ÐkÇO8~Ol't¶câ}¸iƒ~¯@ÌÝŽ­ÑXyÕÕ1+@1«$Spég~é_°òø»í©²ÛÙ��{¾åµÌ®ÏßàÝï~ãã“ÜÔA3VÊöÝ+UGñ :ª¼üòù“ÑóŽR¨·÷hF×mßæ`T8óêµk|ø#ÖµNGÄy¼�Ãâ�+W²áðç|³q†ß¶4;öÉ,M¿Ã{¯añš¬\>k²"#í2ª6Æ›{¾GÀâ™g9ûÖŸ$[¸@oeU…XHö޳ç«$žj‚æ9�…3øÉ)Ôú¢ÔZ5 r ­’Øô×û<ûÄÓœ~îzƒ.yš£(�æJÕ81R$\âÐL)|N:Ð…h,¢¯}V{«ÌoÜàìåÓì?p�C‡�0=3…Šâ´LnkD.¹½u¸Äsä-G9ó+Ï[?T).sí}¿ÌƵs°x‘A:dèûdƒаxá4áÚi"ïH—®YŸ”Úôn„ÀÒégÈΟ²ïøÙÎ:·~É·qù&ìÿì'‘<#+ œ³¼ošã�?ñ»îÁ<º²¨¨À¤ Ä$¶OwØ!¤¬ÝX#ëe¬,/Ó•8(¢ÂÎ+F�ˆˆ‡±‰“&¾F«Ý&4”È{c Zãm¼÷$õ„&-¤¬7 (�Ì€„ ˜<|˜ºu†[¾þiwȵw_BS¥Ã`£K¸ü"½A•]¯ú2ËÝT¬c*«HUS´­šqUèv4æ¡l`'L8N}f7½�U2ã¥3g˜œ˜àýüï|ç;MaáñÏ<Î_øs�z½Œx*÷Z¨<dä¢ lR«Àe„�6R¹ËH,uPFAÅ�ªOWÌ–W–ùÏ?ù_xú™S¤é�±± Š<ßÈÓô)ùe@ù½¶2Àމj„ê×ø(u�ýÇÙs÷+qN¶4» £}žÀD)_øõÿÄò©�Ó]¸jí ñ‚o²ï&�yöu·û&ŽˆÞÊ 6®Éég_ »a´hÔa×ͳŒÄcš]"Òt¸È›—ò‘¢Bèwú¬-®°º°BäðŒ¢¨bb),'#¾îI7R>{ƒ<+¬ø´sþyâá:QÙ Á“w=Fkn/ço2Æë×,܆xjGî`ú–Û)Ò!gÞñ3°t‘$r¤ —Ù¸z½q–•—éw»2½ǧyõŸ|=»OîÁKP%àË"Tó$Œ1Ìt‡œ?}–s/žáò‹™¿rƒ…k7ØX]'Ó”l�Û¤?ì¦ÂAÞ·ûÐëuYÛXåÚÅ«,-,²¾´Æâ�/,pýê5Ò^Jƒ5—Ø= ˆ‰*R�ÃáÄÙ2P hµ®9×¢¾·Ig¾Cq#'ò‘…Q;ëHڳĽŒÍÐ˜Ý Îc&›¤gds.~ûž7²�òB¡TõIµf‹�˧ÉûЍÎÚ N¿ôŸþä'ìÁÚ$¤CÞt³�y½V`ûZÙNY¹ZnßâyÛbÌíû]3²n[Ú²l½Fµßæ>3?ÓO>AžçE1!ƒõå•Ç�¸¿<ËïÛšs"ëÀ—ù(ÚE‘ïv;œxó×��í-âÝæDåÆM€9s“ÊÜ»zÖŠ4HZ f^u‡¿å�Ä-‚ä�.¯±úÒ)¦æ™:v„ýG�—R1W/_ãCïz?óW¯[çHjb¡°�`ó7àÍÀ%ζ@QpjçD@ƒBf!5D b€B ×í±²¾dO~ͱ¦5}‹œGQ¬ �O%D1ϾëýÅ®%VÓAŸ4PP¥V¯) {ã2�Ͼ‡Ùñ6µZb€²²¸Dsßn<ói.¾ã§ðÅÀ@XŠ@÷úy oŸ)Žbš­³GvóП|”C¯8l×Rf‡àŒY×öY¸²ÀÙg_âÅçžçÚ•«¬/¬Ñ[ï1̆¨*F�ð…¢4 äÝ ¬»brÊΣAɇ™y•ý^ŸîÆ+W—YZ[`aa�^Ú§UoQ�ë•JCUÔ)ZѨ#bË{¾ ž‰©M×Y:¿ÈÚå²aJýn‡|c‘¬³A{ߢ‰9‚ʶEñ2¢W(Õû ­DJ©Ž�*åGçÊYÒÕEÒá€3Ï?Í|¿`#Íɳcõ:>ð�¾âQüf·ªŒmzÎlÏ«Vl¿M·É|¹c6«‹Žnñˆ Ï ~ýíoÕ÷¾ÿ½š¥2ôº�pÓ£=;èö~®Ùlþ"v€fÇ~_˜÷Q@õN�û¢8N²~—Ý¾Ž‰]ûð›~c°…d}ÜŸùÙfݳa[_óøƒ“Üò£_Æä‰C¨€'B5°ð§é/]¯8qÜrç1&öLXHéâ‹ùø>ÂÚ E–›w‚ÇÀÊë΀ó–›�\єРhŠ­#°YÙÀClRh»1öïÞÏÝ·ÜÍ+j¯æNÿ0'¢ãìòs,°HG;8„öd›îú�…O_Ep$QL’Ô­À³G–ÊWnÐ;ÿ,ãµ)÷¿æa¯Þ`íÒY^zï¯Ñ{æ#L6bšÍ¦Iï7âÊM0/’%´§Ç8ùwsòkoÇעЦö–––xáÙç¹tî<‹+ ¬o¬ Lhõcú9Ìs(†9+x1ÀÕA0õeç=^ýIÞz³0óú¥K’Ôi5Û„B¯_»|é7ýûÀ€ßG¶4;¦‚Ô¾0©×Z!ËD›czèõ•ô{Эýíc—>þ.¼ç—HWðΡ¢DsmvÃcìú‚»pqŒR RÀòK§®ÏÛûZ³Î‰;oc|r‚×nð‰„ÎÆê °¡�HMp5‡xËÅ@¡TÑ„\±á‰¨¤“íµ‚KÄäð}pÂîÝ{xí£¯æàá6 {S2Kƒ6cÒfÎϱÊ*]íàœ·ªòóo}±¬!Š ´öܲŸÿ«’^:äÌ©gºšñ}öGù¢¯úbV»N?ù´±ÔêIÌÌÌ4¯xó0ô¹vþ^œÕĘæ[­ÆÞ/>Ƚ?ø õV�ߢõ–KÆÚ`•³ÏŸáô³/rýÊ56ºk¶�T+mfI„ÈÇ%e[>à1@Aš‚Ý…8‰q-E‚@�M:Hàgß3Ës²4# ëë«l,lXSºV³…ÁMåÍDÐX‡NMÌ+khA,,=¹@èæÅQÙ�¢»†k´˜8|+.ŠQªðle:¢ê*vòVÆZT5•£ÜG¨OL[Û‡t}…™»_É®»ñìÛw�c3m^ùÊWZ �Ñ0Uµ¬ä�ÐßAGå‘1ºqÔSÚþý¶!½�Ÿú\xçûÞÍ…Ó§EC!µZ�B–´[gæÏ_ø;À9~¿ÙÐì˜sþ2ªoçö;'Ñ ×eß#o"jMlV׭ƒÎ:Ïÿæ³>õš§ˆ¢±íGOpô»¾�drŒ¢,ú RÐÛX`õÅS„a‡È{›ðn9yŒ~Àg>þ)ëì¨�ºÃþ©Bq Îa”Ýa�MPœxœ�Ø0(Cg°EõÈŽÕ,àÄY¾à‘Ç^Áî}»�B� úYŸ´Öc\&ˆiàLÉ WŠ äeÑãùO�'[ZèKxí+øòoøJî}è^æîãØ½'ùŽúnîyð^ …�¼ó$söÒéuŸãu_ú&öìÛÏóO<Åp½g ³Þ Gš§DS ¯ùsobüðD©óæ X^‚%z_|ú9._¼ÄòÊ¢QÂ#版!q£ìÓc‰ Œ’Œ€Ç™wTl2ï�2,#9ä.G�"CðyDˆŠZ>'öÔ”"/¬ö­ °9Ùb¼9¾¥ß`ËÍì½<)¨M×è¯öY{q•l�YÈãÍ¥Có4Z{Ùg‹*¥ð*gS™2J·¯FÎ WP°flãû�°ï‘72}ÛÔ÷¦sáy¼sL„>?ø€…M·ÔÔ‘÷Ûy.Õöíò2£@ô;oR#/ßTMUyáâùâ¦ÄŒ{ñégdØï“eÝn·X[[ûäåÓgþãäììÛÝšûýh™çTõ±F³ÙvDƦ™>~·ñò�ý³I½üúç>¹·ý”µaT”xÿ$‡¿÷ÍLÜq˜à réãˆè®_aãôK„~Ÿ�c<íÙ»�§>ñ×/^±Bé�XNA ,—@ñ MAû,Q.Fx¼M¾Zz5‚=Ý×"ë��}6 (_ð–·pøÈ!{ª– 6|ÍaŒ3ïg ëW#cìö{˜×kÄ�„l-gõÔ"E,yœe·ßw'äÄÉÜ}ïÝÌîš©H QqôÄ-¼öͯã _òÔ’„wÿÊoòügŸbyuÙÈq×cN~Û=ÜöE·“Gù]2Á±¸°ÀKO¿ÀÒ�%ݾåT¢àËž6æåEŸxìuÃûÄ€\� ¹q�mê ǨÎaPàZT!ëAÔŠÍ ¢Äà"vË鈚ªþf8dz|šÙ±YjB¨¸òOË?üXD{o›ôÚ�h=ÂKd` 0Ù¬1æÔv¢>>eשÄZÜHO/·hFÒ&Y€˜)h‚�N­V·ÐÛç>ýO=þ««+¦ûÖô-/µëÕû¹ëî5/(5ÜbP¸~ýOŸz’+W.±º¼b‰ú�Í€ý…@Ú«l0°§Zò^Nžå¶ ÁxôAr1)\�DàbAпЀ¯y‚ öÙB·Á¼çy?³ö âźýa^÷Àë¸cî$�ùÇøÂÚ[xCòFn�N¤*-…:s"‰˜::þ¯õ^®}ìíÆ:Óˆ ¦ßrG¿ù�¨•€#ª’Å‚°~áéÆuÄ�4ÐÛèÒP%³Q ,ÄÛ00‘Z™äÏ)·;P·‰ŽZªhI�6‹�Ím…‡ÊžÙ½ÌLLcOè"ö$oa6 ¥Ao؃¨`Zö žŸd%,Ó¨5íº½׉ò„z½aă¯zÄhÀv½DÌ«zßo¾›�½íý8õvÜúÆ*µñ:G¿ì6Ž}Å \ÝU‰ôN_x‰§Ÿ{’¥Å+T•¼¤)РUã6œDð±·açÈÁ‰Ã<ÂBˆ\Tê·`çrj´gyÍÔ^šSts,‹-à àþH}ø1î¾ëËoÈ&IêS`Ž=8œe¸æÜ,‹,Y±¬€]3H Ä>rôOwÉ—2 A6’¦ÉùKÚ#÷5&�ÞA­^#°¥­s5g [kjFÓ(¶�‘"cç„ñt�‡gbn?ySS3#E™/#rYí°}¾Õ—ï-3j¿óþFÈ8}á|ö~ê?_¸zéÒdwm-ô{VhzèØ±´Èï¤k�Ÿ®²c€Bg;–Æqò�%ð+y–=ß®Çŵ·ýW^x×/� ô×W¹ñäÇHWç)J)˜úÔ8»^s…ÏÍ{‰41¯!“>ŠâI�¡=Œ†\¬å¤k£ßŠÐa€L \\Ã!j“š*šPMrê«ÞÌ6,Œ6T(M�BQÛ“ŒÕlâU”ÚX�h,6™{“ªixB1ÈÉ‹Â<ªÅuV¸Æ€+Ų…³hímSŒCRp°±ºÆÚò*ÀˆžpµÊ¼™3ÏŸ¶'úzRÉ069ξ×âöo½“Ú¸i¬Ù+7V8{ê%:ó&¡C´lŸàqxo2;ƒ�Ifiÿò;›ÔQðuËÝàÕÛ½O$Æ•ì=z˜ç$àÔvß3™âÛI(œ¸õ6n»û8»àÔŽÑP”+\âEž¦Ç:=:Œ1Æ—F_±è˜m!”‚¦c·Opà-G¨O5@”þ°ËÆÆ:±fpá6žû$Z¤UÎEJm3_9�T¡´¡µk b¢E9 ÚǶ!Õ¯9²‡»î¸ƒ]»vc¦¼ŒÉ`TŒ*S…µÑpYeÛ¶ �í?‹bu2�?õdú·þÎ߸|ê㟜믭7ê�µZõ.}î¹g>uêÓŸú‡À ;öУٱ~šŸè®¯¿jlzz×X«Ñ\9÷…�MFæò•|ñ ø¤FtrŽCßõ:|#AÄ!N#©”†Ó�U:Ëp’[˜&ïå%MYEœTì±�B¯ dHéÙ4í¼P€¢€gÇHy¼€ ¨¤ô*Jj�:»çvQó Æn‹íhœI?G/Û@0b€å˜&dš%è„®%À�8žÛÓl\Ù@c ÝñÀÝì?|`Ûš‰P<þ‘Oñ¾ßx+넲%s{÷·|ý v?¼ÏX^1OæÚÊ5N}æI––p¡ …eõj ‚±àJoO%ñ ¨CTìžZGÒV‚¯Gc¡³|˜cd ;E™¨ÏÕÎ|™×òÉ©¼@Û{ ™½ñ�¯7v–¢àÀØ…‘·ýB°PC†ìaMš¥Úv�7ÍE½HŸž]7#Gb!v1�çÖè.™´�y}N„ñV�Aw@óÐmÄ­q|5ÿne”€ŒD¤œŒ–´”žÏ¦Ú/ç¼]çxm@«Q7Àiz6ö|y$R¶/È„ís0:ÂjÓ—utL`õCŸøXöïÿÿ�_žŸß=66ÞvÞk¯Þjm ‹ü}W_:ý§€SìØT­³‹ÆÚ7ò�Î?ºvéRmznîäìî=û»O¼'šÜÁÚ «¸öÎÓk1þÐ1üx‚à(Ȉ$Áojœ÷úÄZ'Ôƒk ZÔÉËzr5€©dekªnEŸ¨S¶¯ÔJƒ‘� xĶa*F…æü™sÙ{˜ñ»Æq±+uÂ6¸µö yœs½‘€r)¿ÀX2Î,s¬ê®éÛ5A4A€a¿Ïõk׌Ùe“Öȵ¶ºÎųè¬l°¼ºD£Þ`zj†‰“Óì¿ï Ûmm˜{k=žþØS,,Ý@ƒå˜ìœ¾qðø :KkÌ_¾d^†%ôËÂÔb2ÀU‘ªx1p¡À" #J,„f^RÀ@Ý<ÈH!ÄÎÂgÆZ‹…¢SX(�÷Üã%“ Ë%…¬ p T$† 8ÏYîäHé[í„?ÎÇÂÇ*Sé<6Aûþ _\`½»&Zk´Ð�?÷{ÆçHêµÍÚz¶Ôm"”A·²!GÃ'( Ð‰Úø¤a¬<ôe™íýËx"#­›·ßqŒ^Nùy+Jö†}ÞóÑ ÞõŽwô�îBm£³ÎM°¡16¶töÅ~½»ºö7þ°ô—ÙñhvìB>|`ce¥–´ZñÜD½¹~ét}cþªdÃÔ&Ù|,æÐ½™ú� ªz 1/ÆÞrT ²Ða¸ÌêÂ2‘�M:&�ré1¼ö0+�`´á3Ï�æÊå‹©ˆ4-î{ô1 ý�?û<â-w‚!ödBØ( �¢ZdN2ÈJæ�ånêήUUî3,/– ÅÄL3«“)k”°ûKÇ��`j÷dÕX­r%;‡‹V[ ÝÀj�Æ™$”õ@/†ìzäQAˆ”�S«¸Ž Y0‰«­rb�‰[NÒ˜˜ já9*&ò›¥�ªíf®\¯# ¶Ø;î©÷iرۄ±¶ÇŒß¹ìÌö¶}2I¶oÙ|ùêe~ùm¿9øÔÇ>ž^9n,ËÓ8„€s®ð�Æå´È?¹páâ_ΰcˆr4;v‘¿uñ¹çþæ'Þñλ|ýêÙ"/€ÈѼm?ã·ìÇi\…ÊBÙƒ>—…¢(QRGë`‰—R³LË:ŽÜžÔ‰€B«Â4 ”3 „Na“§±¥4àÞ@ÅOyB*5FíÛþæm J<6‚°8¿ÈúÂ:E?+¯¡\Ï/‰cOã (ô–{|ü3ãô�—ŠÕ‰\0bBسÉ]ÄÑív-ŽŽ™VcØpåüeÖ–VÙØØ°N�3“3Œß>Åþפ¨åddDÄ,œ›ç‹çŒ&n!¿Hðµ˜[¼—vc‚§?ö¸)0§ý!Æ’ Ž´›a¢–Γ§¹æ&9£Y0Z²=�£ÈìÈÁꓲ “Š´ �Á)v¿Š²S ¾«ÅÞ£{ÐB©¾Ú¦ð¦o8‚y‘€@FÎEÎ2¤W¶1È©K�¦4ñ¸j0ÏræÖ·µICŠ:Œ$A¡�uÖÎ?Ë`þŽ¥yJÑLÀT]MÅÞW¹-JÏö~ÏKù8A`ºòˆÎ™l£A¶½nÙö …lß³ºÎVgçʵ«üì¯üRö‘÷½¿8ýÂsã�î† ú d½N÷sçO=ý_Î|ö‰^dÇþ†Îv¬¼]aµ»ºþh­Q?ÅqÚ»¿ôAjcm ”€×Z•WB%Äè´F¶ÖEkj‰YÝ,»«@DxèÀ´·P öZ5HhlÀPM¸šG71Ötˆmסš\¾“óŒê$ƒ3/½ÈøØ·Ýs+-i"ÞYñãPû”¼0xšgž}ÖúàD¾Æþ¹XÈI {=ØèØõt›I«ßësáì냂ÕÍÐjµØ}bÍC-pæÅ±¼°Ì…—Ι§bJÒˆ(Ç]‡öòÉ·~€~§cOþÎD‚làCd¹Kì÷ÌT5ÏÆ%ÞÂg±ÄÀ+D�g¹íƒÇLã@Šz5ÏGj @£ÌÉP>¸ÒcôÞ¾;P(.Bn¬2Â\ç ‡ðDL3Ë^öqžs¤ héy;ãÈ�²þñºW7Ð4˜2„ë®S—ˆùçŸdâä+ðõ&ª›A¤ªËݪØ,6JϧZ_9™‚”û=Ÿ7¹'¬Ñ”M-b«…TëFmûæd£a4}If‘mZ @O>sJú.{éÙgCÐÐTUjI=¬¯¯-­uWŸÓ<ÿo.ò?tÙ±?Ì@³cÞù©¤QŸˆâ8²Dp»Îø­û NñS�á𛚉9¼FDZƒa@{¹yxÛˆnúñºš«Bãñxl‰ppè°,Ôt€„²Ñ° ÚŽmp!ª+ÊÅ®l)ìÁ‰`^8õüS\ºxÙšaµÆšÌÌÎðäØÜ:wKWÖ9sú4ù 0qÌaoHcÜ’á4'›´'ÆØtX[_F +rÜ⬫*�µ Î�>C£Kä#‘‚‚ñ£Vô)8Kö^|éœ5 àðÑ;¸ëÞû¹~á*K7®!";|™lL±^ Ð.…˜Ç…‚Y"u 8yóî #ò:‚(¸ àÄ@F†½ŸðÌ=¶›ëŸ¹ÂêKV�…#£°z�õÓOÑÜwuÂÖ–™�ÍÖÂÌB)ÃeC­²º§J/]Ó„÷çx¬¶Â\”UTËê ²UP“í 2’ôßü^¶ofB° þØGøÜSOréòå�§C×j´\·×!KÓþ¥óç^ÈÃëœÿ™¨Õê|¾h—íÍN!§EqÀ97éœ3P™xÅq´…åb„2ù/T⊑­†u6®Ÿ&¤hÃÀ•ûÎAV” ç@‘Duj#m‡M¬uP‚ ¥šxÊúE¬>&Tªhj`„ŠÕÚàÕY¨Â$Y²ÂŠûÚ#]œ§»Ñaï¡ý´§'Y]ZÄ =K« ì+öÑp ûLÃt`Àd^J?³cP(ÍÞ¿øôó¬,-ÛuÒ<µ$ýÌ=»;2Fá ë‰sýÚ:Y—P/„ZÜàî{¡1ÖâÚùË\_9G4˜ºÂ8úÈÀ™'Ó°ðÖa48²´0¡R_ó¤ý SÓN0pñ¡jß×¼)ÌsÁ<¿ÄÞЋîež"ðܳÏqË¡ÃL¶¿‚�PP Ñ™DMžW…²ê”””yn0Å4kºnàSö¬°×Fy®;vÝ¿‡™»çÌ÷É72CQè¯.°òâ瘺ÿu$s•“0,¨ðÀW53) b¨¥jï+Šs^Qì°åÓYƒ{âuf5E~[ Ý&ù?6Û$\¶mÛ¬XÓ¹O}æÓ¼ý·ÞÊõùë )Yžy#¥ aeyy~Ðí>‘ ‡+ª×>þyÕOfhvLCh­ "G2ÑB] *êAÔägâ°©k!:ÌÉ:ðÁúóD4ØdŒ‚…€y+6,ûkà€8¬¶ížJ¹ñ† TR*eá¦Mž2,+ìãP†™Uµ ÜÕõúƒ.3³»™ÛËÆü2y'cåÂ2Ñ�±ÉæÄÓ ½Ð'V€Yä…y_›Û÷.\_äì §™ŸŸ' «ú“ñ['iìmàÍ_½ÁêÊ*¦-&—{n½÷>öìßG/ïòÂsO•µ1äJ^¨ƒ�®SÏt)Åãðc�ßô<Ƽ'SºTh¡æå ;…žÕϘ|¿DÎî£+^ 5¯pq~�kW®³wÏnÄ ¾Ì­Q) [WdEœ�-Zäœ×sæ­y|™§ó¤Å�á`H³Þ¢µ·Åô½s˜,Íe˜é/Ïão\&ë¬ÓœšC� ñ6KÒTu5ÕúÊÃl©UîÆ€ªR8—·8õñ•:çÓLنƬ›œ�Pñdª¹%ÙÿÞ÷¾›O|üc\¿q�¤^§ÓÝ M‡Å ?è z½3ÙpøkÀÏϳcŸg@³cu૜soȳ¬nqüfƒæ�YœDrójŒÖ¬AŠŠÞì‰vs+Ø ƒñR&ïÍ€ ötKˆ¢U·Ìª£fÀö‹ZR8òA†¶Ÿ±ýHq 6»CE{ 5p±Ç˜pÖøËJ-úÂÛ5Š„ah@4yd¹*äÃÜ-çÙ×ÚG­Q£Õ1Ï«(l’Èó|‹žæêò*Ëó‹ÖØ+K‡Œ��ãÆ"Ú{Æh6šÌ/ÏséÒÓx³É߉Ռ8q˜T†¬­¬°¼qü¬ ræµðÅYK½²F™–�zjJ ÔÁ·}Y�(H†©oàÔXrb_Xìü¡�à�z àJêu”ó±O|œ/yË[h�·Qˆli1­€O06 L&ÃE®ç×Q”ˆÈÈ}é“™}†t˜’4kLß9KkÏ8ë‹KÄQÍjjQB±|�þõ Lì;Þ##UÿR…ʶª9‡`¯7“*ÏF7±×žÎÜWDLºbS®f„-Û’¶‹í?º±êcßóô¹3<õìÓ¼ã¦ÓY[�¬ÈÑá°?èWV�GQô“qRûL–svìóhv,n·%[[?„ê_qÞI-©ÍÆQ,iÈðí4xD-?cÉ\Kö"(`ÃÞ ê‡À��T�w‡¤z_=j äFeF� QÉd›tÌLÌ173k“èé_²ŠiÕDh$õ 61X¥=¡š°Q ,Ô9äyf½òcZµ±ª¦$uCV:+Ìé.D„±ÆyÓò¦7¥J%Æ•e)×®\ccuƒzRÇg週]Sìºw¹+¸qå:½^Ïäo|Í[ýÎ{bbl’L3®Ÿ¿RÒ¤J-(Á)¨Øú(òXýŠP'¦`mÞEÑ+ªÞþD�¨â}Dä�ót²~ZU6š75bÄ+DbŠ+ýE>ò±�ó†7¿Îäýª¦göjïÍ‹Te-¬ð±Ógêð4^©¤•îYšéw{¤!g2jÜ‹ÎL ~™�õu¬‚?"�5ÖÎ>Ãì=¯"¶Ð¤yU/SµÈÃVoÁ,”£RxV{]AAϯvóÍþ:M`4ÏÂöªÎ•éHÕ(â ‡C.]¹ÄâÊ2gÏžåÃüׯ]e}m•8©‘ÜEÈ3§n¡³¼úIï¢Ï:çÞç¼û4�±cŸO@³c¢¸› óÀßçŽ$IgYêò,Ã5bülj (–ÿ 7°�@.CÄþœy8iwÝ&-‰ i0j2&7“Ì–ŒÆ�ö6ɢ谨XinL˜›ÜÍ+|„v£eù� /¼ð¼�—f–À&x±ÉìyÀr AQU”M�ÂKÚ+J½Ö@Ĥl 6×ðÁá‰Ê®›ÎBè´¥ —yDÁ¾Kl$ \œ ¾ Œ¾ ^0OP¡(¨`ë¤x±õ X»€â¯�ãW~}•·|Á›™š˜”@Py5Š"Uœëk7¸¼|™ñC€))G=¯³ºª,oôègžÅz×h<|„åOœc˜ Á”b|È\¿À`c�íªœ‹º«Øg6d¤@S7‡Ò†™ˆƒ´ØZÄy#Ä\Ík¡oç¬l4„&#ž‹-)CwÁT"6:~ý7�gN=eÿ¾ƒá�µõ5ë Úl¶a}}­èv:é`p)„â§£(úïÍfû:P°cŸ‡@³Cc®k–}ðO�sã"b½/4ê�8GãÄn|3!—ªÄ�àMˆÒBiö>F Aû6ÁK)âØr€À†‚YUq^ÊýSL�Õ�€ÕîÝ»™š�0âÀX}Œ£÷åüõóÖ›§Hbj€…"P)<µ†h¹æ¨¬î$Ë » P›ªÛÓç íáú=B¾g׽ºgÎì™A6ÕDôº]Ö–V,oÓt�Êì#ÇØ‘ ¤!\¿~ÝèÒ¢Vo×mÏ�Óš´°« K¬­.›ºtšÚ~H]À›bb !-j �Á°bûS2Ã|ÝãTp’`3KÜcj vŒ« …É÷þ””«(Î.ò•p§•õÕeÞõáwóèƒ�275K\Êäˆ(¤EJšfœzîižé9ÆgÆYÛX3 ›@ !�B¬¦h�aOÿ–µˆ±ï§¾2  -W&ëË4zÖ÷_¦vmažå›åÿmýÖzÊ,P0 å> ¾|-˜Ùë ¦Ðšp 6Å@A·—†É³Ìê‘z½žy-—¯^e~ažs/½ÄÕ«WX\^´Ú*çµz)×Ãt˜/-.ô†½^'Šã·9‘ÑŸxæó`v€fÇ’(Cõ'€ŸpÞ·Q€ìÇãœ#S¥½kŽ8n¢Odà2¬Â*OAn@”†U¨ƒ®‡r¶ÀÕª`ÀlD@PÔa)*[f{$D jIe;¦¦'9pð çΟAZkì•R’ ¬b"¡œL«*zK‚çV„hT“5êV©^‹ô׺¶_:(Û'ÆX³É†"Ìûˆ’gL³"Ͱõ‘g˜ hÖ›4· (ÖVWŒ` }ZÂÌ‘}Hl“;Ë×)ŠÌ(Ñ0±€œ”’<6 H1Ë{’8«;2˜Ë ©¢®€€yrQ½d®9W04—;ð@¤8õ˜ Jpñ¡ Ýa¤2Xï®ñ®¾ƒDj?|+dØ9ó�³ÒYæÆÒ5{o“r(è®u™ŸÅc*Ò¬t—Y®‘‚f)ZäDõ&c2ÉqQd÷/‰btÐ# {Š D"TÞMâ@±uf›cœ¯ò4¨Øq @åñ\ט_Îñ]åÞdÝÎ)B)öéXY]a~qž¥¥%.œ?o”ôÕµ5.ž?‡ˆ£?°“!>Â^«2  ß߇i6®„"2Ž“ݨ7?òy]³4;V�j· ú¯€78çj€¸¤ÁÜÁƒ¤kË {=Ôò€@¤u”`-@Q)ðškGFª¤½54-ïP)Es¬6FÓ€�P-‚m“RÎ¥¬JM-ÿ+7VÉ�æ¦�•®§J:qè¸MÔJ�òJQzPªjçWV±kŽzEó¼"mØÇx}‚É©]¬/¯`!3Q¦:M:Ì,ÑŸfC“¸ÏЬœ˜¬PÓ¡06–…‘4kÙ,AØXÙ°º jù™z½É-ÇO bb˜\¹vÁÕ}éÙaJ ¨1çìúR,¹?¤b‘e� A¨·ëd�‚"…#…˜\¹iÊYH-n&h š eae$¾â¬F»Ÿ¾d®8' \Ÿ§Ÿy"°}2ðµS�vÁÀK†Ð[éópÊòƽAŸ´¯Ôâ’¯Žàgk$û',™Ö}5è,^¡sí<“·ÜŽ/óCT’2•@‘«@¦ §y[oÛÍ*ÅgKÄWÄ‚ª+ó‡m®n 8Ò;ϵ—N±²²bñSO~ŽKÏã|dÿö««KæÑ ú}\™çÛn·ñqd÷ob|’›ÛWç¯]½‰RUÏåYú«*òÌç}÷Ë Ù±FT;,"ÿÅGÑcι(„ øz‹c_ÿ£ŒOÍòÜù[XR8E‘² 3H†é�©·¼ @� 6‘y¥ ‰�Qœµî–šSIœHâl‰R2ÏlYnjP.�;Ï™]{¸óáÛvRòÕŒéñ)Ž?Áé³/¢6Ë€D  [i©@ Ìã(Œù8@”f« XÒœf£M ÀEB6ÌìØl#·„õ ß·êúååe>ù¡�sãê ›dæ¯^7ÕæÈEVkJÜŒ©OÖYš_$[O YQªMî]ûI žÈÂ_ƒuË•^ž}o»O®î, æc²þk› ‚¯9ˆÅ4ÎÒAjëð% ,@ææÍR˜ &‚Ý_cá…'Ž+æ1õ€>�€ˆÃ°0p/™�Šu÷tž"¶ÏEÑ7`«¼FUEJ�&HIíÚ>÷¦ Ý_ .6 ¼"ÎÈ]ßòuí[w“~à"n(V+Š�<§wí…*¨”Fr2RåK¶²Šs¿l*�2¯-pEN–Xº|–_ø ù…§Ø¥=†*DÃ.ëôÓŒP LÃNð–GRU< ÓC :iºÝî©|8ü«ý"ÿ8Їchv¬Õî~ÒGÑ=QGâœ�CûØ]œüÚâØ«¿„ëO} gá”axW'*%E PPŒy¦±- rðÔÐóH¬À2.ŧ‚bJË‘e— TÃ&[«úW¤&SMûÁ&ýgž|†£ÇŽXo™Õ^ŸÖ\›Ýss>  �´µãf4¶Íªëm…y4š`^ `^ˆs#§¨@w¡c=ôlßÎâ¿ò›tB0¦v�¬«$cíqêûš4÷µ¸±:o”fç,üg¢”G8Jè®vìž-Ù< � ˆ˜‡�Fjç´š H €jqeá+F+uÁ�+À'V;„ÕË �òž C”EýÂ^Ó)[.@4ŒìµYdü/»¯€Op¡Ìë8ólìÞ;5]®YI)ÌÛ+D iNaT@ yx†ùv¿!eÐï�z„~—ÚD²Eá%Û¤ �–õ1±Û¾QfÐÑ&˜�<X{gT­^§è¬åŽšc¸¾‚l¬q}a™kW.›våI†{È`b|šF£nªÜâ<cã”}d\»=öèÔôô?½éÿT(Š÷¦!òó:³4;ÖŠj�ÿÞûèÎè¦ B\o1uç+yõOüc3{MºÝÕÛHœ‹’£HlžLÕE1Òº=�õTÌOu¦s&™ÁŠ ,óN\ÛäNÀCè–åÛ9U¯ú`yÅÞkZö›‰¡ŸwøÜã§xøàÞžæ—®/[˜H#4 æAT³hi*Z±Ï|+¢53Áج%® `â8!`yKÚyn^Gìbë™DZ%¶£8±$}ì"´:ƒ~�Ì€V™Ù½Û*ö» äj•üœžØÍؤ]AL*?ÏRûÎZ€/5δ”–qâ°P#å=É@c!8EҹѡÍS3`òJ&„BËÓ@ìP Ë9l€…¯Göíß'æ­P”ÍÑ´¼2^åphd`m× ;·yPöá-¯3Èû ³”FâénôoŽ®ÕÉháðEç"”Üd‚´ó4’8¶p¥9ÙÆ*¹åi¦*¶™Ñš¥z_å\¶a&W!¶j=×*à¡=N21Å =Í‹××I— ÒÕ B‘ИØMíÈ~’é=„"#iOÐ_[Äm¬²~éE"Ÿà4g£×%Ï,GI½^�‚†»|-úë’»–~þÞÁ ÷+ ÖٱϠٱ‰æ˜ iúï£%ñ"â‚xö|éwq×—§�LˆbòáK|C¬¢Ó±&fy(¨PHZ�àPÉ!T¹�Ð-ªN˜ZŠV‰¤RB£3«€ h¥vW¡öÞÕ�-_8ý,ãcì?²×N—.^4 ™¢eq£kùêÚ›Õìuª8âñ„Æd×ô¥g¦Ø“x(+E4 šÒ QoÒïõ,|491e±y ù!t:�e B„§7èQÔ _X»€BÊ0”3ÓDQTÕ¡¬v–I³Ô®EŒ©¸šy?„¼ÔCªÄµS²®k‚Ä&8óz £0‹‚‚8ÌóðÁ#…È:)¦»&5Šä äíA€Øî9¹…Ç@}a€"‘yX¶œÝÃÐ/ÅO1í3û®Ã|`àëptû ——á5�8¶ä¹#Fµ°Æy!ÂÂy’c-¦&¦Éû4èå~8Ge=9è¨nåV«ZxO움âj Æ�ÞE2³�¼³JÑ]gØïâcÔgvc‘Øz“(òÎEoƒîÂ5òt`=–®â�\{êcH‘2=1N$‚j�‰±Éº¢G:��ïZ^Yúúîúúûëµú_n6ÛÏ}^v€fÇBšÞá¼ÿ—cS“'‹<÷Yšâê-¦ùBîûæ?FÃ:+b#„œµK/Y¥{Ý9bŸy$¸J¡Y¯5TrD=˜)êÀÅQYá^æ]"ó6ÐT@±÷ØK`*j³ºrßBÑN™¨6ï§€tŽ'Ÿz‚_xÕ`½bìI;+Ey½ž…Ѩ&© ²Ðœu•¬Í6hÅm¬À±ßëXµ}£Ù©gáÅ{êo6›$qBÒ¬3³Ž©½sœ}ñ4Ë—/‰§Õj[hs¼Å ²Ð] 7è@ ¦,Íì�=�ØÀº� L&(´,cµ¯û2i¯¸È™WYhÊ�{È”`L6­dç,oÍ$D�¨‚0Ì-,PÈÌ+Á<¥ÈcDƒ¦7µk+v 9h¤• $Z‚¿Š•í+Z䪹Š ÞX|1ƒáÀ¼1Tø$Q”Á“Ì´iݵ—ôÅe\¦‘BsB–Ú(6Uü‡Í�3+e€M¯)¥¢DWSYÉx'r…‹©Mï¦1³»$6« ”ç*Ìcjff!gÏÃoâðùçY|æS,?ù–Ï °mE`M)¤¨hçæ…RÖ& 0PQW<Œú­dš€ÆàZ�iŽÉõd9!H”xjÚŽ‡òó&Ï6L§-K{àÄXƒ>NPÕ*DFU�¿M]Ìf±eÝÊ4³×ºÕã�­EŸ² Œ¼P©f#à¨hÔÕµU1¯¬Ög™�:Œ·šÄ‘Ѷkh8ÅÉÞõµ•× ú¾a:ø·Q’<ñ‡-‡³4; søçµFã±Z­Ö(òiMrô+¾—»¿áGpq�0Òl*òžÚøÎG*jÄžÞ ó ëÔÛ“ È¡4KýJjÀ§ „õ›°¼TO¿@.”yÐ\mÛ–—“YȨ&51æVéñ¨–á7Àj4”z^®]=Òb ðÜòKØÓ½P¨�7™;¼wsÑ…�D4¡…õð'[ÉXž_¶äoÓ»g˜™�¦VK¡^«søÐA.ïÙËúú*«+ÔÆë´�[Ìäû >W³\NF†¨Ð鮣%ÛL¬ƒhál¢6 (ÁD2Œé•Œ%¨óPP&i 8Y0ÐŒÚ1Šˆ3pwAJU�€Ä˜GJ ˆIíþØ}AF¥6Ò·#Lg­T •\ÓÛþÞ9 €P|3²üŒˆ�–)túz�®­Ç{REÔ£R6Ç“:­Ý3,Ëû˜·‡�)¬p5à*6Ù…¹t‚E6Ê&ugت€T9› °ÂˆØ²«ÎUJuU‚êZ¶¿±ÿ !™eÿ«¾„½½�åóÏsù“ïfåc¿Eè­Òíup"®Ùl¶êõúÁ…Åß$"_²üãy‘¿]D~U’dõ¶bóÐì€L\o£úÏj�Æjõz+¨Jp1G¿òû¹çë~I*�Ù¢¼!(µj¾‘#¢ÔŽ(Kˆ´f€2� Œ¦‹âJŠs #—>.‰�ÄËÌ€À€Aªi[f¥WwØ~9ˆ!ÀÂ;”Ì+c” eÆ®…;ÖÔ¢slöh\†åž‰ÉivÏíÃ<rúÝ.Ë 7ȳܒþ͉&Ÿ¿ÈòåZÍ1Û¯57Éáã·TÐZoÔ�…Ôj¶ÌëX[_¡Þn�´†ÃÔÁZ&ãâR‡ „œÌhо]Jÿ·Åö±Þ3N)\a¯jUþæi ²�‘ p‚ƒ©äõÜö‰âˆ Š07»‡»î¼“ù«ó<ýüÓTÄØ{Q+��‚(zù¦&gªäÃ-‚­sNv<(±£PH%à¤"‰PQË=¥YÁrø±:â"J°qQŒéçÍŒÓO _Ök �ö¾V§tL‰�-«I?×M}iF„”7‡Ì òVUü·}G[GW `¤ £©n._]c3ȉu�=ùÓ'îcý�_ÏÓ?ó�é}ümø¬g„ˆV³íšÛ Óa}}}íKe8|MQä߆Ã_‘_˜˜š¹¾ÞÝìØ$ Ù±‰ZÓkQü óþuÞûvžeâjMŽ~ÛŸâö/ÿHؼoÔÑU\ ›¹Z�,š¢?dx~�d÷"A)…³°?)p$DÚ ÙÚËjá*æ˜:Ó%SˆÌ»¨ÀU{¡ÀÀE1°2ð € ÅšRlä¨RŽÔò2oQ¶hT°$µ¥¤XcÅ‘­iÆÛS‚C¸tý½�uÄ >ެÖeéùEšQƒZ­nâ™{÷îµÉ½4±l˜ù˜Z\cfj‡c°Ü§˜ ˆÞŠ"iOO™·–“#)Cz�8¬c&¥·á xÂ0@šA,8¢zlL4³¡š÷¢,bÞSn´5Åw¾ƒ™é¦Ç§ xáôó¦÷V€±þÀjp ㉺!øzBpÁÎ% MÕ@ÆþjFM·k* µ@ÞË‘T0ÐO=Î[Ï«.ö‘º•2O•`^¯ï#*D¡Mmn ÁXzE/³‚ØñF›¸Þ¨Š/ P õºE±íy•£)Gµ½7"sરU_›a�]ÓËV@C /Êã*@«‹KáÇz :™¢ƒ2 föÚÅRñU%2í²Ò³ÑRQ@):¹1²@‰Áµœ…t¤æJÞk™°.+î}ìË^÷<Ô|ƒ¹ý{±‚E0aÇËgÏÛvñeµýõ”—~ói;O¯ß#8|Ë!q™}~kÛ²Œ"·§Œ\�'ªGU é¨Û÷kNP÷ ´§lÒ¤–ÔÁ—ÊÖ]Ū÷QâFlàcÔb°¥+QAˆ0ö¹‹¢°—3æ],uV¸¶t�•åef¦Mm›¸amSyÎ6Rtȹ SOˆƒ� LZ‚½F ÀÀR# 04€Ý[ñDI„7rv?[õ6͸ÉÚ�5‚ÅšD!sDÍ:*(ˆÆ¶,z=(•@k| ì^Z�§Vx•kÑÒËH\Y„67ÀÜú:lr˜C9`‹úsuŒ«T6{/Õñ£Ž· d“´©­tZ€µ$aÿ}¯â¡ïþs¼úïü{¿ú‡ÈZÓÌ/Ü ÓéE±´ZídjjzjbbòÁBÃ_êz¿6ýê�EÊÁ¸^vì÷3ÐìX¦‡€¿œÔë»|y|ÄÔ£_Ì]_ÿcÔ›Í*ž-`#ö`šŠ�moLÏ‘L΂ó6¡…n—þŠ…OðšÀdÒ³'U¥@ŵÆ�œDsÙòK”¦Ãh´æé›0Q*à±¼‹Xq#:,©¼9Ð K…±Ï4”“aì0"AM pP�D x¤QÒu# +mŠÕ¿ì¿åßvœ‚‚`–Ë,^¿BÁÎ555Ã`¡O±V0Z£,¦æf9yò‰óH^ y Õ¨ÛêFw�N¯cyœÁ°GÚOÍû°¦a.X¾G æÅädvÍL2­6ˆ�Ina£�—p}¬9P`ç*´ÉAS­ÚÀ¶œ‘"dˆ­áôsDÁÓÙè‹·üNÞÏÉÒ kŽ–kñ 5Ë[¡ž2´(XMQˆK/KðÞ Rí}Ȭ0×@1d¡¢GkZvïTg×êô6�†$”ôÄ�6¢ž(´,�g÷¡? HÁ‘eCœ‹I×—Èû]PÌÒ‚ªâ_„ª�³ª�ƒ 6ç^„ÑDµ]G :m¿ÍcSxÌAµO$T¿ Õ¾Hù ”lTµ>.®3}ì.üî?p~ŽÛ¾óÏRŸÙká´õ�uÖÖV ‰OâuòH¡áo†¢x[œë?ª;ÿh»ÖˆØ±ßo@³c3c“øZŇkõzduã³ÜñÕßÏØÜÞ-UÓA©~´!l}òÑžÝCÔhÛäÒ!«Ï\0º,+�0³çy‡#flßA|«N©Ðl£Êu‚k(Tù ZÖÞ´¯0™ eka"P{�™M¼«!5 £ª¡±Ú †ÁÎ颈'o±°\†Õ°0ñ*««øFD2VcnfŽõϬЌ›–Ÿi6Z;y‚ã·ß”�hȳ‚ØÇxñ¤ƒ¡Iǧ�«ÇÉ‚]† l]�:`rò‘�‘€åaö™-Ì…õŠ‘ ²²Oÿ˜`L¯ºB(&1“w3B§¨DÝ>.XªC ;WÃP¼³µb|æ ]äJ™‡Š}ìœBM ÀJò‚”µKÖæÀ`^uè:ô²®… MàtàH&'q.®ŠŠˆ¦šäý”   Q›˜±BaÙü ä¨&s©Š8+�¤êW£L6ª›Çm6‘À&ÆT½mlÈfàR›ÎëÅÆÏF6çtUHmrÿQîúÆã�¿øŸÙûµ?F}fQ\CZ�–LŒO$»wïÛ·ÿЭQO–ç?Ÿ¥é?iøøKqcìØï Ù±¬ß?!"ßÞh5ÚÞ{Ñ"ç¶›ÿ¹÷ßõ‘ò@†ØÌ .J°Ñr"?pWkXGHcb-u^[3€ 8�ðZ³p¥ b-yÇ÷ŸDœØ$'q`·uó\(,¤†+Û:#PuV Ìp0–'@I“¶Ql„õÂΡ‚M˜"©sVa ‰ÝÓ4w·ˆIl¬Í/óü³O�G®!´}ÛÔ—çOY³2ûÎQ-æ¾WÜÇÔôPVÍ«’R›A™Ÿbzr–Éæ$¢‚4œ�Xc\ê®u-åñ««‰F³®Y‹f ÊÂÍ`±#pGè(Ô©›×fÝGí|CV{ƒ�3ëË Öú¬-®263f÷ÑFÓÙ½‰Û±…ü 0¶ E¶>Ó ûòIÃÖ²n~ZSÛw”™»!So¹™°Úcá]§H×z¨ýb­c 1^ë€Zî&—!ÉÔsw?ŒÔãJÊÅr/¶ìDìÉØÀDœ‚PN EÊðXo)¿�‚gàS†Þ¡J 6‰¿ãNn9q-7†ªrþ…Óœ=û¬‰&†ˆz1S­Iμõ%ëI²ÑÛ°õGn»…={w!ª ~ˆ#’±&­ñí±1Š,WÓëw‰Ö¾g`ýc(ÔB\K‹óxOAÁÔÌ,íhœéŽi‚Mà‘‹ ˹ róT$léÍ›@C xE�V¹œ` 1.åê�+\YºÂÂÊ ë‡Ã,D&Öm³jüæDð¡jy›JþGÍÃT#!¸`‚¡®°ežÖT͈b2¶>ñ5SGØ®£ ¾Y^µ&ñI % €¨QHÚM»7�Zƒšµ=n0±ç0ÕC�7B=#-þ«�•ÛÔò¹R›rãH¡¦í_‘6µ¨¶W¹�@¹E�Þê9I¹Ÿ9î@ è&€´\hùÞ9š³{9üê/å¾?ûŸä¶ïþ ®uò!Ús{u8°²¶Œ÷‘«Õ�¹]{Úc“SÇãZý‡œóÿä­ƒtøãýáàAiíÍŽýnÔÌŒ_3µk÷îF«å}Rgö5_Á-�~ÎaÃ˦nµU÷ÂM?,©ú°Ûë¸5Æôñ»!n‚ÚDÕ}æ½s‹¸Jµ9«@dž@rÚG0~Ë�¨šH¤ÕŠTE€¹ÖË$ÿ@±Ú›–�æùÄpx¨f'6ÀaûH©%¯VQ€ÕZu=Á÷ÝO3i³pí:OòSô66°ïGcíÅU–>~œ÷LïÝÅ—|Í—Qk¡Î ¶|sä–[k�™wÔíõ 4‡KCjÔÈyYÜ�ö‡•Ç7Öš`öÀ^«ÆÏBf RôrTÔ>{Ð ÐŽ"ê(¢ÂŠ!Eîm*~ÒÛ}Ô®�.K-1Ð2χ>hÏ’÷`þ=ÛÀ!MSSsYaÝBÍT­ ¦$Ñ26Þ9"µÜŽm×ÍǦ'ìõpm@±’“wr«§J&g‰¤*Ø,Ü� 9yš’nô­Si1ÌqvÖ à‘ *s²õ}˜Tè£åp ‰GÝæVÿ¶¯c&l­Ã�­j•g²™*½©–¤”-áèQ/ ªëØõGÛˆí}G8úeßËê_Ë­?ô÷äà};Éô>VÖV¬Ãë`8�$Ž}«ÝjÔêµ™¸?Œ“¿í½û Uýé¼(þx^äoˆãd7ŸgæÙ±ß«;ÿˆ8÷�ÍVóxRkøbl޾ÿ/3¾÷0•∤ҒªÂ£?ŒB!i4Yzá Òåë`rñ9¡3óÊ[ñ— ¥8<‚àˆ@‡ÃILmr’þê<Ùêš�T•2ãœåœ W®§ÊºJÕ»¦P#øº :o̓<ÖXëÄí÷pÿc¯¤>Þ@5°på:�èC,m\Ç®9aÿô~¦Ò)^øÉgH­¸ÎôËÞøåoæ�_ôF�-%Uvz<ùÄ“¬,,YS,óܘxå4½¤OÕ¿'˦÷ÏG5"" Ééw{\¿v�5>³ÿ*Š�g‘šD$‘…ÛÚSmVÖWC"‰Ë}ÛLßrx `A 8¹@Úé'õ&s÷½–‰£w "T”æò¶;·ÕQ¨$it3%™ Ô;;NÊs [Ú5—û�xM•‡Rµ"¨>ÇVv›²5_ä7_£ÜDZõÎÖ¹*ÿcyJù'GT«ÓÚw„é;_ÁÔÍ‘ì9Bom í÷Ñ"GD¨×rs¸Z­–ˆ“¶ˆï^GÑ6ëÍWÜ\|fjN÷ìÚ»~øÀ-Ãã·Ü*;@³cÿW6UkF¨þ@{râMI’LdêØõª/ã¶·|#ÖcÅŸ¥;_.½ƒ@Fœ›„ Q½EÖë°rúYg ój–™xøâ]-"œújRU*ЍlâLˆëcl\9‹ˆ)oQAËß8W…�PÐA%íoá2HÝÙ:Ìû±�PXcUÄÅ1·Ýq÷<ú0õvQañê >ùÁpýüEhˆ±­Æêßsœ³¿zš¥OYC3“‹¹åÄQ¾ëG¾“ñ©Éjƨªö\ÈÓŒgŸ~Ž gÏ[Ïÿz­nÇN?0‹Ÿ‹éz° \œ0>=ÍØ„µ    _ÏXY™7ïÅ6q¨˜’§6¡T¨ïÔa÷Ű¼ì¤I*Z —ª…Æpe7N eq­�V+X^(v ^.ªD5m»óÎXpqA.¨c��•µ>Y@•2ßf`f×>tâV–¯!8PCÛ’ÖÜ^TÄ™l¤ÄòR+ï;Cöì"R˜‚‚µ«8ðß@kïaT¤ ‰�] ¶ÞÌQLµ@°ÓšcöZ‚‚2"/³ùÜ#çB¨ˆy¨<{Í&*`«@Æ�h±©R™s•GS©TòOŽ ôˆšsûØuÇCì{øMÌÜþ %l¬,¡éÀ%y–’癈ˆsâjÎû‰Z½~,ŠâGëIý+ã(þvïÜ×Þ|ýЉ#·î¹÷Žû[·½}æøá3ƒaoc­»vBg;öۚŠàÑÈGSEž“û‡û"£(+ÕÞÒƒ©f) /ì}ó΂½·cð»|ÍÃ'Q,ÆO¸ºÆÕ_û4ôû“`£¬©©ÛTTÖGd½âÅ{,'Só&éo“[×¹1 #€1ʪ¦ðTÕþ–8�@êeNFVÜi™bÃþ}Çyý›¾‚û_õJêÍY‘rîÙùÈÛßÅ�Ë—ÐFÕMЇ‡¸úžËÌðªÉ¦$qL{¬ÍW|óW±{ÿ^ 1·1êS¹ThNŽ19=eÐäø”MàýN�µgWoŒ²`¡(b1ñÌ…K×}J‚öl›F}Œ¬¤Š««Âf–E(ª¾2V»Ô Úvjåýêc¡:ó~,A.wƒÜŽ“šy1h/%:í ÈÉ)†VSIþ¶}ЀW»·š(V ûJAÁ+Ûµš Þz‡áµôK&ÛDBm×$¹ RP2 ó]rßaØ]"�_!íèõºäya“hmz… e„´¢ÞgÅVVd(_G~S+¶¶ðæ°"E J¨D77I× ›÷ߪþLRf 9AFÀ¡¢0o•°I5z´•A^)Soñpªëæö}«¼’yÔc{±ÿ±/âþÿ;¼ùŸ¿•ãß÷W‘}ÇÉkc&¢šµ–ØÍfK²<‹êÍf3iÔwÝ'ÔÉ«‚è·Ñ¿�“ŸŽjñ//®,üz^O9xwÐ?¹ãÑìØï¤™Ùmq­ö�I-™ 8™{ø ¸ó+¾«Uª¸qVTÿ¹«jæ\Á�H¬G›Â Ik4°væiBÃâóƒ¥uZwï#Þ×06Qæ:(�JËL¨ªk”‚þâ5‹×‰Á&7 }5ÊÐOfƒÈ»* ¤‰V‚‘8*1Í0TPµ¦jæ =¨¹GŽŸä�׼Ї^ù*ævï±Ô0íóÂSOó™„~¶�Ÿ¶Ü�Љc'Xÿì*×õ2Öä,ŠQ”/ýÖ¯âK¿úK‰’œTàP¹€Þªô¹~ùçŸ?Ã0M͛ɋ”h2æÀër}ý:D‚Ã>;Y0·{¯µ�VÌs²ïµtcÁò<ªŠ�[Ç18ðV�oÞÆ&!L/�`ß'Š#|#²ï¬®Ì¯ä‚™‚zµá#oß³‚`…¥emRYNí«Ý){‘ "`y¤2³­y ‡§>Õ$ CŠÜ„8ÉzñÄ­C‡qÄx¼&(¶?¨#»Þ£óö³¤W×­ž¨Ýlí½…½�¾…æÔ ›r%@Íö¾J¸›U 1êÕ`Ž+¢�{A#‡Ã¶m ÉɈJ´V¯K@sTÚgn›Ü‘*£!µŠ@ [ÃÔÕv3ÙJVÓ­bÑm!gjM&ŽÞÉþW})³÷¿ŠPk ®(ðâ7TõÞË0ˆ�¼k��×ÇÆÇ[µZ½ÇÉdZd“½Awre}e¼ÓY¿|jhvìemªÑþöZ³þê$IÆ4npÛÍþ2‡nEd“vluõ¼c³RÀfªÊ1½Ú»ö³rù éõ‹�вqñí{÷᧬>‚Èg ÄÚ ¡^¶~Î,.П¿ŠËr£¯Öj5 =Q&�$*ejT!âV‚³¸ ¥´­ÇEàʧêÆÄ{æöûàž×<ıûngvn7uWg8ìsùÜE>õÁræ¹§­·ŽŠO½ÖàÖ;N’>;äéóI[ß™F­Î‰ûïà;ø»™˜š„òqXÊYG-wRÍ*8q&íòéO|šÕņ退")Ì>² _�él¬“E(8,ìÇÔÞYçB�V“ÎÊ:ézŸ"Í«þ5I” ®,‡ Œ}l@�µQÆŸ-œHM@ P,±/™½‡ÛשɹիH°IËŽÕ¤ÔŒ‡†`¡3»÷)¦ A(•*ƒYT檼c|b’ÁZ—P”JÚGŽ‘ŒMâJî¹µÿ–QëkÄðÜ2ëïþÁ÷ñü“Ÿ¥×]'ׂ=Í365Á}÷ß�^ÈyöŸ=‰ëˆµkn5Zì½å ßþÃßɱ[��HØ”V‚ŽVþ�Ó` yùÊU.�¹Àp0$¦æuŒß5Å®÷pãêu›€M—LsÓlMLP›0Ê/–/‰cV×–Œàœ£ô ªÂT« Æ:#O {-q© ”®jY\ÙWKЉ݉@$ǼCuj4jrµª~’YÎä]Ó•3³üQÏ!˜×ƒƒ[_k7hN¶èõ:�*Ù°Àµ›XB?rPv]U ØrXyÿY6>x–a·gaÅáÐ[¾•Ýw½Å!шÇRlF @©<„ÍÊÊf¡*’¬r"àÑMªH”»­d¿9GÃÖÖ¦ëèV/Š‘í#ýq*0Ä–ÕØü�W‘d›üNÙ‚„Ê‚�NÍ:䎻‡™›€Ýºï ´îyÍ;^ c³²ßì��+W‹,wÎIý“+þ=ýtð��þÐìØÿÒš.ºø‘8Ivå…º[¾â»9øàkçª'´Äo’Ò�ª’ÙÖ‡J}kë[/{;Þí¹=4wíçÆsŸE{ë8…î‹× >bü¶ýÄIc“ЦM.ö^«‹tÏÛy'gf¸ý�Û9xxc“dƒÌXQ%ÐØô:¬ÜX&ëd61ÎíÞžƒ˜ž™µ–¿Ïúò*WÎ\àÂsg9ûÔó¼øä)NŸy†+WÏÒY]³‰=䊢&/sèøîxàN:g7xþï=E÷r‡š¯Ê®Ãûøöý.î{è^$®’Z¨¯^#¡ÙBELÿL‹Àçÿš•½þƒ‰T²ÿÍGgyÝ&]û~ƒÀúÚ S»æh5ÆpõV“¢P:kkƾ &Ó¨y`,4¨:‡úZI/.N*LLò0AD(Ò@ÕŽY¨9gb�¥¼\6iS1 g¢¤TD ¯�ˆ�Û{gÛœ÷¹å6Ö—VŒ ¡ �@{ÏqÚ³‡A0P�ˆµi-&@ ½œå÷žeõãgJŠ4ø¨Æ-_ü­4÷C‘Êqd¤>es^D«<ˈ§®ØâuT`Q¥gq@¡£žDu Û?ÚDWÎ+òŒ­ß h¶Íö÷TÇPÎö½slÝö*Ó¶4Ý|Îm¡TWç€"ƒ÷DÍ6~j7Ñ®ÃDÇîãö3«óŸú�ú8‹’ÄµÇÆ;ù ýL:ìÿeàYànrï¿�™[n¯Ø5¦¿È¥ÛÊ« l¡j*¥3 åñ"4göX¿‘� /ˆãˆõç. QDýø4¡‘¡RaÞM¥X<\Y¦»rê‰�9|ä0í¸ÁÌô‡ ]ccµc}çñ`úhR°¾±ÂÒâu.œ=Í™gžçì©xé©Sœ9ý—/œaiéË+7èk‡ÁJߘXêJýZ»Îá#·ðè+eÿ-û¸ü‰Ë<û�>ÇðÒ€v½Íäćï:Á×Ï7òÀ£¹*`SÝ´ÍZ=WfxÀy£Q?óÔ3\¹xï¼…�ÒëCÆOL±û�=,/,‘v‡¸`áA+r$wLíž5�FÚSã ûCÖ– Âî‘y1f29bÀKi’XO¬u‚W(Œñgûp›G% Aó˜Êî£Õ¹53¯É s}b!5£eW ã=Aƒ…õŠ2œxàðaã-.]EQüXŒ‹Ún%j5+y"•ÂFa…¼z§çYz× D׿ÍåyN½=ÁÜ+¿ˆ™ÃGI"W9†‘:”‘Po5ùWá56S‡+�™�·z•¦Ã¬¢U{[VR±5ò‘$=ÚJ‘.£LGr<2¢�6>³mPZµ *�©”¨«óbc«â4Øë°¾ÌµŸùg-Ÿu'�¦�¼Oë�3NÜŸ>¹Ó�fÇ~'ö¨�c%5)$avÿ¤zʪŠ2+¶Må¡læðW ªcí 7dƒ¡…6Š<ÇC²Î*í¹}Ô§æ‹}T …¸ñó#ëõØ÷µQÛ3Nî2œzÄþ`Ø]'ï qh¬ dQ@‹Ü¤õOœ<ƾûXZXæ¹gžãúü5 …¥¢õj]É“ÉÕ…7ÑL­)Ò|Íá[ªJ³9ÁáC·¸4fšä«g~é4Ïÿ§'é^í2Ñž4zhm²Á·}ï·pò¾;q@�*�‹2žÌËQ©ffg¦yäU�ð©ç,ß2Ö7OdþWÙûÈ~Ž;ΩÏ~dd�Á¥s§‰ë1GïÇSŸü"Xµýò§9÷ÖÓøöÃì^ßÇåçÏ£ó0p9_8mŒ«cwÝNÝ5,ßqÛc÷òâçNqãôeó.’éT0–WÃfz"õioh4c)c&YÈ ||)öer5¹½›ÇhY³’gšàcÇçŠÆ³Í[FPWàǡ…`Ã@(ªÅºõkëkÖ¬Lûj€–g�æÔnÄ{ÌŸ’Œ(ÔK�3õh'�ž[%]ì‘ö yË´÷ÝB®º5,TÕœ¸ÑÜa%”¹Õß”gt›0`+[MGÔ̳Ù$�Ù<6×ëØþÕ¶-µg£!»h3«l³4’í³˜¶Ò©#·Õs’MòPaÓM+0ÞÊE(ºkÜø…L|á)‘NgC5+Ö²¸þSõZý§ù�ØÐì˜8W¨5R‘XC!­ƒ'ˆÛãUþ¥ú¸°©tœP6o°ðÒÓ,�>EÚï²ü¹�нvžlcµ~ðRý¨}‘ˆ#öžÆä$âœu§¬Iƒ$ó„¹ô÷ßMç-×Ùý–{h™#¸œ<ߨê4êõ:D�¹œà \€¸ˆ@¬Ç½É�̵gÙµwGo;jIöÅåæè¦]c^M·§o�c�Âê1í¸iêÎYÅŽÕ¯,œYäò.ræ/’-¤ŒÇX·Éâ¾W?Ì7}ç7qâÄ1›¸Õ’þÕã*’U>ÑtH0г: €‚š„êx豇yï[ßMïùs8Q‚*ƒ =nüâUfìâ¯~%ÿGè¥]¤+¦´¬ÃÀóŸ|]†[î9Ž&X(éè}·2½oŽž8ŵkçqugì8“ª)¬© }‰˜Êµ­ÓDIu¬Šð8ò²/�é•©Ç€£^Þc"B¤h?¬ÏNfá<�KÕ�š³ýê�:÷<|?í¹6W/]emeÙ¾· žà„xj׬¡ZÞ?MˆB §±�O¾6`í#g\]¥Õj�ŠQ£‰Y&�ßQR%Á+OaSÞ!×-àSÕ~Q±$·*ÉH.#öÕñÕù*r�ßRÛäÆˆR‘6åâaó¾TË |œR}ïªëÈDeûôœªÆ­úœUÓ¶M`èekŸœØ•gW�Påau¯œãùŸþ'èéÏÑH"L~¨y6>®AØØ‘ Ù±ÿ�¶^Cø^çÜ1|ýW0wÛ}FØ.Ù ÏÚå³\þä{8÷þ_æ¥_ù\|Ï/²üÜãtÎ?gCž’ÄQu ˆ¨†Š<æiº‘§Ùrpî™ÇÞüãqׇƒõFƒ¨5Å쫾—´Éº�‰â¤/,rýŸ£{n ŠäpøÄQ8`! Zˆð…C©ôÑÁÙp %öÞ‹'r¦BŒ ¶D1í¯ù¸òÔNýü“\ûÀe†7†˜Pd7à‡Îjvæöíæ[~ô;ùÆïøöÚ��EÙªcQ*«j!l_ b¡©­C°sXmP½Ýà¥gž7¯&Ï3:ý.éZŠ«Gìº{­ýc,Ï/Q¤ACE}ž¿z™•…Sƒn4Zæq$í{dl|’þj—¬74�Œ€Ý³" ( P‡³ã Á¶á"W·Ý7'0ººÃ¹ÈÂr¤TÉŸD%{®�jqÂíÞÉÔži$§Ÿx‘ÞrĈÂØÁ;ðcuódð¡FI'hAz~…�w>Ïðù d¼÷LÞój¾ñë‰ÇgÙDòÛDJ ¨Vºl[E0G$H6³ºbÇh7ÍÍVy,‰ßZÄ �…­ëD¶&úUGη9¼¶é=-›ê\ÃÒ •ÍÖ6ÓÖñ¶úþ[)Ý�ʪmÅ&6ÛpåÏüä?`å3陵ÄXqšÙ0�~xŠÿÛšk"“À÷zp>v³¿‘Ý·Þ‡ˆ (i¿ÏÚå3,<ÿY^xÛOñüÏý3®|ð×Y}úã n\¢tL3É9G£Þ°�Ršeªªò¢ŸgÙBžççBGù¯ªá75„·/¬¬ÜÑ_[ß휋 ²¹ãÜ~³Htׯ'Ä5†� òA§�(!]ëÒ¿Ðaðl—á™>¡+¸LèÎw©&/U®!Öˆ€"e«ã‚ÂÂ;EV˜LLo©ËÊ™e柙çÆs7xæOqö·^âÒ§Ï3Xìm¹–Õ‹Úbzó×|)ßú߯½Þc’ô[²¹j£òj *j�¤tP„jb T¯½sÌí™cuu�çN=c­zÃ>¡w£CýPƒ=·ïezr†ååezÝžy—˜@wm�ëW/2ÜH­Ž¨Qobzl3#‡÷P9uŒ�ÞËÐî�D£§I›5HLç·ç˜b3`ªÉËã°Ù¬ÆQÔ‘Q`ð}àn”:•-Âfµz�Öúƒ=³g¯)•Ëeåù4sݬ~å²àê§�F¡yàÇÔöo%8Èè‰ÃøÊ£Þ¨›´OO_¨ÉAqAåY%z/šE¶/g¼Æòù<™K³›D=fìøDP=Y1‹d³Ú LB²¹Z{¦!±(f•fã‰&Èe¸ìšË9÷âó˜=ÏXÒ Äþìu Â*±?}­@°Å~C2¦nƒ Æ%@aE­< -ûýŽòÕ[¿ÂwÞKÒLŒ`!"¢ïªÙ\úGWÒ¹¦›F³Á¡ý‡Ù³s‡‰j²fžKB¢c¤.”K]ô/˜Ç¼å‹ÈwLÚJ0µŽî<ÌÞ�›1Ö?Qˆ ÖnÆÓž¹Ÿòºà™˜—zcÒl†`ŒÑ©6÷Â<ˆ«F…†ñ†‹"RIL=ná‚Ŭ^~®©…5ý&ˆ°ã‘;r˜$j�ŠÎút¯¼€òÂxi%~‹Ž�‘&1ušÇÇüØý ýr;ZfØ™R³±æí ÷ÜKlÀØšCÂK•g> â”ÑRðµQ8¶.»è[Q@ÆsÓTâö¯´H®UÔŸýx ¾%0[3q>G,î5 Hg ÌõntãåôQ"Øã‡÷°ã« ²í!�°IØlˆ¯¼qßó?‘ ²ÿDü ui¶HÿÇ07NâÏwõö݆Í|Zî3�Z�“‡1ü$&‰B´VæyËK«E2©€Ä‰‚Fš¦{EøÈ&`;Jí³ÝÂJeHÓWkÏ{WGw÷ŠB±�ó39š^ž¥Ï{#ç>óUÆ1µÿ�ÄÌ©?ÀÁ{È®Û>K46D’˜Å‹b©„ö´ùÅ^6ƒÃbêaÝôäJfQ@<Œõ~*‚ 4ÆTR (ð}SŸ0FMÊ^9Éš)—ÝýÝ\|Õ¥\ð뉚e”ï9YK& l-F0Ç”]y Á8³RÕJµ)s®u CBö~i3âÀž|ùÖ/³aí#H*ˆ‚® «_~>«^½†lOŽL’Ò–Ç7Qk(Q&ŠHCC†ƒ\@¦+Gwo?}½³M³k&Ÿ3i¹±Ó# œâä¡£4êU#m6�œ*A4ØîDISL�Ë�w*2J5æ¹ĘŽŸ ЩbÎüù,?…ùÿ#Ö1BÊøÈ[×m¢Y3–>Ä’àºè»ð åNSôÒ‚@ i3fä§›¨Þ¶•±�'ˆ£ßó�…ÊÜg½�%Ïz™žÙØW’Æ1ÖZÙÏâ)e °‹¸¯� šNó¥�ÂvÒÇ©+ ÆFnCh’º‹:Lx »/2-²qkæZ\5[ëÞö|Ü”‰L-€%-;´P!4‡8ú­Éø–µjdxȸ7äs©NLŒJ*_‹âè/� ÿC]”ÉÓÆÿ1ä$M?d²/IË&BQK,’ŠÙW(“I›"ŠHMDŽÇ€ï¡Ô÷tÔqÓÀ¤aØü«ö¼çöÍ™Sò<]ôü€¦_dñsŸ5O9A¡dS öy‹p<øS6|ìψFN¢•bþ²EF7C#&H“:2‘L’Æh¥ ¹ E£ó}߸(”‰Àâ41çyžFDð=ß�WWO]v1«Ö¬¢gv/å2Ê6_¶ˆA»�o–|ìÇ®Xzª§<âXýš{ÙÄ”B´�ŠZ�&Jîÿå}|ïß¿ÃÀñ B˜†äçYö²•,{ÉJèTø©Oe¬Êî]»9zä !IÄ<¦Ij9õÅôü‚"øŠîÙýôÌšEWw7…l €jm‚ñÚqÔD" †F¥Îé¡“Œ�&IbD5ˆ�̽C:† Ðʼžò,˜¿�Y g‘+çAƒ�� Üv€Ý[vg€4â@(ö/¥oå•(@P( Ù™”§Ou냟{€ñ‡R'Ÿ+š¿™ùËYþª?fÎå7ãù"b~©$2†°�âgò Ô4™`ÆíBìF'Ó‡ši›’rÜìâïkw$@ÖÃö´xÊQ¡9ö8Ø{N™V+îÔM”SøŸ6¢ÚÓ­!nâÌÀq”r)îh�$'ösà?þ:=°þWJ ʘ¤N"ãg†“(úÂäóªü/ M4m’¦¢”z�ÖºWD”6ÑL.^Ò4C.i‹H8 lõ=ÿÞ8‰k•çU�p¡µ'Qte&›ýûbgçs¹\6ŽB/гXóÚ?cÉuÏBûY¨kÔirØ’ðøw>ö/½/ YzÞ*þø¯þˆRW‡âà¾ìØ´�‰±q´(*ã$±ùµkRlÙ\–B¡€öL �L6k:ɵ§Y}þrÙ¬‘Ï_¸€þÙ}&:ÒSüJìÖ‚´Ž«Y˜çàž×ÒÐ*IAµ8{®JÅ^#†À³�¢užÙgðä)¾õùoðÀÏïÅ4R’ÐH”–tpþ/fîÓ  �&#YN`ÿÁ}  �†Æt_|Ì  ª©ˆ½Èì§éXÈ– tu÷áyÆh“¨ÙD‹G£Qgtä”!tÛä™oÕuD¡‘ÔÔgÐʈ òýú»g3gþòå~ÖCã¡Ñ¹v3bÃ}�Q7פ(¼|'åEçQœ»%vè) ±ª�çô6Rùånê§&L µÐÑÅŠ×ü9KŸüb¼r/ͱ!†w­G”G×ÒóÍð/?W4“LE\ °Ýw~ÔØcîs@+,æ5qeÓn_ŠûÜ’× „¦°$5“=Œ�õkò>ö|K\ 4‹i’›Òã)ad×F9ü�ÇÇ6?¦%I=¥ÙLŽ( ÇêµÚ�DÒw£ü@]ègiãÿ(fŸÐZ?CDò­Þ™D8ÝÚvw�èlö( œifHå~üQ©³cV±XöLz«Þ$Xz—¼â],¸â&”ÖÎ|v³/S:œë~õÁwQÝú5yÖËžÏ[ßùfülÆœE1£#£föI8¹=xØx}5›F^MЪ׊#‰îìé4¿„'�weOŒïÖ´¶ñVj¬õÔìc{!¦¬¢ZçM ^Ø”™íĶn�æZ°÷s~&›×´‰À8¼ïÿþá[Ù¼nÕZÕô¸äKŠ K,~örV¾p5™yY<|üTÓH› sâè F††�é¨ÎhCR:jI˜ã/ð[žj¦/ L´Òj¬Ô˜q̤˜ZHËúWTjsÍ06Oáù¾ƶpÑ"ÊsË”‹eP’ $@”²:²ï(;7m1΂‚HSî_Fß%W£Ï1S1WDc¿ÜÅ©O>Àø�A«Ô~ÀœI•Ùò½•ŽË9²�½·}š^UgÂï`þS_mŽëlÁQ‚‰½»d<ë„aÓN‰Øs,!LQ°ƒïì—ž²&¸Äb£ ×SMãÇ©=Ϧâl_Øaj†h¢) ¶tJŒç(æì5f‹Ãµ­kÓ�_þÇxøÈOkÏ‹ã˜l&+ÀøØØèý õÀ6~G .l‹þo �È뀧° X‹R�£Tí¤è)='MÓ¿Ê䲯É …l.çGQD‚"¿ú*®üƒ¦÷œeÖ/mêl{ÀàÐÿäñOþ=Y˜¼ü·ÂŸ�(CÓ~’ª$EZ¹ %8¹‰iÝvbíp§9+Ú¼‰82å•ÚólÿŒyoË�¦îb‰Z×b¯Ãi[W­sÌû‰8d”„!�>ôŸýȧ9´s/žç™´  ¹îŸ>z4‹Bg39â$–$ŠFÒ$ý¶ˆ|ØÃïÔ…íÔÙÿMd�&ÿ3é@ä¥Ô?Ù쥅R±P,–U6“e¼V£|ù“¹ò F¾ÿÀý‡êilq[˜ûÌßpêÁ;èÈO}ÎÓxí^e ÙâZÏÚG»plj›cH�k[+âŒ<´çŠS(rßÃMyÙˆtk5BÑŠr,YXï3Ñ– @¹Q’L•;!(±ûFÅuÏ�÷òÕÏ|‰Á£'©Õj&-˜-åÈveé»z6«^~.Ý+»!«ZK½"Ö)©†5ÒJB£Ùdèèi“’«6&P …j‘_lf¾ˆéè7õ:…:Š”;:ñu@ßü>º;»Œ�Œ�!‘ÁˆÂìûàá™×"B}´Áú×Q«¢ððŠ~©›ÒâÕz ˜2Î[»O3øé‡™xp?i(dü€Ti:~= ÿúç²è©¯ QcûWÞ�ŒžBGUóçÔwóËXõÌW£39Ä�ãF"ö8ö˜¯0°ÑŒ‰Ü…Zl-ÑnŽ Lì¦Fƒ%‡Èn}ÇFPösškSlè‰Z\aCê|Æiµ$3ã.m¦¿òÏõc�ÝíŸ8t(“Ëæ•ï ¢4‰’ãa³ñÅJ­úþ³±ð߮Ѵ‘‘4½N)õ?“¹Ö‚|&›ÉøAÆHn—ÿþß³ô†ç‘-wa:¬5$âvk{Êñ¢2µ–íßüG~þ5ºŠïýç÷rÁykœáÒª�8O[71­ëZw.u*N¤ãD>àLÆ´P6Òi‘‚˜ç [�Ap­~Å `aŽ)§2¬pœ�°aÇ–|ó _gý�Ðl6Éò;JD*4©´åÏ[ÅÜ›æSš]2€†"` ì“`#®Å¦éslbÔ¨õâ(&çç1BЬ©ÕPì)JÓ¤KÙ~Ñ7ä‘�€•2¤ à‹©Û˜×ºI 2øø&m–¨�fû†íÜ·—8J�t. ×3�þó¯&“é0÷JZ¤Ô8<Ìàaàû›È’A+S7$ÓÙK÷ÕOcé³_O\›`ç7>B<|Ê>’8¤°òRÖ¼úOÈuõÁ‡€dšØt[m§aÚsÌ£Ö�Ú†Éiócœ‘ʶ�fz“§%#_a‹ò(´;®Y¬'›�e} ¢ÛGÓˆÁ›¨ãJ·#;ÎCˆNN&¹ýø†ïü»H#œ§´ò3™,’¦R©VÂfóÛQþ0ÌÙ‚vÃfAà’8¹‘?×Úû³\!~±T*”ÊeßäðEãõ-äÂw¼�•7=�L¾Žw“cÝ> žVŒ�<ÌÀƵä|X¾fËW,C8ÿ;mZ”%¥©%Û•çVo1pÎðØ_q ¹LfÚÄ'ƒÔŽ= Î,[{Ô%¶sRp2å~ w˜­">îꀹÖ9®ìË­ˆfB±„™NY‘Pö¥ ÜQféò%tõvS¯Ö™˜˜0rî°šÍK<8‘rôW‡˜8:a¢Ÿ|g�L1 CxhLz+R111��1ÎDaÆ3CëA¦Äf©q P‚©í$´^Ç\E >¢1Çî=`Fdkí¡Z?³½r™Ò9K¨&T=ÂÐ×%œ$É ñ ‰4£ˆH„ž‹®aùsß@yáJÖ}üÏÙ¹IÓ”)I̪ßÿ^ýT_;“)[iZ§&˜Ê´ÿË-1!بC)G#âlZ9³nœÈC�}îiì1m#yg,º3ª u&}:)¾iÒiœ.ç½ T³�zô×ê½ãõýÛ—ûž_‚£"L¥5ãcÃïE©Ï5Î&´‰¦�\.Wèìëû›l.wÁèÐ�ofܧ Ùz/»‰+Þõ~LF3^6ï(xÀ- 2uŽ:·Ô58sÜ¢4‡ÖþÂ:§�Ÿ¤ÜÛÍ’KL„# ¶;ʉ@¦·JO'ݲˆqL¤(Ì1{^:}p‡;„ÝQŽÙó]ÒsÈ CF8U\óÜlÎOr'’Êç,:‡U«W�x022ÂéÓ§MZ*ðL� º®©¨Ð8YC"A”`LL_ùæ¾ + À'Å�Ž¡"Oy–\Ìc VÎmML±ä¢ZCï-¤Z¨7kÜy€p¨i&ƒJ^®HnÖ<2�Ý4�Œ0ü£mŒ~gñÎ!’fd¢˜F“)tpΓ^È’g¿ŽŽE«8t×rúW?&dÍ Ò”ùÏx ç?ï÷ÉärÓ~èiC½ÜÙ6?b`Ç+œ¹ü6¡µo‰F¬ù€u Pæ5Ì~˜¸CÉÔ ½½Ø\#PÜ”ZÚªgÆ (ÇUÀéãÉÖ†Fwýàs»ÿÔßA­E&N"|íK½V­yJoõýà=M’1´¡®ï�KgеFíÖŽÞžgHštCÍ%péÛþ™ÒÜ%¨lO9¶ãÓ¤¤6ï�;ÕPÛáLØ´ƒN"ÖÝú÷œ~àG$õqúÌáMïz3×\w5ß7'MƒrIÄ5xÂ…Vˆ¯ßÃÚºÓR‚�½�ÛÔ ló„Ûwc 5£Ä¶`»’'[vzq,ìgp v Ê43OjÍ»¶îä®ßÉîMÛiTh4•jÅô") tC÷}̽f¾y,öÑž²ƒëÔt%ÊÔeRL¯MK®,æ^žhDR»«¹kóTÌw2Þqc ¹þžGlcgLL&ÛM±k%̓ ª�!Ý4‚L„è$e¢RAÐäºg1÷ưâ9¯%èìeë·?Éñdsâ(${ñõ\þûMPîqæËn¥Ld–ŠËû\“‡R°"¸¤aÈÉ¥îÐ1OÙ¡kNDÕR¡Y¸Ÿ+°3ÿu›«s±Q–HZ;´íäæÏþåÁ£Z•+{|í©fdföHàùciš®õ=ÿ€#´qG4mDõzõÚ$IA¦»T.Sš³˜y×=›\GJáªsÀö#ˆcÿáÚ:æºéíyt-^Åàæ‡I«cf&þàè0ËÖ¬¤wά)…Ì£´|â%ð;í^3û¼)?9}Zç9 ¼¢UÐ6›K\-RÂ÷‘Ö=Z÷nÍ¤Ñæ¹mÆLÁ•>¹i;+@ak9‚=bßÚ<×Sž`¢­ ›eÖÜ~.]L±\F$5Ýóikè\³ÑD5¡9Ô ~¤Fe×(ÆH£ÔH‚E ^®5Y† Õ©�V´ããÛžmHH‹¶j6%ØÚ�RÕÊyÊ£>ÖàØ�ÃÆgŽºB €lj’l˜`âÞp N6Ö¦?§†$~žÎE«8穯äœ'¿”lW?Ç7ÜÇÞÉ^™Fu i‘unÕe\ò{ï¥Ô7‹@»QŒr‡”á陜¦·Xᦾ\Wdçžn¯§§ÛÂ�õZ4]Òl‘n­rªÑf ÍÄÍ€%G­ŽÇ{¾óÑ£�}î+Íã‡WŠ¥N@™±ÜJGuB+ý�FØ|0@¿îÍmÜ|¤ÜÝu~6—óU¦ÈÒWü1+'û|­Üt�£Ìq¢§ûYáì;i7áðÚÛYÿþw@Ü0…íç¼ö¥¼îÞ`R˜Ý!ʾ‘£˜⨌í>bŸŸö\»a�5Ub}E°ù–VºM”%0Ûž®`Z‡ 5ÝœR+ß!¶¡^­1p|€-ë7±ñ¡Ç>5ÌÄØ„‘`üÊ”ñL£žÔðòs×ÌG/ñ).-ÓwN� »ˆJ1*«²� çá9¿"""­ã P¶Y³¡#2i€Ž4›ÖnâÀc{` ESÒS )Ò¡;‰ê¦&d¤cQH÷\òË/bîå7Ó±ü2å.Ž<üK6~òÏñU´ñ¥ó(.»� ßô÷tž³Ì.øÚé�qRU¿Ñ¼…[¤×ñ8W%f{^Ò)ƒÅìtMwÞLÖÇþØJRWñ­{c÷Ý^\§h°3fœqÏ¢€$‘pøÄèºoßê�¸ã Y_R¥¼\.�ïû2‰fÆóÙÜ?$iòÝß”*kM¶@g<�úF¾XxšøÊ˘áSOüóO‘/•móešºöà¤,ÑØõË‹Ik­iêÃ'Yûþw2±ùAòÙ,мþå¼ò÷^E±\´Z¥³ÛfÝ‚ÛãÀ^ç^b£÷( q¿×´;*çæÊ)(lÐôŽ@+"p\�šM bõÕný©V©²wç^¶oÜ‘}G8´÷•ñq#ÐÚ#IcSàO½hüœOZHéœßM0?Ka^Ñ�N±·HЛ%›Íwì˜Ø¤Á�™\ÆXø¤’" ¡1Ü4ÄSª2¶}™Žüê áh/ñŒ_‘¢˜/S*–‰ãØÜ§‰"·ìRæßð|ºÏ»‚l©åyTOâá{Õ#{E±X& r\þŸ¥kÙùŽ"L93ÿ]륜>\kÇ’ñ°pÊd‚«NmÆqpú}±i9l�'L°5¡4µEûiv5`c85!s_·ïÇ"ˆÂèØ�_;¶ñË®7F†JšÍÀ»L–|./a³9Z«ÕvÖëÕ˜thÿ9 ´1#ÔÊ=´qVâ ©È{3ùìâ “Qi¦ÈÅ“uše7>¥”3ü)eZžÜ’Œ­¯·C;%ÐíuˆS8µíQ6}üOˆN%IcÜø®I[šžr#Þ”¦Ûi/)àö݈Sk13 2�[P®ÞÀ¡û¿2s�È@ÍÄC¸Éqïj%IÎiö¹cø ˆ]]¡21Á¡ý‡Ø·c»·ïäÄ¡ãœ>~Êô§”ËDqL­Y14�fÝOª ù�-æ ãÐL—"èË�ŒÆ ?ã!±�)g i©A¢ .fR(±"£3LŒŒÖC’(666…b ?ŸGgã6лxõyÒuÉ�”ç-2®Ë Ìü£ ÿþOŒ¬û%…|‘$I˜hÖ9÷�Ïâ'>ÏSN/­cF9£ŒÓƒb_ñ”šnvs: áŽ{ö¬¾ÃUº!¸™R÷f[‡T¸óûבp –ðÒHÆ÷o­Y{û±#?þj©�Í÷Eq”¯ŒÑ yÒ0 Ç/¸#dþØG¿¥©³6ú€�utu?;_,–Ã(DŸs7þíPî�eÿ!¡ ¶R§ÚJ38í(®?noMEì¹ó6ÿìß šU|Ïcñš¼÷Ç¢e‹AˆØ¾kïð†ñ¸\rÆT™›ŽCqf¨™Á%×»Üó•ÝUIâª+l±À¶²Ûcæe”#6hÖŒŽŽš´ÚúÖ±wÇã;ÕCFF†0 ºªÍ)±ñTóÅ' IЈd² L$’&‰ñ^kFM00ý¦6øYã¢]7*¸0j"@¦P6^e¥y‹YpÅuôŸw%Íb*“à ú’ˆ]?ù¿ý Hb²ù˜uËKXý²w¡´'€r‹ÿ¸�!à[E£ã¦ìˆl”c»ÿÝàQÄ-Øã 9³e?gÌ–¦©ëí1ËkÎ|®q…jEöÇö%§º}xË÷ÿ½�tŠÅR…* C’$­t=Š¢£ÍfãZë¿ýÿ¡ ³íÞÜÆs<ÏÿHÿÜ9 “$ñ›qʪ7¼—‹Ÿ÷z´óJaàäÌ£¤åãä�vVNžÝ)²FÕqúä_rü®ÿl-$)7½ð™¼û/ÞCwO·›ÿ8ñ†[ªØ(gº lú=ìkÓÙÃí×qãK¸4¤~ãõ A á¤V¹æ4˜¢f¶PÎ7‘A½V§26Á©“ìß½Ÿ“ÇNrâÈ1ŽÊÄè8cÃ#¦! Þ¨™fÐ8Žð3:ŠeÂ($ŒcT"d²Yó\e<3×§«³‹Á�Óìß³—F½F"Biþ2.˜l®œwÍÓQ–¨°ðÔÇFØ4i/SÛµ‘f£J£2J¢<žð×ÿAÿE×Mýš(PAK£YóJ§?­[ßZÒØ×Íæz�M @]2 í˜aº"–HÜÈÉÖ”\…œ;.Ú±Ýjƒ'hløy<ððí•ÝëÖ%Ê÷:뵚¯€l6glˆ”Öõñ±ÑmZéo¥"_iãê²\™6Îj\’/¾š+V†a3Àóé¸úY<á­ÿH¶Ôeû×°PͰÆ©“c·¹ôìâ%Âඇyl²K¼yb¿™†ÙÑÑÁ%7^É[þðm”:JnRÝvßÍÉ(ûÜMo¹±Æô™2î Çâß–ËtX2™©®£À%¡™¥3Ë�8qÿ°]üé^* S‹‰£ØD$#ãŒ�Œ1>6fTk™LÀØØ¯Ÿ�ãkŸ®¾„Ž®NjµªI£uõô�ÉfŒ8#MS¾ÿ�ÛXû“»‰S!ÈÈõ/àÂ?ú¨L&Xb°%iÖÙ}Ûg¸û»x(<%ˆŸeî‹ßÅâ량ö3ÖÝ'D�n´º³ú›:»Ø;‹{Ò:›>sTènz ×ßLa=̦Ù×$©³8YMp¹_ÛºŒT6ÅÜS™™1ññ=é¾o¼/Û¿3ñã8ÊÔ›u•ËæÑž¦X(R­TN6šÍǵꭡ¤?RÚø†º¼ÔEg5´JåíA6û¹Bn¶*L0³Ü/zÉÛ rS¸MQî/8¥¨� SÄ ²tÌ]‚]¿¹¤¡‰C­»›½·‘``?ÙÀCTʵÏz¯{ëë�³ñŒdc‡�)·N3C^Í}nˆŸʶéþï"ÓJû¸ä3=}f_Ÿ‘>ÜÜ3]2±ãRÁB°¾j$‰müÇjØý9Ÿ$ ’ Jk“þJP˜Ái(�x¤­•Ýó|s/í)FGFøç÷üZO&_¢†”®àšû.äÊh—IF÷lb÷gþ’±SG‰êãþ=÷šgrÞ›ÿÁ4ÔZ†�ø@šº*3· Øå[­\›£Q9ñe,Nã°} ¯¾©°‚„(±bG¥–8)6+$°¯ K�êàQ‡vÉé�÷Ä'º#L£Ô›a–Th† ͆‰bzzûs/ªmÙ¸ñ[õ±±Ï�ÓÆÿBD“/ÓÆY�®$Ž¿=É’‹³¹\£QW±xÌÊ+Yó‚ß7 Z›â½§RNíÛÁ¾ûÄÀ¶G{ðü…«¹àÕÄyO} ÚŠ?°*ì/Dw •–”á=[Ø?I6£›×ÒlVI%áÅo|5/{íËÈÿk²1››Âr¼•AÔgÏŒ+kš>R@\õ™%pÏwÅfîÎLDbÉÉ%/÷|›^³ã¥[Uq'dTØÔŸÛäª1ÌfÇW»ßu&>x˜¿}ëŸÅ[#J„9WÜÂUïý‚™ã»î4ÆGxl²_ftóÄ�¾JÉÍ]Æe~+�“e­_Zõ©g<<Ã�Î�›êò•�ì9NS1�â ͆óùœ™û­”°%H·—Ç�ˆìè£ê?–“”ÚÀA†þ©ŒmøE2xho2qzH2“2YÝhÔ[>wŠ(Ž"r(Ž“ÝÝ æÝvjßþïã´ñ¿uq{Ío æ™ì_öÌêM¹£³ìi�‰z“Î'<ƒK^õnÐ>C‡v³ëßæÈÏ¿M21JØEÿ¼7ý¾àMèB'î’ëÖyìèçÖ¢;²w ë>ý^ª»Öã{Šrg/~Ó«xñ«^b¬ò]«+o³dak-N(u&¢qŽ8œ0½Vã¦Æ¦Å:S{h\¸÷pIÌy}æÔš %8ßTœØSÅ›!¢™zLZvóŽÎéîx„ïy€¯|ü ìݽÑ>A®ÈÂ缞Õ/y©öÁZÜGÍÛ¿w+nû,IX#P‚Ê9÷mÿÆü'> ßóZ ½3¦éi2"(9ƒRÜ·ó^¦k#<çÇ‹3ÑÒÖl¢0dàÐqªQ“¥Ë—’Ëå¦ENíÂîNO—Ak³pž¹×¹˜ù[ bf’±_Ô*\o™ùS¸|gOrˆÂžäi³‰§Á[dp_8®ÓÊF3ÎdPsüñ ›xüáõ4ê¡©ÑT5ÎyÊËèYº=¥ßGNnYlj~ÞD¨Åýý³Éž{5k^øf¼L¥lkë ‘ Ý6}´�;gÆ㥩éÓ1ŸCk\!¼í�uÒ¸©­«8 2KHö8Óš=íg‹›u$Ž8±ù!üè?¾÷ÛŒ›|¹¥¬ùƒG7óàmÈØ‘G@iŠ WrþËßÉò§¼íûŽ©`êô*¸KçàÞ­¬}ß;iÙmJÁËßñû¼d’pŠ¥"ö†—�”#iv3Ö[Üe:‰)fêä³».±ÌtŸ9ãÌ9¿33Ó´kaƒ“b—áÕ̤ö“ü”ï~ñ[œ<:@‚‡Îd¹ê}·Ñ¹`örI9²öGìýæG©Ÿ¢§œ' ŠÌ~î›Y|ËKŒå�R€ÏãvÔ+\çe°J.­±�Óøˆ¤$µ Âñ!#!VAޏ6Fcx€úè¾5ëHÔ0=?ùž¹fH[®w.ÊóÑAvJï‹bŠ“”êð)j§Ž�3´ñ^üÆ^s\ªC'ã¡“'uØhx‚ЬÖ�VÝ%—ËcF#4ëâAšÍæÆ&FG·'I²9ðƒo6ÂÆc@ƒ6þŠºzhã·*©Õž|¸P‚ñÐ~V-¹æÍœ{å+éê[ÚJ¹(ׂ_)NzŒû¾ú:£‡��b¶—LÚÚ,ºöY¾‡¯ÁqcqkÖf“T8¾éWlûòûÚùJRÓÈyÃóŸÆkßø3¾Øe�#ûxÆ€@�{HÒÄtǧ‰qÍ53÷•ÖÓoà4„ ‚e΋üîpf!Á™áÙêKÓ¿¼Ó«Ä%NZHI¹ý{wð…ßÊÀácŠ\G7O¾õ^ŠsÚ¯�ñèßÅé�ë)ç2” E¢YK¸ì�?A¾³î˜dÜB}*®X$I]:¶òéi¼iŽ›h&žft×Fj#§è A±>H}ð(ÇŽg|¬B˜Bب“j�B÷,‚B™¼ŠˆÈd2ä<¡R­˜¨-£- ¾ïCmؤƒµ§�Ÿ j4Œ³E+ÝhµÖ2yŸ$Žãj’$“çŒ ²Ckï[qßÔiãÿ8Ô%Å2müV@K½xŸRª„Ò( ¯â‚Þ΢շà93,ê-;~A8¶ÿ!úÏwS܉‰lÎYÎUïþó.½Éœ øn½Æ måÞ›Õ N<¾–ñG~ÊðöG4Þ[ÍF�«n¹ž7LF7‹—-6¿ œ¡ îʆ]’ÓMj`€ýû²{×^¶¬ÛÀÈ©a.¹ê ®¸úR®ºþjS'r¤ÉJM�€Üz�«Xsþ�=ÿÇ¡pq†èÉ% ‡ -¦çîŸßÃç>ôiŽï9@&“Ci�lG7×}ì§ç,À#åऻÞI›™°2J!Ф~�¥oø+–^÷,«:´ªl+±ÆÜH­Øól³0ÓÝ&ìóf”PÙ·…¡íë8¯Ü ¯²sÓ&N9¢&FÇhÖë$Ib­%©—ƒR¡€ùA†bg‰Ñá1šµ ÙL€Ä õFÝ�ŠI½‰�¶"—F­ŠÒšÖø‰T„†HZ�ãx@ÁzÏóŽãh‡ÒúáÿÛÓV�eó´qÖ£ ‘¿ÞŽRZ)m"’å׿‡ó¯~�”»Ï‘)cóå7.pJ)켋_}ã�DµÓ(­è\u)Oø“Oлì|PÊmêLRÓs1qì;ãø#wÒ<¶�¾®Osxï>“'/–Jäs9¼b–—½é5<ùéO¢£³Óý Óÿ™›&Ó8¡Z­²o÷>öìÙÇÝ?ù%{7l1�óJù¦£�5×>‡•^ÅœŽ®¹ñ ,\¶­µË ̬‚Cá8E‹…œ™ ”š–vøÈ;îúñí_/…'ær¹‹ã$¾)… µ§ƒ|¾� ²™âÄØX’¦©ïi­‚L†|¶ �º$“‚Œn† Éä²Z)Ýլכ�ZýÈ`PD~¦”ú0JgCMŽ6Îj¼Eëà‚$�uϲåªgÿ“ÌZp‘(¥g¬N‹!±½öŽ¡‹H뜔Çîú0;~ù/ˆ$Fò\šuåŽn’ÊišQDêå¨�ž"£•!˜$‰Éer†hPÂêË.£ÜÙqbý=÷mÉŠh­æ ¨|!Oç¬n^ñæ×pÙÕWÐÙÝ9C�BeÿÞýÜ{ç½üâßcüØI¼r HÓ„4 É”z8ÿ©¯aÑÒ &ÉåZ:»úÉæ‹ÎBݬ�³ážorÍÕe®¾îj´RÓc ÷Ù42š™Õô]÷{œù¸ÌÄg¿‰€\"«×j ž:ÍÏîø9w|黄µÊÓ&"ñ”G³^3µ�z3Dgs\ó¾ïѽx5IuŒ “ãÆwo¢Ñ¨RÈç(Ÿ O:¥.K0Ó¸¯Õç¢íÊi2Ž(À›aœ¸¶Æš´æñ ïýãîdy�Ü·ï}À{6>ø N£è|¹X¡µ^VèêZVªYßósÚ×å\¡Pj6›hÏ“°Ö¨¦I¹,I�ãéA_û“$³!Œšwû•ÖÃJ©i㿠툦�ND6k/˜Ÿ&¡7û¼ç¥OxæßI÷ì•.¹€š¾:µ %’n”5«<ô³áÀÚO€Rf›=gÙ C£Q7…wßLô’ËæÛ¾u;?»ý§l¾çaÆ'*LŒ�d³¦VèëáœeË8¹kµzÈxeÑ×èúW^ÈÐÖ‡Ø:9ê!œ¡V'[*sÉ}œÙ>ÑÖc’)QL`ScØ^™0ÁRÞw,õí=44¬ê,£[¤â™Ge'·ÚˆÅQ¯y@âÚÁ¤Q“Úø(§ƒ�±Ñ‘\G9Ú8‹Ð&š6”òER&7…kQ®þ{zØÓ$bøÔ5|b;'>bŒ1•I¥ ™ VÜ@çœ5Œ}Ì\?pìi’þ¨³Üu,›ÉÎ)Ê›G¢áµ™ ãÕ“¸êy~8¹[™)m‘äüÆðÑ#?ó½àªñáÓ+ß'ŽBQ tÆhŸžsVqé“^Îe×=�ÎîÙd²ù™{ψ»÷öòÐ/¾Æ¾‡n'‰ê\yËu\|åõdsÙéõŽ1½ë»3qÌØÓ3sâÒ}fœYlRöäþ;ïcÿþƒl¹ÿ´òèèè¦î*c¦?(ÈöÒ»p>ÏzÕ+xÒ³ŸM¹Ã°XÔ5s¯l¾ JÓF‹�#5*´|±L¶g. WY»¡XØŽg¦Q3oJ�·ÖªÑDÜ×ìW¶Ö2 ml÷=�A)å‰HJg ÚDÓFŒHøûûç^ú"]ìœ'8˜‘lTԬȉÃëÙóè7ÔàÞû'N ’¢´gÕK𠇾åå§NœÌ+ŪFØø‡B¾²¹&�ÀÌðóy•6›¹±‰‘g¯ÖÄq8­&¡ý,]ó–±êª§±æ²'±xÅ%äòe<ß›‘L¦Ïqû-õ Gnã¾;¾Àž¾�$!Ù|Ž+Ÿzü7bšH=Ï›Á‰`Jˆ¢œ¶Ô;õ-Ä�Ϭ.›¡eŸíÙµ‡ŸÿðgìÙ°•á¡!”Òd Ébä¼"B®Xä¼ë®áæg>“ /¿œ®Þž–’Ί=L3cÿ¼y¦Ç$Ž#¢±!†�î#£u�Z£N©w¹î~›îRÖÙÎå7›Ì`„¦Ø×­‰%3ØÂárpl¢¢Ö}!®WL�)‰âŒçùýÀ~Ú8+Ð&š6Ф(•«ŸÜ¤OZ/Ë/4D£Î ‘2u—õw~HÛð-Ò4VÚ ÄÏQž�çe ‰Ê,ВƤqP ¢PJPêÊ0l¬Ö1¡‡uü�+EDnž<è2J™å åyª0kç^õt.œ$˜…KϧPî1bd:¡8“f„˜i’»¶<Èý?úïYVá6CÁ~z 3¯˜X¶Ùæ J¦ÎU™¨²öÞøÁ×ÿ“[w‚@G©“þÞ¹ŒŽ�\¨lÀ×^ÓŸû.¸ôRòÅ¢q@8~œ°Ñ0 õàÀ)&FF8uô˜‘|{€ÒIšÇöCQ­ŒÇ‘‰hºÏ½´Lˆ§q6QnÑßoE-±€ÖnO­(�ÀI1ÏEÙë ” `}C¨õzMjCC^’Ä9Ú8KÐ&š6”ú"‡éôÎÉèdѪ›t�- 3Ëšåô‰m¬ýÎÛÕÄÑ †`‚lQ2Å>Šý+˜»üz\ŒÖžLßÏH½>®÷<ö Nヒ¸1&i*‘TM¢,ð²�¡“EÄŒ •‹€!ÀÞ¬VJgÁœ£”Òª0w} Vpý³ßÈâIré虃֞™œ9-æ’�ˆP¯�³mý=ÜóƒOsj÷Ò°`fâ/¿êÞúî·rÕ¯BkíD¿Q5 ÓHÈ�n{þÿRÂf“ÑáQcáÿ]÷³åÑÇi �cdºx¤ˆ©O5›uòÅ¥9ý<õ…/äÒ«®"_,pâø î¼ãǬ»ï>†>9`:è%Œ¨��›Æ Œ  Ù¨Æ2…ãÛבz~�¥³§¿{6=+/j)Ìldb6À¦ÆÄZø[I³,–óZ ®[s2äM…ëªÍˆR¢•J´ÖBgÚDÓF¦»«�ü¸`hÇêÈžûeéùϘéç´œ8¸Žµßz›nïÃÏ%(ô2{Õ-¬¾òUÒ7ÿB´DMܼÏÂeOäðžûÔ†ŸþÕ“Û$M#%Bx²¤Òr!0Èe¬ÜTr”_è¤gé…\qÓ‹¸àò'Ó;ëœiäò?K0cÃl~ì—Üÿ½O1|p›©É(…I¹Í9wÏxÁ³xÁ‹ŸKo_6$šŽ%Ï™u‚ûAœ/á�‘±Èi48¸÷€)Èo{|;wýø œ¦6:N­Z1ö*¥b‡97Nb¨‰ÒŠ¥«/àÒë¯ãÁ_ÞɾüšÕ*£'ˆ¢ÈLzô<Ÿz­J©xoï,s]®P4rï‰ñƒ¤©Ð¨UHŽ$×ÙKwïls~qþRò½sñ¬Yª;$,·YS+,‘Øñßn™Ê@h“%+¬sƒDeĦÿJêÝÑÝ3wøè±2mœhË›ÛÈôt3I4ç÷}³Ö<[žôŠÏJ6ßÅTÔ&øÅ—_¯Æ®%È–èYñ$.¾ù=2{ÁÅ(í™zL£6"qTS™|ž(?ÈËÔUuï–;Ô£ßy«jÖ†€RZ”ö ñ 2­:äJt/9Ÿ%ç]Íå“Eý‹Ï5…i‰œ!(83ùµÊ(Û6ÞËÝ·}’S;AÒÄÒÀü Vóâ×¾Œ›o¹‘Ùsg£~£º[ά=vUcg&)—ŒLJkxh˜Í6qçÎὩŒŽ›h&Ž"3Z9Žc´RŠ%h†MŠù"žÖTk¢82)³B¾ˆRŠL�%N"jõ*ù|‘ááAD…9Oû¾ÙºûgÓ3{¥®nlzœZ­J"ʼo®{:—Ç/u°æuï¥wÍ–@P–lì(ˆ¨UOÑØ×­¹¦CJâšhº œü¦î îeßmŸ!© 3»ä7¿ÿþ7ߤ�³툦 ¯XÜ–T«þÐC»Îá]÷¨¿ÀÚ†� õØ]1$ãgŠô¬|*׿àýi¡<ˤÁN}\î¼Kxð³JÀD]‹¯æüëÞ¼EWZ&Xrî“zâ[e÷}% «æZ…R™âlÒ¨Š 0õœ¨À¢ËnáåoûÝý ¦ìq]R±ç9ûa³ÁÈécìÜò+îüê?S<‚HjÝxg­\ÂÓž÷LžóÂgs΢…æÈô›ÊŒ-ù3MÏ<©¸ç‰¤Ôª5*vnßÅ¡‡¸÷öŸsâàQÚ�HÇ€"›Éš™'¾ö@ Q«ÒÉçò† bZû$IÃDCõz�b¡�fÓD(ãÆ0³·6:ÐÙ Ý“ä’ëì¤gÞ|:{{�ˆ åÔáCÔM_Qê›E0%Ý —Ù/‰X<ÕJ‰ážcµæØžg«oçaSj^«é3q:Q€‰hâ¥4qd/<»ˆ¦�6Ñ´!(õD^,I“„Ã;~Á’5OÅÏôá]wsðÁO¡ý,ùþ•\û¼•_“LšDjïÖŸ²îë¯T)h�„�­·3zðauí«¿¢æ-¾PâùY®~‡û*µ°j,n–\û6V^ô\*£Ç¨Œ¦^f÷·ª4çèæµ4µiµ‘3“ŒR8ˆ£»6?À¦‡~Ì–;¿AX¯8ãùýsçðÔ§=•›žt£q-h6F¬µ¶9¥4Bú›‰D¦‰CJJ)¤å#&‚éc©TªìÛµ—£ÇN°ùáõܲ åyT&ÆiÖC#/Ž£˜À÷i4†lžO¾5¥Ö¨�ÉæÌêÆyÏ7¤£P†ŒÂ0$MRÓ´F1qšÐ5{.å èîêfîÂÅä:ÊFÈ‘+ŒL8MSsm'&ÏñOŽC År…<¹¥çá;[5wBfb ýØ×l¯ŒÙÜÙD ÇëÌ6dj ,é$–œÜô™ ‰› ²JÑR:iãìB›hÚðòù¡¤Vû[àÓ ¥SÛogè‰odöÂKU³>ÊžõßFPø~–óoz·”:æ ’²kãm¬ÿΛ0–îFÙ䡃’&4k£DõûÉßñÔ7|“l®CÒ3{5å¾ÔF›Þ�æøIé�{žÌ^t¥y=‰êrrßzôÐCª1v’›Ö2÷œ•¿‘HÒÔ>w!ÂÄøwo`룿dó�ߤYFĆhÏSÙ\ž¾þÙtwu±ký6ïÚOÿ¼Ù,^µŒî¾:º:˜·håŽA`ˆ­•Q¢)­¡5^ÀG)t+j6Bêõ:aqúÔiŽ=Îøø8Ç%ãl|è1lÛA"bHÍó$@�(lš: "Ö®GkmÎÍärˆFq¥5]�=DQhˆÅSšz³NP,pѵ7Ð?w>}½ýLZúPîî1ßO32:Œ &Mgj:Jb"R­µ!²çžÇÄà Õ±Qc²91>†¨< ÌôT�D°}å963v¬²í‰Üùþ0=ò™n– ••FO%cü ÀÏfóøiÒf(ˆÐÆY‡6Ñ´!Êó¾%Ir™Húæ$ sÛüœê™ýA&FŽpzçh/K®k!‹VÞ JqêÈF¿ýÏHD9ñŠ›þTV^òñƒµñjíwޡꃻ©žÜÂà±Í²`ÙµLóÏ&ƒûî#Mcê#Í#€Ò~†E>�ÑÃ�€xàÇÿÎEW>•ÞY ˜ŽIÆø÷l}€{¾û1NÚN³2† ØïªTRìèTÚó¼\&Ëèð㣣d²º:»9}bˆ}Û÷Ò7»ß�ÈÈÈ¥ž.úfõS(•rùBíiÓy6C³X‹§ 1ˆÆÈŽO r`×^ŽíÜKTm�*L´Pîì4õ’ñ‰1’81 ³R*æHUB£^Çó4Ià›è&çgI’ØQ’ƤIj¢ßó@ ãù¤"ô,YJ îßCG©›…«/ài/~s-¶pŒ„R†¸Æ+c&bl}ÉÎùI͸BOiõZ6Èàer¶H«Ö–bã¼X W*ûº�Zl Ì<—Ö5 w‚¦ÆÀ•£;‘�£>KRˆµöü8j’ËxDÍ0•¥�³m¢i#><=‰›ËOmÿ‘½áíœ>ºIÒ4V™l'‹.y™\ÙD,¶þTÅÍ Z{,¸ôU\rÃ[ÈdË �\®L®s>õÓ{fp(VôÍ=Ehñó=Lu‰VJ39ÉSv=ø9ÃûÔèîGÙøÐ�¹å¹oæLNŸ<Èã“çîÛüG'‰¦>a#PˆV…ŽéèèRZ+oF±U(—ˆšq3¤Z©˜F­Z�ÜêôΚE Ç÷åèîCVJkTN©˜´–çyèÉ-ŠCiÉ‹#’(1õ‘¤•ºÒZ#`j1M?4" ÏTÍ•!«š®›�]H’�É ÔÇF1¶ýÍJ [,Ò3ïºçÍ£³–9¿gòñœ¥+8}âßý؈ÃúäþQ�5D£AÌwÓ­á] `Òdq™×DâÖ¼~m"¨r¹“U—_Å–ûï!mÔð2E¼î~2Å’!ÄL.o”‰@ìL‰¶�ÿ6²‰“é: sÓÓ;ËH‡EkŠ]fA¯ŽŽu–6*® ÈR +$ˆ‰6E­Q3 ´ïùÆ®%khùFblÈERS?:m*“Íš¨D{¾)Ö÷Î�G÷¯·ÅKð€¾Ùsééë7Þca16:D½Z¡2>Nbr„äræžÅRÙDU¥R‚B+(ŠhY`Tc'íclð‡öíæÜË®4©6C6&"1÷…�d M[5%aÉyÖjì|=©$PctçzfÍ�K²èB¼lÎÚ÷{ ¥1—)­q§¨*l±?L¦ˆ”�`lô¢lÍ Ç„C4¾ßÑžOÁË º#Z+Ÿ6ÎZ´‰¦ ¾¼$�›ù£¿íeŠ=fQÔ~–b¹€f}TÒ¨®¡gñÕt÷/�ZýæÈ®»Ð ð|_ñ*:z9aMeô8:È‘ˆ–®yÒ’AÛs´¨ù+oâІoV™8´•õ¿úO}ÞÛ@){âЩ#ÜóýO³ë¡Û�h Q# ë¶Nâg²tõÏA¢ˆB&g~ÏÏÒÙÙeÒF™L–Òä~Wß,rå2åž^Š�˜šENn‘‰L†Ož`|ðjx’82ç­á*µz?—gÖ9‹M�_'‘!�$I(÷ô“?v˜¸V![,sþõ7ÑÙÛÇâå«èî›…çy†€1ÄÜjC2ã„ñ=c~ÙÑ"ijÈ¥ð=mKCf™,’ ;:èèê†ú<ÆÆG:~ŒF½fk:´¦i¥M”åµ¢,¬Qe+÷¥”ÙŒ°À÷XzÑ%&}6tä…î<•±L ÃÉæ,1�BjåÍV®l¥„æu+0Ç=×�ÓÊšµnÝC\ t, @i…*÷Ï3þÑøq½žDQœËær�ÐÆYˆ6Ñ´¡õ&Òô‘ôÕQs¼8¹¡¼€li]³W¥€òü¬]<¼lÏÏ ¶8ŸÈÊË^ª{ç¬ÁËdþ²kQÊsl…ŽnR¢r�†är%@�«–Yç\BçÜ Ü{·B{ïb£‚>5Ààà€y¯U¨7;;íkG1£Zµ eŠüaÔ4щ”–š"9~à£Ís ¡ÍY¸ØWÓûvlšª:1N½Z1ÑÖèÈ(§N7DcdÕž¶‘ƒùžçÛ‘RÊœ%1cc#T*ã¦#^)³ ·6e¢­^k¡³Ð#¦ð?¥AÐDX™¦Nh ©´R\vì²izœ˜0Vûô÷Í&ð|gÀ™ –xZj0ÅÄQhˆ§õžf‘i‘dÚŠFL'<† $µRàÙó¢ƒÀXÓø¾ÏéãG±„& ˜ ͤ©U bQØ”"6ÚÒ†yÌ„TSÊçŠä´ÐS;22×oìVjz“¤Ø´ž%bH§Šß�ÓìlhCÜGO[òQ¹r'Aà›Th�Íj ‘$qDg=ÚDÓÆ¾–ܹ JF�mbrÀ€ôÍ?Ÿ5Oý+üLIBöN’Íí¹^=ô“R»7ÿˆÝ›ogÏæÛeã�T?ÿüóõ£ÿù.5~|³B„L±�‹Ÿþ·”»X‚qÛíE¥IÄØéƒˆÎØèÂÏd̯鎹óX}Ó-<ëMoãÆç¾�þy HÒÔ*½DÌ‚� $qlÓ°Ñ0c‹•RvÌë­ôR«QQƒR­{&-âÌs€$6ža†˜¡˜=ì"mR{&½¦Œšmðıib2 +` uíy†$”Ò-2J�”:M‡lZ<‡�ŽR1îËf \™à®m›Í5æu1ûÚ�ý3€Ö=A)m�%&ÅLù~ÙŽN´2V8²ùÁ‡wn|ø»9�E Á×XSLlã¥Ù,ÔÙ²ˆ+oÖê7ωcʨ/W$ôò¤: ðýÔ÷ü$—ͧ´ñ[€v�¦ ¥¾€Èõ ÏLÂJfçcßR}sÎíF‰vÑ5¿G>ßÍöû?E}hqm˜½÷~˜½ ×(DÄ 2ùN)ô­`Í o—%«Ÿ ®òU•Ä¡±£Ùòàç8¼áÛÄ�1ŠÝ=;Ëô-8‡Y‹—²`Å Ê�=©]´µ6‹:iÚª³´ÝZ¥BTocÉ Èh� ¥Z×aP'’)‹¹Ù»ðk¥ é$-ß{ÕZµRæ˜ÊD7>ŧÆF�Ür‡’˜Ó´`8N ¢€­“´lgZÄbŽ™…ßI¹ ÆhSkÓdH2ŸÏÓÙÕCcl„�(2„k^×Ú¦÷ÊîK‹0•¶2¨ÆFG»³�þ¼kÞu/ØS‹õ ­@ÛXÏ2eÕdnÑ_µ|̰äf]Ü”Úô!¥æ}š•q$‰Èc#c‰ A’&�´ñ[�vDÓFø#àHõð£_ãøÁG­Ó±öV_þR¹ù5_’7¼[º]E`F—re㕦=óœ|±—ÅW¾Nn|ÕdÅÅÏG{¾`¡ �ÜÁC?ûWõÃ÷]¤ö?øER'Èä¸þ/äÍû¯¼î�þŠËo¸™Žî^P؈¡ÕbL´"š$1Er3?of¬dŒ ™h�gê6Â!µ5 Ñ´Rl&ò±L’&fN%µ‹5‚½&ª8väãc£Ff6n}H),¿àÆvI‹DE¬½��„’)j1À’£ÒŠ|¡H±ÜiäÙÆã¬Z¡Q«ÒâQû=£8BDœïmë@X2#6Ê7ûþ¦©´61ÑS.WÜöõÿ˜“ÖÇïiI—=ð5øjJ}ÅŽp£Äʜ;RX¥Y’ÚÍÖ~R\„£§Pa�°Q' Ã0Š£I„õ$Iho¿›O¿í8¼øó¸>4ï�¯½¶¸ææ?–Éî}Ó¤)iWßR®xòSÂëôØÐANYOج(Iéê_Fß¼ (wŸC�-Ú¹ÆJÁØÐ!9°ó.udÛOõàÖÛ”—-7´—ÑJ㓆ºÐÙÍ5·üºYsŽé²o†v¡v†‰‰€yDµŠêb–1k$IPH‹`X•×ôMœÄV�†©­Ý¤ˆ�nE!ÙLÆ~«>%†èº{ûñªµ*ÍZ;ÿaï+€$9’l_D$Qw5�F «]­è˜?333333ÓÁg†c¢%Ò®´iz¦¹81"þœ[˜[V›ŽIÚK «)Ê̪6‹Wîïùsk ÁÈWÙôé¶ZîÜ‚–€¡«¡×ѹŒ6 agƒÄ»wÉg¼³ƒÑá!õ ѱ )ʘ—!ûœ Xq¸ôF—%«t™ToeH�£È3´[zp¼ýú·ÿ׿yá‹å»2�ÙS,ñ0¶:\”�§*¨X%@!ªB^Ü Š‚È �V¯C¸“nl¥£ñ!êxçD]:«CxÞ¿¶e¹àç¥Ó{ëÿ¦?¿=Þ»¬Î>ùevuëi5z”¹4:ëöÁ›^¬6‚ªÒÀÓmÊ i2¶»·>,öv^Å��þg™Þÿ˜…Pu¯[£ge‘>DQ«¿²†óO>Mïpþ\eQ²�eUF XÞÁ¬#²i?M4"€„YF¯—\B³ þÌ<ë@@ƒUXtNʼn”г"8ÂÜeI†Êg4 QÊÊC†‚š�é»pÜ—Eà¥K’/3à¸ÌŠ�ˆgR‚¸›À8I²¥ó OÒg·TNÌpt°‡íó“…Ž;‡^·Ä[Y7â‹áHÍ…1æé.tQ(©äú÷ÿ±_£¿â—þª_yõØþŸã ç»LÎÝÿ«VmxT;5KP,IŸOJȵ›°)ý�>G†+q£±RLg=5ؼƒ¢š:´�òo[cvaíW–ÉÑìæ‡¾îâîßv†‹þ©g1Ø|®l?cãFÏxACX£‰Ÿ˜Ïö$ŒÁ|rÿ¸|ø±{íûÅäæw È(B¡®YkLY¼j­Íñ�hõÔÕ.„@–¥PJ¹²•qe$Ízu Jµi²å‡PÝ?@#l€¥È@ 8Y3ØP–z]*¯q¤½"pÐ¥ažÆ‰”|_Må:ë\‘áÊq!7ÇÄÂ–ë‚ ‰1ªæ_œ À¹IW€ Õ&À‚õÈuûþ­xúù—À¹��~)$Xp,5¤–tka¹WGø>f³)@60¶eâÎÓŸwNüë£oŽs{^òŽå±p'Ç3óŒšJ‚~ŒAF€n9+¢Ì&Oh\.’t2™fd”PÇ;.j ©#ðoü€µæ·è"If×VÇwÖï|4 †ÖÙÔô/|&š½SÐEâ9ß}M”E‚ùáu”E*t>Í1…ŒܳÖ\~°ÞàÛ åŽ²â74;Ä&£#,Ê`ûz ýáìó Ä›ðì�•á�L(gû{Øa¸˜r†ÂŒãW¤’ÎÞ¿dþEB2I)X¾l­†…q .VdQ“¨1ô7NÀ(é2…Œˆ{ybD(¸Œçx#-\üá‚3"édÚng’DTÞ*]FSjƒ½{wQäqheg•1ËVZØ·%Í<˜à oëÌ9\û؇at!´5‘6ågø�IaOiÊÈrH?€ð–,[—¥8×ð™lÕ%€] ت†‰FÉCÕ,Lž²›ƒçû~ØmÇGG!êxÇF 4u¼)<ï÷ز<àK�Î^ʦ÷žÈ¦¶ Z|ì?4á !ì`3c@¤f€È|Àû|3„Ø=ˆ0Hó�-åÊFÃõ ×ïaɳ,K*£ª¡µN:,-æ$õ]¹8 ´X$[�äšD‹Ù„löK$¨H—ÁaŒ%0ÊóÌ�CÂcÅ—Ëj¸§dWC²ç¥clž:‹f§�t>!õÚlw‹é„|Ö\©ÑuýKP?NV²Ã4èø'\ ï%è=½Õ!ýò]›N'ÞäX®þë¿÷7Tô¿ñÊâÞÍÇ�¯|‹£ûhnœA÷â“hmœ……pY ­%а`^‡ç ù 0`°* P)K‚€ ˆ}…áÊ:fÓ©.Ò4¡ï£ŽwtÔ@S‡v}6ÇI /âq«†zÖÚ-% ®È�ÈG Ä€)€'"€”ZªÇ‹¹× Ð ±2Ü€q|Éx<‚u€T2ãdÄ€óð †�S‘ÔôºV«“(œ?K|™»p^`4›&É`�ôØù�9?4¶d) —A¹0ÎÖ…$ÉÆR¦!ƒ¾òi,@«Ó,¸¹\¢•$E–4+à®}k� Ðgã&R"pÙ«Óë·U¤sºît6%žªžò\vf+‹mjª™"-v&@o¸NÛæ“’Å"Ȳlýþ¶?2ü;—ÙL£ù¥ï½í±Ã÷nÍf×ÏâÜ—þ l¼÷ó¨×Å ‡€Â §fAÿ_ë,,�ofð¡^©Å„LU'ã’\Œ…GoªãS&j ©cànAPíÈ(k­À% §^Òøáƒz]´W”…-Èß÷CôVW!•tžcR1ñ¾ìSðœ˜4Íè5–æÄx°h¶Ú8+í.IŒÃ8†8Y†‚›Õbˆ_Ñš=ИŒ’y OÜ)9C±\Ž‚³ÉѾ�N»‹˜œ– (ØNŒ¤æ��¢åû¹ÑuVI{ç´L@ë‚{kŠ‚œœœ Y² À� ⹨QÕ.7œºw. ‚¾Wíî*ðQM¢…0ŒT™æˆWÃû.5§?ÿ�ùõÿ1ù9Ø‹¯à“ÿôOâð꫸𥿲=€�Xªri¦Eán¸Ç—€àJguL´€ˆ[tK™�Õ²ÑjÉѧtkE5ÐÔ¡ÝÊñcŒ££}5à ¨TchÚ$A �Ìâ_âN½Dä=•Ũ7@£ÕÄã�?…×�¨oÄ”%õ[H¹âÆ&[&â`¸_úŠn ÏYˆ¶¥#6ð˜W¡ça«‚k– «0Äd2Ât6A0iAë‚ÁÍ96³¸Á‹*ËLģʤ0Ð WÚ²n’i õ`¥ó)�È“ÉÃášJ�^˜y-KŸI¨ƒ®ÍV®Á¹I“\;°�–R´“dñ¨+‰~â}ßôÿþ¬†¹Ûë¢( 1úöoÀk7_Çú—üJôŸx‰³þÆ€‚eѨZÒÀ‹åû64¤îþMHSÀBP£k.„ÈOJÂë@ݰYGQ[a º�ž‰â‚\™#82†+¤5ó5`¥”ebÝXK~d;;w°·ŸTR££šÅ_Ý8]'?7}K&šË›¯ûçÅß5pÂV›D�!hYòh›‘´: #D~ˆ,ÍÀ b,•{ Ñµ®oŸM�3.€•iÔÛ¢5ýŠçÌ>þ×ÉdúçÓ$¹-„$ iÅÕâú?ù#¸ù_þ²ñehËÁ65lOÃcXÒÌ×EŸ#ß¿‹FR¹‘°S¯7= Ž“QMuX+Ë¢0Ç£ãR ? h¾¾S`�f£XÃò$ú/›?rw¾“úJ�?Íbñü€äÂDÌ/lÓ/•rvý.»ØT³t›¹Ë:ÀA�`šjXË`Å •NÆ®ñ3§®} X€€8Ãñ”‚¨tæóqÌXîÌ×å±7Ý'>F[ƒñè�¶(ÈÙpj�ÕmR©êõW¥Í\¦t¦Ÿ øÝþ šTšc±˜ÃzÊß6¥E–gëεk¿òæÕ+¯îçÓéQàaàȾóßâÊ_ÿ�Øù–¯C>=vÀ�¥ÅØb—eÍNȶ5QoeAc¸¡‹¢8¸u{š¦IŠ:NF 4uÔQ–eÛÓÔºPY–¢t†–ÚM—d[~·™²m ÏZ¡ÇØ´±¯¬oÐTÍõµMÀêkË,[Ø0€UHvÉÖ7@5+ `°³pá\’[«kÐZ»ÆMÅÙ >‚Uî‡.«±lå/T¥ a@¥µFÐç^]]#nÊXƒ¢ÈÈ÷͸ïTJE×CÁ5—EeÜsÕõ q__^æª,‹¶µ¶p”¾Íú.ÆÓÿ™%ÉÑl:±¾ ô%‚ñmÜþ†¿‚ü±Ÿ‹½W¾:K*Ÿ¡Ò; îÅáç5û  h¡�%s›gFÇÇÙ|:ɵ5ǨãdÔMu!ºk›Û^©³€zQ‚Ò ôšÏ¦Ü;ôkŸ‘€ç¬8ŽEºÒ•%ë•¢$�¢RÏÑþ¾ã3@a-›L²u?ZÂÒë8!‚S¹9n#Ës˜“Ö1•FC£5ñ$RJ"ö�Žé¼UðûxS',‹‚�CJÊžxXšÑôzΨʢp“7éµÔä:ÜØ¢¦Ù($ûšÍSæeyŽË¡’9èªY§a0ª^[Q,�ŽšMâ~Â(¦’ 5Æ›ŒF+ÚŽW‹¢øõBˆ_$£ÆošÏgÈü9ÄÖj»{wpù/ÿÜ}Ïbõ]Ÿ‰�g? A³ƒ Ž èäìQê\!ªsn§D‘Öˆ$I´’* £hNœTuFSG'ä¶ãd1K!d¢<…v» )¨â ˆ”¶ìf ‘åü—™w›H–Æ`4>Â|1ƒÎ3ׇB‹gä;&ßmØŠ3—½Ðy@›0øàx#£�ãxlU@™Fw¸Ê>$¹Ðð0é@†x÷^º^Np“>�Ö<ÖYJÁ=-lçï 2<©3\iðý€šVó7jzt[™TÉ«;XA«ÛG5è×úèàÀ�”àá_¢2í’ÊVeÉ· |Tù ¾5üZ£KâCÂ0‚¸Ü�¥cñu3qS*ƒšX�À’ÄXË<�ðX!%5mΧSºOón‚€�è\ ;Ce|´WÓ¥gÞ�N·¥$)Ï´Ñá|>àGŽÀðë²yòönßþàx4:BeQX @ ƒÀdh�oãøÿþ \ýº¿ŠïÿS¿ü ¿¯Ý_ǵoþŒn½‰éÞ=˜"#‡àz @+É3ÔÁQMuðFj­1�G…’”D`U•+ï8Y1-–[¸ •X•„Q„•áoœÉtp£eÕË�‚�–ûw*sôÁ£Ÿ!]o�+Ñâ�Ùq-EQÀ$)â¸éx"R«ñp6g„ÉïuNÐ YzžËs€` £×³H€�˜¸˜ÖÊ ehFk¾�t±`î àQ `f°sÀ ÊŠ–` Ô$—β”Ôt³ù ݵáéÕ g·~ã—~©‡]ø×¾ü¼ÉáÑoºyõêÿzp{=›/ö“Åbžç™ÎÒJ„6C¤çÀþ ~ÇÀíoøTb{ÿŸý5¸üõÅtDJ»"Ï ¡PÇɨ�¦Ž:âF£ˆ£ø0If:c¼¿‡¼È LeT³œQKRܲ$_.Gƒæ³ˆ0$™¯±š<¿Š,+3Ì«0oBovǵÆ.q@ôîë¡[¼eX ŒèŸ>CB€Ü­,I‚³2V&8â, J)ZU™µ<”gÆÐS•L®¿:„ô<£éXiš�¨•y8,m àÜ5�]¯éxg}œ-{¢(F:�Ï�ËØ�<üØâ~w¸òŸ`í/Žüèçë¼ø[‹ÑôûLJG—Ó$�y‘£Ë(Œl™/�^,›ÑtþáMX]"O3„Q¤Lµ.cÔq2j ©£c)£™…~XÄ�&‘ÍY’8‡bU™;/ÁÁY«Ï˜?qŽÆdƹº}ŠHøµõMxdWRp–r’D'0ƒ­¨À´»Эà Ê��+ÅÁEå>©ÇH ×éô°¶º¾Ô(é‚ÁÁ:Pe1†òëùµ®ôÆJ5~Î�w1�¢Ì2(IN €�d* ÁïåþðñøX nîÚ~$� ý�â¸AÊ3!B¨,Žc�{Xç÷!¡Ô_TJýòÙdòFûû1žþßÉáñÇïݹsóèààøèèp6�³8Štèû5kú¾_BÛÛv,P ŽZÞ\Goáuf²$¹¬¤z©Ùhbnæ`k\YÇeBòÌ*�m�¥2ÛÉM|më|ϧ_õùþ.õÒ4ÛíŠ_L [®€ËW²²¬[!—§dòÆ�ç$—¦ 9&=iX逋[—ÆP¸k©†µxK×w=4š-ì¦ hg¼™% ÷ÎŽ¨THå5£¹ôW²æl]|ð5¸ìFú>e|Iš M²èÏ“t£Ôe@ò´1ºà¶Ú�ÿ+,i–l&iò¤ž²EùR‘¤�ùhÜò=?òà ð„jL&ãƒÀ þ­RÞ¿°@o5ÐÔQ‡É‹|o×.Ò9Lip¸wŸ6Á¢È+¢Kó`„À‰ �›~ݺç˜Ë¬®�ÜŠ‹ñÓé+Úp˜ËIÒò°3ÀeMìJʘX>l ìÁÏ“@á)$T.ȵ� ü ?$ÜXs𜠋؇¬º” ¡”ä93B²ïâf­v‡DyžQFöŒ#É3=®µæ~%Î`–ƒKx÷Ñ 9Ï£LaÁSÊ+†ÌÒDá'/J·.ÃSÿUÓ´yûžÿ¬í�ŽŽV´£0üÆ4Ï®¸÷ÔñÖQMuXc2Ï÷ Იý�»ôk™ìgŒa^¦šmT‚6¾ ÁÍràÕ�M¬mŸ†Ihµ{(Ӕ籰²‹6ÿ¥R�°V3¯!—Ù 8;YzŸ@#j Õj“’íèø€n]ðë]o }΢(–,f@ÙÈ2’Љù²,\o‰0)„!š½¾�x"S�0€‡ÈÁ}G°pY¢¯¬0S`^‡pÖÀJ ã”zÍf ÇGG¡ßô|oe²?‰ñSº<ÿŒ’²È3AÏÕñ#EÍÑÔQG©Ë¹•"ôý[§€¢€.i:%s B9Î÷+ü‚ë /«J4ÚÄ[�ÂN›7£8Æxÿ¾³8aI2D*£•Y6]‘àÿ»MžUfܰɯ#~ó£CêýOFtmº(H°@YŒ ;㜛É) h®ãg)k³KFh7[ú>ÉÄ©Ÿ¨ÑÆüø$„¨ ˆÜW,:¨LítK@àíøqG úÛÌg3z^‹,I6„ üô†ùQ�L5ÐÔQG‘ç* ¢»^”�V¾ïatÿŽöîÃ:ŽÆVìóOö™'mæâƒˆ´_?}»»wñÚÇ>Œ"Ë œ‚Œ‰t6Ód'è*7#’uüI„´9»L‹ÿïÊXä1vÿÖu$Éœ²šAA1R½vtlÁ}B”epFÁÐÀ�"X¦íQY ¬†ƒ)°uþ"¨4æ:ümšÂídÜF37ää×$À`ð¶Æ ,râÌÆG‡¸õÆëxí?€"M1ãxt„Ññ‘˜­Yc¨£Ž·c鬎:æEjt–^$n…a¸>ŸÏB;�áÆŸÄ楇1N%%æ±<~_JæM¨ÔÃÎà [.àÚG?ˆõ­mHmq´¿‹õSg€eÛyÇY”4û^ÃUª`JÍç¦k³¼Ò`´s‹Åœ2%« ò<§×Qv` QYÜ@JCÌ€}÷w ÙéÐ8ßóhH™qå:%ÒdFœÎ<™Ã(H®B�I]lÕiymm{«kè Vq¸¿‹ÞÊ*š­ší•úf‹dš`à¬Éh3/�-æXÌg Ò›&0ÁÁ½:¶’Š@É÷îÞÃý˯CZPê²ÀôàVk4M3‚Fƒ°(r‹:êx›MuØ0Œt^|O–¦õûƒ�¼(q¸³ƒ�‹°h�J©ŒÝ…]v@›3I’'|uÔñvš:êÐFkÍ¢Ýíz°Àþî}*ÿ\yß`ó©'Ñ[ß@‘¥˜À�"¤³9Ž÷wá)�Z¶4°E�d<ÆáÝÛd½â¼½¨�q:ƒfÛCíÆÌ'c"þ=a�g‘ø¥Ì $¸a²Ûíx¥YJY@š,H!e0YšÐûÆãcº/¥B·Û§sYAs0Ä ?@A’íÂe)ŠŽG}>eB�GQìÌ2=ZE^б�µü9 ^+€ÈÁ˜>Ët:�±†"nưBBI²Ø¡ì‰lÿ­¡¬†À9£Ò�j£é¶Ýé‚ܤIJ¥5éy¬2ŒñÄg}.Î=ö$�ê•ø>ïÝkQtI—åW@ˆ�ÿtH�ë¨C|ÚÊ:ê¨ãÇRYñ¹q»õ{:ÝÞç§ó¹gÚ8›ýíe+ež“í´�¨H™ v^¶XY2Ÿâ)�$•ÎsXX ‡ëôØÑцÐëö $造r×d2¦‘ižÞìõ@:Ý.îíÜ¥çr*/Ä¹Ìæ3²„‘BІ\P–€ÀN)Í&ƒrSÞ¼ô(üFƒ2%$•ÐfÓ1š�””d›)Qf)’Ù Í~Ÿ>5”%Žoß‚)K4:]²Ú™"_¸ÒÞ|F (<Ÿ²AÊ=…N§‹éd©]Sš;—å;[Cÿo5�,»È ó9&GGØ8}ïú¼/ÄÚé3ht:h÷€˜M'øŸÿì`tû‹Y1ŸLßкü*WQG?å@Ó¢Ž:~Œ±n­ýçíN÷‹‚ P%‘æ> âVôk»Õ‚ÑÆe9ß§Œc:›BºNv*!ù‹“S9¨pd=�U¦^IYDERÑmº˜Ãúžk®”�Æ"¦q9tšÒk:ý>V·Ï ß_…P’®£¿2„ .IGã!Ý�š-R”At}¾O€A™�’t= Áh“ÉžO.4ÖÍÀ1eIŸ‡G(¤œ“Ãá&Ö×7 ðàû¿éã•ïþvܽ€è‘O‡[TVƒ50�Ô“XI‰#„ÎaÒ9D³v("ŸÃÜ‚:¼  k›ø²_÷›±}ñ!º>[‡ùÑïùN|ßüFâÒ¦“ñĖ柴Û�?ø¶Êjê¨KguÔÑj´Ïäeþ÷úÃágy¦Ò©¯Ðˆ›ÈÊ q³�<£ÒËb‘‚Hj-|g éûÊ4AÜê ½¾�F· 2€ìtà‘û­v›JM€€çû4�swÿéÜñA‚{lÈlR‚‚æÅàâÅGÐjuà¢ÒÏ"–Š™Ê(dzËÁ“3�¥¿“:»¾ !,‘ô¯3ލé…qLÖ6!õ¶äXLƈ¢€GЂê Á½5�Ü|iùZ¼öû\þޤ”AZdkY’þb? >öSÙPYGž†Euü!EVü*mõ°BœŸLÇX¥ð9_þUèôImæ­�‡.ÑæêÅ êñ0Rñ|~³wÒæ€!³HT�À}7Ö-NEx�>a÷"–Æ[n’Ô¥¦y,”mUæÕ—%@€–¢Lîý¦b�Ã';(�ø(ëF;ƒ‚€ˆ-±œ±òÌKo9>ØÃñý«a„‚ç°$ÙŒ”E¹‰¢¾¯èÁ¢,Ýq,•êp|:ŸAû>|/pƒãöéœNýFåH£K-œ}äQ|â[¿ >ɽãŽÑúk¬6ÿàgŽ«©£v¨£k«õïò|ïO ‡ëOmŸö¶6NÙþâWý\<úìs˜×£ãq ª5€löQÈ…쮬ÊÁô>Öz-(H°sud3€Ë6\(ϼ)‘9g3´Œ!ð¨Ï p°y@e\eÚ¸×RpÓ§R’–k<]læ.À.7©2Xž3PµÈ¹wý*t‘c>_€ø˜ „¢ñÐt.ã\jCçs3wdª£ E1‡8¼‰A¯²¡Q �FƒíÈ…�íqÈ>ˆfà¬^¸h·;ÞÖöéÍ8nü2/ ê¨ã§hê¨#„ŒúõßÔmw}F§¬µRR/L€ ï}�¿ç½˜�ޱí*„5(J?nBÀ¹ ;ƒ6M,và•3˜2Ãáþ²4åÑÖ¢j!Ã@A¥$À£œA™‘5à1n¢'—ªÜÈhøå¦mò 4X€§rº Þ…›ÌY¶ÕqÁCÌOä‡9 ·À冕‘àÊkŸ@Yäš°škE©Ù”ÔS ÚXhmèÚ£0` URðP9™/€t€zjŠ EYÒwk�\\—œÙÀZxaˆKϾ›®ƒdÝe.ÒÅÏ]L§=ÔQÇO'ÐÔQG#ˆ:aÿ®áÚúïo¶ÚA ò<ÇîÞ=´.žÇ“ŸñYÄ«ì\»†éá©«’ù^£ E5–J`ež!Û» S,ˆ›˜N¨ë¨ 7«fËvøÂeä¡fJþUïüÕØÆÆZÃ�3Ýd^…½ÆÀ¥3ðÄÎ º],ÆÄnÌÖp–ÂF—U#MPP¶ãúvöïÝÅáÎxR "�Ú,ˆ8k‚ËXºƒ\0HÁå#7šžã,I6Òä¤AT2‹–r�`ÅÓV'�.9€âä4Kc5¤G°…?—· *��ƒš0€€f²¿Kç]ÌPÝ5QL 2©$ tß�û¿ pÂ!�Î`�î¢ÕjHÓaQ¶—å9ÀŽÚnÌ6ƒj£ÕÆÅç^€…%‰÷ƒòYG)ù륔1ê¨ã§hê¨#n4…*Ìc….ÿØ4™ý¼·ÉBŽÇ#LçSê�yæó¾€"˲Äìø6ËÑétÐh´ÑX=4Ü&É£š)ìb “Í8#(LIÎTÖq3ÿ©¬&á™<'pà$�Èüe�œ 1/ãÀ‚Ë=ÎÙ’…w ~ 3òÌ»Àm–e z$‚�$dåóÂòù íŽuëÍËü€2�úÒ{¿®Ùj}VE烡贻TçÏu�³/½„­‡.ñ W^ù&ãcìíÝÇíÛ·�6ú€tòdæE@¢ŒF+ ‘g •lº[ôËÚA¥t¶4/†mõéqc¡�澩–ƒ^»4‚À ΂˜´wàÆ÷i¡ª";Aæóë™ó0Ʋå � `“–Có,*q�Ǹuå ̦#r3°^Ä|îÒñ4,³†` ÝTRâq¬Fsq@Í<ÏwƒØ@†¢yžW¸%šÕOÒg d¥¦¬§Ýïã¡—>�šO‹"ÇÊ`µÇÍ_Ñj·%ꨣš:~²ÃÀˆ2M?sóܹ¿�¦Ù3Zëáê` �Vív­N/|å×à‰÷¾!%øÇׯasc½ÞÓ4‡×]¸äcÜœ~),ôô½VƒJ5É"ÁêÆÙ¾À‚7U ±4ßeà(hÐZ –2[œ®F‹�“ æ\$+Ϥ’të¢Â)ÇÃCÄ@A`‚0™ø×ºt eX}Áœ�p×±»×¯Òuy Ȱé²<Ãç óò°3ÉSªµqç]L0¿“É1¨û_Ív›Ü |ßgq‚ ŽÊ'÷ÐVH»%‹S.Âz>ù¸ )| óiû{_Œ:êøÉŒhêX¬ˆ~«ÿçžzòOìÞ¸ùèí×/ H³6ìà‘Ïû<<ôî÷@ù>“ò;ׯßpýÚ›xõãCÖB… Mà‚0𸜥µÁ™µVú-ÄdFI]þ¬¤âà°¨r$–A‚1 ÕQÍ|Ÿ`A¯€¹nØ4–‡ 8Lþò4:ÌI!YáFY×[|×,Êvõ“Ÿ@±˜.#òZ}xq“¥Ô.cá±Õà,K�* eUùÞuÉŒ•pÝÕU<àYÐët¡<�¬~À@Ûˆ$špׯ^oeˆÍÇwV:3ÄqÜn5Û¿=)2…:êøÉˆhêð=_Îçó_õгOÿÍN»óÙ‹ùl=Šb"�'Ó1ýb~ž|º’qÊ*^ýþï�µ†š�Ñ­ë{a!>a>Â�«W±·{�6µ<˨l¦¸©�6Læi,—Ó$-ð¹9+á ¸:ÅÌïœ :'?f—m°ü˜_!0£¬…¥ÔÂñ1€Œ±´,_ƒvÜ‹Àäøo¾òr0Z£4‚ú�„û,Ú‘þei`ù|–yæ–Š êø<¥ˆçéôè7P�ú¬ÕêP©,£ñÐ 0 ¾Pv#•äÙ@��?ÈR›Ý¾ãÙšªÓë¿$Œù¹¨£ŽŸhÔ@S‡'•Ç�_ÿô‹/üáÑÑÑ£o¾ò )ˤ”hµ;d&ùž¯ýy8÷ØãpD3oØ·^�\€[ÍVCôÖÏ"ìaçe¦€Kl~>ÃÅÓ+Ô;rx¸�ñè+››‰ò[r#•ÌD0¿`-NJ�y#fû—ªm 8Ëa3t{r�´œ œLŒ5’Kj‹}Ò\Öån¹ìg±së:ö®¾I�—Zqjå,¬ƒ­³Üq.ך%Ú \) ‹cÈù´ÑTÆSxêÓ?«gÏB‘‚Öšäãp ™§|ryvB ZÂ}†ÕÍ-4WW±˜Ï1°Ê2o¶ší_.¥è Ž:~ÜQMžVà_|ú‰ß›&é…»W®*ÙÀ’lY‰G?ïóñÄsÏó†ÊJ¨²Äõ,•% ªí—E‰bpZ…Ž·°®‹®t# óãà â¨AÙP{sýÕ5pŸ 7xr/Ê2HÐPÝ(¹ªE4¡`^†Á€’œ–‹e°sÇbù4s5‚ïâHbN ÆÀñúG?„"O0‚œdÜ^ʼ<�¿lOI×O#ÙZG&#è2'С±q„KO>ƒÐ1¬ ŒB  K½ôÝ6-AÀgljê<ÿ̳°�)a�RÏeùE¨£ŽOÔ@SÇb6]Óiö熧¶ÏÎÎÎùWÞ÷$îí÷dcßéôpö…穤¢<n#f·ã½[7ptëZ­.`�i’#ž@öDXçyIe ’-ç)òé!f#š‚I�úÛ�>†f§ËõrfC@Àò`'VfA`fÀà¦j0¨‚¹˜ªZ)ΜNd"¢*2à·‘ýKQ.õÖØŠÚŒ30c¸a”3$ ÌÇcì^½B MçP! ¤Ç×NüŒSÂ)IÅ9ú.ËÒ0¨š¢@àÊf�¸Iå°á™óð<�¸© I‹£QAœ…5oÒõ:é4© a-•Fm£A3<¥D¡Ë®Ñúçe£Ž:j ©ãÇ2Û~þ¡3�\ú£ã£GöoÜ”�¸�’J)¹.1|öœ}úªåW xëºÑo¼ò V»}¬VÐíô¡:k;]¾¢�QB€~�{ŠÀÙ‚8ƒv#BF´AvšmÀ’c{V.—º(›áÒš9i`¹TB ÈgnqV#%—Ö蘜¡�RνÎÝ·`©4ޱyN8ù½¬T"—Û @ÆtwƽÍ>dwX*‚-q´ó_‚3)ºærv„äx‡2™(ŒÑiuñø‹/#ˆcì#Íæ�²4ãlI;žh0‚<<Á ¼ûó¿B*²°Y]]óý0|Q ñù¨£Žhêø1€Ì™¨Ýþë§.^øÉ|¾YÌaà‡ÂýþI‘ãô‹/âÒ3ïæi“àJUtvt„Û7xƒ�ÌçÈz§PA3R\‰Š íÀ÷ ‹Í64f9IÐéõ—ËMLÚ/ÆKªYõ|öD&ÂÏW^kèœLúWÊ`n® -çÞYÉvHQVÉv8󡌰ÎÚF±“€p ¹ÔûÃÏ Üzã²óI³PžÂpc¿Ý§²�'{œÑyù˜<G xRÀï sj³$K0™�)Éóœ^?�0�Œ‰ŸÉ²Ô¶åLPIE%`¸vœÞ>ÍnÔ+¤µB ñË„+¨£Ž1j ©C›3g}ìŸ<ýÜs_ž$Éæxï@ÄQy™Ó±\k¼ûK¿âÈ< ¸ì¥Rb‹ÿ;¯½I™f‹9ʨ‹pu‹�–«ÝüÖýêÏÆØÛ¹‹ùbß÷(«nmÃe+Ü8© e‰èÎÀÀ’4}Ó-2լލØô³‘%¬¸IÓ93{ Ê“´3U…`.Çe%¤�®ÇÅ£Eç¤à� `6ãî›—]ö% ¼£¹¼E¡—F˜ŠP¡(©üH¯!à4þè.ú�„¤Þknn’’ÌCM°�F“F]7˜üçP•gSi­ÙlC Å è>%¹4l=ñ½f<9†ø¡Ï5âæWFq,QG5ÐÔñÖÓ0[¢åÇÛ«Û[èü… Ÿ>›Îwî"§QÉ+ƒU’Ã>öÙŸƒ O?M›(PUAYZJy˜cçòëÌ‘äYÛYƒ×h�‰krk`Çe*› ÙðȲf¾˜Czz««L¢ŸTˆ ˆ*oÁL,Yv€îiYži#«Šl—ó0‡R–ŽS1¬Lã�™ßK¯uM–¢ÒãâÀ�²Îf0hݽz•[€bpê,TwV*ð÷+¥³ö§ï�†¤…¾Ï÷…T6›Ü¡²Ù 7 ~æ¡ÙgÜnÓù”çQ“åFÇØß¹浤`ãÑv«C#X,˃ßÎ?õ4º½ødÄ©D·ÛÛVR~mY–—PGo5ÐÔ6�=ùi/~ã#O=õs_õã�Ýë7Ðjµ©“¿·²H‰­w¿½ëÙe§dæf[í_ÿØG0=:$Œ o\àæ¿Rk¤yòô‚ó9+sœÞâÌæÂ0" ­ø~ÞÐ* BÎÄËe3&nÀ<†Ë|¸a‚›4éÖ-VŸ ,)Ûhq¿�®80Ÿt%XêŠÊXdc«ç$®ˆA�³¯4Áå¾R)v@�uzÞ÷=¾!PQPT3+sp&[�¼:/ øž�þp p.´Ô£¤uI.îŸåï‘~4¬®®Q&D‚ mà¾X´û4Ξ¢lª(rÌó`<?5ŸM¿ÚÀ*ÔQG5j ©Cgù…�³gþn«Ý~ùú›o¬Î�F´IA€þ`@¤öÙç_À³ìþ¥ç�a`‰£ˆ¢æ£cÞ¼‰v³E€a4 ;ë(ü&ò¢D’æÐÚpã£1†Ç3æ‹–ŒAÜOs8DÜl.58 €3 nšBL”SfTál¸„ æTª3hªCÕ°~ÀàPnæ2» \œåèRW<ÒàÞ–t ·Æ‡ß¾Íã´×Äý‘�ˆ;¼ùÓù阆x.£-²¼Dá@À•Íà-Ž)ëY$s*C ¥0Ü>EYâ"Y�R­Õîr†m¡‹rImç‚Fl·Û]žäsÜç}ŒEa¢7l„aô²4¶ÎjêG 4u¨ÀìÜÓOüí8Ž_zý•WD6š¢7adEŽñtŠó/½ŒGÞû<Í)îU[ ßóéÁW?ð>$£c’ÃÆqÆ ��K~ÄM†ž§¨tæû¤8#°ÒEŽÙ舚4s— ôWVI‚ Ðûh gc±d É`P�¼Èªd9ϤqÁÙ„¤_﨎 ; >A€ª£3»p†¢ÜÜp¦µ4äŒ9§ îܸNÀèû•£lÜ£²™TgEÆ)Ì,gD€§¤+¡)ÐëfÇÖ!�Ÿ2‘²ÄÊÅ‹âEž!ÏRÄÍ6¨_¦ÙÄÊÊQ¡,Š×Êâo4M77ˆ³Wúÿú©³8ýÄSäI!⨞{ìÑçz§·¿Z Ž:j ©c1½púâÅzjû̗ܹ~=ÚÛÙ!âÞó}t{}Ê*.}Ú§ãá÷<E³JœØ¸•â> š??¾v° l†²�æª3`  �QPð1¨Ÿf:‚-S€@TzRAÀ›q5DÅõ˜®‡�gQÉvXêŒJ© .lÕj†>³Ën*î¬:c¹±Ë`–%oB0ðºcPyîdšG$óÕ/‰Òù·_ûe#Z—ž‡áÖD{È<’slæsö ƒà¦*˜Ù>Ê,È·Ì�þæ&6UY�ÛÂd:ÆáÑ!ŽGÇH“ è ü]‚ÿÞqÜ$÷Î}û^ë]¢òÙ"™'´¿swEÂ~¥ˆ‚/Òq PG?{�¦_¨‹�?ÿÂØÚÚ~îæ•7åäðˆ~¹ú+4K&ŒxâåÏÄCïz7˜ì¸IѕͨƒŸfƒ×ÞÿýÈf3z,Ñ€n­Âú1{t¥¡MØa7lÊl†È÷hd³çyĵ»}v0lýâd‚ÝQõÿŸ¡ÕéáÌÃ�ÑõeF`à)2Ï$K ~¯�[s¹4su•Pû«¬Æ»l_¸ˆ•íSdE4�N ¤ Ï=té…'ž}Ï/ sÓEuüìš:’ùìtsã�fóù{®¼öIÿÎÍ$�-ËB (ÏGûÔ6.½øÂ8Zööx¾¿r’]c-e3WÞÿ8:Ú§Mp<c!"¨þ&¨”ä³RŠ%½Öñ–º@+òÉM#l�=¼…%  «ƒÇ–´Ê¢ ÏS‘ –VWmþ]é�Q–�•äÍîºÈèÿ.x6ÌʲàóºoŽy¢JVCYÌî­›€/ê; Z�­!Jc+f™à©š8aÃã26�#*Æ0:§ÏAg†%žKI–žS©K)yžÑ-J�éø ¾.÷.�¤Ðžòà>ƒx³ÓÁ¥4Ûl´É1B ‰ãÝ=yîܹ—Û«ƒ/‰ZM�:êøÙ4uàìùw=ó7µÕ_8›NÎ<m>Q³�Âx«+xò³>‡J#póç¹ÇÄm¨J¹†>)ún¼ú ´MÚÜK]b’•Ð�.L؆tå'!@\»».v‘ÍQ>XI:#� ‚~¡Ñns¥8Ñ/Ê/^Ü™¯u P0yÿª1·a+ æ€¸ÄÆä¿Fv…>Ñ ðA xyn?$s;K)oê‹Ù ã7)›lÄ œzøžyöQȨAïqRphØô*µÔHJKÏŽ cäYÂ\”×l"CNÊI›üý¥‹9š­˜3[18uÒu×ïäÜ”¬rttžS—†�"X!Hÿà‡ �¶7Ï�ý²2ÍZ¨£ŽŸ=@S‡²Ø>ÿäão¥ÛÿÊÉááf‘dð•ODn…HòÁê ÞõyŸ�f· QQp 0™Í]ãJJtZÌG#Üþ؇i,°çQÏZÃmøƒ-å¹¹ö–TRÚPéÉ›5ˆwІJuR— GáR#lµ¨„çxæXªe*¸p&³4ô¬:“Ÿ§Å¢€e®‰ 5ÁÇc>†AÕL†{z\�Q‡ ζ߹ø,d`Y2,voÞ€4y‘Sv±qê ¦) ”ïúv,:à! ¡Ûêgƒ°¦tv7ºD»ÛÃ`c ~:@Uô·‹MÐð³ ¤¿=õÔ$‹?9›aG‚Õ•5ú»/�XÐdkÓ=w–šzƒ  þ›ñî�Ü>}æs›+ƒŸãÅ¡@u|êM^aΟ~ô‘¿ÙŠš/îÞ¾íµâ6uüXhhde‰µ —ðâ—~%Ú½>¤Lr/�† b™À&n�àýßúM0¥&~¥,KšÄ(Ã^o�ÇHçÿ¥Á܈!¡_àÙb i‰4'uÛêÚ:ü p½#ŠKEzà ° „Ë4Æ,úË©—¹|?p6ýlVINÓZW¹æÊ—ÁNT€t-4›Ÿ6uºnµÄ!lôIYÄåTé-f¤ÓÖàµ7îâöHC¸÷•n$´³¼q\‹¦Å㬅NØÙAˆ^w@c ½²‚ Š\£©Aw0€×jcž$8áøÁšŒÇ¸sõM<�sÉVÜ+Ñï­V? +/<ñ¢fa£A×u´»‹ùlºñÈ“Oþ–÷QGŸÚ@S‡/½uhvtüÜÎÍ+ãã#$éÚj ×7°¶y çžyïý’/C«×wû›J“¡€3ŸTžG}ÃÕu\}í¸öÁ÷att€,Ëè׬;ÈÛ§`¥ÍYÅ2YïHâ‹n§ ãÆL¦Ìt‰�TS¦b¹b˜71†îsY�ƒÉl>‡{xIÚÌó`×[õFË@e-—–à¾#ÜK j`”‚ŽÉ%¼<Ïá �‡±Ü»‡ñ½LFGH’³TcnCxý-'§¦^ g?C Íâ WÚbwi›ÎÐ <4¢?€ç ƒÍm¶û/u‰$Yk)û€!76uPÌ\ }7 8 ëÌKßw»ÛÅêCÃó=$iBkïþ}!”|x”Î~néIñ`¡^õú‘–DïDâÿâ<™ÿÅÖ ÷…ölždh1�¨|ÒnaxîžúÌϦ_£°Ö-œ}LMya黲2D–¤øÞoú_È©,…@{~ÁêiΈ””Lˆ»A`”é¾¢ÍÌËæxøÂiô}ôzÖÊ*zý³:™¥Ê•rše ¶ƒ¡åyžó/‡†¥ã±ó²#4o¬p7Ƹ©–ì9Fç©‚Qõ;s‚€²:ÀÝòÉñæßéú²(ˆÏ"I³ è3Vø�ë=Ò†&�²äÙä LF»˜ÌF˜-¦0Vú~c�Ëhà4À'óÒ »÷vày>—ÈL%t`ÃvŽk×Þ¤É��àÔù‡(C ¥÷Ä,;5óÔJ0®w†V‘ç”IX –s_ ßœ9P9PbNF€]˜•ªŽw^*ıü™6`lcãn ѹ§£cìß¼ž&D£K#@AÁçRJ2À ÿ‡�ï81 ˜‚|æfÊ,û+CôV×h8x¸š!Ûe,Y céG�s^Ìp«prš¨ÀÚpƒ€„„`É8èÂÙó<¸Í‹œÊwÉx*V‡Ã3«Ãõ¯i5jZŸ2@SG§Ýf‘¼{ýâù?æ ñyeY\ܹ{E–‘ónÔhb2ŸÂ6T.뮬à$ùkÜæa‰+i6ZX[ÛD·Ûƒ’ä°ïÿ?ÿqSÿÄÁáZ+Cd6€lÃZé6ãœøÃãÀóø—:*Û W‡èvzTÚñ}Ýá�‡™ÉJÿJµ§‚ËX\J« p2\&$$oúîø—ÑxÆ¿ã^*”d`›æ›,Ë‘KwåU ¢ì�çûïß¹…|>‡. j•q^wA³ãÀ|Ë€ 𘠰+µÈöÖ0\YCàùh¶Úˆâ¸j\J÷…�@JA?Ö×7xв*w|–€³ï—ÐeµƒÁ*€¥þ Êj6y÷îí�Ò�¸š½øA°~áñGÙb4ÚFu¼ó�¦ŽTb||ô…g{ôo§ãɹÅxyN}"­N—6•‹?еsñž/ü4Ú�%r÷d�ˆ’jòÝÞ�F“3’�~ßwãöÕ˸}óY›DaD�™²½ #�K–—ÈË’�Gýwdvézh €Æx:¢•f)4xÈ–�E³ÝD³Ó)Î񣄮ÇGXL' 2•fÖê¨æ£ZÍ6�>� ¸�Áˆc*¡¥YJ?HÞüä'ÅêÆÆSëçÎ}ê¨ã�4uˆ¬øb#ñgŠ4}®ÙlmøÂƒ’Žë@)Æy†g?ÿ Ñî÷OüºgÝJE%3"ø,Î&öîÜÆ«ßõðž$.Æ °7J�7V`,¸HøŠËM,.À /›'è¶"(S’’­ÓébmkÍFc ü,*Þ€]ON¥4gë’.€AÈZ 0€Â]«Ãª©óÿÌípVÆÏ™ªúL°`�‚û„Œ[šÎ)jМîïAII%¾YZ@·Ö û[Ì5U§v²Îð5°Ê�ø›Å³Ã{ȲŒ3�B—0J!j¶àÒ±'\’å�žO„½ÖšÊgÓñ˜@,b¯Òu'G&žç“5�­²µ µÂÊ‚»½õïî!K’x¸µùª4ÔQÇ;hêÐæ+û«ÃßÑétžÊ’$ÈÓ e^°èöûè¯o ·±�g¿à‹ÐМ‘%)0-. )rcn6ÚDü`~ácßýíÈç3è¼MÉ´¥ßD¸z ªÑ�xsw}$DZ§YAª)¥€@›f™¶ &Â$]�C�FÄ!ñŒneòŽ á��7fÐó4ÆÃ�gô¸!ß÷]w?-Xæ)*@,à±äš%Ít­ÊÍl¡”+ù$ˆ287õ“$ÍV—ÄEŸ$¨þ6|פ)x (¼œˆÂÍñap%‰tyx —-*)·Z°hu»¤ÿ� (ëX]E–•”eÅ�&IÓ˲ ×Ö øsñAçåfWâëøøì�­qêá‡!Âõ ºŽl>ÇÎÎ] �ói2 uÔñΚ:tž”øq³ñ™�F3Bi¨ï‡õNl�?OóúýôÏ@wuX%Éyã/Sh6›ä7¶¶¶š‘â|®îݼ�[Ÿøq,VkxÊGóŒým”T.£^ ·Éó€1É�‘:ìoŸ>ýü|6}/ê¨ã�4u¤óù/V�øO{¾zIQ‘æ”P语A6[dz¨pÛ¹0ýº§�¦Ùl@õ+T¦qe'Z7ßø$î\¹Œéd„ùlFµý­sç0]˜«ôË–B€Èj�å¨vï [ØÛÙ…/Í&Ξ¿ˆÍÍSt¾êæÆc›«6ö°<�…ÎÄ9•‚xC$À`2�9–-Gqƒ³æ Ë•ùÚùê døK”•²„<òX°J ˜�Ç8º¿Ã}3Ú€LG›g!¥-—±-rã&OÀ:+GÚlr _�«+kœ{@à9>KÒcÄçXåLYGeHà4žŒÈ@ërɼ”Â.¥6 ìÝ’G[àrŸûbZ½>ûôÏ$u·×§ìmÿî=œ;áÝO~úË¿"Ô¶‰:êxûMÉb~¿¤H’Çîݺ­²Åy‘ÓexˆÚmLóïúÜÏG«Ûs�uÄ/8¯/.•Ay2X_Û ÍA ±HLJøîÿýß‘ÎÆð„¤žš¸ÝBg°‚7ïÍ`›]¨ Ÿa+ýnì0“ÛÆTÔTeAÄ¿§|(!1O°zê4½†Ž%«×Á+ù<ܨÉbhj$¤Í¼š�Î ª¼è]qÃI§ÙÙÅR³%N(À\�[Lº[Ë™g?ZãÍ~^^¢ÑhPÖadHÓGáEî\ bÔñïyäkF·]ðg¢Ò£0–£(r(O¡Ù$޽�ÍŠˆ€¦præFT*#�ˆ¢˜Ì5ç“10@sXΤ–{Žˆß Èò¦²=ð߬¿¶ŽñlÊ. óÉ÷vî†Í¸ñr±HÏ£Ž:Þ"¤. Ôëm³Î†~ðW‡[ÛŸ¾ººÖ¶Zc:Ÿ¡Þ[]ÅæÙsðÚ¼ô•_C`P©­WÌ%«—Z­YÊ4šþõp†€�|ïwa|÷¦³)æÉ ý� Ü=˜aÞÚ„�àÍWÕópW¾nÛ%QGžb|tD›)ýºCEÑHÈŠK³ó:s2d0g Âßð}V¥UmT8ØÛ‹Ë‡îÜŠåa`à ƒ-m–¯MÈqvdésOŽ�pxç6LI"è6:ðûÜ ê¹æNeMŸ ¥V)7~¢2H•)t™c±˜S< šnÚnWmý�K�fJ"/K€6ðÒ¸‚JÖÆßGÕÕšKjôÃÄI�—À?n¶ Â�úy†›4áÆå7pûÚÕ,l5¿ ÌRù`¡^õª.‰:Þ‘eéFžg×�ÃÏ-мs¸¿O�Öšºýëë(<Ï~Á“ ”eIKk}ÒÕ˜~Ýöz+d-"0ùîææì}߇}óÿ�ÑöviCSq„Ýã;Y^£ëTVÂÙ˜Ø qn¹Cžëûîyk mža3ÆÞÞ.}r#�²*.,(¤\å¸ Ë�«é½LPóQƒ S3§fÐ{ª“:ù>gb”é€ûvèy×ioiï‚€ìÖåOb~tˆRTÚÌŒ@8<ÙìÓóÒ’®�yåÐÌZ°Ý °<(Ù» ØÖ©Ò<âç¢0v<œYºaÜ lsc“z£tYЕyXËãª=Lî¼ Þà . ctÚ½J6d@€Öéà±_¦ kãÏÕl!O,æó­é|ò´-ÊMÔQÇÛ¯tVGDq7ÿÜ™ —>§Ñlwçã Š"§nÿ�3g°}öÐhÈ4Ú-Þ”xC¶viÆ}ì&-öº}þu^±(ÆÞμïÿüèti¢¨A\ÊC�?�¸·ô6`˜3äBì² çÎLÃê-¯ê ` ”vîì`6ŸÒæÚéõ©â0�jà¾¸ÇØ’†,—ƒx½ Ý}Á¶4Û?‚Üó°w°G@Ónµ¡„ì·{ƒu)Õ9ÔQÇÛ hêh5Û~·Õù­Ãµõ/}¿©+ƒUjr|èÑÇpêì9tΜųŸ÷Õ$,a^žéDQLÕn·îW©ô‹]âòß“,@�Ò¸àk§O£³²‰Y4„ cgðhx2&mPÆ‚úe>ôúL³"ç‰K [æTÈ‹ ÖuÞs ‹=³èò²n߸†ÃÃìíß§_äYA~lPR°h¡2ï…ËX´2�Îv„eeYºsòN œðCtý¸ñ„§|LVž±P€@ ô¹1Ðy4®š{WhMŽ�ñ±oÿVÊjJ­q<:FØ[‡¿vÚñ<äÈÌ›6 à2Q'�PR8�vct�ùäI2Ãl1°fhsG²Haa|0s–µºuŠ ÚŒ&#ܹ}ƒ¾·“–;Ò¥‹XÇåÊ%ìuÉq›ž¯þX;s–ThÍf�f›¤ìy–n[k?×ÓGu¼ €¦)½FØøÍAýÎùb:¼ÿ®°°èvºè¯¯#l´m�¹w½›¬?¤Àå î2ärY“ˆÿ­ÍmÚÌÁ]ö–K7÷ï=àeþ/¦GdOR–%5Un�½€q*¡Ö/B(êF‡Ûݘa�KKäð°3·˜Kñ¤€)JìCI�ºù³vw/çH&3ŒnÝÆ7}ý¿Ao{› Û­™�öV†ˆšMž{O%/h]PL² '@•màÎ[–›Ã0¤c:áR£eàªÎ¯á_ùîs‹ê9IñWut.Šœ”\ÊZhÄ�ŠÌ½-*-úžäcV6p2’Õhe©Ù]€–;g–h'DpàëÜ™}ß§ïR*I?ªÍ”¥ëó ‡°Ô¨jIqH%ÇÙŒ$È1É£—¯I*Áçpo\%]S¬ïcÐ�Ï\YÐ÷ï€rûңعrÇ;·C%KÙíöOíï}�-õÿ0ê¨ðPÇOk¬ Öƒ½ƒû¿©Ñíü–$Y¬Í¦E©ŠV··¨7fåÒ#8óØãÕͳR[ªíÓ4 dÜÆÃ�ãÎI“fÇàÁhæ�koâhwyšÒ¯þN·ø1îÏ‚‹gAå£Rs?‡€ã„xË©–Têa{§®ðÉW_Ãlÿ>òE‚RD(ßyõ¼þ¾ïÅ|6¥r]£Ù¦(yšÀ( XÙÚFu ÉlŠöÚ:I¸}—]Íç3��i–½¤fBACÞh³ö=RÙ‘dØõ�80s@€5$`À¦§œã3x4€ëõqÏÓs”üh@HÜ»~ýˆ~³É4Ý2£ùèg@6ºÔ i9gQHN¼ÙËJÙ/ÓzYÊ]dd^™z>f³)eMe–¡ �vw©'È b£ìQy>­BkÌ÷éï«Ë‚¾o wN%˜³2ÚV±jdÊÀ²¥é“üýèh¿"¡¸ž}âI,‘LưäQtõOOç³ßkŒý;öQÇOoÔ@SÇ|1ýü¸Õú �¸qJX!“d#z›(„Àöÿgï lI²~ÿ‹ŒÌ£ÙlIF0?¿'À�wÀ…幺®øÒ£ã‰G?B¹½ÍÆÊŠdÃñ¶”Í’þ~²ã¯ ,DRöàg†ýdò‰2«¸eHÓ$”pjõ1©6®Â©ÏÃðxÇÚÚŠœS§Ý‘R_QL¥T»Z'v�èv²ßF£) ÷¢,¨ÊR³�à1#Ï;­F ‡gnqQ$õÓf‹F–³°g.\§f§+ÚpIšÑìvÙwìó ‹d�\®A{ÁG4çÏŸ‘ è�ãâéSü¿ü§ WWÕ꣯$?úY.¶×1jN‘{±ÄŒ“IÚ(Üš­&¬ Ê�«Ì¯}�ík™ŽÇXšÝ3‹ ,8Ìoz+YÞ xï(ôx0˜•Æý'~òGøØü78Qnö,=Æ·ÿúßÊ¡£'H’Tuâ¼Ó´8|W%ÄîFÍ)‡æÂ…Y¹~EUÀpî™§øâOþ8YjEƨ.Kéý%Æ®non~¼,Ë¿î¼{(¹9¾)‡]ÎrnŽÿû£ßîÞÒnuþhÚÈßP”Ó^šfH#þÈQ–æÐ±gÿ��‚˜©£%�N·ËáÃÇ…ŒIÜG†:F>ó•/òøG~�µ+—¹vé’¬ªÍ– À¦¶�=òf°ˆ"ž<: £vÃ;y9¦JÑ}€’�!mvI��,ßBÝß‹,‰Ï�1V›–%ÕpŒÔ_žfCËÃþb:¹Íx.xÞ7·oÂí¾¼ÍÍñ}œÌÒô¯ì]Ú÷êN·7ë¼3ÓéRKknŽÛ^ó:�‹*ùRÍNéJ_Þ¿ÿbßk´L¦ÃdÂûä Öÿ?üs$ã1åd**ÊÏüâ[S(ÜGÕÝ£¼‰p\E—eÉV�¡0a�<çyª“cbtõ®�'öáW!̲ÄO·ÑÈb"Æhx‡›Ž0u‰÷��®S�7I$#Ã$"sÒj6�,¦*™L§t;]„ŒèjÉŒòFCÐV‚« �$7ò†d9ÏÚújhæ—H03'€c këÒçY:tT–ö±xø(í~�©h�¥\~ñ,ÿOÿŽ+§ž'Ër1ËÞM÷Î7Py#(¼4µÚ“I­Åã%Ë = ¹&põÑi 8M†Ì\ù"q"çzú+_æÅÇ¿Š+¦”Ó‚N«-ßm{´=ÚX½þ™FÞü3“rúa ææø¦©÷Ž›ãÿêèƒù#ý™Ù7T¾îÕ®&I¬Li¿Ï�o|sKKx‡w�Pb7‚ÄÚ·ÿ ”ƒ‚î–¼˜DÉÏps“'¿ðY¾ü³?ÅÆõ«$µèrQ±ä» ¿|’t~™�’U*×x&J²Toý@â]3|h_Ç&xèǖɺ2–s Ë1yŽ3R™Òh˜  Y¥CY—0Ú ÁQ ×Ã><Õæ íb(e¶ÒXœ3qK©•LÈ��* É‘TY_[Á{/Æ`£í-ÊbB¯;ÀO'”S8ÿÔã\9õœ¬Î�w¤í6ݹyNÞ}Åö6ådLše¤Èºs4öß*å¸Ô©¨¨fQðP”›²ø¹î;'úðù´ÕfulÈ·7pÅXKZyÞ o4¥ÔçœÓàå½ $ÌTÔ­«€ «ªJ Ò¥�V§I²À a¦�m‰-¬µ½AóÝè J9Q ÚQæÛé8xÛíB*^9sšáÊ5 6Æ@#ÏÛÝÁÌkFÃá_jåÍ?QÖÕ7Çÿþ¸ ¸9²f3ñUýýÁÌ[�F·®$Mi/îáÖ×¾žîì,Þ¡b;åëµ^./û–H�ƒzÜk®¬¯®ð#ÿürþ«_&5Füe“ÐiuèÏÎAÚf+�Íî'¼ö]dYEÝNŸy³Kæ%â¦e/!P6½ò6ÄX‘L4òTU,>ñ$>Aû=j×À5å»Òœ?ÉÔ̲äÔ¸,drf{ [— ·×m¯ŠŽq^©…¯¹víËòò^²vO`Þibèõz’ñlmmÊÄÝ뤙.0j‘tÉI ø´!Ÿ§Ñ _<¬ýï<£bŠ-¶iì«°“!Û¾$uÝÎ“í ªDÄHÙšÈÄËõµ ÑìJS™äå;gy.�'“ ãí-\U‹¡Xe˜=G0y°"ˆ|yª*R�FáÍÈåšr½€do¤Ö*¤1ÙÞæÎ×½�'o”ñ“)EU"äue�÷w&Iòœ«Ïææø¦殬ÉÍñ~´óÆCÿÏ;½þ-UUÙº®d"9|×½âïߟŸßEê3ñäe–å¥,,,ªÊ±Ê³€Ü^¿z™ùG‡k/<Ëd4è-)³4»}ÈÚLg�Ážc$Y®ÊË;YâÄü…æ&ÒÖ€FU; µÛi&–ØD‘W°¢Iب²jŒEÁΩ¯ŒÊùïBº%µÇåüC¯ÃZ!½vÊÉ„ÉPy3n¼I9aêJx+i9Â#ÊáuUJa}%’0½n›ñpM&KŒÅ-ÞBûÖ×@šÇR0ŠèµoÆi9TAš„ë&AÛÚDerÜú%Ê'?ÄL;ãÚ•ËÒ�;xä(í…=Üþú7Ûnõ�‘ ²oß!ÉÐþÑŸÿ\~âk¤6}´{ßþN^ûž÷ƒ7HPK3‘´ZíX¿L3U� ôß4à�Ó÷<ÿÂSj°®ã>�€/Ö¯\t_1�òÌg?�nsýêùm:_SWõd´=ü1ŒùÕ6¸9þçÇÍÒÙÍ1Ûô“ü›¦GIŒm·ÚÂy™9zôF�y„î`þqÙ鑲sH9dß¾ƒÌÌÎï�ÅO¬Ôæå÷KŸ~”_øWÿLšéõ´@ ¿0ÒêïYä…+Û4–î&›Ý'uüظË&‰l…6ïäudÕê¨4³ÈRɾX\Ä7¥âUõY3ïP¸µÓº¾¢äÒ<œ'8‚&ªÚ,@�ÄBÐP2#Ÿ±Æ DZƒ%òº4ÈcTQ™˜îœlÁŒ�$L”iY�zGâk\9¡o3Þ¼†s9™Ýd’h»�¤ÑÔþ”w烀ž†A3ƒ*];5)7:¨ð±¦×Ÿµ(\z*–éx,îškX¡=”`ת’ilmmˆštQ–ì9|X äF˜½,DšÍ–ö݈ÿþº@¯—ÓsCË�{ö,qéÒ‹QùÀ"®¿à(§ú³³Üu£<|êk_Å]»J³Ý"Á0žŒÞû·NÇãßäð˜psü¢–›ãÿèHyâ«ú×�ð«¼ñm¼1íV›¹ýxðÝï–¦)(\XÝ #¸¨º:t”ÙÙù¸š¡&\W.žçßþ�¿ÊWo ËF[›(Ëç¼4ýË$gØÜG~ô~ò™EL²�Á5� âÀPÕuì@€FQR±�g¨ñKðÉ3 tUîq²&…âêik ΃fm€ff‡xfP?޲:¼¢áTkŒ0’]Æpq¶eÐRŸfJiža³Œ´ÑÂ6;Ðêã;sd­ öÑU?FeûÕ»%Sj?'µ »›×`¢àBøœž³]Ã>5@ÔãWŠo÷ñí0)&AÞ¯åÁ([õ|‰%�v”Ñ8�²Ì†ç�ÄR¯_ÁL6°bTWKùªÑêpðŽ»èôû`YŠXs«ÕæCü÷œ{òkb-Ðít1wÛC¯¤Õ퀇²*u³ÖÒl´cQRÙßn¬|l2gÂ÷ñáµ`=Ž åH S‰¨Ê‚$±r¬ùå}xk¯­ ¨b<™$µ=çü=ÞÕ à3|c$ÚÍq3ÐH0iúPMk­7�uÞj¾ÉF#Ëfò¼ñWÛ�ÎYšeµ«ÉlÊ]ï|7Gn»]'�hDÏ) HÞû–"�_”�Íç>ñQ>ø�ÿO~ü#¢}e�!¥)½4z –q½w vþ€ôhb¸™ŒúàÏ-…h„�3kˆÅþýa×I8"$Чf:~G¦á´¬¥Þ:øè8ì¾^*íŸèä(#.×i°3°[�TK\®ŽKy»‘1,Ùh´‰fARÚóQ)Ï&ÀcRý¼�×[‚hà¡èõsFn%㯟¥•YÒ ”ÙŸßá;ïÛæX¹ºÓî²~õ*�ýØ¢žL¥¿T»šîüGî¾› §Fƒ“¨2ŒF4šM)¡yÚ_ÒÒ'šÅ€íéßÜH<´bnW8¸Ñàg‚ª•÷`Œ åæ––¹võ ÅÖ&ò›«�i4›Mïý­UY–Îð9=ØÍq3Ð,Í/.Íôw—Óé÷ï¿§O¾¥5|Æ|[wvö}Ybß^ŒFï2Þ?’À]ÖŸYÛJÒtýëéÑÏØÚÿ�IÌH’n–çÆ&–ƒ÷ÝϽ¯#6ËâÀ¢Öºh> /ŠiÙþ‡h6š˜Ðù¿tîE>vÃógþñßeóòE\]†ÒT)“éÌÒ2Ó¬OÙ_f²xig&öQ•eõ"Ù‘I ÃG}Ui¶aBÜ%Öé�¢Ô�÷&:+‘¦jÊFªŒxµ’–Ï*ª,Îd¢æ¹Ù‰€ 7åÞH0 ž5*þú„4 iß„ º©Mw"Ö’öilü]=¦ÝÎ’œ>O€y ‚K÷£Y+š°"ûBV£eÈZ¾cdyàåå(ÇC Ð }¾å[o£Óëc„$%, 4ŸºQB}ösŸîÌ´˜P×µ“ž¼ïl–ké¤Ô&‹˜éd"@áE¤ÍÝ®¥ˆ¢×‚ µô�F£-•ÁÜ­Õ¿;Äî`ÿ®]¼Èpe%ð}ÊäÆhfi~¢ßî}Í6ó³Üß¼�¦»0oLQÜg“ä7L‹â×k¿mùÈ‘÷L'“ûkïhµÛw4óÆ­™Mïhdù]λ;Ú�îí6Ë^íªê=óæ·o2Þ“À&°õ‹‹ùjrc̪\u;ƤÍFŒá•ßöí¢È“ßv¢}´d&uöC�’uâKgÏð¡ýO|äÇþ#g?ÿ9ê ¹Õï „3Ñh´9xëmøæ ~ïmäûNâ“\WƱ§ˆ:B4ì4‹åfj…(+ÑPƒApi”sWNOd�ì¢~ŒÖõ *—hæ2"íïèäÊ}uh´›PN+ƒ“'ÁG]"ñh£’U]‘×Ϋ6:a&Z¶ ×-ìG–"è@¡^/&ˆr± B£rËiyL¯¿f€;hA[`ÎLW/RmŠVœ(Ô®fÿm·‹ªu8éyl¯­ó™ŸýI‘ØqU%Ïõû3Ì-í“ (‘ŒÈI†¡¼¼;Û­Pvõ´L|ý Ä)"ØÜX�‚*ö‰–cwZ8»ëù>[×®ŠÄŽsÎzèÅô�uYMkü—¾ 3››�¦“fÜdú;Œ1¿#Ͳwä­ÆÉ…Å¥CUUö¼s­™Ù¹6Þ7˲l‚o8ïšÍV«í\Ý»qÓku:³&µ“$¹Ýó.œŸµÖOþb©Ëºª|½1æ÷v;½V#o˜Ú9¾êU¿ã®x5{²ÇωBñÁGElk}��ÝGüÐø7<óéGq£¸Z2˜FÞª$§hö)ºËLçŽã;óˆ\KXÙ癕I5žÔ .™i?A›Ù»Jy:ùyÐÉ7‰²¼W^O�Ö’ÙnòŸc”ÎOC*Ï!Ç’ss`Oü�‚Ä�Ñîtå A¥(«¨/¥pò^[•üÑ`'Ÿ�œ@Q“4ÕÃD’\? ~Ú'Âè÷OCvŒÐb ýÞ%ð£AÙ‹!PFyñY)…yï„°¹tâ¤èã…¿“  ŸùÂg9ûø×mnH00YÆüáþýNÍ,ê`É-[…ÔU…Èõ4Z± š.FbáͤÞ,ÁkZL%‹òÎíô Šûdµ@ȰwÜÿ—®\aåìi9¿n·›6[íþx<º·.Š*i·>ÇÍñ‹f˜{Òü·ÇQàïÙ4½§7Ì5›­¬(‹ ¬èd˃,†h EÒânW�ÝÕ›¦¾˜Lªñh´åêú…$±!Ͳ9C˲̽«ÿ]«Õy_£Ù´“ɘf»Ã;~í¯gnq ÌnTñŠ_š·‡�ÿ–/ß ¹=õ©O°}ù²�1†Ñö�ëk+r}MÚ í-àû‹¤Ë'ä¾±�?R9±ö™%+õ(˜í†/ƒQ$Füõ!òÕE6©èf³!iE”‘M}�Ì�òº ¯%q‰IÎ3*=Io tÚ§‘çmbv—å{…ŒD3ÂMæc€ƒ1úº^o�ó ÙˆœOYVz\‚ ƒ×¾‰WuãP`‚ö´Bìq�f‚>èð<­¦×Ú€B¿ÃP¶jë:£Ïýfº©ñ•ßñ�<üæGð ÿw“ÍM>{Cnè+ûÔ.¸�zßuoÿþÐR°f/8ç¢RŸd8R–[ܳOöGTn4µMˆ3–ÝÙÎh4äÅs§ÅB@Iž åN’iÙ4U@Áüü"­4çŸý•?Ã…'¾&ý¢°ˆpÅtza:ÿi“¦pÜ/ûaîýo6OµÕí¾©Ùj¶�IÌŽIÊCkf^à“¹7T£má~ØvNåkaWÓ©°ÖEê]š¢ YõuYUÅtºéj÷1ïýov†Ë/G˜£¯ª}À¬µ{“45©µzðaÞø­ß!«7 ßXúŸ`•›·¸xæ4_¸aN¶~þEiÔ&IÂÜ`–Ñt"°åÍÍMÑ×j/Á.�€™EHR Zq‡ ¤†½§ÕÌõµºv:Q¤Vm´,ˆ—!HU¡l‚`ÓD‰›a‚VkdmrÿûVKi tQdÐÏh/ÊŒ¢`LL ”-ržÚÔG3í÷@ü]ãsVÂ*Æâ¡‡Lú9ôº1ùSZ��‘@Odª&#†!Ûd'hÁ9åe©a¦[ñàmC._oðÕç[Ôå”í'>Š»ð&€î{÷{yÓû¿]&íÃ>ú!ÿè‡Y¿zY ú{—ùžßü;9qç=\¾r‘Éd¤‹BÔ6;Ñ>a£Ñ”mii?6±ÚÌ×l�¨ )ç­HBD8´¬¸pñ,kR6s‘ù›æ7;䌤„ÆáCǹtæ4?öOþ—ž{†º®ÄÔZ[—ÓÉÃáð_eñw¹IêüE�ÑÌY›þ“F»õÆf«9Èó&“¢ ÙŸaéè –NÞÉɇ_ËžùEæ7F46†´º=úGæÉfZ\¹|AtŽ.Ÿ¿ÈÏü§r}ã:+—±x�>ä�ŸN¦EYL/ÕUõw’Äþm`ÈËh4òÆ/ñð·›´�†i5Ûý£ÿ‘j<Á{bxÃ÷~€w~ç÷Ëßk}c•µµk�½½®¤�Jö† fgæ˜�]ˆíÃc]‡x�`÷úœ¨ƒ_¸pVÈ b- ¨yŸsú™€à“@933Ï�ý‡¹|ñÿâ/þ)6.žg<b¤o–V7ÆÙéxüóιßûr673š¼Å7Þÿ‘î`æ7eyÚsÎ0 –r쾇yè=ßα;ï#owI’MKÎ]¡1ƒIh�!�ihm·XŸpíÂ+¹á‹Ï?Î/üëÀÕ'¿‚F7‚¸üx4ZqUõïS›þíöìÌS/‡†àl«o¦“ÉL¬ý]e]ô †¤Ñà�¿ä—JÓÖkBËÝb¥¼rþ#¤¼v³-èŸÉd¢f`Ó ßèÐØwùÞcä½™]<´d”fB T¹ú,³±Õ²~&ËôhQ^QC>"/F% ñÜS… ‘XíåÄh+�TÕ8-š4*[±‹KØ0ºÏ¢,ñ ‘VÀ…P+_§¬j !`hŸ$ºvq9ONèÉDÙ¡–å{ª•vhê{éÍÈóòXüE©öuXÕ{�‹ŽÌÂü âàÞ)÷ß¾ÎÝ·^gavLjkQUøøçŽð7ÿÝa¹FòÝׯ1üÌi%€ôCÞ÷½Á ÿñ/ÿ9ŠÑvè+%gŸ=x˜_ó‡þs{—tÑrùÊ Pòð÷t’A]˜fr –—�gM«0n’Ê®#Ê8kç¥bqùÊyVWW�@ Îì€n›è³‰ô—Ž9A»Ó嫟þ$?yº¿±rUÐpæíóVs¸µ¾ñcÅxü»�‹Ü/Ëa÷™ vo·%Iò‡;ÝÞ²µiRWƒ¥¼ý—ÿFÞõ+3ËGO’ ª*Aêö“)��!I(CäóM’FŠ6ŸÇn f~ŽÅ;îâo?ËwÜÇùsg^¾€LIb²,m{cî®ÊòuãáðÉ¢®ÏM§S^Ê›õÌxïþØx::°¹µ‘N¦öž8É‘»ï‘ˆ /�×ÕË—øÒÏþ,ÏæÓlÞ¸/þý‰¡Ûé!ò1‰e³pY�Ö�;h»Ÿæòq²f[gøùÕ•tš&H;°Í#£-55-„š9Hƒ_›Ôº>ÐýàãÀ&7Ç/šÍŸn´Û¿©Ýiw½ó4;}ÞýÛÿ0¯}ÏwJ*:ú« ®¬bËŸ&´ŽÎ`»¹ZâN/™¬M¹~h‰I»¡Á•ógøàßúó<󡟤ÕÈ¥Q–%Óɤ,&“¯xç©}ì¥Ü·é·{·Ø$ù‘$µ'«ºJm–s╯âÁ·¿3Ôækñå8ûìÓœùÊ—Y9u QúÝ~XMJ/…iUS5gI�àú‹Øî ]YF�ôÀ5›ˆI‹„LÇ3´i¿{èªS—ôu´ A‡,|Þ uýHÝ™ØØLyšJKc!³R“5m4kÓ~œVKqñ1ˆ�oÆ(ñ2îŒÈ¦*¯ec a­B�H Êìä;„ �W•„ø½;A¨òAísÝŠ;ŽmsË‘!'¯³0·E¿S‹ÿ¿5>û•ÃüÅq‚a€5žúùO1}ñ«ä™à‰afa�•+—© 1ŒãØÃ¯ä�ßó9¾[²HÈÕk—�·‘¿sí"»n9çVÎs³ ìY\ÒE@„Гûq5M-°¹~œ;wšõõU9¦SÔ£ ·ì€µ‡cvn^Ô£¿ð3|ä_ÿS‘Ñ�¶eqJpcjÿ󉵿8ÏÍñ²vɦģÝê ÒFãÌïY8ÖjuL]ל|ã;yß�l&o4"iGínÓ»¾Ac<ÅôN¶·C’%˜@Ð+¯nãJÇöL�:MÕû¾3˜áŽW¼ŽüÆíé¯}‰éh(û³Yj³¼1SWÕ}®ª&¶ÑøªwŽ—âÈmö:kí·´Úí`í6{�gÏ¡CŒ‡[œî™Œíæ¹Ç>ÁhuUuR¦å c,£²f;iãö%;t'ùÞ#ØF¸"aâ—‰,KS¹M¬6½ña‚ Ín7G])£¤H倠e" FÑþ1Fá¶„‰ôf—»´QÍ.E}I@1 P–¸ «j•Ó©k=?5ZÓ^’¼íµx¤¤êÕ*úILŽÔ^•Î…1ZQpeåT­@ƒÌN$•ŠeÆ"¼W™™™žáľ)¯º{�ïxÛ%Þûæod-ç8yd•¹Áˆf.%âÿæØ5ø—?qœkk²�“ìúã ª­U¬¯Ù%¯7[ÌÌïyœßù^Þÿƒ¿‚…¥åГ`­<Œ¨ƒ¿v@LÒU‚®^§¢(äï‘7ZH0A;ÊdT”U²YƒÚ?§i&eºÐŠû:hСç!š~ÖZö>ÆÔùgžÄÕµ(@;'hÕ,ϲe.³¨æVTÇÝ{ð(Y§ËêÅó$u)?T“›çù\U–w$˜ë7î?ùR 6Æ3ã¼;Nbni4i«Õâê‹/ré駘·B³X­T¡ @Þ!™ÝGvô>fnùÜAL¦^îš™D“ ï Lv ,*£ˆ3´™�ž“jx9…þjöéŠis>d*öh  ±­êš$"=*¤t_F{B*í¢`OÌ5Q  Ò|—ÏéEŽQLtZûH’$6Q´\ðÎÑìÌİfçtb4˜­H=üNY‘r´“ëûÝ�\á�¯ŽAo‚µžÿÕ1™xþÕ˜áòJŽ+&_c³FȸBܸL槪.Ðî xÕ�LæØmw†ÌXEQw˜…ì-•LÅ9'HFCÈ4­ÕLÆ Úk@Ç �FÊ«»È«¤h6c"îÍöö0”Ï\4_(èC7�[!£v»¹ßjw8rëí\ºx�ÉÆ:ib±&‘óo·Ú©óîXYíÚÕ�¾L ß7Mîôz?�åY.·Kûyó÷ü2qd ,ñ^ú3iQb‹Š¤v¤s-ì ©^nkŠ[›È*{®O¥�Ù�%Ž4M9të�Ì8ij_ü Åö¡Iilšö\]ôÎ}¸ÌKhxç–«º|µmä·TU�o®­ÒÌrY‰�Æ#ÊRJŒ'cZ½y¶LƒÆ‘ûhˆÆü~|’¢–!Q‡ÃÐ7ìÔÞŠ{±ä|YU´w§z°J¹„ý¡RÔ‰ýâaG$¼W¹5H³™ð>ÔC>v�Qe £ÖçTçKÉ��ä©AÁ*È@9úz,cS;/Ü!)Õ¡× ¼Þ(ò-*óh1ò‰Ñǵffè5¯"“¯ŽòK¾í4ívÉÿîxüËÛüË?ùE&g¾Duîqª•s$“-ŠÍk¡˜°40ÌõPÕ2§�¦X'îu’d"•‚éTP�Ô�¼‰~ÿZ½nôºViTôšëïCû.»�yÖ ªJAPîÌ(MŒFŒ‡¥NGìµñ!kÛì—οÈêÕËRr®« Y´¥YVÅ�Þ»ÌÁ£/CRçÍ@3›7÷ÕuýÝ6Ksï¼¹ímïãÞ7¼]Ê3>jƒôez+‚6KåŸÜcgZ$ÝÂ(/l"¨³÷)ÊÑ�૞yï÷—EqïîÿW}ÖàçËñä÷{Ïï¬ëê;îž,Ë;ÒW ÐÝÉtÂtZ0õ �¯ yü²ý·bÚ|¢&q6±zœÚ4Æ�rpÊ¢¦¬je¬W•Sƒ6E¥)ú-(Ȧç£å4E )0ÑòžzÛx¹†ë:iF×UaâZnáêóŒÎ~…låi~íoë17Ÿð�†Ã¶6×®”<ûÔ˜/nÌ'><æc?7â'þã?þÏ7ùø�¯óåO�¹z~ʤ#MøHSÓlæ4R¸vþëׯ³½¹‰ŽÝz‡ˆf.ì; ò-áò†O=ÖdmýVêô^ìÌŒ¶2³{Iûq¸K’f€¡*+‚ˆê²‘[$ÊbÂÆÖ�=ûÒ›™LLÀ��;@qh V•üf5@EATOJ`i– ´:–Ú)ššÄý6…NoooE%ÓÝŸS±¾'Ìêy[›Ðêt9xü$×V®qùEÑT#@âMše­b:=PN'�«Ü/É‘²û ›Ö&À�7ôæƒ$E\‹�!¤Æ`œ—-ée˜Fª"„õÊ62lBÑiFf7Uë“Æ&¼òî‡Ù|Ï÷òã?ýï˜NFx¼i÷:³“Ñø—“É0ɲ¿ô›c“6¦ÜÝaÓì·šÄ|×ÂÜbÇCº²rçÝNŸ~o†kׯR§-:'ï'ßw ¶ÝqƲ¬ ªúQ~wV':Xú|YV»Ê‰B–CÃ^Ñ<Á/_‰{NE…@H@ )çÄ9§®(µ)±¿AzCIý½”›+T+g©'CÒ¼ Í>­}ÇIò.6S'æ�¬ñø9Õ>saòÄ;Íîò4ÕóVok4�ù(@E#öÄ‘ýE©ü"AÀ±®"ªH2¯b‘5Äk’åyêµËtçiÌfœ?½FN;åyûPÅÙÓ\9Û[-Æë ›×*î;Á_óvŽ>p˜ÙÞ Y£©ˆ=Ÿç�Ê79Švƒi«I‘Y&͌‹5×.¼Èų§xòSåôç£oߨÆÌÍ/ŠŠ³è‰ùš«/HíÓ?ñ½ñö9Æ@ìvýo†-R‚�¬a¸½%�ÃZ+ÄãX¦HÃEbt‘²ºz�FÞ”ÌPã8$ ˜Ü�mrÒ4“ý‡9¦º�­ú¯[›òþ(¸!"¢  ªÒZJ¨©.<Íüè¶žrõü9ÒÄŠ\Êé'kž{"¥X?H}½ÇL~€·¿æÛø¶G¾›w¾é½?|RtÚ�¹MÉ+ŸM1²5<´*GwZ1»9b~kÌâÆ˜^噟[àБ“Ü÷š7qïÛÞá{&ou˜ŽÇlm¬K p03'žD�,´Ö¥3/pæé'iõ{ÌïÙ‹&6ê쩚oª‰V–…¢ÏêºD3ÐkbÐT“ªªd?Íf“,ÈýE Z@~_Hð‘c«%xÈjTÁ:Êžv+™+ê’n§§˜Ü€p‡Ÿ¸•ÍÍ 6®^¦ÓiK ­ª*&£QË{ÿ�‚›ã¥hîj´­û: Ïîêt{Y]לxÕ8~×ýÚ,5„Z{]Ó¿¶N:-h³‡toW‚�IÀ­Op+ÛÃ;1l/ÌP5´i‰,½ÑûúDg}‹®3ÜròN.¯\æÌég)«)UY�ïVeqÂ{÷p‰ÿ £.‹žwî·4Z­ï�·÷z�4ù… Ùì’Ì,a÷žYÿ–‡È»³$*¨Þ±·‡�$’òP{ÜXMvsP´ìc”á"F=*ïŸHã?‰3¤ÀIð6a8?�@ã=DHZ½Ýtçè_]ǺZRö“‡NðÌ©g¸víBžË3›Øt]VG“$ùœÇ_çÿà0išTÓéwfÍæ¯nuÚG\U›¢ª°�Í}'hßò*š‡ï¡µÿ„X'g�'â‚Âoц¹M­®C–²k‚‡:”5$€Š$z‹g*YQW©uìibÍ”œC]ÓÄn‰–‹H”o`"”›¢Ú¢?ŽÑc{c 5`sì¸üÂ3,t36¯œ£ÚÞÀtçB°Qï|�ô•ã¡ü…-«#¥g¢¬vªÛæ¼ÊýËç]ˆ"qO@¿[è&Å4r_Ñ{uå´´–Jà–chFÄtHëêS1n§RZãêÕËB(Üä¨È³Üqø¾ï½àÞ;`С8vj�¡7‚Á@R{tįò¿¤v �íqÁެͽNrdïaί\ÄäM6×®I©Iz.£­v›‹/<ÇÂ~ 6¨šÁ®Zµöï¬ذGÀzLé]퀪{U×X†¦:µ~޵«"_t%(ÏÇ<µâÕ)ªŸ¦+Þ€v³!`é1µoâ�ž¶Ý£ÎÚl\<ÃᎠ6Î�N�í ðúÙ:*á(ÄVû0V‚�Z£ä¿èÉõEƒ2 vp¢R½Ëã>8t&þ&"3-ÑD B¹ÎL¶¨O�tû*¥ô1’ÄŠ$ÿž™=œ¼í.Þ÷­ßÃ[ßòföï!_ê’Î7I;dË]²}}²¯ßßÓPL¶Ð&�k ¯,í7HZ¦™A3•ÿÒ£ò;Èð!èxkð�°¡ÇÄ£è®Ã·ã‹’Íñ£áP^sUÅÑ'Ø\]£;7O§×W7ÌØ¼ Œz!±Wù»Wu¥AÀÃŽÞšÑL²–ÅSS,®Z Æ�¥!N œaÿÑÇ‹�áõš›[�û1!T^Æ0·¸—sÏ?ËÊÅó¢‰VåÔUÕ³ÍAï¸9^Ú�f_ÞL|U?ÒévïõÆg‰Í8ñÊ7pôÎûv”0ï\^“•WV–¦›“îé"å‚Õ1îú¹o E«ÉÖ\Ÿ:µDÕŒ�V¬è°µ£¿²†-kA´¹4eqi™Þá=<þ•/RLÆÁÝ|= Þnœ{xšÿSùƒ‰µßÑj·Ž–e™f�6ËIfö“öæq>ê�Ô¡ä ¼´$ Ìî l©�¥1Ð= ‚–¤™U'Ó¨®­MWý'Ô}D#>º“�–À“±Á1R½Y”'ãœÖ˃<Œœ³ž¯itH:s¢¾=Ó¶Øz›jã:Þh÷©CЈÐ\ª€r~r­™‰µ&>žÀ«}Y`Ê Ùö*~ýÉú’µ‹ÔçÇO†ÂG2�¶ì›€4«#[<�ñ�åi˜@¡×n:føÔ'ȶ.Òndôzý0ñ{Ÿ<Á·ÿÚ_Í{~à»9vÿ�4–ûd{:¤3-ÒÙÛLÛkb»¶ß.™í5$ÀÈ{æÛºå{:òÙd¶I:+ïÃôrAlj%\4éoz‡©‘ç‚dø‡—�0™LÙª†´ÛÉlŠéD|ayÊ¥3g8rû]b¼óV´4 ཌª*euXÕŒQ ãh·Û#HHª»‡pp†ÃM9†òuð»J« ‰"¬´ƒ4µÕÞë¿!ó$6ÖY–™²ªF©±çËéô'^✚›�fÁù¦÷þ‡ÒFv‹I’´,*nyÝ[þË@ã¼ôgòI�q’ê“.õäÌx¨ž_�IHŽgÜo³±0KX¬ìú0®­HËJx:Rz )ë6¸í50³¸—/<úq¼v“fYg:�¾Ò;÷ÉÿÏ Û튢gŒéZLß`À¿¦Ñn¿z0;³ÜïͤËûÐé4iLÖØ¼¾ �Y™äå±à! åm¦ªÜJT–2Ä:cB¦$²ÍE3UÙ�{šÍC?RÔÕÕ¼6ý•í®ŒqçÄO 0ºÅ=§þöZ§�Ë"l°­6uÞa|ù {ç4-Œ.¿ˆ›N±ý=®KÈ0�¯®Ý&�ZÀ•L†0¼WÎ�]}ŽäâSpå9:Å*Ùh¶×I&kl®¯H¿Ì¤ loŒU=ÞPÐ9 µ®¶‰ +tG9™Àåg±«§éw[Ì fIÓL&áÛî�ïú•¿’‡^óZÚÝ�ý µˆ™Ê}_:|Yã 'è?_Ô¸Išcšèµ4y".´¶“cpÈövÉ–{’ ¥{:ؾ $ ’ìF¹? ¥¥”£Žqíê ®¾(Ž¶Û›[L¶·¥÷ÒôØÜÜXp’¦z|ˆú&d¼µ(;ãAàαæ�ý-Ë¢)Í2¹ßl¶B¶*)‘rƒj…d3Sq‹-5ƒ‰ÏigàV!p&I¢}ʾ/å¸Ó_ùR "×£ÑææÙª®~ì%®p3ÐìMì¬1æ—å�Æ~¼O½ó,Ÿ¼�Û| ‰µZ:k Çt×¶ÈŠ�6§†ìØœ”ÜÆXD4�÷È f÷;»ƒKØŸŸ¸Î+@€öú6ÚL· él“|¶ÍÁÇ9wåÏå+„mb­í&˜Ò4û|jÓË76„øhLê�·Þ¹#TÕ­Y–ïsν²Ýêîïµ{o-Ëò‘j<þVkì/3ðíI–~_«Ýù¶òßõÀý÷/.ïk÷»3Œ¶¶h·ÚäyÆÆ¥Søášô%²fENÅ⪮µÜþiµÉ‹T™÷ºÂV‘ÂÄ”wM.¨+çÅ€–Á\ØWÖ”÷¦êñ˜HáÙG’ š@´Ã¶±ïµ§B®y§‡³ êÕó“mÒÄÃöuŠ­uLwÒL½öã CÑMuE±q•é…gpçŸÀŸý2ýb•Ñ•Ó ×.1�lÈu�‡LjÇvÚ¦š=„Èø¼;³W‚Œ C,»#Õ4›Rr¡:aÖø‹Oá.âsÙ�Ý ç; $«Q�A”%_½|‘/|ègäïXL'Õd4zÁÿÿgÜ/]M~Œ&^ƒ§TŒ=ºškolKïăÓÎ MÄÈê/�Áb‚�í¤ÝÜ­ ÃG€côIÉ–¼Md»¼×¥éù~åo£¸ºÍc�þ”Áf«••Eq_Y”ÿ¦œNÿzšfϵš­c‰IöM¦ãÙétrûâñC‡¯�??i¤éÀ橵�,_ž=ØL¬M[í–Á$´Ûí$I¬ovZy3o%í´-®ƒÛGk>õù�²qõÍ,%­7Ù|úÔG �]Špƒ c-“»J¢ìzô¡$f‚U²x·àÉLè]$)ÊE å1ë “½6z•¨†+GïG5ï*ô‘t¿ aÕ Œ�Ø4*€¥[ñ_6[ètryFb§¬¿ðIÊ¥;h.Ÿ„$Åû’rý õÅgéN¯³8Ógkq^jø®ö¥g„ÅÎ%ßs˜v^¼y0¡|b Á(Y0ö³‰-ä¼*9h)QÕƒ1$“M¦§¿@#©å³ÛÃM½9^óºGøÞ÷ÿ½vŸs“Ì]ô^cµU¨¶�œ!¨ xƒl.K!y�ÀS9 j~\¢2@¹E²švFÒÎ1 +A'É,¦™†¬'#mbô\›“Ë=K}®ÿ�Ëp ΟzA~KåhÄ¥çžbëþ‡ÄúY‘Ç�ž:�NW‚ìÖpC²ài1ÝUÚB«¡Ï"Zf‰µln®ÑïÏb"„a,Òꂳ×í (`<Å¿ûÝAIm�êªo›Éd,äO´ä\<õ¼T \-À{ýò•mnŽ— m ðþm–-8WÛª®Y{�'éõ¹ðÄ—é6ÛRs6uÉxý:ùÜ>|ÚÀì†ë*rG2%EºP扜u5hØÙ€wÚX•}È}�l xÅDuo£“¯�Ég]ÕF�a â¸x¼ª›pþ�,^³jj÷² �f°H2Xfûò‹dIB»Õ`nfÀtí² Æöt`®ß“Àyme�Qáañ’#÷“ï¿MÊci£%ßK¡ã Çw¡œ§�iefêwPKéPŽ”rY(»Ês®®1+g˜›\eïÞ=„Ùfìßw„üÀobiq~7ypÖÊm�¥‚¤,:M (ަݖÜwV2pÉä½A>c È49Íx�ŒW㌾f4Ú‡¸Â êI…Û*�ªAép¥Üâk2žTJÊÏ>ñ8­V‹áÆÓéDaéGn¿!œ²#Ä®ò¸Šr*ýŸª*~ ‘’tlÄ$ÍjÐÐ…!,~’ЛH0Œ�mL¤®ç'÷©ƒÔS–çz½.½x–ÏýÜOSbEàGÃáÕ„ä?¤6}ÜŠ¹^´Ù›Ûÿ÷�h,Ù¼k �tgfö/..¥Æf,>Îí¯|�ÔXmQ±÷ôÄMÓ{DDóؼ�4ý¤¢:·•C¥ßÃÊ¡%ê4Ý O„o |0SkÞØ’Ú‘„&¶íåä m¹_ Kª� xWÚÇáW¿žþ�ì_¿ÆöÚ*ÍV—îìí[Ö0X>À`ÿ!^ùm?À«¿íûyÕ»¾�ï|ûwòöW=Âýw>ÈÉ#·røøqæ/‘æ¢<½>UÓã<“^ßhpø¶»°�._üðO3oËj¹iÄ…×w=¯‹ ìúál�­™«Ë ò{kn�iŽ&t×·°Ó’²m &””=†z«À¯M°�„´›óæ×¼�'¾üE&“að‘r<÷ÄW9qÇÝ€�ÝYC 5öRþÊõòª '�SE¡¹]×À!`+=é©$!‹íÚ©ýx·Ûc¸µ)ïs®P cE”ÅWª*KÑK·ä¼.ž;Ë©Ç¿ÆÆêu¤Ôë\™g�ËÓrú7ÇË€GcÓ¸vaáá¹…Åf–åt—pß�,av8föâutUÙÍ «–Q‰?»ÿGÉÊëú¡%ª,ÕÏü7Pg’Ú1seUn•!�Yšú$Y"µéâÊ6ˆ –ÔËÙ\˜%~ãýcôV6"ÚÚ£uóÚ‘v3$›ñˆ”ŽXQ'ê°ÈöLÔ°/&mOö1F«²êª©FCš‹‡ kî,át¤¡„&SðìP4Ö‰4Ñ`5Z5›QÄ•¼Ž…k?Fö'Ÿ æVZ ÓI\½ZB£W'W”Ä©:d€JóƒêŒ)Ñ/ÔÜ…îMB’6 ±TÎíà!Mg5:Ór]@mhЋ¡ñÞ9­ûûÝây€–'U \·Ñ&ù¹/³gnF%X²Áoþ¡_ËÒ±“»³—o<´e£�E·ÿÚç“�¸å?ÅCƒ, j挻m)ÇMºm¬óÀ1ÎkÉØë:HÖÔ7©i'M¾úÔW9wþeUHFÒ^\æ®Þåÿ¢ }4ð„×B�F²+%ÞJ�%;3^ öˆ=´ôÄJ‘ùϳFÔƒ‰­žs¦!S*ËbGf¤¨2ï#Í4‚ç¡Ýlñ¡þ;.<ý$ÓÉXx?yÞºÚ}.o6¤, ÏÍñ’4ïïmu»¯Éy[V#y‹W¼ó[Øs}‹|4% LõôȬ ÔgÖ¤a‰Ñٜͽ³l,ÎAlQ“Ý� dE)ˆ3g- •Ì 1¸qI%@ D gûøT‰eÿÍõÊ¥äÕŽP³/çÁ“Í´¢¢Ú*” ‘ԞɠCm­îcnq‰Æ`–§>ý ªÉDV€¦ž2ÞXÃÌìƒ$U©n-= ÏEµi­q{v ©"9{Ý*ýžì$¢Òüž*d=†úÛ@�œ É¡f®ÙLȆT ¤É®å4ïw Ý4£ßC'Þª®e‹Ä-5°`ÌŽ”SÖ;(ùO•Tý ºž2"PÕ‚ûÏì]”,[–Ý¡dÏßÚ¶4¶mÛ¶m£§{¦mK¿…o½�§ªzådèÞ;5gÝuVT®X5Vwß¿âGVf¨êeÜçœ}öv¹zá`òÁÊ]èi6$ã¯íí-<õë¾ ×?åYÌê«ÿñ{üº+`hám~¸r´ÍÑ“”(¢ƒ©ÆÝ&òf²·D²‚OÄd[wOu¦ññ;?Œ0ŽŠ®~äcÉ@Ы3TõÆj~_I‘�o䤈�iä>zvÜãFo^ˆõ‡N§YQ ¨*H)IÐ3#NSm­ÐU#dZ?¼0·¼ö•èmo£N ‰4Ku–�Eže·*­·øôø?5äÄ·ËBˆ¡bl­EÜh�æV²µ‹öùmH?ÉÉn 9�€&™ÌÐS¿ó…°Ž^fºð� 0û²D®Þ"@úü±p”SŠúg„òMr…�t¬ž&וÔ] hÅ9¾Y͸ä¶îDpÊÛÒNò� ×>áé¸ê‰Ï ÷0Š!…ƒî�CvêvSÒDo�åb?ýç*‘IÅø‰ -MÊU;cfñõë{š¡> ’·õÚfÁpdbÉ«Ea¸–£5-Le@Êž±Eï{ÆQ�Kc« ´,»/0'' )˜`Pµ„¦ó†�B¬½Vqãä‡_ÌgçLþ=½¢A¨ éží"¹¶¥çávÏA)G¬ª4MqøÆÇàú'= B²jö¾!j&~Y+¡TÓˆŒýÃou]ü¾«¾v D¼aֈћíb{ig¯8�õ‡Ð››¦¨^ÐÆŽnÇ�’’xž“‰Ê­-l¬­pôÇFh¨HÛ€V R*ê}±^ò¦zï:Ë}LçVIŸåEN‘Ôp8à´œ�z˜rN*QL×&ý~“ùH~ }×ΜÆë^üWX=ý0ò4EYt޲,ƒ ¯—Rþ“5æG•TãÓãÿ.Ð!¬³ölœÄR�³e ›¦hÓ̇íP MNéØÝ1�–ôÚI*Ž’{à¨Ý¨¿ñ½ž\xƒhœÁ7ÄñÍ£Ú!§tŠ­1mk¥OÑ)Å5šºÉb²6ÃÛ8*ÐRŠBø‰ÓI [:–¬¡„TÍ0àHc0ù;5ÛSxì“möÐQº“8A#Š`ÏÜŠ²·Éi$¯îÌOøÔðY °„¿§)Óû–¥h« „¡®‹Aá#TR ¢’z …ýï…ŸØýÄÌanª£ÁÑÈò™ÎCà@€>çk«Ff¦Â2©j¦ ëŽ¢€öuðšnþüÜdêõÛvndX^llŽÆÑIÿ;`Òß(@P‰ÆÈëô�Ȇ›ÔËÓˆH:Sxê|%ºsó•ö—ú&âêpõkNÍNnˆ¡¨�XêSo¼æe¤Š@£ßmaýèÎ\vœ$ž¼. Q¬Ã$Á¡ÅÃ�6:Œpêþ{Q^Ø’Q�ÿAb¬¨¤yÐju‰í¥uJƒ�s”Áôc…AèuȆ ƒÀÒð>ªR „ä9£”æ¾"!xM¯ádÞü’¿ÅÊ]w‚ 4:¬�²wöägÒñ8ô{¬®ÀnïÀ*A‹ìD�]J-QñÜœëWÁ‚Þ·›°“ù\Þ†Ÿ «)Ÿ³´gœùMiC•hÍPá”ƲL¡¶û ·ž5ñ„H –d7ZqÝ "°6¿P^Ê:FŽ4Øh¸ýç¼ô†Gâ†}>œ ˜&<×í Ú8 Ÿ"r´fès`Z±§’–^ŸŒ”›„oãéÐ @¾ÈZICœfó $øýh ¦¶ú÷ÙÞ@ðR”%Kã¸ê¿+Gƒ',!éœþ<^�õ²h?Ž¢ª²&𤢹‚SdPÚKÝÉàS–i¢ é“¢R°týñA ]m8í¯£QpüØq¿­Å¥�{®~ô“f‘UÀÃMD ü>V]ó¾“`ÁßëJˆ=ù=�¼GPHõÛóýC>O«ÆæñEdíÝÇ�\ˆ��-ôz;ØÞÞÀý·~ Ûç`X8¾X0à(E M”ùÅ…eÌÎÌS�š”ªÚ‹Ãb­¾ÙÒ[CÐwŠš?wz[,d ?üCE4qûãJˆJ´U�„V~où›¿Äæ}÷�)¥)ÒÍ&¢F�V A�!¢Òz1/²ÏÞ{ýõιŸÿ÷€Æß'æ�iJ)CS–H„†ó♀:Ô¤Ïäs`˜{©p:ª·@ÅùI±ÌJº¥²ìK#8rîDõg8HOO¥.é’&}ŽBò(<¸#&RiE\ç{]Àài³Ò§w$T¢ÿ”!(7.yB¯DLRáæ§¿—?þ) ”ÛÎÒÂÞ ÌÖ ¸0çAÐ!Ð’õƈ‘é+& ° ²”¢Rá´/þÙÒ~€^Ó>L8@Å €g½j=†8C­9ba“0ã?w¨LôEa˜M'¼/L 53Ú8Ú`µ+_óß3 4¯¤…-@Ç`¦žƒƒ�Úøxœö�ãÎsi ̹»1ÓmÄtÓh/,ãIŸ÷•’dRw¯¾.Ã?×§Ïh¸š(|«xà9 ·V *¨LŠÎº Àó„H)±33…3ÃÚEG�·˜�]Dƒ �néæ&Î�z»»ÛœÂä‡ pš–ÏË€‚©©iRköäþ>2rs'mcŒ!ºóÖö¬5¬”!½RC†t‰’½%¾¸7.ƒðF q-œ»Î] çŽÁ¹pî²(Œ.�Ÿž?Ñ!S2ŠÄ§É•ѰîE�vû‰qw‚ ÀlsO}ôÓè !;1ô±)¾Ê3»p£àÔÓhº�ÝyOkžL ìyG‘ÌÔ*1ÎÀ´æfˆh± AÅùåVÊÂiºN µCð9')Öü¹.Jr ­VpÉW¤Ðfr‹¢_p&�Ũۢ”Ýda8n4·»¸û#ïGÖÛ¥§¬,£èm!Xº�:ç¥UVOŒ>e5!§0ÅÙ?áy*_\gOVi¶ŽŠÿ(XГ©©tüÓ>OÖlVÅiJq�E@+ œ–Á�ÿ~{6OãÞ޲(¯µ¬v‹3«Îç'-ã'N¢ºò1ªsŸb$`cS¶ŠÅ3w¹{�Î×VîÂÌÕ0�på³?�xÖ‹ö›ï�_óû¢&ÊØ ¢–äÂkÔDÔ¢–iFKe¿z�sk�z;ô" ˆ¹rþ,ÎÝ{„p $­¢M-.Â9K÷tU“ U¬Z)C`8ø(º5uZ L ï({´€"¯jÁT{¥5Q— ù!™}é²�³gðÎWü#úgÏ"¦zNHû‡Ió×^�öüQ)õ¢¸Ùü¬ÖÌôg·ff>ßZóìÎììó³<{¡>³»8ÿ™xzÜjž-Ísƒ$þ‚F§ó¥xÞh8xfÜm¦ ÃÁó<ß9÷ <Àw œ»ÈY{) ³GÔ€®Âê0̸OBQM2UZKƒÔš‘tqí@-·ù EZLÛãÊ·Þ!oÆÔ=mµ¬/�Œ4H†)(¢Q¾)ÀìÍv Qnâ„Dý¬½1ëûª7;+K[­ üSºà¾!H(Ϭó½4^ZDTÎÁ½ñu7ãê'=ïÿû?EQ$9¯ÜDböG ®ª˜LMˆ<3YöŽ‘*�¬*à›>%ØàÏ<…TÒ1Li+i”ÆP´ |­ªÊ M`A“¹V œâ MÇåÂ3?}2«hRâÆG:Î f¨±÷Ž"œdÐØw<Æžqæ?ªT°4�£sJØ"CqêN4¥Åh8 Â±žYÀ#žù®ƒ&léÒê7®$ø»R—>›´ p™Ø¾V v2mg”BÑic,òñ�j3¹¥ˆƒU{»9Ñ�ã‰Þ!Ë¡ ª¶ $¼Ùµ@Ð阊ú©L*2ó÷:ÏSJáé D·3ÅŸ@%HâD–§ž%Yàä·áÖ·¼ ƒók@¢«”¶ ,\q: ü•t;h_yÎÜyÄx PZë"/žgŸ¯›Íà©eY cÍ !äìÞë¦IÓ m×<õÉ—lolÌGAÐMšÍ¤Ýju³,UíNw&Šâf:©2Ëõx0(T E>^vyžK¥•Âò¡"/ µqþ¼ ¢PÍ-,Hßmœ5ª·³kÖÆã4L’bni)è÷z)œk(¥¤ÒªÔA¦ãq¾72Ûý�ÍkíN™e}!Dêœ-±à€³NH!pNHÕ�¶Üÿ+ )ʼ RqƒmÓ1± Ð‰!º±§S ¸3;@a!ü¤a�´™ Mbÿ+×€LýÍÆ‘í�8!!kBQdvg2àf8)`˜N[“¿–¨^Kxxm¢eB#Pó¦s”:sÖËÀÄÒ³œœ–ð]  u ‹.l4pÉM�Æï|vÎ@Q»LjúEi}zÐQJ˃ö…pôZøMÚÞƒœcÖ¸ž8VFB‚ÎkŒÏï ¶KàÚk©YCbRÿ¬â=ŧWÁ¾=%E\œ¶«˜jã>²â¿¥í`¹@ KÕ`4³¾ú‹°tü"5))Àƒ8&¿3üÚ@U®e¤±æ€šè¾îÈM\“¬’¨ïÕáÏ+’ûí©Ì/ÆvžÂ%–šC‡»»žU šüçƒ�Šn2ìD€ªÕœ?¿ V`ð:}¤¸¡|d¢qYÉl¢ÕF.#Ëj@XEI‘Mu’€¿¾üæÇâ²G>÷ ‡ØÞÙDYŽ�¼@º~ñÒ(JëÓ Îë™ÎÂÓ�ÛCK"pzCÂGpNþ÷âny)à�‰fmÿ¾ Ÿµæ:¸/ÆY¾hVu®°ò¤òç`+uÚ_r�‡Ëô{–šð=A¡øšÛ^ƒ£\ç½é¥­6²�å бÀ¨§ˆp ›¤×îüI(iÑíL“¦Yg)ÀMO~6„”Õ_û-üÏÜ äß㨇?›|]§ËÇåcNF8ÕŸ«ÃU^؉m¬ãý'�«J†!ݳ´È±ÓÛÆÂü"æ—a°qÃA�ÌЄD˜ššE|1•h�ýaÓÉ5öt±¹µ�¢ ´«Xp“1‰Àiõápˆ�Ý-ÌÎ.@8_ÂÎ=3=�ûo»ï}Õ˰þàIÚ¿ÕêÐ5R$ÚlbêŠËÑ\X7•jÍéÜ I0}ôÒ(ÆT§‹�� ôwwbcÍ�BˆåV³#ƒ^£±´¸xýM7ßÔH’§ôwvmQ sèÈEÚ ØÒâº+®F»ÓAœ$€” Z£Ò¤f@~IAEõÇ€®[ùšâŸÿåŸaœŽ(Å÷Íßúí8~ìÆ£þæïþwÜ}¤¾ú«¿G‚±Æ×!sdY†,Ï1�0 Åp0 ÆãÑ¿D>�Á �þî.²ñ¸��ÇÐRìýz]4›MÌt§ ¤( ¤Y?·Räij¤V½ÎÌ\°¹}§³¶'…<+¤¼¥,‹1€±¢’dü?é´×Ge„ª•D‘Ò#¥ƒ®,®¸àJ$Óm¨VH ‹ò䘙Ȍ; ¤qT—ã†HëP¯Pã¤, NAB ¨HC¬T Ë�l´])åD$SsóÑëúÞʈI îCð|ÃIkØÝ”ÎIrñ¾6âPŸjiOÏáú§=w½û­5qj!}ð£fAê�ŸÆé‰Ðú: ßäN8¯]&ö‰à^l±+ X¬p¡Tž¹EÇà§P¦‘:WmåFMÐ0žéŠŠ ×>„�^§R¯¡ÆQòö�Ešü™—°á¨ à4Œ¯'9pc(\E€ITƒ~U¸þØRù†ÍÒB 7Ql<Œ¼#ÏBô}Üð¹_�ù#Çy²žLŸ9Ž* ¯‰¨§Æêâ@kòÉȆÁ¦NΦ®ÉS0°ð�µ*|Íܯ6wè:Ó³ØMZ ûh �±ïÑ¢ž+QJììl’-B@r5ð}-vRž†=Œ¤Ô˜™ž£”è¡'§ô.§I9ñ‘,�ËZA ¸8nPüuëü>øî·á¯ü'ô··à#(ÂIML--¡}á…h.-T U*=­U#¡ÌÊ(Á×{äÞ¸Ð9Ì�ÆÃ±ƒ›ž]±ö³?ÿ®Ÿúä䲫®zÑ|w7?úZ=z”Ô“WTÍ… VÔ:ˆ*©Á•)Í2>r˜e¥WC@QâÐò®¼ò*ðpL²~qô{QÄdJCž<¿úë¿‚»ïºroÜpÃMxü㟈”r}c#8ùÀýxÛÚëà¢È%ÍæÜÒâ20¿xUQä6ϲñ`4Út‚nØ=œ)Æã{œsïð„¸WAþßm­Gé•a›IT†ak­‘»í™ÇgBÛõÍ@zjq ôæ§Àý,õ™4Á Ï,ƒ·yÊÈ; Àrï ¦R$oãªa?À7š« Æó8D³‚ˆTŸq´$)G#ßTLsöW_�W]�³·}R€nš™P"ßY…˜?îAÆqôà*taë,KרȰÚN€ÒE“1üÔï,ƒî¥fX4G�J€�„ é• �¢ –ÁÜi-@òûÒÀ nú÷<ÀT¥i¸F?AJÓÚË“™ïÓ¢Ÿ�-@ÀY©EqoèøÂƒ¦…Þ>ƒ T°¦$M³dv ×=ñÞ@­’b5“{�0&D½šj)Ò<ª¯¨à‘�0‘:«nTÏbããù¤Ûiáœ( KŠÎa³ cJZœÈËÅZlmmP¤¡´òª 5ÖÏ`¶"”RÔ_“ç9=•—¥áˆ—¶ôòC€ƒ)€öC‘ÄzófkX=ó0þö·~ë'ï§:h³Ñ„¦ý€Ü”�Ý6’ãÇ�ÌÏ@¥îg«u@ö( šMŒvv’fSEÑtÖ~¾³.F ¢(J¦çf›[—è�ïB¬|â–[°´¸„n¼™Ô Ž9JQ�©…ïÓ¡ ë@E.gqy™Rlã1¥hÇ£1mS%¢$†ƒ£($Ï2RÇÏKtCp꘲>u®‚"(Ë’H,A`;z �yôc( éàp×½÷à–·½ èâèÑøŽïø.äylmoãüúZ´ººÚ]9wçÏŸÙx|Õp0xtfÌgÙ"ØÒÜŸeÙÇ÷–{�)ßâœÛQIœpÿm5)”.‡Ã 9ÖRÙpŒäØ,Èj€+ ʶá›LÏñ˜'1íשhˆÚôZÅŸŠï½QŠÒ¯‹wØßÍÖÖÖF÷£ô.÷„”ýÿ Iq}Ø@eL)¥þtùèÑçkb3Îñý?ûKxâSž¢Æ®õ h@õ‰¬S¨ºvÁ!Òcª>±ñMQw#Õ0ÒæÎ­£µÝÛ÷a|¢‹`¾I½-£»7¨FãÕ£ l6Ž.b4Á:u@ýgþCa,–8K"›4B�æ¥Ó ?÷P¥EÿöuêãqByàüñe%}UŸ_loâ/~ø[qûÛ_ô‘a áuÏ�ìγ— K³púÊݘ,éÏT䪜Gµhë<ƒËƒ…­”±r3«Ó> `°â‰F0}™<�6Ä¢uU“@AJÚ—U00r¯�u¾¦U›@ÀcÜ ªÔ„ßI嘮,aO~ÅÙ[é‰9ËR4æñ5¿úg¸àªë POøçÉèûß%¶éwä:‰¨Ñ2«}Þª?ÇdT¯ €úºÏxÐÇ»ÿèpÏÇnÁÊ©S .ü À“¿ø+Ð��…"«€ëvI£AÅù@œÚœpÇe�$ø>š��U }G"œb¢xÅ«²Ñw½ï½xðãÅÎÆ:Æ£!×¢f Éñ#˜=~2¸&è]S÷‘„ËáôVV°sï}˜��E·Ó¥Èìèe'077ƒÞöúÛ»�Za{}“„B =¬(´:]ìîìÀXokMB�JSV#3å† ƒ×í®­Ÿ§£±4Øí�”ÒñÞpyž-eÑ�FÚ(Ã0,tȽ‘—EÙ)ËBH¡´ƒ•KuûÉÛN¶§¦Â0ŠZ£~¿dX!]‘e£‘4œsr …E‘»v·ƒV«%’FÓµ;±’X[=GÖ ‹‹ËøÆoüf=|˜¢³8Ž}Ÿ’š ½Qm¨ßïcxðñO|̾ø¯ÿªÜÙÞ†ônF©ƒûP¿×û>æà>!´Þú� 4åëÁ#±Ö,�³±H’X„I b"¥ŒWh¨™¢™Í# ìëü?@ì"XíŸ ³œG0 ÷¤¯ÏØÂ¹É¢ ï£$±Ü™š�‰ ÕèKKŠÎTÎù Šj„ÛæR�&·�Æ O Þׇ1±nMÍàÊ'< »Þ�ñ`aÒezöNˆöã!¤F eÕ‡�'Zöh—`óMæ£�ª6UÕ€öqƱ.™5<Á{çJö”aðª&}í…#®‡p“íDσÕeˆäà� ³Âpª„„t€`¶d S œ'ÐdÅhP\KM?—½uÈÝsh´»‚€ Ê×íš»ì*`¢Ð@m„Y’¿ ½*cˆ2¬³‚®\ P p€t*/¤Œ÷G* �<’oœ�8ç¤þÛÁ4ëz�™ü®ûÏ¥ “.âf× žj,;F‘�õJ”¦òÒüyY è”ªYXXF ™ °¯�ÊŸÎõ€??¿L“}QäìÊɤ nÌu0V`{m·¾ë8ÏÝt?ÈJ¿𠦝¸ Éü„Ö .Î9vj„¬’‘X¿ï^ä«kh4¤Ã!é3>ï‹>Ÿû¥ŸGOüÙxŒ­Ímú|{{yQØœ~àaÜw×½X=·ŠSwÝúŽIÉöÑBHô{»­~¿ÿøíµµÛ…”ߥš�"ÄaL÷òÞçÊ9'�µBŒò„‚&ùq·…ÝåY öÖ“cBe¤6š©6OFãKžcANa,T¤Ð¸jž,òóC¤îÐ.NîÔ?}Ù €Ò*u œ 0㼞N:z IÄJNt¡»1 é†Bz¦�tu¿7u_aP;T�ßÛÚÀ+õ'pêCïCšŽH›ld5¢ëŸј‚&»aS�xª})\˜ÍC‹?>#á_ƒûTh÷ÛÃoÃÒúQÞ†£KV^ŠÆpTÂCeA=5|ÃWÏg¬õŠ|½¸6Þ#þíª#¨?ˆ�ünJAH úÔÏy¾îÛ¾Ž&ÑÊÍXW©%€�Rœ9}ç×Îã]oz;îþð'�ÇÐaëà`WWέö·¶~À_ãjHÀ¹€Ò¯/Р]¥½B´€Ìúb™Ðñöåè ÉŠ8vÂòq…”vpHâ��Ћbr=Å;%r_ à›Gý$C9 DÎ8ÝŠ£ëõÎè=®]Uº×ÁR8”;«hh�Ng $‘‰ ¯ºRiR”ÐÖ"ŒÑè ÐØ"HsN�Ñ¢©A¸DCD¨>Gª Ð’3–‚%�íJWZ +ãÈÖÜ�rpMVRÔƒÒ3² t »Év×·)�5‰¦K÷YÚˆi�Å!œ &5 Iv'÷æ�_ˆÑêC@6¦ÉXÈÆ#8ïFêëü]÷iI/òšaeå4æç–@©t/ºYmFÿ[ÏLÏR{{K‘rs‘—صQ1š²¨F|‚èâÎ÷³°Ä»çÕÁ F Ål!%<@ ÈåyJ�IÁ)2ÃÞ5`Ùøcs§ØW»áí…·P RY†+ {åû{;¬øéºH¡·NSº¤Óî §@§ƒË.»†&ðx0F˜fG)E,drçD¢!Û!Ùv‹@AhÅ^+"*4EOÁ¯6`Q�ÕG=šèïpÆúuõéÁƒwi�Âi Gë.7pi øhJ•Ñp̤Ïì$Ó³,ŠÈòÙII=k>›0i]d0ÎRl¬­XXÛÇé»ï‚1†8uBí-$#ãˆùE“­Ò��B œ>ófgçIÀ9vbËQ±È¬$Ês Cìnmá¶÷½ì1¹H÷Ok„í61Ÿ cнàf.¸a«åI%ìËu™Š” ™ îž;‡�{î…’’*éÞ(ŽÂwÿôâ¦GÞ!e•�T_0žì�ß/X[]Ç`8Â+_ò ¼ïÕofx)•émomE�ÆÿOGé‚Fcàn€ÇëJ­DÙ£hè€Xq¤·½}å¶17/„xrž¦Œ…¼ô¯ÿÚ¼å5¯ž~ÌÓž.�>¢/»èõØ=ªõݷߦ÷Øts­fsúøå—]Ølµ¶ß±±u+<Ј£&*ãj¡Ô;3S qœè¦nâ×ô÷Ðnu!(¿¼~b™Š�“ÕJw@ÓZ]cS0³Ë'Ïø'<ÏY×’ê3ª Øc|r¾ŠËŒ³•KŽÐu0�ÕHŠøz"·– Ƙ=µVÖ Ñ<Ñ¡ƒÊ@ÒvÃ{7½"‚¥î.:L×S—Ž›|ãá;>�—üÄwc¼yžÒi;ýÌòUh]ñ8P$a--¸>R ç# N{1]Ù�G.–EQM-ñµky[€{ø\T{Ù—‘°þüRÒkN›Ù‰æQkS¥¹Þ£T%UÆ7ª²ã#‹mVÒ±ÙMZƒAR8 súV¨s·¡Ûl›JH�g¿èË𜧼IQB‘Ÿ¥ÄPätÙ ˆä!B±Š>£H™ÙÑz²Ù†Ãþ¬2y–Uæ÷]^RJM8KŸ¹Ò²k«+h@ï� €~6À8‡ÈK/óä(µf•†Ñä¸IŒÏ2 1n7 t¨ŸMÈ}R5E–âŸ~éGð‰w¾ƒµп¥÷ªi¶;ìcš�. %‚Æ¿|ÖF£ÓÅ≠Й™EÔhÐÞéL“*€ïsáïK#ù:Ï™îÇÛ_öbwƒá.QõÎ*Êt D\T…O¤`Ø Sh°ð&+,£1Ë= ~0SÈ‚¯�PHCï)¶!€G LŸ&æ›ó 8ŸjJÐ{p µ�ûÛ ]‹ÿÜ•úm\ïa€„p žÆQ´lj×RVÎ6Ï�>ü L'}¾µ½‰…ùCxäÕ7¢)t¬ Ûd¤hqÙ "P Ž4Þ&¥—¹¯ œ¯´p>B! )<’sä )¸Î%z©!”¤ÿÙy°É)ÍkÀµ#8Ë‘�ße%¥à䏯%Di¡ÆâqJ­�ÊP“ûf¦KZƒe¨¹�æÈU7à®[ÞMì«FÜ ˆ%-²²DzÈs™¦cÙ½õ1œu:(bxðGgnóÇO`ù‚ Q¤)œ5XZ:Ÿže€öv1ì÷ñнwá£ïx vWÏBÂå–êePósÔ1¯“„Ùt¯2¤” ;çÎaóî»Ca„Ò·&Ç7üèwá¹/z6ÍF}:,ðŠÓ�Æ«ÿéÕxàŽ{ÐÛÜF§GÒ/Æ:žñÚhµ¨vÔœ›fû‹Ò•¶ßïïŒûýð2|ê�Ày¿ðÈËB#ÏÚ£Ñ`�–;;7I|‰³n}hJkÒ4ϳì­ðC<¢ÑEe<ñÐñã/ƒ3¦,Åñ#—â{¿ãg‰Û>œn£77Uð5o: > g×�ô†€£*��š‰Ñ¸xEÜ¿ ³“q¥ÓAÛ­7ÓQa•¬€ËCÔôÓ,<´‚pL MHÍ‹§ ”ä¨&["]P¸î°zñQP“¸Uº|ýËñ’ŸünÀ4šM #=áòÅÁ9Ú‚æ§ûŠtÕ �éÆ ®‚›)ËÒðöÕT#€íž« µ*X)Né îs©‚€”‚YKÕm€JÓj�OήRX6¥©€¨W! àᔢW lk`ÎÝõð‡‚€$žðôçà›¾é{Ðn4(:•IPjL(A@å&Ò[È Æ &�Ïö÷ˆi¹¿J¯$èZ�#¦�­%èsWPj�v�±ô ]|÷9Jņ¯“¢ ­Qé(†њÖýù)bDB�·íQn}뫱õÐ}A4Z-»væìÚì¢Éót*ÏóH¡µÒBKEÚ\Æ”Òì ±4º ‹8|饘]\ÆÜü;!¨¡ñÔÃØ<{«ç`² gOa<DvA€Ù+.G2Åó|š¬ªüM4ìq¯‡ó{“®¬Ro�"û ÚG®¼ßò}ߊq÷šÕèAñ¸ëŽ»ð ?üsX;ù0ý AeZ´¡Ö±æ¼7œ³vggg”ŽGe™dãñoBˆ—ãÓ£vDQyžXgs yÐí 8Ðnÿ |ñööf™4",¹2"Z&å†HEÑðÁuMqõ²ýЩ7U«8¡©Xù<é#�hŸ–‹!,uÃ×Q0µµ^×o;ži#<›à!³4H¯Á€ÀF ¶(èzÅ$ªÖ°ÜªêW<ú‰h]t)F'ï¡§©ñxsú6Ä‹Ç!tèç°J‘Š2˜&%B-ÙÚÙúË@kÏ>£Iž#á?WKÂ0s‚ŽhÚ–A‰ÙnÌc°ƒãk„g‘±´—%‘ø<Vûwh{LÊÉsÏ ×ˆÀÒª ²ôGçà”©)áÖO¢GÐZÐ$9;¿„ë¯$Z3]È$ðƒ�Z<ˆ»AÛOÉî£n{L ¹Â×R I°®š,‚Víÿr Ðà5¨ÖŠP,}:pt„"MkZÀÉOzˆ¡}"ÍÇ¢¨§0@&AÀ¤$D;¦€µ�ý Á¸ õ =0D0°Z¡ ‹üÞ{�’"AÑ›:=_9¸ï¾7@à‰î"!åñ ­53BˆH+­Ã0’yžñƒÀÚîÖҨq«�¹Å%Œè,òœèÏU;ñ=Udö¤j?F Ëܳìcú×ÌŒÜ]YÁùÛnCCh¶ZÙ9éñŸñl|ã·}=— ˜�:š+�÷¿ó}Ø:}€¨(V(8Ii]R–N÷FogwT–eîz&Ïî³Öþƒ”òuáTw €Ã§ÇA£ð ¤˜š8ïàaFá£�r“ãÝmJc­„SU˜T²­J™óûQNýkJ?é¢`›d¨¨n¢âæ–žÜüðÒ7 5ŒêÁD ÂR­;)Ün˜8@Þ8šÓ6Â9è4GÙÒ˜LÓõ‹öô õŒàí܇Ý�-z²ÇÖY¸ÑdwÑ{¶~2¬Ú ŽØ•²jñ¬ýµþ픬ȇÀÓÇ+,4pºm�¤¢µÆTi-NíEa@@eY5³m4+øxµfÃO­œ>—àÈp`Ý2°¥°TtœªÀ&3IÈq¼ƒÒØÚ]Çâü2æ—°tè®zÄ�Ðí,\˜˜4%@që}`7EBxPq�ÿL FsAÂnlÊÌ!4Ó±�fF ".œÀ ìù %‘ˆ¨ ”Òzh…ôEó‘T )�Œ8 BV"E8"ÔDB =.‘§†]­ÅòÂ"²�Šþ_pâ�ubxêÞ{Þ˜çùa\àÊ|ÞäÅã”Ò—[^kƒðøx4<%I¼°¼œè ʲ[›Ôüh²1¶WW( n& 2)i²£Ø�†ƒ\)Y&�”ô@Õž˜ü«}1UÁÎl4Âú}÷m9C”(IÍš²ãë¾ÿ»ð¢Ïz>â$š¼Á˜òɃx€~ö pÙU—ãÝo7Þ÷¦wb´¾…ñpˆ0ŽÑ™ž†Ðjgwcóa}ĔŇ�5·Aˆ“ŸŽ`þ›´Îâ(é4[­T)M¹Õ¤ÙE3“uÐÉ(¢^…¶†PbÉÒp.ÜIJi€òèù€è§¹�ìKœB:€€pp ©FèÓhÉ”4çla¡wRD#BŽ>ñ8y´»Y�Éžc\xÝÍx[‚úDP”)§ïF«5 ©Àù:ùa€ 3‡…%©ß„̧¸ÀÎQª#Ê®•¥××7ØÔÌ“=mŒwÕ,½È%<èb›•>½Aj쇣¥dwNGfÅ×É“ |¯�0ö@'|S(,¢Pï‹z„ö×4Á*qô4¿Ž™HÃNÍ’§‰È%–.¸³‹ópÙ�ÛÝ”l�AÑ $]«à‹pÕÁò‡éŽ[í 'àdå‘ÚÊxJ¼–¼,uA Æû;ÚP€"—ÃYÏvôi5D" È&C4B ‚@(R:"ö5 `TÀ P$¤â–CTÈ‹|<ÂêÖ9²_>r³sóÁÊÊ *¨™8¶[/ÉG#¥”îtÛS[g;ž¼±º¶°°¸ÜŠ£xÉ9Y&‹<ò ³ÍfÛ–¥qÖZcJSó^O:ñQH\!®ve½]œ~ßûИŸÇô‰”>Ã~ƒïEøÁŸúA\xÑQf#Ök±3Ól"�á¯>;7Kµ�¥c‡qß½÷awcKBˆ™<Ëo0eñ°ôýÿÍi²OG4JÊ£8v­VY–# 5FS-„à§ZDMÈÂQÂú“b��ݤ1üOé¨vvÔæ˜dt&E�”ûAB…4kÔÐì½]€Õ "·ô!=ýòÆÌ°òƒE0Θv-&4«êD…¦—qìÊkqúŽ�‘Dy%d9Ê�SÐÝ?Á¤M±ÑXEÝX@8Š.@i¬Šb®€gb¡j… ß¡M„Ÿ¼±�Ùåí£Å~Eh_ÌgÐ)}¿LðšV¼mà-¤KË6Õt^'ÉVš»½Ðké©Ï<œ· ¨ÃGl1L‚ž„²Ì€swbZd’˜s½ÅA‚åî1¤«#¹Ä’BQPäìàÀѺô}TÒX K~Gà‰ßù: {%�l½!ŒÙùÀ¯%]£O%:¶˜ ×–›D(2‚SŠ~®Ö~ª¦8"-@}7½1ìù!¨Þ’hˆ™&9ߊN „ 8JÒv®—Aæ%vÎob4AG!²4Í"HÓ b ÀààÀ¿° àí~AszJh'°ÛÛ ÒñpF™%qRa8JS+”VËW {½ö¨ßÿÁ0Š.Šâ8PJŠíí-lo®#êtÐ>vQ»ƒ´×ÃÖ�wŽŠò^ Ç 93…¯úîoÄ3žý4ÌÌv«w5¿¬É âË»KíÉÏ| †ƒ1^ò�,Ë­õ›kçÆ>àü'Æž}�€sRHYg±üi ‘Rm–YÞwε³lŒ»?ö~R5 ë&ñº§uTß«÷òàí¬ó2é =“€^S_Aéo<®—�fªª¸56ÑÀAžíõnŠìdè,¤³ôƒûrÊ� \ºä´£Îh ç©Ù¨71ÁŠÓa„kŸòlÜòª—`Ôßá>•üÜÝÝEDóÇQÇ.“û©ÊÞ3¢JY®�8ßÍJRÐdG@gJË5%(:áTku:7¡«Å:dÕxÁCî@WUc/ÕT‡%B@%/ï‡ñ ÂjÀÞÀMiɪÓN€®ÛX¦zÃîn"nRêDIIût¦fqùE×`º1El)†\áÓ¡T°^ìR€ð`  ^-­¨7eÔiÑkËòH~‘i!ðªAAê(}T$Ÿb)D£ðB�ª0Ôô[@URÇpXœšæI“•+ÀÛY`PƒJ7SÓéBb*ZÄT0äã• @ †ƒõÝõÍÞÂâ×ùu`µ&"‚ÏþM9?lLùCEž?AI•@@4Mã1Öo»A«IÀî<ó¬ðtû©ù9¼ðK?ŸñÙ/ æÑÚ®è* L|Ÿàˆ€Bõ!Õ4€Ý­mœ=}ûàÇÈ&Bè ­Öb6ޱæ.öß ,Ϻ*¸À,œ½Àcœ5ð*± ÀáÓ@ÃÆAcL4©¤*Ç)†½4»S&ü7x¸ƒÿD½ÇF˜å;>#AÒü€uLÓ ¶%0�¢›»ž=ÔGÖ€«í¨¬¯/Y)«Z¼)³Á´¤›‘t �’…Ää)\})ÛñË®ÁÒÅ—áÌm¬¡|t#‰`†[psG)â° �ä Ø€ Ž�©µ7E9~"7ÍÑö€—²a±D�ð¥ò²úF^o¬r‘l¹LмÎ7i’z.Ó• *Ã…{H .ž}Æo²Ú¤ÇÒ*^&Vº}5A°úv9ÜF™ÐÛ[º­õu=|×\u3©oõmAN¬ �K­!‹ÒëšzßjJ…ÁJIR/FJ¢aLYjì—MD×DÇp®ž”Òï¶@Û{ 5e¨îˆ¤OZkT´×y íï ªß%)@€I2ðQèï ¶W&Ö­r{ 7�@,u çšP�Í™.ÂÕÀp0@E»ý~/ÇÿpÞ%¤ü ¾nwgû[Â0¼P+-Ót,„pÈ{= HFJéö†hLO‘jô{_óVØÒà©Ï}:N\p aÕ&æ¹OæÔ9¼çïAog—ÀfØàŽ[> -4©q8á0 é!Ej�<Ë‘�S'ò] ‚°Ÿï\ !šú˜o|îuÂYо�ñ€ãžïÅ.¯Ð4¾À·Á¹Wø;÷(>å�& £­l<¾}4^�4*n4pß'>Lö·¢V³lpœCí˜ÜWã�*§'d�¢ƒ˜~FkÎg;‹¼ÑdGM @²“ï„w»;�íèÓ&5Å{Œ§ZÐ~22©A™~¡I%`¤d´Rk0ÈÖ1ì&ÈQ£‰ÎÌ–Ápg—\[Ý)œë¯!ëm@·ç¼V™õ´fO5?ÝKŸÊ ´¢í=S�?WŠ%õa¼¥g§URb°íé&·ÎÑqé<®ª@`IÄ“Ý=‰µF?qi,åÙ…8üï0Æù'wvKÒpÝˆéØ€ÛQÓ{E:D"dÜ@4@Æ“’C‡0šnÁ nŽäæK'€¢ÃhEÍŽ&Ôôog¥¬£§ÓÚÕ{¿ðϨqÙõ™tJ”€t̘þIQQsw@¾L%‹{– ˆ¢;ëL¤OÕq1�­€±9eŒ+ é@ÿ ¹6ên:lân¤~ À«ò,ÿ±ñxí³ƒ Œƒ0 ù«E± g{—ÜpÖ0æúÁî®,Kƒw¾òMxË?½OÿœçãÏ}Ž_t:ЕW@`åÌ ~ô[¿«žñC$�Ér4ÖP=‹Ø´AÒv: 5=˜�†} z„|w ¥FÀ$È\»ä¬ùz�ðâ”y]09‘œsÑÐå®ðµÞ à¼ÀàSh Sš²,×�µe RM<¹4�|Æ Ii²/ÄÕJ=ØLÞ´á0eª®ð Ý™eÇå$9™ °Ž3mG-4\­ß§&j{�¸g‡º·)²B¨ Pµ ç)2c_œúúÕ¤².=�]÷ôçãm÷ßj¬¬žE¸½�æÜ"ÌÖÃp�)¥.üûˆ†¯ÛЄ.XÀUhÁàí9×ÏQG LA®¦ßÄ>ÐÖþçœÒCÄ’£ˆˆ Û|­Ìù~)hŽˆØ^š® ^ßÌñïä}o¸è/…„—3á:�'=Ð~”þ¡Zž�,Rò?ÉÒɇ؆…˜™! )UÀÍ¿ÖÿÃY)É6�#ÿûÖ/øç �™PG®)øh«šŽå÷1™R¢�† 4�Oo®‹`œ£½ÕC˜f´8ï{# Õw 8âüðÄ(™°ƒœ$[ÊÞ¦(¸&¶¾¶6Z�Æÿüx(·å×j!þJñ‰›¬³², iŒù;}χ>tSØn}w³Ù<š¥ã@H)¢8Á_ü*¼�–üÄ<OxêH] Ibþ#âã·ap~›Ü%…�^¸5@–¥žú¯¡”6Î9QšÒ*«¥Ö�0en„5eY¤£ô¬ûxD‡ú´âÀ6€K\º/Mx°f°óË €x†×ÿú+¯Äì§ÐȬ³; ^FQ'‰|ø–waõ¡ûqôÒ+áG=½ÀÁ.‚<è†IúT¬ÝÏøi…pðD€QÎ)5ÚÇXš(f¾L@ꜙ[àHÊÝ‘ý§n¡RKÍY(鉢²?¯\ ˆUÂ�+ñ8¼çoÿ®( •¤ITÚ]1ÆÎ`jú�/ÎÓ“* o$ÆÃyÿ ‚=Z¤7î§µ(] $JUØé%@ûr§~ej ­ 61“äUC�§:{:3_«òjÀàˆÄ§!) “´�ÃŽu ×p`�¥º’õr9.ÏÀbTÔ¿¡u€4O5ZPa GuŸ2#p aýÏÕ“c_­o¦ù€”(×ÿx]#òÊÁyõtõu½LÈ;í&”µhŒÆèlì’ø¬ó'/CÍ–”0ì©:În ›åÈvXY9C½FÃ~¿Ø^ß ȳøßÀ;´>¨•~þp4x¬1æ¡ÔKén²öö´?�ùhüUAN’DÄQ2%už_YÃ+þôïñ†¿~®|ÜÍ8qñ \~Õå*ïzý[)­Ú d95_Öº|8”κ@‘ÂaC+¥ÆãqÀH%K’³1&×*8e­ù�±æ5®™Ü‡úÑ÷‘Ù+ü€/âàájÖ1€Ü à àþŽê8ÀGŒ?U€qœÜJ}[™ç­BëŽv#|䭯š‹.¥<8&SNµ Ã7kuÍ#Êri¶ï¡t3à›S[=õ¸dI ÷oÍt8nrF®†™'¤£ €¤ßáxаª² éwîßHõn¶»˜¿ðRÜ·r :ˆš[ŒGÈì&ò<€žZ„�Ê;Z¦sd"§Ã¤Ï«^ö†"°T¿tì6Pƒ÷vLÅJ@RmHpáŸm”Yô2ÍH¿bNŸnóß C,5~¢fÐñÇæâ-Õ�@ÇÆZER`„ôkWf°EŠ$Šá,ýNè´§pÅ��¡f^£% ¿:NÅÕÖÏ&>«'±ÈÊvn”D�©_}o—¨×(c"Êñ6ΨÙ@«7@£?ù&Y‹2€À©!ÈsJ±©ÒÀS ITs§¿‹0Œ úýÞx¸µu BlãwŒ¼Ä/“ïÿ®5æ�±�  ŸÚÛÜ~¦nV(Ùpt³…'?v—¼ë–OàÕùKF1â$¦ïk8 ‡ƒÑÖùõ”yþ!!„³Î´ 6Â0¼Ãåt‘g©Vº4ÆlYk¤s.+Ê|J�ÿ7$ð-þÚÀu›ºê±õ5žïðMÞà/¼V(Ýûd#ˆÇ/,cbˆÑî-±óÌÜÜr„BÅ-¼à{~×<þiœÓçð@»Ùº3‚LÆ:ë;ûrKª¡yù}NŠÍ÷o£z¢ÞÂ4¶gQ7¬)Éë"ŒHÞÐßÙ‚- ´gæi2íoo’½¯ÒÚ×ädøÃ$…ù‡V ý“{t¨‰h¾É¶Ã67ÈÏ P ê§)’kÇ—ÀaÑ=“úaïÝËðæ?ø%hS §Ž0Bb¤¦Ð¸öPqÃk}9N#¹ Ÿ?Ñs-ÇÇ'ç¼ÇæY$“»ÿ'2/ðb'é½O‚¡{[[uü2“/�RÎEQŽF=¼ù�q£…‹®½ *€ê™:<™» u\)ÞΤå>Jâúö&N…ú›g±»qž€e°÷ÞÖàßßEžŽ��ˆMZò¢@9‚(¤�6‡´· 'P: •êFw '®¹ ÷ì5OÎ:Š…QA~:j “åîî`zf¤Ö<×€«fd¥€ %)<‡Ã1ÈO^èšÄìA�W\ƒ™å£Ø�(Ø;† ¡-¬d ÇÑ�ŸœYÎEÁ[=ô3ƒPef®ºm ² €Tü§÷N­a@KPúŒ#xj6GŽí™K¶aë£BHÿ¹,˜ ç=vØ"HÒ]bû�ÆC¤d¦Õ€ŒbÈ0¬õ?rQ jSaÌ Ÿt³¬7ô˜¦z×ú ß)Ä}�v÷Ï£ žÚÚÙ||Qä7)Žx\OMg�0tëÎÙß ÂðƒøÏ~ÅGVßð™7ÿ0œ{€ŸðÔZP©É–Ð@…OÍ}€7zòÀ>ÉRg<Î:xY:‚`©‘$b¼¹‚¿û‰ïÄ�Ïÿ<湟�¹ÃÇ!a¹HZÿ×›L�‘¼~axÒLvu,·ÇTÀìõ¶qï©ûpËmÀJчõ©|îÜâFBŒ+2¤ã)»: §Û¤Ù 4ŸÖßÙ’N“å9 5V &MÜõž7#�»÷¶°ÞéâÜÚòÜ ##dÙó³KxôcŸ€ã»—czfS3³ˆƒÐÜÝm kÀµHÃo-=�‹n~,4JÁVõ¶ö9(Æy‰ÅûOƒ&K‚žpä‰6Özë8yÏ=X½ýAÜwÏXY=M¾£|L>ä�Ö€Èóy–±Õ«)K?Á)ÀOо°M®‚�v—(;»ÛBB’=+kpgIò<ˆÄIÝ©Ž;ä@ÒjR·uiK„:B™•¸þ¦GãØÌ1ÌNÍctáQŒ§:,YBãÀˆ†¥ô^ö‹?ŒÍ‡ïÁæÊ9yNÔÌ8i!—-Øö<Äü…@sB)Vq–¢"xé;æÙ3Æí7 £mŒõJÍÌöòÑ£)¿/öËÑI°¤àh³ÕªFmtl%h_Ÿb�³Â¡¯ûû­%‹v2­Ù²A3ìÌ=ï�]»Í@! "úLh�¯øýãÄ•×ðøXgEtpšjp¸¯õ€Zd}$øç‚Úqp’B�CÔƒcœf˜?µŠhœÒ†½ãÃøÛ—ÿ œ¬³ÅÚ™3·:羜:ß?=þËÇß<å„„søÏÔl&¨ãîõõŸpß'Kß�¸VG8` tøkÝ™éÇNÍÌ,ŽÓ‘–R¡(Kè FZ–hÏ. 37�ÃW\‹¹ãÑäŸ û(F#eŽt0@c 3K‡1êíàȉK1¾í6,-ÅÜÌ)lílàäÚýØÜXEocùp c S Ý�"¯OÑ™±ÁÑ~Æ_³0°Æ:!¥ Þ Q¹@GÎÂJ!u {‹##/éý-$í«ƒ¦,@­×YJ Rw•B"j5Ñì´…1mï¬�ܸ$=¸¹G<SG�£5=‹ ¯º� •ÖÐ:„ðµWïÙCÇ:yÛÇðþ—üÎÞó œ?}éxŒ©éɬ0»&îÓG ZÓPÍ.¨aRð€’-�2Éþ-Žþí¸>Çtd0mš{Yª½SL<КÀŽ�ÏM—pÐJñ9ë¦QmvÂ7‡ö'¡L ¥(HÐжü{±/�µô o>ˆðü½Àh‡„4£ ¢—çþð/âq/ü< XêÁg²&SC}®‹^‰ƒ +þ­ÞNµûÔ¦¬ÅÒCç z}üÍ‹ÿž;I�¯eQŽWNŸz+ ¾À9|Œ?ç!᜹@@î#í£   ˜ø3 ügÆo[ÂNÂN€ÖpÿáÈè‡\À‹|:íâj2ð#÷àÿg2Bl~²QžÅÍ�pðX߬Âà ’$ž‰›Ín†ª$Z®"Ùð0NèF7Þ}1KSâ® !8êÂdGL,-o ¬õBÄ�Mªyža4@‡}çh¿ ‘§)²,ueQº0ŽÄÞ>íõõí½¦G8-…³N¶÷ÆÚÙ³Yš¦ccÌho².–�9Þh¶B¨S>¸»zêÔö‘ÃKBȘHda€ñ`h­õh<‚Rš óSÝi:ÿæÖ:BŸû§k�AH…{AàÓ™[„Ž4J Xšì¥oxÚs6IùsËG1{èS(ÍÑ È�ðÿô×xÏ_ü´�èõvèwž]X Úf%HsƒÍ­¤äÈØB´xd{¢5CÇ£hŽÇÙ®™E+áÕ˜«:cª:f•‚<×kª‘OþÜëÄi<Áµ$ç˜à�Iì¸ 0æE1i…@‹s½°ß� Æ�`wÑ`´�Îê�°ƒuªÓ„: ãŸxÆsñ%?ø ¢˜´ëæÕ4xŽƒ·Ç½bõ Ç �õàs0˜ÐÚNFbõ@á—ðì Þú{¿ŒõÞyú®övw7·77^šåéwá“`üÕ{Ž çÌgø]ßDÙ¯�° Ÿh¦ÜÚfäIVÀÅûêã÷þS�•?„~Àçh×�aG>àOü£PzôÉ*U#® büÆ œ{RÜl}ñÜ¡åGº¢Œ‚@Ïú'S™ŽG¢ÕjPÞj5Iš,ì†ÒlLtšŽY@/ÍR˜²´qœ¸@²4¥Û;–“R9z2veQJ+a­:ëú¶(v�’2 Wœ5ëI”læeñpYNyÔR剂“ZëþÞù–„GŽ\qy{õ�SW˜Awyáp‘e]sAŒM–ç�…ùéF/Í-,6àœöújmåœ�F p¤ fË’&Ñé©xª0� ˆ”P–%²,%êòìÌ<Õ„²ñÆâ÷ÏÎ.PÝI)…áh¨Q«�ÑîÊñ LZê ·Æ˜F»�5šÍH�Š¢M%Fã1 4¦f·;X  »$¨0À‘ +TµÉ1!i#x‚‡˜ð §!y»ŠþGKUO�f;\vuàm¼X'ËÜhM€ÇÄ%‰ÞÌj¢’Ò Ó]4Öî�Ø]�ò^(;ý„�&¾ñO_Ž#—^ ¸šhäò©8(eu `,�Ùm¨‡ÿИ¤SW‰ fëwÞ†;ÿöÏPÚy‘•++÷§YúUÞ�O¢ñ³/qpßàçÄø¯”?üf~ý°pt}ÏðÓ®hìx€?†oÿäoؤÔYˆÇ�× !ž´xüøÍë§OCiÝ•Z'­NG !Bk�3e™·Ú™¦ci�-£8ÑÖšÀ9‡<ÏŠ²(­ƒ+Ã({ýÒÅXGLQ”BHeËÒ(­W˲ص֎@ó‹xXJµn­¹M)½ã¬]7Öl;¸L:û÷Ü·Â8 çŽ:çŽ:èµ[�p§·™²L”T×/]pâ2)åü^ñÿ’¸sÆî v{[Za³Ó>1êõåöæf!¥ˆ´&ÍŠ„²4¥º��ÃáäPè¥û‚ é5ùÈ`có<õÎ(AQTÒh¢Óé[”ý,M?žçyOHqɿȢ$™ Ã(ŒãDî î$iÐD¾¹;D�Ôi˜Æ4D#˜^†ˆ°¤ûÀOüLgG",Þé}h(*�òœ{i8rñ>5ì¼ fëAѪô@âÓkU›B5“T)м½?— ó±øi‘£uîVØó÷#& IÎR)\ÿ¹_†ç|íw�xé>BJ�ÆÝDs¦d¡Õƒ¹©“CüÓeõMÑÔäF‚·áQ_CÚ¯ËwæÍ¯ÃÊßMoö{»ù^£æGò2ÿJ›}’�ú‹žöiª ñ_7¾�¯àþsiç=zÀ¸õû”�h&G쌹\i=ç¬ Ü“£_ˆ, ¢™<϶µÎæEvŠÉ6BÌ!¶A åþ�½YiØj8GI–ùÀ:»Nì){2 ‹ÿ‰ÐrºÕù8�w¥9¶;Ø^ŽÂä½TZ\Esù²K™¢èöÏoÄ¥3 óG.ÓLYk½Þ®özˆãXiÀ#JSHg�ˆ“†°ÖˆV»‹v«m··7]–çBZÎZJýAXJ!N•eñ&­ôÏõ=£”º,‰’gê ¸IHy±Òznjfº#¤ÔEY"ZF£1ÃúÃr+ ƒ„"Ñ]¢ôšLÚ02€T�‚£ Ë–lOà_£Õx§NËé7orÆ©3‰TÕš¹šªBDP’£RpFnrc:Ã)7ª1Y‹Â:Lm�„Üx.! BÀ£t==ƒ/þ¹ßÅE×Þüïj¢­ŸÈ®ÁLJÏ P µÚ“¼E25¥¢·‹ûþò�qïý·ÒßnÜŽC©ƒ’9Ÿ¤ã;ÿp­àÏ|6�ÿºñ0€Ï¢ÎýÿìøaÁ]ø⺨ñ_e fð?0¦óá@À’ì{ÿƒcó[�ªH‡³q+Šb! .?tñ%ËJ©hgwgº~=–Jaª3…(Œ†ãAÛ9CdE>+• §:Ó½€ÿ Fܧ‡xê®ãÿèÖ!ôî…>¿y-€Gèú|ìw*!Þö¡€¶óÝÉVÜw{;¡¥ÇvJ%IÒR­F+Ð:p«ëçºÆ�Ä�LRšÉ”IÒXïú)àBŒÿ-tÆÇÝô„#ƘG�Ç£ÇäE~„8d„]’J.:Ji)©¯Hƒz¦DƒÒw‘¬SÈ­„ó5©Á8Yf¤Ž¬¢�‘K+HX¶„’Ò|�VT7’AŒ­�-œ^=…õõ*>K)’™&ÿ÷@R­SÝb¾5ãË ‡¨‘Д9´R DVð×%’Ö™›]ÀÂÜ"â$!ÀúçöÎ:²- ÃßÁU1V[Û¶mÛ¶mÛ¶mÛèç6“ŽU©¤êúÌÌË]½ªW­LúIÝ÷ ι1믽ÏÞûoú ¦§'=>ÌÄø(�f€$‰At¹Ì]žóJ®{‡»g¢²+x%e몂 heõ÷iÿødûÕi?÷io25Æ^p>Ãü-¿üý86r�45ÉÌÄÄ&ü¥û†Óœ³�Üv'ð]`�ˆ[Ö%`˜²Š°�s±·ýèW"ÖÛ31v}ÀP&.Še¦�G¿æ fÍîá !ú€�q‹®¾¾»%cL·�²b„Q~ÈR¹,lÇ¡\,ƒ1ØŽKšÂ0¸p-—+x^‰0Nð› ”­$–cÆBÈv×cN«�Œ39=�LÌŒ‘$1'¬¢(+±)+$I‚ãzx¶ËÂbmyäNš (­‰“­-\ǯ@_ÏÕr•Îj]Ýx®GE4ƒ&ÓÓSŒ1³sÓÂ|´ÒH)ñCŸÁëÞˆ[?îômÚzR�šÖˆ¦ÝÇȘ¤Ý\¯-Âi»n£ÍR¬rŽÓVî¼råY29ÉŸÿÀßÿü ~óçŸE¡Y¬/ŽE¾ÿ4àûœüúà­„1ñ3ÿ—�L@|` XÈÖZö´ŒÓÙ*[J£›ÙûÅœæ<äg߯ ħ‹Ð8Àí2³ ^àfÀ6 œ½ÎÐ>@w x‚>)’ɹúç­mÛ^ïyÅ-a<< Ãk¤&-DqTu×íéé“}CZ)å¤IbŒ0 ¤”d6Z=ÂÀ‰ž¾ß$Šc”T”J%Š…2ÚÒ4 ,Çajz‚ÝûÎcxôH@¢(DH @µ|8RžŸ´y™Ø* D™ 8€,@¬�áhSÀ2ÑùivÝä""nwùE4ÒÀFàuÀ=€ �¶F,«L™-¿½h"“s�ß¾NÚÚ² ^Q¤ibÕê ýQtÆI²&I“�( ‡ ^qmÁ-Ä ¿Ñ�$qhY6¥BÙܼ¡:u|Ô„~ÄIl¤‡&§'Üsöž3;2z¤fŒ € Ô�á–t…< |  Ì 0›íC@!DÄ2Éÿö R&##m{ÅNí'Þï –ë¼IZê‘�úR% |Èú·*ýƒ6oeÃïŠv\Òö2b-U\mÿy«î À@»7Ó sÍZz^ÚX¡q´­²-ª×˜ÙõOÒñtsÁgó»¿ýšfs1]Z\:Ú¨×?.´~��sFÐõÞ#ÒÀ6àÀÃg÷Õ²O³U‚ö ¹Eàà�™øü˜UB$€¹¢#‘*Ù7þD »åIWúdÈl= <ú²™bšs‡Ÿ¼X9¶£\׳ó3rvnÆ8Ž«*¥ª.K¶e9ýRÊãÿ}]Ѷ쩩Ù)ñ—]Jk)BÒT0²騗{Jâù�z^'©ym’&�HÓ´+ŠB¡”Bk »³‹Êµ¯GÏ n‚к-Zi�URY9Bi�^Ú¦´#Úótmˆ[Œ©}»™úëï)[qÊOÿðSvï=;­×ûKK¿ÊºÒ'É9ãèþàˆ6pcàÀZ%ÍVµJëW»Ð€hy �:ðàÀW€ÆuFãwÊ ‚¶·ÚgõAº&[Ç€Gd š“³"OÝýÉŽÔ¤¯GŠc²´¶°Š%Ê׸6�7º1²Û:ë[X1z€¹¸]üm¨åR´‰R{Äe q]h�1ý�?“,΢Ҕpa×-°Ð¨óÓßþéXõáÇ¿-5^ÔÉ9£éúÀ° <$kfÝÌɤ€ =ûkNŠlNŽxb`˜¾—íÿ¦…8˜Ë[hìÌyî Ù�¿lO‘µ_·¬i¶?¶çOç¿é¿"ó`Œœœ•MÚÎÎÎÊ¿™ N�eŽ~ Œg … ÌåàG““sZ°Ì½Bå¡•jgÏìô˜e;ŽL1B)�W,d–œ°pЖ Bзóšt­ÝH©§ŸR¥cRÛAiMWß=CkI¢·TÂq=\ÇCÚ6dÖmÅÙu«òÄ~@â7iŽ�ÍÍ0wp)Q Iiæ’’$DqŒeÛTº:Q®Å_ÿõçÚþ#Î>xÁyŸ¬ÏÎ} ÉÉ9Eº?0ì´¤È.SÄu errNsd†·žÜIJY�JimY²X.KÇu„9áá/I’˜lzt¶&X–…�Š4I@*´ã¡- ©4ÚõX³a ·ºù�(÷ô¥ ‰R׬Ã.R ¤©@³6G}|”8ðIjó8Aƒ²kãh�‰šõ:�¥%¢0IJldf…à–‹tö£´JÿuÖ?&¾÷Ýo|gzøØ{íŽê�+sôŸ“#®[®’“s† Ò¦MàîÀC…}ʲ,·P�¶ã8RJKJ¡´¶´€ X6ñHâ!–e“¦)qG �RrÍ«ß�­¶a»¡ßĶ”R„qDÐl� PJ£-MšÄ¸¶KWµŠ£m”ÔX–E’,ûÙŽ‹ãyK"M½¹à9¶0|ôèY¿ýÑ>àÇþ/Ÿœœ+%yê,'gXÜX\ ’JÙ–c—‹åJ)Ž¢È˜T ©’8R©”k9¶²´¥…Ò#’x9úryÐèu¯v}\Ë¡àÑZ„!qEžçaÛZ[”J%´ÒÄa„’’b¡„c; ÚÒ8Å"F2׌}9>9>yìøÑãc?½`ϯ ïfÈÉ¹ÊØ¸ErrÎp”I’*Æ€ Ï3(„°� èÊ„iP)í!…'¥4RI)em[–í¸ª££Ó*—ªñb½®R“&ÕRÅè–í�¦©4Æ`[6�•Τè㱩qqtô¨.ºE¿Z©Ö„²gpÐUÚ:¼ï‚óvtüØäð9ÿüifu±4rrNK?šœœmÒÔð� À ” ÐÕº‡ÖtÎŽ�Bˆ²0„N¡`;ž×•jG!ôýCT-wÔ]·0yàÐî$ ƒÐ¤i])}¬ØÝ3¯µž>¼«X¨œ4ÉÉÉÈ…&''!×c‰ÿH ú¨ƒq¤Pë!äÁ8§…�E Œ#Ä’�2²ªåS-íÏÉÉÉÉÉÉÉÉùqy11õIEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/kitteh-spooky.png0000644000175100017510000022510215224505336025422 0ustar runnerrunner‰PNG  IHDR¯T¢Ö©F* IDATxìÀ�€ ý©©˜}»àq#Ï¢(~Þ¿Ìv»ÉÍÜv‡™–™™ù›.33S˜™c®Ú«MI©áp2™w¤Ÿ Ãxõç…jcmÝZ«kÆËj­7­©÷›+«ÖJ%¤,exž÷Ë3žH>FdJ’Ä3KBè9€Ñór¨$ Ê2ïǙǡôd I*9|üX‚çyž�—÷òÒË)HNcTJR‘ª”uGnK"©¥ê2"U©HQr©|fÌzr[:2�¡t厴¥#·åºÜ’.ˆYÏÒ‘KÅÿ=v4á-›çy>^~e…8Žó@U&eFæe1}lHYrC:R‘†LHUJ’—�IN‚ !óz(íÌpõÒÇr[nÊ9�Ù%»ûþ5¹)m$3‚>h/~žçãå—VÐ…U²!{dŸl•E“‚ Ò‘ž$2U ëÕKîãŸáP©Xz˜u€®Ü48‹ÈE9Ÿº”º,·2#¿@×™çy>^ž¾ €’LËV9$dC¦¥*y1žEf÷žÞ½85”Ûr5uQΦNÉ 9'—3Ú@†>džçùx½9GË€¼ŒÉ†¼]Þ%»dVJÄxV™aÜw±t¥#7åŠ\�SrXŽÊI9/W1» ôˆ=’àyž÷¦/¿¶*2/ûåòvY–šD<™Y"CéK[nÊy9*‡åߘÎ\nHÿ?>bžçùx=·Ã•F¤%ï—�Èn™”ˆç%3ŒÇVöë÷×0;3øðwù›“›°!žçy>^ÏÕpå� Ù%—�JKJ<@¹ÌÄ †OìÇ·^·(‚\€ª×‡á�WÏÈ>IdhЮÈQùµüXþ$¥çW˜çy>^ÏÇp€iy»|AÞ+s=ÈhòP*zN·�nB·ÿxF˸ÿ*%«ã£ÔÕë1—¯¦.FÚŽa"wä´ü^¾/?—£Òöó<ïÙŽ—1£ sò~ùŠ’ñ‡®ÆD`bÔ(äàæí„‹Œk7ç÷ñ`ÁxÝh.çY^(œ:Óãø™úsbC¿=¹ÏúrEþ&ß‘ïÉ¿0»õŸ#‡cžpžçyïÕ†kI>&ß’·?èp) —1;±¾”cóz�æjžù™@½júeym‰ñàó¦«+bi¡ÊæÖ8[ZlÙ¨ëϫ̖f�ÖrŽ™†Q«p¿åeJöËäó˜íF7­7žçyOu¼|¸Š²*—¯Ê{Ðá*ŒÉñÀÚrQCQ£µZÕ�©U#¢Ü«_i"�çcÂìÅU)ݽþ–J¬,�±¼2M³9Ëö-³ÜÓàÀîQvl.±<Q­QtßcdD¶É§€ÏË>™Ð€Exžç=µñòáZ–�ÈdŸŒJxÐ/gT˦++Os¥ÆúÊ(3ÓuŠÅýA Ûƒaœ½Šîþúõš¡G½~Õ� òâ‚ùéÀ¶V‘][GÙXŸdq~š…ÅY66-°sû<ºÂ˜›.3R ä¢7¸ü2 „ U`C>fð9`ÿÓ0Ïó|¼¼‚,Êä3²WFÅxˆFjÆìtQC1¢á¥R­'yÚ]£Ó…áðÞÐ��KsÆÊ‚ih ý¶zÿÑ¿þEw/ÀÆDÄÊbI#:ÂâÂ�Æ(““ÌÌ6d’úH�8ޏÓ�^ÿuFKYF&3(Ë:ðQùœÄhlj>Éó<ÏÇË¿?+ï’OË~{Øá*ä�z-Ò@”hLÖ¨×k‹e,ÊË\hº¶˜iÖ–rlZÍiÄãu£�´áÂÒëÎ û?!E&9òùˆb>O¡�'&p»“píf‚Æ•al`÷—IIVäÃòy9hXcs³áyž÷˜ þ�iÈAùTæ[…Æ#\<¥bÐU Z­P®T)•Ëz^¤VËS)ÊE£Z1¦'#]\Ö—‹z,23•Cƒ¦qI¯¯‡ÉÃ0ƒ„+×bΜïqö®]¿C»Ý¦ÛéÒé´ét;ÄÃ>Qˆ•`ðÞ°{½4ìsÀ!ñó<ÏÇë1WŒÊnù”¼SxÄ¢È$Òå)‹­*S“uçj,/Y^Èi° 4×*4WGõþ¨.µ •Jž(2 c8|”áRêì½”G¶h»ODf5«[Ýb–Œ3òئ‡÷ÞùÌÌÌü|fXüaÑçÇÌo.㶇̲$‹¡Õ\\ q~v¾XVÞz5˜$OîZG™C¶÷œ'##XÛt\¹sýf—�õ;Û;¬­opãÆ·omÐîtQ—`Íý7lˆÈ„(ˆ9äK°7ð"°'˜¡¢¢¢â"ø9nÐhg€_¾¬|P2OR%MòHÆX¦§k¬¬¤ G1a lï ©ÕVö6Ù·Ü`ªA~¯‡“fBæxHá.YíÞnö•±¹5bcs‡FC%pg}@·³›‰ ¹³1¢?t€~{' �¯!^:sòäæ¹ •¼ÞWgáòÝ…ËûÅ c8rìtFtz#F‰c¾±´4�µ–¹Ùƒþˆ02ÌN7h6-£á�õ­˜þ@è ù=TÆ“ •ívÌÍÛmƒ˜­vÆõ[#Ú�„,Ëèõë›Ê0Ưy¹¯M…2Š5D�~È@2à‡gNžÚ>wá|FEEEE%¯"ö_~8 |@dNiw2Ö7†¬­wYYî15µ+©:µ(`a®E’¤bÈç íöˆ­í4H±ãÅCIK~¶L}¦•}jÑ�ÍmåÖ�”Þ@È…¸’DÞ�ñ k …0€$…8¥Àe �¾. ð“'NžÚyëÂyGEEEE%¯û^çšž~ xˆøÅÐî:n¯ ¹qs‡Å=-šõvÑP ‚é&êi’2siõ¹q{‡+7:¹PFtºŽ4ûö7ÁÃp«JPH“n¿('âå‡Ëxo&ï}ˆ5E«?+{„fâ:=Øé*½¾2Q~ðûyyÅÀ«¹À:¹À”ŠŠŠŠJ^÷<“«<é×¹^ð×ÂÌp¤ÜYOxçJ‡Vk�ÀIš2=ÕÀ¦�W³±ÑáúÍMÎ_Øàü;�\x)ý¡’݇LäÄ–eYÖpä¯]€ P†¿S¯S<›öä StNŠX:=Ë͵Œk7âbìôT2Gå8"y‰½ñĩӽ·Î¿­TTTTTòú™„À!ÿÒW�%Àò!0ŠacËñÎÕÆl�¶·{ÌÎ6ˆ"K’::�!·ït¹|­Í¥+=®ÝJØjßC\ $.JËcr©qrvuÏÌÎò¦Z†Ë!s³SHÐbc[9©ÍëçÚ¼})a»ƒ 4€“¾T;ô;ÿÔ©Óƒ7*�UTTTòú™åÂàó¾…û0ðáQdP«kiÚ/#®ßì0;……¼Øi'¬mŒ¸u'a}Ë1ÝC\<¼¸`²´&¿wÿç‡%)lí(×n:¦šÊÌL�ý{Ø ÎüüµP· 3âšÀ_²©ÂE?¯¨¨¨¨ä5¡-þS¾³ð Є‡Gý(÷X¶‘k`×oÅ4êB`…4£Xêæq_Ò’F\ò G¬È{œñ%Â0†Õ-H�æó” 1¿d8°4CT¯“Ä)wÖG¬nô¸³éÐÊÓ@ Œ€ôÉÓg.¿ùö¹•¼<Ç|ÃÀ‹ïs  }D^‚ö%Ä<´�€µBæðÒúÅ%ò`B“É¥ÃÉŽ`mKÉ\ÊÜ\�£Gúìßo˜›�åࡘGw8©ŸËKʶœ>å6â§NŸ¹þÆÛç~Þ©¨¨¨äåË…{�/_öö}ˆk¸¬+Ài`ú=23õ!i† "™û O "£’‰™ÔýüëYC�s€¥òáÚÚˆ�Í.£QÂôÌ óó³ìß7ÃòүŠc@0ÀpVa@üöSgά¾qî\ÊÏ+•¼|¹pø”_ç:Ô߇¸ÚÀ«À¯—�³^\-À”dýRd@Ô€ÐÏ#RŠIYЃ[Mäþ×½ �cº­ºÐÀpT>à ÄTµ1–Z~sfºÆìtD£žòBð`€A_@é#yÀ·ž:óÄÆçÞÊø9¥¢¢¢Ê¼êÀÉR¹pö}ˆ«¼üMàW� ï(°×K²Ü.ø¸åïÀ"Su é¥7 Ì ~œ"¼Ðüð ‚’lÈ—ÙäOD!,/GŒn¯Ã� Å9%°‡o=Ô`yi†F£�1&ò°„�`DÅ ÈÂ@‡ =à¹À¶s�)?OTTTTòòåÂe_.ü p°<8 t�×½¸~Ù‹ÉoßV|¼ üø¡ÿÌ�!P+EhS^ZËÀA/Úþ·f@yàÆ Av=LÞs�«ЬáØ!ËÜtÈ‘že}#G-2¬,Õ9y|�C‡÷ÒlM!Iœ0ŽŽûaE”Pa?È—zÁOž>óDïõŸ�UTTTòòåÂiàYà§�è!ÅÕÞ¾éÅuܾÔ�yàÇÀ+ÀM/¼ôâ•˺ûïgw8yô˜,øÑËŒY/ØO_Eä` ˜€Üãæ½Ë�òž‚›0ñ[H)¨°¼TçèÑi’,"Ž• ŠX˜o±²w޹=ó„µÝû#Ú;Ýb‹¬­�4¿.ùQAñ‚¢þ0Ph#ÒÎ=ýÄ“ƒ×ßzS©¨¨¨ø¤Ë ¿‘×óÀÔûȸÞþfY\^D»Rêçüç/­- )>ã)Ï/\¾ä$^f=@€;ÀàmDn ´€  „ „F \qZêV”û]Çš,3k °ø�7d¢ÓV7àê-eeEXÜ[cvv¶8h3ˆêE©p7Ä ú¶·¶¹rõ/w¸yÇ1Jê% ‚PŽ¿´�p QQQQ1†ù– ÷_¾,æ!ÄÕ+‰ë›^Rƒ1)eÀ¶ï `-¿ûÏÜ/3ÍÃå=)ÄÅk^d#@ËâYœ¾ôœåóŸ¶ÅD�ɉWYf“'B Kó‘ý†½ †zTú¸Ÿ B¦ÂNÎ]R^{È•k}Úí!.sÖ¢Néõú¬¯­sõÊMÞxó*?~íoœÏïm92BD¤\PløLùï¾ì˳¯�Šœ›ß}Aò0»#�!ò +Nyqý«Àò×òâz øÀßôóž—҇ʩcÇCà)à_þ>`/>uÊðwþ¡V!œßùÞ€¾žÂì´0Ó‚vÖ·•8ä=20kïÞ9°W8}<`nZXßtœ¿â¸³ ™›´þ%Dìßkò¬1âÄÑ)ö/·˜Ÿo†!qœÑí ¹³ÖåÊõn.ø!×n9º}%sŒ¡¨2 tQý.ðÇ�ßV_{ëÍ”ŠL\€@AÓý_ø¡£¢¢*~¬{þAà3@ó!ÄÕΕָÎ}Tâò„À’ÏCJ0Õ2ìß·ÀÊÞ½Á*ªmjõ€CûCfšo]Œy鵌­�É�„QsSB ƒ!ÔkpôPÄÓ§ÌÏ×nŽØØ‰Ùj;\JÈ D€4kðÄqÞkð™³ûXZœ£VkRo´˜™s¤qÌ�ÎWn$\¾žÑòûÅ�&Ðéƒ0;;Åþ{¨×[Ôêõb+'Ñ ±!QͲ²´Îåk]jb.\ÕBP"xġ28&ƒ 2©\X¨jÎ呹 sÙá\T¿0�jýAÿ»Æ˜ ‚Ü´Ön †£‘ã=©Ö·D"Œm‰ ZŠXŒ ±á "V³t@&–Õ—Aœ‹Ù÷ÙßV***y}$MMàY/¯ƒ@Èý3,‰ë¯¯}Lâp@ØFx ÌL Ïœ¶<÷ìÇŽ.25Ýă TA�š›­±8oŠÝÛ£ñÖxÈ6¶•K×Óüw”#ÇZìÙ³@½Ù"#À!¸qƒ7o µH †åz/Åý¢(‚ èØ7½´¸ ÒÔ‘$)iI–ÔÒ,9‘¦É\šf_‘›À«¨~ øI£^_ ‡)“©@ Dl͉ [jl„ ê*bÙ•WG’Æmˆwò’â—Ù÷¹o)•¼>T,pø<ð40 Èd\—€_õâzõc—‡>p‘> �$Z<è{ûN•MmD½9E–ŤÉ�F£Æ™S¬­µYßÏby�Ò�>ܸsëvŸS'F˜å€0 ±�%MI–Ѧ¹äŠ3Æh÷„42ç=ˆ€Œ‰KîãÐ-/`p©�¥�Æ)Iìò¹’fŽT•L©;Ç>0Ë"î ªŸAä³À¯¿š ìB.°>¨š3^Ò[ßÿ"ÆDÌžþß•1z—ÿcT�hÚG3CEE%¯�n'��}@ðâú«À+@çã×ùKïè©ã'b` è8À¶»ÊOÞÌØîv8´ï2gŸ™Vlo®³±¾‰ËFX‰YY*žÙ*Ö¾âT˜ ‚sqñ�$Ç!�sdYJ¯;àúõ^;×åü;)7V•þP¡ 2¶ž5™|�¨·– Î@bÐXp©’%.SpE X ê_Ψ` T©ƒ›öG€¿Þl4^‘­^¿ï¨CYzî/)“¡uôÖísÿ*ˆ***y}4­ñŸžš”çCJ¡@\~Í‹ë§@Û‹ëã&6Ú@ØÁÞ¹¡Ü\OY¹Úe~6àä‰Q \¼x…Ÿ¼r“8N‰Â”Í­„ÞP}‰¯”-ù! „©4êŽ,é±½µA·; ÞhP‹âá€ííWoŒxçzѬAæ)ë¯$-™¤GPt\v( j ™EÓ©B¶FU&ôÕ+U¢Æ©k'�9à ªþå<~³^¯ß>æeDß}*ãÿçÀïÌ"ªÊ¤�\ÆÙ÷¹ßSî�ºÕŒ•Ïþ®RQQÉëC?îäày`/ ckG·�6¡`ø=à¯?y„Ä…@l›^dq"$‰â2XßìÓm¯Ñï­óÓWoó[ßÙfmSiÕa”ÀÆå2ßÄí º½„›··òï�P‚âÈþ½{šÄñ�4�Å'øß‘ÉÙTYh“33PA0»á¥ABbÑ\¦¨fE€×žxI ¨UU´‡‹¢ˆQ\,û=÷³¨þÍz­vm8Å<‚œ9yJœs¢y¢ªðø¹s.ÀúíÈR óÒ*ÿµ®þ}õ×þsøkô~þúžòÿÔÕ—Q¨¨¨äõ¡€&l¼ë¼ ¾ã%…—×0íÅö2ðãGI\c^®# URI wÖ3®]»ÍNÇñÃWz¼sÍÑ€5Þ~Nþç`c.^IÙétpÚ'I`ßÞˆ3'f¨×ýþu§þw˜Ôœáç"“_ò 1®†ÉòH#H-š Åïç!¤8EPƒÇ€ªC1€uX,*Š¢*Ægj€iü>–"2³›…Õ¢èâhׯ>;’£ª€(MÓª- Ô}Ô€ÐGhM *IMë#@ˆ� ÏÇÈ�} —‹mä?—”„—9çôÒµ« °ñê?!Éh�ŠŠJ^þZדÀóÀ`¸‹Ð~Ë�}dÀvù9®GŒ^igú9ƒ'spå¦òƒŸv¸³áxó¢£7¸ÛMˆ P×xÇ¡°Õ†·/9®Ý‚$Í%ÊÊbÂN;aiÁ°¾•ÑLȮƯE�A4Àb]„qul!®I(¤¥8u82Dòð/<~†ú>œ8Åx¡/2Tq8@ÅËài?6Eä/ä{à ì£,õYU�¼„¦Tufìì¶=À"0ÌÓ~ôsš€©¤”…9 õ£)Uº>¼´èøë6°ÜÖð} ôŒ1Ãã‡� ²$�û[Ü—ž¯+•¼>4|©ˆ€@Œ“#[€‚§\JyÄP ïåµ�È áÉœpõ–£ÓUºe§+cÎÆÅå¹{dGØé)Î�S¡?Tƒ˜ÅCœ� NI3A&d]%w!Xl!«:�6±Yƒ V„q8-„E¡Éܘ´�BY\ꊹ‡jbQ-Fg 8-}—8."ÿ°ªÖ€?Ÿ ìUç\?ùj>s⤨ênX…z>N{1­û�~\ò²šó’jy¹…>¬ž÷îãtã¾÷s)ÿµïçC ôÚi&«×Dä¶ «»ãâžÚÆÿúÇ:[ÿß_µ_xþ…�I÷ÒK/)�(òn5ã×8þ-à³@kLZ·�ßþ"ð;ÀŽÏ° N?Ñ~áßyA 1~x¤5�éä�EüXÊŽ`Lb)}'   ˆKÒñÆ ƒÁ—‰´Q+tùXdYâDŠUEq¨Ï¶Üî¨ùÈiæÇr$¸<2WÌIý˜iJª‰¿—ø{ªPJ¤ÀUýëyü9à•Q÷ø8• U‹Rmy)-GŽ£z8ìõïMõR愪ò¡! H¹^¬"â‚'"™Ë$E$Aècvåu«Ù&°×ƒÀÞH3³+¶ëÀšªv�ôå—_VÞUæe€=¾\x ¨q—Ì‹ë×�?üèÊãEl€äA Ô�²vÈ”É₲ç`L\“ç@š Ý�Oü}Á`0Ð ,„Õ*¤e i…ˆ×Z¹éB‹ÑáTP1ˆ >ëÍ@ÿ'å¸Òï8Œ‹¹#Ư~9P<pXD¾¨Qý4Žã>Áé'Å�ª¶€½ G€SÀ™ à Ê‘$c‘&ª>›B|PFDPÕPT‚ˆ#cy÷¾+»aLH�¨sˆHl`º"²¥*^\o?‘W€ëÏ?ÿ|ÿ‡?¬v�¯¨äõ~Öºj^ZÏó€…¬¿ã3®—€-æÖã…H l·úÀ4‚€p™,.™ô™Éâò#”/,�Áj#�Q!¬A!­È Í2¾M”àŠ—ˆóeñ¬O)P/)SRï– ‚ňÃa-¤…Á ^bÅÜÿ*e�…ÀùE�T!­EÑn6z ²  M`8 pJáiDžDõH³ÎÞ¹™ ¢vÙé)Yʽ�˜ˆ`-V �ŸàԋʇŸc¼ÈÊÙxy¤4�€Ý å0ððày໪úÛÀ«Ï>óÌÖ«¯½æ¨¨¨äõÀ0 œ)îA3 öï5rú¨¥Ù”boÉ7ßÉè¦|”²)C«aXZ°Ìͺ}a}ǧc "2&¨‡&ôÑò²Þ/"KªZ ÂðÏ>ýÌö«¯¿¦<•¼ px X‚Òá‘?þ:ð}`ËëqE�ž—×�‚x&fW2q^B€ âÂø°XWó%ÁÝh ¨c ayiy‰¦”¥‰1&"A¼ºaL¹LèE¥ï–-¢‘BW85ˆ–ñ%DÀM°ºâ'uàðK"ÒzµZíÊh†¾OäÒR¨sˆFõià¬À3 G�%  kaaVxâD�³O7©E‚5=n­  •,ã¡!°rWÚÆR¯Y¯žD¬ÿî¤0>¼Œ¼åÝï•ÞósÿB¤˜±vþ‰“pø†ˆü`. CÁóä©Ó&�²À§�¿ ø�ø'€¯X+Gg¦˜:²OÌñCRÈäÄ‘�§ÎÌqæÔ ‡.²¼·ÁÌ”!0Ü?"¾$h™ž 9v¨Îg?Ýä‹Ï·xöt‹ËuöçñÔ©f.ÉiÎo±¼R¯ŒÞ U�Î9²,ÃišãXˆs®žÇ�<^Dõ,ÂìÙOŸ**ªÌë�›<,{‘¥ÀUà7€ß® �9ç.^Ð|­%î7Á¯{( “å$Œ{k¼Œ$á,¢!�Ö ²&�kæPÃÞ•¶$ ?Ç""¥ìG |Фõå=‡úõ„DÕ—½|Ô"¢ˆ ¾­CÄ_û¡sÊ� P…É œQø’ªþhŸ9u:3"!ªsÀq¿Öóy�O)¦��(€Cû O� Xœ�03Ýàôéeö\¢×í³0ßeÿÞÝžc§£t“30D@LQöÛ�–ÙYËуOŸ Y˜¯ÓéYn¯�pªyÖ5Åñ£3 †16ØÄHæee1Œ‹ÊS¾WŒxÆç"⣠iŒ9œVÕEçÜ6�RQQÉë¾`�—×’¿^õÙÖ¯òŒkÈ'‡ØD¸ÒX,Œ#åQ&<ßåïk€I#LVóÏcÕ ¨c 1 øìf,«) ¬œ‰HY^À¡^\  N@TƒziYÏáEˆAñå@,")•)E‹΋M¼äPy�ª¡âÉTuàTû€,/-…Fd8<|ä3¾‹p(a L5%X�3§æ™™™§Þ˜f~Ï\q@hu9|¨Ë ÃŒå¥6W®¹p%cm«,00Æ ï +m!®â¾ a(,Ì…œ<6ƒØ)6¶b†Ã˜……)êlw·s1f G i’1Š‹,ª’—Ri,½76Æe'"cðsÁt>®óªÜ¿¼***yÕ¼´öM  ¼ üMàU Ï' A��+À:p° ÈdkMD�€³�…H!®†Ñ݈0"^Bã£Á £$2ÿ[÷ʨxA!(àQñ¢Bð  (‚ª×šXŒú.CüKåî\Æ® $Qeÿ)°-"¯Æ|nv®7=5uHàI…/Sˆ‹SÀB€"Œ¹/N¡ÝQ:=°¦ÆÒâ3 KÔëMlbLÀ±c)­¦ey�Шelí¸<ç¡�UÂòÒBŒADˆSØjÃ�MeeE˜ž­ÑhÖ&C®­:vÚ[¬ÞÞÊÅØ¦ÓîÒÙ‰éõ£¬S9Óòs|àå4)ÓšØ$R¦ÈN¡©P •¼î»E¾¬{¼íŸçú�YùqîÂÍ;ÞúÀU�›À³µ÷^ÖTÍ$ iXd\hÀ»T9¯* âooŒðâ(Ý÷� 2a�¥x9¯'¼¢Þ}yi¤þ“”U~•?ó®¬î^ PFŒPd’F°Æf6°Q]j6Y„Ï!rÕ/Ï ìE©“‰ ©\¸s`ÿˆ•ýŽ=aDT¯ƒX¢E6ìwY¯¾œ'ˆµX1^Z&�1%§Ê�;Žàͽá&­©˜vO¹µÚes«Ç�õ.ÛÛ]Ú�ñ(f§“ ^V½¡—Óû¤,9<T'"Š TTTòº/ˆJ[ëÜ~ ømß ‘ñ D n—�.0% L¦t±âRAS‹dë,”Ê|"2^Je‰•Æq‰•D7.QDA“%dQ@uPÖŽŸ•³)ÆæR~©×žj èÝ~ëËq� ‹µ�APƒàÙüþqT[  ˆ)(§m(“èô5—HÆN{T”òÒ$%ŽãBF.sÅ[·W;œ§Çù+Ž��BVÔL€ lùù«‰kS.K ò¦Ü¸½Å`䊭]Ym·Åg:Ý”�‘‘5¦m­Xc³þpÀ½©¨¨äe€:Ð:Àë¾»ðr.®„O.°œG¸ ²ˆÆZäý?øòÈi¢¸ÈŒ ¦,œ²�¼Æ·ò!LþŽ àGð÷ð(€T'dQ㥽²8ý8é_›’tE³FK1 ÆaŒ)¢(Ë•š!D$Ìã À~SÚGPÀ#‚¨¢”çâe Ô#avZ˜™„„öÎ6ÒéF!a°µÝæÂ¥m~òÆ�‹×†™%ˆ,>Ó¢Œª’¦)I’1�7@݈�,fg¬o%¨*'"¢FL?°Áj-ŠÚõZ­’WE%¯˜~ ü ð0àÌ[ÎkþÜѸœŽóxW(…°H’”x”’ÆŠfKH €„‰>Ä_�É�rGã$ñMúŠƒ@é¾—�7í)Nʵ„»/#¾%?°€±bðÍ%™ AUˆPEE?*ï�ŠµÐ¨â" 2¶6Û¬®Å޹ÙË{§è¶{¹pFÜÚè'‚Xƒ¡L¹=}81 ŠÑ2Â9¥7t<*¤A`7sA߈¢p{qnÞ­n¬so***y©Õeàmà‡þ.Ç'Ÿ¸ ò ði`F•À¹Œ4MÆ1ñ(Á¥ .ÀŠ3žÕP ÜE}LDðcéûâc\9%ÆÕˆ“%奨“ÿµÀ("®cbðÂ2 b,å*ßXC‚—Šˆ@1B�rßd¤)´»Žk7ûÜZKÙØ†á(ãð�&O�ž!I2zý §¶ÜÍWîü+ÊŒ^Xôz½bî›-ŠxtðY—µƒ ®Fax)Š¢öëÏ+÷EEE%/lû�v{À¶ù“��w¼°_tª{³,›ű ‡Cò—)VÂ<&‰ªTn£,$„÷BÁ�È$É”P–†Žÿ‹{”ò»¨¿ª $“!6Ášc2‡¾kR ŠRþ×.‹lìb"úÉÁP¹rÃÑ I³;]pjŠŽBuQ$lïdŒ/$Ò´X+„Õï÷‹(KëQE�,0v# Âs»òjÔî�ŠŠJ^¹¨v�8×(?'¼uþün×auo;u/%izr4Š[Ãá0Š“çF@AÐÒ?Äý=Å� ¥f1?N¦ü~I*~æ ü0±ã°ü¨•‚–%æEe2TRœ‰óå1ÄÙ˜‘“‡’ÿ-| ˆz¹ ~®~.Å\DP¯¼ž%¢¨ ˆÿ­Ië^~Ž*ƒÜZW6¶EÈ4 Cä–ÁiF½·×a”0QZÝn·˜;çxÔA­5ý0 .AðJ`ƒ[._rTTTòz0�)?‡lmn:1¦§ª«i–µ“4qùˆª"¥" —ÈÝ{êçå$H�ü*Éo»êG/ÊÉå·r~åÐ"Ê¿—á$“�ɈÔóäÑÇås'q!3Ô!(ÞTÞ•ãYœàS–ÖäTJxφBåþHR!SC„yˆµt†‹7 °0)ý¡Ã—‹Ò —Ö#Ÿi•1© ‚µ(Œ^ Ãð5cm‡û¥¢¢’WÅúö–5b�5Çòq !À£Š/½ÝÕZ*£‰J™’¬(eRªäo—%ai1:D5ÅuÙbªcz”ŒLR2“ša!¬ÌŠHM>JŒ#ñÒRð&BǽS6€–ßü�ѻώ^\þ!Í ;ç\‘m�F#¼´Š1I|×àc‚ ÎÛ‹‚ð|ß³Ö^2***yU<È10Š>©ªŸQÑeA,V’¼Ä< c™êP|”³+/$?ú÷`@”wÁu ÅÝ5'Bùß‹?òg2;" †y Hm�ÔôÉÌ'1Ο‚Œþì4HÊS k ! `”ä1‚T¹J1¦VØB\„ra!©¼YÐ+JƒE3)Š¢Î�*凬'Ùê}•­¡×áý†‡#¬ZõŒv726¶ ?„,»GF'…¸ŠŒË†~/Bð8ç I ‡ÃÝL«¿¶Å㈠jŒé…6x#—×oYcÞ´Ö$모¨äU!‚49 "ûóh"ÈÁ�úìIJ£ÏÆŠkÆK‚ç3,Á!âüçK2Á!>󜀠E¨Ù ‡ Y0"‹daÁµq)¼»”€•wÓî ½ç�‰·58~ÈòÜÓSœ95Ãì´Ðiwhw;ÄIƽÜ""c i�—ãÙ–ï",ÄDZ—ñ㉉kw» ÛXûkíÆ•×3„ŠŠJ^UÉ0�fËy,æ2Ö–î/K¥@‡)•ß¿_bÎKKKápˆ R:¶ß3|†F1ŠZÅ .za pÁgSÄ?P\Þ‚ ð�`*…ɨ�É„,/>õd‹ç>½—•½Sôz}.]érñªãêM¥ÝƒÌ11‚µ”7ÒUUÊÙ–_Û*æ³­Ç�Ôˆ¬Zk¿“ÇïåqÍAÌRQQɫŠȔ×  LÀËi<Ê£˜{A9Ì»bËp^QB†"8Íp ¢`R²ÐgZ>ÃÚ µ#0X3y—ò‰ºÒ÷—ê} ÍӪÉû'ïáð¡%+Ü¸Ùæâ•!o_ÎXÝtÄ)€N̶ÄlPˆ ë³-Dð;deÁ���"ëŠãø±Ï¶<®8æß˜—­µ¿i­y+‚ÞåkW•¡¢¢’W…ä-„E  cxÊë\w3)£^X’!ŒÚBXªÖ—KÒ*í]è4óˆÃÙ .LòåQ´¹£&ãÁ¯� à3 <ñ¾Ð±e\áÆÀ¬áÈÁ&ìavv–v»ËúÖ�·c6·I*¥õ:¼´D|c†a<ÛÊüºív›µµµBXås³sèˆÈ+b̯ ‚­k7o8***yU<`«²ä˜œ¦ @ÄϦԜáð/äÝ2b†bPu8É0jŠQ&l”ëD(0j€0+¤¥»Ò F¨MòH½°J[骾÷z–*>Ê»o”>ë§:YNª“+‡Šúk ´ÊÜŒ°¸Ð`ffЍVÇi�xä ÃÊ¿…B­ÖF©%S‹±cUðèrúôéBVo½õV‘uíÞÿ„ @8ü ð{Æ·nß¹“ò TTTòªPÀˆ@S„–÷@ñM^^^Z8îvŠ/¢ÁøF� '‚ˆCCÁ…¤&¨�–¥2¾¹áä ¤´®xqQŠq9iéU²”Ÿy@€ò–PŠ(õš¡Ñ k6ÈÇ0 ÑhÖ…|J’*ÔCØ»ÇphÅâ°Ü^7l´…Ô þac^|ñEþÍóßäOý©?Å•+W–——¹víZ‘‰}ˆ�ËÀ¯«êoäqes{3æa¨¨¨äUa�ADŒ@¨�@îynimË©óâÊñ²R!£˜ãD… EC…(£˜Ûl†ˆQD”þ„ à|”¬T’×x”¦<4^bΑ‡ŠA0ÖÒl6Ø»8ÍÑCu¶vÖ·!°»Ù™åäá€ýˆN’LØê*É(áàÁƒüûÿþ¿ÏW¾ò‚ (Ê„ÿ×ÿõQ«Õv3/_Žl÷càð;À¯8Õó£8ò>¨¨¨äU• ñ4D$ä¾ÐR›»Ãá›1t7üšÆï/XÈ1@è�ˆbÔ0›•Ö²„»Òš¼m Ó åÉc®À;7 ;=AE ÌÄu/-ÅäJ#Aq@ùZñøŒÐ©™Õh”‘$)(4Z­\FKœí˜š Ùéd„�ea.`yÁåó˜K×RÔ¥DµÿÞðïò‹¿ø‹´Z-»ÄÁC+´ÊÒ¼pæä§�…LÕcn­;®­Š——¢%�áçe‘�Ž%i•:´|T!IUG=L‰‡øDœ’¤)ƒaÁ)Ÿþ‡9|ò«Ôš ˆ±Ü"B½^çå—_æÜ¹sܸq‘w›<ŠñQDD\›¹Ì¿៳�ý¶ÀF’¦ÕšWÅ�µVTÕ©HGÐHÊ}£(1Š …¨f©Õ¢ÈccdlõÈßRÀÏü ¥ô…2‹sšËK˜™nÒœ^àȱ&+ûbŒzí5®\ërc5c0r¨—�–†ñ÷@Dz­ñSÈÆ²®²ÌâÔps]øñC£;[°´Ø¤Ñ¬Q«Í³gï§Ø{øY‚Ú^Lm¶1*VVVh4øõÉB\�j ½1ÆE6X Ãð÷‚0ø‹ÖØo‰ÈúZo#¥¢â1E¨x)Ö¼Œ1ŸøçEäïYaÜ+m…aT4Ôó¨E5Â0ÀZ‹D]ž—Ÿá*]—³2D¨Âñƒð¹g Ÿ~f‰c'�3¿g/b  ¤ñ�k—ß᛿q•ßþ~Âf¼l¼œtL^Œ=Ü\HnLx:ù{”¶£2‚µB³&ÌOÃÞxñs_çË_ý£;~c#ÄÖ1yˆ A ÷¾õ-þ³ÿì?ãµ×^ÃS¬�‰È#ÓÀ!™w…á�ZýnÉXû}kíúÍÕÛ �1†ŠGÕÕUàp ˆP&bD(ÖvM¦[­"š�Qâ·9*ì_õg¶žÃ]ÑPºF¡å; Ÿ}"àÐÁYšÍ"âw^Ѝ7ë,í YšWû®Œü¨0>26êä—ï #kŠ�Ý!Ë”�nʾþòþç‚þ_ÿ8ýÌ× ›û°õEl8�ØÄð°<ñÄLMMØÞ½{‹‡™÷íÛÇ£€ˆ¨µvجÕßi6½V«ý±°(w*qUTeʵÔ#"ÀO�'EXRe?`Ë=!°A!©z­N-Šû'ýNÄ¿V°2'~/Ãré�²Æ›8ü5„�°0#¬,Âþ%á…³3œ={†…ÅE¶6î°vg“¢óp´ÅÅK]~ã»ðö5!ͼœÆ$vÏqܼ”¤µ+�á_øø…_øø(øçÿùžW^y¥ø×.?æðæ›o²ººúqH kL’KtµÕ^Ž¢ðW¬ ~×Z{%‚á;W.+UæUñapæÄ  €:° p4sÙb–f&Ë2Íç¨ÓBPÖš"ë ƒ;AZ €€gZ-ìK{ ­†¥Q‡46¶7V•�¶2 * (ª‚ˆ Rʼ„$UV7•µm)N&^YêræT‡^¨\8�—~²A;5G»«´{B–)êÊ;nPʶü=JcY\å6~¹{ä‘#GøgÿÙöÿgï,€âÈööýœ–�à!îNtÝ]²î»×]Jþò¹»»»[Éu·u߸ #¸Ã3ý�·¶»2w.Kà.aû­:ÛcÈ4Ù~æýÙá–[nAa¼ ©yóæiTT´U gΜQÿ×E™¼!§î»n¿u[u6Ïù’…ÕwìÚ¥ü–�?Â{¡X±bçkùbA ׊¾®¥ÀŒY¬ æeAxAÈ$Ç88¡ËtJá1ãÓ8T”6¯öY»ª˜êê"Š òH$ž` §��;òµïœä­½£ô'ßÁueß÷ÆÊõahFFÆûו6Œ#@ýèóZªàûÔ§>Å�wÞ©Rt.E©0C…"r] .T¯—ò^ !ªpcÊC„Ž1I «–„ŸØg×K¾ç¾â¸ÞQ×qº|ßKo˜¢¢ŒX±bçkéÂEÆ$ ÕÀòÐe­#–aL�ø€sÎQ�¯ÌEBšŒ§<æWkûÙ–~júi<=BooJ†,[èqÕ–nÖ®ó(*« ‘[@Yi>E…* Kä#×dY;­È.1<ØÅ„dº­ pE…+V¬P†òZš%È¥¬ŠŠ ‹XèïŒòqSÞ¨ì8Ψçº]9~â …× žï«Šð�ç8�öþpœÛŠÃ+ÖTË„ ÊJ‚ù�¦ÄÃ:` °¨ˆ 5ÖFŽÙ”‹a‡f ª *”h8•æä™!ö 8Ý04 ÆêOã{ÍTVÍ"¯ X9TÄ‘“�Ó’û“9>Ê„·ck“ucc81]ô�Çœ-[¶($wÙl_óùÏžŸýÙŸ•ëÒû¸t®¦¬gËu\ (ï°u[¯úž÷’ëº-¼ÎºŽ3Ô4…}[±bÅaÃ\.0(峌Žf¡�òðy0d‡ý²ÃY¡ºñC‰†ÌÇfÏ4Ì,5œiÕÔ H¥Îõ{Ý´Åá™Ç–±fýz|/Áñ£‡øê·Žð½—é싾köÖ(cÿ,̹çÇ“`¹,5öª_J¡A…Ý.¶ô{ý8àÙ¶mÿðÿ@KK Ö«Ž£G�þ¸ÍÊ��cL§ëzõ ßßfÏÓ+ví²À: Ù)S ­X±bxÅÐÊfê.P¬Vs� 9`ܱ@%™lx�óš‰ä¿\×à:(L(pe5+ÃU5.Ï<º˜�›7ày‡öàK߬ã¥mÃô �k\Η �˜,€�­Xr'›6mâ‰'žàÚk¯ES*.%õööN:\©÷ôå/-mXÙ×ׇœ˜¦mLBi@ÐêqŒ9î¹î›¾ŸxÅó¼ÝZgm々VŠX±bÅðšª-{QÎf+€­ðv5P $ÆvYãÃÉ`Îó3îíŒÿ�Ý–›€[¯öxìÁ•¬\½‚‘d?o¾q�/}»™Ý‡SŒ¤ÀpRZ™÷3‡ûNX K~øÃF.Ky-…Ü.U©èBMNFêëúû¿ÿ{¾õ­o¡òy©¶¶V�O$ ˜ÚŒ1ÇÇÙaaõ†ïylÃñi ­¾3-g§Z±bÅðеdÁB(Œ eŒÙh-ª€À ã9ªñž‹6�ðáĪ#€Î1‚UT%ë¾ûîã†n`Æ *¾˜ŠŠ»Ë²€C_#·õÛ¿ýÛ¼ñÆ(÷ÕÚÚJggg`ÏÛ(Ðç8Î÷íëžµk/ÐtÚdžÓVL¡bÅŠá‡óC@-�Ö`UøØŒlflG5õî+3çd"H…Ð2º­ÝÆ€9çÆVÌ7\»T…¨¦åÍp´!ÅɳƒÃïìB¢P –¶Â¿îºëPY»\Ö’%K¦¤'K|ë­·”õs.[×vüøqþàþ@ !jdÔ°ub�X­Æ˜gc~?€ÖÀ-€€)R¬X1¼bhE}Z³€•ÀUÀÀм\f¥RŒ¦qí1Rį‰)ÊgMÈ}eN»W8Y^GÁ+‚fü�_Q,ª†TN·BW?¤Ó?\Æž™· Ö¯_Ï¢E‹˜3gŽöÉÒšò�¸ú™ßþö·yî¹çP~Lã ²»\%G*ÿã?þ£ò‰sj×®]û-Ⱥ€à×�ÝL¡bÅŠ›”cpù@Eè®®´B€UÏ­2ù5+<§§7ÍÁci:z‚1ŠÇÑ$^� +ǤìrÝ·�ÆÇŒ¹w×xêîØ]û£ R~J.jñâÅhNŸ5sæLAê=s'™…_üâѤ Í\µjú¹—³<Ï“{ˆ2m™âÚÝ–çïØ±£Ã¾÷Bàn èç]*V¬^1´< , n ÝÖ:`.P�“ÀY»Ü5·^WL{Ç0ƒÉ~z§MM_ã¿0 ï… Š`åF÷Ï9¯I4G€ÒÒvõº *Ô''UUU…@¥íì/”T.HG ¶UnHó�9‚Þüùó§CÎLæÕӦ Zç¿Äžïö½6�ÀNÞ…bŊᇠ€!´® /.ó�¢¨OË÷`ñ<�ËçÒÝ�¤þä OõÑÖž ’£�N1)e†-ÏÅu\Œ{.$®q )tŒ )*õ*©øÞ{ïä¨.fI¿“€¥ü�&Phx­Š29]à¢Ôï=©p£¾^ÓÜÜœ@¬²ü‹vt]W.Rˆ˜Êï�ý 0ÏNœOÚßw¸7t_½ÄŠ+†×$«}`&PÜ\:¯r pU�g(+™AQI%ù…iV,顽3ÉhÊ¡¨@pIS"EmcšÁ¡‰AËq´\\Oˬ´²1C�Ž#„ Ê€•– %8i\‘>õËaÝxã�\ýõ­Zo÷îÝ477«gJw‚–v Ö…\á4uqØäÕİ ¸#\«�RÀ'Cë–;l\ã3·º”ÊY3Y¼¨‚ùEº±xéRæ/!52LgÛ º:»éId7Û\Èa¹:ºeí˜X¡‹ÿÍ7ß„ÜɦM›Q°ö’z饗8|ø0ºHê÷RKÅ ^l)l©ßQ%ã‚•n+ħ÷¢ßS®Kô»ï¾[ðšPŸ`ôâ¶—h8ÓD"'ÁÆ[6³¤fy…y–140„ç{è¼íyiÇZê(ÞWŒöÛ¼y3S 4àLfú¼à\^^.§©£š— ¬+«´NlYèø¿¤‘b囀•@!à� ��ÀIàÐ Ãás£@ŠX1¼¦©rCp=V}-Ý–!Kå%K$X½r³f/¢°¤Œœ30Æ¡ ¨œt:EoW3§›xk÷ûަènmb"`y.žë)Ÿ…Ç–V”#R.H9«[o½95(/dK¯ùæ7¿Éž={PŸ•Š1”3ÒQ`¸$§sÏ=÷ ` ^rM± &˜(œ(xéuúù¤ŠÄ—w¿JKoëoØÈµ÷߀ÎU_W/‰Ü-… u4ŽaómWðæ·_CPà›´&)åá~ò' ï°%ß÷Zx� s_¯­¼å›�;€+BhIÅámX<Žã¤ìä’öZ­@Ðì…€K+†×4+ƒ_^0¶K�ï “ÍijëG¨¨¤tf2³¤id8ÉÙ–ŽÖ ÑÒ0šBpzÛaùvéBÌóŽy,Á*–.ò+”YŒðì³Ï¢a¯Ê£è¾ÀªøA¯�7o«W¯ÖVôrd—TµžÎE^^žÖ»ÚzDçã?¿üßœl>ÅÚëj¸û#[)(-âdm¯~í%z¨Z0›ÕW¯¥¨¬/á)Œ¨Ÿ%8*$yQC¨‚ô#�<¢¼sm°ÔæÂæ[�ÿFxÿið ð€!@r¬mH|´ÜÊ‚«,Ç*•J�¸®ëÙ#•Éd²Ï~(šQ__ï €Û�3ÀÛ�QbÅðš&U…a˜æN`Ñxà’Ž7¥éîM20x’¡dŠ \fUç¤SŒXhèÂì%fPUYÈœÙm4œ¡ÈÃó.?Ìge_d‡R££ %ßvYÊiéb-� h*)—sѧöªªJÚÛ;T¡§0”Â…‚™¦×%¿™ã� Våó×FPºæ¾ë)­,GÚñƒ·8¶û(ÉÁ$ ëèéèææÇoÇõ]‚åÃuí||Ñß·ØÒ¥KÕ €©—n¡…W'pð,pâ}v ºø<`B·Ôo¡~ÚFšÿîïþî*;jì{Îæ& 'ŠHèÿ =ÖR1Pccã*û¡®õàÁƒõ¶Éý,°ø(�¼;°^Ó!ÏUl/+�\Î#¹¨æ¶€Þ¢¨ð,‹VS\ÚKwg'­­ÈM””0§*�õ« hnMRßì’ Üó†£ÉëAjˆ¾þAzûÞ†V,­L©�W•„y90�Iz‚—Š4Ö¬Yƒr+ÓTºHÑ×ÛGQU1s–ÌUÑ‹…~Ê:¯HÑýÆC ô´w“›—KÛ©ŠM!+W®ŒÂ’»|^ÿf2ÔßZãbÇqÊÒVaûÏß'n!x¸ ÈFlDá­¿ø‹¿¨}æ™gó<ïs@)cHµ ›‡…I:‡Ž�NTÚs3mêÞ_ýÕ_í Ýìc@mèÄbM9¼¿”, ÁuP<™ÜEw¯¶Õפñaz:Îrðàq¾÷Üžq?û÷×Ñ××Oi±GEy¹9>n"«øBa>}ꌢm3º»ÚÐý¨˜A¯O˜¤ÿ‰õIþ¡‡BhýÌé*¹O�?'�ܪ”Né¾�¤¼"ð¶‹íDnìøöZ6.^Ï•W^yÉLÞPØWð’\+{1Žv#Ø0ýU<‚+¼ñÿïÿý;Y¨°{¾ý‡çyŸJ'–ÌÜO|âá~¡Æ:í `vañ‰Ãë2žœ1¸¸ ¨\&¡Üð=j§¾®ž×Þ:m÷¾â›Ï÷òõïžæù×:PWwŸÃhڌ鲲¡¥¾Û:z-‡‰¦µOF ~ö³ŸEý\º0NW鼩(%á&H ÑÙÒ�„ÆÀ]ÚÊ‚U‹È+ÌG`›·b�‡8öúÊG‹yì±Ç.©s£œ¤Ü².ºúwaŒ) 3ÖtV>°5Œ|äÇ¿þõ¯ï¶û ýªåø¯Uïrª‰Ær•Úǵ`ó�+€ĊÆ—iž«¸J®+3Ï5¥Ò0˜LÑx¢…º¦Q¶í…¶Þè÷h톇òf†P¨1³jPÐÊÎeEUw!¬&¯(l¨­7ä¸t!œÎ’“€ÌžO¯éGy®õ7nD¡Áòê™<þÿžáøž£ìzatÃy”¹Å<øàƒ(ßu I¡^UYê=Ée ^nèFJ€üi*¼XtÙ¨AÛ‹/¾XlÛ:þÍqœ©„¶þ湋¬ìÅæt:}p<Î}Åðº‘×÷ë€|À0I %¡¶>MOß(�§]ZzÞ.{7Æ0Ø5ƒveB+*s¬TX‘Y€ê]�릛nR…á…œ”®’s9FMJL4ÈWeùïù(&}\äçä1:0Jík‡©ÛwœÜü\:šÛQ©|Ok7ÎTWT3ñ<¶nÝŠZ .5©ÿN¹I…º´Y¥k•±Ééjàð4 Ûo á•´ËüŸÿùŸ¿Ñ6Ø_cÁå2… G„ Çr ÆÍ¶•ä0ðp’XqØð2QTp P¸LVÆ—c'}vNp¶3A€÷¿ì�x”×¶þ×H&ž � ^ŠT)\z¸U¨Û1Níø©ËñsýÞz{Ü]êîî®ÿÚ98'@Š$H‘ÉÌÿý=wïçIçNh ™d¾Å³Ÿ!“ÉÌ7{ïo½ë]¶Ó&d¸TwÀÊŸ¤ËÀU蛳ZWÉ©§ž Hf¼!]ïqç�wÚ­·Þjwß}7Ý"vûí·ÛM7Ýdo½õñºŒ¦Ûs< ±"\†u«6 ÀÙÛ�¼aî][ôú|ÛñávP<€mûÖ·¾pemÃ^®‘ØW»Ú³6ÇLØ«‘¾Fœ]ŸÐY< ¸ÇéÈ›O+«ðSÚ[öXï’:0®Bb`ê4s’ ÃOﱯ@æõ³ÿØ/òÀSÍeë6&g¸X×�½Ù¼!Rá¥0c±K*ÞÒ÷mœþoö Š· ñÀ«}æ`&D7¿¯™ÊH_BRó-RÔi¢ ëò߉ßCP¤ÓÃpÒ¤IFA2')gB`xįî¹çkqe Ì-×tÐAê@r$,²>q…y¢móæâq È% s·}EŠ]âD¥ØPÁ´iÓ¦jýFòÝ•Þn=”€àJ…‘²™“sÏ=—½´§û–¿eð>°°°êë&¨=×Eê©YüÊ+¯üœbg $¯l”—©m+1$|ðÆº¶£[ãÔ}¡3|H.@ev¢”âbÚEc1_¯Õ¡›VhqòˆrÍô©î°�L0/@˜BZ‡\¼r‰-_»Âø^»”Á—Ðwå#II¹ôô<�W^nž-[½ÌhS¥Tgš3_]ž©‡«”á�âù¬+æŽVXRÒ»mÏ5hÐ ÜšÔêQxNÓâ�ö ‹ÙRMÊ|ŠsQ0NFN±€k‚\ÍÕ¸ÐIVb_µèÞÉí/c¬IEùÛ5‡+U|?wî\;ï¼óè,Ãý×i—!ÌÖ¡÷v„t¯ì§Ï½DÆWá;ï¼ó‡l¬§ $pÒ_°°¸0Æò?½ß¨ÈaÅ*Ū® …�œµcfDm¬r8rsv˶ÈZ£ ”À ÷C4­"viï°,X 7cª{0£B܉›µ«ãO?ý´©ÞÆÞxÿMû f¹Õ×ÕÛÈI£í‚.µëý¹ýôÙߨU÷Ý`ßúíìäóΰý¦ïoÑ¢[¶f…½·è}þÖüqÞ+“À�eÍcV@JÇeÎ^€SgØŠ¦–x+Ä»Çõwálç²/+¦ïø€Ðq±Ø–òuµ 9C* mÜè¥W‹µ6ØÚ °¯¤¥-œèH®AŸ¼ô åëµ<Û¢…æØ@·Ö=u5@PsF «¦¶ÆâùIeò•Ù™?8צ¨� óá¿_aI!s`SgO§)®mX±ÎÞ{áÛ¶y«Š´WÙ�7Þè´³šû®`°¼ñƆ{ê—¿üåN}g^söÙg³Ž!µ7Ê—1Ò—8÷eßÑh¶Þ)!<½¼‘b±¨©*¤s4ñàõëÖY‹î�1rŸ¤†É2Am¼ÊÅÂÖ‹•n�AøÌ3Ïà>$¾SàE6'u�.aŠÁc”áö®”ûyºŒMN°f¼0Ì++² _x£¥ fCËäp´`êÈÑÕƒ�˜9>÷Ôã÷³c�šh÷aÅ¢a�¢¶D25)ù s-WqœœÝúÐâ&$óåãwÝ($‚pcvùù[|¿úÆmVPR`GÍ™myˆè0uV/ÞóœÝrõ_íÞŸßa5KW«ÎªÚŽž3Ë l-á¸mRÑ5ɯ½öš›—¾'€·\P¦%»ÿþûaÞ¸L±ò­“B§yÀ.¬ä’Ábkýhý]Öá�³{y=×™<Šeí¸á†¦ž|òÉyÔ%rº4µnEbÍýt¿ R¼ÀòÂ*—ß@ag$ =øàƒ{ÄʽÛ�8)=-‰…Ι3‡Ï¥±u…æzŠÖ!H^Y ÑËC6nÏǛ‡•ö=n¬M:`¢ 9¢¹ù&Vfë7¶™ê[Ûß,° @K#Ãu˜¶Ð˜à2L Ђ¥b(±žÜT(Ï®b_ÌV+nÒÖ¶ÿe‘•Õ•q:Z,|k¾=×Ó¶±æC[>÷{ñÞçlñ;‹Œ–M'|ùd«Ue­‘¸¡Ìaq¸Tûš�ù�ÿø»æškìÝwßÅõgÿýßÿ½7í¹pk‘ˆ’¯¬¸ê“4ße®¨þÊ;z©Wg†K>‰_ýõXÃÙWž1_aÝ7Årý²Ï")÷[T¬´ —¢38Zçc ¯�ÝÛb}‰ƒK7G}@«œ�pd�êÂ@ð¢ùS‡DÊŽ8,zĈa%Ç èWXBà¶ ¨Tǘ”)~UdºELý=uÌIÄú—„8h’.ñ^p‘œá;¿w˜Iˆ"Æ„uñ\ Ì‹tõ.uR·€ÑüvóúÍöŽŠ�9‹9ðlcÞ«ÿ°õ;HÚàyÅ-Z1:è«pø`;æ ÇYNQ̺Þÿö·¿íì‹ïðꫯÚe—]fÿõ_ÿeô™Ä5EâÅ•W^‰Ëp¯�†c�=–+sd´‹b²ú¹c{.Ð(îeî‘.¦”«ýÔO�ôg*>jïÚ£[ �È6²suoµ—†k5Zu�8œF�*¸êIv¹âŠ+ìÒK/5~øabŽxBÒ222be йL`êç¸Â À«%·jPø€™SóæÌ8tи¡Ã*#¸þBü ‘)µâÒr›¸µ3s°Í88×*ÊIõŽ9àÊí¸|ÝL‹A¦T6(c )ì]Y_…»å<¼z¸æ,d¯?òн|ÿ Ö¸½°²Öæ ƒý÷g¾è)h¦î�Ëp³�kk­E¯óó§xsÖk ƒå–[n1õÍ3±Ò»Ù(@û—ù»øâ‹QÄŸ¸ìáÄO„a‰„¥ì¡óƒ¹´Ũ¡žGk ’ 4,@ž)–•g)B�vƒöD½æðZ(/‚o�V«xØ’µk-®ŸÇ©ùîç?ÿyæç#IEtå‡õ> :à ºõú�Z—ù/¼`÷j­Ô‘—5I!í�;ÎŃyá]ÈQ =ó Ë�ä@7� a£GzVH_þsqadÿ¢‚HŒNíM;w\€’Ü]qT¾UUUX4Ôj±¨‚¹- öþ’ˆµ&c(á´1 ï&$®…æFÈáZ°2)ž5k–u•#øþw¾gW\{¥íTzüÛO¿ @Ù”OÈùX6ùð¬vÕ[¹`…ÅU‡ÐÐPYn«®”ûp!­›,ÑÔFº¸æ»Š`=ʪ[ç…„æÆ#3þ|¬t§6“nO�šaÿˆGÄ%KÆ$ÆïÁ>à5h`ô”¤¾¬+ç�k ‹­†µ×jU—DsÙ*wFÕ"�G²¼ã|ŽË,/àŠ‰‘N8óÌ3§vŽï©Pü©FóZ#ümJÊ.W"Lk‘Nà9^3çÌ3�8™ÖP"V§„�R•#¡ÃW‡Ò}EÌƶX‰sµö:…Z1î ˜ž!ó‹¶…íO7þÙ¼7¢½¯tø’²R�V«yé7°Ÿ!ÔØî¬o´¢¼B›}Ô,›={6Š8ãÀ;ºï¾û(øÅààÚ †…‚¤ækùà y0ƒ¥­_¿žTj€·0ÏñÔ‘ñ>€' “±†��ÉïÃ52Ârßêów D*ØÁXW¼|cË>àú0 ”«o¦jØ"°ÔŽöeÛä:ŒkÝ–j¶*¦HFàt�Ò Ÿûœ%ÖÄ}ôQX1gkÒ½Y¦ä˜©¦Ô&ËÕ8PŒxƒ;½�‘Ò[’}�QR*p¾öÃpn^xuwÇøR .$"ÙX—”?=iÇîR�íF …“ö�íù׬¥-G¾ô\khŽ ÈÒ÷%HÈ ®�Õ�ÅCàæÄ"íRðò 씣N¶ñCÆÙßþö7@Ò»M-Q�`~ÔŒ¸É75XI¿R+ÐÏfÿó±vö§ÏâZ>Q}WgÛ^­“ëè7¿ù ë@ñ¹dïai^û u} )€Ä XÈÙP(9”¬us²Ž|—ÇÄ^~ùe[¾|y¹®­ŽËt®Âõ$vò\–é(Ö¹QÇÉ 0–Øî¾Ý ‡?øÁì—¿ü%�Y`À›úŒjOS�Vû9çuüžõÂÀˆê>,ðŠv°ÇŠed+A£DŸÁÞHc ð>9r!VËHÝ®×÷z³wÔ|êà•ë‚­ç;«t`ûD”Ù‡GíŒãúYK¼Mgoí´7熭YÀåÚ ¥U ÞMˆbÌzƒ`ÝâÊR-MÆNR†­�¸aèìÁÏÌ!L„XÍçd%Ó8—Œ»½`â7ß|3 ’y§>‡X™épÂÝ‚m¬pó40$á-ËpœlÞ¼yÄâL¾7Ê…M{#˜ƒáSìñ®Óüß5ШË2ð­ù 1ÅÃÔpxØW¿úÕ±Jn ó=:+ÜoÌ ¬+Ýz%&ÆóìuÖ<¦ýw¾ÜÑ_•k1ÕHâ^þ�?ÿÙj5Ÿ…2BØCíÜ—ì/˜:íϘ÷MbÜKµ&×83,�€yuÓ±þQ*KÍ \ºªÍæ.nà8[µ.lñd‡ÀElƒÆ³0.Râ{Uj7×K‚Ê…Ÿ©NèdÃ1\7(æ’±·E¾$U�fþºbM[¤lš÷<¹ëÖ+Å#÷í÷¾÷=ëHWƒ%!®kVwô •›n ösÉúÁž.o|´IĶ:áçÒ¼³ ¸|;«J±˜¹Š‡ŸrÊ)×g¹Â˜;æ‡{“=G¼*¬}1P† û%’û|Då#[d„š«!c¤¸,1t0Àx=L–I�> À+H•ïÎX.Ãáé˜T ­=÷F«½ôvÈjëcrºTø4‰ÜÜ . ¾×uzÀíB R7 AÐ%ð‰€‹†Ž·÷¤lèq7@qŠr¹þ¯¸˦µk±Æ©eÛ­kÓ‹?©š8 ±Þ ›Ì4Z Ñ!Ÿø˜ –>Ê �Øk†íåýÈø¤�€R‡¯g�¸6J|ö*ó à⮆Õò;º¡ëõW=ÞQ�edaFÜxîA¹l(¾Y5}úô<ê»Z`Ô¸ô=3Áð5gKdä4 üyn©öËïäxJëܪŸEÓ|£Þt±E >FDm±öÑP0Ô ˜W72¹ŸÆÄ4¬Ë)Ȱ­Þ >…JØðÍuS;fø6OXùܽU¨="f‚â”"ÉúÚ)\6üãí]w¾”Ð$¥/®3®ˆU¬,×X&E¾Xíªæ*cqæÌ™i�ògÍhi…R#ó’¤ @ ðÇ·P(Í#05HØ ààgÖ?V=.K,s\ ¶ºÏõìÜ×ûábæ3Pæœ÷äoqmu´œìŒÒäl4×V ÊÇyôîCÍVlÜ-.֕Ȳ£NNШм�s¦º®P&âƒW]uL0:XÖÎVkm·Ò³PÅâj}—iÿ7k>CZ¯u›Úåé k…C‚5(ÓÚìо,´@ðʰ„ÕÇP™ŽuqTEÊGÒ ƒ¥ÆÍÀϽ]tb,­ŠèTnY,Ä,ˆ�Ñb D°~²‚g(ýë'Ÿ ë¢VÇæJ)Ýüä“ö� CiãÔçXª¤B“=FZ>€Âšä ÿ'&ËÔ3֚ў!ñ<@äûVòÀ pqq(ŽÏ½k’GÞŸxµcÛMžûë®»ŽBp˜¡=ðÀÞè`?ºž âYã¢Eƒ‹]©±&‹îAŒÇC…êÍX%àÊËT=ó+w$`ÃZbXØ"ûÂVU“·Al•¬E � •? Ð:±Vxøí¢˜æ;æ’aò-�¼2,1ç2 ˆ¥²®°;@2àr vÓuÌðÀ…²è ²téRÜVÝ ^(x ‡”;|tO{Ò4L ÷Q µ)’"#WS™Ü†9ú@‹ÿow�9Hj¼:ÈZct˜MÈúâŠÃ½ÇÔüYR°a;ì¾àåëúÚg=²�¼¡KQ³YŽÞØ£Dßùüüóϧkõitðà;Âq{zÃuXA¾‚«CÐnÑØd=/¸ØÎXUÊU8D†C™˜WÆÊ`Å”*`Ä`  €X�Ö£VëÓJp¨¥ ”�YyC{&Í}žÊ†1DD}Ø· Ioi�à•a—a©óµ�u7Q(¸`\<¦.— ÏMÐg€ ñ}ùޏ»2-dùÑ©&E,`È,`”Ç¿þë¿d:�_”]®;“+â¬d‚ð«ô=èm�ñ}ˆ±íMª?ƒÏ"ÐÏH7Ÿ,¸±'ø,ö æ™Šv‡+‘ïDü†Ñ‰!ÌJÊ”)tõÀ•Џóùa­g\@ZêÎ÷šíNX~/fO$k^÷Óœ –‘2Te!\°™Ÿ¹ç¾ÝÉú¸ýejÔº•jM’)î}Ø<ëÆ`>S×ÞõW¤õTâÏþs�öÀ¼L×Ï0¯(ƶ«æÚÞeH“]˜V,oß.„ïƒ QʲeJ`­œ�DlÀ8ùüÔþsÌ95bÞ�Æš`ñÂ.`�Z‹:âSœ�&˹@Ч•�š*tÉ(#¸ÞaG Þ…(®«ÊêcÁŧúŒŒž®f‰KñŽ;îÀ¥°Á.¢€½¤¿öðpw*ñF�·øú=ëš*0ªŽ ÃìQ®=Ó¾†y­;Ý(†Ì ÌH³ö@½ö�ùú.¹�£¡ìÊŸ¾�6 •ùU'ÿæ{ï½÷ í’aê,�¼2!cGŽâ¨ô|ç2í:oƒPî8xǸ¢.ý ®ž> \NèAÊ|ÆÀ‹Äί|1Œ\f°æ™yG)`…ã²!A€ðÂóœç„û  �'7g“@ê5¹ ¤FˆÙ´�* %—N\ï¿»Ú5b|¸ù<óÚ†uò™€Œ°Ä…éc Ù.êhÙ“ŠùÁnfÍsޤJ`½‹—8æõ~´ŠbÓ<¨¹-S{­Ø9çœÃš[&…µ%st™Öy‹€+G@PÅð²è³Ç“ý Kw†O’9n/º£‹RûPò\RÌ{µÆó®íÖV $¯ ¥„‡Ü!wÀåµ;�KÀűð±Žb\¸‚ú2p!ÄlH†ÀÒ¥ …y|ê©§ìw¿û7½§öm˜`Gôæãÿüéða÷ù;û¡j¼–ˆ�½/0¬ÓÚ Ñßéïþ¡ú¯Í¸Ê`LǤøLÖ€£S,W ßÃbø,ºnÐIƒ b€n¶ €€ë�8"síÒúK5ôØ>»è½nÎ@äÞ›.÷éÅ‘SWÈzgZ8Ø”½Ý¤=‚Qs�ºw/wô81ì¹ ÃÎÝL}׋rc¿ªu_.àç~g¿2�ô¥LiºÝª®1‹eDEœ;6�¼2&Ø–s–ñsÈŒ+#Ë0µÝ›·O3®€÷éâ]^ÌÝ/îºë. ÏllŒŽmÿÒ—¾Ë#o�X ë†{ï'jít³@g‘2wÈÀ Ý™ßmÁ•H²…¾,�ï”êÚ#^…R‚Åá&äpH® ö pñ<Âú³ö¸))&n…"S†œo@ð¹–%Â|Òð%ÇÀ{€sXåíë�»Ó¸§›\ˆŽGj®&ˆÅŽ’�9ãŒ3ºeÞ04(C¨ûù0µû‚ŒŸÜ‚)R)#åL��6Vûá!Õ=.ÔÚãÒ&î%–çáç¤N¿Þ(—áV§ 7X xeøšû¹&šÅÜ5¤Áç8àÂ2OI‡ß§€ q>~êž`]Ò½ãOú…¾Ì#r” ƾõ­o}"«¥Ç{}ÿÚkíE½?n¡ZYˤ<ŠIíÒšñyÄÎè_Èk}±*ëJÁ1L�ƒ Ü‚ìØ.bšíº8~öI-ÔYñ»_|‘÷$NFš;�[¶tè »%K" —µÔHŒè¯õÝ¡ç¦8CJñ Æÿ˰»pºÆ41®JIë’Å×B†*sP¥5>Jng€kwr¨l^—+z¨ö÷>F† m¢0ltCÓ³Ï>»F{š„¯åq $èm˜á£O8/è%gŽD ±P9 9â’L\°~ÞW„¹�EI]Ì'RÄ̧ SPLÌ�›ë—Qßþö·»<ÎÐÈ&(ïSܽÑ�ÅŒk†„'&„2¼ˆÁá*LËxÒÞßyç˜vT¾û<¿c~°ê‰‡à…Md€3Ïõ8ãºY]ì gm¶(QæCûšëjÀ®´ÌÉT�/jžËCjá`YìÞpÆ:§«;�øžôTð| ÀnÛ†¹£V0–¸¶.-ÎU1D€›½Ìž6Ú1 ÄB.V³5ìë@�³.¹2'j”_tÑEW·6Ÿf~— ŒžTû§Çå¦ Ô3Úço)‰ˆÎí¥E{n¹Ø6{—«€ž¤êwÝsÏ=‹pí¤éqw´Ü $pã*ë”Ö(EÈb\ᨮ4Mv}Aé¾(t€G±SŸ´§‚ÿë_ÿJ/@âD(\�vÞyçÁºÒÖ�¡XÈ$Í›¦³¸éxÎ15þöcBpç ˜SAÂ×bùsÕ2²/¼ðBÜ@{K"Y„Â*)ìÆ5Égøî$‰ðÝI~4˜ßR–Ç5ôsiü ϶ì?âh¸ »J˜{ܳ¸:×$¤çÆ T7ÊP[ê »‚ ÚN"Dó6Z×1YGÎŒ¸üòË“n-#`½}!ùßFoËe¼Mkµ@.Õ<šF àO##‹LÕ‡”´Q£õe-wbÄNÅO—è}¶iÛ\ÉÁ2š±H^®ÍKR�QWD®ÂÊ"¸P˜Xýlr”ݾ*Ù�œ©ÔY×!J‘SjqÝ‘$�ûÅLì ôGç·æš$@‹Ø †ÂëPø¬J~w,‘ô{@„öVħP8°ŸöÎZ²®¼' Áñ+§Ÿ~:×ö‰»6ð^ ¬s€ ã{ð™0s‚õÎ÷jH%×Âþ|V%¿Ìt–U—‚ÌðrìË·®âú¢Jb9P,¹ARKæaƒAÓ¯i U)Äh}~%Y¬dkvw Ü�~ô#»í¶ÛØÓl.€ŒìTÛ©}A=às*™ æ ÍD†EÓG ¬¤ñf­ë*=nw`U£ñ׌w‘$ˆyM?>¤ ¥Šs�‰F.�B>H@sÛûÅQp(”�?Öd_@Õ™RXÙ„n2æ° “�Z-2ßPÖ틃awêN@rnZ€ÀÂ¥EÝ./N þHR¯£¸fHø$@�š-ßGÀõéö](tN@Ø-MŽåZÂ`.¸f¾¯[¿¯xäZ,ÿH ›ÁLø¹+ÏR£c=‰3*¿Ï™#€-)àZ)Cã]íùÛÍì!.­ tÃX�˵3 ¦Ï)ûìg?kÿò/ÿb=%¬ÁM7Ý„›Dö £}Á9L?Õ¨iÓ\½ÿÒK/½'À¨øƒ�.Iãá€uàÕ-rðA °Ž×F½Xc¾. ïQçc"û°$Rôq)œõO€JGŠÖ£››ì=”¬WìðB)·wïw"n„|`'¸Ñ`yÄgHAçÿ©, !æ@ñ-MyÀŠ@:ëj€ŸÏ$¾ÖcÙì!\§€:‰|o®%Ê5q�d+rz3-°H�h3!̱²ãìûßÿ>Ù¤ìwÏö`�I]Ï2Åu^2³_0ÍŸP/à*¼Lï}°öP¥ ‘ ÅCvŠ�Ò“Â<�%ú‡?ü�õhßÙ…=Žqƒ±ÄÞãù¸õëzý³¸W]RË�º³½V xÑ ïÿtõ†±²wFk&)nŒŒ €p¹Dë¼MëqH'À+äÚ¬]ªq„ —2ÕÚ ´FÈe"ù„±7±B® w´ï-者=Èï}él�G~fð:Wã½üqêòØwü ‰+uÚs¯hN~ef¯q­¼²ÍUШͿLŠužnŠñº9 ucDô|_fYŒ„@«I4Ö©V`µçGZ*֞߸³1¾£±á3šÎ Éq v™U…z>�çPa·uN(¿ŽšçâŠúÙÏ~Fö¡!dÒ}K°ã}j£P ¸°páÀîÈÃ"Øžc!óº@ºTHÉgÎ)/Ø#$¿úˈ˜èNb^¶›û5\Ô‡k/)¾R‰:d”„0Pˆ}îmK1úLâšv,œË~ã=[¼Ø[°o_:\¼'Çì³…¹'%2’ÖißÝ"—øÍœžb�1¯,‚ùY•X3ei}Qà5S@ÖOû7Ü“04Ú¢‘p“XV]N4gET ¥›~®¾oMCccyskË ý¦ãò¾9þ*EÝ:‡¥”î^JQ/)ç$dà"Lg5£éD(¡aXü`G %ÿ::Ð fçë³èf€kÒ±.\\( d p]vÙeÔä1çæã 2>–‰…<æZF58—{Ò5£5>ãÀ«XŒ'WnÂñ2N²Î®–‹÷ÙÛ,MZ}Ñ�—3àBwÀ‹ÿûk$¥ÃMY¬¾3Æ+.l~0”m°k‘ˆ~"�pop d^Y-Cªªr¥(Ghãž$àúl[[br[¢Í�¨Ü'„bì¸F£bZ›<«u·¯kmi‰é¼¢r¹ËtÖ:–U«±£]‚F‹sV¸XF¡ûÝ%E4]qѨ×IÛ‚mq=õV¸| µZĶ<ÛÂôüóÏpöª:wã2D1¡�°°°;NQÆUˆ5ÍïÉL:?5`Ô¥a4ÀpóÁåK}ÓFýžØ×ß\œ8æ �g»=’«2ˆ|¹nG8JgÍšesæÌé’ø#̛뭇‘ƒkîO7Æí)c 76@ÙéÌRjÙ£®×fR1ØWÒWËÐzŽé°l–@ð*ÌÍ åÄb0‹ñRä§¶Æ[O–R§µ°W30@+Š+c§ØûP`±4ª˜Ö¹GU²R’P<Ë�ß=ä,ꥮ·Ý�×0gYŸ«1ÎuáÏwÙV+•^\¡d‰R\6í ì 7 -‘¼‹Öô•¯|EãI¿?ÒåÉV„m¡ŒÈ„•Q›e_ÿú×}w ¤{²î`:4c†�^¬©À«IÞ bŸ 5Öbw¸¬ÂˆÜwÃ$ÕZ·*�\b˜tãïê¢b\ÈqPâ_°+@‹BçíŠÆ©ïË">†6_ìëK2¶Þ³@ðê-R9hPX`U¤±¿€ë%,ÌÖÍ2N`V¢›9Úë#n‰†Ãõº!W*žµXÀ5/'’³0i‰-[êëÿEÀ5‚8½+Ìnv}ÙÞqGa¼íêz®V§¿Æ¿h¯1Ü10d³¬Ü¹ª:†”x*^yå{ýõ×qïÀ¼P °-ºj0`_iãôÀТ‡x£—Mœ8‘F·ô/ôJÅéþîbÕ(xzDÂŽ‹äv‰~;¯q ?°� ¹‡h”°Âî´ëL,@FF$c�™8q³Ÿþô§þh\޵/¿üò‘rW/±@ðêm2¬ª*,¥Z¤›c^GÈ}uŒºIOI$åºYr³¼s~Ò�Ð×&%c¬ÛZ¤“ Äb9KÈ$”¢ÙÒkjj7\hfG9…%ý’Æ“˜{�dšµ=ЫNu`q¯]"…5PVwàƒ…ŽEL < ŒØÃIŸw †LC”=0â,(K¬hÒåy=Vt±­Àè3Iü‘Z>Ö­Y¢5^ ó’Âï§lÂ*­í@Å<£°\v½€YÒ W5¬’½Ö¨½{¦öã#H^½Uª‡ ‹yå ¸*`‡h©�}ˆ¬³IÃÅf±,±¤°£&çê[#€X"¦²Péî‹Å¸VÊ�²Ql§AÉ-b_‰•kV'Óc“èdÑéo5&“M¦á{<ûØ%¶4VÊ,€‚�a ŸtÒIvÁøøƒO¾ Eñ \RjÄ— ìÑ÷<¤Eskö¤ª­œ"ŒWå•|ÕÄ?�Ïßi�*Ãv¡ÈÉéŠK/ g“K.ZpE2N\~ e¬ ­Ì0�#•OÚ©øð9"=c�ü`F¨â§Œ#ºBÝŒ1W©ZòäÍ“7òê¢ÅÂÿá“'ÄÆB\ÆÏì—�ÂZ˜I cÉ-ã¢`˜½IzÊt6l˜1ȈN# Œý]FxèÀŒ–¡9—HôéïeTÔÆ^-c=˨Žõ¼1¾Ë¯¿3¶å4‰7=îž{î¹tZÝÛáy=¾ÁÉé‚T¦�+"<âJ(Á#]wjÍá˜?ÀØG!Ñ3´3›š?€t©pªªu°fÀ{ÐêBE¸.:‡›Ä#!ÜMa>l0/=ݳaT¿~ývÒ^Òtc讽ùæ›F”ÞdV Ú5]üd`ì 3fÅ™SpI7 f0åßt“Á~-ýÍ LI½Ûàh-á- ³áWø ÖÂV؃€xðÁa82¨ý°6ÂJø>ƒ1ð6t€§¡”€¢|\~¤É:ùºk¼:C[¨5á˜Û2™—¶ÃzXA+é$wçFøÂxިȌ±@ºð�ºs7¢ØF8Æ‚GNª€U%sä�éïÆLBc3°g\ªÂ8ÅÚh³åHSæpPcë…ªÈxR¬ŸòX}ÔU]N¡¨pˆ„Üp†¡ T‡zð<´�^á˜f3˜0ÅPÉó£`( Ĉúj= ÚÀËÐî‡Jp;\ Wʰ †�?—�‘+ám˜Ë`<U 8t‚_à¤TˆÁIHìíJe}ÄÝcŒê1‚ÞE�õ0cL”�œÒبQ£Œ ä&ãÒ߉µ,ÓMc£L7"l86–EúÕ8a@­ÏÖ¹ts8ò�““k-B$DCÈC!¸š¾ã üÓÜN˜¢LLtL9ž/M°8OoÁÀnÐû@a¸ƒ’æ�ˆâåÈ„i…Ÿc3¾ÆÀ2X«¡\á.†¡°ö€‚_@*ø…à{7&,!Óº‰ oį-xäĆrUU^kW¨Òbf¥±ÝÁØÇg“&M2‚69²êb ´Œ8™P�ºÿ†Úp:999ñ�N_=Ü{9þÈ<¹sGçΕ;&Ë•?_þ(⺑˜X„¦x‡eŸ.†¦ð¬ƒÅðzw§7ÃdXÛàOH€ãpÒÍK`¼F|ÞØd¬‹™ÖÄ‚CNZ£ä”yqìŒ�3Ærª”fe@´µmÛ6�Çà×JCM!999å‚ ÐzÀ»P.ƒh( ±°ŽAhKÀÖNVr¦——œtv•ª.¥ð´®%óRºPÆ¥t¡’‡{ö챜*¦hhs¼6$'±þ<-tª.''§p(ma"L†wá…€ñP’t7̃-°|°æ0�|¢Ö‚On­‹QIjzA ­{ɼ¨À´N”£Û¡ÚKؾ}{cþ¢‡Óá!¸Àåää�A7Ã0¸bNÓJ¬ß�/€ Ð711ñW :¹ ÉGcJ~jÿgbɼT�y-CíûÊ©âŒ.Ófë'Ÿ|ÒOÂUÝ€!¡Qu999…á0&@+È—ÅD‚k`,Ÿ€xøjÚ´iA¸hâÄ d›8q¢É¼Zµj¥ñ]ª´dZj#j­Kï“c6ÖºïWjòd¾|ù´ýãA999EÀíp'ä9Ë�v ‹! †0Åûg :9ñw‘AɼÔZÓº� ­?~|ŽÞ‡Çq2fííRûs7ë®®êrrr:­É̓?`$ÃIXÍù\íÌ)hÛ†2)Ž=1 IVëð…^P¦¢’z9vÃ5'sk-OQÿäÂ… ¯Ò¨5prrrò$•„Õà�Ið‡þ ° •“¶.¨µÆ±ýZçRpC•XNå‰3ãŒVµõîÝÛÛm·©úŸuºªËÉÉɩ쟀ˆ‡ $¾‚<ïÖ½FŒaª¸tv—Ž Ñüœ*yBÕ¥x¼ægc0´ZÙOƒ“““ÓßZ†“`ÄDä·CsÊk_ .4ÅÊ-Zd$C-§JÓ[ÔÀ„ÓIM.á1øä''''OÒåðìƒ�p|°ž H+sÊQÓ(r²t䉌kêÔ©~Ú ûHjkÇ}tŠ.Ý!rCdÃybNNN·ÁOð§Ÿ€…ì³që]NçmøîâÅ‹MûÖ^ýõDNž|U—gZÅ¡5 �P¢ÂΫœœœêÀbðebí§7ÍÉé<��ÒÚ�¢þœ¹Šyšãx >áAbZáP^(xoωw<Ö?V\~w�é¼î5( çñ8$''§f° |Â_0�(öæätŽ•��` h(îOŕ±'¿jª ä ¢jë6èsÃÃoέßvüþzNN}¾Ç¤ä‡šŒØP¸jïQ¼­îù­¾œœœºÁ:ðp^3³oÀÉéœJ›‘ulK�5üW^y¥R®K¡V0T]2$¸·@¥.CË5øK³7fïðî¿íåÞSM<×}Ò‰ûžÿpïSÙU^NgÒ•ðŒ�qð< wB�ÿêÁîÔ ¶@2Æûœ™í'§s:Iƒ}jÚŒ¬)øÚ[¸&À¥A`\y î5¼>¥v‹1q˜Vúà�~°~£¿³&}¦É¼¬Eß™©­Þúlï÷·ÓÊ�œ" $t‡Yð3¬„U°À0šÂ5ÿ79½ñà‡TðÁnxÌÌ~§s&×.Ô:WíÚµµÁÚ8ìt³ÖZ¡„g³qå‡'o­Ýoö“�?>ÒgØWöyìï6fÆ"ë?v¾u2Ϻ¾7×>˜ôóþ/~Z[.ÌÉ)“Š@7˜ óá'‹à7XÀ° ¾„PÎ^Æ;õ 8|2`Ügfnƒò9“‹õë|8­sɸî¼óN­³þúCÁl6®Kà…[j÷›K[ð¸*­¹?þaÓ¿YeÆÇʼ„�ž±ðàšM{†99(7< 3a9¬�]à‹ŠŠ:ž;wî´ýôS÷`G�Õäl ò´}ûö© ¡£ ô~:QVFfj-R­é¢¼¾…Gœ�eYyù! |°ò™ÙjøGääôÇèÿW‘­D‰êž$³Yû ÂxÈŸMÆU^ºû™÷?îõá—ªÂÒúÖ„/±Žg[Íf£üªÄœq…¦òÁ¿`2,€xH ´Mˆ˜˜ã«T©’׳ÁƒÛž={Œ)çYöÑ9H‡óéYmvô>mÄt�>‚ ‘ rx ‡°; êòÁ·µq«™°@Nn�kݺuúß´'žx¸k]tÑIŒëÔÖŒ…ðT6Æá3-cæ”úæÈo¼a›þ³‡·—zMÙßôõéíœq…–b ¼_Â8y&Ó 4¯âÅ‹kó¢ÜS¬öÿ|DÄû￯ó�´6ø9õµÂh‘aNáжC"ø@Ì>räÈýf–”9!&œœþ“ƒ%?ûì3…3‰—qÇœçÿñ :.°:@D6�|ªÎ¸§Õ^šøð«#íÙnŸÚ‹='S4¬^›ñ~ž®{àåáO;ã -]5 | !åÿb\Bk\µjÕ²?üÐ:dÿ‰tüyŸ>}´6fŒšÉl`‡a ô€\aμúÂVðL”ŸÅ1ÕÍ,,99Y-›3gŽâÍzÙÎ&'§èÿW�«\¹²q°d�ªê‚€ž.�.•MU×0¬lƒww=Ôd¤‰{ŸûÐ*6lå òWqØfÞþ¸}£!¦*Ð>ƒõpü Ò• dM*]éAÚ«ºÈèÑ£‡Ö·ì¿ÑŠ+ÔnÔÚ—Ú†Þç&µ¨¯§öØJ…¸yõ�-àƒ=§*¯%K–ÔU•åóùtN”.B:¡WØ?ü`NNgj*QÈtx{ôÑGÕAñßpà I¬?krøàx¢³É¸.ƒ~E«õ^Zã¥aiw=ý¾•©ÿ®•ª7ÀJ×`÷¿<üHÕ燾äŒ+ô õa¬ƒx8IS!Œ“jT¯^ݪT©b¼,Sñ ¦@�J#颩6 I ‰ÕÝ¿uêÔÉêׯ¯M�Ö³gO6²üÒñ 4P—Ù eh'ô½ÁX¸&„Í«lì|G»6VíWU°mÚ´±¦M›z§õ¶nÝÚœœN'…¨Ô&Ô�ãƒ>¨ðÔ ‚»† ì�íP"³É¸"¡íÂ0­ãT^vS;vsÍ·d`VãÅa‰�ÚOìíŒ+4UúÃrøŽè))BkQ©š ­;3%õ@׃œ*Ì3–"EЍGn;vì0içÎ�6jÔ(-öªý ‡ŒOQ[í±åË—+ÈqZãó«×®�Ì�˜PQmÌm0*…h± l‚ø€»â­l=8~ÓM7ik‚]}õÕŠ4«Öü9™™99e–U�;wÖ¦cÝ”úI ïæÿlu€q-†±P<;CS2®kx}IÕç?L|à•᪴¬hµ>vU�׌u¯ÔZÍF÷S�#Ì)ä �`ì‡d8FxbUSšNK Ô±cÇqWúH†¢ I{·4óL†¤¼}üñǺó·Š+Z�5lDûö¶3[Az©O“& Ÿ|òÉýóçÏ÷ë®ïtúöÛo­nݺ20µ$ L_/UÓ;à;h‚GŽ7„õ°×3/àn9…¿™gøªŒ¹©ÐYKöùçŸ[ œ\›Pø† Ú}÷ݧý˜(p”ÇÍ~­uÃ:X-à²lýtSÑê}V²žu’¶ i}K­ÂJ�¶ÇZ�³ç{Lž©ñPa!)§‚0 ¶A"øaÏ«¯¾š¬ Efiñ_ë(º0ê"©50ýÌš5Ëdl˜’©wþÐCYÕòåmr¯^–ˆ¥|ÿ½¥ÿø£ãØð~Mšl¢B˜½hÑ¢Ú‹YnŽTÛ‹efÓ…YQú£°fBMÈ"•ØÃ°v˜×!Lëoaýîž}öYS‚ÌÉIRÛ¾=7“TZÚ·¥ÿ%=nRÕi�-°&B… ¶}݃oL¹çÙNVhøÀêano¿ý¶mÛ¶Ít'x:q2°Ú�Ú&“<]Ê1EnX3¡-Üx�O©¯ +!ÄHËü»Q¦Ê—srÓà7oÞl&L°GyÄn¿ýv�eÓc$ óJPÛÂ'ðD°t3tæVùƃv¨Ê’qU~â=Sµ¥h|Ë·fÆ5{cúŠÏ‡…¤œ ÃXØ©à×]mAG–-íãR€CH�€R‹Oa‹)S¦˜ª2UL嘲ю¶ÄÑ/¿4™×Á¹smû´i¦ ì«LooÔ¨‘’p2Ä3N³Æàt~�FHýÍÀ¦«ƒ° æÁ@¨%àBꇗ‚…°¶C “yóæõ«mHûÇô{Ñ´“Aƒ™Z¹¡-§É“'k›þW´Eû)ý¬Yçq³SëÜ0º@a iŸ–”¼ï¹�k�KæE¦}]þgº~º¡nëq äs ÙäZ X à<¸SI­�ñ¢÷õ×_«-¨Í‹V®\9SBp†$ã�76™Ó;¯¾jª´d^ |ŒP6š±Q×2ÓPA¡Zgû˜lèСF„WUXV&–'àì„�°bách søàß k`ø¸Óݴº8©ÍªÈ³n@B[®â;v¬nhôÿáÁÿKšªvøFÂ=TF€1Õ`ŠÆê_‘~*ÏÆd™—¯HÕÞ¯ðö¼a!+§kàØiàšüþ%ÕÒ™´iÓ&E°µDi6™•g\B/—ż5oî™W–HŽ�µ�ºw·ë%¥™†3fÌ�QþŸšµ7|øp{øá‡M›š5Ø—ï9³‰žuu|ûal…U0 ç°ÍÏ·C,ü;5‘Ÿ»êtý-¢Q€Fsè~ùå m9éë’B×YXî+-,: �ùa ƒ«!¨¤V §!­úÂÐ$mDf†¡UyzˆÕi1ÆOÅõo¿.,dåMaQ@Õ•ÀúUz™2eLQ÷3I4H+)ɦƒé4¸Ó3®¾}ûšâðˆiwÅàh*¨á˜Úˆ½\O»‘ý_Z'û¯ÒRªÄÖ¬Y£Ãñ4¥Þ uœ?í‘™ì˜J‘â,ƒ¹0 ÊB0«8Ä‚ØÃv†T…gxjJ^{íµªfÝT�ÐH z¨½.þ<”` Vn³‘Ó~µ2µ:Yä��-êÖç-êŽæU¼™E^_×" ßs0üÒâ3Ãr_A�‚ϼJ01cÃmuúió±a`2.U] ¼üLXHËé˜ [!Øh|„ý HèŸà¬æõ©A% Õ.ÔSmP s´hÑ”fªL0˜çãXë’qÉÄ©¯Æz™ö ið§L( ¥ÃY¥ªM)G™¦Úf´Ðdd™79 ½Î'¸È+œr8 â<»` ,‚Ï T Âc=ˆ…ù9²¶åí¹Ó`d�»¤v‘SÎ5¤´´tKNIµCGmãöxûqéÿùï8û&}¦›r5=ÝW�A­µ’uß±�½cb×>Ð'=úަþ¨âMe\@©v‡1ˆï „[Õuã#}ÞôHßíãRÅ%ózªË'þgº}ËÛ ¸Ë·«º–ÂHƒU«VMÒt†ýû÷Ÿ64¡#MT%­^½Zqx™—ÇúõëMLI\\œÉ”TUi}êJRMÿ¢Bû”鳩̞à�ëÕ‹}||¼�E›þÓ£ÊPk�fTR_G ÕºÀ«¥F¨Á35­(r8 R ŽÁX³¡'ÜFv¼ ¯ì$1¦q]žqéFBFÌ*éDŠíÜwØÆÎ\d�wüÈtÁ¾µv?c¢„]Z¥»]rg7#¼`ù*t¶¼å;[žò�,w¹NS¦£q1Ï‚ö˜”hg%ÛZT‰Vé¼ýOø® ªëÒ² ~ƒ§Éˆ+?ùž6"kZüñZÍG7 i9]“aWÀÅz�ÎÞšI¬=sU£iìù2Ue hb†6 kS²ŒK†–y¯–. Ó¨¶äдµ´ÔÎÒºŒâÛ˜äÿuâ ø¯$#Sä;ÖÜ4s‘V¦~­i�Ì31=Uj’j-óºY*ø`7l…åð%´‚‹á|ëbx³Îç#婊Ëkb\n?W’ïp‚-\g:ÒCѵùV©:�<*\µ·Ç•÷ô´+îéa—ÝõÿML\\¹›]T¹«ÌL&&³ú»‘ɸNoóC2l….D*V½ÏS¤ ·žJªmX»ùh?Gü¯t“â�ž‡%püp”a¸ ú÷ï¯v`àÌ3U0§NO5­…É|´¾Âû[Íš5Mk\Û·o—Qœ¶�·víZÓçÕFYÅéeZÚ¦á±çQž‘mÙ²ESÖÕZÓZ™~µ7U¡éMUf™[�é�úØ [`Œƒ{ âf�1AûÞTMÊp5J)Lm7n9%&%ÛšÍûL‡&v4Gk8öHÓQ¦Y}\´í†‡ß”aeFæåU_T^ža¢·ç¯ØÅ{9êÌæ%NÀ Eƒ¤êФÚúˆiI§6$Ó2doרR‡½ÃBZNùa<¬…H‡í¤·OŸ>Ý�K‹þ´­4†õ"-¸…$üÖ°ÉqXïÞVšõ* M••æÊÎhªh;ÅZŒLUëlú^Ô¶Ô$íOÓlF™¬ ƒ�¡i#¶Â j=ªí˜ ´"ÄȃE0 šB>8* C`{ÝRe¶º� úÒ^;m<Ígv¹Öà–�>{}ø×öt—OìÑVceZº0kïR`µ¥V¡gD´=3Re%ãRÅ¥· UcªÌ Ý×Ë�.¦ôÙŒË#vÂÏЮËfó*Rºþ»ó«<5$UëwZËÓ4 Ì,Ž·ÝÒr*_ÁH€c°‚iïþƒzw�¢ª(|ELêy.æ.¼Äàño¾±×h#^K;� ©9þâ©‹ ­Oï,­ÙµlÙR³ßdf*¬=iª<ýT<h×ÅÑjT+q,…Ÿa>¼ Eþ¡µ±\ð-ÁXÚµÇ4i_³õT楔çìÙ³Mß{ðÊéð±D=c¡É°4âHåÛÿõ¶)IwK­·TmÉpdJ§­¨„LKƤ�SE"ô±EªÉ°2ÓË®¸«›å+×Ör•ncÑ%[k­++ó©pâ`! † • æÕºâãƒ7ÐB•¡k½+#°1b�›¤áÔ~€ p¶b@>­OnlTkïaª®ÊT]Ÿ“â“qÙÏ?›Ð„Œ‘:˜¢îeJ•ÒzÒwÕÏ##Ózž¶hò6Õ© C{Ódbj3¦˜8¢ 6ÀZX á(þ_î ‡0‰êjæ•.ÃRSFTñjZ¿Î? ^9)-øëªme”¹ºRµ¤ÊI¦t–Иg^T!ªFô9•*ÌÂÀD/*²žvýƒ}¬àÝ„<Ê´‘I�“g0±Dø–Á(xrŸ·EÝÊ]—”m0ð¤Ì™Ä¡ÖµÇËÿÀ+#¾q—n—2¿À:ABm+í2o JU—¦È«ò¨Ä¤ø[©®f½ù¦g^Bû´:p!¿š ê�„/4�ã–gdË–-³ÁƒkO›Ö•¤T%¤µ'Åó“~(رþ€E0 úB}¸.Í¢"‹€h¸úÃ|ØÈzÖaÍ�Ô™–R…šÞ¯¡ª:ñ6xå´7þ¨)ˆ¡Ö sl¸5µøÔ ÌUöôfÅëÏhdj ʬ8ˆÑdˆZ/ã(Urž�1ÄV&©³®t\ˆŽ ñ*5½¯÷¼·W ÆT¾ž ++NÀ>XÃáˆ<×Cx/«Ò}/S5ÒôýêgPhCë_7Õ|«_XHËér˜ K Ž}][‰³§+aX9©òÒÎü $ ‹qanIÐâ0û·d\jjbÆÒ†ÅI¸Õä\/µCEj-jÎâĉ5¿QÉK%ýÔÊ“‰éPΓJpB2üû`,…oàcèo€ž…i°¶Á>ð)$&„ŒK“LÔ¢Õz]pÊIíóÅ¿m·çºORe¤JIkXY¶I Ê”ÖðLG&„¹(œ¡�j)zF¤VZ½6ã7ñ¯–cdjú8½�>�ךäuzž�ó’{ZGÒ×Ëêûò�É�¡ÜvŽÄ!“¥ù]Ö÷¯ïUOõ3›ßÁ×mÒrºæÀØMd=½]»v -üí½téR«u÷ÝV�ˆ»Ljƒzãÿ{ç%U™|q”dÌ9'Ì9`N˜ÌaL`Žëß%,b`Y\qÍŠ9-Ì #Q0+9ç<�ar®ýÎùj÷;ï¼nz˜iì™ùêœ{fìé~ïõȼ۷êV•ÖW¨wM~ýu颣™ŽUЧ{wžß( ðJÚÿÝ@K*‘õ#šFĺžhLU–b¹b©b±b¡b©¢@‘£X¢X¨õ´"Ÿ´8®%­Á˜‘ÁQZV!/½;é®ÿ¦ô)-Å�û�ßR¨ HÌOBJ<ç¿$12]ýš^o¡ÀP'~Z’×BP|Om쿯»òÞ7x>©8žCê2ѵUÇ X1Nqu:R‰G\öï.ZÿË×÷ áò~¸öj}ãõ|'4iÔâÅpÅ"U «éyR‡al½ŠÔa?%¦=µ®ÕZíã;kz Âz@o¢—©yáHuáu:ÿ|™£ÍÉ�=pQR#ìÔ©“P #•HºÏMx¯Œ4A—xDŠí¿•¨–ê¬ÆJ­i¡âPt˜Eè¥Ã­ŒyùÅ¢3÷CF Ò„‡‘C" ~ ˆ¥Ý_TS3°ˆ“n„Ä ŽËÏùjDѼËó!LU.²Ëé˜ ‘óxœ¦gHµFÚ{>çOz} êbóý{ÔåT ­ެĽšëuÄEÝ«j§ÓîËj”&�:B½ë ̪ VÒ'Ä ì&[ÞMW–P÷ÚD›a7ÒiåÛhýåí+ºR‰lŒØ ñ?%F�Ü�!ChÀÌA�ÊT˜¯ÄŠ *}<‡ÍµÚWVÄ” È Ò‚üמçœscº ưÒ$ó›�©m™Šò9˜Ý=¸ y„‡ââ8– ´Iê6M�Ù~<An<�Ðå)›ýÇã—¹ VãùÔɘâÁ¹ ‡¤×gˆ�W•b…â%E;ˆ§.ê]Š;OèòdŠQ ‹”!ä-‡]2`ªþìð&!m´pÆ� jXÅ” kkJCqãüDÓ„÷_½tÐ4b7íôM5NþõW‰�@bLaRÇŽIùÑøì¯æ�‚ ¨4&H×®]å믿Fie~:6³©?‘ÒÃŒ�á‚z•©;”µ­XR0…ÄsPF(+ˆeI™ÑRâ<¦š°Ý¹ñ÷!u6ŒãD‰”ã“Æ´šd8.k©Í$ îVµÁ¡÷äè÷y®–¥xOq¾¢i-ÉkKE/µÄW1S›Ó4")Ò9ú³›„h´ÑZq©âu¯å1-ƒ 5mîM}üP½™z�¹ãöÛo&ŒXó3 ‹=hô�]}õÕ–¬g*+ôoÑpŒB NƒÚñnôi�>$ýÇM{|Tå�âóIâ!=AQŸâ{\ƒ(TÃx!0ÎËÍÝ”)Cj`—ýõ5A‰¡\¢iCžg0µY’¢ƒììú ±ÿ~8D¹ý)}°Û/Qðu©â;Wk^ òÚ_ñiPˆ°=e¨}_“CƒrؘŒò¯3ñòIMÑ„»vk3B„5®›û¾-fÊàæµ¹3wâØíŒ‡ Ô¤‚Bó݆¾:‚èx.“â!0êY¼ÎTäF­‹4"dKH€$¯�,Q‚�'Ï�xž­�Œ8§MæˆÔãxò4ÓçÆªÏ÷eúþ ®,Gd?*þº–FÈëd#/®Ë�Ãê^cl€pˆÆ;Œò§ä•‡Å›A¼3R�!Bä–0—�›«9Q& ›‹!T é?»!C(¤êøêפ|2B} ¦HB€fAÁ¡ölö0r´Y€�j�k€,¹N~n¤ec©ì\æLäš– µ×B¢Xø™Îá×Ê Tâ¿Ç™•·Ð™ˆ�MtÍÌ[¤B^jÌø�sC–Á¢ Q^¬jÄ¢•[!?Ù æ�©JiÅATÖ¡CT$¶\Iì%žj‰Vîþÿü§tÕÁ´OèTyæ2ëÐf.ÐQIÔú½Ù¦›’>l0¶ïaV!7gnòªB�(¸y£~¸ézs ãš„¹ ÇöS™­Þ€=ÜnÜfyGqQ«òÓ‡æäq,ò�×_î}ÓÆDÅ©Aë+3±ŽE¾÷ ’…ÜlšïÝ«ËÑwÆï€ßO©«A`¹ÎÄÑŸ>®5�×g;œÚç7®böëš’„¼6 ·ïí_;òZ¡Èò°Ô­÷£jløÂ… I'BBô,1qCnêÖMîÖTáŒAƒ ,ƒ”icméW_É÷©kjÇi€N•¼Ã…ÈØX¸,—~+ŒԬ̅©�b“®}†Hÿ—¾’¾Ï��n}†ZZóC\ZÐú©’®>�äÌMè­<á JÈŠô -«$Í©a­‡Àxê%–(†û1NEšúâ&Dl×Åk (~Îù¨qMÔåÌ8B½m“#þVìÈ«ÒYéU<¦Ø:qµTŒÐ÷4ßÞ'¯-Ží52¬ÿ'B4UÜàVvÌSä¹ èUÞÙ|�ú°P—QSÃUX ríUWÉ :ÓðÝÞkª â*ÖIó¬M£ëèÖÑH>ø`½7m„ÎÂ[ú¾cÓÙ! »‘›‚À -Lz?5œA¹¨L¦€°¶ŸÄ0Bp¬„€(íæmÇ!]HýËÒ—–„LIëQ—ºéÁa\ÿ�ÐHÂkPl\¿�ƒç›¢$-héCžç]deꌯ¤÷ 5³Ð“Ö„¸ùA^ÕšF,w6úŸ�+vŠ!¯_éõäéë w_r ßëÏ[ðÜ!(¢>âöz-w„UA©Š]Ü£GY°`ä%#•¬.RâºèÄå;ÝáyAX(.Ö¤¬Ò4"ÓçOÒÉcÆŒ‘ú!BTVV¡¦üY�q ‰5¶y‹”ÊǦc@2ÜØMA~Öø›´7Ì™�–ãø(=K÷áþC}‘^ä�DF] ÿ=>Ä�hÖz;7鏨営;ˈãÚ�$íu¨0Ž QA^2åw@��cA^|åwæ¥E+Zù7¦q@BOGúºùˆßnò}¥âü÷X«sˆ›*ú(F¸}Sù¦¾<0¼—‰é�*Œý]‡¨²B}�|òI™©éÃYê>þðÃÒGÇ�¬c§zßpƒ”——K}�!†}ñ+ÊÂÜ„Üìí«‘‘‘7jRw¤ÏH·aÇZŽÏn¾ö|#š5Úì烸8žMÌ0²„Xì1Ž ‰q <%�B2(<#¯è ®‹ã™º³kAÉYúÉs8‡½# éQ¬òæNôÇP‘:ä÷f)GÈO`VécEºÝy¸[ny€ ôeW—bL«£ºWò»õj~€ãŒ äaÒ|;ÅóŠQ®æUì¯ðØi§�rž{î9Kʹº¢cçí·—mÕ¿¯º ÏT×ᕺÿp�Rð{È—_.K'M’ú!B̘¿åbjØZnê¶øÑ‰©«…‘Z#åÆ ÞRަܸê÷\q\#·XðÒ€¨-ê\æ>Dá ´PL�޹¹Fž�„ÈPJ¾a#ªQwÔºª ’òëhR¶%ï‚âü·ÿó=ˆ‚á}A fhQ»=ïŸJmõßècÏ+NT4U¦˜¢Ç¨„`¹>@Œ´þld ¯DbsE'·åw¦b•©0�†žKê�Å‹,­d“ïáºxq;]áÁš”Í[µ’]¶ÙÅ%ýn¾Y¦}ó�ÔçÒ…Ì,äÓ¿)$Hò -Øý±�©+á>„(̶îdâßÄm5ŠýÌŸ¨AÚ�›?Ä—h’;7rêI¶îߦk3sç@%±òÄÒ—¸Q]IW²ØÜB;>ÇA9AP¦¾¨‰‘&µt#„F�…J­�ט²Díùým\›M9÷æªõù+õøï»•*)Z)¦n~LÏR~Ÿœåjƒ‰õg£×4µ>D0rœ®xÜK#Vë�Õ:ù|&S3P_Ô¿†*�ÔMx¦Ö¾þrÖYÒKmñà �ÕsçÖhu“:H/ÒÐŒ“qÖ¬Y�d0z„øÓâ?Ÿü„j²Z7wÔ*R“!Ÿý"Ÿ�™*Ï -½žø‹:é9T‹¯l,•ÝíeäeÊ %gã�bI†ã@.6"õ�mlêÍ&ÔóßÜü!ZÀÔW,x „g‹0y­�­"ýǵ™³�ç¡sMü>ø]üûõoQ�\CÂô'Äáa©¿®÷àê³o|ž¡¾ï(^UÜ©øZ?T@è4]3; ^I)ò ±¦ØYÑEñ¡b™S` [·n=òé§Ÿ^µlÙ2fbŸÇ:Ï\D™«„•J32Ï�¨–,YÂh)¹ûî»…z‚Y¡Ïöa°ûî» ž{÷î-?ÿüs ²uaà.u*Ô‡Ÿ´:Š€TDˆë¡�_`êÀ.ÏMž^ÔEÈëQ,q¶ySPÜ J,ÖF)pnT”¥,­Ñ˜±P�޹­îÄ('nþ=ÿ‚KhχLy¿œICŒV_ã¼¶¥ÙR ö3Hfø¨)òñw“h`F]qžXugû¹P¬¨Âö×?[©d¾Pþ©bä…±BwîM~ǨN~‡?ò ‘J ¬½âUÅtE–³ÓÿܪU«‡ž]XX(© Í3gΔaÃ†É jàØÿýe«­¶%BÑcɦÚÈÌ()6÷··Ô­Í{h M'€ˆžW $DˆtÆû_ÿ�‰Þ€!nð(%Ï�4P5܈¹ÙRgâFË 5K‰ÆAqS7k:¤„‚‹ 5Eû=dÄÅ50¢ 5Ĺσd}ò3¤d„jÏTÄŒ’dð.éP']ȵAÎÂEQr<“GP_6óê2çz�¼l_e¿ý+œrš¢Ç.Ó×CZ�ÊŽß)çûMν)Dˆ¤q€¢¯b¼#¯™Š)˜b¸®¸9cÆ ÈITÍÌÌSO=ŤzéÒ¥ ó Ú[½ï¾û²IBŠÅ†Z7ƒ¼ÔR¥¤V¦«ñËôq3ŒiÍm™ü-½öÚkåý÷ßË0C¤­§ëƆAq©5sšb2³©0ú¢ »Á£À $È{EYï•Õ½xÌlü�Vùâ’rYž�™Ù,F3™øf®5Kj£f‡‚”ÝÏè#­Ž¼ò‚!ýª ½'×­UÙ«I #DˆfŠÃÜúnÇr—\¼4 €°òyÜÖpÅcŽ�Qì¨ØD±�¢…¢¹ë/[Ïa}Û\q¸¢«ë=ûÔ)¶©ÓÔ­˜³ë®»Vï·ß~Øí¥cÇŽòÚk¯¥lêâ�K0;X:0¤ðâìî–¬+@fƒ‡H9gÜsÌäa›�±Æ£Ö°Å“Šƒt 9› o¶yÈ ) y’†„Tø�@Pƒï-uÈùx®‘#ç-/¯da'mföày�jŽkµß%iRÞŠ‹Ÿóá‚”æÝmǵ‰ô(¯Šmš„Q ýAŠ{/(ÞT¼¥ê¾ç±W)NVì¯ØV±!¯…˜jYƒƒðNTô†H­ ¨)$Wm÷¥»í¶›ì¥3�Ò Ï�:uÂÔA´$‹!ÞúôgÔ†Mˆ…G,>ü”u3…8~ìs KëHõ…éR0r5ã¤BjÕ“ìš!Lˆ�´!®F^ È‹öçöü;ú¿'%¥åµ7~nFlG˜]“oÑ·”œSŠwK3%0È 8òzUÁß�Dˆ¨¦­ÿ%HzñçzÌV»”b•®q©Pgc$vüñÇË­·Þ*Ó¦M‹5u„‘»ºˆ:é:ßdaàÆŠB ÝÅWÈUaN?Ä_•R'€p’m`6D®°µ$”ÍS4W õ¤ù†Ž„}_¨,jeÏ¥ˆÚ#•H;ç°ëâcöªB©¨¨¤îaÙ¬GH�ëâwgéFÑÔ+äåXžâ‘†¶Ë+DpDžä‘ß;‹ý2g(ÉQ5–£ÎÄUÚ]uºNÁïÛ·¯äææú¦Ž!0P{±y|61Þf󡬟 5Aú õáƒ3äV—*ŒcAŽÔ¾¨¯Å=Çìè\§ïþ³Ff%Df“-H!r½¼&aí�Ÿ£ÖüTªMáÀ•‰Âã1k FíÍZ�yaÞ€<ý4'×ÅïƒGòºŸ¾'ˆË#°rE¿†¸Ë+DˆæŠk�äWˆKQèÙì³ÔÐQtà�RcÚGè a.Cj5v£åWc‚Ð 3ˆÂ b@uq“‡\¢*©Öà|( r²e˜¼OÔ%×à“j ò¢¯Œóøýd£~šMÚ�Ù�¼¬ñ{Þêø^JZwyñ=Žü�7Èb'çPüM±Âë#%êJÌS+Ýyç�¥sçÎ2iÒ$›ÇØH#ÄÊœƒ¸Ù¯µ £]‡¦~P^˜&PJIÓ|1SèI×Ù5©aw‡ Å{N˜ê4ÂÆ4b *­¡·%#›h+°ŸA明¼üíÒF`M÷¼B\oéŠ!toÚ‘Š'£ó¼qS¹¦ÆMŸª“N:I^}õÕF–J �êÆ¨aýHÒmÞ$w6Á•Fs-µ/3¤¨ÒuuA˜�4ïƒFkJ†!•sð¾©ûaÜ@É™º³á¾�-$Éu£Þ’ %æ|‰\–˜9A“õ›�u0çrC�!¶TÜîV»LṖÀ¢SAÔØQµÝvÛU]qÅl‰n$†Ž‹—çÑeMÉnê0 )H 0­£À±G݇×qíW@ÙÐÀLÚrަG=••–~¤~Æï+Åô)J‹ß¯åDJ�+a}‘kEí6?àVivÀí²ÞFÛA^ÌE-p}£ ÚÀ"DkÅÙîûGŠÉXÌÈ)H¬tÛm·]~Ûm·å6üTbˆOFN¦–d‹£kùI‰Ad¨,Ôÿ�¢ fµœzÔ6y¶,S»ò”ß§l8�ˆâ'³ÞCX|@±A|¤fQWÉÌü®Ûž©ŽÝ/–õZïy¶P”» =­!B4h7â>Š�­~”7U!0P‰Å^ž'<þøããXœ¢A®ùgÚ7S#/›îž¸ïL•ÔS šŒDP^Ô°¢½e)CPW5Icr~(/äI&s#Ú}Ôq—;‘M¶úy‘ò×É:en8ÂöŠ!Bl¬8XÑÃ:¦;3Gu J³µ&6T7E/©Ä†×˜lé?æŒèT ÀÏ 7n¸Ü|Í%·.HuH˜�©8,î¼çØúš©¡?\«‘×ôy+dÊ”)l’��7ÞXtÕ’è0Ñáܤ‡)öP„Ñà›Û¸ææ)F*;[}9ÊËK)ò}Žâço¼q\vvv•Ô>2€CLœ¹” ¨³�CþrH«ëàF$ÕFš�ç›y"í& #OÒkf`¨# >„hµ©ŒïƒÉœEÙB”””ȃ>(ôl�i¦„9«ï(ÂìÃ�fNã¶ŠK\*ñ7ù>+F�*h=l‚nq^Rÿ]‰!¾; b‚ˆÌõ†²²)ò8ælý`.Ÿ‘F‚u¡:l’;é=jn6ÃÐRm ¨OT/¿ÿ¹‹³Åc{;ýyU»2À[¡¢±)±=7:W⌆Ž2÷øtí{_S‰s믊 ñÈ+ßÐTkµ,¿Ç‚b\ä…MÞV  €xîºR,�FêÌ·ïs-–ºlÐ ­i[¢MyùÖ»woÙ~ûím{…#°W‚ 1Dc2|”â~×¶LQ³3×ý‘ŒïСÃàœœœ QßFB±þ2²iæ ³Éì�.CˆŒñO¶ßk�§Ú8éLÒ˜\×™Ù†åz ˆ9%òByÑS6IŽDG°:ƒ……´�-R<åÖ)…Ñè¬õ/º]d% 6E¯TÌUËî×C† ™R‰õ'òò‹és‚�¬ÏÈÈ E *›ªÁó�×–ˆ¨[¥tžƒâv 6ò©ž§SÚ@mÓèé·[°4¾ueáÂ…¬>’6mÚø6Wq_cmdbgÅÍŠOcÒˆ|Ÿã_ª˜t 'LÔY‰ÅõÕÌÌ „L—€ ¢!’¦ 1@ðüd££¸£ ìXIÔ‡©@¬åÔßP‚ÜÈ�øê³â2ggJdχÞÿ²¬Õ’(~ÿýw9óÌ3q�•)&¹�ì!B4Ú ÒG(u ‰)V@`N…Í×Þ°©Ýºu›ºråÊ n ñå¸éÌ LH2¨1n°�‡8€  …¤ ^o*-Jr ‰y‚Ô¯Hÿ%mvæ&ÏóI]ÒÔk[ˆ±¸Ûu×GØÔŒXâNBäY¹IÓÀü±qÄ8�À cÜBÜF!Bl¢¸ØÙqg;²š£˜åÈ ¬vX ©Ä1œ[ZZ*™!>5û{l:‹T¡5&ÈÒ0¡NF Œ¯ õ'SS€s2åeõ4dÆÍ9‘â2Ä4b¯±™ŠõÔkšö„àYr™,Ø–þôÓO‹nRÇ}yU»úô°`¡\‰Úɯèí&tLv5±e.…Xí¡rÛwß}ÿøùçŸËC*1óÆB±ò?¦§@Jôua�‡,ÅWƒ ìÅjÏ÷˜ ’¦#ö÷(�³àC  oúVÿ2ƒIcJ7'¯HÖrÇwˆ. 5õU¥X®èç>€6ꢩ›Xÿ†âgO}UGP¥È×is¯¾úêÂùóçKFD&¢¼¢Dy ¨˜º�UžZ ¤ŒdP@¤ñØHlú- äœÔÇPh(*;.d a¢Ä 8ŽWŸë[ÖM åTÂå÷QTœš!jñâÅl‰`xoÔÀÑW,ô!B¸ç;_)¦;ËW”ÅÌJÌÝ}÷Ý—?ðÀÕ:¥CþÜ1uÎr9úÊÇ¢©:È‚4 ¤$_ô/úÀH2a2³†e¹öïo¡Ê˜ÀA“37éµ¾ÁCR(½ý;ôç˜L|‡Ì¢54ž X­-c]„\+iTÔ¨µ€Ú¤:Qª5É^üöÛoÂÂYÈË¡ÜmX¿I,ô!Bì¨8S1Pñ“«ƒ-�±×ƒ2~¦³ÙJ_ýuÉËË“?/Â* qÊÆ¦lP]Lž¿éÁa�äÅk!.ÌuE$(s7š9d�0+?X— 2£Š•ór ¨Pˆ×`AüšÞÚ€ñP5�wß}×ú¿u°ÕnÜÙnºNˆ¡¦ØMÑÙÙêWÌM2ð·h§�vª<ûì³åµ×^òô!Öm•²Ÿ+¡b2AU‘B¤¡¹ÿK_±ÀuD ,­ ËלŒ¨-®³Ä @þú³Ñ›õ?Ý0cŠ‘?$Šz4Ò²æn~–NMwK´¡ïó#¤¦Á6f n¶ÙfF^Õîïò3Å�îï6Dˆ.±¯â!Å8Å E~«ÒZX5Î(HìÉ'Ÿ”üü| ±î¢kŸ!¨•¤uR_¤ñîèÿžÜÖï]i÷—ǹ)sCΈš×oC„!®uÕ¼ ‘šòóÌ$(FHÔš©×NméÆäè”�¡Ÿÿ*k«W¯–Î�;Ë[la3óœÑj¨b;E$B„c¦Žq†Ž_9Ö–^Ö;ì²Ë.rúé§ ÃF—/_69¯ƒèùÄ'¨‡¤½E¤½ºôÄJœ…Ù$Ìõ@döýºº>ˆ ÒŠ¹žZ‘(ëþ}õ…Ê¥©|mƒ ümmµÕVìÿªV#ÇL—yÆ­HŠDˆ!6R\èÒó£FƒKi°VvØa9ùä“¥gÏž2mÚ4©¬¬”é‰!ŸýÂôŠD7ZRt6pb¦°úVF�\òÔPý„‘pý]8;çU›?~¼uÔQu°J7›t’¢—D"Dˆ³ïQLPd+*âÌТE aRö‰'ž(ô¬Œ;VŠ‹‹%D�³òh<¶å’FLÑñP(°ú°=ÙRˆ�n½'/À÷¸/¿çu)-«¨õ æAƒÉ�ÑVæ˜ç*.õ¯!’ÇV®öƒ³Õ—%"0°ÑF‰;äè£�–.]ºÈ[o½%:Á^ªªª¤Ö‚ß#›”­Fc;ºê+¬Œ¯ ‚¼À.í(Á,SQQû ƒ³zè!Ù{ï½Q_Ò¼ysûÛûIÑN±†"8wPôQŒqéÄ\çL¬„´¢*Œ?4”˜Në�sÏ=W´WL&Nœ(¸©j!ž6VξéyÛ¦l7þ€ !¯]ÛßWÌÞµºª³B¥k×®‚QªeË–ü�ÑĮ̀·Aîï2Å"ØëïW|í¦t,wÃD«£ [o½µð‡Gþ5öüóÏË¢E‹Bm¬©Cìï4%§Ò�ä›ÒO^Ûw¯,Ï®[îœ9sä¼óÎ3õÅÃJ%²ïõïîqEKEŠ"D ±½Ý¼Ä/‰åÚL#²V­ZU©SªJ-¿ÕFd¨1֠㤺뮻ä�wÞ©YZ1„’~•ôxüöE­q"¯£±˜þ,›‰!&%‚÷T×zmöb"¼�6Ÿ={¶¤#¾ÿþ{a3m)§œr £¤9;\QË"DÅyŠ�‹…1j¬4ÉÒ‹ÔÊ„>—Ë.»LüqÑ-Áèábø¨)¶©˜›oìÜèMú¸«žÌô«—«KŒ¸Œ¼ mHËŒÁÃfbñ U ñAF´©óχ¸˜ qQKd:? Õ6F¯ ÕW³ýºI“æ›ä®¹æIWôï¯DÛ¾=FÖ¢J3Sõœ/ÖÝôù!Bl 8Lñ´b~„¬ry)*³ E�Úðs5%“Ó»wïÕ:’ª¢1=ÔËÄøè*C”œH…12Šý[ Ÿåæ^Ó…~JÔj~ü ¾²ÛŒÇ!:ˆŠ¯Fæ¼Öÿ½øñ™\ÂÀ`H‹ßOª�ÓÕF^M÷¼\4ù F^8nÓsçÎ>бlÇw”M7Ý”y¤£G+B„QÇæŽ�*¦ºQ7KY6½£† ¿¬X�ùº®¥äÛo¿­lŒõ1†¿šeÞgÒà†Íê›wX&ZØ‘¤c¥ ÔÇQMÇw~ÒVš˜K¶vÅ”W,¨w¥H^U͸½rý-6â2P£ª–4æ \»ìc‡Û�þR˜¼"Dúbc·Õù)7É~žSaÅf·¯!r”Èæßpà ÃKKK¥1EqI9;»H¡ù #ªª 3êcìøâæni³L麄 Ò“î³MѼ?p¢Äx�OMS‘^Ÿ*q)²špëô&Í6–(y½òÊ+Ó$=ÁQÔæ0,»ÀÕš�P„‘æ�í»iö‹Ö‚ÀÊùmÚ´ÉaUËÛo¿-XîKŒúi6)3TÄ«2Hƒ�vý³¨wqcÏôô ©&Þƒ¥)@[ÛU)k³=âÊQ|¡¸]ÿ9ÏŽ�WµÚÚg¦óU÷îÝ¥mÛ¶Â�l¦Ï+‰1¸÷ߊ!B¬£ØÐõŒuQ¼©˜¨XìY„–Š[‘%~؈ùDÊVÚÆàR$]ÊâC¿× "3ÅÂÍŸ_¤ÕŽºüß¶R¥Q% !Æ!�qA†<'EÕU £8_AZ|¸¢Ú'¯ 6Ø`) )]¡éræˆ26Š™¢ØR·\vsEˆ!Öqm¬�bE7ç úÐ ÎKFb6šŠ¾1ŠØŒ£ºûî»eñâÅÒÐ#+·€zJÅœ…Ô{ŒÈ°}£¼¨{™E¾ÁÕåmq¦O‹ß�Ÿä¿ÖÐjØ´]¦˜­¸Êý»m®ø$f‘k6K[ÓEEErùå—Só¢ï‹÷8xU\£ø“"Dˆ@bÍÝ~±C7+^±éökZѲÌm¶ÙF˜ÆÍ÷èÑ£|Ãó˜_æPÿAe˜â2Gu.R‹¤ y<]ê°æajR|åü ?«õ¹y}ô¤ù ¦à熺jÌ®V¬T<ë—ÅmŠ9‘—Åìºc;rmó�–0±†Ö—±x)4-‡‘9õ±–n D¯¬,޼L�ñǼûî»Ë9çœ#/¾ø"ŸTôº”g�¦¾�oÈcXÈQµPÉA:ã? Qa²àrägXÑQƒÑë@!q­ÑŸ#a,íôv¡ 1kÐãÕöÜ~>Yq(ÏtŽ¿*PŒTDWòóß_F×áœ1c†¤+¾ùæ9ì°Ã„ë:G”ûœÿ]—†"D†Å&Š3�›ª(€°¢ Æ5ŸLièäSjAA�4Ô`{o—žƒ¸yÛ�Öê'jhàqH b¨‹�Èrñ§^`eGùQwƒ|b'` Š¢uüqí<qÙñ!.Ži}]–6M#*3—7‰�‡¢½‹dž}öÙô¥Œ³²„†èm·Ý–ŒëR ܇»[!B„ÈÐháòûß©Ò*Œ’…lu! ßo¸á†Bì±ÇkÐF޹‹³™¢a):K­A±j5täeÿ†¹õx-ª‚�ˆ�c£ìPT6kÒ¢ËÒvICR<Ï7MD‰6J`Øá£SEÖ�ý¿Z±B1P‘h ÿ>ŠÙ#‘�:d±d‚‰3ÌÅuÈ5š£‹\Ãò#.õ"Dˆ žâñ†þᮎS_Ü<ü=c¸³^~ù彆eä�³0iXÏT¢~)ˆÃL?&o$lÎ…¼xî¦ŸŠ¾*^?‡À8„�¦ÔÏeÄÇÌEHÔš‹ùy\ÄÅ4 OÑñütW¡âkŶkø·ˆÁ¨ÂOgS�b‡]ºbذar '`ÜàÃóD§;×îþŠ!Bdh4#}¨6aS^IѺuk�À†Ú w~½öÁ�Àû›PJçÜônDrXÓôuŽ a`�tJ †2K©Þdç·Fã(h862´Ô �T¤‘.óÓÜx]íÒ…Sg4YsôVøê‹ú+»ëÒ îСƒ0ÔšzzÎ…®aùEˆ!24š*^eÂ@TqiŸ éB&Sa¤WX'!£F�’†eår[¿w:쌨!‘f¤�™tÜZ ì…ÔÌ ˜j£0G*ÒÒŽF„(F`1ñ3ži’Vü7)DŽ‘ÎmÑÕYŠÞŠTF/¥XYý#W]uUZ-ó�:ubTÊK\äDÅ_ƒë0DˆÌV^ïêztÒ†Â�ƒÆ±Ç+;vœ†‡rÖyT—n~ÎÊ•=¼÷ò{^�#0À¦³c|€8púý™�Ä>2ap°W‘â)ÁTb#ÅOÑ6Ž}÷ÝWÒ5mƒøwÜ!{î¹§èß�¸´ålg™ßP"Dˆ �Î1Bÿh‹¬®Å” ú»ÆŽ+Ë–-“.]ºÈÁ,äÆóš6m*L%`¹ŸN¨—†Ù« ™i·Qò2¬­ãÕƒ)2Dq«Wñ3À¹2~$UâzO±c“šÅ E¹O^ØØ—,Y"é š¡!H2 î¼Ëœë°­"Dˆ»(&(y•š«�`]»v•+VëÓ§N�*�+Ô¹‰ðn*�O«·Ýv[ƒž‡˜“WÄæeH&Ö0aµ±µ�oæ`')H¦»û`ê;&\ˆÔÕ¨‹yëI2 VãÊU|¦ÀAXÓÀ[läeª>úè#IW|÷ÝwÂ4þý{ûòF*ÎU„"ã4Å$%¢nÓ{ì!�Ë+ÍNüùçŸ ›gùtÊ(]§"¤y>¤Ö³gÏm¡/,.•›û¾M]ËWA¦šj}ÓGuáüƒ¤Nèò”PCÃL�„¹Š s�ÿÈõ÷ ‘»~_ú¿ô•t¼íeÎ��dMˆ«L±Ì9 �o²vq€ßïeÿ.o½õVIS�= „æ|#/0Ím6"DFWÅ,M[=‹š×%—\"_|ñ…ØX(Fôôé£kð?\ +Rˆ~Ø�(�>ú(ÏoÐ&Ž÷¿þ»»5ôBd6:©¶S)l¹£MôÀÏW›Š±ÑSÆù™ºA­­6w®�k¦Vç�ŒBIr�)ç€XS8ÞÝF^«?*.R¬_‹µ?ËŒD,MM£|šþ}QOãøfØ0òZ¬èv|…‘™3û*æë͡ȷÃyä‘ò·¿ýMòòòÄŠÚ'N”‹/¾X˜yÈÍõE#³�“:ôÐC套^’†ü¦Ì^&7>0Œô�™#ì†Ï×ZMªˆÎô…¸=;¼?q#•¹‰<‡!ÇNL(6Ý#µy†¬ñW8ò¯¸QѼ–ãÌfûäøwG )²ˆ´¸G^+�©)Dˆæ4|Q±DÓ†F^1ÑórÞyçÉ—_~).˜rÀ‚@ÑíËfÜ0û¼íD”;ÁzP{ù½ñÜàÍjŽjñ']ÔØdÁ1x­ ¦W+ Ÿ¼l6!χØ*�º˜Ùæù9Ï£¦l^"*Ž1ž1ú×�z= ÒrØ·›4kÛå}üDs4kSUF^ë¢Y™l�Êv^E¶âUEkEˆ!2l<ÔÅ %Ÿüèh¨½öÚKî¹çñkY³fÍlÅ–2ô�͘°#Ž8B>øàƒ?‰žfæ?f,‘óo}uY‘‚3†2KFIÇ=A*ìðAýëÔëž«}ñ=�ñ3&{}åc<×È-!ø9æÈËFPq­œ³†)Ïÿ‘Ö�wHÓ=.—õÚì-MÖkÆÆ‚]ë(Í6YQu~öÙg’®¸úê«…t¸çt\åþ>¶U„"ÃFC}àÖ¤¬Š›¦�ÊÂ.oÁ`ÞÁƒó8 -J` 7…ÀP`Lá°1R :V­.–�CÇB –ƃ´L‰Õ˜ AjYÖKFÝ‹úãŸPY¨=T”GJLú@eAz6&ÊêgF¤• ®ýjT×·IÓ]/�&-ÚpÃ7 R´RÔ6~7öï1�û½n¹å!ãàM¶_åìò{*B„‘aÊë5È Ä æÅ]häEQãÿÍ„yH*ú�ÇI¿@`o¾ù¦`·o 1gQ¶Ü3àCÌV÷‚¸ ©ÖŽ?މJÂØÁ1©‡Apq;¹¨m¥¡7ÌÔ¥âÿª›îØ^š4ÝÀHËPéöÇÕ6~1VW}â‰'$]qï½÷ÊöÛo/ÞlÅ\ÅÅAŠ!BdØh¨¹O¹ 5uXåã¡Úµk'�•Yæ¥�}õÕWÂHëùò±é¦›Š)Ä�úSÁ¼¡ã—) ±µ“–³´aÚ'^@dØø©w¡ÔÌðQ×f#±îþ×›o³s1„ƒÏê`2ÅŠR#/Sõ�?þ¸¤+˜.O]�ó9ä(Fº‘U!B„Ȱ覯˜£äUæÏ6„„.»ì2aç‘()Ò‡3eæ�( 6K!>üðÃRRR"�%ÊË+™NOs3)¼t7£Ä0`ØòJR‰¤Í-X'@õaø¸£ÿ{²"'_þþ÷¿—' ¯Šëµ‰qŠ’(y¥1mH«‡¥ «=ÃÆ�Šã!B„Ȱ8Ô¥Ff( ü”!ŸBÿ”““k-ÎÏÏ—0&Štaì*zÀ¶Þzk¶Õr³‹›ÄÑàM£ž-×õŒbA!¥ƒÈ¨‘Ù€^j^¨>ÛC–JZ$}.52Ì!/¾ý½”–Uÿÿ»wïG\ÒùÐ-=]Û§¼Ò˜6$C`iC#¯,Å$Å)Š!BdXlän4¿*r�| �ÝvÛ�-³Œ‰JØC ±[·n‚Â2²ºh Œb;c§î¼óNá5�1æ-Α¾Ï�` )¾Ú56Ç÷rÕØŒAZ‘×�(yqmÔ×HE2aÿ§I ÌAj{°øÿG^Õ(yÅåµp~¿Žk^‘˜,í󚢸O"Dˆ ¬{½¥˜ X¦v÷J# þ�IΛ7/¹ÛnÕ*¡Á“>/_¹a��¦id†ìV®\)�5P.|=Q.¸ýeR{˜+ #�ŒdFê±KÏAòêdyv~ÜT æZ2w�ÿ¿vÓ7*Þ©Eíkº&¯áÇó�Í/‹y}¬"DÆÓŠ1ŠYjÒ(6²¡¯æÌ3Ï”qãÆ­±g Sãu¨“ñÚD`• Ö¹sg™9s¦4ö()-—AŸü,WüߨÝI+þ©$‰Òà|gÿ÷åËqÓ©m­q& ¦>¨D¬Ê5Ÿ³–6~4ןòzöÙg%MÁ¿sæv’A¨öÔ#ä5J"Dˆ Œ»œ«jŠ~Š^BmÁ”nCêZ©ŒåaúAûöíc˜m =éDn=˜h0zôh ñ¿4ìoÓ˽�~Dj‘ºVx¬é¤ðHñÕŠØü¥—ÀæÒF¿ØSƒF d…Ù$ÕàßE¯^½ø §¾ò\ßW‹µÈübMÊÎñÆoHºâ—_~AyñïÔÈwº#¯÷!B„ÈÀ8Qñ•bŠ~º�¯Œ*³ÀóÇ\“4)ÆsÏ=W0i�zŒº!/~Æp_ˆñŠ+®V®ÄGˆÅËó“Äíÿ|O®¼÷ aš“æY�BS4j #ˆ? Òã+Æ —q!BNŒ„b¨/ëÿ»ö"^ýF¦Ï[YÕj õÅ¿™�U¸Å’5�.ßJ1ÙÆ40�3FÒ&Làƒ*²Ú燧!B„ÈÀØ[1\1C?ufëTùrR4܈¨c1Œ÷·ß~“TcÚ´irÁ«U¬öÅq -Èk÷Ýwv'�|òÉrÆg«.²³³¥fTÚÊœùöÎ8®3i×=fHŒqì03gc'3Ó†“›u–™9 ?ÿ`yƒµ79›f63Û1SlÇÇŽ9&YÒ¹ßSÕ¥:u꜑dÏÈš™÷©ê’Ö¬¥ñy¿î~»¿©s—G”÷Þ:=zoøÌˆxĬˆ_ÙV܆Þ$�™7ß|³Q½/^ŒÍž9YVtÄG0´Œxq} wƒE\p�бJª„7qˆð{%ÃN Ø6ïØÈ;æ–'Å‹þ+·‹·Þz+:à€ίõÕTË}„äJk–!r!þ×Ô«Bßk“—€"œdGydô·¿ý-úôÓO£ÆÀƒ†­]tQ佄ˆ,Œ«T¸�™¾�8>þøã%<Ä,6oÞL¹˜ÃNZöµ8ÄýÞ˪�œß¤¼&ñ5ª¿÷½ïE|Ÿ"A? ‡-c".^us^—š¢Ùò'?e® 'Ï�ñ«Npr³2e•ÆÂZ¨þýûG·Ýv[ă-Þ£�¨�uÖYÑèÑ£Kw ½ ,Ée¥”„ÓÄk“Ûæ;Xý´÷ 7%¾Æ¶ßÿþ÷ż­>âÅ�Í3F[å=»óMÑl¹Ó›ä«‚°lq‘!†ì‹­;ÔŸ™?~tï½÷F§žzjD_�‡%Åë®».2dˆ—$E)ÃïñôÓO'»NŠWMˆI \³t U€¤M>|Í úÓŸŠú:ù·û·Sˆ÷¼¶ºx ÑׄÍ–+ãöää¦xÌÜãµ3Î@DŒ=‰cÇŽ%‹ãk•_©P¥CÊÂiÙ×!nÀÆ�Ë|Æ«6á°³æí·ß.Dv^epç�wFÁ¬ÄëÝW\â5 ĉ&„h¶œbBü�0ŒW_}uôÁd:ׄJ‡ŒUPfN¯*_CÖͲiâ·!>M~n˜ Ü4uêÔ¨l‹2øõ¯]wÍ�™m±Ò÷~nG´°Â Äç~ÚL%ô,\‡bE‘ ‘$”›-p,ÌóY ­B퇤,ö qlʵû›Ž?þøöAX¬T[kÖ¬±�=šyl÷þ›�x‰fʦ«³þq‡Sµ…Ÿñ6œL- !®¼òJ #–Awîå2\†§øŒWÛqVsÌ1¹‰WUÖá,,Œ¶ÐSK¯*/ѬÅk½ ˜Aˆ½oÁ¬a4!²=#ëÙ³§…¾QÖ<áÁ!ÒR3²­sBôН“rñ˜{Øa‡Y0mX¨µȸÂhGR¼¼ÜikLH¼D³¥6Äg.^,ÿ¨)Ξ=Ûª("«tˆ€…9AËRzZdV'‡Hr�_·ß5ñ\Ûb<U+ -…pÏœ…Ûâ%ñÈc›»%…ÄK4cºˆ¡N›âÙWØ~aœLà Ê6bÄõ½D&}ûöµ° Ì2Ø'¥tÈŸ]Mâ–¸B% 1/ÄÀà`ÌYahk),_¾Üèy9ñÌk³ ‰—höÌ÷Sæö´þ=�p«²›;ï›i„™@ óRYe¾ÎÞÛ:2¶Aþ§! Ñ=±qKˆ�Âæ‹B¥ú‘eœŒTÒÄkµ•/!ñB°Ú{bs<ûâM¸·Ë¬˜ ‘=¯p-Ž…áö´Òa+·žßï%¼ßúðÞ!v gñ“O>ÙæöÛoŸaEñ —kÆ{½Ä6¯B‰—hæ¬ó×Ôþþv{²Á�ãá¢Ä’…Pß‹Ì+lª° öðÌë�WyÖÕ%QJ¬úÙÏ~Ö¥_¿~¿_¯UÁvx¦@Æî¢Ãq˜ì¯õ݆Bâ%š9_†ØÛ1Û’Æ ÛË–-³,„Ëœ-,¸5È0nâ`7pôH” Y#Öê‡?üá^`± ×ñX¸š1‹±Õ·‚Ì2!ñÍžß^ëDñÒ!ÿÀ¯pë¬e!DïÞ½-,¸¥ïED½¯žqÅ3+²¶Üý÷ßß{|S€ƒöóÏ?çµ/n ±Â¬�x 1Ûˇ[Ò^cüÛ¦l¸hÑ"ËBˆ£Ž:Ê‚CÐÂÝpÌ|åò8ÿâaÑ^{íe¿ùÍorçŸ~“ Ç]›8iÓ\†ó½t^H¼„˜è%“È¬Æ ^¸Ã’T" ]»vE¸8ðxé0/\VÝxã�¹;î¸ÃûeŇáä�?þ˜^.½:ïûÚVØŸ`%ƒÄKˆ±Ù–(ÄÖ¤€…;ºèØœ9sLˆ4Ð*†•äˆW½V?qii˰—lÍš a ,À¬aQÀ{¾ }£ü+$^B¬ñ¸¹íÌõ*9°ŒÛðÝwßµ,„èÓ§O|Ö+5ûâ�BÆ•;ãŒ3Z†r!ý.kJæÎ�K¿Ë¶Î¿:Äg¥Õï’x ±%ÄB϶ZÄE,Þ÷¢G0yòdÊ.&D_ùÊWX•™yÑ WŽ� ¶x6Ò[SÂÿ/vuR‡(àF�í^>�¬d�x …³Èw Ñ"k6fáÂ…&DáSL–F.ÀÕ)të¤ìÜsϵ¦†ò7®YJ†1ñÚbsiö»$^B ^ë}ö¦UÖ?|¶ÌOš4É„Há´�P¥2·k׎�±ÓN;m—\³³xñb‚2xü…í^:Ÿk%‡ÄKˆU!ææ{}á4d¿!âÅà²iì½÷Þ©}/^?dï¼=âˆ#lW0`À”-Aui —ÄKhMÔ„ú6j#Z”é ‘¯ï•ï-Þ65ˆ&%Ãø÷Ž�‡Œµ’Dâ%Däÿ€—ÄwƇX )ýpÒÌ™3- ¡ ó¬‰JƒrÛZ¸Ž¤©Áa8þ|Êßq‹¼¿µñ¥{{²ÄKˆE^>Ù`)0DJæE³×aš£LV<¥Ím¹`0raŸ|ò‰55£F�²¥K—’�Yìªÿ�~`ûÒDÉŠ—+|=Îâµi%CNÎüãÿôÓO“KMAö2€L–ž:ïÅ5$³fͲ¦fäÈ‘ŽÌÙî¥5ü'%-^Blñ Ëy?yjF¸,fdV®\™Ú÷ÑÚgŸ}(3[ ¼†È¼š4sǤÁV „3f�'É"_â%Ä4?‘~ØŒ»ÅxPúáa�†'�tŽÃLã×ë4¥cuüøñ\ªJåÀœm^]Xb™‰’/!>÷²áº´íÚ¾ã�Y™¼¥!Ó3]I¢€ïl2Ç!ßr̘1Eè×ÖĪ [CÌÔV�ò/¡Ë)ç„ØêQG|Ï!â5eÊÊ0&D’cŽ9ÆvÛm7ËÇ*åç&€>eJ¾gME-|®«Ê_ëƒL”…x ¹ûj“‹W*8)Ãð6!ÓËw-ßë‡ì«)˜:u*ÕJÝ›c%Ãj/�Ï0Q6â%Äç±;Ž¢Œ¦;½/6n¤6Þ…Lp‹x³ú^ »7IÉpôèÑôØ6…’w.Q2\æB&ÊD¼„ø"Ä*ohW§•/†>_xáe_"U¼Ø´wæñ±‹U«VY‘Á`Ä(QN^åÏÎZþØc¼‰²/!6†XéÂUcprfW+w”}‰$½{÷fMTfæµhÑ"+6ܘ<|øðîlD<«Üˆ´Vý®ò/!¾t»|U>ñ¢áÎÍÊÏ=÷Íp"wuá8Ìsø)ê¡�:tèú�y­sá"¶úál…—ÇE‰—‘g^[BTgÙ�yCé'×´iÓLˆ8�;w6îïJí¾~ÿþý¹¿§},ë"Öú`r­‰²/¡¾×¦¸xe7˜Ÿ!„HÜšÌå”™Y™{13¯#Fl ³ˆí\¸j\¸6»x}d¢,ÅKˆ•±­Û5ùÜ\4Å™¥á�$DÌ agŸ}6¦�Ì×M±öcÒãzòÉ'›Ù~!:zaƒÊ–zÙP”©x ]N¹>Ë´/2¨Ì¢^†—…ˆsÜqÇqAªxmÙ²¥X¯®ìÙôá‡òÅÛøíà°Õ³®Éºþ¤|ÅKˆ ž}U!`ùšâÄôéÓµ¨7í8ìÖ­[ª]žUcÅØoˆ0¾ôÒK›Ùþþ¼l™¸î°‰²/!6{Ïk[VÙ0 Pö!óârÁäõêBôêÕ‹+ÿé%³væ‹’y±3ñùçŸG¤:…€øpò¢Ÿ™([ñ¢6Äz�êú4ßYã¾—H +s9eê¼âEäaÃn�ÇÛÉ]†Pão׆˜ä¯gQ¦â%D�o—_ã¥Ã(ßž: Æ £dBÄ騱#KzSKÎõÌvÚ‘Ù®�®u£†+f�ÓpUˆ±&Ê^¼„˜bµ7º«³J‡ˆ!®XgY¯IñŠ— “÷Ãð¾õÖ[”÷±{¬\H,ÒÝ]•!^B¬ôÞ׺Xé%óÍPè»ï¾kBÄÀ*�³0íuæ÷BÏvEÁiØÑ³®VnÒ¨òmýu,Ê\¼„Xƒ€Õ×÷Š˜6¸â×a|vG\¨ÉÞV@¼ÂÂÜ‚Îv½÷Þ{µÁÒ>¶)f³g]KCŒ3Q1â%dÚàÃÊ›,\cd_ì«c‹·±`Á‚Ô^(‡æ ÅìÙ³¹þ$ŠÍuÕÆîíúÌb¢BÄKˆ nÜØP_öå¥CJ7BÐeï%YVÚ<ÖÊ•+ 5Û…Qƒž—ÄKb¼vVžx ]�2ÊmÆ[-N×ô0(É2‚aaî}ËڋɈEÁf» ÄÛ\¼dëÙN4Qqâ%ÄÀó]À¢<=nVf5�Ÿ¨E%C6ôÁÄ…EsŽ7Žò$¯Á±mÄFw®5Qqâ%Äç>³<ĆúJ‡”nx�ˆŠ†þD*K¼"Äkg7Ë“Á½þú딬ã_k[ˆ/}ÔcLˆÈDʼn—Qˆ7B,òì¦|˸˸Á–÷EeBþì³ÏfßÙ+.^¼ØvÊ’lwaP>JlçØâÛã§š¨Xñbiˆ�ýa°%ÄÖ|§éÉ“'ûCITb¹päÈ‘6qâDö^æÝ†Ák„›¸wÖ¨ÁnÍÄai‹�xÌ÷·¢‚ÅKÈ6ÿº[Ž7å1oðÀªárÊe˖ɸQI¸ -\¸£ÂÄÿ®wìÍ7ß$lG@°Œ'ËÍÖÆ®?§’¡ÄKˆOBÌõy™±(E¼ª0n0óEÿKT Ìû±ž‰å¸”ôñ3f̰gžyƆn�%Ü”L$w$Ö¸QcMˆé&$^BÏ&Ͼ–‡Ø–±ó�RNÔµÌ{ñ`Qï«r ëB¸8¸Ä“wß}wëÑ£‡uïÞ�Móñ²ef5j”ýãÿ°yóæ5*Ë{ã�7Èð“÷‚m÷˜jÞŸ/!¦…Xë#Ô¦4ëÛ†-5˜6(½óÎ;4Ó­¼dZXãɤXÀFùã�?Þ~øÃZ¿~ýìØc��¢Þ‹Á|ààÁƒí‰'žhðk…l‹ÏI~|Ì"?2þú/¡K*û{#¼&ãáÐ"” [!^4Ó) qªfx¹|Q¹�ß5%CnàtéÒ…;½¬OŸ>öÈ#�Ø£�>š;ñÄ·ær¹š¸ñ‚Ï2dˆ½öÚkÖ¸‚‡•P)ZnuƒÑ\/!bLñ¦Û‘kó7(#QÂ6m<ð™˜Ñ+F fº0]°«Fü2JJ†\‹rÐA\pÁ¹{î¹§Í{ìÁ !ÿ:pX®KI°^±¤dÈk ¢@<) 1:Ä/!bD!ž÷žBm}} f”’(ñ<üðÃöàƒ"hÞ§¥î.d3‚ÃC :ˆPü:ʆíÛ·¯/¸ôÒK[ŸvÚi²¯Íqñ¡ôÈ€;U>¹9sæ¤õS«ÝP49k§�x •ÿ7¶vg{ÚÆ�øI™%­ˆØÛo¿mO?ý4fþÜDé‚)ƒ�‚”ˆ™µbÇ`®ÿG¸ˆ¶mÛšÃû¹Ë/¿¼[ÈȪ̉ÀˇdUõ5ø8>>Áf_&½(Ë"/$^B¼ï«w¶z q{C¨RJDÀèƒQZ¥ NÁO>ù„ß©1Û—“N�:Y»ví(Z 2.»é¦›r'œpBÞ� ›2È̳\ª|œ¬di‰CR­g]CC¤²�x ሿ„˜�µu# ¤õH¾øâ šó%~÷—à†d²/œɃHëÖ­­gÏždYvòÉ'[za8[„�Ë™“ „¯—ãu1aÂKG#Y^ÒeXëØ™!¦J¼$^BÔÇÈÿt ýjkÌ1×#JºlH†ÄÛTqêÚµ«uÔQvÖYgYúaçž{.%EË9~¸ac†õïߟ÷“5ìý÷ßÇBÉÙ¢@̤±Ñ+Kë+ ‰—Qˆ×BŒóÕQ5õf_±9z`¢´÷’!2IaêÕ«—|ðÁö£ýˆìË’ Uˆ—Ä—1�Xœ1cÆÐ+å{ÖF�„QcYˆ �ɺ„ÄKèÊ”|®fkc6�c⥠ÃÈMR¼:wîlûï¿¿]|ñŶß~ûYlÜ ¤†–“VxJƒŒVÔõ¶xû /�uUrq uÁšŒ¾5x0YH¼„ˆ=8V„ØÞÐù P¢tç»ÈŒ0PÄÇ(’M±Uã«_ýªåƒ í;ßùÆK ý­ð½ìÅ_¤…’b”b�_bXc×A ‰—ÛB ô²Ígn¡¯ª¯tHæÅ¥W.d‡!³Vˆf @¸èuí³Ï>víµ×&3ªÔÒáé§Ÿn‡rˆï}z„æ¥Üx0/Äàéu ‰—óC¼bNlqo½Öy‰Wiº ?üðC†‰¹ ²n#F‡;餓¬wïÞÖ°ÑŸyæ™|ž% ·E¶Åh[?9�Ì02ã–ÁòÏ+ë’x ±³×¦<âÿ…Xé˜åY¦ Nñ¢´z]¬ú¢¤‡ pÄ‹�ä0xL)ÐÃqLJ¥ž²cÚF 2«ô\ås]‹MH¼„(Àê¨ÿâí«êq–Øu)‚>%6v2"œ†ñUPaÙ®]vÙeˆ˜5>÷ì³Ïæó=ûjظ…&ÿ£PC!ñb}ˆÿôù¯ê|%#NÕ¢4à ÁZ/vò¾—|1[ Xl̰óÎ;ϨáðñW_}5¶zŸ k5!fû:(!ñ¢`¬ ñV¾Þúh ›â�uÅ_p@CKŽ5n zÆ„ÄKˆÓÂÅ+—•uñÀ⾯ҰËk ™ŒkåÊ•˜4ˆº’CÆd]Üš ;*^lÛð]‡'Åð³ÖíñMH¼„(0mBô Ñ"C¼-Ä«™_L)èKR.\´h³Vñ¬‹2Ÿíµ×^vå•WÚÎ@ßlÏ=÷¤Éæ ËÖKB¼Z £†�x Ñ)Äž!Zd5é-Nól Í·×Å%˜4H&ã¢ßdIô¸n¸áÌv¾â•§ôXb�_69Ò„ÄKˆ"‰×–»ë8ÑSŽš9sf3½UYåBÊ„Ï>û,7�uÕ-ÊEd<ëb”İž¬k«oÒ˜íï‹¢• Qt Ñ9ëÀ…Xaàaˆˆa•¦ôĆNÞ»»4ô©§žâ*2d ×eÎÍ5×\c‡z¨Œ¿{þ¶…ᅳšÅ( e^B´páÚ=˰¿xð£�>²ÿþïÿ¶ÿ÷g‡Ý.¶Ï ʸܛõ—¿ü……¸¼Oo²N¸âîÀo}ë[V(Ã<æ²½*¿xr¼ e^BI¼:„h‡x5äaÉž<ÊSôW¾öµ¯‘…¹{M4%ôµ&Mšdï¼ó}.ÑJíOýüç?·}÷Ý× …÷»Ò CGÄ­Êàך�x Q"­¶E¼ãj>|8³_ìγïÿû8ÐLúYôµÆ�GöË!‚H.œ¢lÒ¸þúë­€ PY™¢F¶‡Sd¹ú]* QLñêévùFÁÝPœüÙæpÏ=÷`æÐXAœ©—_~Ùþþ÷¿S&äço¾1>µ´wä‘GÚƒ>È2Þ‚o¬ÏÂ{l|Ã�Ýu($^BœÚ=üõÚhåÁÌ1oÞ<îq²ßþö·\ïWÍ‹B²lÙ2{õÕWí±Ç³þóŸô¶2þ<ÙãªËŒpþô§?µÃ?Üš²®œo�ŸoBâ%Dé¢U81o ±CÁŠ+lذav÷ÝwÛ·¿ým›?¾²°Ín 2ƒŒ=üðÃ6hÐ ²-ʆ¶|ùr·Ã;YV�=ؤa·ß~»9M™yEAÀ¾ô9/!ñ¢hì¢e(õT‡Ûr£�ÙîÀ>½×_Ý.¸àëÛ·¯}ç;ß!S 7¶b¦2áÀ�íñÇ7¿ZŸ!¶x~Öd½uæ Â{\,Þe}®Ðø,VQ,ð�€­V¿Kâ%D±i�x…Øé ø`àaù›ßüÆúõëggœq[5p¸ñ€Í;ÌLæÿßyôŒ°»#üp’ óó¶C9ÄN:é$ôɶÜUØtpáwš‘y‘±wPæ%ñ¢XìbO±~j®ëw!Jô¹xxÆáÔÿƒüÀî¸ã6Ì#n<¬øÜ-teG~Ó†àçϨ;#0üœ90p…?[3îºë.Ï~›„‹ßaÖœW(+n2!ñ¢ˆ%ÃNh–g[5<,ýôŒ9ƒþVæbÖ[o½³›nº‰L€Ò"_#¯pñyXÀ)îê²{›³yóB…YæŠ+®°sÎ9‡Ã£Ö»woÏvwÝ5,Dµa®o½ ‰—E â�ØBÞí!êæ4³;#Hy³2�_üâòõ¼jÃ]cL¨l(DØÝ…«cì¡Ó’F<'gLô¿0k4Öh€ ^ì5d£9·øöêÕ‹L Á`m"¶Ë.p\¸p!ߺ¬�ì‹õK~øaÃŒ$‚ýŠd®Y¯�êð3^kBâ%D袳g`í|¯a /23Äž¼6 z·Ür ƒ³”½øZdf@0ΚDjÍš5lb'Œg†h�3†²¢‰zaåW¾9¯ÚðZZgBâ%D‘®A9­òì«C(ÒðÁöN–ÄÊ¡�µ{“qa:Àl€`1X‹sŽ™¥]!^3,Þ'óBÌØd�˜‘•á@l`ö%ñÊø9‘UóìÛhBâ%Dè‚N…hÛ,ŸóaX ƒ²°³Î:Ëþõ_ÿÕî½÷^ÄËvd‘n×7Ï4Éx‹�ÙêÕ«mâĉnDùlòd¯¼M9° jÛÐ7/!Š@÷øŒ—?x(éñpg@¹ [|É/ý®]éèãÿ·ÔK0î#Ó�ú”³®gáw2xš‰ÛLH¼„(�C´‹=t°e×".XÚ.Lååиƒ2¾w‘L‚¡e"?/~nˆWÆE”¤®5!r&$^BøµÙ36Î�Xm âQâCÈ\À¬Ü`¡0Ƈ’a�xÁÚµkmòäÉ&²ñ!ó,ñ""¯–&$^B�v!öŽ�Œ£ðÀÉá2ôR ²Ì¼ößÿä•.ü}q’MÐËÁ´¡¾Wþí6²îò⿯!ASæ%ñ¢NîñVE(™µ 'êîÄ«37”l¬ÀI_/Y £×ÅC™Í d`"fâphfØäùï«èyZ¼„ÄKˆµ±Ë[c§$„xAŠ¢Ìù™›C1®Ý_±b…‰Lñ"SÍZ U^Kd][ {Ÿ—�x á¶ø5‰{·rô€Bö…˜¹ºü`>KoÓ\�ü½±Ì�5ÊD:ÌÆ!òYâÅ�Ñç¼jLH¼„( í\¼jS2à”…ÊöÆcv*R:ÌÊ,)2ïåF‘¸A™½�=A[\´6ú2J!ñ¢�‹W”"`<¼±‹—íÛ¬kï½÷f VÖ-Áé¥C�h±B‹¬<­$ËÏÎÍʺ$^B'óÈ%NÖw^±.©lo)öÛŸ3K‡K—.åÆgs�™eÉ’%Y%C~ž¤ëž} ‰—Åȼ¼|˜¶p•¾™ï—#×^{-Ë‚ 7o8.^˜W† –øû ².z‚üŒRÄË3yÛâo…ÄKˆ‚‹tH[^ëÙ âU¶Û&XÌâa²…´¾âµ`Áã>²:×Éð³IuÆjCÙp+Ͼ"‰—�x ‰—g]ée5Jj,Ô-ËAeïÏ `ô½RK‡ôû-ZdóæÍ3QçFå"O¹É¼’ó]¼VªÃÇ�uu)ÆóOH¼„@‘ª,×r%[å)«•+7ß|3"�zg#\¸8`ÀÏFÅìÙ³mêÔ©ˆWò @é•Cé±{±žBâ%$^Õ!6§e]Ý»w·ýöÛ�me›yùžCŒ™¥CÆ0mÐÿ3sçÎ¥Œœ¡¨ÛƒéâÕѯÙéaE@H¼„Ä ¶$gq(£a%GÀ¸>¤œá�Û·o_®�áýÔÒ!=nX6Ó|׌3ØÀâ%CÇgÙ¸á+µöqxˆoúâg!ñ¢`´ˆ‰Vµg\u·(wéÒÅ(òP/w®¹æÄ:5Ã䡌³nĈ»2ƒÉôôŒµZddØ6÷ô¬ëü½MH¼„( ­bn°(^2$¡‡Að~¹Cyãb�漤t8iÒ¤Š¿ãkúôé6þü†n]iâ ßqó†�x Q¶ÇÄ+—,•a…æ!…˜•;l—¿ì²Ë2–)“1¬ÍÐvƒQƒå†Î½åü–î>!~æ}0!ñ¢ âyÄ3 „‹þo+¦TvþùçsÏÙWj9ŒÁí�Vì_h „šE°.X½BÜâ>S($^BìÛÒÄË3 NØØ¡+Æ"N�ïÔSOµÎ�;[X7n\ź §M›†ˆ5¶4Ý5Ä�!þOˆ»%`/Q(T6ŒBäÒ†QÝ9Vë‘èïÝpà ̶aÜHË<è}á:¬È™¯)S¦p Ù‘L<çØA!nñ*!J¼„تХ,ñbn‡µHd_•R:Ä´qà�2l›é:ĸ±xñ⊳È#Ú\ý¿´ñ ìú؇�x ‰W”%^¸ Ý*_1â…qãÒK/ÍܸAß‹™¯Ñ£G[%�q‡¿7o³ˆ |îâª÷÷…ÄKˆFQKd½NÙi¸çž{ÖYæ+f¾öÝw_„;u¯Ÿg_•´qók² eVÉùØE!qˆ ‰—�̼RŸF” Y ÅNCú]da•B�=¬OŸ>d_©w|!Z&L@À*É"�ó´ÐNËn!Îö ìk Bâ%DMˆÚŒ’!â…óóBEÌzÅ…ûºë®³}öÙ'Õ¸�icåÊ•6räH##)w¨Nœ8ÃJ1Œ*�Cô ñpˆ+MH¼„h¸a#½÷ÃNCìãlV¯4N>ùd;餓بŸ–}qÇ–yìãaÖÀeÈøD‘èHæå6úë¬�‰—PÙ0ßkdd ôºÈ4* þþ—_~¹õêÕ‹ŸCªqÇ!W¥ðP/c(Óï*ö!¦CˆBÜéÍ™‰—_ºxEigXcÇŽ5ÞÇ&^I ãgŸ}¶�p lØO} #^ØÇgÍšUöâŸDdàíBâ¾7e]’*$^B¬õ-Õi,–Ð.Y²[¸_¼Ž�€&MKt�XB4Êùc ÝË º¬þízüBzí€*q‰Ó55y„I�8Z£òØõ3â}Kø9k…¿ÎÚ0:/‡?…Ëd±Mø%Æá´P¬€â8¯\Å;ì`àª/^lBóº/ô;„3CeævÛmg† –5Î !cˆX#"Êb‡M…+Go£XÅ„#…;Ó™¬§U¹ÊS�]Yè—GÆ<¡ÿzÞ‹Ý`wáƒ+[ ÜÛá–Ì·ý*´a«6tp¨Nñò]�à¼ÐiÀB–«€xŸ}öÙføðḾ•™Ó§O7Ƚûe�xÁqA¬‚ÞÌá¼Ç\ØQÂm„£…#è–6 Á- §0m&Ü^xŒð2Vì�þ,œÈ|ÐÂc)bÿ X– ó;Y¸¬_HOåÚ È|Òì�ç¹Þªˆ³8«Œ‚ ,f�H_8˰:/‡ÏÅaý—,P1?ç…Š:ìiÂ&UtZÏUÀMzè¡åñp_^7‚E {‘ÄDæm¶Ù&+öxL¼€| V�5*œ–Pa¼(ß[ÈP_ã5¾ÇÀÀò÷IxW)�q��ZŠГ"• îPkxMŽFŒï­›x98L‘\V½,P%~sœ�°GX Õvèõ—Ë8á„ÌÏ?ÿŒÆ¼ØFàbCÏ÷Þz ¥óè zñÒaÒ€@�Ué©L[u-Ï[$’ÉæÕá* àF(RxO7%F8_¢Â§E×V¥„Ú2'–¯;NfMáà“pØ Z?Š“°»ï¾»•VQÖZ‡I“&YÉ�Y™wæ{¿Ä�Z)Þ°O?ý´•<¡ 3dsº•6Y¾¿'ˆ…Ö±]X#\îa­ç=u:³¤¬S_c¡pªð7îùz[ø”ð\á^ ¯ÆÂÚaÃÁ¡¡¢¢îË`oBeÓ¦MËé¢ ]:þùç›õ×_ßwëÜêäÉ“‘ûB÷�СDoË…C3…J»Ê2º.�˜:¿šlò©Hlò|N/~ äû&³±Á‰—C˜ Jwk’á0#à˜ë@,ü€×³Xxÿo«­¶Z-âäû„-ÕˆV ,á  C)í™gžihý®’O´Rc?úè£Ð9Ën¸A»ÊÖVV çÑ,ÐÕqŽíÎfŠîõ—t\÷ÿO¸7÷¸e5Î�x%[¨ÆŒceÿ’uð_ä|ðA‹ð`²°+î«ä­rذ@övÙ /¼¥ÿ)Å ôÉÉ,â‚:“cwâNhÚ̸�p-a“Ýw„÷ OäæíH® £tp(�ðP4Y) Uk([l�È´¹-Ð(ŒgÞ ÞA5Ó­V¯W³­Õ±ìùx.‡UÎÚ\/‡ÙÓ÷[¨°— CS�úp{¿† ‚öY)çb½ñÆèXñB}[‚žHàén°3„Chf¸u…ªV¸†Âô“ðÿÙaÿá•Â_½÷8WÄËÁ¡Hªf´òkJ‹ÎòþÅ@¿~ý ‡š (Þ€û’òsèéÑi‚S€Õ^£ÖÀ¡^¸˜"5�óö°‚s]Öÿ°9ñ+Ê�¹%^Qa‰WT˜Ôy9¤t¢ã<>Nêd0ReìØ±htŠY^è–O畉û*Qs¯ÊMîÀš  œÖ n&^Jѯ~*üo ×~³Ìœx9èÆžùª�JŒÌWSRÙð3ô ŒIÖh’î¯ÔàN1¬÷)eÇŠyóæ”Ï(‘‡ó¢xeš÷*Us±Â†¦V ‘Ua½y`%é t]ݹÎ1\ø‘ð<á5jpd§ÁÅZ:Opb¨Q½Èb¼VsqŠyÌWätÔ¿�ÐG55µYØ€#Y§ž–– «@žoÕy½  e±Š$/"8ñjÙ}¡ ;œ*BmÉÜ ÄKš›M7Ýbä�a["ÿ�QĉF XKa‡j¾w-]SLØ#ÍüVµš„¼Lø1‹/y+ÜâåDª§¯p$‡Ì âë>žpEw%6ÝȈ:j‘ BqUº 6)±ªcºGÕûm™pcÙªnÑñ.øcÅ=ë) oÄø ,ÄË!5 Z§–D&L0©Ò·o_T@¸6L�¨Áˆ¡…êkae ß›O‡#[ƒ8E\&üJx¿W´Â'^zzè0ávÂÃ8ª»§ú£ âÌžzÕ@3®žâ´°á=ud E­ŠOoËÈEl³B‹Ù^À½ím­í–lš2Ü—s^É�b t`ÇbkI–,Y‚É˨àĽ lGytÉQ4«HK•™:ê“þ±‚‘š_…·³'¤5DøÄË¡ˆã»·æÌ™-9x® D9ÄRÃ’ ÏÇq²TñoÝòÇZr5¹ŠaÊå|½Ž¢Ø \Š#ÐCøOŠWÊÏQ€N¼’6¼ð fܸq(Èh1G„Ð"Ü׌3Ì–[nXñÂÀMä½rM”FÕñ} ×¥ÂvƵŒ¸,ÞFÇ7D¸ÄË!ŸÎjgŠÕ†¬b:¯l@LK€)nžpdÄM¹·Z W�/œ«æ¹*u-ÎÏ/ænüѸ×-,¸>‹²î f{}õÕW(Âhõb�Y˜‘†êΠîÿîY楙FH�5ŒvÔ¦'TøÐòXÜÆŸe.;�¼Ïr÷u†�x9tc¥Û¡Âm™¿",£˜EŒC72êyê³"M%p8ÆS´–‰ðk«Ü`4•p!Ô…Y^Òa\]qxíµ×Ì×_�ᓸOéCÀ}Áµº)/Å5Ó¼W—BõYlõêÿQÔßq1LÏÈ…j4\¤* 3Œ5³q‚ðí¶œx…«#òÖlÑÿ7†ûò)'Ï8´ÖÅu'½ð  �,`iuÉ4Úa±õˆ— "ôWYY™¶CA. 9%äÊÊÊÊL�‡ˆWHaU4¢ZýÝÇ褢ª"Öªñ"«èÂz û¨Êä~­ò Öön>~-Â(…v(ž,¼Oµ6ÙA¸�°Ø W»Nf�ª}i`”ìžJ¸¬ÀgoZyö&9@°8i8áƒpA ‚øü07åMÚ*ªãÐDñiN!\M<Ö¨ÏY•Dl,�Õ"º3 ¦ÜÒV.§`%(vóÓl6cº¤"ØëŸ¯"áÂó…{S´ú†³rÐy¯… š?üЬ\¹Ò,îØh w’±xAüðùA/í*„F•Ë‚sªõ¹^Caó hB‰™¾¶–B4‡¡Á]™¨ºç¬¡€-Q"¹,Ínîë±Gá?‚[�æÄkŒð(áfÂþÌi…V�l¤ÇÏ?ÿl>ÿüsW6OAÏp�GÎ ‡ØWÈb”€ŠW7íZ”¨ÔðX%´ê}QŸºa%q~îïÂï)b Lkø€[O(†ÓDM°·^BÖ݉WðÂ…#X¥Sê„+{œö&}ðÁ{€Œ»oâ…óÂçÂyaÏWP…9€)Ý!<*WnU1®óKj+Äx®œQ  TÕno\Wû°VrËN¡Ï÷/V0Î�kÓ,\Û�Ek{AL�xqÆ�#Š!w_x‡hMš4 %ð:çs^Hí´A¼�ó âx”òƒAuØ \A®MârJ)B8²÷é_9÷˜º†óç>ÓB::¯‡2Oµ)Ã}#„ùuî¯L …lÊp§ðR²¯àà'á/|"©3YS¸±hÑ"óÙgŸ™ï¿ÿ>—+0q/099㜆p]Ø À¾†׌ƼI¹^… mŠý´8׋B3˜]dzQ¸€só=é¼F3Í1’© J[_8@5‡èET× 0¹‘ðß…O 7wâ Tñ‰âq–¢®âJÈár_(Ø@SÙ§Ÿ~9›œ/ˆšgê¾–Ãæo„b­µA¯ :¯Få^J(<=Ô$|Ü‹ìKáÄóÝ| Ëb«õXX6‚BÕ‹Ÿ;€×{Q´zóë–ª™™æ®"t}{r­¼çœx£³úµÂ3„/ .Wñé�ÂmÎ…hýðÃæ±ÇÃëœ/Î M‹5Òí\2wîÜÀ5ÀÅÏ…°pÅKå²úRXúQTŠ)&yi¤+ò•õ¡ÚB¸%C„®2-ŒüÞÃx�E mB!œ'%¿$îÄ«ëaYÁsŽðl>]¼#ü•û'Ö©=!�ò!óçÏ7(Þxùå—s®ú�à{öìi ”±ó‚8 ç…á”L¼‚<%ª:¼G“umOËýzH­Ê… æ±�Ç2%�Ýù:ÖNÅ(YÌñ‚ð('^Á@�×ÂË„wŸ¾%üŽ-S*YåS«Gp .t¨ fÏžmž{î94§Í9ñÚj«­ÌÀ�3Î{Ø'†þˆÈ# Óä4gÆ«ÒÿÄ*Qâ¤+ q­¼�÷®F ÝŒ£ÿÿ׉W°*…æ°9å ÂóçÅÇOqDÀÛ´‰tg«œ SÀ°ø¢£úC=d¦M›fr ………f�=ö0ýû÷oSvÖ¬Yfâĉ�r¯Èk†5¬œ“Fƒšdî 5G(ö8²„.!ò;Ø]nÈ&×CGœx ͦ_)Z×1´x¦ðHáá<žJ�{€¢÷5�Ú:µ*ÏäâN‚0œF¾üÑ<òÈ#9µÿ ¹®M6ÙÄpÀfÀ€m08ØÀ8k-œWКòÆÕ¬º„Ij°kS .©H V¿_³j3åE¢£ > MÎ>GæÄ+¸°ªË ál–Ü¿'|Px�ð$6ùÝM¸‹pw¾>]xsj±yæ<՚έ½ E°È!ìõñÇT Â�å 0Îd—]vAø•‡àžE¼øóM¼ª@ŠÐj~œh½ðI«£ Uy@°Öª1ý¯FÏrc'éGoÎ<|ž…YN(V;àûrsáÆlDzÝÝ ÷¶BXEÑlôäèHbÎ`2JÍn½õÖV˜•=P6W ù>»ë®»ÚX,†û�6¥ðÞvÚiV‡6'hO:é$[^^žÑï¶ûßG˜ø°¹ŸÛ¬Ä.…+ø5pm˜œ„SÕÐJŠY§p�ðKá®&áÀ©Äª’hcË[÷þŸðNæÝ~¡û[¢ú§5$7'`%%%v§�v²/¾ø¢•òo› 7nœ…hçççg´Ð‹{³|°;v¬ ðïvØa‡ÙÒÒÒ ‰Wƒͺvüuüš3”PM¡˜ý)œé±%J;‹ø~Ïö)ÏwpPM=ÙÄ“·'ÿhn¾.üV8UåÞª…�Æ8ƒyç�w¬„žl6CBlö©§ž²V*3Z苊ŠìŽ;îhüqÈæk»ß~ûÙâââ ‰—vP+U: ÞΉ¯;G Ôl�XVRÄÀÅÔØÉl:ó`CŒƒCšˆPàòÙ:fîe»�Âö£pãóõ¹(`{î¹§…›�\ŽÍV`¡ÿïÿþoÛ§OŸŒzÉ•Ù1cÆØë®»ÎJ× ÛÕ�|—Ý}÷Ýîxñ ÕôäÅJ¼à\J²ˆ¤“¹–"û¾p·öšæàD-Ê�ä?95únø\8K¸JuÉ>ñR¶ï¾ûÚ/¾ø"kCˆ¿ÿþ»Ý{ï½Û´Ðƒƒ¶§œrŠ­¬¬´] iW…Ð/rx¹&^UJ� çããÉ‘(�I5Æežð væèÓ­¥œ˜•p§þÖ£Yù$ój‹ø AØœm¶ÿþû[)¥Ï:A¾ôÒKí°aì”Í·I¼�÷B¨UÛ®†L P9¼œ¯Õ>´‚¹î&Õ=^‹ˆÏKt‘€­ ;ü‰¹ú�;JÀº1ÔXÄ'¥ V�&¼‹îl:'´V)A µ€tÐAVYZ䈲ãÇ�GnH;”Œ‰¯ñ�üÃ>üðÃù.¯žÜtÓMm4 ¤xu`^Ù+\UˆBŒàª.üÿW£¦;O>ú/öÎ8®+éÂWaŽ! ¶ÃÚ˜½ŽÂ ËaæÄ›¥0ÃbÀåef“™ö�™™™)ff ,ß¿¿ª.Õ«©‘4’æÑ¸OÕ)ÉÒH²FÒ;¯ûž>-lµ€ììì0µø· 8?Ôó³%z—µ3èxL¼x GݤI“B3Np^$y‹~Μ9žvÕždú0€À|ç;ßñ’mØ`á‚Tn˜>žzê)ý?LJnݺqÇY\RÅkWH‚¸> H[s¨z6(wÇø÷W0°ÀezîÞÜ µ¦Â¶Øù5„LÊÈ‚,Oº€q.ÄYÊèÑ£óÚº[¹r¥3fŒ§jùÞ÷¾ç9ƒ¢Š‘ô �ñ€ )ßøøã�ýÅ_¼À7˜2èìo¾ùfæ½buOþýï÷ÅÅÅj"[†!‰ÅµÈ/ ŒÆÔþ1¹[õ5å#¼ê+ÀÂÇ5HØ`HÔµ çg´`d²Œs”/|á \üëÝB¤Â’$v?|øpÿç?ÿÙ?òÈ#^R.|»ví|‹-¼d zÜ'œp"†‹/¯íJI\÷ÌA5jÔ(o›6mê%+ÑOž<Ù3(¨(»téâÏ<óÌ$ŸwU†ðØÂy�Ù®u|lž]ŒÛBz>*2Äk¥¦•¸Á`‚vžðZ <þ—΢-¥B ´+â0æ¡$ ÐwêÔÉKbµg<I6lð²üÒËþ0ß½{wÿúë¯û»ï¾Û3E…pÚi§y.ü´&™™jÒ¤‰G¼ ‚öüóÏçÍíÈÜÚË/¿ìÏ9眼 —š6øžpgÆ–P‚h¾øâ‹<§i7kì \̵;Qk‚Çr/¸(Ï#.»Bh&�¬¬¡J% †ƒ¦�Ë´•ðGݧ6Nÿ@Wë÷6ýƒ/mHDV]M œqÁ~à�üc�=æüqÏÜçID&Stß}÷q^FË‘³lå\èùx„0[uWUyátüío›71èÑ£U^Љ—7"¼´"û÷ïŠ²Ã‹çžç.íâµ-P…èfŠœÎ¯V —*wäq¸zc¨­}°p næB2n í :þ� Úm=ÎÔÖÉâÀ�fyÞ,0¤+;±ÈÒ«*3HEELçeF.)<QDì^yå?aÂÏ0qC!+_¨ø¨ìò.\*ºUâµcÇhÉÞyç�|�i¯­ú{»ˆJJE¬2— €;óxž¶+¢ä�µjîZ¬wCA[÷U—ãÙ:‡ö¨FÏ|¦ÚTá윃HUÀ¢&ÖnΡÎ>ûlÏ™Ô�~ô#/I÷Aƒcª0…<óÌ3´Ó02„&^—\r‰gÖ‹ (ð\aÿ5×0|Cv—éïîBDª–3©ezÓ¶:xÖ"VªxMѶá?Ý~ƒ¡(�ÌßLx¥Bþ#¦³${’$`\ô©pâ4|öÙg=yƒœ‘É^ª gn¿øÅ/F¦UªøRy1g×<Üš5kh‹ÒÂL§x©¸Ni�pO-çcóUè–h+}_ YªU×½ݯa°s³Ã5œø+j™ ,�CÀ¨z+Ì´1l`‡ÇqˆKî£�>bG ¢•ÎÈË.»Œ¯ªÓ>½è¢‹&ŠbЀA;�¶m-ÌD3ýë_ÿò¸ó°Îoß¾±É[ÕÂça¨3 _?¬vaÐ…‰x͘1ÃÇ…Q£FÓ5ÒÜ6ܬ¾TÀ¶×ræ5O¸P_ß›Ò`á%lÐX˜ƒÁÎÈŽÔô�ÏC1qD‹™­:øn¸÷!çXþí·ß¦%è×­[‡>´Ÿÿ¥—^¢]¸˜‡+^d Î�;7¶eÜ”çŸ>ÿ—BÈ5Ü­‚µIóËjÉ5\4j¤Œ¥*\½jn †fÂ�r]QŸ3,†™9Ãúö·¿íö³Ÿù#Føõë×G’ý‡;ñ�ü#bR}b|-Ñ’’?{ölh³2V@LÿŸý,”·R¸#¥ÂU¦ÖþÂK]­0 O ×äI¸¸xãr£Ò!i‚¨'„6Z¤+UGö�áXTËxTD¼ÈeŒs@™9ºôˆ—q—¶;ßÓp‚`0¾.›ñbÎëôÓOg6‹ó¬*Ñbî(êÖÙÒ¥K©ö|óæÍƒç\‘°cÇŽ ǶⅡðã�?>âeÜ*œ¬A¼ºœa0¾*ì[Së0‡ƒÈ1XÆØ¹sg¬â~çÎ�>`úøÉO~‚i!踋Š|]Ú£±Íx}ýë_÷�7.ñª,`Ñ*ÕY´¡ºð\W' †»…Cª›ŸÉ%‰Ù,R+0Fôë׋»� “¨ÎRÈ|ÚâëR�¶nÝÚ4(®/Æü½'àügcpUI�q³p¾žo}[x´Ëƒ¡Hç¾^ÎÊVyÕ2ŒKk ‘ Ë÷ ξXUrÎ5`À×]wa‹Ï»på*^-[¶$ÊÇÂ�™ñâ¦"í⥋$ N¸¶'j>éÕêü̓ááÑz×74ÛLLM† îê±bs¶‚³máÂ…:LMr 9k ®ôo0ësö‡Ã²gÏž>°�2š"’ö9¯}ĽjŒê«áÚ'»:Á`°壄' ; ÕE¸8?"�”wfµ0%ЦKhUÒ¶ä­ç\±‰×¹çžë?ùäøº´q©Œ“#^ÆÂiž}•ðPW' &Z§ SKî¬ló0ÕU[„åÒŽbÅ}Ÿ>}0Eø¤%·�ø'm—Å*^¤ˆ¼ûî»±ÌxýêW¿ VžñŠ—q�p‰&Ã?¬s•u€Á`¢ÕZã >Žn®.:'›ý��W╈qbñ$-º¤€s.r ‰›R‡]ÞXß‘ăç*bàî Îx™xÅ×Ü­IÃ4»­æ‹ÖƒÁp�š1.SãkcMY†™öwL2HÆà<‰;û$eÝ>NGvå{ž«¾£-Z´ðþóŸãp2ªÀÏ-Éâµ]M { ÔE¸To#ì˜{‹Ð`0Ñj"¼AE«›¦lo͵҂Gy$¦Z„4#yX±bƒYçÌñ‹]¼š5kæÿûßG^¡.Z´Èßxã�´xkL@‰_¼4{°°ª­uÂɺ±árÍ̓í£ÖÛ7…£…ë…{2=kË#<ùä“©d|×®]uí~"Á™¦‘à rRÄ‹¶gOQWª¤™Ô:߯û`ŒâU)ÜS•W Kq–îÊû²ðX— ›Õ:LØRøsá”\MÁ;qBk‰uâ®��›6mÊ^5$Ç AeƒAƒ*‘ï#QâuÒI'Ñj�ü9d ÑTüÅ„uƒÁp˜ðu3ÍÊ–ý–Ù䋳¡›nº‰*‹ì?\z„¸&º=˜m½=n:.ÐjÐH¤xQ¾úê«‘W¤ÌºaÉ%�ÒÄ+gîQÛûµ½7Ó›Ç:À`0®ö÷¿ çg ÓÕm¾œm`ÙöW]u1N˜¨²üÖ­[µ5˜.,Y²Ä?ñľU«V¡'Å7t= n?æ­¢žw#a„­Õ¹ì\3ñÊ©ÚÚ$œ¤7Šg ­û¹–Á`8T…ëOÕ¥c ZœeÑd$6rvj}þùçã”N0ýÊ+¯ø .¸ X5„Ɔn•¦-G…%ØýÍo~3/ƒÚ&\®T¸@øoa«ú· ÃAº¨î×z'¸-óNÎ9*ɼüòËý£�>JkÐÏš5‹‹ZŠZƒÙ3 ß|óMÎéæDWmâEÕËé(±aÃÎùÚ&^õÞ©á¹½„WZ•Õ0 EÚgï¢ ð3g·ÄŒQÎ)VÞwéÒ…ó,¿mÛ¶T¶3ãŽH­¿þúëÎÂtˆéö‚öxÕ­25+ÓÖàdáÛj€:ÃDË`ÈŽ>-ì£Ç­ÂòvíÚ•²òž<½åË—Smù´£´´”ê‘{æ¦R#\ÁX%ØÜ\RR’Û*¯JM�™¨ÃÅOë™Ö�Î`0ä ¯Ó?°¹Ù 2«µ› åG}ÄÝ·¯¨¨ðißÇ~È:{L' â®ÜÅ ‡'-¼(o"HúÇÌRû*¯2]¿?@øKáUåd0ä'iFá0 ýÜ—I¹`•a‘fso!T\´:{ôè�p±+ \é/’ø¢fè;*ôêÕ ñªùù2ñÚ)\$üXø¤¶ã(üì8Cô°ªëjmgœ&<Úe H ÈCäŒËÉ�½“ —K3ÄX⤂pÒþtb�w‹/vÞ{6¼ ß߇d/òÿwQAÎù>²?_¯&§¥Â!éôŽp…pŸ‰—Á�_¯6ÝæÂ«ëÇ‹1Ã}öÙg\ôÝ{ï½çÄä�Ê ˜$×;‰}rÿüç?�œÛ¹  ©. 3WNª`·lÙ2^wQ@Î�œwqCã‚0¸J59ÍÕµ¨˜…/ƒ¥Ä—OѪëˆÌŠ 8… #^NfºœÂ:YÏïd®Ë¥î;ï¼ÝÒ¥KÝüùó�´S)\€O+‘[nâĉTÄ. H²†“ù>'g^.(* ¬P“ÓTÍ"ü›pºÎq…/ƒáHa±°….•,ªí¢Bå"+1Ü_þò'I®S§Nîÿûo§ p•HAÛ¸q£ûë_ÿê>ùäª-·páB'†�¤—ð×´ gÏžífΜ‰Ó2>ûì³�¤ì;¨ZÁ?^ø¶šžV Ë÷Ç!Qƒ!ª¹®ó„ÍU¸Ž Wmg-2ÔË…ÓÉbB'öi' âN掜˜h+9Ià|Ä�p NÂm�¸ùª.xÞ{.‚U¤å%ócU›oÐVClÇŽKµådKÒZ…¥z±«ó7¿k×.·nÝ:¾7צMÚz¡‹×Å_Lë•J|?ûÚ.\¤ÂõNÀ©[áBƒ‰—Áp°š4NTñ:¸>g.\µj6„V “¾žµå„¤Z¸K/½Ôýüç?wgœq†Ë¨DhmRqa6áŒK_&±Mè•uÕãúõëù>yþœ$Í;IA U¼¾øÅ/rCâÄÄCÅíöCìÓó­yº�õÏêÒ-†\þ †c…?Ò4�Í™í8H.‰ôÒËËd†�o¸áÖš×w„Ê$ŠoÍö¾\Ÿ·óÎ;�õ(~Íš5Q,é$–Š`àPž·Ä;-SSÆw…GØq�Ám…ßLøG�þß]Ó“�T¢ˆ˜+ yŠÔâ§°z[E2lð5È‚Ü�b¢v'é ßÅ&\ñÂ`ûºzé�sE¶]]¬ïGDÄO²†'×�õïµDñ~ª´-rlÚ´©¿ñƉ£âN¿Ú” 9ÃòS§NE´hz9/Cp‰NJ­hÕ·…Ë÷Í^µ)S¦D­Åú˜¼'|ßÖ0agaûø² ÚeØ_¸<Ë“eƒˆ!¼œ£��HGTSEÃ�8BÂÚ”šÎE8*$.ºTY[aÜÝ‹‰ƒ 21Hì ó2£åÅ]G•áï½÷^á…"Z<ÑJA›0ñâ{/..ö2ÀÒH6Ì×+äÖá^]]ÒWcžÎ3ካ¼¾"$\�mÙ$‚ðøã�ûqãÆ!\U¼ë®»üñÇ_íˆjí� bBø¨©¬ÕíÈGyÄwèÐÁ‹¥>ëç£MÉòꫯækÓ¾DÐ|ûöíýÉ'ŸŒ�"|‘g&Q¼xN?ýtÿì³Ïz™kóaƒ¯A??߯r�yúHÿVŽ5áJ ¶-ù.áp¨Ü$Âò•¯|Åÿà?ðÓ¦M£=GëŽ (qïU{î¹í$.lTlwß}73KÅÅF^?pà@.¶•CµšXé!†ŽlmËW¸â¬VqlÎ�;ׇ ~'XàIå\`âU*œ¯O­µSQäb‡Á`8XÃx‡dk"BW^y%¢â%M£Ê½ÆKÄ‹–buNAÎÈ~õ«_UUjÔL‘»ä’K|çÎ�Dß»woß¶mÛ\ÏOR,\á‹— ‚³oKÝšáƒÊ¼eË–œoŠxíNÓ5&Åz£g0‚tÝm�ì þÓ~b¿mºnݺ±"‘aq#o«Vd¨°ØùõöÛo#\l(öÏ?ÿ|¦ØÑêãLŒÇy0}út.¸´M´ ^Á™/žÓ( )ܰP5Âó½MGG^Ðó­ƒ�Á6 ‰KذÈW"hÁô ‚^e餓;jHU Э]»–€[— <Ž¢—HÖàßÄDñ9‚ J.²$t8Db†HŠàu—$x�KióQ€X0’=F�ÅÏ/Í[t†«‹Æ=mÑ·,˜×�0l×ó®m™Yl^@Œ’8ûœœy¹_þò—NοH’'¶Æ@[V§@. Ä3qÍ|<Á½dòɬ¹ƒN\Œ\ø`bD ¸Â{ÏMC¤±M×\s qQiÝóV)Ü )ðOk7b“ —U^†äb½p‚°¥Þe–Q}yIˆ/bY#bÄKmžšîøÉ8$YÑ㮜ÜÃÌ )‚ÆJîÖY¤(-.*:DÎ*­<€ª–Pä¨pÖYg9™ÿ£2';2mµV8S£Ò¦9ƒU^©€­r˜ª¼ùw¶‹9ç\"HÐ#\5�÷#BA±’�Aâ¹Ëü8ÚŠTurØÏ:.z´­Òʃ RýÎ+óu.*°M@RM¸YI£pMþŒÊ˪-/Cz°X8PÛ&›…¥ ¹�Ra‘pÎn©aƹ!C†ð:âUí {Ïݺ‰V~ÀÚ™sb pQBÒSÓ: ×$áßUÀ*œÁÄË�ìV»üXµÌ¯×³°}õ0Z‚´% Ö :”ö  &ZAPu±,RÆ\”w*­C6,§A¸Ö–GNÐÜBƒ‰—!eX!ü·,Ï®Õ*lWÀ�X'£ªBĨ¬83ÑŠ¦uÈ2Oª®sÎ9'jA,ùºT|i¨¸¬´U¸ÑL¼R ³Í® õÞþE8!Ójl¥p£ŠY9�O“¹Áà ˜4$¹„­Ô:ž�¸è¢‹tÖæ×x®‰º—Ë;ƒ¹ S °5ÂM*Z}t»rá³…§ Oàí�—Gé —ÍgÅ *-wçž{®n¢ŽT^’­èV¬XAõ�ÔVáuûñVW0°ÊË`(×xœ%ÂiZ�}*ü½ðMáËŸkæ[ïýd¹ãÞ*ÜgV¼‚ŒÓ�ó.‰Ý´áâé'�tsfI® Â? ™p™x>¬+ÓÃì­Ú>\ ü?á„?¾/w؃… Åe†ùÛ`EÄá¢â" ¥Þ-CžFËa®O)cóçÏw²Ì'_?ig\…¿ŽÒßiC(rC:P¤iÛ_Þ!¼Dx”TaG{ï*ä¿ò0 ²U]8ýž~úiwÇw𘜄ŠD‡™;Ou`Pœ—Bõ±<†—CNÕãõq¼ÎÇ1¨Îûãýùjr†¶ -£o3Ø™—a?Æ�ºìà@N"<Â{¿�’¼<8ó¢k‚”0W…IBB�Ý7¾ñ�œ„ ‘1b„ëÚµ+ÃäœQ1‹ÇÏ£Š ðz¶—A&±k°Q[…޶d/ƒáP5jœ®Éô%ÂVjà8LEíªpàE‚` ² «|NÂÕ§O'[�0fò¨  ­Ý½I�ß0á²3/ƒáá‰ÂK…7 oÔ׋…Ç �´ßáHÍœ/AL¼½Öó)KȉåB¸ Éê5§s²°»°‡ —U^ÃÁÂæÂ‹õ|«£°— VlàŒ‰–_NíÂ9sæ¸AƒA„ 1Ë4~0+FG3L›pmU—ì ª.;ã²ÊË`8Hx®ð»õeka“xg­úB¸0Plܸ‘4WØзo_*/œ�™ÂE2†“%– »›nº Ë}­A»&öøfÍš‘,ïš6mg<J;S…�'«Ð †$¡H+®‡„· Û#Zö»šqŽE< )gqûðà K¦eèx|f¬wõÕW»+¯¼ÒóùC·råJ„2«Q„s¶víڹ뮻ÎuìØ‘Ïƒ˜ †8FYu¡ÚóÕ ñ[›ã²ÊË`§ŸÞ©g[G:C"\ÊRÏÐê ¾Ïñ>Ì&L`EMæ•’kÞ¼9©ðîÎ;ït?ü°»õÖ[±Ý#bl½®6½žj ázðÁ±è»çž{Ë>UYTÂU&\­íÂÿ¨=Þ`âeØÏq„ð á Âfê4LLÀØ·¶o̘1TXUsZÌeaƒG¼&Nœè¶lÙâ2�ÍÁ¹ÿþûYçOEåTœx{ÖÖ!�!I£¤¤‘s­Zµâ±|¼»å–[ˆ©âü, áªTgសkY…&^ÃÂN«U¸vI†7Šd#uE¯^½öôïߟÖ-<׳gO–ƒR™e¶ò¨ªXe⮿þz’9²®9Éf¿çã(ÎÇ2SäO=õTfΨè¢FÞ¦Yœ u/Ý^ûM0·¡ÁpºV\gêì–!Ùà,ë q–yïW¯Y³¦±T?‡SqAª°l�Y yÍ5×d«”0ppö•U¼¨È¨¼2ѸqcwôÑGóù® �~Z )ñ»í7ÀÄË`8LxŸÎtã i1MGrö%¬gµlÙòD±SE¸ÌæDhÈDäe6 \Ùμ˜+ãã`&¨øh+F0 ¾Gó6' ×X»ÐÄË`(R7a[„Ë\…õÆ>=“Ù«¯Ã"ý?8Äç±8rõêÕmÖ®]»A„¦4‹É†Ê²ÿ«Ú’Ë—/§š«.’ ¡ÌfÉÇ4BÓ Â|nW)‡ Ë�Áμ v¥ÃÇ�õ÷±ÈÕƒVhÛj«¶²«y`/œ#œ§+gV·…8H{ˆœq�*âs„WdV^œ�q>² ÍÒ¥K¢¬â%¢˜é\¬ å]¿~ý®ÿoï`eÛîŸ=:z¶m[q§f\;zq»¶í6¨íöÙ¶uq4ž=³w¿;Ùm&'×÷ÿÉwl­¬}ÖZ£öØÒ5ÃSÜÉ ¢ >óŠ¢ ‡Rg¾p(1‹‚>]Ú4˜¥^ØRèÐ¥G›-lëxÎâtŽãx¢º\ç"zpxò»l¸°Ã,ltqÆp�׃>8Öh4¶wirlffføz£m[íè°ß;-™òèi,EMžáAþI—(¯(ks[ø0%ºN•õÚ€Œi¡CƒfØÄ,[ÙL�Oò,uR2vV™*‡soçbŽã–¤\ÿ€ìKn¿ýöá¾.{³F75Ï>¼û =¶÷-—%‡Kï-øÎÄì!»õ£ýèýÞÞÅçUf×dwò&%Š¢hX™8žðîà%:dkÌ€]š,°…çx„[ù+?çë|‚ëy#¯á:._ä .dœ„½­Êa¼ƒ¿ò]²¥f¶•´:úЇòn¸!wlTþ™Ï|&ï{ß›Û|œ;±ÃL-Ù.'r _îßøÆ=üàéí}‘Ûy�Þ|ÿæy‚¯ó:&‰âÆ(£h»MPã8ÞÄeÅaÅ4UV¢Œœ �\ÖƒN¡Î<3ÌSg+϶2KcÑå¾Xžª\̸‚S˜f©K b©3 {VVlN>ÐÌêÿg%îhÑ…×IkµÚóffÏ{4áxe’û«”&›¸�ïño:D1xEÑ+Q-͵\ÎQ:2�M1N�2;+ßÎ}�…Œ”>)=Ú….-Ú…fÙÂ&ža–ù‘—KÉ`UVæ4ÞÆË9‹#(±äíÅ�‡öP¡Êþ¬Oƒ—x¨ð¢OƒWívejLrÇrÓÈÓaš Ê”¨²½RÚ‹¦Ö¢MºtHGôèÒ'#a´œ]ÖR Çò^ÍåœH•�PÊOóÏðn¥Iƾ–0AB‹(¯hƒ..J§ÄÎJ(1(ôÉÈ·cO+1A…6)kµ„¹†Wq-çr eÖc=¶òøˆßñuRöW¦È©EÑ.ašƒ R£ÌrVc½Tå >À÷¸�yúdëD�‡ø_áz.ãp&IØßU8ˆ)¢(ŠÆ&9� Ñþ©ÆY¼‹ïðuzdkP‡ÍÜÊOù4ïáB¢JÂRV¥DEQ´Ä•9†7ðynây¤d«LJ‡:[y”¿ó#>ÏÇx#§3NB·¤EÑ:+§Áýü‘ßpsôÈaYþžä…)]šÌ±™' �qå&nã�ü–¿ñ0³t‰bÁFEl+Ã'ps"Gq"“LP¥B™ eJ0,g@Æ`D¿�Žh1Ç,uæx†gy�Í´è“1Z—”Œ(¯(ŠbSù°œŒ2“ÄÁ¸èáij$‹6whêÌQ§I‹:ml+‡�VÑcXEQM¢(Š¢(Š¢(Š¢(Z7ýÇ}%«…N=IEND®B`‚PrismLauncher-11.0.3/launcher/resources/backgrounds/rory-spooky.png0000644000175100017510000022615415224505336025135 0ustar runnerrunner‰PNG  IHDR’­<®ò‚,3IDATxÚìÝÕsÜÚ–ÇñßZ{Kju»Mt Éaffffffz�?dè}˜y.33ÓaÛn�ö^kÖ¨TN¥lLeg}n}¯ ÷éÒ¯$YêçœsÎ9çœsÎ9çœsÎ9çœsÎ9çœsÎ]š{Ñ>±E/»½È*-‚sιËt(¶JkÔ·hmX×[×Y7XwX÷[÷Y×XG­µþxŸõÅÖ]Ö¦5¶–¬cÖ=Ö—[ôÇ#u9ç.Yw%ÿgÎõe‹êñæ`0ZŽ“³/ñjQ�вª—\UTÃ3ÓíS‹óÉéMU]ƒ*R;[d mU¹Ѐ¡TõŸ·Z«´V¬kûæÖ3Ö´“Ûû#�·Ye}Áj�ŽÂ9wÑø�8²‚5°®²ÆÖÄ*¬¡µHÄãñêÑÛ«zñ¾²_ÓÎvVsnꜚu,‡X¨d¨l8Ð6¨d³ª @%+�ˆUD’Jn šhUÒ\E& U0)@àPœ‚ê霛g$·5 +Öëë³Ö»¬7X�Y3Kàœ»à|Hõ±µ`mXËÖ�ý÷·ôßO™cYÔ‹7e},Æêz@rn˜ˆ£ª¡€HsLˆe7"’@„ÜÎÁ! HnÁ¡À."0ˆTTÕ1˜ƒÚ ê¾'f0G-ÊaVè453͹‘ùt{§��9-9m`ëŒõ)ë=Ö[­§¬çÜkæCâ kl­YG¬›­ú¯×¬%"¾*„â*�™Ã€¹ s‡Uí@î†�@Q7"Õpiw4ˆØêÎ&¬Ø}ϱSqØ=[!bU¨"PjïÕÖ©gòÖÉ'ϪjÊin#³ó¼ª6éÇä?­'­ çÜ+æCâg¥µa=h}½u�µ@ăXWˆhÈ¡ˆvˆ¤@Ä À"ôÿ'ߊ 1VÝXpˆ`¶ìëŠîgÄ¡;²wƒ†pa©E0ݘÍ&§1Ûù¿N¡�ï@Ušl§)*òBÎí§Uº³•wZÿe=æƒâÜËãCâȪ¬»¬´¾Ùºè°ÕÆb0 Å`3bQ£¨†( (Ê8Ä.ƒ™íÎ�9"Â¥"µ³nDD2rÛ`>;‹ÙÖ €"N"6(iöLNÍçTò¿x“õ”•-…sî@>$ެ�u½õEÖ÷ôÃ2² ­j¸|Í`¸´Ê"ظ ¬Ç°A鯅9€¸�KXjçÝ™‰J†Hó &[/ �m†BP5"éÉœÒër;}“ª¼ÀãV²ιù�8² ë*ë^ëÛ­‡­%"nÊÁâ­ÄGÀ¡¨ÐÝó¨ËîL¥‹ˆq©R“³HÍ @sÂΙç0ŸžAN €Z±¨À£’óGR;ùÉémý œµιåCâ‚U[·Xßm=HÄuY�oáP•Ü–*‰9ºp€}�áxõ ÊÁ¸ûžˆpéQŽmk €B$a>9ƒéÖq¤v  �8jQÖ[àð^ûýߨ¤GûçYž´æ–Â9w GV´Ö­»­û‰xÍî¿_GÀ­Ì|Lr»¢ @µ ÖQ —ºQ‰±ˆ`.©û&öP$TÅRØ%®îÌÄnÆ÷ƒ’(ˆB1@Q Ÿm¦;ojçgQÕ'¼ßú˜5�sîeñ!qÁZ³j눵Ì¿‚B¼%õÑ‹[¬1q‰d„Q ÆÝå¯�� ÷ÕEÄa?ªzÑÎdrj»›ð9·€;Ú�tg(m3�}o%‘ÅàP$…~:5“OôÏ¥|ÐzŸõ 묥pν,>$ެeë¶þÈ­qˆWƒÅ»‡›)ðµÌÅr°•É©pˆBA¡»·2è†%Ú×Äq÷�B‘ Í ª !Vç¾ÉÎi´Íèh7©�u5Ó-$û�ä9‡€’HzNRû”ïÿtø¬G­íW6(Î9ÇVÙ·amZµu�µÁ�Çq³¬‡1–Gìë«C,Y%ý‰1V !©�äbÆÂʵ(Ê!.š`6=‹f¶½çç"í|»û]j§HÍ Pˆ¨HÎ;’Û'Uå­Þfý‡uâµ�‰s>$Îß˵aµîµ¬ÒºDU,†KõÂêÝ!w¨¥? !"ç¹m" 7±²yltptc1›ìs…JÉ-D•¹�Nng€ …ŠªH{\sz«ˆ¼Ðø¸u굊s>$α5²jk½ÿúk!–£#U½x[,ª/Ñ]Љcw))§9ˆãÕcX\¿!¸ÚfÒ]êRU ¿ô5…ÝO±& "ˆdHjíÐ<¢wäÔ¼W%¿]U> `ι 4$Î�ÿj–Ú…[mTŽUý5Õ`t³H¾v¾sjÄbÕ�•Øe®‹v>¥9fÛÝ“ð8Y ŒnP$'ˆô3ÛQæ8§ÀŸP‘‘ÔþCÛN> Õé…;Cq·Ä9Â9…µf�­!s(8”?è×�øØh骥õkï@Q�p‘tÃ0µ3“œæ{Æc¿AQU¤fÒ]öRIÝ™JÎ lÛâgEòßÙ_ˆý‹Jþ4€­ ?(Îù�8G8't1ýsüâX�nµ‡ÃâêÑîéù tQnÂÏg[ݽU ïÇ~D쌤»Df#Ô� @Ý ¨æ·Úïþ:¥Ù' jƒ‚ã–À9×!8wa­YßÅ!þxˆƒ¯-£�=䈲^<÷j–¢ÂÅ�Û9¦“Ó�œÎ{!¥žû§ýï£4“n„ršÙq€*?«"oÊyþvÉé£>ç:Î]XskÂÃâ&T‰¡Àt÷"ˆù¢üE‡ˆ¢ö¹•wÇ„p0bF(JÄr"F‘äv¬šï&_byD5 :0±f–¹ ȇÄ9Õ%U¹  u 10„¢@È©Ù;&ª.4"B,k€€œ[@»ˆ°?1#–us´8¤"#@ï°ŸiˆÕŠHB5öÚXç.¿´åü]__jý&qø†²Zب†KT/¬÷cTõe5º¨ïéjæ;�ÔBTÕ=7ã÷§È©Ýý+¯Ôì ¥9¤âs’ó5“ÓïIŸð9ë¸ÕZ ç^çȺÆúq€~6ÄâÖz¼1¨êE” pÝË!Ëjˆ‹MrêÎPIJ‘ؽüõRT¥{J¾™mu7å™R;•›ïœü7Éíž¶žõov·Ĺ¡õ-ÖoÑÕhu­Œ¹.£»Ôdˆ̱{ëpa?ûÿ¤*ÉÝÀXýÀ4Ž‹ª ™vcÒýk“U2ˆÂGìmÄšÛÙûœ±NY'­ν">$ΑuÌúë§cQ¯—6C¬0­€ˆaº#‡€A½„PT¸´t—´º3« UÁ.Õn@l4ºÑi›©}=q<Þζÿ"µÓwxÎzÖ:þÊ_ 霉sµõ#ݽ¢#õxóªX B5Z>漏ݘpè&Ä—ªþŒ¥ ÉÝÀ@U,í~ÞÌÎXÛPI*Yæí|û39ÍÿÀ›­ÏY§­/ÿìÄ9çȺßú#ë¡XÔÇêñúB,ësg%=bsDm?çq™Ø½×’Ûyw#?5“þ8Im¶Ë\SýW¯·ž²NZg,�s/ɇĹEë[­_%âÛªÑÊѪ^¤Áhuϳ$Ä!Änd˜.7*9'؈ÀÓí“Ù>êW’]ç’ÜœPÕ÷øûþìäé~PZ8÷¢|Hœcë^ë�­[B¬n(‡KKõhµŒó�¬ÊîÌ„˜q9R•þR×¶ž~áÑ49û ²“Ró¤J~¤?3yŸõˆuÜjàÜ�ü�DçÔJÖŠµÕE¨,…XRQ�ö{3pÏ¡E,ªî÷—"3[UÍý§6–Ä¨Ñ@rÞ ÖÜzÌÚ�s/ʇĹÖY·*°FDcå�|ED’S÷uˆ%.WD¨’H&I-Trh­"S­µh}ÒJpî@>$Ωµe-@»!9 TõÄû!BG$£ .W*UKInIUk"Ô*²  ±NYÏÀ¹ù�8§V¶Ö2wZ‡8xñwm�€î�—3"F;ŸvgVýò•!1“J> `d}ÞšÀ¹Ëù‹«µ¾`=%’?¥’eº}â¥ß¼K„ˇh«@ÿšè&û™�*Ö­›àÜ‹ò!q.[g¬'¬ÏŠÈñœº×²ã@ª b±¦(ënL‚}¢’˜äoð•ÖQ8w çÄÚ±žƒ¥ÒZ³�SP„ˆpœûƒB9wá*#âðõÖí¾Á*áÜ�|Hœk¬Ç­ãªú¬Jž§ùšùöÁoî9,C  ÇP/¬£{Ÿ´‹úK@t€‡àÜ�|Hœë„õ ,•|J$a¶sv {‡Cq(s7 èÅr€¢vg&ªJÐ|WŒÕ­î´–áܾ|HœSkÇzÊzZ$?¦ªÒÎ'HVGqÞ˜¨ "Çâ¼ïËz eµÐ�‰H.ˆøK‰Ã}î¶"œÛ—‰s­õQëëŒJš«æî¦»HÆ9"Æaã�^QP«!ˆTu=Äò²p‹EpnçÔ:c}ÄÚV‘Ó*vV‚ÜÎ( 0ŠÃ†CÄùe5¶PÔc(„‰øæãý, öç|HœKÖç¬�¨Ê •ÜØ"-öR!{ΰˆ¹¿Ä5B$§!Çê!ñX¿„ç|Hœß+ù�uF$O%'ävÖßQFD q¿�éþŠk0ZÇ*ù:Å÷‚è+,¹}ù�8×Xï·>©"ÓœæÒ63HN{>ìê0ÙûJ ‡õâ&ªÁPeÅXÝ àf‹ºœ;�‰sÙ:a½ÐgUe&¹Ajg‡úS¡C,¸t§ý™É8V0Câø-ÌñûÜçöåCâÜÜú¨õA•<Íí¹™¢Ó !â0áPXûR ŒP�×@èõ¡|mÿW\×î]Uç|Hœë)ë ªòXN�¤4?ïòV(J&D„Ñx!VØ£­¢¬a˜9Üby?€/·Fpnçëëm*i'5´–é?ÔªÂaC̨VøÀ.‡ƒá2BQCr»bù ¾ÊºÏ"8wçZëy듪ò˜ä¤6&Pp(Àp1 Çë( ØO¬†öû5P( *7r(îp̺ Î�LJĹÜÉãªø¸jN¹�A¤E,8̈ƒáêÑÊž·3GÕв†ª0?à»­/³ÆpnçÃûi>¦Šã©�#53e�+AÑ�}¬ƒC£ ¨@ÑHDZbìÓêÝ/Þ¬*ù(T& �H ð©j ŠCYæ¨wšá-ï]�„/sŠbJ�…wGyF1�¨†,êK|1¤›(±õh¦…Y(Æ"kÉ ÏáØQ–È´ @Ìsªþ¿ Øü¨ˆ¤¡IC#’ D¬±1"TqeN”´Èa© Ž2GU)ó ³é :÷A�.T!UˆŒ§ YäXë¤QÉd–ÓÉ„~Ç[C;k±t| F tpn=æÂÍ/¼3æ�kÓØ«�g…î"òm¨^þp ð44|HEÒÐ À ð< ¨�¬M˜ã\IÄA�CYLCO£˜Mpå,„µ1Ε„~ÆQ6Ö’D–3=ÏjDZÔ.8µ$¬ö…8‚v£cM‹^+¢Û6ˆíÔYÁÂ9(‚pz5aš{GNöåÊÎaéŠR·�–€?¼úa–IC#’††u …HdlÔ6Ö‚Þ—'Ævz·ÊÃäÓah€Ó^]�Jgxï‰eÂZÛÓIrV»ÊÙUP-8³á½²Ô‰X_ŽYîF¡âˆ,D¸ ÀZ?â+>³ÇÎAÉ+½d±ÙŸ¥^¾ª’ÉþϽ’ICƒÐÐpràG€_(Æ®gíå§ÓÎ íî:ˆ`£„ðØY1¼_Ôw‘‡:G‘O˜Ž(fC¼s”Å„ùÖ&ÝTXk—<²^²Þ/èµ 7üõ¥˜VjH" ÀA„O nlåüƒÿ²ÇW& ÆN÷î°tº l/øß€~Dïyòá�ÝT$ �=kãD!ÈÔûb ÷‘*Ô¨Ó‘'£ý0tUæSÅú)K©Ò[r´SÏc§�´ ²pn=áÑÓ]º-„5"Ü3ÂÏúž¯XÁxíÒDf¹ö†g¼² ÄÀ�;ÀeÀ? 7ÿ°€ƒ€­®)à«(€èTçeõZ(�RE _…<¸‚iDÒЫÀŠV¶Å­Å¡£ÐWˆî¿@„Z$¨UG¨fc9 =�41¼_ˆdò_ºB^½46Wok{<õ…ÂS@œþðo€ˆêH Ä@ˆªk 0è…óZ`¥::Õµ(+™î ̪ã)0r(‘44° l-›Eq¶ Eq® "»/?©rÝó`x¸šçê±õ¬´”S½’³+%§–…ñÌqz%æÔJÆZ?&K„,Œ©{A&_¶Ìù�„õ¿÷í»×g2+ÔÏÀÀ籪NJîŽR#õµ;†‹„Åk¦Ê°@8 ´€%à1à!êó0Ö�N%ŒIõ¹p•@b ¬PË•t†À¦ˆ‘!е ªê—Q�§€ H 0v�×+¹¯—ªë“ºbi8É"ihx xVDÌ‘‰âÅ~ˆnì÷–ZFYµ¤œŽw™�%’’S�’•޲Þwœ^†Ïx¼Ml3Z™!2ÐÉ,‘@©Q>HD`c9 øNfø§ÿmϼvy*…Ó®*èßt�¿\”ú/ù°\åT¯• Úèë€fUÞÆ•ÚÔÕÅcÀ“•ȤŠZËH"„T`. 9"F�®‰‰â£ð6J¼ˆqª¾¤Gçî(rk£TŒ5¨&e™�"JZ9"ï\áÊYšOq9¦Î—ªÞyà‹€p x¸ܨ„rxp4œh‘44+Ú—“ŠX6#AU! x_ÎêÝ“&A!<ê˰b|:Ú ÍrñKé”s«†õ%áÜF�GÏ´YiMXjCšªŠ5ª@-Eµšvføâ绨*¥ß“w®çIQjÛyU©*AùJ„} EQ@Å«jš‹Èäø\@³JX«DqL�¢ºžU‚I­®—"R€Œñ2¢’cìTÑØš$EbL+I;*bð±±±Aï�ˆ1á#Æ&c±Qzç°§ ¤(„Jׄ¾[’v°qÂl|ÈÁöeÊ|ªÓñÞ¸Ì'3P[UjË•D¾˜ÿx8 áDÎÚjhH�_ü%qÚ�º+çH[}j‘€1íþ:ÆX>äÎ $Z’φŒ·ÁA&Ö(«íœ'Ϋ}KE<ùpŸ'Ïw�Φ£]\9´úG™ƒVI�Åó“Àxêù¯¯ކ¹x÷FÁ4÷€âEL‰�«÷–zXJA à)Q< •LjkÖH‰`*¹ˆ„Ž1!Y›'* b¢DŒµÄIcãªTc1$Q¯üß·(½{5%-Ò ‘´ò¾¢ê@a6°»ùÎþp÷êª>ÒJ„9°üià¯#N˜HêÆéï!ìú›­&YÏt—Ï…ýµT=sÄØ°pg|"HBïcF19dve>ª†¶”s+†GO)½¶å‘3]N­¶8}kix]Dw(óŠ‚jåˆê¸Ê‹ò¨ßsLîŸîóo^qj%h¥`Œú²Tý!tGÈZI$ê˜{‚º" IйFLÁüQÁqÚ!г£h…*¢Ƭ¤ ÷åqÅi»O¥pwBlûú£Áî•·\™Ï«©ýªôª)Ó 'N$ <üvóµqÚY= º+ç 77”9"†´Õû¸8Š,VêK\1¥˜”]!Ü(1Z�%ðÈšãùDz �³��;í˜46 J-ªõ"#@kq„Ô3¿ 'L&ªpc§à÷ÿÝ17s‚@’,Tb,"ï æÕ€s9(x_â\�˧¨:€ê9(Q8�¢ PEÕ‡06ßu”Ã{m‡÷Ï'Qˆ˜û°,´FæÃXY�Ÿ A&»7ßv{·ßÙQïZÀøÀ/«ú&ž†Ù#ihD²0™”N¹±�óÏÿ×”K·Ë �¤Õ?Š"†š;ŧÞŠw%Q’aëá'Œ±áØ•y8V@½ÃFIAWÌ(‹YxŸVrþäD>Öd1d~¿(ÁTbüTª—¬»b£½ÖZ1M@#à `PN¬H> è›tlœ§]D„;ÑÅ¡®y–*|*�ÙQ„]u«ÞG;z­�ŸMYîÁJ¿Ãó�¯pv½ÍJ/ÅF‚¨$ÜÑ,W¬�±&ÂWy‹¯Þë�÷•L‚h*�ŠòÁ0œ8^¾0æÂ�‚ÿü:¡GgÝ»H È´Ê‚MÛÄq �ð©`m\ U*¡Â)ó &ï B®du·ê¢ÒÂ𙃵Ihœ‡JÇØ{R¶al$e ¼ Ìh8±"ihH�GÅØ¶±6N²þ]wùU…º +ÌbÂðÕ„Éx/ =y—“& Kʧ#ºm@[<ûØ O=¼ÄÆj›4²ˆe�…þF=sÌ xLn2àŠA�#ˆÂx¼²zƒè\& hÝ ~«†ŽÿùÆ0T#W·`ZK¤EÚZº‹Dê‰ qÚ"ŠÃ_øÜ+‚˜lba«ïqn.dʼÏŠ(ˆµDqZÍÒªªŽ{LøïO™ Jlï@8VN\�¤¡A€ÓÀŸ°Qò•QÚYY9õÔ]W¯iK§¿ŽµFuA ãÃÛ¸r†¥“8YÚZ©åÙÇWè´bΟê²ÜO‘ù0‰Ô•ˆ¾W$ºØ™ßð¦ÓC¼+Áûª2q¨¯ùÞ×ïU¿Ð|?{%ª¡9š¥5äÚÖŒÛûÊ[7SriÓê­QÜ¥¯DÅ)ÆØðXa­dÎ}ÿ�¼+Ù¾ñ[Ô•Å&è¯f^=¹"i*’†F$_<,bºaú§±w_y#X#ñ”Ó!£Á¾œåT·äô²çÔrÄúJÊcç—xôlÿè¸EY`^ÅP…À ²"ª²Ç«À˜¼¢F0 ÞR·EÀ£tUqQqŸ UBä¥çíkSnìäXcØ$”Q�vû½Ï¿Œ±ó¦t8F„�RµÝwTç ¦£}T½‚àM`çäJ¤ICCø�3ˆÄIÖî\y^Gd ¾œ0<ÜBÝ ñSÖ²œ§J8¿‘rf½ËÚrÆãçû,õ2Œ1ˆHÄÔë \«û¶Õ¡*‚¢J%‘ªÂ@0x¼k£ª¼b¼QP­*å½Íöû_…Œg> c)„ݽ7¼u#b»X%ëô*‰²˜ˆ8i§•À?ê¨g6Ú§˜ Kïݸü`BÉICƒ§D¤+b«ÞÈ]¦ñ¢ø2g2PL"¬Ž9·\r~]øŒ'6ÂtÝGÎôÂðUWcçÆ`¤²ð—.fn�¹8@Ñp¬!¡n$T$Þ8Ħ’НÕÏ¢®JPó³û`U˜Ì¯_'d0ÌP¯ a¾p¥Œ5¨ñˆ�0ÆàUÃwªTÒ_™��áÊ}ʺ¹“óÚå #ÇhâL”Ë·-7m’ÖJ¶²6>ÎA"ÍV�zÇð`“ƒíK:vË|ô–ªÿ¿À?þ30¤áD‹¤¡a8#bRkcm¼°;¹+gä“hÉz§äÜòŒå^ÄÚJ‡Ïzzõ(Öh§1bæÕÅ<8Ž •Z TÕ R¿W! u‡$äy(ƒÇc@Að•”,b"P�¨0ÿ?¤î� ÷± q^Cõñ_^²}PP”°;´\ÝI˜úŒ¬»NÖî�Ì7Àl¨ñ¾äpç*û·/j>ì{WìÛÀŸ^}0*‘F$ K Oбíð—rXMèƒä³¾œq~©äñÓž¥¶ÃyË™µŒ¯ü‚sœ;ÝǘJó⨘Ÿ› —…›=P�JH¨¢F¯(UÓ�J FP_KË‹wÅ€x€EA�ÞÓíªà«*dç äÒ­ÙÑqÉ8O¸t;bPf´º«¬tVÂ,,c"îÄ•3Ž$²·ù®+fÃ)ª ³ê‰44"i8ñDÀ)1ò°16M².Ö&¡)¦6ºO?ZÒo+ýŽåôJÊÚrÊ“ç{,-[‚D ’‚ÔÍô*×�v¨«¤þs€zaí—ZZ}Vçâ²uã~q*qýáúÐOO ^9»ÐL¿º•ã<ÜÞs\Øê±;�IÛ+¬/�"Š[Mõñqîm^à`ëbá\^9pµÚÝ÷_†ó†O CCÀˆ± ?˘h9N»Qœvȧ¤ðìé Ïž�ÒN=kýˆ§Î·øÜ§{<ûH‡,µ¸r†w%‹�ÆæAÝAL}L-šÜ,T,R« �;>gŒ­®Q£€èBERŸñÉJ¥ì Jþ×[#þÏQ\ÚÌÙ>p\¸eyõæ2#³ÁÒúcGñaóE¡á.äÓ!;7ßÒýÛïNœËÇÀAµ£ïþ.p›†¦"ix  #Æ|¥{ÊØXŠÙ�³½’‡VG¡ YëÇl,'|æmz­1#¼Aê\9ßói±2©�žŸ[˨ÞWOÑ…º€PDõ”EªªW°€VIësEë럘@³Âsc;4ÓÃL¬¢ðL Ãåíe�Nÿ ig‰Å!,¡aU e•ƒ½kõ.ö€·�?W=´j‡†F$ bl„êWZ."]#ž'VG<´V°Ú³¬ö#>ëÉÎÑqŒ5óÊ¢B÷²êL¨$¤º—‹©ƒÅªFïÒ_¸¶ø�±ÔrPæ™…m=”ôã¯JŸäžÃ‘ãÝS6w‹°ÕIé £¼Å¥Ý.Sé°´~&4Ò>^?$gt°Éîæ…él´½­ª{ÔO:üSÀÿl†³‘4Y¥Ý_c)ùDÖ�4S{§ã}w®épÿæ ˜ o—€×�üG`8@ihDÒð  O€~�ó­ˆ´Ÿ}Èòèi8½Òâ3k±Ü�â ¤J Q }kc0u�ð^ù¤G}”q' J­c©%âç⨴QåŠús5Õ¾X¡òxéÝ1×¶s&3Ãö æÆA‡ÜÖt–NÓi/¼�xTÓ/{7î_/ŠéhÓ¹b¸üÛjË“ €ÑЈ¤á�A€èW‹±Ÿ�$ѹ$B¾èÙŸód‹‡6âè½:¨ÎCQƒB½Í8ÂÇEêM)‚Èâǵ® úµ æuc£z+tÕ*×ÕIý‹”.¬ Ù:(x÷ú 0\ÙŠ¸¾gÉ�Á&�¥uºËg°Q  ‹ÒðÞÍŸêH‘�™Ž8ؾ‚+Æ#WæTým`§êƒü5`Ð �H8ž1_”¥ñóilVŸ:ŸñÅÏu8»#òñ-´ØÐõõ6í€rçì)ETÂ1èâöäR]Ó…¹.ö7ê\]2ˆ˜P•ÅÔãUÁ×ï�Å!®Ò+ûCÇÕÛ3Þ¼2 õi!lÄ\¸m窎¤Ý¦ÝÛ Óß@ÄÐ0'T£ao¬b6 ò(gã ”Ép—2;ïËCïÊ` x¹ê…¼Ù c5"ix0‘*oßœ¥Ñ—ôÚñ3­,•oüü§W?ŽDänÃJ‰Ì‡·êÂB ±°{¯¢„„Š.|­V/,TºØïQ‹ê×°qSðAlžú½TÇàœg0vÜÜ-޶4aw`¹¹³5ŒBblDÚNˆÓIÖ§Ý[m$R¡ê)ó “Ñ^Ø\1Ÿ˜?aQ�p.Ÿé‘<\9óªz xøOÀßöOÃ+’†F$g�ï1Æ|g§=¿¶”Æßýe¾èÙkø„P…Ń!p®$R�¨A ×Q4djë½vEbYì¦Þ2@-ðõ0rujÙ˜º*©eâÃûÉKåú֌ͽ"ˆäêí‚»–�Q—̓U°UÖé‡ï'í°B½YX¨8WiL;ǽ� õ%@áUǾ̇ÞCU¿”ÀVµÙâ?ÞJ‘4<°`ø1໺-ûgÖZÙ÷}E‡Ï:#²|ú• nd;‡÷+¶L%‘ù1Ä êQ  •TÞÛ»X¬8êÊ$êÈâë ñéäudâ½r0*yûê„·®NNá`’páVÂáT㉒qÖ%ÄX¢$£ÕYùˆJDQU\1 CWùdÀt´Çd¼{|MQ-TýÞ±4¼+�òPà ° ¼üŸª±>”†F$ ,$Àß×JíóÏ>Òí|û—vùœ'ÒO\"!)   sQTYñ„íÜm ïíià‚@„úûו­‡µê¬¾ÎA ê¾Ìb•‚†ßÁkÂÛ×·¹~{F·e¸µWpé–ãÍë–Ý‘¡pá}Ä­6iÖ«õ!ÉÚÕ³ÔåÃ*‰…íi4Tn޲œQÎ&ä³Q¨:ŠÙïJçʼpÅtrt<òên :n¦ÊooW"Ùö€œ†F$ <Xà €oMbóÌg<Þ]ùñoXታ1ÆpwªºCî(H*�r>ôIP Ò@E«ÊÀõóMëþˆªÜ¡-ªï÷ZW3x_ËÂWÏeGCøp-¼ÎdVrs{ÄËoðÖ¥&¹pmËs0‰ØxŒµ$Y—¤Õ ÍôÅE‘BÖ^ ½‘›8Ô‡Þy>‚j ,fAùdâʼTõwåD}9TU*9\^6+QlW�ƒJ àhøP‰¤¡‘È7ßÇöËŸ~¸»úí_ÒçÉsucýÑRT�p¤µ0ÔTý Á¹ïbñà�ÁP‰ þ"Ô®Ï%¢R KçrXè}Ô‰÷•4ªêÃ9Ïp”³½?9Š)¯¾³ËíÝa؉÷æ®á`b)½€62DqFœuBR« JÖY"ŠRt¼+påqÌær ¬fW‡ªWïJ¯¡¼ÓÜ{7Vuõ>WU:ª¤pX‰b xx ¸Œ€ð€«ÂÓð�"44Ü;ˆ�¯~™Œ²�˜ ŒÁš(\‹â”$ë†×@jyˆÀÛÇß)Eë¾ÇüÜÏ{0žÃaÎ+ïlóÆÅ=nïN¸µë¸}àÏ /Á+› !ÒP]ˆ�þÿ½¢( ¿ïƒ÷À)e.UïÊП†©¸C\9E½Sïݱ¥ªÊ�’†fh«á#�©$ò³O-Ç_‹I9½Úâ>¯$òi¡Õ,«ùMLÅ#*xuÎá5/•€ÔB­÷W Ÿ¯«�÷V" òtVEÁ�ÛCþ×k›¼{mÌåÍ’�!x…²t¨ 6n‘Ä&Ê"ÃÝ Ó6N¨GÞª†'V†Føt¸†­pe¨Fð®ð¡òp³ò8¶ ÊÈš<�­I“(`–—ùpZ$ÀðÕ{ß”†¦"iøH"À3"ü²õ~üÍqš>âMK~ð+Ú|ÓdáSdqˆËˆ@ˆyUbà8*“´½„µq]‰Påp óÿ¯ ]|­²à½’å±8ÂÐÕÍí ·vf\¾5eûÆ3&¦(K�ˆ8i!6 •ˆG‚Tî†1IÕ#16z€âCoc6>d2Ú ÃVG畸ª^9Š#PÌ""Öˆ˜,‰Ž"ký+½6­4¢p^†ÓÃ7¯l])�?¬Ö|ü)àV#“¦"iøh²üÔZ?þÚ8m=*QÆsç3¾ðÙ ‘{0¯ óc@=À€ñ ‡w%ÆFHõ^ð õnÀ !¤:wF“œÃÑQ g¼suÀö~Á oí±=P¦3ÇlV„›éKšuˆ[ bƒ=л?À+'NÛsÉ=0qe~,ŽÐ÷˜ C¢®˜†ëA à€0‘IlMËZÓVÕ0^•,ŽXîµ8µÒe}©C¿“b�_“­ýÑÒîáhõæÎp |.ðyզБ4|t‘TU¿q¹}ëÊR÷‰ÜÇ,uc¾ý‹2Vº|š(¨ RÕá@�xdD‚ÎgwQOª/Á{8¼{í€[Û#.^²;ðá¡Qƒ‰2ž–eè‹„*BM‚'&mwCó1xï¯U?åÁÆZX?s,‹ñ`{>ÓJgÓ‘›�÷JõÎŒck»­8ï¶Óv¿�uz�¬ŸÆ6IâHT•N㵞ÚÜo§œ[ï³Ô͘?k?�#ú�léH$›ÀcÀiš¡­†�’d}òéágôÚÑïÝXí| &Ž»­„ïûŠŸûdDd¸G¼ç1¸Påªo-ÖXâ´ÖbÔPaVºP]”Îs}sÈ oîreóx¸Ê³?,˜LŠ¡ô„ì+IÅI‡8íblbæâ¨ãîUHHœ¶Oð0–†þF‘�qE˜Q* ïŠÐ8/§‡¸bBYL5[ã{íÄ­ôZûÝVÖieq/K"ÖúmâȲ¶Ôf©“‘&‘1 P:ª¾$²½¿~J%°7˜ðŸ^¼¨ÿ÷톓ü ÚÖäw5�»=y4"i@¨çß ©bÿ.�0wÙ;]¬�#±ñ³íTíR7û!Ùhc)åÛ¿¸Å熿&½zl”°ÔMøÆÏËøœÇ‚Dî3Šzð€ó0+ Š2(‰c^¹x�W/re³d{H¨6\Y‚Œâ$&Ž3Ú�Abñ Î+A w6ÏÐ�;#+N;Ÿð�Õc*‰Üÿ)»ÞA“áNØ‚=T!Åì(ÆÄÖ‰'‰ kË]–{fyÁ`4¥t޵~‡‡O-ñ�Ÿaµßƹg‡FÖ„ª& M,Š–ß‘44"iXžžÎVçg€Ó€·ª×6€.00FºË�,ëvZÇQ´$°á¼�Kçe:+he ÆN¯õéwZ péÆ6£IÁdæˆK¿ñµŸ�ðO‰  "Ük‚,f…2œ8®ÝÎNilØÚ/�­å•KÛL˘Á@‘ªR±I‡¬Õ%I[•< ªTâP¼‚*!£ŸœD¢8#iuOÜö&ÞAùdÀd¸Í¼wãòqõ9ˆMÄÚR'H$�#ÆÓœYQYa¹ÛæÙG6xþ±Sáµ{L�R¯•âõ0@›†÷�fh«!>ønà9À+@ ´ 0\•/½8²ý~'{<²v©ÓJ»ql;Þ©¥£tŽãl­ i§1í,%;ÊIdà æúÖ!ûã™CLD'KøŒGàKŸ‹xäT„óÐïXŠRq^‰¬�DB›pE‚Pe‘ iŒ§ŽÁø¸*ÙÜ •¥SnîäÄ‘!w#áê6LÀ{@Å„­GŽ"JR¬�«•î :%d¨¯ñ‰ ckƒ‚/1( ”ùc„vÓï´B•ÑmgìŒ(ÊRà ¾�ÊSç×xæ‘õ ‘ûõ»ÞÚð�þËë‡o_ÛÙ^þðÏ¥ážÒT$�ì# Ö€ÏWyX§ÚÊ�¤Ê0J#òôÚRç\¿ÛzøHid QdQUìñ±5´³„¥^›Øš0~½w8bç`Äx:#/ÓÜ3˜8ƹBµŸÔhVò¿Þ�xûFÉã§Y\ÒÉÖ€ª’Ä€¥Ž¥ß¶œY�éåYîÙÚ/Œ=íÌðÔù,H¢“.Þœqu+g÷°d4uŒòòBØ9ŒÙE¡:Q¤^J:$i»Z³a¡zÍÍmQËâmž×ˆ`Ä`¢˜$ aY¬�ÂõA�Äô¸ CXålx‡u ç=IÑï·Y9Šv–Ð:ŠØZ¼÷líÃbÌ•n*�V¹µ>O?\Iä~"h^”@ ´€”†{FS‘4ЫzZ ÃVç_´!° ”À*¶In­�]ëŸmeécÝVº²ÔkÑÉR’8 •FZgâÈ""A›»‡lï ™æE�ÈÎᔃqD¢TýÂ^U"†8–3�5%+]¥×‚$’ ­ÞÛmÙ &ç@zmËüAP£©’—BQ†³ˆ8nSjÄÞ¨ c÷óŠÀFQœ…>M¸~¾€QOôÿ±÷Ðqdɶè> Ŭ³%3s3 ѽ3sñ]þÿ?fúÌÌÌÌ �èÂ0s£Ì(&*ÆTc S„·ÑX¾Ë¹²¾OŒ>Öú¾�²¸�@y5 ÉI�>©€`‘N„™ªŠF|†U€à¬Ø/³’/¡Xö秸Ls ±‡Fºmî¤y¸=»\ý›ßºðÁüj©rSü?ÿÛ�‘´­ $m³L�ãŒxÂÔ6l,ãâ/Š+�;¥Ô\ÀµK’®J½£¨=¯.Í�ñÎd|oе»Ž �„ºplÛ€6\µÖ %Lͯ`q­ÈâôR¾Šùµ*´âk9¦P´öèfN‡›”ÏP 4ÿ^Ðn"T€n è(4š X•|¡áƒÐôŠU…J]¡¡”‚VŠ�M™xË�p!F PO75ƒ¬>¢*,�c¦r^º¸Iƒüù“-]RB¥èw�/0ñjy(xPB@‚—�å’Îä¹mšÅü(…½õzq"©XCÝ)‚ÉëSÞœ[ü?¿öŽ/“’ðŽIm}uë@Ò¶6�´Í14Ý=¦xÞmÒUUsÿiCá�98ÖíïéˆXˤb¥t2š.+Ý©¸-`aI·±³²œ û5V1m‹»Ð‡4îT§Wqmj…ŠÆB®¸1RlI¡5E�õ;}sÖšåá­²‡F‘Å`.Ü¡HŠGù™)�…_Ûm)Ì,Pü(+.–v 4–ï|lžHÖiq}œ¬Ù¨Q²¤¸6ë7„Âj–¡›5>o×±™¦”÷Òuî§°xŸ½þý7 ²V(ûâ‰HEƒÈ$Âüݱþ‚ÉnESo\šœøßz¯¤ÁÍÒOÅÿCñïmmÆHÛÚ@Ò¶¨øqS8OfUÝÜ?.Çø˜T³Ïß«Gº”Ri mÁŒ}êïÉXºé¡R®úç¸tyk¹"¶`L‡,®äq}zE"‘:æóì@Œµ¥¬GU΢ێË.tª×u®Óx`Š�}^£.Ç&ÁGnóþu…oÖÏ¥·xl‡õ4|Ô ¬Tß-®Rÿª^Z‚n”`)0Ú ˆH�ƒéË`ÀiÈÝ ¸&úiKíG"ɪît {ú2Œ\vÉø9ûî;7>ø£Ÿ\qÌçüMÓÙþÓÍG$mkÛÛÖ!~Öô{„@cÍ£çÔá‘ÃÆûûº³©t àf=O;�¦ÇEb 7‹ÁÞåÈS‰( …2~ò³KX\ÊQ¡vË ²šÇí¹U,¬U±TTp"ÑmعSÎÁp‚B†¡ãÅ´“ø=µ¡ŒµäSžJÓõz• é¨Wóе,4¡L/F<ê7 Ú&ñ„M…÷až)ôvÄ´c)K˜[yœÝ¯ïx^ÎÔúšFea mÛ´µ�¤v5*ª�žlüÈñC£ÇzºÒû;Ó‰dG:nJÔj ôw§qxß z:SˆFBÌy·äÖµF&“ÀÍ[3,\oҘΚ_Îar!‡[óe¬•5µ¦œ`d‹‹4/ʤ¦¨?ÅÆ=F5�f­4×SnŒÄjå5”r‹¨”–Ѭ¬BiöÎ0e•гˆNòD0à Ôýë;¤ygâA_XÑ \ŒnDšžn5&êGxJ•:W‹ÊDÞEñwÅo·£‘‡³6�´-hRW‡Ä?/>`[VaÿXoâȾ¡“cÝÙd0›NX!…rˆ< z<2é8� Œ)œŽL—®Ü¡üù&ŒÔÏS …”1µÜÀZIÃñåÏC±GZø-Š)’–Ë4Ó3`ÛÕ•I2C17Ç)„ Ý(À±\×ñ~¦¡¤¬�Xìܨ͂Ò"Á {Hz:âl ¼ÛÖ÷Ùˆ "’Ñ D(×êâfŶYgâ¹e)þŽa„=°Ð¾”+ù)6ÇÔÅçÚÓïom i›m¢�#¦˜ž?éØVbÿž¾îÎŽäÐøpwêäáQ{x =�ž�VíÀ¶­‡J¬®Ø² cÁõêÄæWŠ‘rÓ!ˆH êþ Ò*¸óÆÚSa3þÖyfDo ÛKs�Tné•UxµÙn ®'GÊï÷q ›J[æ ed“a‚ˆˆ0"äº-òœrµ�€k³§È±- 3–å¼âßïØ”ÛG]ûé4Þrm*-G‚î+e¦NÓÚñ²Ik-ˆ¯¡mZHÚ¦LÇùI"ûÄ{-¥:¥ë+›I � t&�à•ó‡06ÔM Ø’)…r¥ÊhDkýP¥ïJ¥FY\+a!¯Dl3ÿ<~�B­ggµn¯Òsƒ¡™WÏ”‘ öHF–£�ÕI¦±Èn³‚AÒÑ™Šû …<ß„1b¡M?ò¬u¦²>A‚r(‚ šç àQ‚¿7¿ZD6Áè¤éy⚬<îd¡¿Zo ðÔ¡ÉŒíÒ¿²"�7g¾ï‰O·ÓZµ6�´Íß/þŠøQñn¥Ttt ›êïÚ'ìªDoWZ½rî $*a§ñ£˜Í´‡KÅÖÊh4=v®_½3�Ù¥5ä«6*ž J o"·õ©õ]ßLaI}S38Ú¦á5›f"a¥¥[Ð_¡· PÀñ?LcIYÖæ#ÊÛäK•FW*zK&î£|�Íh€fÓ)ûO,EPpmË4™Rî�,¯®TÄF*D¹_À-ÂÔ¨4b¤'ùÀÍK¹RôÉìrþ‚(#„M4R`¡½µ3i[HÚ¦ …÷“f„hŸ¸;:˜�?8zXj½£C]JŠæI lGó#™t:­ï�—–ÎpˆˆóQ€‘(už@0L¹‡v”Z_;ç)Øýí²·D¢˜ÍÔRÚ¦5LŸ ‡K5Ê+Ð¥i4*�qg@¤+GW&nš7Õ öšÃ¥Ôô@Wjx9_FoG¥j6¥gxPúd9W¢Jo!¸‘8‚‘H+*ât�†P:Ž>RÚª�!MÖ¼F ÕrõâtEÜ«üµÖ¨}‘ff-äQADx~‚‰Ã�€ÖŠ‘«(8s®új‘cp[`’/‹˜êjxš]dÁdâa‚P­®ýß#˜äJUD8àòsv/¡G->9¿ŠÛs+˜Z^ ÖŽQü½(~m{v�¤m�c¦#}�øXÀµû†û;ûöíéëøÂë§¥�Þ×�á"±SlS@cff nÐ%=ó–œçKe²tL¸HÔ©àë/ZÀ"NûQÓS€jÑum7h„3ÆÖFÛ¹ˆN©˜j%�âê4š¥%¬™WÊvØ�žˆF qtí#§;›žf” ]8DzÑð¸ðƒL@Kùõ¦®£]¨Ô`äöÉÊòZ`Ôäõäýb{Sû�Ã:J£áù5‚G­Ñ  Ü HJòog— ¾¾—^Í—s&ùÀŒ8¨£mÏ(�´Í6õ�sâÏ…NZcxd°«ûô‘=î§_>†±áî*wÊr¿úåÏ�ÃÑýC,fn€°ï£Ž‰é%Ö=&ç–°,À!)+ÇJ®,ÝèËTp…œP’Ýäd_Ù.,ÇáP'12¯B‘ÄNžm㬔¦xÃ/¤3iTr¨äg©Ö«‚FõÒ1Ù?´”Þ–T÷j®ˆË¼ºt} ‹ËLH ëú�%Ì,–X¸”Å‹ÑE0’„ãÙçá¸÷дR`×y(šÚ"ˆ´Á‚ÆÁÔ¿"p454åè xXºË+C5ËÀƒÕ™í ‹ 瞇ÙTèÚv 8xxJM“Â.y61®k°-‹‘ˆ£“R•b�ž†^4õÆ:ÚöÔ˜ÓzØ4þCâÏ‹�JÒ!tÞˆ°±Bg…Êûù×N¢§3I¥S¥Ô&ÁBÃŒŽå¿¥zjÓcïÇôÜ Þ¹x.Ý ™ÅÔ|ŽMb Jˆw²€n9.ÓV÷þûŠ@ö›Ì÷ÍG³îË ôZ ¨à¨låÁF³5o„ÒêÁ bအÛZ�€ÃyñϽZ�Ÿk~ΩǕMFØ€X(Õ`Y|½òZc†i­¶=Uæ´©½La7tÞ×-¥F»:ƒ}ÙðÞÑ^uúè¼rö ¤.²É®bE«îQ(V$ÚÈq�¿91�¹¥Þzÿ&.^ŸÃÂJ�=0�(„Èi‚qÖ?”e?p" üœQK;�µÞôσ¹öøÚhø§ Ö8t“uÔ|ª®ïåò3K×´ü¼¿Çž ×±pÜy;¶Mö•¥AÊUʽs'®ä¨ž­—¹µIÒü‰"l@¬ÔšhzâMí÷’Ìx·-ÎØ’§mÄía“Æ?ï:ö¡l&žU^ûøÁŒôwâ¹SûÐÕ‘Ä&Œ�æ×ïÌâï|õgÈåKTs]Í•1¿´F í¥ÕY,Ì›Û.œ@”€A]+‹C¥ \Рfä,7y¦ÓY-°Ð¾³Þd„Ѩ•YÓÐH”R`�£R�Ÿ¡¼š-u]Øc�HدsDX$w›Î4¥cüHÄ"€*ìÍ0 ªÖÏ\³.½^q�z^Íf“Ró¶¥ÐzB)|Uë6å÷é°6�Œ˜Bú€é9*Æ'ezF@„óAö uãÐÞDÂÁM/f?yç*þè;ïàë?¸€åÕ,êùÓåŒ\‰íG¦¡ä(e½ƒ‹œeóx_ T0g<çïYxwžEðo²†á5t9'xÔ*yÔª´f]‰âˆ� ЬÀó�^­4”V‰ k›h#@R$€Aƒ€âim$G`4±@f–ë8XÍá™�éú†eè±ÏÂta•h69²NBpµÔÄb®ü†¥¬ÛMí¡mwk‰ežwN|Eü`*9›ŒG3çOŽ«ó'ü$i2$ˆlÖ¤ëœBykù"Ör%_O‰2ÛBXa‘<dºŠç”b·�‡ˆ<ÔGÊ‚%¥Øt(�ùLÑnQÔJh²^¯úQ‰  q1ãQyuÀkº�µŽ¦çqþ¹åPë ×?ZM”t%LÇ&p# ‡ï¥ã:¬…„Bîºã¡0ÐF#ˆR…5F¦ò;\P›ÏÆ[ÓêX4ÿo„B ³‹kßkzúýÑÞTùæÌ ÚÖ’§!Ÿ4�†GûºRç;2‰î“‡FÕñCÃèÎ&Ù�„°ã¿êÏrTkuv[ŽÍV0—c˜QÉ}êÝoR_œ"4�ñô 0ÊhÖYè”Vý”£ šW#H˜=k¶xе ��–«lø‘!¦àP¦$âGFi×µmCϽ;mØbrlÃÄòÁ&pyŸRÞÖüY4‚GÛxªÅ2vÀžÈ¯§…'J ¨¤“ý=©!pÝŸ5�6�´�ä)¨�$�VÖ9‘÷íé?$uÕÛ•â¼tIm‘™õ(Öß�!MøÂ¥ÛìýP–ââo@„ ¬ûkŒVlv§ J\ŽÏŠÔÓSÕòÊù%‘#íy`mB7`�#a ùÀàòœ‹½Ü: äK´åÚ¡ ò;Òc÷dS8v`ÂÒÚù ¦J¤ÎÂôÖÿøÿ~ó«(Û…eZ‘MY0g¤ö`<¯5äYˆ6hlpàS£^eäQ..£VÊÉýuF$ÐM(íÁR"õªà:!.æ©xŒ&€»YSŒ´…{I´+ÐøïLo�o¬k„â.êM#£îqðA¤ÃjÐàãÖGRÉÊ•*B¡ ª5Ÿ\Qà"¾]Q-à°PNÙUn²JÐ÷‚oS޶µkõiNtťУ5k‘6ÚÖ’�!pDͰ)-¾ÏD"¡x$ÐßÛ�>7ÐÛðGÞ¾&ZY�Éíjcn=™ˆâKŸ>�™ùU|ï�˸>¹†jiÁP” -e0ž-)÷VÝÂ0«<ªãVŠ+¨WòŒBx_½Ìô•ö°- WÊ"…—zUý�)ÁAœÑ6´søï->6ë! \ôÍ#rô­¤­ø3ÛbÄÛæ‘À(Ò�—EyÛqp`ß._½�î® êõ:Výáby²ºP.W¡ñHÆT˜B ²Ç¢>Z3õ) Ø¬z0ÖEeáɹޕŽ÷¦} @@k'í·mkI«7䔸g¢�A(^6�íê={dß`ø�Ïì@*këQIg&�?ñŸfwºönâö\ù•ixÚC$ÖAŠ*¥M”zª áÚwp› ‚BµÌûZ³ÊM§¸$>XûðÄùZ(� c!ìhÄb €Ëúÿ.À/Éh葘ÿVk¾–£È¹³–ešE ž]£¡˜ÞCÿ% O\Ž-VV0`rìÈ^H0è" £T®xÉDܺr{]Ù4~ø³÷�¦Æ£X�#m=œR€c¢£|±ŒH$ˆJµ~÷󥻎%îð9Ë¿—£ïhqh{‡®ñµ¨‰7ж��µ�$`†M�5Åô˜‰Dú”R¥Ã{û{ŒŒËütÇo4|áôþG–:Ù¸0KãB³w´îw?‡ÿúÿ#ØömÜž- ´6G¡?‘¡*3ꈆ‚(˜âþzyy.¦n¤XKªÔMÐJ�iRt[Í…ÛPtgDÒàõ*[)U‘ûBhÛÇÀÚ@Òcæ… ›þ�Qñ�c[ /Ÿ;¸ï¥³Ç…ï¯dü­ÔD¨—µ­lÇñóÖ ³dºƒ‹Íá}Ãø'ÿü¯áÿ[ßÂ}ûmÜš-¢°:ÍTW8Ö�H¢ drYöÇ*E%QŸC¹°DáD@¯ÎôÔ:Ù‚…ciÖ(àxˆ†]„¸Ë·,\¤ ÷¥16ØÉÈc|¸ÉXo~8‰;S+X0¿\`�bŒQŽ`í„ +1Ÿök(À6ÿŽÅ˜nu^«XëhàW \Ä;³)‘^H8d4£ ¬¡ÁîH"EQ"“?üæO±Ek†€+¯'�ˆ¶t®›‘u¾ 4`DûçüYÌïs±[à©�¦*YUH1œà]­5üçÌæY(ËÌ!Ñ�òš·"·J³îÈŠ÷’1Ù¶6�<ጬ“Æï�ø‘ˆÇQ™2$52ª¤GdG´¨šÍ&ÁĶm³è8…¨×kŒL~û—_C$Æ×¿ÿ®Ý^ÄJ! pAÅ2ìL7Åø'¹ N9‘jiͯm0=Åhƒim@D#°�ˆ�Œ‡åÈ¢8Öò%ìLãèÞ>6ð…üÈ#cª¦¯+éG"T@^Ε06Ô‰¹Å®Þ˜§ö´íN�m±ñ� ±ïWžÇ†DO{­âºg <>¢¡¥5˜ÖJ§â\¤TîÒcSÈdp NxzviË»}þ;eÁ5sÝM�‹I­qá‡×"¸­ˆ@À�Ç€ eÙ å×�ò®ƒR¥ÊçŸW=­ 8µz�›J­Ž­™òkFL§±Äe©T³©OxûINqµ­ $®xÜHÂÐ|ò³íØ;Ú‡=ƒ]èíJs—¹Cœ~qîÄ(®ë»Ës­!6ï3ðw§07pãö<&rf·Ùdí€S ÃqJ¥›ªÂnqºST!×BOG =i öu`¨¿‹��"µïƒ;ü¿ñý·ñ+Ÿ<‚NŒ¶¢6K)6ø­¬•`ÛG¹NÍ­qa¼1±€ÅÕ"d6ë¶¼7²ˆ²8­Lwz<4˜ò±ÉØ´mA1jaM�i¹€ËkýhÝ�ÎEž�¨‰DŒ ¼Ü¿QáY­¬æ`Û¬­ ^olæú[Ï!h¢&vÚ·f²ƒÒh8|éz>OËÖ&H𠘸æúÌï1=¦5#0¾–¥(–­xû ¶–>¤Y¯Uëþc§›`ÆÀ}‚�¤mm !pDÄsæ<«”òų'�¨SGF¹+ÞÁF,‚†Y4‰D[·Í‚Cp8z`"M�oýð¾ùƒwñ³ ×q{j •¢Bµìw¨‡8¤*–î•óÈã¨�°ŽÑ¨—åØôû8"µJÍzY¼ÄÎp×V`?FÀÆPO‡Æpâð(†:™ÞIÄØš‚D2æmtg"8¶¯¢$Å^M¦Uò‹9,,L]Â’hmWoÎãöô2òÅ*´Þ†ât�©@xÛYGá¥ngͶH´h cA}ã´M À‹t:ÎÇŒÇ"’�Ld^Ãøè¦fx¾©´©m“±fÒo­k3ƈɤ¢ŒÁ’CÐaÚ  ‚à;­jà wÛü�z£ÉTŸD#­š�©¥lê½PV«qÓŸ­Ðߦ·�äIYñ´ø ñ.ËRnog*yôÀpøKŸ>KÙac}$3ÅU©” $M“ëëîÀ/~êœ,¼{ð“·/ã¿õ>¼róË9”rk¨Ø«¨–Œ¦ÁÙë,N€L¥Ö+9Û^¶S!5‚†€SWÚˆ¢Y…ò*p´Ÿ"aÑ—QÆPo‡ßÄÉtáØHFå<›M®+ˆû®I}Íå d[%ã!.Úëz (%2·˜ã¯žÎæ—òŒP*µë[dÍqA3üpkÑŒ˜H$ ‚뤡ýZPë®›÷ß7‰EÃ<�ǢȤˆFxû^@ÂsâÈ8nÞžÙtDÅHÁÌ=Ó”¹QÓ!iåM?­&¨HdÂçÉ(šúI lÃÅóZ£Ñêàws„ž•Í45*vmÛŽÊûœÖ€…¶=u¦ðtXLü—Å¿l}3"ufVä¯ü#_�”ÒÀ®1lɨ‰Æ˜ …B,‘�,.Z³ž"îay-�…¥5Ù�Oã†,0?}çŠtÄßââ[oh4µÅ£²\¸¡XKWËrì–·Ùèè´v‹F§ƒ÷Ëñ~z­æßxh˜^Ž:µ«ª†²[‚ÒMD\�X˜”QîR»;qK¦IüšS¯€H8䢯§ƒ�fqý¹çÊçów¿òc¼x|¿û‹çÈÎc냫3¬¡üþ·?ÀìbŽEßÉÙU䋞o‘•Å¢4óÿfAìÊ$Z=,R`çôI‘ëà{°I&W¡øš“ŽÝ‹ÞÞNŒIì^váÃøÁOßãßù›ÿ;ÛµÎJŒ!)°3eÛŸ×ý¬Þ`]‰`ºž8°Iãë4=¿Â¿¿°šÛt­*W¬pƒ$u/OëMOÿ:Gí¶­‘<�}#Ÿÿ-Ó?’”Nçðó§ö…ÿø¯¿ÎºR»«5T,L¢r~?ú©EϦH'cêë:òA|ê•“ŒN.\¼‰w>¸�é¹eÎ5)WÊе* Å@‘® Ûa<$Œúÿ–Ø£öH@²é8ÓŽ þß6i®‡0mD›(±.@'Ú@ò¤YH”‰@þ¤ø^ñ@0àè—ÏŠüÚžWG í6ª%X­Vá8. ìٰï„yó…OÄ#Èfè«ç�bBré×oÏâ‡o\”Ø%ä %© lÇ‚G–NÍfhZ¨54ªZA)Êrü»¶cj¾lz½ƒ À0 £ã2ñ´‡�ÎöõÇÑ‘Œ¶Ø“K·M!ZÒXl¾‹ÅÂȤc¼‚È&©ô’kàcšçÍň¬"ׯ÷߼ޫ·æ±’+ãQÌ2ZXþnœ=œ—n£\­�r&Êâël€\á¬P(£·'Ë÷{x°Gƒ®Â},“J°6¶°´úpé:­[5pÞÒ-¦Ö.#6¾†F–…@²qôÄ£†n±ÈÂFI™´i¥<´íI²6�ȇ´OvJIL¸—^<½¿ü Ÿ8Õwþø8‘í´B‘zØ'Àîàz“_®»Íã"Ù@¡�§D†D'Ôaz 3ÇN:G*gºèø¡=8{|/Þ¸p?zó¢Ða§8ãÁg5ÓDõšqh6í9ìÀV°]�zÐh¢¦›¼O;A4=6 "tx ºM$£>»&…XØF_G„½ŽcÃVª%AbŒÛ]�iD£!ÄcaþÞýÒòþ0M¥‹×GþVÒ}ß¿2ƒ‚Õ¢‘ Óeñ(ëLo96Á£•z„5Ø,–ìˆçë�íH¡O"ÉŽLRá–JD1:Ü‹¯~ç�‡ÒÛÒ…(¥žÃëîH†Z´ßÝ6Kqž X¹b�æG®ßD~$4pó ÈhĤ a$g\£0ñ´í ±6�¤™ê×”RŸÓZÇä>óÒÑwÿÜï~öS‡÷ò‹¾Åô¥µ"¤É Z3½!�0¡7›œÔ¢?¬™~ÎDÙ?6 lŸ>|þõ³(KR˜^Åwø."‘®ßœŸ‚m� ³üMF «¹¢ÿ7¹Øv�}Žm›(ƒì³ãµý]<�bdØX-qC.| Ó˜Ç~‰T*ÊDë(Ü|ËX^+0¤@¿~Q¦tŒ°²XholA6„À»NÖÄRR±t«Nh]wÀqy' (I/)=v¯÷õvbïØ AêAæR¤„r¹ÂÛöÄI‹®7ÌÀ,»ÕÙþxŒ)A^s¥Z4¡¬D¦Û|€e£§ægÊ ö¢6˜yNM£:ñ7ÄëhÛã´6�üƧÏZûÛo�úè?bY*�ÚKgöþôßø'çxgG"níLèÏ!Usó«Œ2—rìê†6,¡{J©TbsW<ßòbf[{2’ñˆãp-…›·¦}tC,`ÐÈʾ¾wÉJ�ÅRŠé£zÓk�“ö´5R^y.žã£â‡\ìâdyŒÄz{2­Y-J=8Í·’+ay%‡l:Æ…Æ4@ÕÞ —¦Ø¿­7].Z°5EÉŒêLÇ•9ùV$²]fš "2dž€ú°Ï¢á0&…éw»OLÍß—²¼>Uº©ZÎöëÆHšÔ£d?żæ#›‹]÷¶…hˆ Ìr—2ÈæWòJ)Òò‡L¯×Úö8­ $S +{S±ð?µ°ZVPµƒ{ºßø×þ±ß>( pû´ôˆ*•‚&´Þ6ÛA¤TA®PFQŽò÷|µ_†òœíí:“b±Èëz0ùú[Û’ÇQÓ)—/bZê(ãcƒ˜Ÿ_öAƒ¢�ó Ë\d[‹h­a[ªµ˜@ÄPbä“íH°&ÒדyØkçb?3·Œjµ†=û€ë‹[di•*ÜáB?Üs7ý:6Á‚2$ ñ|½Ä:û&�Ûa®ËqÖEºº2èíî å÷!�€œ/ ûåýº3½ð€¿ÇBx?sôÇdü.ƒÊ•:Ó[Léš^œšÙ|h ¦Þ‚f6 £ñ€‡€ÃîyÓ»S�Ö€ŒúÛ@òt™…�™ÙÓë.,ç¿�+VOù!swjê_ük¿1"J»Gü/k¹\k±EVV -þþv˜<6ÇçÞ¸9ƒ\¾Œµµ"VW‹,€K-€XRV7°«#—[“³ÊB¼)¸n•™ÄI‘LÆ14ÐÅÔ• wÉù|ñžÿŽÅZã¹ ó‰)<2Ì2 .¨ú¡ Ò¡÷N2�vh¼÷#Q›RÀÌBŽ€ó0ÆØÍ Çô'DBL¹°þ"‹�ϰ›¶MÁÀ?©¾RáõJtÆèäaM"¦Ãï²B?––ת‘’µ*£2ü­ÊÁ ï­z—)¤³¨ÎFÈ€ë§ã­¤ãQÄÂ!ÞŽø?s��ü®ÓnJ|ÜÖugnyo¡\ý‡µÖuHUý¥O�îîHòç<Ì-¬baq ù\IŽ«\Àgf—·íK¨,�Q®Ôd!Èñ±§fù7çæV\JÐZ¼ªÕ:#"ÓÚÑŠLÊå2òù¼ü¬òˆ`b¡§'‹=£H¥df•„±=ý¦œd*j»mIÀZ@�ÇS‹dŽ9–ÂþÑ®�GŒFh>T†I& Ia±Wœ´X�9¤(£Åв¶yå%SK„rñ�iÖIZ›0ÛRLoÉ^3;¤¹Q(bzu˜–óŸ+Ÿ÷b´(SUá‹‹ú_FñÚåèá��¿/`ÒÒ+kjí5 $eF^‡÷ bÏP7jMlD7f£Úè@øZo˜ (¥@ð09øMGM¯‰-›bO ë�b3³$_,ãØ¡1ìîà çŽ`ÏH2&#á€ÓŠ>?æÖzïÍ{i�}Ø¥õ§mm ˆFü€xdïpöÌÁAaýÆîµ`СÁZ†„­JGG‚?“Ø–û $|·¥æ¢P× 屯¥BP“ÔSn‹‹9ÜPv¯‘uS;™˜Z�å¶–¨‡ÑÊ�[S`ý…ÚZù‡Ms0¿Æ¿ïl\»1Cݱ[wæ6\ÔçטßîëÄ`o–=4F ñîŠóÚ-¦Q’iº2é+éAX×K�-Y©VÆ•¹ë˜Z�A¾R@Í¿¯�z³ŽJ½ŠBÍÃìj ×äùL/¬`yí£u ÙÙ%F—Û¡ŽÐÛ•a’HDpêØ>‚‹?’&5#‚ˆ§u«å)0$ÂAF“.Ϥ·\´­ÝÙ¾ )­çÄQ¼OØ"Ñûú÷w¥ã`MC©-WÐb1…òwz,|£[1{{»põê­-çÛÅ™j3)1 “?gêHŽÊ¿«\k"�“Ê»ºšgZÅÌá¿…¸‡1U´Sõ‚;wæÑ'à äØÕ™Úl›sJº² FR¦ÿƒ~·Ýׇñ¡N|xm–5¹vÒŒµ2rûÖ£§°v¥"P­¯ WÉV”Åω²Bø�ßþÇ„ðWþÚ_À˜ó¨£=Ø'ÑÔH_'ÓY²Ø³æ³Öß›å{è²sÝÃÁ}ÃülÍÌ-Ák4¹ùéþMu³ ð1Ú6sÛÍâÍî~CLP�Y`5À>‘̱dFIPk}Ì(v¿‡¶µ#’¤ûŽ‹ÿ’Ï™:°§¿w +zØšt›³f!½%\¤nB*n8 ¨˜Ü÷¦‹ŒCYÎüئ…y½„†xÓܧ1=9M-­÷?¼† ï_C¡Xj¥Ï–WÖîx!;e…B s +ô¬ÅF&˜8z7pÉ–Ë—*Ü�nÐõÏ¡X' °A¡¥nkd6X¤Ekháá¤Zo Tð$ª«ð¶S]<‹„£Ø»w/}lÏX‹9uéÖ~rQ†ŒÝ¼�É™EŽÇ½|õ6™€Û 9Bfß`kHÃÝ8}|?^8{{Ç0ÐÓÁ:ÉCåå§V…7‹ÛÅNέà–\ïí¹Üš�ÇÕ©iÌ.³9�ÑZsmÜ,ù/l4·>%žDÛÚ@²C 2$þ9&IihŠw§²›Ø¡µ¢µµÓ*b’ÎlÒH–°0ŽMGÅf³솕JeÜ™˜“El^Òa3Œ`fç–¸ &Iì ±Vrèà›ô6b” ögIaõ�”_Ågܘ\üÈ‚£}ˆ m™"þúÙ"æ÷�´ËÖIåjžè�ã"þÛÿö¿Áÿð?þOøÉOz—,½‹PÔÃw®áÇo]Ä�òž¼³ØFc$2<Ѓ#Fñ¢ÔK~áSϑ٠H¨4ú¿„@×^üä½ëøÖÏ.Ò¿ùÓK˜ÏÍ£aç�@ ùæ"æó‹¨6jxLÆh´O"Ød,¢¢¡€mÒZûÅ�ïÄ:Ô¶6�„M:ë”x·ŸSMÇ#ݱpÐÚ,ÕÒfS¢Xn6<(¨¨@o¾hØ‘‰ãØáa¸� vÊŲ¿¸²³|v~‰’ñF"ZÓï62ƒv�7ÊÜ}>Ï~—�vºë‹ê¬UëXlLϯQ:þîF½ÞÎ:Ó1¦ò< ¦|(·p¸ÐŠ=J­€i¯©Q¯sàÒG€ï{ßù{ø/þËÿjƒ&AMfY( L®LcfiUNo1*ÙN3Ó2YŒß¿w/œ?FÝ´{GXuÜœ™Å§~ýOàW~ý7ðçþÜŸƒŒ¢\k T%ÂÌx·xýžö°\ZÆíå;XòG*7ÜD±>T-ACïJÁ=FIÛ'DM�$$~‚…÷¶m³µ�¤Ót±'‚®³Ö™ŠíÉ$£¡h8°E6’ X€É–R¯°�Ø¡½ùÖ&ö�÷âè±½Ø-«Vj¸!róæ4#É×›œ.ç­E›òÞµjuGé>5ùÞ»NZý,8©{�ÖjÙnÕ‘;3+w7¸QÐñ×>w¢ñÙ—¢§#A6—0µäa‘VA=â"f„=årcƒ÷Ô¹×®¿•Ò †€3·ð�¾ƒ™ÙEì”Í ø{5ç—ªUܘ›ÂJuÑ�Pù¼&&&©ßös3ë#îµÃ¦D&ó¸6oߺ„+370³6CPÙ%‰Êü‡‚nÀ¢xS<.>Œ¶m³µ�¤‹‘ˆeUeVô!iØ g1~a¶b ²àMN-0ï,SRm qËý'� #‰`ŒÑ”ÌôànX(¨†fÌ…Ž�rŒr©Â/ëää ¡0vÊÈÀ 8|gæVÎ÷ªÄrQtÂQˆÕÂJáî(†»Ô3G†mé)iúce}ïHÅ8'>à8�\(–"/wîåbe­ŠfSc½eÒá ÿF$ìþ<š¸<}…r;hlz|þ¹cw« 355¹: mW)ëþÏþsÿ þÒ_þ+øÇþ‰‚’;� 7¶•µ Vsjˆõ$ºáØÎ®I¬P¡Ù¶J©Y“£-‹GyÞ¶6�lc}¤[|Rv¦YK©þÙ!.6›4.n·}®r•tÕžî4®^Ÿâ ñ®®Å"!{DZ[¦5•‡™þX\Zõ©ÍLuÁGR…E°’z^bœ‘¾ƒFù|‘ü …YRGìÞÿè5sü¯©ÛÄáB�ôÖFÓ©�uâàLªÒÅnÿ¶E"„�•ŠûQNh:Èj)øwgƒwQ¨•,èÜmZÕñ÷¾þ]‚çN¦�^8wòþ¢ÑH D¦%zhê†é©±P*Ìã�¾òUaÒMÜÌÖ½¢,‚èòj…Ál<EL\í¾ÄŠd-­ECï� qж6�lãõä‚®“³,ëye¦èId²¥_­5Ó0œ˜p$%±âï¢ÉåO%£xT‹GØ%35‹ xåÚÒGgf9­qNEdó™gŸššGwg†},; lÇç6ê ¦ Ý #FEåWéÞ&++�IDR4óÙ7жÔKœmî�`&�êµ ® Zkþ\‡ýÁ}Ytwù»™”‹³'º»ÇûûÖïa~i;h|/G†$…zt +…î,O¢R¯ÜE#Ža#ëísÌFV,ÕY+ìÎÆÐKAíŒ(ÕúNú©­�a@ ffmÛFkÉÅ®Lüe¥‘ô†Ïò`Zb«f(³äÓ ˜ÇH$ˆµµÒ£MYcÙ%3}1UND¼ykF"«;~tÂÅü§o|eØT×%2±l‹µœ�– ¹=19PF? ¯/Må`�ŽtãÃÝ~Ê�'Š•\†Óû‘ÔÇ'žßk÷uÅ·}‡ß‘Œ‚ò°P-S3ÔÖ�I8ìàäѼü|ŸHó÷!“ݧîSÆí©Yì†%1¡ðN¢¶ã* lÄ©à$Ê�ÙkM¬åªèèãðèŽï"¡aŒ�ûœÉ"n[Ö¤�ê& ‰§Ä#hÛ6YH¢RWÿm×q^sЙ‰³àúˆ‹pKþ (ÊT 6é¯-÷B!ÀÌÔÞ “ë×>�yyy eùý1dudÓlF“ˆ„ m\ØwØØ­óöœ¡WëõéÉæz”žÎ4Ì@VÀr‘¯*@Ù€%®,СxL%"øåÏÛ¡}h½ÙÛE!ïáæ�\«fb"R~mK=˜&¦ç ±£ÆbúÛ¯£È¹5zƒý=!¦áЙð“.=Ì/–ø>œÙ¿Ÿ<{˜�)i½�Š@Ä[røð´fͧX©5gV®h�†)¸{â±6¸ $ÛY9. Ïoض‰GÃl`JlÓ<„µ&˜ˆwѲ›ç"¼™�íús‰œ¸#ìïë|¨‚ð#â i�"•ÒˆÇ#žDsRì^$�`qq™ ÏÜü2{N„6¼Q�Ãá _Ëu�ßñõ`¢TKkŠzRókMh;XAÀ·â |·1<Ð�çO�ìHwõÁÑ>ìéÏ"‰ TòûrŠ\`¥n²~6ŠÑkkˆûB•5,¯Tø;æç(”JØiiÞ;SóøÖÞE¡èë®U±ºV% ¬O?Ú�Wžï—h*+ªÂÝxñ|?â±À]ôgÓsþHè:>÷üqüÞ/¾ˆFCËs¨íT­‡=FÒtIÕäá¡^‚ˆ óýöÄÂÚ-9ZVÅëâN;µµ]Ö’ e©ótl‹L›þ®4w’ÛfZSïjbr�*¶7o϶ւF³Ù ×uï‘Êr‰ˆ€®Üœ‡íØ\à¥�Œ`’É$! ý½vhëÓb›�d´öå‡mé�iúä�wÞ¿Š7Þ¾L6Úå+wDk’±.ɹŸÛ£„ÿïÜðÿ.&�9âÖÝÏ¿ÁaW9(Ë�¤€@7F@¡[¼xf/bÑà]³:`\a‹Æ(÷О>œÜ?Œ¡îN3c¦&€\ÄÔl‹ËÈêj Ke™²DYÔóÒó&)±"#˜P ´c‘èòjWnLáÿú{ßÁ„€I"”€Wwf“3yËZéÀpÈA_OŒ‘Èݵ‘R©ŽÉéÿíË'÷áSÏdOÍÔÜI¥J}ÇÈ!Á@C"ö ûiMj‰ëé>uh¸+4M$’×íbûvYHFÓ±ðË:éÀ‡{2¤n·™y!â9,.®ù»xö^”9–×ýÍØGkù;«ù–æÙ…Ü‘N@::RŒNîån,hÔ^­@A)‹�g‡�·…(@UÊ·X,ayi W„üÆ[—´Ì0aº«Ùôäy­°ñn·lFþö[ïÝf#ä:S�VÚßÏ¡PªvˆÎÔ–ì°xˆÇd2ŽÏ¾v¬éºŽ¶m3+žõ­0™?�`TÎ&cííBOªƒSÿ,j±5¥”ŠBnÈg 2§ÏÙ Úb=hiÅo -îT Š’2_ùΛ‘ïÿôýj£®‘ŽGÑבE<G¹¨\ RŸÊû‘‹çÕj“ä�:#ªV K@±ì Ad´¯¿øêqìîÂâJ‘*ÎÒñÏ(lŒß)ΨïÉ"›IJä4Îï†È„Ï�ØË$ã–IkUM�¤Ž¶µE·Ð^”´}¶e©Áî :Rq(¥vfPQÃOY(Re%Ban<‘ˆR’$ ÂZ A¹]©T¸ˆI¤Bîýå󘙙G:óS5¬Gôôt`y9çG*¤Å9<Îó…….îžÖü}™õÎ/ØøØåâ%Àëiˆo5S"ÿVá±£3N† …Ž<(µÄ]ð¡`í;oøÚR|…¦i „f4;xu?<†[“«úÚÍi%éQF¾‰®Î4îL˜ðÜqhmcnfž)’to–‘�ë8’îA_›MÏF$]Ù4@ùB1G,5>4æOOäbÖÝ�!`iig«”K˜˜YÃøpö¾ªý=›ž[Â7%ÿÿÙWO“Þ ¯Æe„ ¢lz(´î”S]]i¿W†4nÏÓfz ~äIò>Ò'�}{‡ý0©û…Ï<�ŽLŠl#¥€WÎçfàÛßWŽM¹?°ÝŸI~Æ.]Ÿäˆ�ËWïXþu õvà•玉Å«ÏÃèP7þöïOvžb˜Á -X`ŸI½Þ”[G¥F]C]Ýø­/œÆ©CCêIãòÍIß­I¤R„4‡î„�c«òèá1~Ç’‰(7[F¦©T¢éi Æbáev ÓLÁ½ "m yÄ"û‰ c÷HhžÍ%2}v£P,w†ø{ÇzÙÌ'sB¨¾0ùo?ñ<Î…àïÛ–_QîõSdŒ&Ò©8‹ÈòA:cÁYvÌ\¤ä‹ÄŸç‹%¿'€)µ’öß3€oÉŽ|t¤™L‚Mg’�¹½ýÎUìô‰úž;3 úª€ç�‰9ü�¯ü¿õåW9rÚ3KªY�,öʕ͂¿ËeS¦ëÚŒ ElÛz6bÑ�¿·'Ë ¯§ëŒ?ûµ/¾êKÜ“È�NÄ¡··›“%ç—Öd±_´&ÀjhË J)Ö _ù”O©Ö’ÎT¹|‘ìÃR¹"`RÆJ®ÈôÖp_z’øÂ+‡)ÑŸ+T°¸ZÂZ¾"ÑLk¹2SaÛaÐùþi$�¦×JAÊw„0( °1¿¼fËÕ°Ö:`ÎŒ‹pu†{ÛÚ@ß+²I­µÝé/ÌŽ³[M~¤ÊÖ›ªqéê”ãG&Šbw>€4¹Xʤ�4ﯖªþbÃâr���1É�· ý2HὕUŽýe­ä�€‡¤¿,§Nì7,§Š¯©Ä¨äÃË·îbv©–†Öú/ë“n«+«œŒØÙÛh±aºpßž>‚pYÒ[µjJ�ç�¾Ã^? Ûæì f4Éà „"Ø3Ò Fr÷àƒ‹704ØË:Pͱ ôZ{â›§§ÊbÇ÷6# Q(ñÈ¡=èÆFÖ×ÝÁÓêZ^R^ÝPÛßÑÎFS*¦‡¤ܯq–�VF'�íWþÆ¥œ_„íÕ|¥ÒQ$b!mØ{JäÁtbA޾ªÀÊZ™ŠÌÛ¾¾ ôuùûü¬ï’þ¦ ôöt V«ah`Zƒ×¬ qíÖ<šžçNujfyÒR*ài]kE"��Új[HúÄ3Òx˜©5šÌY[–ÚÕF¿?¼î„ÂaŒŽöc-_C2a›Å,òºLo0¯Nà¨ÕÜÞ«!‘Åôt²5K^Ò^ÔÊêìLsç¶´´†ŽL}½�> ±;ܶ-“²�1%çZÜfA-WxÒA…×/$ɽTƒƒˆ»ÂÞég½áÝ‹·ðÓ·/BèÞܽvgSî5Ÿ ˜çöä¸D%+\h5À”¢½ÏX#(³ŸÆó6‘Ö"h1bÌfÓÜIôu2r¼ÇûÊ÷üìñ}ÇdÁÍ|¾XWèfÄüúŒÎ¤ãÊuœõ€Ã1½º‘†ªç�f¹È1ª3¶´Rd¢µÆÜbÞ?'©d;Ì5³ó…` •¼6¬_U@R‰¸€|m-›½$’Ê\Å�;³’šëmT–�Hw¸òïë^£Y0-žoƒH›µõ(ÿ@(à¬\{HkÍF´Ç“ã/ãÎm—Ü€g¾“.k"Út>Gü…PÜbÎ÷A]íJ¡ÕŒeT]©f 3¼éä‰}Tò•"?Ê}ãCÈfSð�i×a�lO¿¡&³ƒ÷?¡F¹–™Ù–þ׋·mXk6õÐ^ UDþ] o^¸„ÅåU¼ùÞ5Ì.¬´À”F¶VCýîå¢%¡è=ÝYÿ± �zSÑ(¯%Û‘’Çí±#ãòZ�Î}Ÿˆ�"×ÌÏÀv'pšçåƒZ,Õö5ý:ÛF¦œ0)Ó|�,›l·uÆS±T£/,,zÓýM8¿¾Åè:*.›+n�d#DJ¯L|dtÝ#Û‰£ûdCÖ‡^9\2â|ùýk“¬^ŸX ÅSiǶÂr} qmÛ¢µ�$*^8:>°Òhz±¦×d¡úq²�¢¤í †`ÛŽF(-ߤµFµÆã,<ò8:Ü×ZœÆÆ‘íLãÜ™ÃL¯H¡’õù7¹..zŒòKüŸc4ækG�^·9t¦®Ü@ deÑ0ô��tÑ…Å%K%)8O´¢ S�—ƒ"ógÏp—D Ýè̦ù: Y�ŸD{üÛ²à=T¿‰Ö¼f׬ì©Ø7âŸC=æ¹æ#ƒ]ŒÊÆGú Á ˆ½h¹qxÚ›;À>'ßFìôÍ&d”ñë'…ã/ú3/_Ž78æ½%àŽïDWW†¯“¼þŒ,%‚âýa³ñp` ‹?—Úÿ�ÔCdspËkEÌ-å‘«Z VÀN¥Óu­õ-³â ñ&Ú¶Ek§¶"âïUë�_’´–­5 çxLÆÝ­˜Iem¿Ÿo!F3ê�:AÂó<òìe·ÆEPG~IEÞ�Løå”ߨE"À^Ç“hZk.22=’#u]Ç2QÁƒçR� ‹ Ì04 f�4ßMÃbc`k…²O€ÐûFûÕ@o4eÀD{ü÷£C]¤’6š8±(º:3x÷½«¼OjQL“I ’¹zëÓ‚|H²Š’±¦,‚.Aç °îlGö³ÏFÄ.UQŽ$"lhši-e;€å’2 ÛÌŒR5X¶%®psrZß3Êâk(ý+c(°ž·ºšÇÈH‹ù•r•@"¯£ ©áðµc_UÐe$mÛ–ÿ8¬5eRq‚bSo\¸æHBqÂiì•çV®kÕÕ™5ÒñpŒ7жvD²Å¿½$•…ç,?”®Ã¢ác0³[¶IÓU꣋äv™øwBÁ CÒ,Þ÷˜�ÞÉãûù÷Ξ>ˆW^:ÁŠì É$’¢)#1‚ΓdRl%(ööf娪°ì”ÍB!?³ *–eH,e¡#��(£k¹’…�ѵ¸’S?}ç �`½].>Þþñ~YÜ’lréówÃ<Ž�öó|p°‡î®ùþ (kÐX÷2é1åHýêI©;ñ;pPÒœö ›Nr6˵[ÓGêÍ*]ÙÚ4£íÚ•( ¼.’né_ñs¾ž–-`E èîdj�ÌBcFÎû…=Ðß…=òÚî•ècÿø0È}#òg3 ?Êf³©ëÚ¼~‰TŒÙîLÏ“Þ7†X2‹cGÉßNÈcîéìïën®lÔhÛÇÖl<^;.¡÷‘®Lâ·ÊÕzŸ=2>ØÍê.›ýû†}fÏi-öûZÇmb謱êpq³›yrOkÿËÌ‚å{\'¸9<Æ]žç‘5$ uÃ̵?–…�”Ô›ó¸¨ÛŽƒÓ'0Ý!Ï…´fA‘8¼N9²‰NR‡Œ¼f±H“³K,bïêÁ`_sê²°qwËÂö]`" DP5‹ŸLn{r”káuIÃ)__*φ~Ó¨òÁɤvd“|Çeê O€qQN%¢$Ð×�!{KÎ7VeŠyvм.ŠÂ£àG#þè„5~®ä93"–÷Ä�àuP5 $S-�Žs¨4¨¼ ¯?Ó†}½Y„Y· Ià¬ýÞ®”ß/eÒ–6ÿ]•ZCyw�/V‰gPGŸûôkÈvdPñX,¨ nÿè§o9F¼ñªøÚÖNmm�­u.èØÏKØÛ­[s¦‡iÏó™ULËä E~q\×_ô¼.‚ËÎdÐ ¨øðF.p 59™Œ’.l) ^¼aðX à5)Ž(?–t–\ �lìtÍŽÔgò86§·Z ,ÑÑÔNX˜=/:P?~û:ž…Oœ=Ä^Šÿû÷Ò¼|cÚ>sl ûÇÖ-¤r4Ú[Á�Åi�š· Å2ë×&üÅ‘ 7¿°Lv‘ë°ÈÏ÷VÒ8$:ôvg4Qðq4Ó�òÚ²>–ŒG7œÇÃt–RM,ð6,ìõ+ÕkØ· Áp”„ íiRqWsyø*Ò‘hˆ4fI)²©VØbŒ�ù7Ý€K �åûEж,(ˆ¹6L#Íß Åfß®l k¥&&–8}ò$�Åç6:<è¿ïV2ï° n·k$íˆd«÷EñS¡ {^k Ø–…h˜�¼>f’Ø ¸o|€�ƒ)#Ñâwª‡CT·%£l§sé&õ@÷4`SQ®TH²ä1ÿvˆ ì8d”=I`ÂÈ$lžÇ=MYª�¬?ŠÙŽÍúQ– ÊT£3y}˜2Ðg4"i+NÝ”´*ÿy:gÊ0.@õ£5 à,Ûl|Gëx÷÷ˆ2/ ËkhhW' õeñÒóg©óÜ …bóoÿý¯üì� Ì‹_ÙlDÒ¶v�dHü°é’B;ËÎtœ ò˜Œ½ssËð¢›wf±°´†t¦·æjèîéÅ+/žçÆg}$+ï½7!²Ä{ší¹ííÔÖVÀë¨x—xL)ÕÉE3à`Oåñ™Æ×¾ù3¡áB±TF¡P’®ê>ŠÜ~,;V¦®¥,¥%£fg—¨Y$ F`‘/;Ï•R>e˜é¹Ý3ÒS©§$µÖ&dÑ2îõò‘}¦´ZŠeûGªŸ;±ÏF¦¡Î�Ü�BéVHJÍ8þÁw>í¨2©Ø}P“2SrmZ„)�F£ÉŸ5=�)-5ñ6?sO�­WIß­5Òé£Ñãëà55�EHºa¥Ö§ À¶h3}�¬MÝžš';ëàᣘ]FÇâ’¦€ m˜�[XŒ¶VX¼þñ-¸·Ma÷-,þ§Ä÷JôqJ8ò/Ä"A%½$88Ú‹ÇMÃ4ýü²‰ä5ÓÕz�)‚Çöàqšä�=6ûüü’õ3‘Œÿâ/¼„·Þ½‚üð¡jöcaa™é®]Þù’&*,¸…°Àí¦&r¯Ú ðÝôç´šž æ³·&«X++,/¯b¸7‰/½~‘ Ám{,ToÒ…e.Žÿž“¹51½ˆ÷Dêgpdú†÷¡·§›ʽ ?üÉ›¥/üÊ?ò¯åòaÅÿwñ›hÛCZ;µ•2ÓÐ:%ï×Ð*“ˆRNüIX<Ï£\÷¬{ç–YˆÔžfƒ•¡¸®çä3ØÅT—%]Ä~šÆKgT%έx”B)Ss�ƒ6-rø¬�³ˆ»{Ó³q?ð1ni9ún?‘TaOE_&$Åö!ÒFçW«ø_ÿÞÏð�Ÿ^ÆOd¨×õÛ3,°{»ÁZkG¯ßž•ù3×ñ£·.ùïÁþÝKwPEùš_× ·X‰÷{ QþBE@Ä5]íwėѶMZH\ñªmY=>µ±Ç§`FBxRLæ’0DŸ”P�À nIîW)eguKŠ^|—#&e ô4Ï�>¬ýü¡ƒ£,ŒF#lÀÃîb1�”i"Û¶¨ lÛöCÕj Æî“š!•øä‘œ‹ãÔÑq>Ç‹B þÊ�®àâ­%|ýûïrÔ·~x�“wOÚ¶²VÀ~ö!¾öý·}H¿–s¨!‚Ç_ÀùçŸÇ¹ÓÇ‘J%ï«ñÖ;ïã«ßøîšIg-êo mk×H6‘Jˈ‡mKE¡�Ȧb’®È²WâI²f“ráD0D‡Öš 2¡p™Áð`Ïc(pÛ®/Eñλ—Yl—F<ö`ØvK »dìž&Ã Š³W\Ó;�iÕ˜ÙdœÞŸ…eŸÁw~ðYl 9O Æûqûæ\¨ÑuJúmÆGûx»`¸6ß_¤µf�‚j±¡žäTÖíÉy|í{o£P*“X‘/–©?wæ¹—qäèqFÅ—«‰Bð-™·rsÂ|nˆßlw¶·#’ÍÒ~³â®cÛÑ€ëºÒ¤ý>�ÆÝöÔôÕze’"oÜšöCsÍÌÌ"i£�Á¼‰�½U*W¤qñúØUîíV-ɈÍzÔ»™ TŒØäNízj8u ŸùÄ Èv¤Éö †"²~™¾ý¨ �¼}M¢“w¹Ðí†Ú±¤Ö<éÉÐM#ò0ÈS-•±81�©+7¼‰Kצ*…b O¦‘ŒðþåÛø›øF€…ùå"ºúFð‰Ï~'Nž&ˆ<¬åóELMÏ6¦ffWM:ë’øêf ímkÉŠ¸ œát<¢ú:Sl¶zRMº¯Ù\uóÖÞ{ÿºŸ¾!3jqy�2ßJY��Þ)ÚVf}޻ľ¾.ì’1•&`B ­mÙ5ÛÞé^ Û+àðh¯¿p©dTÏÍN7ƒ®’é�'0ЛAYGqc¶‚½}•àßlz;®9é…øÁ÷ß¼|ûÂÍÙ·QXY…wïš–_YÃ�¯`êòu,MÎXËÓ³½ów¦KOj$rõÆÓ†ÕpèØi í=†W?ñ9|îó¿ ÄŠ½|ÏÖjõ:¤“]¢›™ù;ÓuññM¡~ÛÚ©-O¼C)ÄeþÈÈ‘±~$ca<ÙF0aªäƒK7päÐf¤¯58A©–$ùcˆJl¦ êµ†ß ÏzÎn™2r'Ý]iÄbaR€µÆÎËŒx5ØÆ¨UúÔû|h_½ü>z^ÄËgöÁ·®LâÎ’FùÇÂBƒ�Ã]쎗4Ax;H^ºµ‚|~¨YŽ;KS³(åòèD¼Ã�ÎìŸ[�µ:Š«kX�H¤°¼Šuf•óùŽÖgè 1OkÊÔ|óÇáÆ»ñùO�—ˆ×WöMm¹ysiiEnâÆ�ëWE&' @oÛ&­‘tˆt[¾×é°Ù?6Îb‰“åØ,w{bZkÊH¬®¸P=®"¯í¸ìÌ–žÜ.å[¤cšC¡RIŽfÇø®˜W‡£š8<Ö�¤ç'›Š ·<džº×ÎÀ+â…b³yKO/ý¶dýþ7~†mºP¬à�Eÿ ï\R©X(ªÐl4PZË PLam~‰é«Z¥‚J±DàXšš!ˆ”Vs¸Ûäw•¤·ž„„¬,ßòe 3… ö9‹O¾þš¨Rï§Ä‰¼Ï[¼…Åe,ÌÏh]Ëw›âúœxmkG$›ü[/‰Ÿ 8Î�t"¢>n�`K‹+xó�˺6gzK£"ÓKšnj…±Ao·Õb—V‹Hø:a•ÚnÖ�ø\;L‚\\£¶Ý1M0±•…áþ,úºÓdÏ)ËáÆSGÆØYýÓ ×TÉq ¼ êÅy¦#Wsަ=àwsvóÚ7iTaž�_ñŠkùP4Ó½>J!P4ëuÂaØŽÃûêÕ*�¥Q­mXGñML_½‰ñÓÇðŒÏg~q«¹êM`DÒWÉ>éHïÚË×5f÷ÈÜÄ�Û¸sûzCô¼jFå§›ekµ­‘$Å÷‹ÇÂA7V3=·F¬µÕk$o cjv~™âw"òÈÞÇLTÜfÓ¸�¥“1œ?{˜�ˆ»ùzº®MÂ�D%&Q»›šÑ ñ&”xÀ¡NÓ^¾Yn„ãz?û‰©6»\h œêÇàð(:3IŸÚÍþ‡ïþä}Ê®lò=ãûÿ�Ÿ¼gÕëõh*þ(Ûo­€¥™y,MÏbefŽI½R½ïß)狌jv±ˆNP½(â–ã÷¿�¿ûõ7ñ½w§1W Ãs¢fŽŒKi“íÈ”J%Lß¹Œ®tÌ•úZÀ‡â·Ð¶MXH”‘1¥T¿ä©Ãþ޶éi| ��‰““óxëíK˜˜šÃ�‰9ŠÞAkþÌä˜wI¢ÄÅá#¥Ev¯nT5gme ï»Na%�gµ'Î(…÷q‡WE:Åù3'„"<€@0ˆÕŠ�½û2±m —¯K àïâêÍi.¬›‰»&À]ák 6ì±-Åkºã+_ªâÎÜ2>¸5‹ë“óÍr¹ÚØ© �8)âÓò÷¾ý£ ø_þ¿¯ã¿ý?þÿàoàúTv´[€v†¥Æ“L$¶AM» Ý$“�~íÚµÚää„v8*Á½e(¿´mÖVÿ�‰ÿŠøYǶºz;“"ÆaGûöÑgw¿Ï¤ÑðP©T�Hc )©µ:”KU¸î¶Èx¨-ÅB™ê¿—¯ÜÞU)™#‡Ç)�qž=Ÿïî›ZÌ9­I�Ê�2ÓþF†‡àºAæÿ—sU98Ž‘Î÷À­É9Ì-¬2-Ç™–õPŒ¹ùÅ5LÞ™FܯOE7•ú!€¼smRðE¬Êpm B>©Ž«º7ˆí3Ça^½ýÁu²¯þà[o°^´°œ‡§èìÅé³gpîÔqÜ7†žž®í`QDµ"ÕjÕŠþáw¾ª'¦fm(4#áÀ¿þÆ…?ÜlïHÛÚ5’ýFí7-i-WŠÓʽácldsMMÏ“=%ïIuqPU"ǤFÂ;­ÊËþYÄ™†(îbKB*cW;Œ4üc3íñZQŠ×;5+P^¡PšÅâ¡Á>i„»�•²‹ñþ1öâËNò“‘° Ž&˜<¨.uöø^|øÖûÍkS vØ—¸OÄð0XRªÔðæ• ÿPwÝé8þŒ”l&(…ìðC¦:l$�LÎ,J#á[< ��Ýé°è ¥1¶ï¾ô…O£3›Y/ó¿MJÚUÔ*9’ægWVW—2žÖ~m­,c.²«½míÔÖ&£žCFí×µ-+æ/´õ†ÇöãnÔßÒsó˨Ö(«v-k +PjæV„‚ŒJbÑ›wK“L ²Øcž.¨™F2 B'˜�NU@I{Ö“Î�:†Ñ‘!XÑd{FðIg|xß0Ó‚?|óC¼ùÞu¾§zÍ»;ÓxùµsÕb¹ï]¸ÑXʪ¨-i,˦âµ_c †ƒè´l×yDáûÂþ™KRûøÿ~ÿ{�ü ×nÍ ÑâÉ,FÆqöÜ9|ê/Kcç+"­´ä¶€SY¼fÕŠ«3zjòVúÚ­Ù¦=úཥ&жMZHB`DBs›ZG �€k3µðëï_¼�·.\÷éÀôë7¦|í'#YÂ�ê?ßý¬ågÛñ½¥LŠ 'â�FîZôv À@*hÝȳ?þMÁ„ ÂóFXæg ÞgºïùT¬¡ä€PˆSòæ�9ü_÷Ûøð}¦ÁîcL…=wöpä—~åS‘=Gö{ïÏ®5ÖîC+®Ö�è7§—P,WÑ—M “ˆép,†¾½{Ϥ)ÍÙlzd£}ï§° ýùßÀ7%•53¿‚ÎŽ4†GÇÑÑÕ#ZX'ñ‰WžÇg>ù #´í}ß4õ2•ꕼœWѨ•Õäô¢š—kñ)ÚoþËÿÑÿ›GÛ6iíÔV\¼S¼fÛVÐó´£”2”Q O‹ås9|å«?@4å®pff§Oây³©Q,V 8ïºÙd4¦DR ãI?  +E§®kæ”ß4;aœÿ­L$òä¼�ÚÛâN hV™æ‚²?:+Ü Ç K¤â2=ù–¨Ì-®RÝöô±q~Nï—â:z`Ôÿ‰/V%oÓ:jtk¯'æWk+¥jýØ¡ÑhW"ÒŒÆÂZ–9ÉÎÄÒ)6xn•}5/×ûöû×™¾’kçtB)¬sÀW"Õ�!)¢Ÿ?wÚWOæ”ÈŽLzû"c�<šÅeT ‹~}DZ•«uá|‚Âå›37Ѷ-X;"6Ô_˱­TÀ±9ÂSíÊœìÝŸû^Èçñ}j=U°¸¼êG \ä/]½Íѧtayù‹ÅÓ·a^5'ÝE£‹Ý0KYì#qlë‰ e¨ÁÆë@£(^î5 Ñ�ÁI�áåW^¥ÂpÀuqåæ¾÷“÷9DìA@žJDñÊù£xñ¥SÑþ½{T¼#ƒp<¦ƒÑˆçƒD¦¯ƒû÷8¿úÇ>üò¯|Z?÷ɼ“/œr»G‡T<“Þj:’4æ7Þ½Š¿ýG?ÂEa Ýœ˜óër•Û•Í`HÒX_úÒ—ñ«¿üE©à¬ôu©¬mW:hÔJ(,ÝB97O0ñšuåGÝ™dL‡‚®0Ñ"A?Ä4wÛÚƒ­6 T¿.þ[–R=R9ՙйcƒÝ”FÙ+G¥žR„¶m@¥%¦f9(K)+ÒÓ•!mxßø ›½Ä´qk‹ôNF"ï¾ßþî[ÆÛé${FüÔz�NÅð$�à Р.‘>À â~V\žÄw¿ýM²¹,Káàø^…hº/½þY ö÷� ñ‡ßzƒ,¨ÍtÀkÖkÀ¦ÇGÄB²°~úîU6þø­Ë*54ÐÅÅù¨4£f°jÚEªï†÷죬»©�ìx*×kÖP+¯¡VZc:KîûH4rI^ƒ«ÂV³mk^ƒÖDÛÚ@²I¶V@žOû�3µcSvÜŸŒø,X£^ÇZ® i§]„ÃA”ŠeŽÆ•ú ‹¢ÒÄø¨ *‚óêZ j7$ìIp‡=3O´±NâµXp÷j¼�fŽ�K¦³CãGøÉ”Fa@½+é®yhýð5¤X,Ä¿Ev£¾kÒìú�¿‡w¤¡ðÖÄûOŽîáXå«·fd‘žF¾A¦w�ݽ»º9Óº‰f­ŒzµÈB»ö“–r˜š]ÒÓóËìfߌ¾VÛÚ@Ò'~†@¢T8à:áH(F%â~ŠëÙ0¦¹¨ÏtC¾ø•r•ô`QæñÚõ @¯Ëo1"aoCWf'‹ídjQå7¦Ú°+Ÿ|óLѽ ‚ Y\e­x—�Àrÿ�CÛ{€ý'¾ýèÍ‹¸xõ*d!5™â»_) óßJÿă/ð.“ñ¶ïk?Æ›ï_£$‹¤°^�tÛßüÊ�1¹X…Št¡»§‡ì%#kw?×u6 C‹ ¢µþH4å�\ö7L�Û“ €åÍD$mk×H æñ=[�C€Äæe‘ƒï8ž%«–ý�[U/-ç Bù�ƒ.ûdœ/EÇ&C(qÚl ­QoÈb2"2)·Z;`¼F× û…]Öw,ËbMà‰7­ÙSBÀ®­‘ÌvdtY6²H$‚ç^|?ûÑ÷P/­Ha{ßÿÙ‡ì9éëÎ ‹àüɈF‚÷ì5ɤâŒJ6±¡ãç@hÈ–” ŒìGªÕ[ÐÙÙ‰¾þTUû„ÌqìÈA ô÷È5D°Û¦”í÷‰À²Ü{m(Z}TBÖò,�ͤpÛÖe¾ A×¶ýÅ1pYd—sŸ-cÁT]½>ìíÍ~]Fø¾ Àõ ä"«M!@É'óØc«©éõ :™`k¹eæÒ…jÌß7M�ØN+J©+×Ï&7s­ø8XKà±éÉ¡aØ\,¡nÜËúûz‘üÜ/àò¥‹ˆ$nCÐE«ê2G(Èšú¥Ï>' e´}wT3Л•ãf¦.–X¹pñ¦Ÿâí†tu÷âÀ­h {ö £«³#ƒìKzLz5ÏÚ“m)ÊЈՅ]f¨‰ж6�Ø‘'lD‡ç¡œ_€ö¬”Üß“ÁèP7±pG&Öf“BÛ`툤.Þ!‹ÝHÀµU£é!ðA„¬-´íç»T³iùú[\°/Ýst¤RX‚0ûPÍ&S Lq?7|ˆ¬˜r• Û¶Yo@anz~aÛaáP�£„MjÝ‘�µð¦RâiÁA½•~z:•Äç>ó*öïÛÃy'Zk ö÷òõ’÷ŒïÃÜü"»Í“‰8kA9#ûM˜z¼Ï¶mî'1ŒfJ J¡Ñ¨¶zGd±Hg��áÊ�iWF3Gç—‹a•vz« $ËØòg�dƒ®ëË$@üs)4£m¡ðr!éîΈ´ÊMæÒí!Sjrj�ëúz² iͨ`ý‚&ÃÅge5O¶ÖÁ#ÛYpgG¾D’,´{þßxûñËÇo}ø±^E{�Ô  ‡…~½�ˆԼ pÞ97p?RãàMè�KT×Ò «–VѬWXdH£°gWG½�éô‡W§†Û©­6�<ŒÝ–HädZd0�â‡Iò¥,Ô¶mcVÔâRNkO+(Öl:ÁÎàÙ¹EN\¬Õêm€d½ÉmvÍONÏ£¯7‹ƒûGY¸ß&c#ev Õ’U×f^üÇÛŒ„Š!�lŒCÚÜTàxÐ�ñýºW�œ7¡>VéÀF½â÷ŽPW«Ù|X åŸý6©D$f²¯‰ÿƒvDÒ®‘<ÈJÙT,)sÙZ;/K\¡m÷2‘OQð|VÏÜÜ’ø2ªÕ:wþsó+X\\ÅÌìÓXw½¦ÇÁVûý‰�&S\�j¬‡Ø6â¤sža¾…Mãä“h›N™Ø!@YLtëHk”Úís&}Z+çÁì¥eÎcÈ(ŽÒö·§Ø'•LDm�œ@Û`íbûûƒÝa.6š;3ö�„C÷/·Mû²àJT‚µŸ¦ºy{ZDï°^k�÷?¼ÎãFr>øìäÄÄ啵íhHd"r(Ìã»®Cj±€#˧ɺÁxÎ.Ѻ5ÿ½mŒF(Ê(t_£­Uxؘ�S߸p�½1µZc@ÙÛÏ‘”Ó¶{XHš]©ëA©�Àìf‚®�H8Àîö¶=È´_¿.Òð ð¡pÐgq±¯dva™€q7å2òA:äw)]{bÛ`¤ª ÕÆïh'¨Ä¥ðþtÍÚ¡îVU¼fE‹×A@¡©vÚÕ×@ÈÔò2­¥LSf½Þd*TêG5Sh_2êàÇÅ;Û/rH6´Ccý£žÖQ Ýêζ,ÅcÛ*•àG%¶Û  Pàѱ,_Â� ¼çytc”u×ZóçóÛu<ŽíéÇà`7ë0,°?5¦�f•Ì-ë«`OI£bÒZíϫ׬3iÔJ¾óöf¬§+…Á¾,VV‹:“N5z8 ¾lÀ$-n£mYH:’±ŒkÛn±\#uÕ±m4ž-´íáÁÄOOݾ3Ë©‡2 ‹ŠÁâdrùÒ*ž§�T¹‚ö[ï\æím¨�0Ò‘"?�ñX”´Õ§1mÃ4V³Ìs 9Ös,àÙNò{]šÉÔ’(D¼±Y !»ï̱1ªGgÒ‰_þÕ/¼ôR6 x_üñ�ÛãÛÖÛ²«õ†’œ¨ÏÖ2  µ-Ìfh[¹\ÁZ®H@–ú VVòbuåÚ;¥ âZƒ€"JÂ<>¢zëX)v±‹D¡ÀSº®jq�­žÓ\–K`y†�}#õrŽi-¼MŽ‚Á gÈú•S~w»ïg^:ô˜ÈË[n‹WѶ ¬ $V©R}™s�X­?íˆdËÊÁl:ÌËœ¶(àÌ–ÔRÈâÖ‹âkkEÎ:© à(¥…©EݨŽt}½�Üì~“ÜîÀjMT„6©-ç™gky^�J�su´çmú߇Ba6\Þ7¬úº; «¿'ûé¦×|åžÅö¶µ�¤+K,®Ø–"p€°!K:\™âjÛÖûLrkyÖK,›2%l@œžYÀoøç”Xîcô²ÅAYì¶ŽFÃ�.d¿xÏúð´‰6@Ò�Ä ˜¨ÄƳjºÙàëR¯ä¶<Ÿß²¸)A0â¤ÉSGÆ8è*p»¾øéóŸ²-ëÀ†ýlmk7$FB�ñ¦ç�{f!+WëÜå&¢¡'±PÛš�Þâç˜Ûùù“aLeýðÇøš^¼t“é.¡å²–,,®�¢+u•-Š3Ö9 8�’„…Á�®g€¶­ÍQv°¡Tž=QQ44êe?¥Å¨d F–ŸXKJg°¯“ŸU m¥±Ïœ<²çö®M¸ƒ¶­³vD¢ÂÁÀ€mYñFÓ[7ƒÄeŽýI ß[‹¤9šë6w=‘uÅôÌÖäX,W*–R’êZäLï­.üJ�3Mº²itŠ e“̱gÊК<>sÖ¬•Y©– M´¶ÅMÚúî~®/�=Œ¾®ä‹•ÐÞÑþߌ†¯Ý=ìªmm ±eˆÍ‰HØ íƒI4@*Aðñ¤F>:¹X/�ǬF±RÅr®ˆ…•¼¤RqÙÌæ†ùý‡ùÒì¾i‚†à)U~ãæ4Ù\²øo1Šâ.’Ï7“I Û‘dɳFÛ&€�úËÆÄg)iQ~…­Åé‡ÍZé‘Ò±-¹±±‘;4óÝÌž9¾ï‹Ž®Ï�´­�Ú Êâ{¼éixžf7» R±Èn‰5rÑãœléT2+0¦[Cwø{XZ+by�¬(‚ŸëØ¥pv‡Ëmþ®a ñy\N~CH|S��ñ¸ŠñÓZ­TœhZo.Å't_ÖH$ ¡ŠpÀu™šx¶Ì_@+`ÔŽñLeá•F³Q[,²×6…8ŽcT² Ã˜mÙ80>ˆáþNüðÍKªXª¾"€rÉót€ï‹—жgH,­‘-”*FB:H IìN¡� ’IÇ1¿°º~–Fkì©§5 ÈJ)èø3[ΎͰ»P¢òŪ¶ ®‚’ñ ºš/¡T©{:p­f£aÁó�Œ…�„�‘çH i4¸ðJ¸ÎÛ�!MGßB:‹<ÿb‘½(lnäÙ3mØZ °œg¦à®©Tl¡V^C½Z k¯±ÙÏ“ù}…p8L PùÈgm '‹O¼x.Ý’ŸÛ�#]goÜ™‹ÈŠÿ}ñ<Úöl‰Rª·Zo±,Î 8⮳« )eÕMÔ ¡ R3 �ÙnÞ™G._F®PB8B]ƒÃ}èéL’®ÜÛ•ªX`é]%À£ Å2eû@tcbžÏk-WB¥RÇ�É\»3G`’ˆ†ÇL2Š¡ž Òò7Ÿp32ô”„§NW(dŸ ò,£‘gG"Ek2ý¨îÛ ÙR‘])Å šé!áq£tó gabf?{÷ŠêÎ&Ï mýÖÜbÎ�iIHâ¥J-í§Z\# 8Ø%k�5�FƒþhZöBܺ=‹l6‰½ãÃMÛ†�îH"‹ðK“N'šñHÀ¶l -s�к:ЦC)¼æùG@ËQÂr,®äñ;‡Û(WoÎ"¿–o¥¸4À¨…ÑÉÜ+ i,3é¤nÄ^CÛVxÍô’#í�zƒŒ-°!Që�Œ@XO$Âlx²-‹ùý´HLŽ~½äóŸ}=)P;*¸ý2ö á.ú,>ùâ¼þÜ!VJo‘Xk•Þp¹,‘Ñ ‰ZfV!QۓЛbºØ-ø©;* è t·Ÿé´<9:Ïœ¶V£š7v�MZ«À. ˆž¤µôÃ0*÷Žöax  „oÇzåÕçŽþÃÌ´í™�HÂçšž¶=Oû µâ,lï([ɶ-þ�ñ±>éìžÅð`7Ȳª{ìïäùn�Op¼xf?ŽÆäì$LÇ%é·º99�ɹTÊ5?ÕÇp¾;“@wG‚,0À]¦„Y»ò4kHH¥â„êi1¸žMS Ùág…òË÷»Z\ñ í¬‹hqÚ–€$àG#ÖC~žIN9w|?fæV|ùçÈþ‘/¼ûáõ R/ù?¬¡mÏ\DÒcYjÌ/8³ 1`Ñy;êJ­oTtYÍý굆ŸÞâ�íH ¯/‹ÃwD6ÙÃ80Ö�Ͻz_þÌY|ñ“§…¥r{÷ô² /C~�¼/ sUŠ÷Só«˜_É�–ZïhÐä®}tve02܇H$ÏPž«F‰À÷gÖ,çmdχU‰T<‹ì[yÎf.=ÓY¡ ì›ø· ˜ã©£cdV6=oàµç�¿j)õY!´í™ŠH”øÁzS÷XŠõôf“èéHn¹`kY¤èš~æ]Ñ4)­ÁE1а ‹…™Ò:qÔ—«Ž0­õ˜Í¤¼ìßÓGÎü+çarf o¿_ûƒ.â–œË^®Ô¡¦ä“ш_ 'uÚu–Û1@Èʪ²–$Ev2µDô‘Ig6öP_<ûlO²ÔZ3ÝmûiŽF}”só”ŠoÔŠ‰šdüù ùµI¦µ¶ô]?qh#⟼}ÅJ'cŸ8¼oðí÷.ßyÀwÅ+hÛ3‘¸–RÏ‹g\Çf$26Ðÿ|‹fØŠ¡/h¤õú·¹sq\Ý])tdèÏ@††ºwD¼Sc¤”JÄpp|¿þ‹/à¯ýé/â5?Bée°’+¢Z¯síZÉ1³´Æ(%gFì>¢Í#^ úz³ ˆô÷w±ùPf±ûs"üB;…G†zY|fM)]7[Àò´ 36ëœ~HoÖ+x#=\Cï…RA ˆD"‘GR�~éÜaI ôªóÜɿԓMðºxôÉe~´ÍÆöYÚqì¿jÛÖ^?¥µGöþîô£2¶¸ðµTgµF*eä!EsÉóç¡§·ƒµ)¶?q2õ†Éçaι¨wfâ¼f¡%S†„ïBÐ@4d±»É ˆÑpð‘Óƒ1CwöA¸§;+RóSZã›LÄüH¤>,"½6ÈžU3Mˆ†½!Àr�°óT¥³”eóØlT©©Õ Š~¨H$—/š£˜ µjñxŒMˆH¶ &}=¤çÛ–5 ò<ûç—’ Xxú»ÞÛ©­!¥Ô\ø2ɬG¤NšÅ—5ƒH$Å:ˆe) t¢#›AWG Oºµ„ëZQSt�È �Œ²@/L7¦ç®^›¢¾W$D*FìÑA„Öh49¤Êó<ÊžHM„×upÿˆ¿xý]«Ø�x–­õyÕ S°1Ýš øt]£F#åš�:6S°°¸ÆM¢ãò³ÌÇËÊH$ÛÑB õ’q~ϧç—GΟ<ðëÓs?ŠÉ÷"àâ³âåg|ª¢2î€Ñ­f¼!®?މ%þÉHÈ•ùvÂ’±�Næø7kJÝÝßà€ ±eq÷~èÀ#”I ¥“<éFiEV~é�´öxrìΦ‘ÍÄ�äKÊ»Tj�VÓ–ë>z/N½î³Ú4jÒ˜9Èëéʦ©H4²") ¥ø‚¶ÁD›s”“‡g"O“UK+ŒFŠk3¨WÖð�Æhùgo^ÁÒJÞן#€,/ç1ÐßËlÁvˆ˜òûžI@j%h4¼˜psbnEk½À�øÌ3�„ÍÑ3ÀqHüUñÏŠÿ‚øñóâ'ŇÅ;ÍаˆxAÜû8ÔH‚âãe«àØ”FÙ:жj Ð`‘=eúB|Ʊ±~‚Ô“d&d†ø[çžç±ø(?—c€GqríÝ€k˜.6‹ñŸ}å8~åsÏ‘Æ, ?ë#×'p{z‰`ZëG®“ŒŽö3½%×âwõC)åIJ¢¡Ú“ê éÆZçŒL¬ÀÓ%̨�Fµ„Òê4ê¥U@ëM)úÎ-¬brr×}zûå LN/éb©ÂñÏÅbË+9<¢±7ëØ�È,õ_zåøoùÕ\³Öü¦ø ¼ýôg�öˆŸNüËâUü_”uäOÆbÑW�=zþ•—žûÍógOþÃçNŸø³§N�ü½T2ñ'•Â_è¿E`ù¤¶ºM‘òë#â��ÖÒ°-61Â2š=Á ËPOOßpÈ}"— ‰4L˜¯È`Ñš¬�¸r¤öë!JÌï…4j­.à½{ð…הܿr}š�±’/¡P®`Hž<ÚRªK›L­½~TR¯£³#Õ”×ÕIײTÐ6š‘Á¡Y®¸hû©êpW–e$P(ß’þy˜p4A£ÞÄÌÜ2JF”µž/™ù85õæ[IâV ÙÛÕ0;.Ù‡Þg¨çsÿÛßúÖU³Þ¼(Þ!þmñž>ë?&~P¼ß¿‰DÓ]]Ù¡h$Ò+ª©žîÎð‰£,˜�w3î>çS/Uú³·§/|pizaq9ãyÞ"€ï‰çŸd w„I¥%§O†’e©-�-GÁkhæ_³Ù?”áp€G‚È“8]QkF¥R‰·å '¨¸®$Š S¯ûÉuÏó ¶í6ZÀ|ÉëXY+à´äˆ3ÉÞ¾p Žcq6ÊÜrJ)Rƒ7ošròù‘�72Ü»"à•¸Wö¡mV«.òѬÀÇY:fý 7 7…çá5ªhŠ+eAC·@EƒÿÀ¨Tؘ’èxrjw7'/ßÂéSùÙ–^’mû~ÁÔ]G»ƒ�ü‚ˆ­þŸ÷«?™�6‹¬-þ5ñÂSTÿ8,þŠ�½²>d$u8>4Ð×åºNØ'È9´Ÿzí%É`ŒpÓ­a˜­nS3³Aù®�*K »Ž{4_(tÿàÇo|ÐlzO,�8â=âuÛ²lÎ躛¢ý®ß YJ1šqpÑ‹F(w‚®Î”V–“aÅë´[Qˆ8o³³—G‡)*m0J15ÿ(æ—Ž÷ATªµ&©¸¯M×Kåš“ˆ†TµÞ@&�X”—û*ÿl—2’gæŒw¡HSÌR®-ÃÕ²m÷ ›5P 1ž›ÝÎǺÐn;!„“=Å; •Â"j¥58nˆ2)Íz™Q‹ÖšÍ¾’¶âqqi�…ö�”õæç–PÚ3°ÝbŸ-Ea)¸‡dÖïÈñ«ðÍ7Þ0 ’¿òès¹¦þñIó¼÷ïÛ;ÔÙ‘>>4ÐêééÄ™“ǰï<0ŽP0¸^X¶uŒÅöàsŸ~ ss‹ÖâÒ²p bŸ|åÅóüö�ÉëÆ­‰ê“X#鯋,¥…r¡�dkS ‰¬š¬"I½°Ç¡«3�Ç "Z ¢�äÜ6�Xl›),sîðgæ¶œ“¹µ±LI @¾ü.g¡Ü7âÏÑS k+k¥Š?’”u�¾l �éØC1M*�óÛ+�&Eè”~mÄjƒÈÃ)ÓÁÙí 5JOÇ|¥à†bFS ˜$:Ç�ìÞ‹XÇ=’êG4=€@(ÙÐøBž¤üNH]¤¸qoÉ"s +:剢’ñ(g¾'¤@zêÈøë/�=h¸!¾ô”LWtÅω¿$’u¡÷¯¿zðù³'μpþdè¹s§ðk¿ô|é ŸÂÉã‡ØnCÉóõ ÇŽħ?ù2ºº:a)Ë–h柒ÿ—íËŸsž4 qÅGÍ.Àª5þtDÉfZƒæØ6ȶÈRbJGz,¨®ûx¬Uø'0US³ð;3…óA‚``Î×5#š£ý@z°<S_£C}Ø3Ôì²CÑhÕÿ‚Î/ç8ëd33B¼¦¦m‹…{¦'gƒ÷FÝu®Úuˆ·jMӜ虣~j(À€bÍ–‹r3€\ÈWlTUR;‰a/Ù{±Lç�?i”cîaÌ"Ìϯ0ßæˆ„™ŽÃû†�I‘á;¼äË&r]üêÇ< ‹¿lRu–„©Ïæ/Þ¿çôk/Ÿ·ñóŸÄïüÆ—qþÌ D£ÕôÉõô¥çÏâ…ó§8ñtie54=;ÿoýê/}áß»ðã?Š=I©­„¡˜9J¡»ÙÔN( 8†v³”_J¡Øš‹wü¥;¤6òÓW ZÃ,޺Ÿc=ÄPyM4å¶ïÚ!ðþMSO¯¿p �€“ÌÊff—÷J…˜ßý~p´�éøC}�44õ´Ò™$&—ò€ºçºE{åÁœ<dõS›Þ2o<Ð,ƒÖ(€ÏY=uÒòŒ Þ|û}HÚƒJM¯ Û¶´V#=ºYÉÕòÃ÷Ý\®ôÀ��°µ˜I(•+¬qZJm'˜�|þä~~÷®Ýž9òÚsG^ùö�ß÷ü¾ømñ>~1©¬~ñŽ#Gޏ¿ðÙ×¾ðÒs§î•úÇèð@kdñf-™ˆã…çNØÿ�ÿçïHëš#rHUÞó€áI‰Hl£�Ó°”´å:w¾›Ý®d׺m!•ˆ²†ð˜ŠèëCE“‚  0Ð]×12ìNë�6M”<ãí-kC/Ÿ;‚?þÇ>uðøÑ±ŸC¡²(GåÎ*µúÏ 1Þmœ!ÝWŽ”BI¦âH$ê u¥ì ⤸€ÒÌŽµuNS­îoŸÚj½ÑÚjT+hMPyÚ�µÖ S(—ËȤSFúzùÔoÝžÀÿßÿ/êüÿ÷€ mSñ]$ITÂþ’¥ÅU>þv/dž{ñÜ©èH%l‘QyåÐxÿ^\ü— ñ'ü1 «» ¥÷˜xòÄñ£�ÿò?û×~ï/þéß9üÙO½ÂZH @†ç–_¯¾žnØ7&ÑɤSIÜ™˜V÷÷¿ögÇz’j$kâǶ\ódYØÝ¤1O�B˜.ìx„9WÛ²v8Z@ah»-A:ÍÏ•X–mÒR’ÖÏÖÊ�i©3ÇöºÿÜ_þ�Óû÷�|ϲmϼ;¾ðã¥;X\-ð‹šJÆ[R2ÆØÅÞÕ™Áðp/º²iôdSøÁQþ¹´„j "Ê2G°\ž¯:�"â4›·i–ût‰6¶„«rh\ž¶ùíJ)Y¬ÆðêËÏᓯ½ˆO¿þ^3ç/>Ï¿ü B©nÜ™[¦:µçé>Þ�úAsu5�ùÅUÞÞfkÍ0ù•Ï¿€ã‡F;¾üÙ¾pæèXÀiñ/‰Á¬ƒO83kÐôÜ÷¤8>ø/ýÓå7?ýêù=ƒý½\·ÁŒ‚Å8܇±Ñ!tt¤07¿–¨îuÁ'¡³½.~Bü|(ètGCÁqAÕÛ™D&ÝRW{&ç‡õÀþ! 2&ÌîbA�ý Œ„ ÓªÕThXZëÀá¯�Z ˜ð¸]_šd<:}lý‰—ÓÒ ¼#ìº%ÚìȤ1¿°ˆ|¾¸rãæ�Ü_{œ‰�¯HD‘r][‘¹´ÙmÛÔEöŒPfÝÝT¤ÝM)se×A…)-À€#�VÁý.Û±ëHÿ™ßùœó'~ç!i.øì¸Å•®MÌáâ�i•2é½–çÔgRìWAGG’2(ÆY|ûÃo½Aö�)px°]]hLܘCâñˆ‰NМ0Á…ˆïDЪÀwÕò§,*¡·Òy–�gÀZÄ’=#CøÌ'_Á?ý�ÿåøÐ@¿7¿’gƒ¬~ × ææ–¤@?ÅsÏó˜aØf$‹kLËÓ:ÓÝ™ù�¾îtÚÌ0ɘ4×Þ~2Ì6ò._4i¸îü«áO<ÿÙO½úÅ×_y^êèá{?¥Û�©²Á�>d3é1ñãâÁÇ�Ú1…¢ âÂ*R†ï½yÄtY¨††&zjO󃲻�…€e$‰˜ô›«Ö¥«L—ºµËÝȺ³)ü†õŸü‡1>:˶}@¡|J¡RÃìrMe!*ÀqöÌ!J‡Ãä™spÐkÏÅM©¯|÷'ïÃS!…@JÁr <*F0‚îÀ.º)Ǩÿ3þ>AÅD)­ºŠI“}ÌÁEó9Ó”e".�gͨm7Ô—øò¯ý¦òiÀSÁÊZ\Êá­w.áëßú ðk¹"»áwÂ~7päÀpâ3¯žþâÁñþ3ŽšE»÷ ¡§ÄEü?gYêøsgOvÿC¿õ«Ÿøô'^:ý…ϼ榒;·qÖf�ëíéÂ'_}©T2 `^£žÇ $–øƒú�–B”©Ø–ÚŠ:-µuæçWIùyêÝ;Kí+Õ¢ôJwºád»—õ©¬]7åˆû çrÒâô¯þyýÜéÔۮ5šX[+R‡¬\«£¡_Ïý{‡Zת ð©—N`ÿX?�dbbp¢Ês;7þóõ å2"áß´ƒ þŽWñV¡�àb€ƒ?WŽIý(*ÎGEÙ¿¨dý¹nàYµS'©Î¾a޵–úÜ}ë%�F3³KX^Z“âZO`ff‘öF³‰í´®Ž”Dìêë‚Ȩ¤>ùÒÉ3¯>wdP)5jÖ§Ãf!·JïïAÅ®@Àýäk/ù�_ûâ)éë=þ4:³;±®�Ý695ƒ÷?¸„é™9¬®å$B\’ãÚ-‰¬ŠW�D{Ù„G‘h$8pI‰†1Ø�!ój ; F%}YRã±Ý«�˜&B<šÈÃ^_ÿx¼ š`jM9atf¢êü)ââ –––™\¿3K0éGgWT÷%çô éï¹%é°KW'0<Ð�dvŠ aüÖîÛF+°MÒ(ùàӢƶFÑZÐøx´]ª[àÂßu>~u”@²–Ä#íŒLÖŠU\½r µj•ºNÑûŒÕÕZs!ËfSÈçK¤K �c ä±¶5ÅÕßÓ�ŽtœM‹ÙLÒv»[ú׮ߞ]°— �€5»ñÆÓûŒ:ï~ñ´R*:440øÙO½~òô‰#cŸzíEëàþVwú#³ïVV×P(–ȶ+•ÊÌîüì­ øûøu¼ûÞEˆL :;:053'·?ü;^º3”é;âk�«�¤Ï YŒH¬0U]WÜÙÒ Ý’B`*f÷R[ŠQ‡ÖÍ[ BX1Ïyòq¥¶‚ÕW s+h×µmʻێ½iÚ-ÀikŒD< wQ-‚ìp8¬ŸQh90­�?k!žg3)üÎ/¿†Ïâ,í¦úêÿÏÞU·‘,ÑÞ˜cHrŽÃÌðCtŒ9üÌÌÌÌÌÌÌtLafpˆÌÌ$³¥ÝÿöÕÔÍ�ÊÉ�eÙ±ë;’v§§ûAÿþ_Û€…Ô²*dr½b¡p]¬dÂMè]ÿ"¥ßÿ韲µ)¦[·«‚¨¿êoê Eÿ›4{ËɃ¤qüq±¯ÒÚ2þÿ&~õŸ>u¾°Åò+U»¼h6=n~Ë–Ì“u›6K[W¯”ÕP¯j ‹ð ‹`{QI•4;ªøÎn¶¹ŠŠ*¥··Ÿ÷<ª„Ejz(üÄKnY¿H¶Ü²2ò5l¾ç'_~Ç]oxèÆ)Yé &»¥3ÉD|ñ{Ä·•8h—#¦€ŽQöö·"2ðžÙo{Û[~ùý/¾þM¯ypù¦u«<ë×®”%‹æµMNœ •FÏÎ]{ûwîèÙ÷Ä#GûZ:°‡±…US['i±^Y>=Wò²ÓP­ÅÂæ¿‰§Mþ€GµÝlÆuR¶ÛˆnE­s*‘x€Ð‘t¹uDz¦9˜Œªi³Tµôò qYƒIލ>èÈK�Ÿ¢HϨ)CØZ²-½qlQl*]™zñ0B"cMYµj’H¬T£/} ÕÆ·ñùü^)ÓQ™pÙZ?8m¯×..¸?uÐj¯8€»°• EKxtD´¦~gmKWd1±‚Ñ´aW4+�hÔP(­WÑ¿CDW3öÕ?Zâ0ýí¨ëƒþ­j4nÛŠ±‘@—ˆ7ÞI('zq-O 5! ëäésÖ¹ÓF3ïÎ=WÁÖÐܼ= *Iøžj¨’+¢¾¾°·™¸û;E ÑøÐ‹ªÕ¥ýã&åf˺Õ+œùþ}%E…xÁ  í§%Å_±% pžÕ‰‡¦®¤Ø£ é¡h6à§n‹�ˆ¶V’K,]Bm{†Æu 1A$Àãb¢óñ÷ö�ƒ`²Í²ÅnÇ�#¥µ%h»ûºúbux¦¡·]ƒ=}¬j™ÍUÕN4†Muüñw?Ç$ë´×!¹FˆZáH"=À§víÞ_vòÙGíeÓ³³SbžÛ³’cE ÎBxÜÖ"™ÀŒæÀR ï5cjšÈŽ ¥Ñïéjmïp©ŽR¾ž¬­&Ф£éà"&’‰bGƒã«»\,m±™Ó\Ìâ/‘aM$*i(-i¿ºÍ6’Ëôc ¸VZ Ã+ÂE¦±]¤˜Rº•$¦Ò{s¦Èúõ›dÍêÕâÌ+8%û¶='©0èFËHM�ÛÖÍ—u+æšùy¥àÌû׿ÿ;ªš:¹ÚX ϳÕmÕÆòÄñ+ñQ`uDŠÖ£¸¼|t¢qÙ²¸‚‘AžWO<ï�é Ǽ[˜Àú[ù��Å~Ѷ¸ nc,š?Ç»iÓF™>g~_Usw{)pºæöNŽ;ÀqÀdRYY‡6W›]RZ�ù& 8Ä\äP¦Ë—ËÑǯ‘GΰRÁÏòwضMl%Ô¥ðA2§@§œ[î¿}µ¼úþMæ;^sGâ‡ßþিþäã/{â�_zÅo¾ý¾™/Y2ÃP´Øõˆ7"ò¯BLÊDlD¬VLü'?ö¡¾­Oüýe³ó3ßÂ$æé–{ž~R ÷>•»0BžN"Ú.)Êè—Ôø&‘( Hy]‹TÃ�¹£ €ŠHƒš}_z[[\¦ÊÂi«›Íi†n7ÂÌ=¬´#â(³ÙÒš’—Å h˜+^ J¿ƒ’“Ieäq['å,ËÇb+êmŒµ£0Rt©„â~.)OÌÍ’eKËþC'¤¼¼Tv:M!ØíÀP2Ó“Ô{ªÙ.3¦LP\¤Ã×jVW×Ú/É¿~JÞüú—Óº~ЯÇ¥;Œ¶_˜(n¥O‰äãÏx“„?Cqc¬J*ÑÂêĦ`’ϳ�$ªj³z„?cv«ÿ¡M'õï¦Ñ¢=<Ÿ•P­FÅ`{‘®Ô”d�€IàŒ””U ´z³²&ôV”–ø‹ËŠÝìN òHAe�ÇÁc%¨+éîé àyÃÄBÏ^ú5¸§»ï9ÍI{{'…ÉMM­ ƒdˆ[^!í¦IŸ®çœ\jÌ5þ®i‹Äáû¹ gç9UE½O<ºõp”¢G"öªVW·hZï$eû>áOLL¬ÿõ/:÷¦µ  ®$†ÕPtÑöŸ6NÍ61Dp@awzruxñz~>Y�ûÏ”Hmc«¯¸¦ùqyLU#M×ÏkK«Ús±†a&›+âT'F\g Åaàt5ïHM6¤j]Y¢ŒxE¢7Bn”šZJ5TL"WÄîàÓ"߬\¾‰# 7l—\,ª$fröb9_sðÏ'ÆE`6v®8'±övŸ<½m·ìÝ$4ÃL#¨R“—7QÓh½Iê{q|¯õ*¦WkYøØ% ôÖà¾éÑÕH GÿM¶{ÿŸ¯[¨f úïXýÁ-¶[‹‹ÉM0Ür×ͲyÃK >nÎüy®üsìîÞ€À¹šâÅÚ¦6NøÄˆ‰ç_ MTŽû°]�ÊU +L[„ö¡^NŸ)äs—‹*ì„À<Ú¯¬Nð»†2 ž¡Œb¹ïx¸h8 »„õ«æÝ:);ÕR ûJÄM*©¸Ô©n‘š…ž‡è½ù¦Í�mOþkËí—¼‰3ix¿vWÍåó59i‰œ9в±P�pŠ*“·ªŸ/¸4Ÿº\ù×—*v‰ÈyU‘P°w=u$9J`3åÔr|IqÑ’œÃŠi£-j`ú¯áN&AJtë¬@ô~Z[.íaå‰Õ•ˆJªm5XM òå’ N¹óutÊù˕ΠPpâ’à÷>5'II‰<µWJcs+í1RSXÅ„ž( Ca;6_“ÚŒÕó.ÍìÒV*�°]&Z—âÑí>«�?ƒÄBÜDðúÿac_ÉJÎ^*“¤„¸A _3D·U‚ówªÖ£K^ÌˤçU *ál†�•ššj¤e¤Kii˜�mÒêhHpíu;3ß-:WÐÝËpÂ).üV$TÁ·#AßnÁãÄÄ8£©©�-´Ëðß}Òîë [ñê€|ȸ ç�À05§ûÞ½GÎ5( $Fa&ÅêºQÙ¾×}᳟ÈüÐ»ßø¦™ù¹óÑ•1°d˜IýÎGþî«ijOuöËØh­? X¶¯¥£·¹,� çÎ]0æOÉr)Û)i‚XùÈ…2AaåÓgJë¨êªm4 ¶2Y*çbDۖŬŽr6ä1Y´¥µ“Ie¸NQ¢¾ZV@tâ°¯£àÐp‚¶$šBËç¢CnÑ,Y8W–,˜ëÌä¶êÛ¤º®Iö>#Oí<Æþsp’�Ç{?”à[6­–…s§B�\%[wîEK¡%,•—ÆP´Ê]ašÙÅ$ŠÇî( ¬+ö+w ŸÓØPT ÁͶÖÕÖ¥âjNCåÚ[Ëjį«ÂñÅe¨„‚–™�‰°¼Z¹z�äO›ÎFg0�ê†V9_R-g +ñ}÷Ui­å嵯å¢Jã±§öɱ“ˆ£ì;p Ö+­´¨.[a(á}Ø€©·ºeÃ’Y˜Âh«ä‘©ü±îSI$ï¶[oêÞúÄ?o}ó«ï}DÀp(nl*96gfIc[‡} Ý…²ÚfVf­=­§»“·öN\mÆ%ÅD'gšè –ó>Û[�]l?z¡|ϱ˕'Eä ¢a4LH4Ti·1i/^ 1Q*òdË4¤eðÉ…‚ ÷R³Ñ•àÑ«Z[L±"1Ílk©ÍTµ§4FÂäAK½if(7ňwßq³¬\±ÜLJNÆË·¥¬²^þôß�rðø…çD8CÉR"èÉ••'%¥åTÆ¢�Övž.Ê,í™,Ôû©H^ÝöòÄ0yð{ΉTx�þ+ò.|ؼöƒ€ ¯ÑkÎÓA 1ÐkO&i©ÉŽc0Êr°ÿÞðÆWËš5«y½aƒv‰´X)Ƶè»:€®æÅ÷‹cI”öK¨$Oœ¼Hu<†3q¿Àc2¿ÂlÉ@‹ËûÖWÝž�Ç�JX˜€Xc׉¯zùƒy¨BÞ°rÉì xÍ®‘ðÞsnëõwÜ‘ï�K)Dbí)(–ÃåíR™c.\ºàžõ«'Ìž9]Ì”,ã\O¬”Kªì)l¾øÇ§>vàlé1Û&Ûö"Â?ZFíf"f ’¡ñxQÎ9ìKÐPš¢N#–=\Õˆžv¨}…J2îaÆBôRUm èŠÔTZwœÆ”ö"Ä×˹9P¿ÇÅ' ñØqdpýì�O2© dÃ’ž„‰Š±n™:)UšªGŽŸ²;®cABG>à½Ò£nY¥E$ëdËv_”¦³ È„£“Ïó $õâk†úŸì¬A$[ÿ.ÛvB³ÇÆ× V&’¬?ä¶›7Ê�×ʲÅóåÁî•·¿óòÀCÊÌü‰l}G#�P®Õ˜?Œ„¾]' .Éžý'!Æ»,uõÍL(� ÐÕ"G&Íú²C¾”Z>+51ÆP›¯‡YدyÅ«Þý–W.Åè訨ídÀb>ÿ¡7¿5=oÙºÖ+ÖõeÎ\(©ñ“r³Œè¨HŒéÎ1&OÌ1}¨Hž+ñ?¼ûDYM D'"}ˆ�º‰Ä£Ä7–Çe:C­ta‡,õç) ¶®57hð¨[êªQÓ å9ÖÖ0«Ùu_�ƈ®�u«O>›r²èb¢£Qåeééé6Â>óé‹eò“?<.Å ZW&±Ñ‘0Á‹Çi,_fN™àª«© À“üÿæáxc‚’‰©ŸS�Õ&®+ ¶›¬R”[±¡ $5X]ŽMYU=õ3Q #T2¡ÀÒ#¬€”ïÙøº2F ±�¸ØXY¼pŽÜïíò–7½F>òÑIZZ*¯C8†´_\†О½'ìí»ŽXÇN\°›[Ú�Pš°w4BA_Éä‚dBJq»¯s <öšTãçàGwòl±§ÿ:©ÎÍÉn~Ëë_žûþw¾>sñ¹Yž#¿ŒÌŒô„-÷Ü‘¼vÝjovV¦Ìœ1•“IqŽ�5 *6æÏ™YQS[¿íbaI�J"Eª±FS"Q¨´¸Ý¦‡¥ a„¬ÿÐ@h/ý®èùF�]9øª Ä‹ï °#´!ãp.Ýÿ�Ôv"Ê�§k­gß� tÁ¼Y2“Ñn˜<Ñœ˜3A²Ò“y"?zªPÛvDŽŸ)BŸÕÿü÷‹Ã‚ò0ã!r–Øý]îÊÊ Ìô>M6Íð¿O|ð‚Zš"íÒÉD›Iê Îz¾O˜s=rìt±ì;rNZÚ:Èöõ3´ª™Ú˜¿òoÚ2¾á �“»Ã¼"frïƒJŸí’`w•õ-¬»�„ X®§ .µ¡B¹XRV-§ .³õÆ+ïú†*æÕèo¥ïa‚Áÿ¥1Àü÷=]H—ìêúæÚ6_Oû=wßÕþ¹O¼o·Þó¦ÌQ¿î6J°œçøÜåKÀ'/Kž¿0¼ªT º>Ùz¸ ×Åvä\¹ïž[åen‘É“'³…º/ÁgC ),«¡�£è±œLè“rÒ9�.'3IΞ) ³(nÆ‘sÒ†-†i!Dã^ÄF€:<bædTÖ=6–£E).©"8_ LìÜùR)«¨‘T(hyK©sfгÖÿÕý¡Y©CÛ†vQlÇÍ·ßñn î$­O�ÖÅ×PøËßýõäŽÝg+ûújåm‰ÄTJÎ„ßæ+Šz†òF+…y@P–Úö�Z:AáV~ZÄFÔsÃ?cD¹ÞêjƒŒ¬(íèkÒ„qØ Ù;�‹e›kê´ì_÷ôö¼4Ddù‚é¼ÐãM�9Ô[¸�>Ùºm*“ê‘çGëë4ˆ:ŒàU�Ê8ÒÃ$¢*<¶U¦M›A}€¨ œ¡,ý7-ÒŽ™Tü�¬.Ç×àÚÍŽ�ú�·n–ŋʒ0MH‘Ëåu¤û:{Û&§ ‡œH(â _gÇüVTÕ³29YpQvï=)�ž%f‘#݈‹K«‰«PÃfÛR„öØÃÏd«-kÚÌ…-÷Þ±yCVfº{”'.Tg}O>³ãñ÷ì‹+Õa¿ ±K)Øe´%[µµÜˆ(CăDB³F d†´cœ×Ž7Dz‡hº§f®ñ¨§f®+›ÓÂm…$ _G+´¸P™ ÀyT'u|î"*–ƒGÎðgÏâñ¯ÿú¬–ÖH]c‡D'fYk֮ɯ¤¾1‘D0Ȫº¯§^ó–÷G(±x@á"e2Ëâé°^”U2¹Û¡ñ¢wh,N\Cß’-—¦&ØFÉ!V ¬>(:¸N¦V @{†ç3¹L| ½WÙ”jwZ²‡ôiU%mh(÷ˆß¼ÎD¶’ùó¥§Ëg•V™iI¸‰Êebv‡‰=Á*Uz¼ý’ŽçÑú¢ÙÓÏl§õÊ«_~?7è1°Ø²¨‚x«í+Ðï`Dˆ+(ö¯ÉïÑ®@¿ÂºB%‡Œ'Ãò’•K%6&†�Çy˜ÏÛaÂHq(e`qÞ lW^ ™8)Ëúõ_ž¶ší}h�FÈêe+ì5«W¹âãd,,Ÿ¯#ðßÇŸyæ]øô¥¶öŽéj�ÞÏD¢.ÖÑV‘Ø*Óuiæ–Æ.Áàò ßÒ죡f°Ÿ­žô¤ìOªa ×ÜuÅ.bh!¡M]«­ìÖ€ûÈ߸¤ .Y4_¢âÓlb,GñÞÑÕ vS!YsÁ-Pe‰“`|©,�?MÜ.Kþûø³�3F7ŒÈÈHå& ¡4 ™�¦ô>Üí~V™Câ½�/*âçcò"ÆÐÊâeˉ•@‘Íûx˜f�Ò²¥°¼^þøŸ]þ?<²¿«­“>2{ÖtÁ ucZ~¯ÿѾÚÚ}½ùç#OäS_݇$¥L%Ï þƒ1ð΂=Š Ñˆƒ}@Ù¢„ã�gE’�ƒŠ¤åZ/10ˆè zím-‚膬+Nµ:qMO�Ö‚ÃQt œ”›¼dŽ™�œJmEEu_`p ùóÙho%ÆÇ|¿!7m�ùû¿'f¬œ|g�kŸ3q2gÞœ�ަµ½3”Aºdñ÷h+yjƒCH&ã ÓI…ŸQîDIÏ™,—Êê¤É$Ü«·ßO#Éó�Wa²csgŸ7>>>ci ‡ðª—n‘sg‘]6ÚY½ÿ}ôé¿|ø“_Þ Í{öJtøK…�Ø£9‘¤©èEPŽšJJ „0X™°Ç À‹Öµu-àƒ·²*ÄïP,-/ÊH,=PM/´üÊå6~Þ´‘rëMëÑRXn8`gcPI6Kie}p{šJäɹçW/�%^—-O<³Cvï?,ceEGGÉ’%‹ZV¼hç q6I¯Û{4ø*B8¾¸wäB1gÖ4™1s†$¦M°/•ÕrãÃ"xßÔÖÁRY×B{WD4W.uV÷Þu‹¼ö•àïOg;|ôë}þ­;öþímïýÄ T"ê`ñgÄå¯úC´GID˜Ê9¶ß&ö l!„cU×4SïÐÁR­¢è¹!"³52å©^Ý1ÊêÜCú©ßGÝ"ð~ÇÍŒü©Ó,P"í#§.IFZ¢` ©ø¨4¦LÊ$޲pö¨Þ§çyvóà¬ËYܨ,XÀëôÀ±ó¬JB‹f‰ØêJD1·¬Z`ãk0Uc ’=æép3Ÿ6}ªžH»¦±u¨-.ªè�¤t¶¨šŸy@L¹aÚLYºt1¬\60‰Ü~óFj®Æ°N÷ãg·ï-xÏG>wÝšHiFlUI¤!c!‘Ä«ð¢‰êéóGÁÏ™·+áZ==œ9 Ÿ¯›b¢Á|Àúge„. Chsbº5ƒ'У´!ƨÞX® Y9ýÅåuÎ GÀýRQU°�uBù“²è¼yõéëí­òœ\..•1²H™ eu$®×ó…hq•†´I©’M{�õµóz¡@q<‘„ªyZ´`Ž“LŒ‹=v„`&|¨`;]‡/áÚnhñ±*É›:­íþïܸi­¼åõ¯�·ÜNÁnLL´Œ…UWß(OoÝÝúÍüêÉÒ²JÁ*Rí¬ƒjt®=V‰©Ê¨°ˆ±Å þ“ÄF…p{Ülµ`3£0.6vp²Á*ddg¯³ ô)eµWëEFïâͳhþlÃå‰p9[»�ÜÐÜæXC ˆ=ee¤�R9!-Nª«kœ yLüceegg£òJ"öv f_gè}xV¡�n%L è*¤¿cdŠñd2!#�þQ1Μ“ò¤¥ËOmÉ`0?&�ËåõHDøÞ,Zº¬ì�ï|sä˼۵åΛ1ˆk¦8^UQ bŒ…U[× þÇ#þþâO»wîÞ©ÇnDýõèÌ)ÃOgˆD!ËTB{ÃÄ2 QêiƒU(¦v¥7רi" :‚ õ:yØeyîõ7íœY3ùÚ—u´ Pj�@&T¯«o¹¢˜,.6šæL ȉSgˆg�¬$ZV.[DQ"\$ åcg"1]º½Å„âBkk<‘àž§Ô¼ÙÓ%99Q¢R,ãìP\Ã"1§mñòÚfb,é™}÷Þ_óG?ô®ÌKD¤§¥’v¬GGŒúEÓÔŸüò�Ö¿Ýzl÷ž}�Ê€ñ?ˆ�DÆV"éWªö(dy7> ¿ Xf„iOV³šù!Ãt�@{àš­Sl›JÕaZsi«wåêËjÄ‹ˆ3'Òì¬ É…0¬ÏopJæu Iw*AªßƒnrV-P‰“ÈÐÚÚŠ‹¼i mT¦$%§8×+A÷gŠ4#Ðì…JM+Q&—#PÔ øñ5¤1¾‹ÌEBÁäELêôD'v�+®±¡|§ã‚’�åÙÑÕ‹èáó�hƒ·õVæäéýK_²Æºé¶Û}oxãk�W½üþ´I¹ÙPÿÇÅW~ý‡¿Ù;÷>»oÿ�"¹€x8È„qÌ%’vD#¢ÓÛð¸8¬`oX6o[Ù<ƒÙF°›ñ0·x\#k‹Äa[ºÒP $’~YÊ ~L±wbÑ>˜‹“_K{§aÙ"è^�ƆS b¤B|Z^6>Cz»»äÀ¡ãH:c¢½Åk4 ^Zê7·ú¤²¶‰,Á¡Y¦øE¬Žøe…°}<™äMÎåLøÓòÍÅË–ºógÏíjî¶z +ê-'¡ƒº~¶¸šÖ*ç¡H¯míê÷&eûg-Z*³çÍñ,_¶ØÜ°nUÜÆµ«<¥0戨.Ì¿~R¶í>|yßÁ#'qý‘¿é¹"c7‘4(‹”b±¥Ëá`ÿ¦Öž ±À"b"Á‰‘sNàŸCçVÛæhÍkôìòÓ©�^KzÀ’š�Á$¢jǽdá<°¸pÑp Àýô"ß¹+xR‘<1+ ‘Ž*±Ó*-¯dÿv¬¨Ü Iltñ¸(�d0„òY«ÜýÝÂKkLÆ×PœmY-ßvӹ㖠Þ[nÚ³zíZÏä™óú²n°úÍ(; .ËÛ·lÕšÀ²5̸Ä$7‰¦#(ܼaµ¬†j2‘1™DèÿUR&GOœm<~òôþ€ßJµ³ÊGÛÅJÿŧZ[^[$ˆ…>¤ËÙ€ìðÝì¤ü‚&MÍíÍd‚YŽ#ç5ÃÙö0z2�0øX3´ÆÞÂi�üýg¶î�3r�4Òö)—¤�]Îãð«#…Œ…E¥.Üô¬ Gw" p~}gw?±¸1 ãÑÖ8ÄÉú4ƒ/ЭY|ã+ÔdÂÍ48 6‘Ñ…yð.Ã4]^¯¬X±Ì6DÌÖ¶vï´üÐK�$ŠøGB¼ãÔ«1Ò1™DŠËdÛ®C-ç.–ü§©±±HDChšäO$mJ‚?/¶¯»¯¯31>Ú+ª¥ÆÅÄáÒVïìe_Û…1Œ-PtâàÈWsŒ$­± žAgàm;÷9FŽ$N—ÕÒ“j ÷ñì]…S{ìÙÅÝ%@ˆyrîî^vò÷œ»»Ëó<‹»»ãî‹®ÛÌï¾éšš"¹„äȘ®újØ+ӿϺ7×T =5£3èêÓÆÇFÕÀÐüB0Oƒ>{oa]JtEÈÎ̸wÙU%।Ì|�ÊΑ$Æ";-5Õ$úêHß„‡Bb!�ËehË"‚�Ú�ë´l8qöÊœÄþó.t[ÓYæ-Ö ‘(‰6‰4Nn)ø4M+ŒÒk]%ü •!eåC¡(²³2#q³Ù¾\¥-ˆZ‚Œšëiû`Þÿ=!’:ìi爫QÃô MàÝÏmç‡ô>Øú[ï§â.Fk÷°{dt<îõú“^èŽÒ9iiˆë4Täô™žÐ�Õ3‰ÇlOK�øÉÊ_·ÍÒ7©©fæk¿—y»F ‡qûn³d"×Nœ½üÚ•+W\•h�Б¤p=AqØAÜ0b€›˜º‘èÓ3{%SÓ^ŽçæfAÀ錇4ö-¿’‰ýw$'tlŸÍ…§%E…ص} ¼�(FÇg0çõÓ®ôaHKãôÞõÌ63ƒÑæfg !žî“�Hs²³`(`Ö"‰<Ù…H=à[ì?ÃA¢Æƒí샱 ëG|ߨÌxᵃwþùâ¹à¸E"qk�HR%Òãq#¨-k5mYš£~ˆ>%Ræâ~ƒÈÌð ô¿D¡A·Ê_ËZ«·}»™�¬ Ô­›kÍ ÌùBn˜˜ZT㌓OÏìÞLkÞ±á~- ÆE(ùGKËKÙ²ÆG™•<ñûêžEE ~¨ù•, Ÿ:{ßùѯC¿úÿ/ÿåïÿ9ÙßßwÝÚXï¶Idí ‘&�Äã&�ð f@-KMßP†™‘pZhpØMã÷Í ©X°åâåûË•` -qGÝ^Ztà@)Qíśީ}o{}ÿñ+·nßi2 ã„å³>m¿¡Ö0‘Ä%b†¡b2µ¥ÌÅÄHdyˆSú#\FlmäH°d&ô%“rjdló¾À¢OI~nä±¼0··µ¸ÅÆ7Ée&&1:> Ã0äÖƒ@(B2IèhøBÂ0âÖm,u)~ ‰´wöà/¼á9zòÒñ¾ðÚù±±±Ëþ#qCbÂ&‘µM$dË0E7âÁHÔ^J\Ž)†Ñ±i4µôq,Xfÿy²œô̲|ïȯâ‰3--Á™‚eéJh©àDŽÒùýµ™†‰ùOÆŸß»C �yhû¸äH:J ó(ê891š=42¢'ï‡üý¡°éã®B¡°2ßO‘XlyÊŸFŒD¢$ G€¨×úÞ:–Qq@ñ†æ6H2óúþcÿyeß›u]ÿ€ßI\—ðÛªŸë€H¢ÖbLŽ4ÄãRvâb¢ì“,ƒÓ*sÒãEÿàÉeÞd)káF»Û�bJÂ/ pY¥,îŽX%´µ'OQ–ºg×]²@6Ýõø­µ•$t1Úq]»~K"OÒß TþwDÓsB“÷,'ÔÂá– šb>‰y«oâ—ñ?ä`ý  £¹µ/½¶_½òúÁIé‹ì»[/5dà ‰“÷‹/®"±ÅÝ’‰è¦u¥a(NÁ,'B¡KZ¡p”ž%šÊÌ[ÿ.t=N¿öÄ_à)Æhék¥ó–ºZk4öÉ/®P5•%¸z»�š[‹€£À¢@Àå¾k×®i¦_B²B³Ê¢³³ó¶ûfßÐ8w”–V)4âbVfbCÁÁÚ†d¿8é^Þwû�:|ôô.\ºvÀY‰Î…£½ë‰H4+ý¢’†®+=Õ¥–ûIÅNÑࢧ{eE¡Yââ„‘7ìÑÀxwZ–&\Êœtjlíƒ×4¬ÊË3˜ç÷lÁ;ê¶Rô±¯§‹¯C2";+{vÓ»�%;q…di‹CVJ·}L¬ÆS©˜à€×nÜ1ËYªµ³ÿÖkoúSCCÓ�¥Rò½œ‡HüfÄâÆŒLmùM_åh\_©#å-rr2árk˜�õqËÝ" -‘K‰JÁÎL(!Ÿ†µ åU›ÔέU,)^¾Ùêz¨0§S*…>%º¡0Ôß�‰‰É¤n”=ì’ßKÆ–I$ãžú“¬<´è 첩£Ï�ƒ§+¿vó.Î]º®:»‡nÿý߯þ«¯¯ï<€[‹—²"Ñ­ìó¾à¼×Ü;°¬qW&…ÔYÛö`hÄÍ¥=È-1±å-wúºj„ºR³Ý{vlBiqÎ]kÂøäìÃ†ŠØÀ~nÏfZ/wô  ­éŽEäɇššj<»{3rs2©š0:±:DBq0b~–NŸÒCг©Þу[w›bí݃Çß^5(ƒðŽçûÓ%ÿÞÔÒŽ?üõ…¹ßüñ_ç88 Gâîãg"Ni+"Ñ%Á‘“@(:#§»Øàø §aVŠL¦§çFÍ2_XMsÁåvÛ¾ÏO šiÌFëìĨ¹Ó‘WTi6Ó9nyâÂf€9ís X²ŽÕ646M-m†®ëÉ”‘p11§ ”ŸJ)Ž7_»ÝŽ¡Ñ)¬ Û‹½OE9‹’'?ýõ_}ÿ|ñÍÓmí®Itÿÿ›êN�¤_"h†4Úõ™y¿ß3çÃÔœÿáe%MKü Ú6ˆáÑ)DãÆ<>„#z‚¬7•µÍnmµ§æ±Ù¾ÞàÊ*CMU*ËŠÐØÖ�ÛMÝ,= +¡°È¦ÐçcxlÒuýúM%KXI·'S�_€âÒ NoE¢qtõ�âÚ�öÕ�6s¥Ø¤Â[Gv>ÁƒÆæ6¼}ø´¿¾¹óíÑÑÑ.Zn†ãø¿à ,öí³6ÝÓd!1F ‘ç^4‹H4™ø¼>´w�á�×pðÐEÔ7t°¯T‚¬ua€r(ö Ö\©ØP» {ÿ3ØP^ĬdbjöA%.ÊüKVBÅ‘ñiŒ�k7o7 ÙÄ‹‹ Q]]�¼œL(¥(àxúR=&%“Z ØFXPÌJ8¬‡’lØÉBd[�’'�ž�ܼÛr°±±ñ€£–Õ”„rˆdi}’K£ša¨T_0ìíœÀ¼?´(™$ÓS3˜›™A~^—‚tC‡!‘Ÿ ÝÊD4Æz„;£ÅER¶*giKÈ„’(÷�Ós»XÞª¤¿ûÐð°ÖÝÓ«fçæ“ª¼U\X€Ò’b”——qPCƒ&ÔÚº† V¥ç®3ì^‰R”Pq� rGwï€iD…soï6u¼uøÈÑ»–ô{++3K†“‘è œ¼bi"'žœÕ;Æm�¦•4éWååu6ßô4q{QŒ±þÀL¬²ªÅ…ùfÖÁm÷Žža¨Lq•åáýïÜE.iÎkós³ÆÐðXR�çææbçö-’•pŸ„ 9ÉïÕÏ�€U�Š[&XaZ2 v8‹Ü­oæ¶úÐÈÄÄ¥ëwÿñÖþƒœ�¸ñÄ=‡HˆY+3Ñ%\J©´`8ŸQÒ+YX>gÝ|%&)²2ÓJ^VYk= ‘’S&ÙÆFìÞ^줩£ŸúZÒ@ÚRS‘¢çzttØlL*ÙüM*’çžÙƒà½B|ùìù…HnÔwr_fµvJlŸ›xÐÙ)Ye#4Ó¡±¹ûŸÄ ¯î×ï6u^:|âÂï.\¸xS)u@CâHÄ!’)f%$"=®ëÑYo`jxr @FFrs³�™•ÎSê=ÆƒšÆ€µÔöd`�žÿ^ª�Ö’xÄ& �÷m�Fú�¸Ö÷ Ñ��ªÚl¦K„£À²ƒÁÑëû³’Í2¹U]YÂÆ¶èo¹ôx4–l Š%Å…xß{ß��èÝ(*ÈáÿupÄÃÆûªý_ã!«Ñ.¡¹W§ÙîôBhË|ìä9üçå7qôä…9é‡üó•}oý¡±±á,€·$†á aD¢$g$†XDj¾`Ä7♓��&T¥%ù(,ÈAmmg÷%8ªkMW1[ÉÎΠMî“(�J„“8G�µ¥‰¦Ù»#¼U–_œ2ƒ;«â È×kbjŽ2óÒT¿¿Á¬pÏŽ�°\4 ™Ë‰>¿?é–·mÝ„o}ýkØ»s­ƒ]š†»Í=a_$’I<€•mØ8‚èèêÅKûöK&r õÍíC'Î\òÞ:n¼ßmîÆåK0?ëIº¬$'7;vîá�€RÀ°dX·»ØZ(ƒÁÞœ� /çN‰c�55=Cyëàq �Œ›¤ÒtëöÝ"‘È¿–˜]Vqˆ„Ð%ŽK¼ q“Y ýÜcé’•À•âæ¢š”¸U\YQl³��5e(-.@qa*ÊŠ¤V@1=—Kã(/ÉäÑ“_ü™×À¶-5<õ.YEÓ77ŒÙ@Z³÷>×Ù…•ز©–BÙBc{mk€å-é=�Ü¥g†¶îAôv'�Ä<§ü*k¶Êû² J)NÞ‘¬¤µkpõ\ø˜…h ô({'‰—Nq07ç…ø§ãç¿ù+IdpxTÞ·~ÏÔÔ_쓸½r꽑p§Ä"“Û°Ü#±˜129+^%YíX…LB‘È0{'”¯ÝXÆyqqž|˜‹(_Q^„¢¢<þËÒ•�yX+ÈÏEjZ ï a-yrÆQb©¹Ö¥„ÂåNÅ–uØP^Œ¸ÇÔŒWšÔ# /¼ìƒ½÷¹(/)à6pÏÀ˜ÔŸ»y?ÉÀ1àêM; yŸ*·K£ oWß(<3óOÖ¦PŽGCY�w“D|A8HpC}ÒƒÃ'Îàç¿ý+ŽŸ¾€™™9l©Ýñ‚óLMл¢SY‘1Kwë¶••x  z'õm#†Ë[ÙB ¹9,a™Ä@¢È6ï§§²o"$ÃÇÔn,GI±™¥’|$ƒY”H¬ÑR!§³á΋ؒA½#«?bÄœŒö!È)¬À³{w¡¸ £Ó›Æô¬¸ä·uÓl«­4OyüYcs‘HÒeXy¹¹x×ó{]eÅ ñýÔÙ;byÖÇ—N‹?îѪ zØšâŠ'ÐPÍ!‘ѱ ¼úúA¼²ïZÚº8ê›���D£¿»xåÆ C�ð"à`¥3;ú$ÎK´KøtÃÐö¾™ !(S:Ãù%A¤§§2ƒÈÉÉb)$S¢¬¬5Ò|/*`†a’ ³e¨EË[Ó3óž@Õ†²¥ù9Ó´*ö˜/¥ãS잉ƒ{¡¹ÜؼóYÙ+Ùˆ¼œl: Ê80‰|aykÛæ æA�²5žé965“ ´¨©Ú€jyïHFÂr�¸BrOÆo«4,�´Åãþ]ÊÊD\ Òxs D�ú¦Vú©Ÿ¿t Íí…Ã(*,ˆx½¾¿9~æ»&W�DœŒÄ†aº¼ae&ócžùÔ}G®xeé]ápÑCÄMb‘[–»„PÌÝþ¬zC ³ù™Ü²<M¿^H,rŸd’žž¶ôþI#€²Â4[JÞÁÿ"%#Ïì­3™qtö� ½gøž±ìݲĘ/¯¥æÒ¨ ì!‘$JKKðÞ÷¼‡%×9¯ŸãÍÕ%<ì<ÊdM„¹ <&ahŒÅ¦Ì0c¿ë€9'Ï\Ä›û�‘DÚ;{Ì©=•›�=2?ïýÞ�;�ß;Œùä ‡H”DÄ1{ÑÒæÆà‚ƒ¨�Èå«·øœ5¶´ãÊõ[¦Š¯B¾142ö+¥Ô[Ú—F"ÜX~„-Ç0¯ÄnYs/íšL“Kj­ô@²3Óëy� ØGIK3”ôt~àä{)âàéOÞÌPÌþ˳ÏÕùÊKò2—F&Š#¿$·åA’’ɦ»ƒÅ !-3Ñà<†GÇ™•†b–âr±F‹ÞæÎd¥»�O|ò3æP„+IS,¤« ÖÐÔÊ�}MÓð‘w×Ù»MP÷(ÂJlÆÏyf�ˆDÐ/$qîJÚúÌl…£Ï]Ò¸¿p­Y²–ú¹4´öáÆÝNÜ‘ûg¯4š�céÈ™[,ÎÍûY¢MIË4³‹C“�4µtA�`P¢­½Û$ŽºgefZ%_ lðzqåúm:v7n7`vÖ4 ª`(4)YÉß¼’ôòï‘Ê"]b›DÆøÔ\®\lØL/ÌË~”“¡ív(My–²¤äÅž‰n(¤¦¹1ï šÙ Ë)Ù99©µÕ%®¥í‘°0mo¶[úZ�×qï\8õÍMšží¼¨äò5†õö M˜’)îä#sfÓ$ AqQW„™Úb�îÃï«c¯DƒÇ›ù>,-Î爳‰nsdxÚ‹ÞÁ1ŒŽÏ QÈA|NHÒ;¢ÀØä¬�ÍËf²üHÂê7#•ÿ?âúÈQù»:ë•\xÀ4Ÿ·}oÁ­»M�ýh¤§§Gå¹jŸðWJýùÉIÄ� +‡˜%1ð™†é™›øÎ\iÆ¡3·qñfõ˜WOKƆÙ'‘]nÃo¨(6Ù˜7û/ž‰ñ”Ðzo31♤ÁÁã!+¯»¶oá·ô¸Ô'£%-Ÿ‹Ò¢<óûZK[§;™küù…¥xgÝV³´Ê÷ÔÍkË]ƒv/Ú9Ù™ÈÊHg†"�%Á¸ÝZ\FL¾6¤w¢¤¬eHS>.ä‘¿;(£Æ>q�ôˆ:@ŸøÅß’‰¶c�=ï_ºÙòÆñó·O]ºÑÔ!:O±—_? ^{ó09e–³˜…ÈsHM¨§/â¤ì@�Ÿ½øê~úe;yo:�ÛõÍÌ×[OE)nªóùúÍþI_uÃ0TŠ;¥³·oàµÁ¡Ñ7”R?0ôä$â +‹�Ä[r!)žžó§Èo÷͆î|9½±¦þñìå ö‡'{l˜‰®ó~Ý®Z@O�E…9œ ÊÍÍYÚIÌ�Ž\fWÃÁœk6£²´ÑÜ â¦ûÀè$vl®¢Ús{¶ð~ùÊuã#x7_£d„–’�:q¬ª,Áø¤d­}œàÚœUËj•} —‹Y–¹kÂ÷ܸgÖÌŠ ™&ì‚ÂKRòŠ´um–!ƒê”—ùþtÍ{ý2VÜ=ï ͆ÑoéÔ%2$¢ìÖÅôg¯^¿³»vcÕÙ; è6Îl�ßoFLFÙ2Cf†2Ã2ãcf‚C�™aÚ>‚%j“B˜™É̲$K–4ó½ÿÜ3Çǯ›¦Ù6I]çûŸý�KнGªæÎåðÆ×Àä!Ñîu{ÜÁ)ì’–’DÀï³))ÆÏxÜjöÂ{Äk¼n¥x"IB~ÎïóQaaþtC¯aÓ�w�x—L{G”^~m�m¼DV8Phb2“Êì�Î`Ýn”ª+K(?ßJÊ—ñ—鿯DÌ(û•3ºØ5"O ‘æøß&%37E§mGŒ?Æ{= òƒôØ}+-ÃÏ˰v`³b[×ðÔ¯üê¯:/œ§Ïʰ‹”ÔÝrŽþòŸŸ£Ç/òÈüßÿ¥�Z³ÃØ ôÂ"QÎ7@—›:Ù`"ÿ—+ çõô¾p¹í˜~ým&@5(–.‚Q`¼î¿k§¥p�jPªBAÿZü7ï·Œr†¿ß«{=ž@qQQ *“*áùs¦áÂk4#gh5Õ•Ü£•ŸB^‘ÃÈT."§Ói5‰r#f"Ɇ„CbýX!džŠ¼¨­½‹H«+Ëùw¸Ü.~-Œã¬óB†`±|ŠêÉdÊÊ#%»{ûZ::£'c‰ìY€é9a@”!aƒÏ`êïw¬Ã®’$ª‘€ß¼f=õàJ‚Ë#ïDÞèÿ»išœTC®„ó¸Óâ�Ü~ŸŸ¿07ï éaxÒ$6$îbNº¿•…&J’Ú›/Ó±#Iš&‡¸Ö­œÏ]ñdŠ“ÉÈäVo|ÈxæÉGðq9gé8ñ8}ýë/˜ßyq»–L¥éƒOm¦’â|îé¶Ç§†IñääXßÀè5t¡³{8E$ÏÑ‹ qƒïšä[¨6neÀŠ€L„A™Óé¬Ö4ÑÃPT\TX\P�ÂZa‡ß畦i¦à™$óóó<ÙLÖ×Û?à4L“›}KKŠÏó$m4èÁcô“Ëéâ‹4¾O8wâ5Å|×_ ï¿“ÂáB.‚B0ï„ { î7¿» Ôš=!iÕÆk×[ö�¶Ñî9ÆR†„åOkšXU[~e¾ë0”ËmUgU”ÒÓ¯¢ûÖYã7n)D5Æ�«X -#ÂÜT?‘‘bïƒ ;/â)á2`¥·&|‘éÀžíÔÖÖLŸ—ð™âB¼‰ÏÏ^jÁ]þ%*®N=ûÌ“¾ŠòȬL[;æ_ݾKþÓç¾"`(hõ²yT[UB‰—ðÒÈx<ãе3˜j|)™ÊtÙ¡ÛÃàâÞß-þ¿ÇÂ2€šM!Xj!„(CE£†õ]]I )ô%'SŠœ¦kõýTÁÓ¨  5æcÙ—RŒŽ�O?µÂÇá¢Bª­©b£‘L¥ø&�x´L€–Âs©„Ç) S~öz½¶a¹³‰~Ó4¹ ëÈñÓôêÎ}–11�Dï:ƒB„�D´t€¹;çH–ªÀ£ÕåE[ÐQü„ ªD˜@·š k*Âô¡§6ÐÚåõô{íJñ¦ãO</âÄyÓ{Jn*GÀöHLüGW{$lX”Þ²¢]Qzeë·IDÖcxãÊÅutãÙ�œºBýÐ}=NÖ­µfÍ&cÂw¸�¨Ž:|ômÝöŠÑÜÖ©cmp7;­ñDje¾==}Ã�ÃcñQ"ÖØ Z€œEßið‚,ÈHà�êÀcâ/Z¼°_néÃÈÉä“°ª�]n £D8ÃÄa1SJÒ„Æ�œù!x+aÎËÔÁàX7óCÏÐm7RJDâÔí¡}�Ñž}G¨§o`!¬N<ÞLD_@æž0 Ê�°ð�¡€{mmeé¸z€$å›RŠPÀKKÑÝþ�'7 i[Æ#¾=§¥7{!^¯xonH„FÓ›I‡¶¼"ÝûöFR(q\}ï®íÔtíäyPç3¯%¬@ßD#ï0‘ÂIϾïC´lé"ŽÏ !fM¼`¹2êK_ÿ¯ø+¯íì‡qI!9þ<�X òA?8 ®¾KÚ\ æƒjà ÀÄ÷0Ò€•Š%– SŒD<¾RNIy�n!Hp)2Þ/…Ápð8þe‹ÐÞû${,x-{6B¼åσÿzh8y>� uùÞÜÚn4·ttuF»[Þî"¢ÿG€1›?eHî|îdQ]U¸¾¤(ï#™¬±V*±*S�œ§gYCëVÌ£bÕo2… Hqqø¦{I8bf§;×Ù ñ–rÿˆÒÛ¿wvEåþ]Ûd2‘Ðp Ïr-BâzÛî„p÷Q”WÕÓÇ?öQ‹hš6›Œ ßõîÞwXþÅßéTã…Kq"Ú ¶� ˆ®ƒ¦9rÑÒ@”/°ä…v˜l9 †!Ï%$£±¡¤Êîž>ÁS�Ipr¿ žÉõñO|RÖ7,…pˆk6íónïŒæ>ÿÜ·¿ó¯Ÿû„}ñºö�RšiP\ ÈKì£XšAûy�FÉó¥$Ä"LYÎÇw‡Ä›÷4�¥¡‰ñDb÷é³;m�cf/™T •!y+PøC¿×¹iÝŠ†ÂO¿ÿþÂÕËê¼N‡CÀ;aWyfž$.yã “ÐÙxð8O1“Ûþ¶(qÃÛ¶�IÆ]Ög±qõ !ŽþÊÞSÖ€Cnè{pËzìÉ÷P$RÆá ¼n¶äJx[âÞC'Çþâï>ÿâ±ã'SvIépü†¥¾JÂÆBÀ ?¨ûÜ,%AÖ~m­ýóip ´€A Í>/PI§w§âRÊëHć¢�`$7Fueô�á˜?“ɉ‚ ¥;µ"K‡öï–ÏÂØè£ÏÞ�÷(=sÕê+á‰[Ö-¡'Ÿ~/ÏëÂg5«*Ð0d/З_øökßüö÷šˆ/|¬í �”~¤Û·¶pXIy$w^Ã` ŒÂ;1Û£C‘ö®�,’Õ²B]àÈÛíÐOuãÁsxÌÞÓÜ|Tº#â»z0_t÷ôÑî sÖ¬­‰<ì0“åeXÖÔ\ÜYRVÅ7³É+Až�çK!ù^—_N65· Â3©´cÿͪZHI�‘wÊÀ+àà ¶0†¯tŒ`4}ç÷wœÈbµ/µuösÓ�/J‚8ÑžKÝ…qñJ%ábÚtßCi.óFÌû¼<½€:Âð[«myb+f$q9è,}nWP}]•cÉÂÚGß÷Þg+ì$r (%%%呼«5†A'HšR†GÆž±ñxl|bÒ‰±Ý.x%¢¤¸à‡¦ O—ýên{3¢NJwöbŒ²O�X{ŒªË‹r%¼Už$�ŽMðò¡PžµV9L^/iš¡-Û+áéÄÄ�D8täø0M�¸ ¨¤<’w·$Hƒëààp¦o(¦:ymòå=§‡ŸÿÖ®ÜÖ�Ç9±+_?"E÷0we¶–_ŒW­Z)<�"cÏ‘Fö1ˉ«¸°£†C]×._ þÁAÎK¼sÆÃdHJ2� ÂÏõµU5ÛÙÙ¹Èîu8GJJ*´5'”£à"xüø�djªÛì²/ï>‘üÝ?.÷§ÿü¿˜õt‰$Q6Æ£ä•î–!!Þ¸·dñ"Ç`,;5<:AKæWcäÍ|Z¾¨–HE{úèÈ‘ÃFSKO–Ò¤©ÉØÌÐg6|>3¿!MÓ~.‹óœõ {?,ÉÏñ%ÿMžH&=Á ¦¤L‡¼„9Uµ¨¡ú"Š!Öøé�ù¤¤¤ Éœ‘ Ð N�¯€4M¹w,–L]kéŽÿï‹ûÓ¿ýg_§¯þ׫|Ñ2sRºkâ½á›6¬¡‹—£±É Ë#Ù´zv÷¯àùi†aÒ•Ë—ôS'NR´»�„ÐØ À;˜ö@$ŽéÄ™fŽŸËeÒ~"i{),“ƒØ«`# ¥#lLøg’ÖïàoJƒÏ³é„õïøhJŽõñÏ^cÅ7 ÝÊïè¡@ €:mMkgÿUR†dî“H€N° | ¼ˆõ¯}}ƒ£Mm=©ÏýÛVó7þèËôòöݨƉßÅ’S¥šªr¬%_ç°ib¤|V×5”—ÐÒ5ê‹ÑÈ`{(®�i¤;Ý”ï#ÃȲ0q̤&¦=ë\Óü¸”“Š�’°wòkºn{"’sé$ŒP.K¼, À8ðÏÙt’=�ÔÄI"â¿�禒㔌 ÀC™²�)ÂÞy±nõ²8&¯¶ôÌDRRR†dÎÉ0 ‚ÏÃ;iDÜöbÄNœ»žù‹¿ÿªù×ÿøE”¥§ññ_\”îüè”yuÕÔ?<–·uߕĩÆ&‰ Ï( öÑÐò]=ýtîìYêŠv±ÀÅžÉ�L*Îy‹l†¡ôä8{.é$¯‰µ�Bœ= 0æ‚¡á×áßóãø]¶šœâŸa(ìß�f#‚ŸÙ+Ée&­¿ÍFf Kf“tÿ†åô‰>-. %4M,ôaÊ))©dûœ÷P@hãØ¸h¢£ZOaÆyGW·yåZ³èíÔ‰a'zïäXsUì¤ó. TÕ9Ó©É\&3娯)ãæDÌO£d2EƒƒC„}ãTS–GÒÌWYx;çÁ ¾è»¼!â¼— +TÅÆDØù„¶8T%%YF�ÏuÝe�³á™Œõ“îpYƉs#—ÇòBØÛ1ð{G~­‘›"¯/Dzv ¯3a‡° Ý“H¦/™¦ì %%eHæ¼AI‚.ꤔ1ì9H¢›:�5žÆµ¦V ‹uœƒÃZaA>a¥(�øPº#^ ïüèîíÓÆ'Ò2™ˆ ŒNn·“:¢O¦¸xdtÌ\ÜP.ý]HÉa(N‚ëN�åu°1°'§Û‡ŸSü˜f‡°$Éé ŸOMŽ‘ÃícïC9Uáñé£Ë„‘%ÁC?Ù€q~…�çlrü·5Mºž¸|«xëQy6¼eÍüÓѾQ“””î C¢&“¦A'8®â‚6>9™Ö7ÇÐÐÈT[{—yèèIWGW�..äIÂÎÛºxIIÓ¯Ù�L¥©»oPO¥séËW›é,¢}Ë'y×àЈ¨-/Ì–‡:I“iì@0 2íj,œÃ�ø)3ã ?ò*üŒ°ŒÇ8¿–óVÈ*²ʤcv~%g{:ÿ¾žÇ|N„˜®Ãsœ‡Ñ5áŽÅZ;ûœÃ£ñT&“9“Le¤¤tÏeLÀô˜•(è–R6ÃC‰ã.Ø984¬c'´û•{�]Ñ^qîõxøNZy)·­Û��‰”D‰ÉŒ†=ܹk-º5ŽÄ©ë44#ô‘%RKæW�Ë¡B×'¥”N#—ü�änÉ"—INWd!yÎùx'0lTHbÏBñs‚¸ Œ=Ó4pnMCâ\ â?!ÈNÜãȯcc¥s_L«ƒ›{Æ0"׸bPIéž4$ʨ$Á èëUs4OHx+�¶Î¨ãð±Sú¥+MÏ…=�Çõö×Ã*q¤®¦’††G4L"�mmíb*›c#�4{&öÞwª­ kÒÈ&H"Ò… �Hòk{øÀØ»€¦+¼`ر ±ð˜$ÉŸŸ$ëiË‘I¼Ö�×$ˆ$oò&-I@¿Èd?–Í޾ÁñÐ¥ë�]¦” »9Q’’Ò=»�DÉœÀÂ`5¸OÓ´Õ¡P >RZ’.*ô•—•Ð’Eóiå²Å´jåR*�ðRzë¥ÿýöK´c×ÞŒ‘Iº0t“,¡¡” É÷¯¤ŸþÄc„åXq»Ï ¤“à�iÎ5)Ù°p~ƒ„fyìMXb£ašüûøœ$ÿ�HLI?¥flï@{ƒ]r„$;ýÇ÷öѶ]'�5µ÷½DD_qõ‰*)C¢ÞÐ@�ù`eaaábÜ=?ˆ+èºÖÜn·5Ì� Ê“�?H êÉçõ(/å-NÚm¼x•¾ñ�Í‹�çhpxL³î¥ÅùÖ6LZÜPA¿óó y5ŽcY!¦^·Dظ죤é)�dÚq,�ˆeÎ09àià~“4~ÎÞí~äôUzá[{šœ¸üm"zt%%R†DIØ8fP#„x°¤¤äi·Ë¹ a¯""SX¹“¼¼mFÇö#l¦Õ«–R¤$L^¥[]q?Ï«¯íJ_ºtÁƒ�2œ”G©6å½òç?ý¤xòÁUdõœØ2AÖöFr8Z ¸�9ãµh6Ò> mÄ ¾#73$¬®Þ!úúÿîŠã¥CŸ3¥ÜÊkc•””!QºÉ{çk5M{Éú‚‚ü•º& ¥4u—ÓEå¥TUYÎÞÉÚUËhÝšåTTXÀ ú7ßü§¼+7µ{ßaù�o~+Ûxéšk<–àð–®i¹g]£ýâgŸÒ,/å d!23ÂP�ú ÑÎÜ0H ÛÏk7XkhV²Ÿf(•ÎÐŽçŒÿ}éà?œ¾ÐzÒ^k IIé&R†DI?X >îóùæ7ÔWÃK©$Ý$ïÚ(,È£ÕÈ¡TWUPUEÕ£›» ?�BÁàMF¤+��Ç蟿ð¼±}Ç.‰ò_AÙ\Nb«¢•'�nYþ&áC6(ÆLOdÚ�ð‘¥ý°2mhÄ ö‡³!‘Rz^—;áFÅËMQúæÖÃ/cë!Ë#ùO0EJJo"Uµ¥”]àP6›mì�$:­}óçÕäå(%&«Ò«+ÚC=½ýÔ‰£…$ârbLü{ƒRbÕ¬ˆ)ÁÚÈØ„ŒF{Li& ]פÇ‹kø½»‰4 ½~¾X0g$×�@»ÑèùþÁq:ÙØîèl2 ó 'î•””!Qú J'8�'Ž´¶uœ„ Σ3Þð‚¦iøGÇÆ ]ó„¾>¨` á5Âã¼ÂUÓ4šPåĶ4!ๅ¨¢¬TÃêÄšHHcj*#à™ˆHIÁ­¼WbÚ @¶aÐm£ Ý ¼5ów6rã踙7?>‘¤(–¨õŒ&Ñûb�âQRúѤ ‰’¦@ �.Ÿo¼t5–H]ðû­Ø X„0OJ·:µ-¯¤¥­“š[Úù82:J½}ƒVß yÜnUùe_ÐÃÅE´få2!4]kïè’éTJÃ,.iJ)j«J(è÷Þêû43ÉÎç7yq(Ë·€› Þ]¼Ö)Fb t»OÑi�!%%•#Qz›ï±¬Bl@épÁÒ%‹W›Fne2™(ÅcM{$É úÅ æQö‚¯_³’JKŠÉív)<=úþ¶ôÒ¶íÆå«×�ü GxÓR}õÒ:Œž¯¶zKèHZ‰{)¥ë¾3œ'9y¾™^øöžö݇/!¢º]›•””G¢¼”МJ¥Æ:»º&»¢ÝÝé©Ì¸”4d𤻙"¢³wªzKII…¶”\À ›ÁBP©éúâ€ßW‹ª®B?’%å‘lX�­‚[6®¥Í ¬´ä^ÉÂy$Lf¦vÙP0@X'3ÕÚÖžÊ÷d< ëÂô˜ðÊ^¯×M‚î® Ãä.÷/ý竇O6¶¾@Dß½SãR”””G¢d€,È�¸ÚA£”²•I…ðZ’™À$b7ö£`y+¢¥µƒ;Àúâýç÷ gÂCQÇFeýšåŽ‚‚)ðSSs�9‰Îž¿(e.eæù¤¨­ k5åaª,+â±*w¡3Ÿç‚mÛ}:úOÏmµ<’=à))ÝE)�DIÚL€+` 8Á”a1x()x&™žÞ�XwOÿÔ•k-:Ê…�þ€�w¥˜¦¤“nï…}&<†^Ó4.F#¨eXÅèDRO9x,é¾�!Çx<)¼ùÀ�}_x¢0µvõ{'âÉ ýC±¶wr€£’’òH”€ù` (~Pc?æÕ4Í[^É_µ|qèá6¹‘KÑJ‘OÉ xKÀï¿wÏs¹pzt®5·gz{¢ÃædöÇø#ÈŸ,œWÁ9”ÛÒš)küý«ûÎÒÑ3WO¿²÷ìwˆè_fC?‰’’2$*Üå^�o•2°,õÀ�伨¬(3æÕÕh‘H‰kaC�¯²<¢[C#çÕ×°Çr�‰{tÐü)_Û¹t¬ïz�Èt­\\KÕa ø7uÝ‘. ™+W,Í>úÐ}ýšUU«–/ Ií¸W†Fr�G߀ù•çþ#{úÔI§¦�£ß¼fwLJ‹òÈéÐoKÂýè™ë´ïèEÚs¤qggÏð×í±ò))ÍR)C¢$€ƒ� ¼ Ô€*à:˜IÝáýå_úå‰Gï_]²d!v–†k|^—K×4BÌicÒÑÙM»öš:uâ¨688èLN¦Ù+y`Õ…T˜ ·«¶®~úÞkÇéüå¶³ÇÎ6m%¢¤¤4Ë¥ ‰’Å ¬�‹@X ¼öã.�")ëⱇ÷mZ¹óÚEÅÁj”;Я1'�Š”’ûOŽ;%�Þ'1¶F‹ö�P>ðkW4ÐJŒ¨_ÔPI71ªò;Cèj§ïo?N;ž»tâ\óvÛ�ô’’’2$Jï¢ÏÖ @$mjÁðÔŒ|Jð‚ Ö4­óÃþHâéǶÔ=ùè ¥á‚Z§Cs!æœ1‹Qgg”NŸ9-/]¾aýú±_úù¯_±¸~9Æ“cØœÚ?Ïéݽ´gå¥S“z6g°1YÏäúNr` ø«¹½�þçÅcÿù½ýÿIDFHII¥9&1ãè^›‡À „�ä@|éÒe=ŸúØûªhÓ†HIq>*ÁÈëõÌ©]ñÈ�ä.]¼�˜íÉOˆ†º2zxÓ2*Èû¿öîÈí Šãø4'ÕF®¶mÛ¶m۶ݳm[ªmÛæ9ÉëoÓÔöá}g>5ÒÞlö¿½o­ÆîAn�€r0ݾ‹•[Dœ“w´ÙËØÄ•b•Çqéb¥"�LÐ v€3øCD�îܹ¹HÇ÷ÊÏ?yXÖ«9mX0„Žùo¤+1_òD…Tèr´9ïšAc´~¡§£¹\µ?Åq—.÷Wò@Cvà¯)”*U:dú¤Q×B¼íäÇ÷�ìÙå41L/®ÒáhoÅ´Ñ=#ú´  ƒÛ’éÚqt>bÇGCäbÔNñµ >þñÈ�d¸j �éßJܵ5rÇq\º^¥h}p#ñX ®°{%µjÖØ��òt_¸‡"éÉÅ41LÄP ó±•ÏÛ+¾{Û:Ô Â—Ñ¥è]ï†E°íb:â·á³áh½ˆ&iGµ*'ÚGBVà8Žû *Y -ØA8Dkikïíܱݱw«øø‡çÓÄ0Izr‰B}�nvkS›š×¯H &õ|78.c ˆAâi:—.Eí|7\Bì–ÐÜñݨm“j²Yõ|ðÓ´€ã8Žû¤P fƒ#ì�C+” Ú¶aÙ½'7�§‰a÷àlœñ½b;4¯N]Z×"7£Ybh`xì¢(çd½y2� ÙJ"v(‡ÊŠý¨gûzT³r‰ëØ›ËÇq÷�UJhž°WÀiyïu+æ_¼yn¯ØoHñÃ"öÁYºp,‚ä_x­Š—7ãl�×?iV¯"‰·¸VÍêO§18Žl"wãÙä°cí÷\KöۦҔᨆH�j¥âóäÈl�ÿ‹A�8Žã¸ؘ¯Û! èêê†L7âôÉAòÄÇ){ßD¼>7;ºz*æóÉà rƒ s“»¶®MbUÒ·Sl¤�&«M“hñ”Þ4uDGÞ»µlX‰êU/Mu«•’/”WܯÕòÇq÷ƒé@G°�H8ŽsÚ·iqÓÞ|=¿}2åî…<½D~®d°e%=¿s꣟{týXüҙÞ‰•FõŠÅ¨~�24 kcš" îÝ{!U 'à©hÁ{<šã8ŽûãçN ÃRÊ•3çÉzuj$êÛ�‚¼l(öþÙ?|îã"½ºæ—ÿ•SÑ´yí’(<(°¯×œa,�~�çÿ¬88Žãx ‚u#-PàZ³ÆõcG '«ôðÚ‘?öˆðÕÓ1Øãp¡ûWÿÒïo¹zÚ†¨È>ð‡Í0ŠðŠƒã8îÿ%�Ú`‘¥K•8×±] Ÿ‘É|×zºqvÉþÀ¾É½ËÉËÑ„Üí�p.$›æ—zÓ}O˜—$¾0*ƒ8Žã¸ÿXvèkðX°G±¢EζlÖP¹2Y:*áÒÇßÞ„ƒàú™Ýb˜(T ‡5½º÷souí ÷+]À –Ca^‰pÇ¥œ´  ¬Ä0ñ*V´ðå& ëÊ{ukO“Ç £“ûÿÀ0¹¦|k+*Љœ­wb˜XÑÝKqÕɹïþ^œ!‘¯Y½j­ê­­Ð 2ñ á8ŽKYéB[˜…aâX¬XQ<ÍU9y@Ÿ®´rÑ :±/@¬,þÈ…‹ ÊÕI˜¯=í uoŸ}ó×û¸Z>Ì’%³Øqƒ04€ã8ŽKAe]¨SÁ/KæÌg*U(“Ø»{Z¿r�?þǾŽý—=!nÊ�ýË'¢Þ]�Šß­~Äà:í#ïÕ£k(^�xÂxh˜rŸÒâ8Žãa¢u`8éèèÆ[]ÏÚµjJ3&�"OGczzëÄo¯NÄ'†û9�áÖUä`± 4ž«å öKÇ#•¿æþÕ£ñcG ;¢©©å�× Û¡äKùokqÇñe�E ?à­®ýúúÒõqÖd耞´qÕñD×o¯N¹œLiÛú%de¼9qÝÊ÷Ì 6¾ p?}ñÔAƒÇlÄU.b% f0¤ü¤Çq\êH ;lûL™2í+V´Ðs±:Y¾pºrã—>þòFè«À«“ãÒy¯×¯S]¶dÞŠ v¡øG¿þÉ‹â"ÆsGÂhÖÔ1ŠlÙ²î‡#Á 6ªL}Pç!Âq—zÓ††°bàHî\9ÏtlÛBfºs-%þæçÁ‹§¸V.žI9²g=­úó=a= ‡ á!Âq—ú“@˜ >Q§vͽs§�“ãÐ"öK~ýœ‰8Kb¶k•-] ®!Œ€  Çq\*€¡°ÖõîÙ5tɼ©rqÅû¯~â¢x lù¢Y2©Tj¥ZõÔåÇq\Ú.#Ô……Ú::Û»vn9gÚød?Wszy÷ôO±á>eüð§zzzfâ@$”Lûƒ„ã8ŽSƒÚ0–6lPßcÚÄQ�ì̶ҭóû~øœI2!ò´VtlßZÜ껆@Öô1H8Žã8 ‡¾0¿Pá†=»wÞ¿nÅ‚gg…(pJ]9PpÛ¯ØQ>*üøú1åõ÷¯ÂMÀä‹UÌÈ¡ýîèèêú©®†Zéc�pÇq@RèËÁ*s¦L¶íÚ¶ܰzÑ _WË»nvFq[V*6®^HógM¤þ½»Ð ~Ý©Y“ú‰¥K•¼„C‡‡Äæ=,�æéïÀ!Çq'�r0–ƒø«ìªR¥ªAõªUš6ilVªTIiþ|'òåÍsF]]ý$~þ„ÂjèYÒïj„ã8ŽW&åa,†]à–0fÃbX›a Ø‚ ,‚Z��‡Çq}è`.Œ†ÚPM¥"T‚êКAIÐŽã8ŽSm(u 2èB†OHT2¼ÃqÇq¤Y!¨Çqÿ ×gZǿڬ‘IEND®B`‚PrismLauncher-11.0.3/launcher/resources/flat_white/0000755000175100017510000000000015224505336021724 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/flat_white/flat_white.qrc0000644000175100017510000000424315224505336024564 0ustar runnerrunner index.theme scalable/about.svg scalable/accounts.svg scalable/bug.svg scalable/cat.svg scalable/centralmods.svg scalable/checkupdate.svg scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg scalable/datapacks.svg scalable/discord.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg scalable/jarmods.svg scalable/java.svg scalable/language.svg scalable/loadermods.svg scalable/log.svg scalable/minecraft.svg scalable/new.svg scalable/news.svg scalable/notes.svg scalable/packages.svg scalable/proxy.svg scalable/quickmods.svg scalable/reddit-alien.svg scalable/refresh.svg scalable/resourcepacks.svg scalable/shaderpacks.svg scalable/screenshot-placeholder.svg scalable/screenshots.svg scalable/settings.svg scalable/shortcut.svg scalable/star.svg scalable/status-bad.svg scalable/status-good.svg scalable/status-running.svg scalable/status-yellow.svg scalable/viewfolder.svg scalable/worlds.svg scalable/delete.svg scalable/export.svg scalable/rename.svg scalable/tag.svg scalable/launch.svg scalable/server.svg scalable/appearance.svg PrismLauncher-11.0.3/launcher/resources/flat_white/index.theme0000644000175100017510000000026215224505336024057 0ustar runnerrunner[Icon Theme] Name=Flat (White) Comment=White version of the flat icons (dark mode) Inherits=multimc Directories=scalable [scalable] Size=48 Type=Scalable MinSize=16 MaxSize=256 PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/0000755000175100017510000000000015224505336023472 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/flat_white/scalable/settings.svg0000644000175100017510000000200615224505336026051 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/shortcut.svg0000644000175100017510000000043615224505336026071 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/screenshots.svg0000644000175100017510000000034415224505336026554 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/cat.svg0000644000175100017510000000162315224505336024764 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/notes.svg0000644000175100017510000000037015224505336025343 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/screenshot-placeholder.svg0000644000175100017510000000060515224505336030651 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/datapacks.svg0000644000175100017510000000513415224505336026151 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/status-good.svg0000644000175100017510000000036015224505336026463 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/star.svg0000644000175100017510000000033715224505336025167 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/jarmods.svg0000644000175100017510000000047515224505336025660 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/status-yellow.svg0000644000175100017510000000033315224505336027046 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/java.svg0000644000175100017510000000035515224505336025137 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/reddit-alien.svg0000644000175100017510000000132015224505336026550 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/bug.svg0000644000175100017510000000076115224505336024774 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/custom-commands.svg0000644000175100017510000000060715224505336027327 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/log.svg0000644000175100017510000000037115224505336024775 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/checkupdate.svg0000644000175100017510000000065115224505336026475 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/export.svg0000644000175100017510000000140415224505336025533 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/copy.svg0000644000175100017510000000041215224505336025162 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/minecraft.svg0000644000175100017510000000102215224505336026156 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/externaltools.svg0000644000175100017510000000051215224505336027114 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/instance-settings.svg0000644000175100017510000000200615224505336027653 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/language.svg0000644000175100017510000000755415224505336026011 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/centralmods.svg0000644000175100017510000000046015224505336026526 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/news.svg0000644000175100017510000000051015224505336025163 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/multimc.svg0000644000175100017510000000074215224505336025670 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/refresh.svg0000644000175100017510000000051515224505336025652 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/quickmods.svg0000644000175100017510000000040215224505336026206 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/new.svg0000644000175100017510000000035415224505336025006 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/server.svg0000644000175100017510000000076315224505336025527 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/flat_white/scalable/status-running.svg0000644000175100017510000000032515224505336027214 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/rename.svg0000644000175100017510000000136615224505336025470 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/packages.svg0000644000175100017510000000046515224505336025776 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/discord.svg0000644000175100017510000000163215224505336025644 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/patreon.svg0000644000175100017510000000040515224505336025662 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/delete.svg0000644000175100017510000000166015224505336025460 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/resourcepacks.svg0000644000175100017510000000060015224505336027060 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/about.svg0000644000175100017510000000050215224505336025322 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/proxy.svg0000644000175100017510000000115315224505336025374 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/loadermods.svg0000644000175100017510000000046615224505336026352 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/coremods.svg0000644000175100017510000000045115224505336026026 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/launch.svg0000644000175100017510000000060115224505336025462 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/shaderpacks.svg0000644000175100017510000000506115224505336026505 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/tag.svg0000644000175100017510000000127415224505336024772 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/viewfolder.svg0000644000175100017510000000032615224505336026362 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/worlds.svg0000644000175100017510000000072715224505336025533 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/appearance.svg0000644000175100017510000000054215224505336026313 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/flat_white/scalable/help.svg0000644000175100017510000000146215224505336025146 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/accounts.svg0000644000175100017510000000063515224505336026036 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat_white/scalable/status-bad.svg0000644000175100017510000000043315224505336026262 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/0000755000175100017510000000000015224505336021371 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/pe_light/index.theme0000644000175100017510000000024015224505336023520 0ustar runnerrunner[Icon Theme] Name=Simple (Light) Comment=Icons by pexner (light) Inherits=multimc Directories=scalable [scalable] Size=48 Type=Scalable MinSize=16 MaxSize=256 PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/0000755000175100017510000000000015224505336023137 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/pe_light/scalable/settings.svg0000644000175100017510000000236215224505336025523 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/shortcut.svg0000644000175100017510000000217515224505336025540 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/screenshots.svg0000644000175100017510000000233515224505336026223 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/notes.svg0000644000175100017510000000241315224505336025010 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/datapacks.svg0000644000175100017510000002620415224505336025617 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/status-good.svg0000644000175100017510000000172515224505336026136 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/jarmods.svg0000644000175100017510000000505615224505336025325 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/status-yellow.svg0000644000175100017510000000211215224505336026510 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/java.svg0000644000175100017510000000574315224505336024612 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/bug.svg0000644000175100017510000000271115224505336024436 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/custom-commands.svg0000644000175100017510000002552415224505336027001 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/log.svg0000644000175100017510000000220015224505336024433 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/checkupdate.svg0000644000175100017510000000212315224505336026136 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/export.svg0000644000175100017510000000413315224505336025202 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/copy.svg0000644000175100017510000000171115224505336024632 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/minecraft.svg0000644000175100017510000000175115224505336025634 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/externaltools.svg0000644000175100017510000000217515224505336026570 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/instance-settings.svg0000644000175100017510000000236215224505336027325 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/language.svg0000644000175100017510000000657215224505336025455 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/centralmods.svg0000644000175100017510000000173015224505336026174 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/news.svg0000644000175100017510000000127715224505336024643 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/refresh.svg0000644000175100017510000000156615224505336025326 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/new.svg0000644000175100017510000000176115224505336024456 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/server.svg0000644000175100017510000000135715224505336025174 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/pe_light/scalable/rename.svg0000644000175100017510000000174315224505336025134 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/patreon.svg0000644000175100017510000000170515224505336025333 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/delete.svg0000644000175100017510000000623615224505336025131 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/resourcepacks.svg0000644000175100017510000000213015224505336026525 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/about.svg0000644000175100017510000000150515224505336024773 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/proxy.svg0000644000175100017510000000230615224505336025042 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/loadermods.svg0000644000175100017510000000214315224505336026011 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/coremods.svg0000644000175100017510000000202515224505336025472 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/launch.svg0000644000175100017510000000213715224505336025135 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/shaderpacks.svg0000644000175100017510000000542715224505336026160 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/tag.svg0000644000175100017510000000233515224505336024436 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/viewfolder.svg0000644000175100017510000000154515224505336026033 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/worlds.svg0000644000175100017510000000546715224505336025206 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/pe_light/scalable/appearance.svg0000644000175100017510000000552015224505336025761 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/help.svg0000644000175100017510000000615015224505336024612 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/pe_light/scalable/accounts.svg0000644000175100017510000000275515224505336025510 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/scalable/status-bad.svg0000644000175100017510000000150415224505336025727 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/pe_light/pe_light.qrc0000644000175100017510000000347215224505336023701 0ustar runnerrunner index.theme scalable/about.svg scalable/accounts.svg scalable/bug.svg scalable/centralmods.svg scalable/checkupdate.svg scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg scalable/datapacks.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg scalable/jarmods.svg scalable/java.svg scalable/language.svg scalable/loadermods.svg scalable/log.svg scalable/minecraft.svg scalable/new.svg scalable/news.svg scalable/notes.svg scalable/proxy.svg scalable/refresh.svg scalable/resourcepacks.svg scalable/shaderpacks.svg scalable/screenshots.svg scalable/settings.svg scalable/status-bad.svg scalable/status-good.svg scalable/status-yellow.svg scalable/viewfolder.svg scalable/worlds.svg scalable/delete.svg scalable/tag.svg scalable/export.svg scalable/rename.svg scalable/launch.svg scalable/shortcut.svg scalable/server.svg scalable/appearance.svg PrismLauncher-11.0.3/launcher/resources/shaders/0000755000175100017510000000000015224505336021227 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/shaders/vshader_skin_background.glsl0000644000175100017510000000023515224505336026771 0ustar runnerrunner attribute vec4 a_position; attribute vec2 a_texcoord; varying vec2 v_texcoord; void main() { gl_Position = a_position; v_texcoord = a_texcoord; } PrismLauncher-11.0.3/launcher/resources/shaders/shaders.qrc0000644000175100017510000000030115224505336023361 0ustar runnerrunner vshader_skin_model.glsl vshader_skin_background.glsl fshader.glsl PrismLauncher-11.0.3/launcher/resources/shaders/fshader.glsl0000644000175100017510000000106615224505336023531 0ustar runnerrunner// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // https://code.qt.io/cgit/qt/qtbase.git/tree/examples/opengl/cube/fshader.glsl #ifdef GL_ES // Set default precision to medium precision mediump int; precision mediump float; #endif uniform sampler2D texture; varying vec2 v_texcoord; void main() { // Set fragment color from texture vec4 texColor = texture2D(texture, v_texcoord); if (texColor.a < 0.1) discard; // Optional: Discard fully transparent pixels gl_FragColor = texColor; } PrismLauncher-11.0.3/launcher/resources/shaders/vshader_skin_model.glsl0000644000175100017510000000215015224505336025750 0ustar runnerrunner// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // https://code.qt.io/cgit/qt/qtbase.git/tree/examples/opengl/cube/vshader.glsl // Dylan Schooner - 2025 // Modification: Implemented final Z-NDC re-inversion to compensate // for rigid OpenGL 2.0 context forcing glClearDepth(1.0). // This flips the high-precision Reverse Z output to the standard [0, W] range. #ifdef GL_ES // Set default precision to medium precision mediump int; precision mediump float; #endif uniform mat4 mvp_matrix; uniform mat4 model_matrix; attribute vec4 a_position; attribute vec2 a_texcoord; varying vec2 v_texcoord; void main() { // Calculate vertex position in screen space gl_Position = mvp_matrix * model_matrix * a_position; // Invert the z component of our Reverse Z matrix back to standard NDC float near_z = gl_Position.z; float w_c = gl_Position.w; gl_Position.z = w_c - near_z; // Pass texture coordinate to fragment shader // Value will be automatically interpolated to fragments inside polygon faces v_texcoord = a_texcoord; } PrismLauncher-11.0.3/launcher/resources/flat/0000755000175100017510000000000015224505336020524 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/flat/flat.qrc0000644000175100017510000000423515224505336022165 0ustar runnerrunner index.theme scalable/about.svg scalable/accounts.svg scalable/bug.svg scalable/cat.svg scalable/centralmods.svg scalable/checkupdate.svg scalable/copy.svg scalable/coremods.svg scalable/custom-commands.svg scalable/datapacks.svg scalable/discord.svg scalable/externaltools.svg scalable/help.svg scalable/instance-settings.svg scalable/jarmods.svg scalable/java.svg scalable/language.svg scalable/loadermods.svg scalable/log.svg scalable/minecraft.svg scalable/new.svg scalable/news.svg scalable/notes.svg scalable/packages.svg scalable/proxy.svg scalable/quickmods.svg scalable/reddit-alien.svg scalable/refresh.svg scalable/resourcepacks.svg scalable/shaderpacks.svg scalable/screenshot-placeholder.svg scalable/screenshots.svg scalable/settings.svg scalable/shortcut.svg scalable/star.svg scalable/status-bad.svg scalable/status-good.svg scalable/status-running.svg scalable/status-yellow.svg scalable/viewfolder.svg scalable/worlds.svg scalable/delete.svg scalable/tag.svg scalable/export.svg scalable/rename.svg scalable/server.svg scalable/launch.svg scalable/appearance.svg PrismLauncher-11.0.3/launcher/resources/flat/index.theme0000644000175100017510000000021115224505336022651 0ustar runnerrunner[Icon Theme] Name=Flat Comment=Flat icons Inherits=multimc Directories=scalable [scalable] Size=48 Type=Scalable MinSize=16 MaxSize=256 PrismLauncher-11.0.3/launcher/resources/flat/scalable/0000755000175100017510000000000015224505336022272 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/flat/scalable/settings.svg0000644000175100017510000000200515224505336024650 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/shortcut.svg0000644000175100017510000000043615224505336024671 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/screenshots.svg0000644000175100017510000000034315224505336025353 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/cat.svg0000644000175100017510000000162215224505336023563 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/notes.svg0000644000175100017510000000036715224505336024151 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/screenshot-placeholder.svg0000644000175100017510000000060415224505336027450 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/datapacks.svg0000644000175100017510000000513415224505336024751 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/flat/scalable/status-good.svg0000644000175100017510000000035715224505336025271 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/star.svg0000644000175100017510000000033615224505336023766 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/jarmods.svg0000644000175100017510000000047415224505336024457 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/status-yellow.svg0000644000175100017510000000033215224505336025645 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/java.svg0000644000175100017510000000035415224505336023736 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/reddit-alien.svg0000644000175100017510000000131715224505336025356 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/bug.svg0000644000175100017510000000076015224505336023573 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/custom-commands.svg0000644000175100017510000000060715224505336026127 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/log.svg0000644000175100017510000000037015224505336023574 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/checkupdate.svg0000644000175100017510000000065015224505336025274 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/export.svg0000644000175100017510000000050315224505336024332 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/copy.svg0000644000175100017510000000041115224505336023761 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/minecraft.svg0000644000175100017510000000102115224505336024755 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/externaltools.svg0000644000175100017510000000051115224505336025713 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/instance-settings.svg0000644000175100017510000000200515224505336026452 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/language.svg0000644000175100017510000000755415224505336024611 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/flat/scalable/centralmods.svg0000644000175100017510000000045715224505336025334 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/news.svg0000644000175100017510000000050715224505336023771 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/multimc.svg0000644000175100017510000000074115224505336024467 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/refresh.svg0000644000175100017510000000051415224505336024451 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/quickmods.svg0000644000175100017510000000040115224505336025005 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/new.svg0000644000175100017510000000035315224505336023605 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/server.svg0000644000175100017510000000302715224505336024323 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/status-running.svg0000644000175100017510000000032415224505336026013 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/rename.svg0000644000175100017510000000034615224505336024265 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/packages.svg0000644000175100017510000000046415224505336024575 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/discord.svg0000644000175100017510000000163115224505336024443 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/patreon.svg0000644000175100017510000000040415224505336024461 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/delete.svg0000644000175100017510000000035315224505336024256 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/resourcepacks.svg0000644000175100017510000000057715224505336025675 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/about.svg0000644000175100017510000000050115224505336024121 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/proxy.svg0000644000175100017510000000115215224505336024173 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/loadermods.svg0000644000175100017510000000046515224505336025151 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/coremods.svg0000644000175100017510000000045015224505336024625 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/launch.svg0000644000175100017510000000060115224505336024262 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/shaderpacks.svg0000644000175100017510000000506115224505336025305 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/tag.svg0000644000175100017510000000065715224505336023576 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/viewfolder.svg0000644000175100017510000000032515224505336025161 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/worlds.svg0000644000175100017510000000072615224505336024332 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/appearance.svg0000644000175100017510000000054215224505336025113 0ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/flat/scalable/help.svg0000644000175100017510000000146215224505336023746 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/accounts.svg0000644000175100017510000000063415224505336024635 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/flat/scalable/status-bad.svg0000644000175100017510000000043215224505336025061 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/multimc/0000755000175100017510000000000015224505336021250 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/multimc/48x48/0000755000175100017510000000000015224505336022047 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/multimc/48x48/news.png0000644000175100017510000000455015224505336023535 0ustar runnerrunner‰PNG  IHDR00Wù‡ /IDATxÚíšc�'ËįmÛ¶mÛ¶mÛ¶4¶mÛÖÚ¶í|ç÷¡ûEOü³ÛW؈lº2ªÎÎÌz’<°ë64llØÔ°ÙŸ 8l '¸õæˆüF†­ ;ö6ìgØÿOÂ~p€ œàÇU @%™x†áZí†;þ$Ü ¸À np (e¸ µLЂý•'¸ÁqUˆ7\v “¼[ò¸’Ú¿rßgά§Í}�húˆw:F¥ˆ¾ÌîT60ÔCfùü.1fî¬&ñ>{fƒÊ…黚WÒøžj‡FkÖ¬†Þ®�W%€¤ÁU·z&¿·ž¶ûtk5�Hï#'ÑæöŸv’^-x@ƒÂ¸ä^/|HOgßá!3}F­6|-�ש9³uÄOꉬÛÔ3&C-#Úø¾�Ëí-àV¸ÁquHž;z jxW‡ý¸¿Ìi $/t}p㻺3ùJOÛ�ñ¹Ú÷›=Äó° ùÚòãÍõFÑÚ2½zU!tÜÖZ¤ŸÊ¾pÀw{éÔ c\L˜Z.úJ-|.�:KË,lG(6�ãÙ�?nJ™¾¬|Q×Å] “~;RÏçÞC›ï°>^Àkì�á tJÐÑú¥þ-]qª°<"îM½6àøEsÛtMìù„•ïx&ȇ5Îâ|ï¯wÓn_ãóñƒ¾ßÇݦZؼbsãÛ>×À±Ùbî!?ì«�ËžñM‹±ó8ïXpµ»PÛÈd§Ÿðp=„!è'Yy_2¯CÆf‰Ýꫪ—Óò)ÉÌNåŸ€ŽÆ”?ÿ p0yT•¦Ž©Ñôquê}Ÿ8¢B3&Ôi¦aÖ¤MS­I£*µpF«;îÔfñ žÇžG (cÁüi-|“vî|Ó?í ÉêlJÕ€ö,ÕWι&A㇗iÔ "ñ>¸;WÝ-éš4²BCzòÔ`mGVºóÇ -c¾µUp7â‚tGCІ÷/ÐÀŽ,�X¨¦êx„ Œïù' ±*^]ÍibDYpX¿|M]¥þ홪*ŽÔP#=vh©¸OY®–ÚDÇšÀƈù„Å Î�R"Èö´f0Çîéê2a •qBäpØVŸä¯¬5¬¾°ÜèÁÅBX6·‹0�<3!ž¬+�á;ŒEæ‘4Èš²5¸+Gx£½>Ù_i ß)%îkå¦ý¤Ò¼Psw‘¡üŒ_”›þ³Ø¢ ¦·ª¢0\Ù©?ª0ë7õoËrçó\V¦qÃÊ”‘ô½ŠsƒUY!<ÊDó�‚¬_•ûµ²R~0üè›\Ë"fÁ8÷´MQN`mB�6b´»I Htr…öÙ–èXëO_+ÚðwÖ!$çLnäÙßm´ÍÜZ[mnÎ¥�E �0³p¦h[<«]´!tì�R�€ê’(Hrw�0m\�Zë’ÈDÐŽ0' ý€U A(`mˆHÐÆXB¢¦4Zcþ/ÀB%ËBÊõ&ß¡�Ü(ËßènNGká1_`9<à�`WÁò lžÃEž8óÙ¹Ø>ÉÇâ�ä[ŽAx&´xggB¤oø{?‹°˜óŒ¶TÞa1yB1ˆs#rÅünæà¬ÎwÙá80}¾ `Qb·B2XÏX¸Dqç�~gœ'„ChA OÌœXO¬C–w Dˆ9^äÆã¾ €–%dX”g¬ÊY@ ;9‚v%ú=èë×–¡��V2…SN0àÂ!x¡¾ `€›!ÂIJ,;b8¬I=z”†au,AÀ9€w™�hæ#‚Ï×$fQˆrwâ!ˆpÂQž¶Þ9€eéc«e,¡B_ÿö,DPâÆ8ðA€? DÀƒ²Î¡†,N9ŒC=䟦YÉœšûµ²‹¿ëªª"�Å-¬²”™üƒrÒ~²P gß§¢òÄkT¢–O%¶Í†ˆ2#5þ[•س(…ËUú‚"JŸVPîÁyO(µõ%±ÕÏÑî"©ñ!Âõ@g1O~`qKØjbÞ¬žJøQ¦S 1†B�Ü"�z ¸Í°ßZ ¸û±+tâ‰' œqÖ©/~t3íá@’bév#åìZ„„�B޼@L�%:çA€ÿÜ`ØÇ°é.`Ô bbT¦–µª-�1�dº¥3I‹GðB8 ©pÁ¬‰Õ ŒÓÅ†Ý ›üáÚŒ\NêOʳò›ÂŽ{av�JrC¬Tÿ–2šÿÉYì£ØðϬï7ýøõêjJÒÌÑ ûù…ãt‚a;ÃFk%àÓðûQðò'·èÉ7®wñÊg·Òð`Kee×!lœ“›Ð¢½º$R)qß°ÿñ×ô¾:ëôbf¶êK¾f|.5ìmØÜ°ÁÚè�´‹›§Ï�GeD�˜w :Nßðß>TLø§ÊÏøUýZS5yDž4»Tš•«q¢æ—[ ‡¶v~Á±N¥ko¹À#àü‹ÎÐu·]¨¯b (€ƒŒ$æ´¦vš9¡^$*[æ·_¾"-ì/Ío—æÖKsÊ�|žæ�O^qò‰‡¾d\Ž5ì`ØØ!¿N.¸äLHć¿ÝPIŒÕ!�–Ïë'zQN0òÒ¼#_!Í.ÐÒiizã…Û#ŒÇé†]ÙyœÐù3ðßK§œF˜ J$aù¶‘o2òUF¾Pš™©œ¤Z�ÃE½ãÞ7'�t"wgž}jŸàº“Rº&Fš[mä‹¥YÙÜ<�߯ î�ßNú) Ð6ºJœ¬$±óßHÊrj£o>{E“G:I›£‘=³7ÙdNÜà ÛzâÞ/�¼x�îú*�¶X’ûû¤‡ °$vê~NÜ¥s:EMaۥ攉¤Ö1×ÈßnkiØÞC~]ü–ñ"ú @WÀ„îÿ�ÁÂmbgj« 3òÒÁvy«òì8›¬*îûôK¾%³;ÑWPÅ2? ø‰?J\6³@šB'³×÷…|Íê?¨oÆÎVÿæ° ¶Ö�†ÃûBþOÿE79ÐXÔ…¡ ‡ó}!ÿgþ©Iz³á Ãi䛳Ûô�üŸþÇûö4ìhØÂ[ßôÆŸÛðÍM ŽÕ×…<øùÒ–ÚüßO„IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/48x48/status-yellow.png0000644000175100017510000000272315224505336025415 0ustar runnerrunner‰PNG  IHDR00Wù‡šIDATxÚÕš5�#W†ÿ*ÇfÆœ7²”2çšcå¡Ò�–i´DÇ7Ǹ0ËÌÌÌÌíþK¯¦¤ºñÝ’t³Wõ¯hôº¿ë~Ð=‚ˆ\ŠF,¤Œ"4j!}8{ÈBç …õ�P�=ìî¤wå#Ô•�”˲{¡/Ï"0]„ðT†&Š cªÑdX¥2¨ê/€ô©zó!=ª®ßy Ùx Y¸ *� Ða ZsqÒœ‹HC¾MÀZ9~_)ÃŽÈ¢j¡D+![� ‡DšÎ¦}…Z¼é/2Q D¤9;�YøýòÊðÎj9RWË +ªåR}¬€ì<¥#׉jù!¤Û£ -„P5f#µLm_@~_uŸÎSØy¤FiürÅT›³!m )Ò��ûMéxÿ\t^Ó¥s™Ž rXEc‰ÕÆsH{¾Bt¾âÓFsü>s�9¿vr\GÉÑ^-¤§È�‚Ôk$˜N§˜+A*W™ÕÊõÓ¤LbRª»ÐD!K!2‘z*€™"üÎõ}Ž«LäÈc…If$ZóL²@ýþZ€i]ç§‹°Ãµ�û/9ÐÛÕÆ3H“¨ËÄŽÙ'¼t7�pW�Vm<äþÐô]HTN&"ž£ÅŽâ„gš™ÒSä}’çCk~4 NNœt_ˆÀ3g™Í'ü¢¿4ÿ€nœ8€‘BxøUM•ûÏyŠÑn¹R›…€ 0AxØœ ×ø€šµ €ª&a`ÐÂÏî„àÉѯ»5î<�ÚL ú-¤hÅ$ !㥼Ðßj�€Q €Tie‡ !ýªEÛ;÷¸¡Ì>€t—AÚŠ)Ú -Úô^ 'n¹ó€iëT–z„`ÁᵄÍ?„üû+ä»/ _ù&!âØ´A[´IÛ¯ú³ôØ`ÒÑ�›e!¶=vÞ#dì6äç äÏ?ÿ”’’’„Š6h‹6�<6^Ä­D6:óÐÙmŠmVI^Ã7!?|Hð?Ú -ÚôàDŽ‹@'ÖYŸv«2¿ÕÇd`¹XW¡®$@›¦‹jvö®b µæÀfG€[/¼FoA~ $mÓmžj ]%„X}⽌NÚ�ß~�|òäƒw!¾—qlÚ -Ú<å2Ú’‹[„˜¾ãû�˜™9J˜�¬E·cV<ìô^�£„{”4faÈ@˜‰ì/yO`s˜3aVÿ„˜³ý 0cÇ�ƒÂ±Öœ¬þ;"U§ãw؉iʇLÝ…ìÔœï`ƶyKÁ+ŽïpµIÈ�Ì@DNT Ó¬ŽŒÝ‚,>‚¬?'ëWŠÎò½ègãzM«ë°«U„6þSƒªtÕqÇ…ˆ‡‰�ó?s¯s8fÒìñ2 u:¬ò€yÜ¿Ë1’ücoU¥!E U¥#]m.Áú¸N™ç¶ù,Äk/ËîËÁ —¯IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/48x48/status-good.png0000644000175100017510000000335115224505336025030 0ustar runnerrunner‰PNG  IHDR00Wù‡°IDATxÚÅ™ÕzãH…+$$Ù{˜ïf_ O± Wƒá 333y2fff¸^ð¼€sX¢Þóµë£U“œú¾?féuU««CQ‰Š5)ôjùçôbé zê.¤Ç˺é¡ÛK÷ÜStÇ5E7]^ºîꦫ®BºèºAœŸÓAüfQãýj-Ϥ÷Ëë¨`Ù,y– z¹TÐsð€wÿ¼fÏÀðâïƒ;àÄ_W!þ2¸Î9�'�ÓtÔqŽŽàØ–E%PíÊ\ªY1I•+•ƒˆ/ïA Õ«‚Ú’u% êO4ç§ÏÛñ^ƒ ßKÅÈØyÀ8N8µOÒa{.ý€sE5:Ö®¢æ•T¿RP-¨†ø Pã†0‚Àq �Þx\˜½­ó(€c0qrLÐ÷*ŠJ´­I§ÖU>j^…+ñu éÑ“Âb"d T+H+ŒÊqˆ? ƒíÕÓ)¢¨_¹™WþFM ¨W½[ÅIãX@Å1‹0"'x‚ýöß`b3…5�t¡zК�çƒ ŸÐBšQv�4Â(àq/Ø¢ äw: õ7B®KMH™ÎsËáú¸¤a ~؃‘È6�JQ<¥Ë}r–©õnCÊĈaœó¢&�F< ¨‰=©«ž* —OÈù½T.Ã�bxåÇ ¯{0Çuÿ(ì;õ‰ù§ØwËsé=ßœŠ!¾_‰�ðQ0úÀ�ÁHS¢¿ö€]0“gÏ�ûëY6Iþ´¦ÅN|'x ö‚j0h0ñÖÆiòõIÚjvÇ~á>G¯ Ü ]œ÷1ßn“PV+bÃÆ ‚¾&Aå` ÀÄ8¡ N#A9ú9ú$ž¬vaý2�Õ¤�&Ú’c.>//Ox<±é³Mæ&J¤ÈÓ§i[àðÑÒLz‹°÷ÎEÿñãGñ矊¢BÌ•ëdkDX1ÖÉåïchHYñÿ7Q ù·ïl\2�êHÆ=4÷ܳ0á_þ$,–xs[¸ÈÇ@W<ê€ äê³t�Rˆn¹?GÃáo<^:ƒ›«G™±¨‹ŸßÀ88­úë ähŸšŒ²[º ÊÓ?Z@-èfa–ˆ7¦ó<™ Y¸A²O½Î-_ƒm~ñ üÖ€» ŒZ!Þ81¥Il@ŽB!¡½ëFŸêïWÛ’æ_¾"y‚ŒŒ ¡¬R ŒŽZ-ž©O �nBoê¥K0pô&Ì/þk)^žèÇ”‚ Ì`Â*ñLg¼œJy¼„Æz &LÈ IÎÅË"¤ ‰g† ‘ `*�"4ÕS0�ÛÄÀ¨ßÄs˜šhc,onà”Ó+›ê³ 'áÓ«?“HUSÅ�7ÄÌÌŒA‚‰‰‹,Ï)Ä5À)tÜÑM'y¦91”07±LÖˆ7/⣎B˜tª”yj h–‰7ŸF9oÈ]€c  yžY(h–‰7¿‘íÇíøoe\Öø‹™°H‚ئ§Ã„b#´?MPQ§J„Œ�bë° dë¾Ófþ†� ü°™#0âQd—1¡/Ì Y_,š…K&ä9£<Ån×r!~ÒhDrfÛü#S›ˆ¦>^Î$@>Ç{þ+ýß9¦3Y8v¶fÁ?º�wìíê9˜™–Œpš1™8V†zN;f�Åg‚:ڡβs2ÌÀo2ð[CkQãJ�˜Ï!æF¦�ÝÀ ¦/èæÏn¾+…øˆ¡�YéIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/48x48/centralmods.png0000644000175100017510000000470115224505336025072 0ustar runnerrunner‰PNG  IHDR00Wù‡ ˆIDATxí–�K†ŸÆÁže¬kÛfœü¶umÛ¶mÛŽ�k;¶ÖYÎLÿÓÉL�E°›k¼UO}ã~ßé¯k†¯Q?ëgýïÞš‚›G×L¹ob�Y÷L¨©¾èÅšÿñ}Ó‘Õìÿì;Õ¦=<þFµ{Á Õ‡}ßô}òÍ*Ó^žRåž÷ü÷(ÄV÷}dj¥é÷Oª4·Ž®úV¸aDÕƒî«îÁêtøÕ}˜Ta¾Ïýpu_V§Ã¨>ðîqåæûŒõÈêô¯;—ö»ôÅEæ²—¾ŸXoÿô=Ò\ßùå/¯ziþùW¿¼àü³ŸœûàÉ�Ì0ßg¬GëÕz>äÎi¿äܧTÝ2ª¼Æró¨òú›G–›ï3Öcè÷¢çVË/—dÓoÌL®à­YÉÌ{sS|Ÿ±C¿Ÿ,ʤ¥Rª^+Mˆ’ê{M+¯uÒßHj¥Q_#ZI´>¥ä×ôÌfXïþFƒÖšPR¾ª$Q•¦P7P ’x(šœ"RN!9/‚A²®FÊõ°ÞU£Vaƒð�ð¢ŽoKá‘ÐizIJMt";nЕ%K¿äKo6mFUº'/Ž Œtøù°®6M2¢tSÛ’–oG” Éлp;ŽeÏÌíôXoo¶ï°‡xŒmÊ>¢GbÚAwüùmZÈ÷Þ(m åj¡¥b]‰i�®5lR4�­ê¦p‡KQ�v"±çCt­‡M£ï²qñ,:Åëˆj³nã(ÝQíP]N·øzW¿Fbã¿¢;o˜<2BñnWR´x2e‘rºÆ–QiD+¯#ã!‚¨o‘JvŒˆò(Œ$é+§»û9%u_R¼Ë¹àåÚ[0�w¤¬òM:Ç*(‹Ö_1 ´̶3@²Y¢N§G\§}#UtŽ,¥Ï²—)Ýíld¼Lh‰=VºÏ•”Ö~F©»�²X%E‘z¢Êí` bg@ÔçûѾÅšÏÐI—³¡3™­SOÐy³=)Üöß²>Ÿt2tÙt>à"6ÊŽbÃÌDº‰ÙD’A+‰vÐbÛ ©ý‹ØPâ,`“úçØ½üÌù>?���aôÙv(e»œ d�4ˆ�ÂøBÚBÁƒè¶Û™lQXÁ¾³þÅ�é}ÙoñlSs=Rï%³Z-ÚH’׌Jí‰Ègày&î¸n'Z)‘Y¦åwÑ»îUJ"Ë)è½3±;÷‰vßï Âó1¶b|Á"ÀH "¨˜à˜—#W3ƒLù'¤-K? Y³ŒòÄ�Ìé~%}A ­T­”"��\Îû«¸qlzG‘#cäZˆ{ ü2} …‘jJ·ü±ÎÛ¤°@ZÓ>²µy‹‹ÖxÛž#lE˜ «7SIÓ‚qÔÍ}Ÿ7 ŽgQÁ„Š„@89o¨ŒhµBø üÚzÊd®‘êÏFP¶õ߉mNÅ”lü{R9…©_D O�WÑØ!a?d"è)¡¤ 9Naoæ»;óÅKcœFpW ` Bˆ0ˆç¸9 Âkm>ƒðÒÁsÒá³YôÑ“Ìzç=ª»iáGKÂE,¬wÕ"¥%ÆT+‚@kªöºŽYŸU1mÔuà$ÁMÙÁ,;p ²�ÙÖ8A B[LóûSà¦m¥jáÛ|ðèÍ,Û÷~d¼¨…•ÙÆz—rå¿�|‰…­‰2ê~ó ¾2�YSî7añRKð-Â Éæ Žaòo[†óÏK²¼|¯=“Ú½®Çé³mý-d=k‘”ÿÛ#b"ŠÔÚ¾ôØšìßžaê½O3ïíg`E;5$-ÏbC%­±Õb¯³à÷{– 5óqÞé4ít*ÎNÿiëôPD‘þÏîOh-Ú.â¶°É>ˆ=ÌØkbÉg£Á­÷i¯²b¿ŒOP-öLp­ññêí6�ƒìÒ‹†ñÊ Ç‘Þâo˜§£Ôj|„‹X‹z Øéh ‰ðaMÈ~Mìo×ú!î!Yû¸Ë}j~^,ÆÖÚ•˜àœ©÷I!r ùTÜ�©|šq·=AºO´ÿl¥äš<ë9  Á&£A)kׇ#Oaö”—Øþ×ÿ >Xnð= œ`lÐ:ÈUBf¸ÒGQ–×ÖRß” ~Òý k’õ¨$HAC€œGcÆ$³FzíRbïƒÓþ é�@Ð9PY�–ðW¢D ÿå5 ”"Á1—lýrŠöøåºÖ(éi¤ ¦L#€Ä‘uRõ©œ®OfuC*C{hrÆq »rsÁ™³ÏÇ,±T£OtÖb·}œ•H‹ Â�¬'Û5®õh½ZÏ€�€œýñÔ�Ýl:.q¤í!š®EÆ@¿ÒµäMê\hλ7»ûªÞùv_÷ôt¿þHˆá–Š2;Ù8Æ)ûHáh«Þy1ÙR"};Õ íóÞØ, #œN žšYB1QwGé­Ùí¥tMÔío¤ßËrRJNÿËN.9€„Π‰(ý½.«ã¤ÆÜÞ»Rãs~е%ýe‰û+Ñéþ2Þ‘òþ�(ˆÿµ ûH8�çø£PR&Q€\ªÄ¯W¤V©ÿa¨j‡•„¤„�PAƒë¯˜- áƒÆ|þ¢ÀÒ^ì8¶:~I´'ßžÙ V—zîûªŒÖcI¸`¬v�Ô¢³¿ƒÂx�Þ»þ…âö„¥.ºå@xVÏ|@1ŒTí �2j+šäù…lsvŸTÞz(èãåK”÷ûàúGO…Ìꈹïm†ˆ÷;‹ÇrH¨1Yw@ª+*õkµêÕ¦›òúòB[:5{›h-¦�©`³D˜cîk-²�‡”µ¿|æÒ"o5˜uöM‹GÆ9’{I(0Z³g¨9àS_\6P;à—x™D~ 6Æk÷�î+Ÿ¹­,µètjÜü�|œã8mËežz™Ù–ü�ï3 �¶Kà9Þ_ñ·Qj‚)ÞÕW6ýPEZ$Eø@¥Ðø+y'¥,áÂHõîƒZòûÎN.§)MZ âÿ¡Ü/hYï-›~³ž• :•hÍ"ùG8i0úzwkÁà�Ô«Ó>‹Ý"Pá‹|–}{¬Þ½·.»= NIàV¹¨~òjÖâ÷Tg¶A‚Ó Ùœ�ßjîÏÐIîí¬Él‡X{ÒâEeÁ4VF,¾9¯Ÿ®z. +ñ¯Qr„zË¦îÆš_œª,(âé¬õXËS¾•o`ÊJ¨h¥L…ÏqÀ¨±€Ic¥\½æÿhUÐ(u¸q}3Q»¿‘…]hÅγá.‹ —*0£˜Y\Æ× •JdàŽN{ƒ°]Jε#7w)yaîi›Z®µ&­uÕ�À¬³Í�ŸËÆÿP³v·?­cfUr z¥U�.<è^³’ !,PŸÓý¢ÔOËÀq<^h Ëjåôv^�hsÝCX 7©äm|[$fW"Hñ4V À1@‚=å&B!¢�@N „7óJ!-6çÍ@­2¶S�Vü�•ÀÓRlîk…ƒÐó"¢„'DðK¸Hˆ 'R©�h”‘¼I“•ù< xÃZýt¬zçÜ ¸lî+­Dcµÿ_KYm;nta°¾Ý-ÉL‡ 3¿ÿ3ä6Ìœ¡Ã¶¥îÞ3O­Ub*³ƒæ³=ƃ ±ã ´M{ŒŽÍSÉÛ9§c“ñ2�$J. ðø e× “ãÞ‚=QÀ�`bÐdÒDø~!¿Úµ« !D^ $ÃKžŸ9¼üOøNG7Œê � è!õÄ‚ ¶þú°]ž—&6/ï L"ç´ëøgÀÀ[À Ÿ9ÖÒ€°ÀƒãÐ\èß 7ÛÕr^ΪXS¿àÉ0ªPïÂ[NyxØÍ¥!T� (Œ1¢ªC‡™ÐæتŠ-‘È%1ì�éÕ^U¬È9qìçãýƒO‡ƒÙ�²&Bƒ€"TfÄÇ€ð xèÊÉÀš¶”ÂñÙ!ÃÞˆÅx‡:Ö< óM)žéº–Í�ó�+¿òóåŸzU0J€;îã^âÝ5'†¹ZƒSÁe‡¿�¡—R†ªÓšöÕ\R¼|r�épÎÞü?/´iMwÕ”›HÇ ƒb¨¨bEb¬íduÄŸW/Ü,°4jÃ燗ÿøEèpœe¼3R1ܹ5÷ÌOÕ"ÌÀ÷AÿþlÆ{BóLÞ9äüýŽ<0 š!›WªŠ5f†·qÜoˆ³‰Ë%“JG›ZR»ÎM¶ïv$tR¿~~~ÿU„ ‰õÉ1ëðGQ3íƒO€-`ØsØ‚±ÃtE÷úŠö�޼UðON,vV.h¾¬‰8œ‡Ž€¿Äf¬ËN:=ätå�¹q?QWcšÆ‰'�ASðÉõ1£�B}`T(³DÙ+”Ñ&Äñºà�P$¡ÖPgh°Sa'†NgçŽ/�“Ýðx³m¦[²\¯Éð' °²!ýª¢®„5…Üõú\··Ñ¡D ÌÑ#Îá8á¹ V°¬€%øRh¬ [rÛ²nOYå5dÀy�ÄÆ†ŒÔ!Ks’õ‘eÜDº>�'à¹ãàà !ä€T*�Š¡²ÂK ]µ&ùg¾ÄpÁ?(Å©ÔIQ™IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/48x48/screenshots.png0000644000175100017510000000464515224505336025126 0ustar runnerrunner‰PNG  IHDR00Wù‡ lIDATxÚíš�ÝöÀ÷¶†;“5b'bÿm<Û¶mÛ¶mÛVŒsmc¦{ºï½/ÕUÑìl¾ÍóVåWuê§Ï9mŒ1sZ$s›› ü©ÜT@<ñîÿ^“OzÊPZ *`«Êõ‘S¹^Åýg‡¥g˜£ÂÜ#~Püà—×É«ŸûÿÇt§”yÌ}j£¹"»kV?óÑëGÏS]ݨ.´vZÕU%b× f(¦#3 pCkúa†»Üã1êW¿üqøOaÈgïþðnþÞùÕ/¾Ž°ÓƒØM:›ù»—( ¹iŒÌ!¢°ÄÕÈ™|wpè<£c]sé8ÍÕ„e ØÌ€m{$ tõŒð·¢¾6‹ër átAeÆ'Šì>+nXö·:ü¶§…÷žÿçYÉ×Î-æSç×_.ï»`Çóü씤5£ Q™\.‡H×3C¢�r¢(BNu³5} [hˆ r\b˜Z:ÓÛ©«r)' Ë,†ë1>>†—ªâ õOtÊåLÊÝÑlãS Cb—Ñšÿ49'O;K�RñWÐhZ-ÙAÌÒä ÎÈq\!ð' —p� , M¡¡5-´áz$—¼ê#“NÑ/qRlbO°�\pžÉÈ¡C®a�×ÊÊd™GÃTÛSHˆ¥ rœ�Ý«ùr]k±6¶’1€½~Ì«ÞøhžôÔÿÅhƒåIl#f¨ÄÎSJ/ ­˜ƒ"AL½é$tkq´A}A–„j' 1¹©lvÎrúä1Dv>ãÍÿC9‡F’œýñ[8qð»Œ�cŠ€½H¢•Ay׿Øzæ;Å…E ­¸váeè.¦`!,`’ZÅ:ò¦��:z8}ö,Ÿ}ç‹ÉUײýùÛî=Ì¡¾‹öc?`ë]ŸÏ�o¿—‰Þ>l[‚"!�ATÔXŽ@ØL#ªh‚Ê„éE 9P’0€-™ÇË‚›cQ²ÄD´€p4_C´“áCOkÁKX�Bû†_âåX®Eª:�Ö7mC„„È×§0JÏÞ…‚óß []‹•ßJÚ¶±$d]b" £8BÍE‘Œú\€¾ñ¦Ä“££Ô5ÝÆ@碂ÆIZè"djRø¥!Q )Ž”â¶° ‘–@ˆÊ.©ðjŒà:¨ _¥ïøþ»•¦~ÙNÜfzÛ[i^û/Ô/ÚoàçNý–ÓÇÊèP;'~ùE& ì¤EËpÈí�‹)NLD_Õ$F0†ÒH m –‘Dš%‹–Pœœ$—žWÑJE, ¨ÈðÈ8AP¢®6�ãØôžþ%ɤG80Ì¡C_ {t˜êæÅܺù;þkzöUº >ãØ¡ƒ[’¤Œƒ�L††ö¾A¬”$BcKbëE®ß–Ž”ÂüúFŒ_¬x+QY�ðƒ�ùÍu45VÓÑ5€1P,„,[¶€åÿ¹�©‚ÏÙ¶öî9ÊùÖÿä¿]Ì�w¡š‹Œœ™Âòn¿bECM Êø:- §®ì1‰Ö…h+Q9ˆ£Ò�(ko²¯„³g»¨ªJaY ÏåðÑ XR°x~ )w„'> •E ïÇc¼áÝE¥(Y–ÞÖJÂ>Á�^©„GU&��è¦Á9ÁÉ„Ú` H)éïíÆ�F1Ñ:ʉb (.a­×êóêêæñÏÿ°ŽŸÿê0¥0¢­½Ÿ¢ï“Ïeð<‡ÿþ�-8ŽB <í1#£ýü|ï>NïaÍêf~ýû]„A†@FpHh›`l€ºÆs¤ì.Nû,Æ´ÖL”JL„aÅ ™Ráì-ðäÇße4ÇŽwò¥¯ýšþ�aV­haã†e,]ÒHU6/(¥äB{+}ƒC'%é*‡© HC½M2=�Ÿìþ-µõçµ ön#Þääp|çi´`"Jb%Ö@é,Q`[/m3s­îèuHk[?»÷§p ),þç?7±få"’iŒÀ¶-´±_võòã=ð‡jªHà,Ò¼¸†RÿÃ�ÔÔŸ%á.Ä�Z0ÊE: ql›¨4L¶~JýÏü,>­zZRŽ ¢hö·ÓÇO¶ÒÑ9HsSÍE7ÙÄê ©Ê§‰e[h£8×ÞÍ�vàH[ÝÇÇHDÃ…©xZkü®1|ÏÂmÌÆ¯ÚºŸ¢Ã±åÂH�Èâ$Zp½û‘ÏÏ}T_¸6ˆ AÌŒؼy%«V4S3/O*•`bbÏMðëƒøí�Â>6²¨®O2èû$[ò±Ô)ƒ.)[Ò”ö˜š, E-K¾HOÛ}10±¢Ø–…í¹ØB�«ý"gr&fÿHù_ÿ±‘MëVQWWß ¸®C"áñ‹={ùéoöÄ›7B l¥ nS“¶Ie“x™$‰l‚TMš|}6öÝl.�Š–Rè0Éd©…å8ØRÆ©…Æ– e5•¨•ɤ=ZZV#¥A)C*]MÁŸÂurÌË/aÇ–À‚R’0ˆ°6Øh#ˆÂÛµÁÄ}â4ŠãƒÛáv‰´ò�´,¤h¥°¥‡6°ss(r ‚ig!QÉ¥Ò«VÔq-yþ»a®PÄ÷Ç!×rõþQÑ,,P_·’¿âÍ—QvÒÁÜ¡<ˆç¢ü4 ܲénìßý´Òh£0Z¡.å•Bi�ŽEa´Fiu%¯Z—÷UÄÏ—1êò¸¸¿Ñ—úNg´A]3GœGØUDW¹�xÙÓÿåtWÇѵõ-c0Æ`ˆ3€‰“¸æªú8gÀ\éS–`†vc˜†`ÖA RÙFõö�¾;¯|Ö½þå·®<¾kóŠssIÖ­ž ¸Ó¥#5¨™ƒâÝüÕàæ‡î› ÌqþÁÃ5>´þIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/48x48/patreon.png0000644000175100017510000000115615224505336024230 0ustar runnerrunner‰PNG  IHDR00Wù‡5IDATxÚ혬QEk[ŠjƵßÌvæ[Qm[ŠÚF�j›aÕ¶¡ŠPÛ·»µµjû÷&'\¼ó¤Q¢x�NËÉïpÈÕ·Û§ÔI°ÎË6�Ë�lébÄÈ@t²š¸`]kƒoBúè·¿W @A°¤ƒd7X_Úà'¼éy›0¦D¨è¬-Az$¯ÀŠß†ôšMf(` Òg`…ÞÉ/ûØ>  @�B`YÖ�µî ²­“Êù/@2¬ðÒ=hš]Ì7°ôúXó>A2טÄÚ`½ïgá?Ž ‰÷^€tX r ² {&JiôqŽŠ”lïXf€�BºÃC½lƒ€yŽ˜˜Ê®ÐIë|\i†4Õ½i*X’À÷¬ƒCXì…ÀÄÖ{!0Ö!±Ö鞀Ìr/`Ä„&`d¨{�æ±åÁò<È‚ÜYí<ÚJèþºÏMSÄ«­D¿f ùÞm%Œ)Ö ¼i3�ÏÖÈVyhÞ¼(XOPø0É5ü9RRb#°Üñ±ð/@–åï¡>Æ"ç9ŸŽ’ƒ¹‚’[Î8Ï`´oÀ[Iõ@zؽ€\§t çjѾärj,×ÿ Öï�uª3E‡z¹ëĹŒBŒ•Ò�NÁ~p‚{j³œ2&­‚?·Ó.ã,ÿ m…Ié Ön Í„Ii�¶Ù% ¸ˆk�ð D‘@$ D$ÓÁzòÛÈ¡Q¢üŸy ·X:¥YÓdIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/48x48/settings.png0000644000175100017510000001006215224505336024414 0ustar runnerrunner‰PNG  IHDR00Wù‡ùIDATxÚìOÅ@`öò�«`ÿí48É׺®kñd ÿ�»<@D§›¦.û~h‰83‹0kT51³ ê ;Âè ¢ðwí`€AFF¯ÁLîfðlè­]s€²#Û»ø¯ª®Ð¶“t¤^&#ýlÛ¶mÛ¶mŒ´fò&"´­ô•Júþýj}½’'¤çÎZ{ý ÷ÞÚûœ}þuÃ1‰åÓÓ“¿üÞ÷>û 8pa ñ/–õë×+RžÛÚºìÍW\±»3‘HøBŒp8¢D"ÈÃ%F‘ëÈ1B„à8„ 49¢ˆ'¸¦ây>A¥‚aX|ç;?ª‘ß:dYæ!`nÑÇNvv®{Ïãÿ¼æ‡?ü2êê*ÿnÓ¹.1!å!Ûv΋–e!„‘ˆè‘ë6®ë*‰DŒ¦¦¦–òòê]ssSý@ ð% TÒõl6=!jÎf ˆn¸¡‡þþ3BDÁÐm<×Ã0 ‚h‚ "Bpž°"„}‰*®>¢$Z–B8šbÏ·rÍ5{¨ªª UTT/U€8‹0::êÕÕ5 ŠˆKŠE€™™ ^ýê÷aûÝ(‘JÐT|%!�*�s”2‰!‰Zp ªÁ9 øó< f,ššGسjjª•êjyà @dÑßu½³…B†bÑ ³³…êêkå¹»ðë# ˆ…ƒÑ <Ui ) Ã+p\°ž§"ŒŒL`Y%êëkä·ë«�2%Î+*Qį=…BÎ×õ@@KKí­qð‹W!¦D• jµ FÕ ™‡BÂ+ËŸC×Å¢µÒ âS(K&“q€E ÿß�NŸË %$SÐÚÚÊ’ÀÊÔ´`¡%þaˆG$FY@Ÿ#›-°uÛf"q=è”áB>@tˆÂ)@î!äá{ÚÎò­_Çcû86mÚ%«“ä‘b.•!—Ë.Ú€ðE èȧ …\,D€�ïC}]•täL§Át ¤.X( ¹�l2‰ ƒsyèMÑ0×OGs-¦áñÉd‚¾¾öÞñ»looïà$0 X!.²ˆ…Žär™¬ P•¦iQßÐ@ç²röÝ“…ÉT'¡"IŸ Ï«àù`Ú=Ð È%(ØÔy§xƵaÊʸsï1²¹"…b‰}wÝ„¤ì¼ã8}Àq`ð%À0ôiñ㨤ÔÊRÉ�TWÁŠe�°‚l¹�.AɆH!èÄá`C|>(�í–%OòæîW]þöí?Ì{z@D[fžêê Ö®Ý;thoH&À¢œ={Üܲe×�¤Ô.±+;k!>X&¦ b‚¾  ‚ˆªÂ`Ù>ÌkØÌÚ5Û¹éÆ›ùî�=˜]FmÔäƒÏ\φ [¸ýöƒ2o,‹—Jõ~é€_*•úòùÍëW÷òÚ'\ËìœÉ¿õ5¾uÒe°áQмb*Žç3–2 iPUU¡µµ-_TáûI€s&›Í¸Ò�V@&ylÝØN‰ mj‚@H(¬Žˆˆ�½‡·m>Ç3ñî=Þ˧¿ý~=ÝH®óÑ„ÚY˜;i1†¦óxž�Ì"hhhij€øý"À4õÞ‚i 9¶QÃQÖ.«�°ø\HTÃóˆ ú;ì½¼ëÚr®¼ü�üöú[øòïnå`ÙÕxk!|ËpôyP *â3èÓǰl—D"Nyye%PD݉öî½~rÍš®Žíì¶l€�ëZP¢ÃøZ$!ĽP”¸1ÆÃËïá%�Ø-÷ªùê׿ÎÍ}3í—Ñ^VEÒ8D[e˜e+*i«ŽQŸP©KvP]¹ÃòˆÇ£ÒMå²z«r]'v¿\×UftÓ¤”+0<=Nêð]lÎ�âžäñh¶Îñ‚®�¾ú�ô�¤¸ùæ¯ã+6O¹n+­ IZj“T—ÕÑØØ$zã(jÛõp\ÏUWÎeq�Fc’¶ @üE xË[¾pu"¡=ÜÅçÐoËÑ�¿�åÕ•J##èÅ�uuÌõžb²µ•ª¥KÁÿS>¯–Ö>+5|0vQ› ºðùeÑ^±ãšú¹[Ûy˧÷²yí*vìcO�=uŠÒðűQòýýèé9Vä²÷í%-fã5D²IÝØØðdE÷`p¸ˆrÑ^ZYymèÞ½_Þä®î¼â�o§¦k¹¡!J§O¡Ïïí%ß}è3³˜¶‡æBÂq0“I–¯XAç’NŽéÙcšÖï3™ÔÔÇ>ömGQÔ¥’�»d÷®KUiO§ÓÇzznûÑ�7þb°Ñ{£¯­ªJê†ùþ¶Dè%�|áó«Ÿüt,!V8s]ãùǘË鯪#‰¡è%2ËØøâW‹€µ˜†Å™3g�ý¥é–æötYY¢!‘,«˜ß&5Lƒè|JÕ¦§gRããã7�>süó¿þõ÷Ž:à_”€—'ËvÆ-ý“»woÝyù+^IÕÖ�‡Ñû{1ÇFÄó‚ÁŠÇŽ1^(q°m%æ%—Q/Ä}½€iš,Ù¼ƒd"‰Xˆ²ŠJâÉ$ªF GQCQ-ŠIb;.z>…•ŸÅÈÏ‘ÍåŒÓ§OßÒ×wêÓ7ÜðÓ} BþY/�DžÕæóÞ3Þð6Ó¦xê–7Ä2º�/ˆßÏœfªd²¯c¡=�à’«D}M­ÓpÙü¥²º–dy%ž¯àz*rÎäºÀ]wHO ¡˜çÀ.ÒÛ7`9rà+¿ûÝ>lú `ÿ]oŒÆÊ Óüòæöº'?êSŸ¤êÊnŒ‘AÌÁ~!?Œ!©ÒòRó–tØ)ÃæöÆNªŸð"žøÜ§ÓÑV�aºø¨¨¡yÂ`Û' +ç8‚àX ÑYˆ„–åb¥‡H*ÄVüú׿¹óøñ‹s¹ô`üUoG*]ÛúÉuÛÖu_û™Ï^Õ%5|kd{bž¸˜·Î�œ� 3)ä÷Öv�xôÓY{é*åTT$YÚÙN²"ŠïƒiýÙž(๠).Ѱ‹cèä³Y<ÇÀ³udQÃþý‡øæ7¿ñË[nùå›�Á¿ð:-´"ì:ßø£ölßõ±Oá%*1OÖZ·ÆG1¥æu¢ qwjŠ Ë㮦å$þd6]ÕMuy¦°-uPT*++¨©¯¡®±T Aðóþ·_�F_£ªu×ùÙ×½iÓ§¿Œk˜X÷Æ£ˆï1çE ¢¤RLy¿‹×Ûéå¹råúp2B¡°$]‹©±1R33´/m“{*–¡ã¹.²$•l4‰$’åh¡r!X/:=ÊÊó´¶—“LFE€Š¤Xb±'NÌg°hÊ.h�7À·Û½ë™Û¿üMÝÀé?ƒ#–±Æ…ø˜@j]Z/“fØ�p=Iãç¹â�B82~ÉÖ+Ö®^µvå–-[Ë–.]Bmí|Í)xž·¥ó ‘�‰‰DD ƒ´>à{>r_ì— ¼8`…n¸ 3¸!‘8q4b.ÕæBæ,;‹G]K•j²ÝBH– \USÒžÒ\Ćö_ñ;nûc·ŠdcFWÚlV'û·ˆ�)z/x¤œnúÕëí/½(G7n/q]KË-ò,Ïi,8‚ž…Çј»ŒÆ¼eT꽸Z¥T)ùN]ÆÓé ²>ŠV‹±�íŒØÁè¾KÙ{1ÅØ(˜„Ùƒù ö7 üUáí@¡_¾:L¢k ½ÅçdÕ}KO¢wñ jÎ[îZs1Ž¢4²,,�,LY—‹U% ÈWÕò0²1€B�”7Ý=ÀÈdd†ö\ì�m똔’/ròãcò÷õËW…9Rø|¨WovØY·ÓÜþS­T@1Vš€�…K1ÓÊš’6Ý`P‡&TLµÉÐÁ-ÚüûÏ{âÀÞ?´ÌÍõ‹Wfs�¾^ëë½îÉ׿?JµéJtUSÇv×4òÌÞ:-ul¡ï(6ýäõ m¿r%�›é'/ ž˜ÄkŽ>û-쿎KMÐ�—¿8˜r4hkÚší¶6ŒÆîøê¤öþ’V¨2ÐÖßy—e?¥×æ)Ñ‚ô 9Üùò5«®Ó{½~jÕ£`e˜Â`•ûü49Ó9’ÎL÷õSÙƒ`Q4û¸øûoÅ…›ÂÿÏàúÁ‹@°u¾ÌWz4�rÍÛ’…E(”I@è’Jbzº½|šò6�¦ö¹oëø·íÜ+q'ð/�€DJpâ C{F‡6­üŽ­ý8%ÛÉ8AÛÛÉ2H˜ä¶Ò”µJºóüò>·µÛç:�Ûûçôsá¯?胛wŽ8ò$'ÿˆyš  à¯$«ßæ�×\TûnõøÓon‚…mäR~(§½„;“E¥œ(]/„1 ©Öds虡·ŸKÖ~‘�/.d^(¥/–þôígŠúìðT‹çvj8椛‚ë@]SjúŽäN_J�rηÍj,劋¿Ç¥?û•ƒyµI¯R"�‰NÀo"iþ«w>6¯ÿHG}Cáª)×çtõªRGêœÈ(i Ë¥`€u+›‡÷^¤Í>í‹W¡Ä{ ¥×é /ZföCzYP˜ѯ¶Ý·Ñû[úŸ`�™BWõRi”ˆ TÀÐ.Òîßw­d_ž¸`µPáÏGûE3-|ôÕ'22ZúXV× ®yÇsè­?•:YG*…i&½N\2õ�3<¼\NÚ¿ÆFqã5ñ›ß&ý›„ï%|9`¦!�3aðV›'Å‚olúÍúþso´L¸n»£Í—ÿ‹ÔQݪª‡E1`Æ÷à¡KäÑ]v�(0RÀ—nŒð~ì'‚¯L‰n5P&�7`}d×¥cO¿þNjápP@È PdDž@“f�‚G CÜ­Ý� œ€È@”ÀĪØq98ñ¹½¦Û¢?/ ºÑÊ‚WÇ14²õwTâïEU¨Ê ‹Ü�GÈ,eà ¨T"(!ž*pdäJP46h\xm‚1º�§H7,£e©Üî…°(Cä˜ ÈÙTGØ%äXêNƇö e8%ˆ‘dº“ÇÝVÓ‚,"P.Ø Ô62 ååy ( PGA' ›ºoÖ¾.EšNˆ.ºP$[,¨Vµ-Lc(/ ¤RPÔÖ´Ï!t˜ ò`–¦H  y«E7BÊXQ©¡J0©ô'¡R$th9:Ô^�J__28�"r…©ºɪõŠ¡],I‰ hÍ*)‘§Hµ‰Cœ ¾ÁnL8TV“ôm�aÖÔµ¸¼d)pcŽœ"ý­H`¶]('yñäMÅ„Á¨c’Uy‰©*êRÈ¥ó;~÷ ¢Æc{‰#5`pVpA39ì`ì¶a€M)&rÜŽN¹|ŒK0qO¯\´˜— êÝ#0N7j6}õá”u,<È@Éb‚+6Š+/�dX~4sTìW^.ý™Ö ˜#õ¿Å¬œÓâ^ù¥t‡�ßeÁñ× œ|M1¿'Á¸!‚#$CŠ˜.èõ7¡K*¿¼æ}šGž¾l”X‡�ÅEkÌ%¿NcìQà 6tb�! 1F†™£CÆý0÷²8|ÅéâÄó+–™Z—ôòÝ· ã>¸”ÙðúÓxçí*øÝÏM_{É\¿ãޤ7Þ„‰7Þ”•o¾Ïyã�9þ “çLn‹¿‚7Ý„ÚëoÄáoº)�ž´ŸLÞsä-·ÁŸÖ¼ô‡¯^3½ëNÕ¯O>»�.è�7]�Ðá½×h3333&fŠÍìÔ‘ÙŽœçΔ›™CbfÈÄÌÒÃá¾_y÷ì�`• ZðæªS=<è¾íAÒ'¿yìÈv`îH€¤Ì}ÙAq�i^˜ölÄñbU‹¾\˜Iɽ¨rÄ]¡¬»†^V‚‚Û´üù¾ê}Àß �íä"É@®€O{=|¾ò“žý„Ê ŸÒµõà”ìH°\Û¹`õØs‹üÕ´×{�¿·”ß[ @ÂÞ-¡Ûg2>1‘^÷œ¾”xÌl韔à¼4)î^‡è%ýø À·,`ƒ€–f=XÄíy°ûŽòªgõùŸK7OXmüu cÄÝId&ÑÒŸ é…À  ²e‚4l†dh q ÷•xÕ³:·dànf +ø’ëZ\rý0}ø¾&Àl¾²nS@@¦Ù4³¹³„¶Tî„ØÙÀÀv�æÝæN™{Ž -A@ÚÇ0dzW€q0à]pqüGšÍf0-͘Ø"·™lŽ_@‚LØ´£cZ m›Ñ[À€H›Ã™ŒJz› a›#bWÛ“fT"!-tûCè«o =ýñ/¡NÍfH“©QB& Ì×C¸ôºS¥¾à)�OE¶ÌðÐNÏ@Œ‹ZG_‹C^ð”ǵú÷‹®øøz“Ï–4yÂ#º¿i–›5£bôëéJ·íɳ_Z.']\¬—}é/›IU�vá–)$„#c°‰Z¬s¨Ccö®—?zïozÂêì¢Uì‰!b< èÀÐ-fËÍô¼ß\5¯“®œ»úd{È?/Û,—ë Û�!)AëjY\}ãÙ§æmV§}9sã¹|Ò®V­¹@šqk¥°ºäê…kŸ®µ”›CRW;J;ÅHPJ±f?,pS„èjÅ6™�Ñ2è@”¢�8Ì^k=*0© Có®ëÈ " BÔz°@wx)è;TŠØ f¤D„ ºÂMµ«å ºBLz±}�Q2”"Z:"D§rãa�›Ò¦V´7 ƒ-C­ÒjcE!ÝXû®Ü¼’ÄþDÍ2#¬!°M_ƒ¡e”º'k_ËÉ–¢,Ð6HŒ�°aÒ‰ÙŠô‡âIWN®R‡zi½‘)[L{PK”<È~ªîMê™ùÊK ô�èª#i8Ìh(%b9�ÔÓuZÏžžåbhì’ðÈØ44ºZÊìÄ´œ«�zX7¿át›µÔCm ÃøÒNµ«eöˆ‡v³øâÛ§óqöðÉ4²ÀÛû£ºls ©™¿ü޽yýÚf~ø‰r¦¯¶C$16F«/â'êiÀàÕÏêO ™\zý,Ö›†„1f|lð“5åÙ�ëOT€g<&ožìO¸þLÿ°´ˆ2Î'ü™Ï²5ýë~÷Ðåb1€±%¦{{«×¾ù­g�%�ÿ¬zÁ[X¬/£IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/48x48/copy.png0000644000175100017510000000235615224505336023535 0ustar runnerrunner‰PNG  IHDR00Wù‡µIDATxÌ“ajƒP„¿y-�gér€ô·Gê é-Z) ‚ÆæÁ aó"…äƒeà±ÂÌ®»P˜>š30˜&ó €ãÔÁüÜÅ8NO€…Øs;®ø©k¦óIÀ‚ø7Œ¥n#‰²,ɲLã8²9@‚Bšqaø>r/®g²Šè°=€¤U�‡aˆê'¿>õ_A"„ðb¿�¾_àLÍïCßóu:Ѷ­7ký׳FQäyþv©LRÏ š¦ù>-Àúæ qò]×QUÕ¼ÿÝfÌÔ‰Hz�â6àL;UÌ=ßu ÿqð¦[ÞmÀ™w�Ä8xþh1„aÒøÿ/-踚ƒ’i•]–ÜV¤ð”¸ê³(æ |ì¹qÛÍõˆà]ˆÐ[Þ^…ðøà\g¸Ok„Xaò˜#ô—.p½r!P@ ÿ!^òù˜á€ ňyð™–Lè@?ö ´d@Üt!"Žø¿ ìÐÏ‚/º€"ñàdÌã•]x^ëBfww×ðh;ÖÍÌ‘qps€„Pü®㥇;Àp+xÞº #¢Á'1¯tïŸ�btFàU/N¢ÂËU÷‚´Q: óz¯Å4$æh؉(¼ÃÛûöV"�28F"|^ÖÄ�Ay"¼Þ—YöBŒ3� ÏèËì{!ÿ± Oˆ¾Ì>Bq'|Ofy�iòïð¹+åùÉ9B.¼Ã.·ÍÈy®~ñ×¢C˜ð×Ì9³€uüxŸðktæxphþên”À+¿‰5ËÁ8ŠŸdz�hmë�ÖÁÚx›}‚±mÛF�éÝ¿Ù&ã/69×ø�¿J˲à°"m6áÂ3%?#àð`Œo†é`÷†a09Ïíÿ’Þbtrz°»»k À:¿„oŒ ¿ZQT’ UÓ`èÌûŒÏ' ‹¡£¯¿?=ùM}·€.ð!x÷½Oä5–$ ««k%‘q 9{Ž—.^B8BS{GOFJJæâÂ|€RB8â({®{7ò¢(b~~Ó33ì³ñx7®_ÃÅ‹°´¼ŒªÚÚÞ´äÔ´Õ•åþ0=ÀåDŸäFŸ²_#KK˸{ç>|ÈÞ7 ³ss¨©­éÏJÏL&øV«.Úwê�L(¬iÏŸ;¾¡¡~úëç¯oÆÇƈÜsd>Ÿ�·ýqè�0~së†A&tfäÜù³p¶6àëÆ~ÿú󺣣³€´ßI¾Ib§løK�ôˆ~âp˦°`Z=¶pëæÍ�²©«é}ÿîÛ¾¾þFò¾A%i¤fR%øª!u|K&Ö)¶�H,ÆŽùWUUµdeçT†½;2Û»hIÓTÒn›Ù 1QI¢ˆ$ñ³“ YEç·øs“•¼…]"›qNÁFÄ1„=‚×à•x¹R)/YŸ«e•î×Ï�êÌÄŽÆqUª{¡ í¹ €+¾((|©°PµÍˆf8úB’¶“è‹y{�H«û×:V÷˜ 9š…Ìb ¶ýÍV¢s/¤Ô6>–Qt@ZtC$]í Ba±[ ³SâƒÜ¬�]b>ØÈÃ÷­ËÍ"GŠòÕ÷BA徿’;<VM£jüÒŒ�,ñâ9~4Ÿh¦©fc5ˆÐNiX±D^Bì^ãyüs”»õUÚµ2$Èzî…¥‘O·º�«îq~ØàÄ”m9ßYáœåÑL4’D³ Ô†\"«¦Œ ÍB¶.3‹,"E±ê^¨ª¥Ä�²Âö`º¢¯j]Ô¢4¯4®P¸�†ub¨„X¸|Ø ��€­¹¹Œ.ÃÒðƒ¦w}…æráƒÝÆ ·Ý¶ÀhÄ�k¤E€Æ»y’˜œ¶˜tCTÛõàÔ™XĦ�Ao€�Ìç[F¶äÝæÃµ;Öyñ�à�ÃDH‹ÑhC4šI¨­Ë“˜l'±’‘V­ Ñ*8;séÉ™u؆u$ÿ‡ÂB÷Ö¯ÿs®;�œ`Ôî7©Q5_…H*ÆÃmI€˜ítÅý£h¥ÞŸü$u"¶ã<\ŠåÊ/²žæß‚7Áº‚“rIZ…¨*_åºàw oØ‘øäŽ/5J 0>’+Ö 3!‰Bã¯{yË?ld‡/6㜆-hÄô4ÿfÌ’ûýŹ B7H·.ÎøEÂŽ‚SÓÜOšÉ-¤šÊÌ.»šÛúӅ摟¸lù‡g¥Ùñ8ÇâB¬„£ß ÐÛ<.hþ*wè–ÜúŠÉU!’œõ9ÕÚÐÊ•ÆI€ãC yÁ)ivfƒ“qPЙ˜/½gÐáÀ6ü ¬¤\ø£Ü7e~ÝÝ>´:ÚçšÃ·¨Ñ¸üèw@l_Z^>qv~=¶àœ€ó1WšoAO€0ó„»á!‰K° ç☔CPxúÅ…77 �+k!ÏíãužÖÏ-k$%D©T#,vóI’üpý–c–³l;.ÂÅØþFÉ×r†¦Àp4Ž ‰Œ°[™ûÈo3÷X—ûe³�§Ig*â:ÓÕ–Ëó©3W.ýØú­¿<}¥qJ9[„ Ö öHŽÄa8 Ñ‘.^‰?-<÷û™ïž³!_MIÞV£�mÎmÿùrrðw—¿_(Š3p4ÁYX G~-ghzŸ!éx¾†wàÕ0ÊéÓlœˆ¥>,Œ¶È¶¶oŒlÙ˜+ý[ƶà|\n¯„k k«ðý'��‹ÑÂ@P 0‹ù Y,u´Í œ Çáp@P Š‘£‰RdÈ;Zæ?p7Üû¢‰spNÂòÎVìlGøoÄÃ5°R\бù—Ö_hRßdr.›IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/48x48/instance-settings.png0000644000175100017510000001006215224505336026216 0ustar runnerrunner‰PNG  IHDR00Wù‡ùIDATxÚìOÅ@`öò�«`ÿí48É׺®kñd ÿ�»<@D§›¦.û~h‰83‹0kT51³ ê ;Âè ¢ðwí`€AFF¯ÁLîfðlè­]s€²#Û»ø¯ª®Ð¶“t¤^&#ýlÛ¶mÛ¶mŒ´fò&"´­ô•Júþýj}½’'¤çÎZ{ý ÷ÞÚûœ}þuÃ1‰åÓÓ“¿üÞ÷>û 8pa ñ/–õë×+RžÛÚºìÍW\±»3‘HøBŒp8¢D"ÈÃ%F‘ëÈ1B„à8„ 49¢ˆ'¸¦ây>A¥‚aX|ç;?ª‘ß:dYæ!`nÑÇNvv®{Ïãÿ¼æ‡?ü2êê*ÿnÓ¹.1!å!Ûv΋–e!„‘ˆè‘ë6®ë*‰DŒ¦¦¦–òòê]ssSý@ ð% TÒõl6=!jÎf ˆn¸¡‡þþ3BDÁÐm<×Ã0 ‚h‚ "Bpž°"„}‰*®>¢$Z–B8šbÏ·rÍ5{¨ªª UTT/U€8‹0::êÕÕ5 ŠˆKŠE€™™ ^ýê÷aûÝ(‘JÐT|%!�*�s”2‰!‰Zp ªÁ9 øó< f,ššGسjjª•êjyà @dÑßu½³…B†bÑ ³³…êêkå¹»ðë# ˆ…ƒÑ <Ui ) Ã+p\°ž§"ŒŒL`Y%êëkä·ë«�2%Î+*Qį=…BÎ×õ@@KKí­qð‹W!¦D• jµ FÕ ™‡BÂ+ËŸC×Å¢µÒ âS(K&“q€E ÿß�NŸË %$SÐÚÚÊ’ÀÊÔ´`¡%þaˆG$FY@Ÿ#›-°uÛf"q=è”áB>@tˆÂ)@î!äá{ÚÎò­_Çcû86mÚ%«“ä‘b.•!—Ë.Ú€ðE èȧ …\,D€�ïC}]•täL§Át ¤.X( ¹�l2‰ ƒsyèMÑ0×OGs-¦áñÉd‚¾¾öÞñ»looïà$0 X!.²ˆ…Žär™¬ P•¦iQßÐ@ç²röÝ“…ÉT'¡"IŸ Ï«àù`Ú=Ð È%(ØÔy§xƵaÊʸsï1²¹"…b‰}wÝ„¤ì¼ã8}Àq`ð%À0ôiñ㨤ÔÊRÉ�TWÁŠe�°‚l¹�.AɆH!èÄá`C|>(�í–%OòæîW]þöí?Ì{z@D[fžêê Ö®Ý;thoH&À¢œ={Üܲe×�¤Ô.±+;k!>X&¦ b‚¾  ‚ˆªÂ`Ù>ÌkØÌÚ5Û¹éÆ›ùî�=˜]FmÔäƒÏ\φ [¸ýöƒ2o,‹—Jõ~é€_*•úòùÍëW÷òÚ'\ËìœÉ¿õ5¾uÒe°áQмb*Žç3–2 iPUU¡µµ-_TáûI€s&›Í¸Ò�V@&ylÝØN‰ mj‚@H(¬Žˆˆ�½‡·m>Ç3ñî=Þ˧¿ý~=ÝH®óÑ„ÚY˜;i1†¦óxž�Ì"hhhij€øý"À4õÞ‚i 9¶QÃQÖ.«�°ø\HTÃóˆ ú;ì½¼ëÚr®¼ü�üöú[øòïnå`ÙÕxk!|ËpôyP *â3èÓǰl—D"Nyye%PD݉öî½~rÍš®Žíì¶l€�ëZP¢ÃøZ$!ĽP”¸1ÆÃËïá%�Ø-÷ªùê׿ÎÍ}3í—Ñ^VEÒ8D[e˜e+*i«ŽQŸP©KvP]¹ÃòˆÇ£ÒMå²z«r]'v¿\×UftÓ¤”+0<=Nêð]lÎ�âžäñh¶Îñ‚®�¾ú�ô�¤¸ùæ¯ã+6O¹n+­ IZj“T—ÕÑØØ$zã(jÛõp\ÏUWÎeq�Fc’¶ @üE xË[¾pu"¡=ÜÅçÐoËÑ�¿�åÕ•J##èÅ�uuÌõžb²µ•ª¥KÁÿS>¯–Ö>+5|0vQ› ºðùeÑ^±ãšú¹[Ûy˧÷²yí*vìcO�=uŠÒðűQòýýèé9Vä²÷í%-fã5D²IÝØØðdE÷`p¸ˆrÑ^ZYymèÞ½_Þä®î¼â�o§¦k¹¡!J§O¡Ïïí%ß}è3³˜¶‡æBÂq0“I–¯XAç’NŽéÙcšÖï3™ÔÔÇ>ömGQÔ¥’�»d÷®KUiO§ÓÇzznûÑ�7þb°Ñ{£¯­ªJê†ùþ¶Dè%�|áó«Ÿüt,!V8s]ãùǘË鯪#‰¡è%2ËØøâW‹€µ˜†Å™3g�ý¥é–æötYY¢!‘,«˜ß&5Lƒè|JÕ¦§gRããã7�>süó¿þõ÷Ž:à_”€—'ËvÆ-ý“»woÝyù+^IÕÖ�‡Ñû{1ÇFÄó‚ÁŠÇŽ1^(q°m%æ%—Q/Ä}½€iš,Ù¼ƒd"‰Xˆ²ŠJâÉ$ªF GQCQ-ŠIb;.z>…•ŸÅÈÏ‘ÍåŒÓ§OßÒ×wêÓ7ÜðÓ} BþY/�DžÕæóÞ3Þð6Ó¦xê–7Ä2º�/ˆßÏœfªd²¯c¡=�à’«D}M­ÓpÙü¥²º–dy%ž¯àz*rÎäºÀ]wHO ¡˜çÀ.ÒÛ7`9rà+¿ûÝ>lú `ÿ]oŒÆÊ Óüòæöº'?êSŸ¤êÊnŒ‘AÌÁ~!?Œ!©ÒòRó–tØ)ÃæöÆNªŸð"žøÜ§ÓÑV�aºø¨¨¡yÂ`Û' +ç8‚àX ÑYˆ„–åb¥‡H*ÄVüú׿¹óøñ‹s¹ô`üUoG*]ÛúÉuÛÖu_û™Ï^Õ%5|kd{bž¸˜·Î�œ� 3)ä÷Öv�xôÓY{é*åTT$YÚÙN²"ŠïƒiýÙž(๠).Ѱ‹cèä³Y<ÇÀ³udQÃþý‡øæ7¿ñË[nùå›�Á¿ð:-´"ì:ßø£ölßõ±Oá%*1OÖZ·ÆG1¥æu¢ qwjŠ Ë㮦å$þd6]ÕMuy¦°-uPT*++¨©¯¡®±T Aðóþ·_�F_£ªu×ùÙ×½iÓ§¿Œk˜X÷Æ£ˆï1çE ¢¤RLy¿‹×Ûéå¹råúp2B¡°$]‹©±1R33´/m“{*–¡ã¹.²$•l4‰$’åh¡r!X/:=ÊÊó´¶—“LFE€Š¤Xb±'NÌg°hÊ.h�7À·Û½ë™Û¿üMÝÀé?ƒ#–±Æ…ø˜@j]Z/“fØ�p=Iãç¹â�B82~ÉÖ+Ö®^µvå–-[Ë–.]Bmí|Í)xž·¥ó ‘�‰‰DD ƒ´>à{>r_ì— ¼‚9¦^€ (ƒêÊ£N #Žš >böæ�ï…ƒp^‡À+8¡ùQ åP EÔ™OÝö?A‹?`öà vÐPx ¯Cæ…z·J½P …Ô��û—ØMFWà>µüa�ë×-Ìeˆ@ A>ä$kb;]·÷»Ô*GáYèbýóc²z¡òÐ’•èpÚÂä�7n•Ù‡Ô½¯[™çZr A½ðFÛ¿Tn`pëû­6O}Pæ¢ÉÌ ¥�*ï+Oº{P­^È…l´y—X6¤ŒuÚœ6©ŸøÇp6ÃÍDzJin*Ï9•á3qIC)r �Íî°+ÙHVkg½`~ñ�4¹B·.]~M¼«^üOi"Ê3Pe<ò›`(kAG6µcs‹,'ÁJµÎËÄ÷‡!C†Äªªªbýºw�Õ{LH¼KãÒº<.¯+ÃoBykLÃÒÑjñÁ¹¥ûbÎ#˜ˆ-‡Ë ˆ ñÇ��}üø1ÖØÐà„z"ÐòïÒº<.¯+c@&Až d¢uTü�“cÊBÂ6Á+Ϙ�oy'àw|úôé/QïÒ*dB=�=õ7𿤙�5SpÙ;_‡¬Óžn<›ßœ‰K�ø  Wö9Ï<Úiš�ŽfsÁ¥£3|›£ãï£f2Ga³&Ÿ¿n^¬à¯pe¹2]Ù®Žh3î@® d Ù]ŠŒsúкx¬L`ù ´¦×DÒAiÕk ,Ç š�Žv›b6ušnM•È» &g"ñþeX¬Mdg`ª¹{ê]ùÎ*Qˆ&Â/ÇÈ@»q½;5Q‡§«J¢‰ðÄ‹söÀ)ciz:^—mÏÑÁ³1%gBâ=ŸŸ[�ê�§nKûìÖ×ñð\‰% —¡š˜¬ÞÉ]¬¼Ñ$ë|&ê�ÏVÇ—ˆfw‡4PC7Ôj(Üí C(;~U2ªõ2p©#Nâr–¢J]¦Ot€eôPp-1›Z¦7™­`#[ÜÈ ÙŽKô”1¥%"�”p¢"w0ÒƒÒÝv~˜Ëæ\ä›í-Te{ÚùqZÃGÇiE®YJ¾nÿÛñ…¦²‚èžcö%O—çÃíðJy0~õ ^)]ð�HŽ. 5mG—ú(T�Z_—ú@¸§ þù6[[õ¶vô¬²5¾õ›|VQ0®22ä4.´õÖ4äÅ-�N£5îÙŽ×Óå¶î¶áÓâ](�:Þ§EEŠY¿T³7ir\ת/ÓBuFâÄg Éi³DƒIü#ÕuÔz{"|îÅ;-É~Z3ÚløX2§@ªöˆó­ þ”H¸Ž àý€Ãob îeÄØêÃ# [ 7^ëè G›ÍÛ™šêg¤8BµSe¦2Þêv.ïd{eûÖÙæì�—;Ë_þÉ5@P®‹y !/Çq <àà¶§Þ–¡�!Šù"ù|ž|�GDˆÂˆ0 ‰¢ˆ•ú 츀_øcv­ì�ݽ¼‡a> Ć߸Á“·ÜtË›?ûŽ”{j,ï"æô;œxî~ö=˜Þ8MWºT ëa8¦lËÌ,Ìðƒÿ€__ökÄc«xúJà¬�nâ)/ºéK>yÓ�7á£çä `Ë‘<Ã^Ë~U"½ÇÎä±·z<åñ2½5àš0œ&¬†|ão_çï;þŠúò÷8Ÿ<˜å‚AïÍxúª·ÜâíœSù?¼âûw~/ÁËÖÁ¦›ü0çŽÎíŸÏQ›�b±³ÈµÉdq’¹¹9Þ÷«wÓr�Y“7÷þ  9y›zúòGœòHnêyú¯ŸŒ }ËaðBy ´ôǽtã‹îùR¢|L-¬‘¸äZM­[C}ån§Þ�Ý »—[Ë�6¾lŘNJð²Ÿü0Î<úl:q‡?ïþ=ÆÊ�í¬M®-Ô¦~ó¬co`xû³-s]³À"�»Ã“øÕù?/þyçï¿*>r¿Ä)S§²ÜXf(?„UÁ`M�R�$-¾xÚQgØ{Ýú>Ì·æ¹ÎV±§¾‡[�q[ntÌ�ÅZXi®€@7ìb�Á²¦@®ž›HÑ�ÇJã¥;ßâ,4X/&1ì™ÝÃâò".vX,ÆrùÅR‘£§�&&^]\8ˆùÆ<(÷“¢Ð¡ƒEP1k øíø»& ¦î§‡°®p8‚æfæØrÙ¶lßB'ì¬yÒ¥B‰“Ž=‰ãO8ž‰É‰µR”ýhšÐ„xbQq‡(×ó÷OêÑ­ît›»ÑÐ)ká�Q3ÊO~ó.¼òB‹�fÔäü­çsÁÎ 8zóÑÜàÌ06<¶vyM“Ñ�ºxX·Æ3 µø�c¥ †&‡éD]Ö¢ä—h.4ùܯ?G‡dHºFØØ¨‡Cdb"?&ñ꽨Uv,í`÷_vs‹ë߂㦎[«|—N£‹Áô8H`¼“{z·‘œtÆíΦ֩³… À9çžÃ¿¶ü‹ Q¡yø]‹ÆŠ:09­€S¯-Þø"â!G{"Fó Iøó¥¦V9ýØÓ³Â‡*Ÿ&‹àŒá �¤â^21º‘ÂH‰0 9¾õ¹üÒËW•°#Iˆ½ ÞwÈëÏäçÀ€©Ë„&zwBî.%ñwÙ ulH4æÀÐ碅‹( •Ø4º Ü7à@E±Ö€UY%0i 'všññ#ÇNÐì4Y‹êb•¿^ðW2L@>ö�"[í¨÷@àfø"½ œ¼­;~ÇîÈ�Òv1í#²‚çΞËm‡nKÞäWÏfŠbÅ€K- ¤H,O ‚òÈPÿô9þzîþò9c)I@nÜûê¢×:1-X.N Êöòâ^IS†0rîܹ„&$”^HgèzÑtvâÆ Þê×à>å�aB¡¡r(vÎì¤u„!SÀ/˜­»[µGÊ: JÞ×» ñÉÍëG0Ä@&2ä yÚ´±bA¸�p ‰@xbIN9…`ï9ñ飓eºa—CaÔpÅö+È$‡(±ñî}$å]%ùfg>~Hë&!�;Å�€D„7Át~šœË‘Ä I˜€’’®O*�ë:gH ±0vBÒmÛØ8¢(âPÔªé \F [¶?.a�ˆÓÏ·f⇴ïѽŒ8aÌŒ1e¦È%9èBF¯ÞûV«Eu¥J½Rï縩a¬Øý±FÇ»XQ+k ,­,ÑÇk ´<ýëdrxôž3î}\ë.!áãFìÙH.ÊA;-.±`Ä`Œ¡Ú©R]®²¼´LÔŒ „@ I¬hì"2M8!I@�®)Ðh7 ø`#Á©¢Æý‚urï{œúµÅ›ÕøÜÊß þáÑumv…;0‘ôb�P�}³ õzúŸƒ�ÁK|Äž5Ä¡ÃÏëŸÉ\”­Î“„Žƒ°Úú±]–p9ëä¼s_ZÙÕykábIÀPS‘X�Z1âaðK&ð°<0Vð€ÉÉßœò* ¾… Q…jµ‚ñ=Œ1Xc+ØÀ¢(.pà“JøŠ 1ŸHsíâ¨èNU¥²·Bì;È€˜¼éOÒ ®®Egô,à"®!.¯Öx #zDβUEñ0ĸʧq¾[% 9E÷(qßáš È#ñ®8ïŒ8uΩ ²/~/£I^�xf-©l…’«œB –1ødÙ\%@ðÁ©¹3ðA®IÛ•r¯ìî®I£ÆRÕ„ª8i�HS“BlU«š$½a”®è�2^Ô¥»-ïy±§Öêüj‰Ð:FOÈÍ$÷ŒF¼Ûâj¢wLîtO*Ånd ¹!"§9§ ¡‹]äb?´I·'#’wÚu"—Ix¤D2(ûs=Hshc…>Ç)¹f`ãnòµéÇ1Gˆ)É�r�ÜdrŠv\SÆñ¹7ÂýQî áƒïw·wïöI4ôà‘¾jé*^"]YiK#ݾRÒïi"ýî2”ü¥&2–’±à72æg�!÷%ÒçÍ�}OX¼S7‡45)¤©®&m“¶‚IW•A¥Lú½Ð“¡*‚ P”ËdÊ©†RÈì›CÃh1MÃôn}€4uLšZ¨†I*ˆRhŸ@*ùBòY L¹P6”ÅdÈä{ ~½òVp.½³ßOï62½ÛÕ³@dÇçË2­maÚ|Œ)-Ä”Þ)’×›Û™ÖfZŽíË`:?Ü ÈùüdÝ1—b¯óIôÖÁ5ôNsˆÞib@pÂdZya2ƒ¦´¦Õ8^µ ä ‘e×jräÿŸ†ºÆKéíˆéíf&€i3Þ¯k#1ÑúCL޵ yÉá½’ï6_Ã{Ñò‘ZÞŠ´�ÇXèÅU 0�î…¿!v”“Å=f€æk`¾±\Ò-.-oD~oì�“ÅU›[©£ôÂN‡«Ÿ=ñvå) ^Éqäº@˜`>¥3þæÕñ‘XãÞ0“u{i²O£>CS÷ $³Œ@Xš•A:ŒJ¢Pé…í€ØöIæ«n•iQæw@Ñú©¡žO°.çž8Δ�«öž=wQ·á(=¤J\YY ´Øt¼çƒoie:ý<¦ÇS&®cÂæ‹ *ÈñÛÉÔ%´åo’\M« @¬8Òû�pÍ5×ð]wÝŧOœ‹ÞòÄbu�šF�û Rƒ^ðþk˜@°,¬u}O•3f ÷Ýw¼aã&ž3w.ÿ{ÁÓr²XÈÕ<1OéO�xŽ„®ô.ˆÁ±®;ö ;;›C¡û|>Öéõ|Í„)L÷.eJi�-Ćj1ÙÜwR$ô{·bñÅa­‘Ž––þå—_ø‘Gá³ÆÎbzû‡ö”˜€<_‘Ö8±@¬:4(5êêê8ã½÷yáÂ…üŸY·3-+‹ ÄšzexŽŠw2/$Yòîa2 -4T‰ÎÎNÎÏÏg³ÕÆc&LfºÃΔÒ6ôiÕº3 Àdv- ÒP1±@8ªeá¨ÑÚÚÊ’V�>ÊçŒ�Áôæ7CƒH.d† „*éC’j©˜iy0¦jÔ××óGÌ7ÜpÿoÖLK÷ `e•2Ü*$ X PpÄ@ I«Ä¤$ž2m&ÓÍz¦Mý?§h]“:�]È/ ÄæöxH´µµqff&?÷ü |ᘩLÏ|4€ó¶¨eÈ ‚"µ#îjƒAþâ‹/%­þ?õz¦Äü~ d`?¡°n Dzç°¨QXXÈ‹–,á©Ófô�V𨴠§’{”dQT³@¤�‚J�N!K ,[ÚFá ¶ø=d°@¬?8ò§ÑõÑÓ¨Ùÿ!ı²v\È*ÃæåB¯b¹ �\2 –ù e)aݱP g@ �d �èÅœmGÔbKR”kGˆ5�#{9m�ZNKXvl‹’òGpA“£¤�K š0À](Ï�òm–”ÍjëG•”(�¥P¶ Ké–Ñ9bŠzñ’$­/RŠz%,ž7åK¡„VÕö põÕWówÜÿÛ*«jÔÖWn«¨�›Eø¢R6�ª©´ùȸ±u”ɾS5ßÃ�-òêVl Š2u�°[‹©Ç˜²Ä|tî÷v×'؈# ɹ0+׆a•œ3)W5¹?¡>ãmÏ)dszÉæâZcØÌK¯'çE›Ï"ͧQ¿Âì¾ åG@}ꘈ›ä Ÿ˜ïÿQ{!Ž€ØqÐÕÕ2­Åeª\]#K…hóòӠ˜yÌÿ.*Hr€iCò46WX9VR¶VŒ3zÞM¦ÌK†ø3ë·'‘=s ̇ºY['9;¨<_ƒ}»‡p.—ú3ëÐ�‹ƒúÅ|´ì8é" ôULëƒL[ŽJé'õ+„×ò¾“õ¼ P›Ü˜R%cMÎ�sÅï¯V×0€¸{¹º‘»¹TäØÃö‡k §@ÕÑý‡“}SäX',¤Wœ3Ðz/ÁL^ÿs¥xÝuˆðZ>Ãw² ¶•}bÐÚÇtF†)ˆÑJIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/48x48/help.png0000644000175100017510000000670715224505336023517 0ustar runnerrunner‰PNG  IHDR00Wù‡ ŽIDATxÚíZ—9—½R�«Ìl7Cº‡g2¼ÌÌÌŒ–é_,óXffÆp23ád:ÔhÆBIۑˉâ8ß—Í2è©�÷>лOn‚ÿAÐhU?_àÑ*ðï4þ½h Y`=‡„]†ieÓ�˜Ð BBß ý|·�n½ìt „Ä%X, YZ5ÓégÌXò3ž8`XvÕˆ%ÓTÓLA( 8cž; ýa3tWÙppÚï7�;¤s�ó;úØ&X(Ù…ùç´Lös¹ê§äË‹KÙr5žÊg¨�J¢›p9AÈ"8X t†p{=1hÕƒÞÞV£×Ü; j/õ»�—Ð>y€ @üG âˆ?ùBª¸øå™Êü̬>´P^^4R…„iÁá~(ö'G×ex!@§†F`1„Ã>µ]Þ¸z¹×ØÞ8<ìîü�£ýpj@ðA@*±ùÕÏMåæ¿cní‰g—{ØJ+BGÏpƒ!àBÈ}w"` „%†N75dLÀ èl^çÛÏìv·/üY¯ví×Ñ;ý>÷þ@ÝßÐ�‡×â‹+ßZ\xâ‡}ñã_{æi#LæQwºƒ2p>aB 8pý Œ 8GÏði Ùr‰ËåAìIÆØ’ {Ãí=þ¿í&xkyé»*+¿ûÉ�ÿÄjéÀiZƒ`d]1F<Ú !ä AäcAÈ!"㉛äÄ(Ôúž€O¢T-ëµV˜¬:†µ‹ÞÖfDâ� P`fÞ^]ûîêòÁï8øqŸPHÎ-b§¢ï²$ "àê~ì à#«CñQq!0ô9¸ní“(P�šsaß™s¸µ�¡$> ›\úø¯)/>þ½O}Â'”³ Øî†pr¼\•}>òƒ ­€)¦ù¹Œš¨TöI:ç :yÏ—á7vð-�ò÷i¹Ùµ{òc?q5»¸Lvú nÈA € ðãÇ¢�´pD`*‰±'È�"†‰J)GÃA°0ð<7luOýq¿(òëëéü�zè™�ùÄÅ'žÐj.0ôÆCÜ6sJ‘† €©ÄtŠ˜AahT‚ ŸšärL†0m ÅL¶: žË®²áÕK¼û%�N–ŸýÊÊ�'¾å±_H ´8ÚÃàÎLUD $ØbÊÄz%�g–2x~5‹g÷×Çæ’X*ØHÙx”´Œ …„Šå@@dÞär)˜ ÉnsÏrÃØQ¸;õI/èSÃ*ûô²‘)~ñâÃ�æôT­n.n›J�{�¥”)Á>¿’ÁúLåT ¶IA©>¸ÑÃK§ëx÷rKþò±' €ÛG-%�5¤íiáÆÚÇ:µOñÛ¸  óÑ<�4ókŸ?³üä7¬?óŒÝELž8Pc]É�„©á‹Ÿ­à›?iO-fPLš’”r¸>—DmC“ÞY«$ñø|JZøZÃÁÐgwY_»I€ŽH0¤Rqh,4»µm¸¡sn»@ÜË8X°“•O®.­fôD ½î�q¯—�#�¦†�¶‹‹;}œÛ`{ïø\VÜå¢�g—3xd6…¥bßô‰óè:!þüøÚÃ0 ¥q2ãÖä\ ç äçfi*7÷̰±û¤‡� ؽh(ëñLþ¹Ò\•EÀ‚1zuFÀeE}ëbKÆõÕºƒK{C´>¼(Ö)!Ò¯�kâ›>aŸôHåt Ÿw°„£WÚè9}0!¤W(p›$é¥b:�ÜÜB±µwùyooå%`c€˜FÀ6õü#™âÌL"›ÇžÇ ¸Ra'= d—wûà‡˜�sœ¤"J”¾ÇÐHZÖª ,âX.ű²?/ì ÀËÅ#œžÐP¨Võ«Fâ ²¹2Úµ1 u`)aZÆã¹R9Ó„0@&ì´JKÆ ¥úx^Àð¢×0.ä|¥î`§í,�"cë ` 5|nïAÜP ™Ë’T:¿jkñy5wU;m˜‰•T.G©å•„“•Vcb“S¨ÅmˤœÖ÷' žÌ@(`#ëCñ|&`Ø6ìl!Ï©6so�–Ö,«b'ãÄãò‹'ÁO-dÓV!”=€DLÃc³IÌçl@­ëáFËçŠõeOÉhJþº ;™Ži†9 6�€•NÆìxÚˆYBð-ƒO�P¬®j"¡*! Óñ±²øŠgQJ›p|†7Î7eH ¡XŸ`bÊÇFÆ"–mkš¡� ™Lbb37®é1‹êØ-I¬ÈãIõ U>O€�š—„¥ãÅÕ,¾åçñÔBZVâWÏÖñ×'wÑWw‚;ó$ªÈ㔌4àzàу L“PBÅ-¥�™ŒkÅúj‚ãøˆ¤¥ã“Éïƒ_دI™ä/Ÿ©ã×Þ¸Ž‹»C„lD’L³> zBN hš p:�€Ñ}‡àÜØÉjžDÚèÙå4¾m¿B?<“”½ñ_ŸØÃï¼³‰‹;ø,’ä“Ö�¤„ê‰q}¸ÌT€L¯ÄeNz.g Z ÓÁ+ɪ¸ BÙ2ÒGŸ°Œ‡ªIósr¿þæu|XÊüRã\µ:€ú%’ BAÀú€Æ§%1‡×ëžÛ \:%Jب ‰R&Hb,ðTâ2æ)%8~µƒ?<´…Ë{Cx�&OL†�’‘>ÒÀá;CÆC^6¼i…LÀ{¾7܆ÂÐJÆá!"Ð$òÄô¸?F °X°‘Ošè»!Þ<ß”1ï‡\i'„ÜùØ„62u°Ãaß oWi1'4Ü|çÃ^«ÅM" S ¨–ž^(à¡\§BÐw™”ç¶RÓLmt rç)d�ã`Ðn´5ÏÙàÝCÌ]†þú¹N­æ"ð–¡Ë»iyõdWÀ«Œ !““q‚WÎ4p£á`à3œÙìIRP­‹;CÓ<²¸AÐmµ…Ó©_uØð:~ö‡íÓíúöö ÕZKægÐv‚;{]¥�ï¹é�€qéB��Ž7ö†´þ$%Š•1}?~ @34X”ãòæVèºý“h·v?†zpÁÉ5�×7wWJjêô–f�B¸^‚”w?b„"eéø¬'Šø¼§ÊRþÙ±¼y¡)œ‰Ÿf}5Ĥ‚õ{m46¯7ƒ^÷pµý‘œj:ƒüµŸ3»¾’ÉØ)8¾ ¨•!BÎÁ8ÔŠŠÙœ…/~»–ƒ#y}n«�­–{§Å`¼W�‘ø£H›[6E§~ã”ç÷ߟ¼7¥¸{ üAóÍöîõ“µk7DÖb:w^ÒÚ>“}nÔ ß}Ã@ n©N‚ÑžÞ%“ÕJ‹)Ö'ÈÆuð~›—ºÁ°õhŸ¸€}´žXÀÛuh¬”`Â|1_­ÚÔŠ£Ùà…\ÆyÈU¡GTðr„’A<¦Éfç¯NìIÑé4î} %Т8ªº�¸©£§¸~ö<ÿðƒ£owk×~Þîuâ~®U_Ó[Œé¨‘x¨8S¥.'xcòª>¹û²Ûg\*Í£¼~®�Ó›}Ùz`T‚—�©$dèT3¼Ú.Î=´Óܾô³¬vè-îýÞ 8;+™L$³ÅRµHÜÒ ÷º"?$dˆ5ú>šÑ0¯“Ö§�«N‰ì™í`€Þ9ìn]þà÷�k»¿ ìÖîûf. ½øÑ öt&“Mä‹9ÙÞùÓI(�•”bu…%ê>Ê*5Tšø¸püD¸ñþ‘Wº;×~þ‰sØÜN×½ Ûßä”ZŽÃËd3VqŸ„Ï ¥±¦QÁkHïÓ¬%¬©STÒ1¤©�‹'N²‹'kîlü4:G�Cç@ƒ¾ßÖ®xœÅܾ»žN'íR) ¢éð.îM‚N€ï'c?Ó1“5‘`Cœ?vüá£�ÚåŸ@íÝ—tˆ%‰ðF;ì�Ë�àþ°×[34#Y.¦I2a�2ƹS¬/ANXÿ6pËÐdÈÌ$5ø�=œ9rÜûðƒÃ¯7› Û‡^ÐQÀ?•D+ì:ç<Æw�NsÖ…T"¦•r)¤âæø4òˆr¦+€5ùC•=r)C%¥Ã ‡¸qá¢8säh}óüÉ?ìn]ù4޽  @ü{ÿJI¤þ…d¥ú•éòÂç׿J‹‹z¢P×cp�]p 1?*†¶Aai@Œ0xýê[[bëÒ¥~sûÊñncûü­úßïmðÿ£'6�êL¬ºú‚•È}¾�™ùøtu~!]¬XñLšÚÉ$ Ë‚®ë#/†œ^Otë�°±{£Ý©m¾çw[ÿ0èn¾‚îé‹zøÖ/õdDd¡€ÒÜà ;ó±â-;¹nÚ骳šiB€2ø®ë Ú~¯w=t»ø¢Ìë\ÿí�kQ¸„ÿUÿ+A¢\Š I \@Ü/À4‹1ʨÒÀ8±°¿_GÃom�jñÿÓó„(ëxˆ)ÿ±òÿã_´½”8—w;µIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/48x48/worlds.png0000644000175100017510000000721615224505336024075 0ustar runnerrunner‰PNG  IHDR00Wù‡UIDATxìÔÌtφñß3gß��YÛVT»Q§vÖŒj·aݘuãÔŽjÛíÿ³_왹«MN6Im\«Ù½3×èÙñþÏßF=ì…ïƒg¦í¼v„V¶ÛFLSsôÈ¡ã¥þÅÔ}žövÈý`?>13˜Ñ05:&EÑÂ�¯}È·tÆç¾ø(ÿbV{—Ï�¯ühÏ/N_VÅ„A‘°*¾ó³r›\Mµ�D2µMH£Aa`°|_5Æ JR-@püèjïW¿¸êþø@Ýé/€Üù��uew�*!E…*VÅþ ‰£‡'‡WÍÔ %ECkS•ÖèaRª–Ò\UT•U›> K#€p0¢T¾û�¯íã(@Ýþ!Ï�Üá¾�¶¿Þ§aPµ,¢B ­˜GT"™�>ŒÄÈf2 VX 1Ph‰Bªë°B©"1aFU9vøˆ3?û*ÀÊ&ÜŸg™g«bF6GzÑBB‰fveoß|°gÌæÞ7 "¢ŠM‘Ðe2ÌJ‰$lÜ#� ›Û;rÜÉ«~a�¤Êú`OŸ×ÚfòshÅî²˱{û»v/Ÿwùü/ž7úlŒ(!Œ ¨B!( QH�µ4‰ Usøè1WNŸ´Àª÷Aö®\2ÏkÁNÑ7“îƒ*Âò�>Ïöw/¹pæ*¼×íÝçî·u°.š?H)±@¶~+ÄFb=Çú`~.^uó>r�›ÜU²ÖC 4Ì¡AÄ<ÏÖûWœ=u•·¼é¹¾ò�“zx2 ñçÓ0K9Ú™üòg˾:{ò*°>ü-™‡BcYXê!éï4÷¼Ûœ9{EúÐ@Ca Y>�%+@!þ°c­\õÓ¨¼ëSß¾ï=ï|ッýâÏߥºçÎî:uz×€Í-؞ؒ‚áO;V;‡|þ#ï�‚zÔË?”ÛÝêVÆè‚`B‹¼¶vlaU¤JаíØbËR[ý–vØi¬c‹��Ã>ñÁw-%´··gjŒD‹` LÈR^:Z†…Ù6 ÐQHPÈÆ¶µí¨Ä<¯¬Æ@È( ݶ´AA–¼þþŽl;:*4µÈƒ cFÄ’m—ÐÈ"…ŽñOp¶KZ¢ =40M¥÷¡£a 84±î–£/¦0Šñç8ŠCS™G£••�£³‰–mÇ܇�It « ‡ž¶Ât†R¶ïãýLH1Uˆ?쨈¦µöëæËH’îË⿇ÌrÛ›­mÛv8Ö­ZÛ¶m}¶Æži”�|ïÝ�¯§:&£×Þq«*uë᜛çPÙ¬±u{‡©œ XNƒDè�k'V˜™Ÿz¤¯ˆAÁ?Ó!­ËHñj¬BSÛž2;�¨€üøûŸ­¡ ÅëO_¥F¼ÓÛpjr‘jµC½Öc”�>ý‘ÇZÃ¥¿yƒafu޳ït Dðã&ãeßÂÞàöÉa?±Æ0Ku�4Aè}5œÖlÝÜ¢{óïta…|1ÇæV‹ÍíÝn²ûìt‡X«ÑJ£”àÇüò,‘xþøgÿ†³ïy‚¥�ćô`÷6g$‚Rƃp<ŒŒý€  x@¥{õÞyáq b ·ž¹Âᙘɷ®ñÖw:„2†K¿ñC/p»:`iy’C‡¸{}IÆh 3EBœ:¿ÊÍ—n±y{‡·¾×9ŒNÐø±î–:<ú¶’š@€‘pdœð”€ Ù«!(mxîŸã�NÌ`bËý­6‰º}lj3«LÎOPî:2…˜J£‡ÎFL¯ÏÓse-·®=àòÍ*Û›MV, Gž'ÿäy¼2’&ñ8öÁ%Èc\›=L+p¤îI¢Æ>cxîO_âßi�r­Go˜[œ ÊXº�„™ùÄ1M1ñÂË÷(ÅŠÉ¥ô¦My§Eœ8ÚÇ™ùÃ$áÎs·xí‰KDZí#ñxLª+Ki•R§aY àÆ) *7·Y/(‚R´ºŽzgÀ‡}ÄyºÏd!ÂÅ÷ÿÄÜ.w9sr™ß(csî×ú\¿¼ÉÒl�r¥M6CÜ‹›¼ÓùEÚGxù.3¿š‚�Èc\%ûÄ•—”´Måþ·*cçe�áÆ³W™ž/Ñ&4ºNžXfªïævsÈýú€�6œ;»Ê‰‹;¾@6kX˜ŒðÆ23_dva‚‘´{#²Ùˆ¿ù»ë<õä âúµbŠÄé™ ÀïS�0¶˜{]&µCNÆEh/?q•ã‡gh´‡8¥YZšäã>â wwºtŽ\.f$ðžïv„é©‚æ=ßý?ÿ[Ï“Œ‹Ë%z‰g§5Bk…� ›[5ú­!xPîrüØ<;—“wž² cR ~L ·¥Ô`ãq·ICÇïS�!2Ton²qn‰­j�õ3\8¿N¥ÙØüÐy:Ï�µi‰çîV‹ßýË+,¯ÍЬwét½QÖœQ!�ÖšƒÇWè5;Dq$C¿ùuÀG[¥™È�(°€Y€ñw’28‘õ/˜£À%Žgž¼ÎòR‰åÕIînwX®÷˜ž+à:ý#/ä²–™\Lµ5äÕ›5r¥,wˬRHd0 Äù=rïN…�s(£È³„‘S¥Ù‰°¯Ü®p¹ü ’ÖÿÈ>œ“ÖúéßiR7ûärY®ß©Sí%,¯Lq§Ã©ã‹ˆòD‘!“5Ä>0HÜCòÞgu>ÇíJ‰"‚ÖQêÑ7 Q ½&Š,ÊÀÂl‰­­¥é¢yö¯_x«]žÌÊÔ‚B<* öÙ;•†Ë8õ>ߺ]½ó:Ç�+U29î•»h«v/*�Éd¯Üh@d(Æ–�x·¼q§Á¥û-´¢8¿W\B~"‡‰,ïvq‰ßú‹¶“Œä¬P>$û´ßÃŽRæ†1¬ÜÞ3zíK‹%^¿ÓÄæ3\»Û¢å!—�øÁß~�bÎà�£s”[l”"[È â#´B4(‰ÂÐRœÌQŒ ··ÛX6›ÅZ³n}’$ âÓË�„”<”ìÉáÔ.ɘZSÈEìTº 1C­Ùnö8rbžáÐSk™(•v¡±Ùèg,•^Ât1"k-å»M¢\„öÿH>ªí:2t{#òqž×n5°‘ç>‰íÙƒ‹œ;{ŠÄbÀ’2Õ!�x¬w‚È(è DZq3ñÓ?ý…ù¶Q˜ÈíBç³¾åÏøòO{ë‹E~ë™;d#ÃK·›\«ô1Z³4™¡‘X : ö0J@9AG%rùµ[˜™Ì0»Yë°Z2 D)r*£^p°79"«Nð{$šZ߃ pAÐAxñF�‹gùÆ_|�/ÿ¤‹¬-Lð§o”¹Ýqhe‚Í!õ$"‹ÖåSØt‚(aÐîò �êÝ Q»ƒuC”1W,J4 +àS-2x…’4�(^C0·4…ø@l5C¥‰äãˆðí¿ý Ÿòž‡¹^í±º8ÁNßá�%ŠÊ â=$à�GkÁkÀZN*ñÆåm|§BÒ’órÑ‰ë‘  Æ€gЈ(àq Ѐ_D¡Ð{¿Ñsâðgpp9�Ég"0JPIÌ+÷Û„lÄú\�ɬ%FáÄ��]£Ø(nÖ‡üÚ·ç�aI‚VQFvîÝkî4þê`�_ãã¾õ7©]ïÀ Z�™ Ç½Ù¨«0FÐ@¿¥ÐÚ N‘ `ØQôÛ j€ í·å]áï:AD[£"­ñÎ1莸±ÓãÎrŸs+%JV3 °œÓPÔ\)¨6zì”›˜]n‚‰3H²µíýÁ;WöûÏüýrI Ê©?½:HÄ•rJYƒóB¥9à÷.×xÇJ‘™¬`«ã�;õÚ³6•§ìMhudð îºõÆ;¥ ùŸ�?ûãßyöÌéÈ×Ï ïr`’÷8:Í{šâ#�LPŒ4Þ >@¹3äÒN ç)ÒrÏa õê~õV«¼sx‘}aøŸ�ò³þUßÝ휙¹q½òη*}î4‡rr©¨”Òt†ž—4)i(ˆExöRYFÕ¶â~Y2�Æ7 4ùgBES_ÄÿbLn9ú%RÌV4U:¼ql�ãgv_f7´i×{´¶ä]ÿõævó§¶_yýû�ÿ#*œ_�Zù¤O-­}ÆWÏú¼o™Þøô¯+-}ÄgG¥µcüâ –SxìÑûIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/48x48/log.png0000644000175100017510000000324015224505336023335 0ustar runnerrunner‰PNG  IHDR00ý 1 gIDATx¥–…n,ËÕ…¿Ý8=0ãáÿ3Š’¹Âˆ+offA˜™a˜/“y|Æöxšj¥Ô’,Ëž> [­úöªjõlíU ‰à¯ýaöãÁ›£˜î¾cø÷l¼ïÍ?|M€HžÐÄF3‹˜¨:åG¶“Á§p�àlΉs‚DÓ¯RÔr8ÒNÿ}ûŽO=@Lfz° ÂÓ©¶AVü¹|ùKè> ÄÃ4Ñg”2Né åÄ:¥K¤’˜žŽ€Ìk#ֈسà”ÚÇŠ…ŽÈ0fZTè9»Í�2Û%c¤ÈæüÌ žQn]™�éQëp¬Usž I|Lmžgõ2»Ž45f%P#o‘0O‡Ez4—!ë$$äô‰YÄ|�9c�»¤ô˜PyÂBR ˜U�S&@AÁ€ÐÇqt{=¡¤"Äa7î‡LÓÅ1JæÒV� Є)‰rº<ÇKõ<ÄL3%W‰0srŒè(gÌ<ûZã� ¨¥¶5XTÐl·Â"†JlAª-¤RhÂ$3‚†›5F  Af„µ% $D€k:ˆˆhô%Æ€hˆp¨í²ñ”8&Úd�UžT†´ÀÐÆóX7”„f[d¾Ô¡rÏMoX­®9NZIx~óÅT^:Ï9Ûf©2×|—1Ւͬà�1sôxš rr ×Ù!Â2FTÌ1¥çuÝœòë<럜„’ü—Žx  Ùu�RÆ\Ó˜‘©‹S�.û:¥¢«ˆ„Z‡@ ?Ò;,q¢!û¬kÂÿ#µU`™"‹èÈl¡ù8²Ð÷À«�u)˜=+"¥3•3q�R= pÊì˜H2ײLø;ºœàXæúL‰™¶é‘5çö˜J 6Ùf‹†Ü%" hî¯ m»­±.þ?À+r‰ÿº‰.½i±HÈ*�YL(3‡5÷?¾˜‹‰\úû%àòá�©Í¢]ž"äŒ.ò\åß§ä”$ôy  hèÎø} m ´Îæ…Rk^"»T<€“Ì8hº×AX©ÒEVª6Se†„Aâ 4"™ç©b‹° ” �µY$öø'91)Gd¬Ïã0J2nówR Ëÿ$ Ï1{D”„Üòs}îr‡Û³-(dQËäz9”êð:=Ç�¥ìñZÅlËxÒ:Ï’h…Œ’®Œ æµJ‚Ô¶–(°S[HÌT‰9�ÌïsÅVÓõó=2V’z}Líµ´oCÌÏu¨åÚ,B#ö%úŒµ‹x)�)ôú%Œ™hŽš¡¶�B …œ�{î+zžŠB1)kmçëëºe”ºm9"Õ¢õtË–¸®ÚBðŒ.œƒš Ñà|´ó7¥Õ0{‘¡ bJí�#ŽHèÑ%ð hœkƒ o´XÄ<ÃKǧÑml?h-éˆm N “«ÃªÔN.�\ûu-•*7˜rª33a Y;¹4×v] 1ड़7†Ôfþã2333,,33333óÇÌÉ‹cÇìñðhÄÛ9åz?FU©f$]éöéîÓW烿{•wÁ–™››ûºÉÉÉO Ãðz½Þwï‚-Ë;yð/�¢è·ƒ øøb±8›ÉdþÿÌÌÌg?ôÐC ðð{e'NœÈ7›Í/j4¿˜¦éçès¢V«áy£ÑÕ�ÇüÊ+¯|b¿ßÏ–Ëåg€è½¢€ÕÕÕ/O’ä·Åô§åóù9YAÇ,Ùlß÷988@çfêõú‡är¹ÏQ©>ßaEœoú»�áíÜ>S€¾^ÌÞèº.¥R �Gìð��*• ²“©¡�µl—Õ^- ß üâ»E�ÝÝÝŒã8_¦ÉMà?¿ÛíÎÈLLL8b` 1Ììì¬íjb¶¶¶^{NŸVäp8œÒý¥">ãÊ•+Ùv»ý8�¼K øÏ°ßWŸ¦É§4™�Õäf—íím._¾Ìþþ>º†l…Ø·s{{{¦ŒúY`Ö:<Hûgêù}àÉ·ÚBŸû«¼Ù8Ôö™²Ç×ämŽØ·É5™ ´Z-¶¸¸h€äy;V|iiɾ_¼xÑÔÑ3ì~==Ë®I ÔðvNj¾ q?¤ã_‚·K�B±ðùbø÷4Ág+Qæô`P­V¸zõªÙcee…;î¸Å¥Yãœ�=sæŒ)õì³Ï¢ûí^5¹Ö8»W$ " ÙõÕŒ”øHpmî1ðØ[]À‰ó‹Ÿ\.»¿)k|¾˜šËÆ�&G5¯4KhÜkÙVAD k`Ž˜6ÀÚ­É5Þ”šžž¶±²&m©P{¦æ1e5®qòäÉ›ŸŸÿDÍמy“¼ßçÞþKç¦~œ/ô†sšE$’ÿµ69ò®Vó½–M]7æÅ¸‹AûÔ»_62pg…‰UTˆ=S ZÁhc———mŒvûÞétØÚÜš)×J3w¶ùIÉA!ü÷u4fËÿùñB©ž§Ò,±0?ÏüÜ‚å·v=55…Øz­=žþy“_ŒYóJ%³Œ&6æ(ï[Qºf ­sV¼±óGkÏ׳Œ$©ëAì‘­&´÷»<û×›3ÿõG/~0ð-×°rËÔ§õvýéAËÇ-–I�˜—/¿ÀææýÞÀ"RLêxÓÀ €}ƶ&0ÆT f«µµ5kNe»lej)�ìE°�xS©Ýî�Ëæ¨5+ø™ÝýWéðï¿ù"OÿÝšˆÉw�ïæh˽¶’b6Í–SüQÈ ÿ¹Î ìP›-ðàûÝIÞUäíuL‰r¹b…¤Å¤l`ß/\¸`ÇR%‘1*Ÿ(6åþíßþÍ PQvßÿÿÿÿŸÿóþ�)ðܳÏÑëöï±¾²õtŸ�'z´Öû†aé†rÂù1ú™ßõÿ8Ú^åqËþûï»·V¤7>dã"<ú§ùÕûg¯Œ(éüÜêµB…8I¤€‹Ó…ÑnGM·# IŽlÍà: ÝvëIZAÄvk›‹/¬C9ÃìÇÌVb±Q�¤qœ2r<ðêiƒK�m³þâ.å© gn[æÔ¹ŠnÞ#%ZØŒõj¥Ê8ñäË3UŸåòÃ-žþ—5v.¶qÒ”æ²gì"MVÉf’aBÜ Eü=ap2DyÙ(NŽ/`«="�C¦Ôä+žbó…CÒžCþl‰^Ø&[‘¤—ƤWC‚û24+.“µ kÛÛl¿Ô#RÉ®sêÆEægi·ºLMN3 ú$¹�Öv‡Çÿâ"»Ï=Çp/¡çhÖÅòd†°£~Ê)ÔJQHF¾ÐÂi©f•zM—Î`LÅL7*Ç÷ÀdÑýò̓Á´jeiºÎ]7-Ðïøl®uÈ ÄüU�èj@V–š¼{Š*N‰rIʯ-4=^xìU^#õº�ΨÐì�$„‹ÿ¾Ç3½Íþ³:ÉÌb�d#4«f „/Ž)L¬YÇ—†R=$õÊ•†='—Ëâ‘‘Ö¨¸Ö×)pç¹EFã+´ÇtÀñÖ˜dÛãæ[X¿Ü¦s0"wƱætŠzh1¦pó.ÁT@Y±z¸Õã•çÿ“ wž¤T.ðšÿ:4�J9&¦\rN–„”ØIÈUò8‘C€/•%$�.Éïe~U6ôéE>ŽT‰e·öÐ;ÞBnAÉQ-±yУÝ÷åw§13§´)X\®ãs œP�:`ð=âSc–ï]d…yÚ×"˜Œ™;1CY ‰€­?ܧuyDu±@Ù) ,Œâ±€$8y¬1G òà´UÔ8%ÍêÐÏ2Žv Ò˜FY÷æsÈ>Ú#rÙì±XsTJ5Y™þȧÝ(*çhVS¢0d¯=Tƒf™Ê¸l?6¿äÑmdH»¥g38O%ôV=œÕŒ uq§Š”¦C »Y"�Ì7kDµ˜Ñ¾‡[vIóZ”p¥‚«8öd©Hª*Ô�œÀòykÞ¼,äà�ÍjÞpËp´í¶øaÄê\ƒn\aº,©‹¤ ·3�l~rië�-�sÔ„©wúOôYû—ËJ‡z®Ä¨=Æ/‡ _â �¬öò€Ä�eIŸðJd \�›š¬Px¤LDšƒ¢ÀÖÜ"g—g˜nV Äü@„bÈS†ãàxz4¦Ä@Ÿ·kißõØ>(·c*ŠÇÉú ­î�ÃØc~¾†¿9TÄ•˜¼ýÊã-‚]ÙoÛǽ=Ïài�¤®þ�-ÃÊ${—º*xÄ„@�.Ž˜.¸dªŽ7"…A^À –@µ’Èp/À!º×RHçj•âñ)taqúËå¯é‘°+Ð9å·Žp¼6nÉ¥¯‡Ýtb–{.,eq¹,œ”lu†LäQ-%dÇ9ú¯Œ˜›®1ô=v¶Šõyðú¬ÌUƒUZ;ËwOªö†> S5Î.Nb?™õG¦�ož�‰"›Ë”(Õ²¥ÐuŠ“”ª[Ð^´ÌmëA»=Ÿ®¥bN7g¹¸yÀ^gÀÝç—¶@I…5]‡­aØïÑlÔX9Ñ$*¦”ܬŠ�¸ëQ ˜ªå)K*êšï3–DIš2Ó(³0Y3R{#Ù% IR?,B™Ò˜iCAßUÈñ ܲ:ûåj’iIÇÖAÏÞk0ñ°t t¾¥ zš`c¯Ëüd•ÛϨOÒÄB÷2öCÙÐÀ‘UzdsxŠ_}‡™Ê”åzùäu^êš�T”X,æzv�Å)¥_ÎÁËV se‚Qçx²™Œ�̩ӗg̨�©áqy³�ð hµˆU�˜ÍÖv:ÆÜ�ç—U¤k¬‰<1ærjaÒ|Ý÷B���UßWŸ…à”')rb4£{¾‰5Ðäé ÇÖHò¤MòÒú>O]Ú¶kJ ÀŠ8³4I ¬ívÐDÆ`³æRk›]NÍMÈ.¡šÏ³sÏ7€eÌ�v5GŒŸ/øÈmR17´w!g�җ≓%êíÑëdñ(O "Q m¥Ã±¸:×d¿;ÂRR´˜Y±³S5MšG–²ôŒIjj^Ùë˜jó5Ä`³X0Ë ê£l.o@õ,+Pd™me!­E®õ`WϦ¡Y8“F,èþzÕ=¾0{ØäË3 sq«e¬©Ñ¸°2MO,¾²q)\Uè½D>Íšj€|  ÃòtÃØf£²Ÿ³g°¥Ž§q‚$Û„ÆðL³l$(e ¤úÝ.KGf½¬ç0Y+›´È[€5àŠ€çr¶òÉ»¾ÎEq秦¹´}HEìTÔ`k›ûœ\žµ1A_ u.ü(2/ëXkFÙzÅ&DFäeGª‰ãDà+öнfÝ$c mNÐ5WÏÈàõ‚g}vlò¯ÅT^´d) †Å““1 íþX,ùÔj×bÒgu¦Nµ\â¹Ë»�7Ÿ˜3æ^XÛãñW¶¸åÔ,"Ïò\“›"�|/ìG6sd¯º¥Ì^»G¡1K&—ÓõSb)&5­X�Ñ9SˬvlŒ>tãÊ—ka™Þ:è |dÖ�¥ |F6:w­i£€N˜%™>O!ì3Yr¬ˆíVŸšŠ=5?¡¾ÈŠ€�Þ•Üý#khU5ödËuA³ˆ46Ép¢1Ià›šRàh…ÎٱƜ1CøîëbTrž%[Z=N Œ`}·‹6c3vr´ö÷( 7‰‘ÍÃ�¬a}cÞ\W"ù‰ÃÌD…¥© n©`@ ùœ1>Ý(‹õª±;Qs_ÛA˜àŒÕã>~œ²$+뙯]‰÷;C£·À°q¬wŸ]HûCÿÆZ¹P;­ô™iTL®öÀ³U TÌ›¢�&Kd�Ô±Iü0äPj¥¡ØSÆ's7S {Œ}ZÃÀl™¦–ëD)¶ÚjÑ´èTêXÄÊ*xQl‹ 0p(bR)P°Ý/wdÇþñº€­»…—L½ÛÏ,ܹ×–Õ�ÆÖŽl•s ‡i1C/Ô§Ù‚‚€lìv-~g�ìúÄëÓ>Øc;„º'SRÌ[Oh½ÑžA»Í#ÛXê©ÿìºšÛ ÔâÚŠ’ä[´h~*ð·Ç)ðZ'ÿ©¼þiÙ&ç>%J^¬‘/•ØØn±/fê•"ëŠQ5—ýø)ö휫1Ä!­�WéõHu‚8µWq*¯ËªÆ~EG¯XƒŽ}{ï± IÁ¾ Ù±¬ô]ªí“�X�ÞÚ¿À?K×_L’è …;+'ïù9ÂÞ�šÚ‹•Ù&³ŠÁç/ïIö"j0RçZÃO±µß£-ûˆe[�ëjö¬±Ø«»¼mlRQ·÷Ÿškl«°øPùÿmº÷s�?ü·÷OLàß½±ÿÓ™ÉÞT5ÿÀ\ÞË“+Ø*<k;‡$œhTAUõO•VgT€Ç«›-äËtÙÀÞ·,ZÈ\^¶pÙ¥ÝX«ñ·…‘Yåï�>oaËðÖm=àÛß?%éèUº­=MîþhŒ&5pjJtÞ½X «¥¢�_œn˜¿Å*sU{mwÒ˜\¥�;½‚Ôë¤Iòƒº~øV û®ú;qßš(‰~VR{u—ö¢Þ�,)íuÛ·ÂFj¡�ý®½. ­*ÜVÞQ‘uråfßÉ»ß͸#Æù ÿîúC÷øYàÕ€9½aÞ3U+Ëâ[|¬Èµæî |{§ÑªjÌ–>™0Š‚K³¥Oþ¿§þR?þFÉò+ŠX¯^)ݪ¤)ucEjÆÀ¶TDœÄÖZ‘nÁ©˜Ï~×î/ùg�.Š9­?§)ô†Þ½jîŒ^ÌPv£†�”ç?’¤|<ðç6þ½ô¿U<¤ˆŠøÕÞ0¨Êë“rÎïk±pcÜã�¼ýM1&I£IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/0000755000175100017510000000000015224505336022031 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/multimc/32x32/news.png0000644000175100017510000000247615224505336023524 0ustar runnerrunner‰PNG  IHDR szzôIDATxÚÕWŒmY\[ŠÖ¶mÛ¶ÃoÛ¶�±mÛ¶mÛBmßJÎü÷ù°ž¤.{ºêt×é›w€ Èß¹‚‹—ü=`îsu9O$¿Nð„à-Á»-˜ó rPÄÉ.<‰þü�Ð84®S`‰ÞR�ç¯>i›yýŠÙ“<¯™„_\?áyGô|>{×êEôw§j×<b÷Fz3ñ«Äí�[JÄ–Xë xKã:�€wUàËfOàÇwQÛ¦+€Ä…æÈ¨tá³É^ß2&¿Æ‹ç>?¢«3oY>ÇØÌJ×+ð®!H* )BÄœVÀÖ¨¹°NY�;wÝ ³¤Õª2DcK$Æ/Ç«æO™$€giÛqb ¶GÏã3÷¬�$¯h­;®‡GÖ.ôv%ccÄ,ì‰]Œ•!“±!|¦ñ* ‚þJü”å º8Þ(ÍõGy~U£®,ŒÏTlA¦:âù?]M‰(ÎöE}y8ïûZSLh�ì7T£0Ó‡÷‰Qö$.ÌòALè±­æ ‘¶ÈO÷’8sd&¹".ÜáfŒ� ³6^€¯Û>D["9Æõeá�ïŒÖÚ��É'Ä‘”D)±ŽŒbl‡ ïCö=l´®:#Ñ…¥íiNBIŽjKÃT{›�ìÎ÷-5Ñh®ŽÖÞÉ9ŠÏ ³|¥ÁF �Ú 2Èé .hOc¤"šæŠ@¯ƒIý= :Ä IÑöZïÙªL‰‘–°"R1ãˆzñ€¬LJ™—æE/ä¦y� 'Õ *)�š—î)¦ Õ È 4UE¡¦$”~aåŒ@åÒ;V -Þ iqN¬@†Ü§K�C|�L$ ò9,Õ2GªÄå¥y"5Ω±NlcZœ³i& –¤IÑ!ÏðBV²›VN J�g*V„Š Ý´3ÝJ3ªò‹šÒ¶AfKZ$f*Ïä �{®î˜ ÝXþÎÆx¾¯•Ò˼`òÒ½X£ŒõfÉv‹ÓÆÑ„ûeØ!Ôï=PJ�Þâ?ncc[Àäf~3qØga:6³yVÏŽøÌdœÄký¦+#) ®,œ“•Ó¼ÅRIï$à ×<ñÄÄf«ßáš:ë�þªžQ„êw”•á$ž£ºˆa�9”RÅ ã}Ù„p¼*¸ø/ày�ÓqvÄGØ 6Ü1aÖÜ-~Ò¢’lO¤Ç­އè`6S#$¦,þ ßþþ¦.ý\=c;”€\™ mu±4gwS¢f<Žà0ñD ç^¦Y££ÊqôчîüJ8®œ«W€]ô®zÞú¯ñæ»/rÕo¿ÿï广 %€d1¡VÚ„äîÙ³}±ŒåX 7 èÃ`“æÏør£ä¿Yp¾àlƒ¼üÚs$W�{]'~7Ä„~p²Ùô¥ yÐéÇ5Ñ’û>ÁEŠÜ(/¼ôŒîùDünDšiS“ß~W;Ð-äþÈN8Ð,yŸ\.8‡|F Ð5áÉhÂl�|ªeõ[‘mt¢$üû¢‹.zMò^­ún°ËàÙqÈg*¬ÃgñZŒ©ÝóÚ,`–À‰YšKÊбÂP«?Êslz¸åN0�>o™0 x_�î„ܤ£ ’ë=äùO3 ˆ Ü›'y^ÒOþ×ÿ8}[ð²à^ÁU�õÏó‹¹Ï'Ü®$•Jf�š0ˆIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/coremods.png0000644000175100017510000000307315224505336024355 0ustar runnerrunner‰PNG  IHDR szzôIDATxµ’”+ˆ¿j$éQæ˜×¶mÛ¶µôlÛ¶mÛ¶íw4ÈÌz3LЬÚïœôÍš¬^}ïñì]üþ]UŠÔ=ß8f­RêI¹ØR…}ÊÖ’Ì‹˜)Ô×�È[>|éïçÙºã«Gç7Ž­}øïo�uõI#lÈœ_•‰æ¸üzúgòºß=SnûÚ1ÿºùËG/g+²Øxû «®½j¿{pG¨…ufƒÙͱ�e•8}͵”½ý4"ÏÜåÎyñ¾V"ü‡¹¿S «´“�¤�¿%êv7_ß\ÇñëÉ’]n ]�™øyýó“Í¿ñãÊñ3p_wøÖØ—)'ë¿W}/[‘C¾¸îsG.²õJÙ¡ ±:néÑæd¸lÄ)R.w�¡ MH¤#"±Â[Dµ¦‚'Œ>qïK—_£\ÕTJ%A bkm~ýÍÿü E¾¸ø?Ç[;øÒ«÷½¥Tws-š¢]b‰·’½ËšˆVÒ"‡]x¤C v�Ë£ÌÒŽ[I§;›m4ã­�¬›ù[§ò“ÚH®�Ñý=uÀÕË_vÐ…>ëMg~¢{´±‰‰MÔ­4Ô~Å}ð4ƽ>ÝË£t½¤sÄþ4þ+þôš E‡-*h$¶kñu»{¿±{°°×îBR3¥Py47inã`ÂZ¢ò $€cy®íhŸ@·{Õ§€,È,€Ò§ÐÜ~E - C“g ‹‘¢S"Ð�®�^)8ÇHÚ^ÈóÍ ,…H‚!߀r”rËÎèhq¾i˜qÏ@ yLPÌæHåi.J3T שø“t  õì}�á}½kMdýó]ß(,-ì³bpm=O$=h˜ô~ô·C"ŒÒxn _·˜ ×àØ L,©�5—.uÝçÓ‡ïsÄÕ«×`?éÛsî€5²ßè�„[ª7½�3&`½ªÓH–¢€C[ûŒ5'A ®«PJ�c@@9Þꢲ=û޽_}õ3Ozsq…}—ì³Dka©·”épS¶J’nìÿ¥ÕjbË&!¡•4èè&­¶aÃÆ02‚¶ÝÁ‹8‚Ž©K$8&6Nù°¡[OÛótfãqZzžUå¥XÊb*\ß…&™*ÚšebÝÁ—͸† ØÌÌ%løøÔëª?­~¾|üèÅÞ>ÞQ&4�úÏj‘ÈIZÚØË;båðršºº3=P{÷kHˆññ¥Ù½Wß´º• ‚Jï·'âæÛ†úo×MýŸêæþÁp@ä˜D˳[¶¡£kéã2¡i?�zKÞ&4>¾ivßD¿l €þ‘6D±DI3©a÷—‘#Z,m[cºþú…�Ù´×r2㩲s-m0(×´�$4äÉA ˜×ÕZ{~9– "äJúƒ"OYC¶¶(:¶r×”N°—¾5ptù°Íqm<ü¬ñ¹ézj’ÆÚ^·÷äù«´l+ ʰ„­Œ+V—=¦¯^ýâàÁËN\tãb¯¬Öý¥òÿÀæùѿۑD³ß�ýðºcÊç\¥<×f®1Õ8tÕEGå¹õSJµlØåœÓ?q�=xùrêÑ U¿yFMqð (`Iùü¥OYqýª'Šv©Yñ'DáœzÜ+‹ÉVÁ’IòÆœ!"›˜¿VÚüýƒŸü²ö E*X¬JÖÞö ½$©ÆÓËÙëug]vȹ¥’Ÿ»«ä[@Øú©ŒUþøµÊ§ÚŸ©Ü©2Ï×, 0zûš÷ž{Ó±7Ú…z>8£¬¡Ç›;ÓŠùÃOg~ÙzdžóœÌqÏŒŽLSÄ%ˆ lºU“Òÿ/ CÎr×!O ѱĊab2°ì±ný=H¶J#È€=Œ`ç0¡ÑE:‘dïxë°Œ$Ó&‚q­!Dòe4ò¯¹�XŽFÈBd»*ΞX¬ E –ó˜üqÿOëgg#e«B?8ýæo,’�?ÎÉ$›:´"_ X ì xìzi`ÅXÖ@Ö„›ÆÝ¢Ðÿ«\ÈSžTpIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/status-yellow.png0000644000175100017510000000161315224505336025374 0ustar runnerrunner‰PNG  IHDR szzôRIDATxÚÅWvm[¬�Å?šo[ËÄW0…ض-œX:á³¥“gö«ŠOt¥^«ÂÞÕµÕµ/‰«eøðB RΗ ýl1Ü3EðÖ á­Âu Ðîæ!e®"–a}xÿz9×ÊpæJ)ì2q±v¾v–Ø(‚­ÂV `.±˜›ÏÙ¹\úþÆûˆ&îUà¯ÛåX¹Y»Aܪ�y°§ƒ°#°×ÛxΟðÿ­ļŠ˜Ë#r±2™‡¿ÂŸõßx÷AÒïUÂîTÀ4Ã^²ˆM…†§Ã\!Š�¥ˆ™\ØT.ÒʼnPâF!>`á†û•,\ËÂÃ"� �¸*KÅ�›ÌAÃ<¹ƒÎüv4ëÍFØ›qE‡—c°õ H€Md£áÔ•¸Y†tí÷}·IÄoȵVA1ž�t—KðOºÝªæÁ 2óHWb¾`cYøëÈUãõZÑ5{>¤ñÁÃ�9Xñ]ÑsEènßnTb|±^E1’�vã|Ψ±ÜåõsÒÅfüö›9Ž Ä!.«vvdÂÝËs Ž.Ó…¦mQ†8Èåç~5±'ÀÃB<ŠHžÚ¦Kÿ¶gN’¶€vÙ.ÛÜì÷'Ýí…ýø ì»�`ßG�‡¸ŽÂÑÝCH—J™¦g_ó©Œ+ü×p’Í@~í–&NÀüÁF¤ OŸáJ°M& íõ€3Ø :T€ QÄ_Àj•fÈŒd�£´H)ÛˆWqqïÍÞoÇŠa>¤l¶P�‡øSÈ=ß).î�?˜ e"�0 Y®„= ã­¨ÜÕJÞ-ú“#“"VfжMêýý‰#Sôóöß.ò³E;E·±"Dº.ÃPÀÂv1|YÂÞïg4FcË`ë$q ´.áíÀÕßô?å Œx ñšwÉ.6bIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/status-good.png0000644000175100017510000000177715224505336025024 0ustar runnerrunner‰PNG  IHDR szzôÆIDATxÚ­WÌ+i=¿TkÅkÄÖ¦±õlÛ¶Íé³mÛš��Æiì´i°º{r‹YÍë̼¹Ééøž3ßÕŽmç}8”.`¦#ý »Ò%lKU±9YÅúd «ϰ"QÄÒxSÐßìR*�39'³ÍûÒ‚=ÄΔ`+±1)XG¬J–‹âÌ�˜Îg=Û“Oúq=¿—r5œÏ NgçéüfPð¨Wð¼Kð¦CðšxÊý{=‚3‚mÁ’¸`!17VÃÌèj G?\Ù­t·ß3q#/¸BÜŠ ^v ~�3<¦ Ý!á*fóÙéQƒ98²«Ù¯p-_ÆMßÎ^ôªSO¸MÑË#‚™15ZÆäøWx«�ç›_Ì•õ­ï0¦o:½’[xÁm WA0™"lWâc~:kâ\Npƒ±6Õ�?xC¬£ˆ©11bþNM¯Æñ¬à\ЉÕééSÝÖ�Ÿw01ÂI1>²ÿ°}©<Ë«†#>Ôûî䯉KÄϺÕc=³[Á„H cV‰boÊÐÚ>õ‡üäƒ?�Ùsfë–Ç–ˆ�ac"†Õáv§*Ø›ÖÚö…ü§䨉còÛo¿é–Ç–ˆ�Œ‹TêssºÀ–*8ó•ü�?þšn["n¢žÜKBÌ]…°‘ý{3\ ´I&×äöô5„‹À‘ Im¥í“É3ù?óàz·0££ÏÀR xÒå"™¼“š*`l¤N¯*V&t¨¸H&ÏäŠWMU`q¼Ên 0‰›m’é4wäöæ3‹â:Á,mœÿá–Ü.scÏ0/Æ‘Ûí&¶žÉ×È5¾™„ÓãE�ÙGûÝd·wrâŸe89RÐQ¹:ì¦Ä<“+–Ù �HÛá”hÓ(âa§?u®hÿF+¦ñ„¡SjSÀi§óD®Xh,?9[ÆÑÈ“57º�ˆðD®¾õí­qlÙèÈj�Ëœ�~<´ážœ>Õ79”ëߦŸI£Â&—F°<`;|”ð¦Â1¹úZ¨“�µû$SÁF†ËT(XÊžÙ¬„©püæy¸¬ö¦"¾Â°p™BÓB‚kïð‘r�ÏÎ5JŽ>ÇÒ·S•ÃÃ&!A¬ÜïtL¬÷® —›ÄĨˆ©>ݘÆi“ex¨Ö2—N�^ÁÕî:ÉË÷ëçŠ}‚Á:éhE�ä«Õ—gÌr1(¢Bˆ¢)h¤ŒR4�+Ég´Ô|2íZC#’‰gD‰¹RUp¿q®È{\ý=ÿ ôºÈ IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/centralmods.png0000644000175100017510000000270515224505336025056 0ustar runnerrunner‰PNG  IHDR szzôŒIDATxÚí“´äN¿Ew%UI�­þÛ¶mÛ¶mÛ¶mÛ¶m[×nGõª&/Ã;ÆÒwÖÚÝáÙ¿Hò¿�æ’çÛ¶¼ëݶàîwÛô˜Üe¸ñõ¶'öºµ-ÇŒÌÕ/·žùà‡-zB\÷jëK{Þ2‡¸ì…–3ïz§YOŒ_kÖ7L;ÉùÏ´ì͸¹ð™¦3n~½QÏ ¬‹qsàíœuüý¿ë™�u‘åôGþþùŠçëº.¾®jÐ3‰ªužõØß¿8?ÔE¹�ÿŠŸý_ü03°.ëüö¿Èw”R%Oy˜”;…H…'¥ýŸ¢ó¬k¤Sª’ã+Uð¤Â¢&I/?aŽ>ÿ±ÂàÏ™«ï?ôñC”’“u~æ³nÇSªhÈŠ'‹žžfÞ~¿²]îTöX¢�­z�Ç¢ƒ¾¡�Nf‡Â:­ûÿ�X&çd_9 ïÕÄòêaæœ{ ¼9÷`ŽCY¦ÇóæŽüINéIvd>ëvLaÑ“ÊH9q”„>^‘yz}Ã\á×ä>›žK]ȰŽ™·ï· Ìu $í±.ë´nÇ,”<ÞÈL”œŠÑó_æ)‘!ìÈÀÞšnéáV˜=z�%+·²Dî­v.š’¡ˆý·à Yóv^e±ò�Ì}ŒïÝwJ°Në–¾¢ � 'š,}ª¿2{Ëýäù–áý%=†.HnÖÈåGVƒ0h�‹ì3Œür2è¿�Y²î%*MWQß©©KðçàÝ)û³bã:×ÈD�yFOØ‹›Š›X�•Xzë+ɯ{9ý—ܟܰÅA†2Z—Ò!Ìrú_Ân"2C.Cÿ¥ ¿Þ,³õ¬¿ÚRl]MúŒ~Ömß�Rú’½h¯7?ñ%Â*DD\KR1T 5ˆDaÿízº½b}|X!á§ŸêQÒA¥®Ñï€oï€J§’.ŒDºüµØå¼w멦£¢Ä¥¬Ô`Uˆkd#·ûÒãŠ`‰ŠT»þæ�;.¢n©KÉú­Ë:­ÛñÍãÞ‹±$/x*o\}2I­ ¢N,"ꂸ€ˆŒÄ XDÜ‘¥“ ÔÀË—žAãJ×âôË“ugwÀ¸KNNai¦÷{ oÌ¿sÀÛ×�¡¢âKÄÙöþ4»ÞàÅS�¢m•ópf]b¬^ë²Nã.˜DÑlÀuÒŒ‹ZewþñV áû!j0Ô!âÿR’„¶Ë? •ç ýfýüþÞct.{jáµÆë´.ëÌ¥ï€.D¡–¸… ¡;¢÷"øï(¿•× ú<Ô1Ül¸‚»!4òøspZÁ� ÒAe™Ýºí³.ë´nçÉ{®ëìêêŒËÕª[­Õ莰ZB85ˆA×uà4‚ìU¯ ª2HåN„p!¨tuÛg]Öùô}7t9WŸuDÇÁ« Øô¨5ûomØÁ°ã¸|~öê{½<Ú�€§"�Ê ™\Äà$x½/4Ï ÝôYÇÖÖy凵K ¾¾Ýdë9D?¯�O„ÆJpÃTž¸)B§hðûú±¸óÝó¿'/0~4)Èñ7Žê!^O?꾫ñÕ£Ýø}Ô’_*²äŠ¡³‰t¿vñzùè@&Éd$…Bš–ßZùñåÆÎ> nЗ‡xú÷ðûdá׎-<犵=Ø î50ßp‰-™yyËaëzꬥ7÷”Ÿ<³–søóɾt“6r™ýǽ¹—üâëËçhn]µÓ+�¬‚`2s&ðü*LVþ¦Er"—ÖêIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/cat.png0000644000175100017510000000214615224505336023311 0ustar runnerrunner‰PNG  IHDR szzô-IDATx´ÑŒ\Aà{ÆÚ<Û¶¹<Û¾ :ŪâÔq«n£Fu7FmÛÆôÍÖíòa’ñd¾þ‰’ ¨h‚qÚL±gTã8Ñ%a±áþÐSÒW'SŽyóhÓÊØ÷’ÌUÔãP²ê+�ùI’é(‚˜¸îƒN¡¿S‘Ñxʦ�y§Uè>˜Ô¶'(‚�hCÂñ(i ¢æš\þj‚%팤|are<×}‰Í1€Cgv½�õÁ?çF½ê­œµK@}žg3J­àì¥À˜ Îá¸)·«Y|²e¾Y§0~€H°Z“é7€×2;P7·9=&ÿS(œÄÉû… •jñ^íXÓì,ê¾bÖØ!²Ò$Û' <í\ÐÔÏî,L©,-‡ÙoYÝá,é¹›`I†…ƒÃ:&üÕŽE}_ÝôîÒôZ VèÃ…a}ÉU…À¿žÏ÷–\I´¦‚ #Áa½,ŸpÌ—4´=3¨-¡1ÿõo|ʹ`k.측–kùâ°Îñ £rô°Aeæ�#Èk#�¼ð±¦•=ɶLÞ8¬)¶Œ=¼_ï(ì8ƒc$/Cq@â詞hâ…O´Ì$Ù2ü^Ž¢( 0”ŒCü(§@£Ð¿ã²hå@oÍÔF9£ô{9‚ ¾”¬ÕfàQ‹>ööŒk™â@c~ÛQK3�>\§4 ÅþɀěS.N;—Јñª¬¬4­ö|8�c¸/ 'ÿÊ\³jc¾uJ¾¶\QÆ¿µ6FGר�¨nã4îß]ŵùl^žsfözsmþ’o°£­ï¹Œ^ì¯Ï¿}å"Î;¿û¯¢Ö-6Ó›ýÀ#gQ�.Á?ï§/N½ò`—IÕmN`¾»øÕó29˜§ßï“GÊAä±CgÒ’-¤T/sIB›yƒ}òzI15IôØ ¨Dò*‘^lRýuZC„Ë‘­°)³ôØg‰ôª#ëwì&$¨^0ø(­3¯žr‚�\Œ êPæ²¼úÜà}�W ëeøã'`ä¢à€\ ›êÚçVGõ�"+io5NgÉ$˜¥�ÛK·‹QÆ;ÁÇ�a¹ ^ñÂ>Žm5BV�íb473î¯Ök1†œ~5A™w(bÕVRdÕVyE•÷è•}:Õ@DO—ŽœÙ²¦¯1ûø¸Ö0^¶‰c—LÑ©~Þ°× ^žW¾Ûöz‚¾÷AœsÊÃÌHmb6"ÙRHzµ$þ"È]�«ÿ*ò·a7³|pe7I‘+¡L�M2!H?aÓ i.Ñô–µyAR[­Ð&CA—y¤èCÞöämFýä1«k@Í>¢¡¢–nŒ!EÔmˆ`¢€±‹H‚Œ©×èÈ£uð~Â0m=¶‘¤hxKIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/resourcepacks.png0000644000175100017510000000447315224505336025420 0ustar runnerrunner‰PNG  IHDR szzô IDATx•–”#Y†Ÿ[U©JÒJÒ´ÆÆb<³üÖÚ¶mÛ¶mclÛX zÐÚ©¸ê¯{¶ûLw¯ŸsÞç}ï÷] \ÅŸà¿Àa€ ö‡Àz€Ñ[שɸu,p0ÞöðPøãüÁ™Žz:Œ·><]òÆ¥ÁÜÑÏ¿¢ÏÐÀ¹¾|c @Œî¼ü4þ8c‘`3b€á?|X¼ûȾ‰5�Âþ ‹’±-!ìÆ½Þð²÷õ M‘ÔP`9¿ƒàùçù|Àh )l{ �|xÐ5#Çœÿ_Wßž]2yšKÃ4MêØ»w/õõõ´´´ åJv«ß4S™l)ܬ€*`ûiA ð p!p¹âÑkŠJüü^㿯ÜT0âБÝòºvíŠÇíAUTEÁãñÈÑÓFV‘éë1¦&àßÈ^< œþGZp�ƒÄ¶9¶¬lÌyCŸ[hºn�Éd°2¶m#‘�š¦IÑFqq±øò¯^ùz"èmÕþLj~«Ùmæ×Ðï¡¡C‡ò5ÃpK3R©Éd’t:-ÃHaY.—K>"1b‡O<ì_ƒÜƒèˆò»¶½È÷êÕû´.Å}�?Ò$‘H‹Å¤ˆÇã2„ #Me$²ÒÜçóáv»Å”›È�ì§øá÷Z°ð8·>æ(]ÿg<ÇÔMi"{,¥± #Õ¹ôîÝ›±cÇÊJÉïÓû`Ÿ¯ü߆gï×éJۢʀW€dÇ’¬l}Œ*zUõüþûá—có Qø‘d0H4Å”†²Ì99(�zq1ÞÒR²{ô £ëøý~äè%ªª"ÃÚÁ÷Ø]ßóÄ}Û†�'ÍmÀÁ€ ¬bm¸˜Œ$Çöy¯¸cˆ½a&­Šf;Å€¸¢pòO0úoë`®pkÊ]óKvèUEÛÀQ‚ĉgõ@0‰V²ªð¤óvS÷W);àþqÏ=ô7i,‘-‘¸�š8VËfŽ™¨\>ûµxÓ~s‰(þ£ 0ÚûTF›s<–tÀxÛKövüxŽ{ýu.ž9“!‡NVV–œtÈr""‚[5g(zþ8öœƒN?èb ‹öð)@5°™VýMæ¦ñžj5;hŠB ¢‚ü èwíµüwÙ2þûÉ'Œ8î8òòò:ËG5ºWb=$ÃÕƒ–;#¯ݧ¬(5{+¾ë –i€änàX Â0ùÖ�g–»kþ“ö�!Ývº3B9ɼ^/’Î�$“ÚôY®&ÿ0—Eq¡è…¸òãmþBñ^3¤Gè¢àÇ�tŽ­¢L–‹7Æ÷á8¬<¤Ì ¼òŠ(wBäää „à—°í ‰½oS·ázj º• &»p®Ü�hY5‹Ll‰à\êí§_Ê9 øŒV4“`nà¿@©�b«ûuôK_såöíÛüåå£ Ã@×õ!äZ§a©]“ˆ¬A#J¶¦R®ÁÈ*@Ñý(šÕ(DQ½¨Þr¼MÛĤ¿V<\rõËžUµ³€� Ðï î0ÑQ¹P98æJvÑ>öôý¦²ò’'žâ˜ ÙkÙçýd°~¼‘øÛ²Ù(ÄiŽÙ¸\.ÝãEÃF�ɤH'MrÄFrOEÿ¢ï¾;P�¥Ê?+)Ó­Ÿ5ÔÎdìì]»|óÍ7Ÿ566"7#9ê6„Ðå7#„Šæ2éÈó W¯CV!eî#«& ’I6c£!ôBT‘"z|·Þ Oø›d茾ÜÜ, ã¸5ÇšššäöÛ!„’; üEÓ\ÈR—…HÕÐÙG¢e/IÓ �`[)P½(î®èÞíÆ–¿þµgthî{=”ÐJjµ±´û¢Ò#€{–¥Rá¨i 4h´¡œí[!²†c‡?B�þ©¶…°LÂM·áB(.lG ?K%cÔ¦Ç ‘Œÿým yІg}�Iò€öyñ'†ÇãvUV€½xñâû+++ƒ¿Ø £ ¢ëy¨š¬‚á<ê¸4—$’UØG*Vë¨Î1�Ólõ¢!Ó—© _ß·HÓ��%ú’lեȂl@Rûî»ïÞ_WW‡ !�ã!ºœF𮣍:ÂQ¶‘ ©¡³±Ê QM<"רNHssSâû;^ùõ[±™iN9F´3Ù¼yó‹7n܉Dhhh@îùmEG”ÝøÓMu!¥:ÊÒ k0›ª0.‚Ö!˜±ÓgÌx!™J®ùÕÓlˆ†Ãñ˜eÅÙOô“O>¹9 ÙŽ�Õh»�Èjˆ¼ ؾ#�Pùè1Àl ½‰ùN#˰hÑ¢9;vì¸ °4à—hÚ‰”JSÚ±gÏž¯–,Y2o„ G´mJÙÙÙÈI)_ÛEW@h1BM�’ÂV²)r!‡\EóæÏ›±råÊ�Z£G�æO2êâ‹/^\TT¤9¢  ¹:dY 5ø2vÍk4Ó�xþÅÄì"yeO~ðÁÏlß¾ý–΃_ßq�èš”%l;Ë4MÝçõF[ñúúJÀ|é믕âQ£^›8yò©Ò\JžŠm±Ò&vt3¦2@Þí5kÖlš:mêu-Í-3‹Nˆ{¹Tõ¿ý'M:­|Ê” ¾¼<¿°m�ì­#l+“±�0™íß|³~ݲeéóÍøû,,++Ë È“RnÕ²­ØÌÆM·N�:õÉ�;v~4ó+ˆÇáõ¾pª\vžòrôÂB4yï3 �eM¥H;ý‹WUß½x ^ŸUX8wʔɗõíÛ¯§ßïSEMCÁðæM›×¯Zµrξ}Uk-ËjÒ@ HJµ{žlq'<0 ®2@Õü~áõû“-.—J9»AKKìûP¨zg4ºÝ„F@t2iSv«¬Ve€´T»ç`üÓ3"@8+;þIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/jarmods.png0000644000175100017510000000257615224505336024210 0ustar runnerrunner‰PNG  IHDR szzôEIDATxÚµV¬+[ÝgfÊ+4�§^>Û¶�è[Ñ·mÛÑ·mÛ¶m^›Å´sæï•æäeþ�×Ü\ìdå°g¯Í©F”?fÏŽý9gÎ5Œï‰?,°xláý÷gý1sf!õC´)Ÿ:uIùmxÍšKO?}ŠyõÕÁè%—ø"ç�)9ì°%�éÓ/æóOŸ4É�B çoÙBZ~>ÙÝÝd·¶’ìê"òû)¼d é‘È&qú�¸oÔ(ÍqœI©$(vz{³H§ÉI$H×ÖRòŸ¨Ã²"CN Ñ¶éµ¶¶' $ñá‡$“I$²c*Eñ·Þ"]ˆÌC]]· ˜À¯ÕÕ%¿O˜p*Çñ OümüøcG® ‡O/Õ´¨ ’^P@P«¡#ÖzI ÙŽ“ä»Ç[YyíÏÕÕð{ç0ÎâùÙ?UU�÷]UÕfb´y£¬ì¬Š¼¼‹gÒ´¬"¶ž#¾d”—e2$ãq‚ûqŽ5FrA¶µ)bømöŽ””©©¡î¿ÿŽ?ÓÓSèI`²ß//.¾dGyùÑ+®€{ñ€KƨöpN–…µ ŠÎI͇:¿þšŽil x…îͶ­ ŸO¹–‚ˆš+Å.å’¡öÿwî"/€DC&#< XŒ€!&€`-`®wY ’ÊZŒP¨)Ëq_�A$#å8Þ’Râ P„B*³�¾nU!�¢Ý犠"á"%t{ xð !"º^¬ª‡Üîu+ïc5ÂÅÊ#ïa?ÓÔÄÊ•¡ªµ�a¼]^¾×˱Øa£ c´iU†iªx{Y("}B Hà>º%,·›› áâÐ"Ïv‡`Ÿ‚_‰®?ZY]ýðÔ¥KoºÖ4¿ïóMöUU©ÃèÊh¥P‘”Õ˜£t¡’þ÷_Ê´´ö@û*ÆÃùšvà‚ââ]%'œ@²£ƒÆWWG D�FÑãÝ–ïv³ËRÌ û†AÄsU=v{;ÕÕÔX|aõùÃaB8»¤ìHóï v…1?Ú/¼lA9\nÄb` —õq»ÊlEB‰JD”*AðFª¾ž®ok»æÅÞÞ'V†B›&3Ž¡§ç^Id‰ á—ËÊþœrÈ!¦>jTNKU2©ÒTñ‡µ^‚³zn8Ûjj¦7Ùö¯Dd0| ËH2,ƒÝ`ä Q ×õI&ÕBÝ�EY®qõ&À°µV‡”u(û,Üb°mžCF¼”(·æ”Üç*>6а0÷ƒA­¶Ýkm5aåÀÄ›<…žRåóÍé8// …¦r‰ÇþÉdÞ¿«³³C} ó/�FŸÜgäÈuøÂAT \r(ë—àÙÚÚ¾¨Ëd~]›—·—»x³·÷ÝÛ;;×ýhYøçƒ{_ošwÇLSh\&(½–ž2c1|Z=¬ì¿ ‡Pÿ”)„¿m?ù%¾„K˜À‡ DvæçŸthqññ¯àïét ³fôè‘èbÊÈ@sßÕMëêêèÒ––sÞŠÇ/3ð6£ýéžžkQ«BDZ¥l8;¹†/�DÓÈ¡¨¿¤\¥‚*ýþi™x\ˆÍháLíhu ÞâÄlÁ�ÁZ‰êíJê ((Õ´ 4APâ0T®ßn(tâñVCnáöoJ"ŸAÞâpƒJ !Ȇ 2WeðX¨i’H…ÀóO‰�1.å”å>O1 ]d·±'`éüÛÝM N¤GYÙ!À»ÚžH.ůÿàFÁLýÖ¨GsZ*sœñÛ_g“ß[#ʘÏX1 XʘÀ$ X&è ú-fåa IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/screenshots.png0000644000175100017510000000255315224505336025104 0ustar runnerrunner‰PNG  IHDR szzô2IDATxÚí—pãʆ¿]IV 9džÆyüŽËÌÌÌÌŒenÊÌ4Pffæö˜™òb¸8/f’´»�jâ;Geîçù%-ÿËcŒ1ÿRý߀xÁ£î2žî+(­}Øju8pV‡U$½KGòFÊvÊùyñ’gÝ|¹Û* Ha@€à�¬˜ÛìL½æ�* ó»×<ñYaÂ0¬Ì—Ö+C§Ñ&üs..”î}GÞ}ß…Ü2iKá+Û¾lXâ·Ý€‹ñÏàCï{3` �1ô³\>M¥šû‡¨Ý®àû=$°j⥴9pBðë=\R[9|ioö¢úù›ïíMó�ýÓaþrcˆ®× ðýˆ�ù’ÇoˆJ×=«ª^.Õ%®%9è]}AýÜ¿[½›³¿{ óÞ¹Î0=ü o "h­H�LsÌÚBQfŽf~à&a¸'«•gÖÎ…éÓ© {~ÿq¾ÿÕ·Ò#ˆNAÔAC Q[Z`\.O¤(ˆ9Z­ÙîvŠ"K76¾�Ê-x±1î8¸‡µN ¡Æ>×®©p·Ì~óÍWòíï†~üÀ§‡ €ˆHƒ½é¦ÚUFÜ6KÞ ) ã�bÒRëhuà¨I£ðôi™c¼} ¥g”O©T ’¯ÔÕÅFÀe­ÝbINSêÄY”YNû3Œê…ð{½}�!³Ä€¿Ä:ë nîÇ,Ë)Z2(˜i¶=Éw~ð)^ð¢ûóËoa b º °/4Aàsy¦A­åSÒ.)ö[·ÇŽšuŒY R¦Íwßû,Æn|OTf‚ß}›îüt¶~ùm϶ÑB`Å$Ú!:Ÿé]�'éØ0b�1Ðô9‹ŠgI9P÷¡c9ìüågiwÈ„`Û§ÞFlÈA¤ ð�§é6^#¸ð„7«ižÙMíð·X,•½âÖw†VWqí­À‰c[ùÕ®p`ëç(.Ÿà™ 3ë(•K\7S…´–ºJ�J$I§Òôè?ül.@¹ÜàÈ‘y6m˜cb°D¹rœîr™½ûÃÊo!Ý’ +Ù°.Ëío»‘[Ül=µz�bažÛÞêo|É.âé“d·¤Pñ{Q“wC›;#cO"‘¾%Z¼@Ñý(\|¤”|é+?§Ñìpï{Þœõ×Î!…�ÒB>ô™/Ó\4äN^‡–C©•â&'(Öž&�“¼ ™exðÆÔÊ nÙôQB°Š\®„(îwŸ›19>ŒmKvØÏB1Ïï�¤¾PGµÜl¿éÑ’ÌæIt°@Ü?I«™ w€Ü[Ÿº%Vòzú(¥V C”ìì›Ö¯ax8ƒëZÌ |ýG?)ñ´�5GyšÑ‰4�†‡Yùµêc8â,Úh­°Ý4–%1jŽ~ôŦ Ý©r»[_I?£#SÜxÓMøó‰si.1“ãëùG]Ò`øз€¹+o§¾ùÕOH­”Òh­B‡Z)”V„o¥Ð:¨syµî• コù”êå ËÑõ}S¯ ò’gßã+Ï|üí*ÿ mºzæÝ0ø/’ûÿÿ†ÿrÔ±§›Mç®5IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/patreon.png0000644000175100017510000000067115224505336024213 0ustar runnerrunner‰PNG  IHDR szzô€IDATxÚí–ÌœA†kÆ�j3l.ª¿™Ÿw7SD�SÆiKmxQÖFÔÆ¬ãÚ¶ÃÚx»_µwåªþ'y¢Õ³žV-‘X§�åb ¤ œÚÖÉ(°V ;@z,g {Aº,�hÕªÍ�;!™VÔ@²á»m&”Ç�uHÞ€ßFΦ$é|°¾6À #iX‰B¡}´XÙz¾È6»%`™V»ä!˜Õ @]]7�ÞÍË"^ “aþ,óly4«C.$xˆ,ëä,€1ÒÇ^·Dԕǹ dåz[–ˆLgº °LI-`˜ë#И^@f» Ô•ØûŸˆº‰äw H¯¥�§hlìê'ÀZI&@ºÅÿ×ÔýÝý�x…ñÅ‘ÞÕõ"Y÷’¬‰Xúí1¢Cœ€ùNÁºÄïeÌëÊV¤KºŒ(SÉÓ/‡dä¨<#uJfWƒ¤Ò¥`=–Û`½–c`]‡L¦bP±cdN�@‹@‹ë4�^ªÆ>™-ñsã-î™(/’ ªZIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/settings.png0000644000175100017510000000426615224505336024407 0ustar runnerrunner‰PNG  IHDR szzô}IDATxå•x#y‡ß™L&ÕH¹¶Íó­­³¹æŸ—gÛ¶}·voÛµ]§mš&�33ß|{fóYïó¼Ïxþ¿¿ù¿G¢ :vìhEa¸Ãál#I²ÙlVÌV«Õ,ËV³¢(f‹ÅbܳGÅ8ÊÆ}Y’å¿\È'H’Ùb\Ûüþ@ðþû/6ò={ö¡ \pÁU·�wÆÏ1ÊÃd0‰&D“ˆ®‰ˆè:hš~BUÕŒk�¸ª‹ÇE ‡•qå•Ý{àÀŽ[?_#šÀš••GË–-øøãU¬Y³…x<™X4J4¦Ǹ¡J\5Œk·PUÁP44 62bd>C‡�ŽË•ßÅ�ñƒõõu4Áþªª ÂáÇ�•²hÑvHî 6¬vPd�¿Ö,‚(€ ‚ªU>lk«™8ÑL^^Aàä;i£ïKjjÜ�P(J�î�p¤5‡¼\hYm Ûv4ì”k ;¶Ë†ÖÐ*Úº8îu€®ár¹2�NGwÉ á‘Häpeeyi�ÇG‹Ùäe‡dÁPÅô•VËwÚd°˜!I¡6`ûÖb èêºýöûn×®Ã<@I( ÖÖºVWÕ’––FÇ..TPd°š¿Ò¦€Õ v;8ǯ´ÙÉrz¸ûº¶téÖ�nÝ`Q2­º®�  @,Ýáñ4 J �ÚgB4f ¬²¡å«“’ 9ù«£ÙUú;ÜL}2©éYÔÖyyæéûk÷ìÞµ H$@0ØP__@§Î¹àÝ NÐUÐ$M×@UÁ€J?9á�œ=ÐÅ–¢¯ð²qó:p@‡O�ò„[ ´ôÈ!¿ß׉¨´j‘Ž%]M‡ú T7@™Ž”ƒÒJp{éš²—§f7CbÌ\ø,O½ö.…ùtîÜ[Ü€–p¯·¾ÞëõVúýAZf’šÅ N–¯L²BJ2Xt&´;Æã—¶fÿÞƒÌ~¢ˆ�¾“ð§ŸÂ¤ñcéоS& èÛ÷”ºªªÊR�ÇGzfÝÛ:@ÐÁ*ƒ]F´[ m8477t;ÎÍCòxñƒÕܼ*LyþDpP’i EhÑ¢E–ÝžÜD=Ï!ŸÏ‚LïÍ@ÔÀòÕÔÓ$ ¹ê1–ô÷2¬]6 _Z΃Çs ž�IŠ![˜âe4xëÈÈ̶)Š5°$ص«ds4GZfª !n2ŒÑSÞýgB®bâÁWß"�\À…ý²ùC�J^˜(ñÑÕ)<=w€QxÙ®l1%%-P$dÀ€¡}üÁF^»ñfb+?a¬?�XÏ)dÉ~.Ú�}ûp´ôç�FNV&)N;9¹y¨š _c��ŠÊZV,_îohðD)áƒ�îÚ¯‡sªF͆õœ;g¹O=Æ­˜î£/eõе˜Ì"cÇL& ëÇJk"[êöG*+?>rÄWãvûŒ=ÇGeUee‡÷ƒ¾h$Qî¸çå%¥µ^}ÉÌëõçÏ8En+Ö�.Y¨ß×®�>gÚ\}Æœ›ôK/�ìØ±çŠ¬¬‚Ç-ûŸ€9ÀÀp` Ðh ¤@Â- †ý6üáVZ—fÀÅQ·i�å´‰…©üø-—Ne¿ß#ïÞ]\<àråym¶ü £ Dš`ª3éRå½gºut¥3|ÖL4¿�ºåŸRûÞÛhÇÊhqäzu9—]5ÍtúéCF"ÐpÝuKó¯½vÑèk¯]xn÷îý]üÂtYáÈvšô»‡�;ùœAW^E<¦fýZ|[¾Ä½bÛ5žŒ\Bè4»z{ d×ÎTU¹÷6oÞJp&¥æh“I==»·¿ôÁGo? lÿA€©ü,C:µ,¸wââ…íszô»}+¾’-ø ë7md�ÙAã9WÓ¶ï`´P€¤”tRÒ2±;“‘­ETÌèXˆÄâ}^üî£4ÔVx?ûüÃ'ß{便€@˜ËO¸vÈè3ÿ8tÉ2‹`’ðnXCÀиcÛ‰Ú¯�¨³~ÇÙW]FfjºѨN\ˆÅ!bS!þ�h„ƒq¢ ˆájÞyçíMO=¼ãÞwÛyðTûˆ‹%\ ·Væn߬¹¿5«®V[³Ê�DÎü@DÆEÄLŠpû.w‘{!Ükl×=Ç6ßæÁˆq„Ñ„ho³xé_Y»æÏuõþ à4uœ¥¢ç©²ˆj ðäAN{ŸŠ�Š‘Ç#rO™ƒÅ‰í¥ÊÄnÊã[žÞCœX�¾Åû$ ¹˜Æò͵£´Öh­ÎYŸÙkPÎPõ?õª¿NÊiÄÈJ³÷�ØqžÜMit3¢´�‘p|CgÍQz�€)Ñ®� ¾:OõèÅ4çÏsN{ÏÁ¸TºßÁ{>œ€N $Ü¢áøF¾·wàÛ”â"¥™ƒÌì¼/ÿáƒwtk‹·1™Ó‹ÛµöÅË ÿ@­Ëq|@4‡p£å0ÆÔu±žµ¥+i,-^n�?Ë'­ÕW^sîéÍVíjpé‚ϵ@È‘pÜWF¢a.q–Ô®üË×ZÞê+€õàöÛuÁ§¬¬,Î?jzûf›2ˆ^‡t!§¿Žþ½¢ ð96)qáï¾è:ÕÆëÀЈ<ºgŠó³¶gmyþ³;·‰1E�tÁxzÇ¡€°Í¡‡Ïñ…!ÎÿÃ7´q|åŠÿ`7¾ˆüÖq9­SÏÆ××ß}ó®í‰è-"ÆC W¤ôD�d¿z%üõ¬­~Ú«¾èÜЛÐÎ ñçÖÉô–©[¼Õì¶] ô-.9=‘pvèbÊ`Ûhýzì4ü‘?p塎͹Ÿz§/šÓŸ†Mõ ,ßyûÁ«v��ï6¼ ]¢. ⛨«AVEÛkh}ÚM4å:ZEaîrŽx«/ê7%€³zøÅòÜå/ݶŒ RP$ñhb‘(Ó­¢BxæH šäeq°V²6ðè€�Ò¢ˆÌ§5´QE< €Q@º�  Ä€’Ë È2Mt`›!êM’Þé&,ƒ€$?î �/>—!ÊÇ%EðŽÉ.p^Œ¶”ÊáÚ$ H Ô“ïp@ÄËCŠslñŽhpGŒêærB‚H�È�Å�†³JEPÕÙÌRÒ�þˆ¼¥ Êt©èÂÈ “—…Í)0±LúŒÒÀ÷€Ë(DMp< pd^øó¯àì¿«zw¼§Èý»w)86 $±+ .`-QQ´€Ô]ÄÀâŠá´o)Wž«W£|}WÂ$Á.�¥�ûÎÙuÎ^©Î5.Î�Çœß^{|Û\Þñº=Ûâ�]Kˆ–Hl¡³<,ÓÅX[ìšèh¬Æ ö�¶M5­  M†ûö›‰—I¬n•ŒªšÁ¨ö-ÀÃuñÞ˜øwŽœá ÝÉnÖ„�0„€çVÊßþªºsuÍ× G<Å„¡h7ÍAh²«U`°ýƒAP ÿŸåÌ£rïÃïG·OY‹±ûØÐ«>jŠ/ª†0l©!xX9LõäsŽÉ©)ü_`@»9zW5zÿÛÑqËCO³ @ªÚ€±·mCƒ$ïAk¼å{`ë¨ê‚ ÞvŒÀˆ=ˆß€u¡ÍH``­6¥€iui«.°- ÕÈ4¡Ýš6²½%�œ¢%Óí É� �žyÿ£Þûøß“û�¾ù}˜vv/|üÝÜ?|ó‹U�yëkßN÷ã¿R3uÓƒÑ(ÆP0‹ÃQYHS½¼ß ×–ÿ=™×êÚ}ľ ÄÀäïýÞIV˜õ«º7Õeœ@Ú'„P«Ñêg?¯ S qqX‡ÉÜä¦n„hÑÁ+Uë¢Å”Rœëuâa3©ªF°/*;]7uîŒÝ)Ç4×IÄé‰àÁš�Ôj#Ú¦“ÅÊ€˜Æîõf;Iqª�D�Z�¡z90—×Ýã-�w' A@mb›^GZwnp§ôgŠAãTŠmR,ú]±îüÇ�cü+ÑÍR7B åXw… ‘cú+urþ —â`À¦] MA&ÐÉãRŒ³vhŠ `ÚBÆÅÁ–sŠ¥‹OššËYÍìJ�«Qƒ$Zl¤ãî5Ǥ¹ä__˜=è¸S—«ðÿÒ)H´‹%òtöÜ÷_~4' sèG_ÛÍñœRAë~Bˆ^5oýøÝ×w«ßïh0L‰}CÝëõþÞ°k ijh2WzIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/loadermods.png0000644000175100017510000000300315224505336024664 0ustar runnerrunner‰PNG  IHDR szzôÊIDATxµV�dIÍúhOwt¶mÛ¶mÛvàlÛ¶mßà4^Œ­þýUU—?c*öÇn÷õ2#2Š�/ë%~3X@9öÆú%5çátWÆØò†ºçËq!¡ ÷Þ—RÞóÔUëŽÃâ�#¯ýcÃcn¨yî£NÙ2kRŽMºrpÔ‘³ú,ùóß#òŽÛåQ××5võï•…li°`rÿ¦k””ì¹E%¤âŒNú04îÑhèl±v)”§Í•‘�K 2æy݃žÓÐðªOŸ$@¤|_€Ç%Ø® yÏ�=Y˜´üòE΀“€†º__ïì·á‡Æp\–# ëð@ɉ/~]c~GÝ�.0û]úkiÄÔNÒèB‚#¥ô„>nù%ƒ}�ÓâQÒIƒ]�dƒs eéÌì³íŠv¦Pw,›ö ržtq^’"ØÄ`ëÇzN$÷˜k ê¦Î(Æaš¹˜ÐWsIwÀ.žù¯G¦1ŠSp&ûXÎ$ÂM�Ä C#p4LРr†C{ €ö @Œø>ªb�E¡;·ó8àY %�ˆ’¤.Ñ7eL9§×rz­rJ9HÌ 4úSÎéÈ*Ni_JÉr†@Ó£ÌL”—`–+cÀ9%'çwéÕh.%ÝS¬Ð:°‡½Ç\ZãCg;°æÞ©Ê5÷GZJ›?½òƒhªj¹ª²(PrE-jh®TÌ•*PH“108ê uK wɨ]û0ÓŒ—¿¼éšeû ïFïBoÓËÖ$¸ Ç8ô:rŒÀTÒi 2஄YýY@¡fŒ�i°Ù!ˆ/ÅôHÑQ«­ºÒ¾çºŒMz°|m¢<02­Ä„¡1�À�0í\Ì5ÒCœ3P]qd܃†Öq�Yy±i¦íÛ¶5*¹†äžQ²äƇoµN)�5¼de tô~`Ô£W+ð0½V ûrª²¶À3Êt*·ÎZ¿»û¶�æ÷^/[vÛ]S•«¯Ã½¬…ëgЮË4#žØøØ�;.9j¥Êšòèlª¥8§=ÄJ¨ôHéµ9„î|]7 ß?¹ïZn¦¯eªõ›¨"8Fu )<#3Ë �JKQ¬@Â¥¥öè<$ÄV¡ØŽïúöh7 yÒ9àZ`¿nÔ»ç0ð¿"E¡s2`ꑤ!²NÎ; µd£c>lÚg»*UF/ˆäp˜Â÷]ÃÔ¿}ÖžXÞ•.½Å±tí’ÙÑßuþþĨúûã=u M3«w\“Oò¹ zÍ'RÊ II =¼þq×ÛÝGáçùàT2Áº[¿ÿ?x;Nöÿ帽½útÛ²[í€uÊ‚ïüð˜KÙ»ÆrET»ó–œìU”D`ÓÍ·^§ë¯¶lÆ3>|”Yw+\o€ú› CyÕjû^°ôƧž«cJZ#í�;n°~u*a(ã…C òŸ%buÌ ,�mÐôÅ-W u|q S‰ŒZ¦éÑeõhQ¹g ö®¸íU·m¶ýÛc-ôÒy1·s3z³Ðòós/Íúõ‘£Õ·€£ îŒ ËÑpñ²CƒRP †�/*ê–‘DÙ2RrÃ? UÕ!ã¾3�Fr4™…WçØþ+A ÓÈ›O¾ëý%ã‹Ì õá2¢©"œëF>Ü·8Ä€� –|yr†ÂÊ43…SÃÈߟlíœMÓ %^ap)æúˇɵ|k¸½ËÑeL�äê¿dÁÞLàyάáÖz8ƒÜÂP+P—E�âŽ:€:S9�Ï “ÆÅ#>*ÿ2nÌn~ωIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/copy.png0000644000175100017510000000141115224505336023506 0ustar runnerrunner‰PNG  IHDR szzôÐIDATxÄ–MJA„«›É„Doà2ž@WâZðžÈ JôÙÐe6šló;?S`“¡ çµ ø¤§›—ž©¯ß+3qMÓà?#ãÅ9wàœKü]ðdïÇ~˜ßn·“}QÀñÏóJœÓŒ@ÉkdY†^¯w ?B°ÇááÛaˆxHCs€£€÷ž bU€¹¦ 9— /¢ÉºnípÁ¹Ë™Ü¤ Ú‚S."‚ûÆãñÃh4êòÇ À…*x/‚#ALƒ¹ªªP”e§?N"ÎtÖuxÕé�L©-(DW8€HTLýŽ·À‡ ¨?¢@§x¬Ü*Üþz'„†$·€‘ Äõ¾8€ôÙ7ALŸ(€ §ºÞÙc´†Û C\ýa˜�ñ[ñöÃ[ï–”¨Hj_)z88£,KÎü½=âþîžû-” H�p2ŽÁ`€ù|Ž—Ék·üÑ|o¤Ió€ÆìCv9�°Áœ {ìgÈËÉYN0}Ô56¬X8o^Pì²@qFl¥Éçì o?~bxýâ9CJb2éã5CsGÛö¹³fÇÀ,GvÀ-`;ÍG20]¿~}P†ã„„„"ÂÉJ�½]GgN™æý0²!´÷rWôöö†' ÌP"œ>êš×/[´$ÝrH6Ä(Mìÿ KK&㲜`. $}¼~ÿáGwgGãž]»:`rä:‘>Hï�ž>JHÑ€wÏ{~§ï–B©IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/bug.png0000644000175100017510000000304215224505336023313 0ustar runnerrunner‰PNG  IHDR szzôéIDATxÅ—Œ4ÛFש¶ÿáµmÛ¶‚gÛ¶mÛ¶m¿wmÛÖx ÓÕµß×UÝt&=™›Ë�¬ìö·Î>§…™­ àDITDÁÌx¤x°–±�˜Þã%ð!±‡(=„I="_ûŠò#2©‡ ð%q°h‹Š( ·ú¤º{ì±Ç»óÖåµ|w²pý¯'¸êüégã|áGã¼æ«cœ6ê­>©å¬.¬X,Z»Ý¶-·Ür-/Ü`déßëbÿg¬—rº.ÿK¼¥ƒíXäçÀ¢¹lBN”ÏȪá;ì°ƒmºé¦¶É&›<À¯og)ôÜõ± 2Îg¯—Jýcìó#\þ´ÏFEAxâÃâÅb¿üŒ,?½UÄî»ïþ¾~øN;í´,]M¢kp\Öxpd9 ♄Eä%f†~Ü Ÿ›7~“�2ŽÄïCxé,üb&•� ¾¯žm‡'‰é®\*q„‚?Ü„CK鹸ÓP>1 óÌ, b:gÆx�Âmipõâ/‘´Õÿ ¾(¼/‘wÉt$á$1ÓŸ„$zùvdÜÞc ˜êÄ+Ôwº\tK�ëóàÈR.AÇÁáêQ’Iô·!=˜’H·Ãµ2 $‘ÌÀˆõ¶¾¸^,˜Êcåz×ß¶LG/’”} ð–ì_„ÐD&± ðEÙvHbVsC‡�ŽË•ßÅ�ñƒõõu4Áþªª ÂáÇ�•²hÑvHî 6¬vPd�¿Ö,‚(€ ‚ªU>lk«™8ÑL^^Aàä;i£ïKjjÜ�P(J�î�p¤5‡¼\hYm Ûv4ì”k ;¶Ë†ÖÐ*Úº8îu€®ár¹2�NGwÉ á‘Häpeeyi�ÇG‹Ùäe‡dÁPÅô•VËwÚd°˜!I¡6`ûÖb èêºýöûn×®Ã<@I( ÖÖºVWÕ’––FÇ..TPd°š¿Ò¦€Õ v;8ǯ´ÙÉrz¸ûº¶téÖ�nÝ`Q2­º®�  @,Ýáñ4 J �ÚgB4f ¬²¡å«“’ 9ù«£ÙUú;ÜL}2©éYÔÖyyæéûk÷ìÞµ H$@0ØP__@§Î¹àÝ NÐUÐ$M×@UÁ€J?9á�œ=ÐÅ–¢¯ð²qó:p@‡O�ò„[ ´ôÈ!¿ß׉¨´j‘Ž%]M‡ú T7@™Ž”ƒÒJp{éš²—§f7CbÌ\ø,O½ö.…ùtîÜ[Ü€–p¯·¾ÞëõVúýAZf’šÅ N–¯L²BJ2Xt&´;Æã—¶fÿÞƒÌ~¢ˆ�¾“ð§ŸÂ¤ñcéоS& èÛ÷”ºªªÊR�ÇGzfÝÛ:@ÐÁ*ƒ]F´[ m8477t;ÎÍCòxñƒÕܼ*LyþDpP’i EhÑ¢E–ÝžÜD=Ï!ŸÏ‚LïÍ@ÔÀòÕÔÓ$ ¹ê1–ô÷2¬]6 _Z΃Çs ž�IŠ![˜âe4xëÈÈ̶)Š5°$ص«ds4GZfª !n2ŒÑSÞýgB®bâÁWß"�\À…ý²ùC�J^˜(ñÑÕ)<=w€QxÙ®l1%%-P$dÀ€¡}üÁF^»ñfb+?a¬?�XÏ)dÉ~.Ú�}ûp´ôç�FNV&)N;9¹y¨š _c��ŠÊZV,_îohðD)áƒ�îÚ¯‡sªF͆õœ;g¹O=Æ­˜î£/eõе˜Ì"cÇL& ëÇJk"[êöG*+?>rÄWãvûŒ=ÇGeUee‡÷ƒ¾h$Qî¸çå%¥µ^}ÉÌëõçÏ8En+Ö�.Y¨ß×®�>gÚ\}Æœ›ôK/�ìØ±çŠ¬¬‚Ç-ûŸ€9ÀÀp` Ðh ¤@Â- †ý6üáVZ—fÀÅQ·i�å´‰…©üø-—Ne¿ß#ïÞ]\<àråym¶ü £ Dš`ª3éRå½gºut¥3|ÖL4¿�ºåŸRûÞÛhÇÊhqäzu9—]5ÍtúéCF"ÐpÝuKó¯½vÑèk¯]xn÷îý]üÂtYáÈvšô»‡�;ùœAW^E<¦fýZ|[¾Ä½bÛ5žŒ\Bè4»z{ d×ÎTU¹÷6oÞJp&¥æh“I==»·¿ôÁGo? lÿA€©ü,C:µ,¸wââ…íszô»}+¾’-ø ë7md�ÙAã9WÓ¶ï`´P€¤”tRÒ2±;“‘­ETÌèXˆÄâ}^üî£4ÔVx?ûüÃ'ß{便€@˜ËO¸vÈè3ÿ8tÉ2‹`’ðnXCÀиcÛ‰Ú¯�¨³~ÇÙW]FfjºѨN\ˆÅ!bS!þ�h„ƒq¢ ˆájÞyçíMO¼ƒ/ðÛñ^ÂeØ óaÌ ìb "–ò±«RÍ5é…<wà;ÔçÈØ ³`L"Öb*» u¿(5_‘‚®¦õòÜu˘@ÌqÄV&£©kÎàhµ¾ëš·¾H¾ÃF˜ãˆ�¶%öÓOǤ']Íë<Á<ïròù ëaŒAÃ;&áËápÆSó:W“{îêDòòùóa,ŒBKQcZÕ2½ûmy…™ÐµC®Æ£Ñ�¢;¤˜Íí‹Äûôê¼|ùÒ®�Ü|ê#l†10M…+Ü6©e‡gÄÿˆnjj ÌìÚ× |qØ}ߟ0‰)1ß¹F¢i+¦ÖI=l”ôÔþžkR«UhÑ$¦H�˜•±²÷<­°FÁp´µFŠ“Dpêsh,ZKÏ·Ì-`K&×réáj)xõÆ@_¿�÷]ß cÀx£aÚZ*ý"‰àÔçŸDÞâÆ;—À´µPj]’eÕ«ƒ/0%¥;|Í>Åù:q/¿’ ´j? ª™À,é×\[€ªÕ3¤‡VàY…á¨pN’â¶gŸ®Ö4'õ ‰`U¢Å02\ˆl9„–‰®oʽ¿ û?\ŠÍxç™åÞŒ6&›?¦ÐlkäeÂ6‰ÇeÜŽ‡µOݎ͆IËFº ßÊt ™ #Á´”jvL*½ŽÃrø[â#ÙJ'>"r$ó%Q3Dj&Ã`YØEòíñfÓP&áû@‡¸)ö¨ñÇ0#9åšGø�åþ–€ †5ð&á·°FÀp°®ÍVsï˜@|×D˜È\8œÈOÃÝ?„Ã0߉H ÿšjý¤ZÄ ‚�Á0$ÂP‡{n�˜›j¥1[µH=�ÃCøÅXi5ìÞ½‹Èóïù?dÂVåa:<ÑIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/refresh.png0000644000175100017510000000357715224505336024211 0ustar runnerrunner‰PNG  IHDR szzôFIDATxÕÒ”%IÆñÿ�ˆté½RÛ«ÎØX{wó,ûºo½Î>ñ“Oœ¼üm—ßpêKNýDç“:ïʳð9JÂC8’6"ž¬­~è­6Þªó´Õ§±¤w q…y‘3Zehbˆ‘ѶìÚÂçÿùJ®ó·ÑÅ3�I8ãùËÎ_~þ"àg¢ŸÌáÜ[2ý’ËO¸¢çüΧ©›dEÆáxÊÃ5Ÿýágùöõßs^ñ4à LSÝê¹W{ÕÓ*TÆ€²ð,Fãú k»×?ðšó®C… ë,AØ5¼‹OÿäS 6w¾­Û‡bž·©÷¸'Þïôûó¸ï? @ÇB?ÄûÊé«Ïºô¢Ó/!u)ÿ O<~ú‡ñ­­ß¸qcÿ¦Õw8ù.xÆã¥?^«@ûxÛñý'Ü÷Âs/£i›N¨C²<£PÎ9nFü¯™âõ¼NäE¼ù¯nè|pÌ<è—þ7ßêÒÛ“KÎBJcÃc\¿ùz‡÷8k-½]½”»Ë,é[ŠU+ˆ“˜ƒrs…>tÓÛ[zÂŒ$ ·Üî²»–½Äg!å?ûåÏøõŸ~Í …BDZçC³?QqæÀ™¬Z¶jÁð¹øÊçË;?Ú*ÐwÿÙ¶¯_‘lxȆ“�Åᘯ1Õàëßÿ:“ÕIB˜T*ˆ“笋òÄ‘–sŠØ��UKW1pÌžöÀ³¯žóøþÈ[¸ÝSÄãgŸwi› ó‰¾òí¯P­W Åf^ê—ô[µÖï~ "²4h6œ?°¶, Ò~ –•—1°n 5ÜÎE;Íï+ßYñèÀíýÝñgÖ�~ói¥ùÝïÇæ›‰Ä£=6Ç]æ`;Q(ulmkó‡c§Ô;lWœÐK;–Bd@ÞZj[ú1Ô@àʨ«ƒz£Î|ã£ãû‡k§èðƒ,ì O9;”>3k³v}@B‚Ku©Ï� ’ Òd?“× €¤ÛÐh6˜oó¶ÍÄ…‡éÔ/:ÜðÆÎÆ[*:»¢z¿&Q±T-¥ìÊH]h4�‚K㌠ŽÑÕÖ*i²ŠèÉK9õz�"ÂTm h+ž|ˆCè�Ë›¶F»ï_}B•r©›rVFªÂde ¨NU™šdb÷TAjB¢»†ŒM-@WZ¤äMËŒ<˱¾E|�:ÙÎÑñ?qëŽï]ÞãÊ�ʃa¦« êªCáDƒhшñ…^?A… ÚÔö´(®0…unph1 ­4b„ÜäÐ.€b¡„ph_"þ¦À�a¢6YMrS€bZ¯ �KœéÉÚN~ÅQÊê6DS¨@Y¥¤Z‹²€(ŒñE�´O'iEz¡�â¾%)…ët_v[[ªŸžvVW4Ûjq5òÌ—:F�YašPËÔ9^C�Øl³�póÄ– zPŸôùÆ8œ¥Z±Ûž!gJ»MIð‹²ólo¡mÉ©F’K›øb›Y1[@K=¬x×ÖJÙÜæq+žV8]��²cör]rïªD5ʕە]×yJ°¶±ØÆ‘�>‚ç4&1žr;Q@}¶@ö3w½~/� JÆOÛçž‚Qj£5’¨öp*x´‰ÔVz›�€I$n4››a÷¦¾““ãªë²N%’ìM Dˆ€ï¯TMu*kÌ,¡™VŒ³¨L|ÆTOÐFð´ÆS{cfßk¢(- UÖ´wì3Ñ¥&Oˆ­÷äN<;ó‹ËûÎK:ëý6VJ"%âOoïáðã)m¼�Z7$Û ¤7/�¸oSî¡]‹ƒ¨g­÷ÇÛ›Àhüé !(yЭ(¯�¼d�çç+ñrßž ”(­D´n_ •Â9·¸hwÛnœ¬Ú²û3Ó4sò¬dG:*ÁÌbE¡ÁÓ­¡ž1û_ç§U?£ddAAî ET¤EK±/çútâ¶ýz¦¡ý$�/(0ë?]¼\ude‹ÒÒ*±/3%´&‹,ilÉö& -yP`}GaN9*4NÎኲŽÝŽ_L0™§ïF˜Gs Ï�¦õ3W‡åu¦¤@Ó*0=ÜFŽ4iH#KMðN;Æê�Õm¢bÇo&ÝîÑê‡�±€æàÞ?”VÛË.:kÑ¢6i*‹g4ÚS³ƒÓx.Y`)ŒkMv‚B» ýµ¢W)vVÞ¼“ƒ0Úã®ß2ö½�ãÕW¨«Ï=}%C¦Ja ¬_ìÛz¶ˆS“*ÀàYX6ÞÎöL²ù¯ã?î lá„Ë9�‡$‘ÿÄãÖö.¾ÞŒ¡×¶ß±|E.€ßlß�¹Q¡6ñE¿þÙ®îQÀGùîÊwæÙ܉Ïsg~Ùvµ7¨ïªF¸3¿ß›¯ïÍó÷æ€áÿÅÜjd%÷ IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/status-running.png0000644000175100017510000000202515224505336025537 0ustar runnerrunner‰PNG  IHDR szzôÜIDATxÚÅVÌ$i¬bá{�mÛ¶…™ùÝÿ¬mÛF¸‘οmÛ¶{gßU^æ[c„JjÐzõÜð±M·ÃÕü\ »[Ÿ‰ØºFÄÕŒ"¾Š¬hDBY&Šw!±ø#ÄæÝŽ�ÁÙõœ'áj;�˜AL³ ¶‘¬ÄÕ’Õ‚øJ²\�PJ  Éü$後€áê›Go*œÝg' w,þ^:$X7!Ø4%Øvšô6O Ö� –óüœ/" Iùdn*sgÁ/ü>ð9 G�÷æÒà)Áñ�›)paƒW@…çLÂÊþ>ÁÑ=G ;úÞzfëCâFFev Eä�™K®öŽß4äzmÑë­žÀ�n¥ó«ŒArÚo—1Þr?\í¶æÛMï·ëBÃí¬••�E6’SïÇy˜#׳ºKYé¬â.Á– <ßF/žü…Å×\$æy£�^Š9]n�m¯Á†ÉKÜ<%�<ú¨<óÊrý—»Õ£Àjb˜Æ3É F9ýÓsÔhoÏ빌ú)yùµ7¤¯¯OÖoÙ&Ó{Y�˜˜ˆÅÕ*VZ>qµ39ÕXñõlŸ©+ 0¨©©‘ïþUnyökÁêNÿl7i Óf‚SÌÉ‘ÊviÑ | °m[þþç_yõí÷ä¦�Ö²Nü˜óнRà?�8¶É²^¿ ÉÞCGå®g^cÿóMÀŠ@žgx1EÖ�$À ±±Q\ –ÜùÌçt¦éÊ6 j0EàéE|Gíd0�G2²²åÕ·Þ•›ß˜Ëvž¼\=P<¥[l»'Hç§eÏ�Cr÷S¯\\b0�2&ÁÕ9ÅÎ"Š €mç (è¥Ý`ÑIARn±®ÍõÃ.Âtá0*œ ÙíQjC+Ë +‡Ã¡xÐôÞ÷¾÷y|Bˆ�‡%@"¦2E,�o}ë„Äé—]9B+õÍ€ì#qc‰Åò”qMi°¶3Ϩw¯eèw¦ÓÃ3×]÷Ã’,àРș‚¬ÜüÖ·¾åÿö˜¥mf¬ýº®|ß«ò#ƒ»»úû,ÃÊè3à1N˜çqæ´˜W[w[ÅÕÅV±tIqºvœÝ𾏩öP:Ä|ª>b€!æÛ`¥•`mìžÙÊBáïÅéúß�3—”÷/@WKB1æW¾òçMÇ�~å†÷nÿÜ®§?i@Ïõ“¶/ˆ)eŒs"7Ú­vÊÎ]ä˜Û@0wo«tÏ]'-ª?Cí´»•ˆû†.À¿÷½ßmþç)׿kp÷žÏlÚÓò4Ñ%ªåà�Ä’L‰l/�"Êå·3Ê„H.gŒú˜W¼óÖÓ «_C嬩£Eh¡õaã}îsKsò-/íݺã;žþŒ|�Ì@pl̈à—"B¢,ì("„€1.V=tdo|Ó¡#¯ìíù,ðÜ^D#b;`ýèGõSO=÷P2›ùèΧ>¹�&,Y 'êû¹F€dLG:¡ÃJêHD*ã\:#)ªN5h£{»w¾=74ôàÍ&"a„ä8æ'Ó»ÍzÇö'ÙËõ‘–ÏÖ”Ä1�`$ob{ £9S�p†…Š�›fê¸g©‰RÓƒä�r¬�zvïÏ”KâöÂ5Õ*îŒ 4Q=!¤q°ôðË3›·NЩè•Cå}C^ÿ¤AŒä ¥ºFzð¢.μuÇ\S@Û÷À9‘NiD½ÏO÷�îmÛ­/­¾ÕçΗ5ÙrÁؘ™I ˜/èÛ±mÉ4¨Ë6ÈSè:�O®¯`l¹…J˃Óq`ØÂóöõâ]ÏÁZÝÉ7,Ë%D‰ mJ�Þ’´úî}Ū³vŠÈ ÷®—³Ù|þ݃ƒF»SýFÃÝ×ñwÌ70·f ›}Ñù>ü€Ë†»fb]¾ñ)CxöÞ<.¾»„嚫Р³\©n’éë?dÎ,ïp€y4œ{�¢3”Îgw‰ GD•ªËÁAEV"jŽ�™’ƒªZ>ð‡+òŠ »}¡!DPäR1˜qÊ~²À×b°úúsÙ¤µ_4c<œüâ¿0 ËMve²ºZ*\Í2cŠ\eåˆz àjÈ/É'×5y¶K¡+R5f*ƒ� –ÉÄb]‰ÌP;ùä“ A��§R —“�e£æ™)¸tE�•¯Éç'^x ¯züÖ[>.º{M:E4:^…Ú’‰$1ÆPŒE÷€AKh†!ìW$,‡ÚrLdÆÃ«‘ÍØ‰—îÇÇ_¸UVÿÏ«qÙ½ùþ°ùT–ëJfÁM×LŒôë¡ìÙ³‡� FÁyh» •ä÷U^‹ØýrþŸ¶#‡*DbÌnÑÀsg�¢î¹¸MîÚ•õsì›™‚˜ÊQ„R�Vy–×Rjx8í¦U9’…õv”Le‰H#‚ ¡1ÔÊe»Ñ¨�—yP£J‚H±RªÜP_]aî#GÙê§Vdõà ÜôBÂ("‚ CCÌwP[]™áŽv7%qBÓ©{×j&éØ E,{Av½$ ¨4 OØÚ�/¿b¾øòírk« 6¡:›�[,Õbå’jÕX‚ЍxÞ¼¦<;w-©¬ð„¡)Rq÷²ñ˜âÞØr1]“0:$²ÓïÇ@¾'E[(ML.UJõ³E±µ�TG4šãmø;\#ÁŸ“Þ4hÕ}® çѿءJÕö0Y´qÅXwòÊôNÕš‚® ÅåLgæoöö»ÿ^ž¯�L6ï_@{´H¨“±Ò‰'¤ûú ‡IÕÕ"T¶=†™5óeG‘Kb„ÄJ„®!“Ôa” lúºë®,O~ìºçÎà÷+@ŤÝ$›g ×Þ’I'vZ½}z›ª”ôRE(†C;ªêðlèYÓ@¢¾Âg®¹æÞâ\á[ÕµÓ¯ :î/œ‰jÍÙtìÚ¦LBÛ‘ëë1˜f P“!£“Ôr‰V­,7ãz’€¾¶Àù�ËS3ß©ÊsT<„€Ã�,ÕýÞ;h½®ÅÚ­íùî”™îJ œ+” †P7t¤ã:r¦°�5Ñœ¸Ó™½þ¦Kç'ç¾S_�_œmã~‚à!ã¹¹Á¾tö¼/72údkx4£YY�Å“ º¡Dƒz NƒÛ+KíòÜüøÚÜâñÅfëDTΚ gþÑ PGÿ †ó±î'›™ìóÌ.ëqÉ.kÐH&ÌŽó< ®ç´Ênµ>Ö¨Ö®tÖ›—×ëÅÙè¿e�Q@Dö[Àp=Ÿ¦q‹§MsˆÓL TP¦uÀ8ÁÁÿKüŽ‚Ù׿LIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/worlds.png0000644000175100017510000000414015224505336024050 0ustar runnerrunner‰PNG  IHDR szzô'IDATxå”lkËñ‡¿Ù³Çއ.ãã÷.ý™™¡(*3WP•+VQXfff¾ÌÌA'q4ïîÔ²Ž•QØŸôiwÇ0¨á�^ò¸×|àD-IA$€7­‰„Œ�/ð–])-|âãÿ‹ùåÞC"AÈÄ‚6Ê|í{gï‹âHQƒ �žæ§ßIñ@(€*êëÿ|ÙÖ«kœºVba¥J$(0>ÐÆ=(V@¢Ö 6Œ FˆT@D0&­h$ ­Sð"úƒ£çŸß À'uŠ+ïi659WÇyO¨W¨Õ«x ˆ@z ¤ï”€*ÀFÀed [ Ów2@$¢Z­Bð?WVƒâ´IµL±8E±0G£‘ ((`6Ë €¨¦w@ÐÍçÚó¬-Í5l­R`¥8M!u,?•ÞS.¯0w÷ï}ÇK±™,ëÊ/–ðóRR©¢*ÿ¶¼¼~ÂNݾ @q­Œ†bU6¥Šs5Fú»©Ôáâ¹YBZRPÒä~Ú[ ­Žüô 1”¦Ú§?ç7ÿþ¯i£ÑÐ_™C¹Úàê�eÄ€aÓ© ˜HPç¨ÙˆL“àÒÊ„¤ƒËJi{s²´sðü5ç±Bøé~Âfôj C@ˆ ¥‰2}uš®Œ!öޤR§Vs¬5Y©4èîed[?ƒƒ=$>lVV¡^-c“z5‚5 ivÂf 5@5 N Ó—¦°ëëzx‚©éU¦ «—*8¨VŒ@hžåÅU®Šðà_ì£-—Á+€4`\€  TÁ+ˆ‚`T!Rp©mæäuvõDüÃ?ÝÇÎýc×j¬V<·+äFúÞ5F‚Ðz»îâèWÎR˜.@ ŠÑ¨b í¤mLÛIZŽ•(Џö�óìßÚÓÚœb#j‰çþG¶S·1Cc½¨wì»o”û˜`ëö~æfKÌL.1¶}�+?ºÆÂL 8Àˆ@ÐM, ©sQ€´"QÄ™o�çOg²°N¾§�îÞ$2db‹GÙ³wˆÓW¹6¹Ê¾p‘¯ÿxŠïž˜âÊ­%âLLßh7?üòY|µ�*˜ ¡JhJ… „&^š,.¬0+¥�ó¥ vdql(×WfV™Y©ÑÕ×Éèx¦Q#η1±¥§ië ¦ÂÍ™5Nž™fm©Ì¹]&Á"à‚’ú¸´ FÀ¤Ùß>~ƒíã�Wêüß=@W³¥õ„\.f÷®âö,`šèç»ç¦ëáîR…$Šß RK(×anj™aSŦ兠Ó:p€Qˆ$}+X î,ÒßÛÆèDÙ\–Ä0ÂZÕamÄØH7G/Ìry²„f3¬&"ƒm‹Q#Ì,¬³ó¾-­ý ‰cöæ4Fb£¨‚÷iæ|:í�F…©Ë3œoö¶’®ÝYnf^Å£¸¹¦ƒ�ÞJ«e–šlÙˆ¨= Ù Q6FšTª5:zòäó<‚H£­5 šÎ€MOiâšdP@Y˜+qï½£|ùû7),×¹Ü ¢-Ž)”ʈÀ|iƒé¥2ÛÆ{øÿ¿ÙI.Ÿk9M!ÊXâ¶ÛäoO€¼�±ÆH«|ð@Gz—c„�xÖ* ¹fV×fV8œ8Îß)ñ£ sMgz{ÚÉwf *xk°Q1†`À%jË’ÍXÎ\/‘ÍZVØS7ç¹0ó¼*üü@DuJaq�üPQl9~¥ÈØxsp‚³wWhŸ<»@_gƒùšÎE”ÈjUèì°”–ªˆ÷ØX°[úÛéOW ~ZF@U¨Í-‰�8O62`„·|þ2ÿõWÛéëÍó±Ss­m·TnP®8l.ƒ8�¡�‡$BwW–óç§ÀARX$Õ±Á+Õz R�ª¤ˆº²HPrY‹SšmXãOmåS' üé}CìëbÇp'wVêÜZªÅIï qŒFŽ‘î×®p+ëôX¨5–±÷îæ�{ïu  NÀ $ ±€XËä÷®³^IZÛ¯{ <CŸ+óâÚÃXo;G¼2»žpjz•3wJHVq5Ç#»ú9zr’H=ÎAw>¦{´ [XÚ`l¥B=8TA xÀP@Œ°ÿànNž¸M¥\§Zª°Ç¼ ›(‡¶öP¨8T=þ|gí‘°¸TflK¾¹’oc 4œâ�k h×D& TêUÊõ„ºK¨6©ø„Zwj).©³çð¬wÄ6BGRmð½«E¶õµqi±Œ1Bd£&Ã}cÝ<îÈçf6P§øšC¼§Í†ömA}Àf"3³Z\—È PÈD°æ@Œ�p6€¶þê¿æÄ÷ocºóiKàÔíòÝíüû}9¶e-U¯´÷ÂüZÂçOÏQ(¬ õ„H„`c†-ý]¸²?+tïx2Æ´ "¨ ¨€ô®fßÞÝÿ÷/˜Íå4îí�О%?ØÍ þ~;‡Çò¸ 7<·æW™™]á½_»IÆ7Ô—6díÊÍÎ>òAj� +×Þc€wñ[êÆ±+ïxðÏöý0—ïéÍ�véøh—Œç"¼S–kŽ ël”6ÄFÕ7ÄMÞ¼œMñ»«¼éÎÙ…õÙéåƒÓóåvÔ~|e‘Åùu¦ï–¸xô6mÅåÏ»…Â?Ÿä§… ïx í£«óo3�¹q1¦ ×(úõêÅ™ó¯ú" üý6]w_¼5 ´IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/log.png0000644000175100017510000000141715224505336023323 0ustar runnerrunner‰PNG  IHDR Ùs²ÖIDATxÚ�Óµº,E†á·ªg¦·+îîîÜËÉÈй Ã%""ïwwwØ>3-U¸ôÓǾÕ]UÑ¿<Âãû¶Ú�|0û¥~ëzæŽ;Û|`kò4ïæ¯Ò 7¾ o!K’aX¹{ë:=�5*Y!Ë‚�lj ª5FÇ„tÏûµGõh|*›ÕjL¬6 5’y«sŽ+òƒïñ¨~•h¤À®¡FmUk"8Ê’Z­Ì9¾H¾Ã£=�Eg#JZC A$ ‚9'þÅk< àÖ»î¸ùgI–ÕfÌûÁ ¦f dÉѾùW¶±Õ¾´Ïc@„,ÙSKæQ«,J‚‘V0BáGZ5¯4ïØbpËO±“B4£•Œ-«L’•ÊÀDés…Ö®BaÛªåÑgQ‚DCÉ®¡h(šÚ6cÙЬ�s¡uRhe­ÆÑh•%…m‹‚_QšSXô³ÏU ÀˆJS­mS¥,KjCÉŽY‹~RÚ5¯v­¬�ÌYp,8}Ö��ulûÞHÀœŸÎ8èf�ån 5Jm¨é#óv#ÛF¾sŒ_jK¦fd¹[Ä`Þ¦YS­µ»ZÁОŸL eK¢9Ážõ~ÑF¶ m+%˾w¬_Ñ’¤PkTæ…-Ý,¸@—p2€,Ë€“ú5ȶ|¦5’̨d;J§ZY—Vꦰà<$Q×oÖ'õ#x_¥00²îkË~RZö«`A0Q˜±!:¡A’¬¡µgÊm­ŸcÃŽÂP­–Õ µÝ�EÉ®`ÍÄ®c4’€dVÖZ´a¤•t#�dÁž5›¦~u”“$Qà÷ñ€V7KÎÇã4 ôo]�-ï ´d¢r¾¬ÇÁ»°à:ú§[Y–pø$“®@!2è¾ûsXçxîùs>Æì/K�»ÿβ'ž9:á ˜ùø„XDd€ƒ¾S^ýÎðDbœ3 �WÑIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/minecraft.png0000644000175100017510000000440415224505336024511 0ustar runnerrunner‰PNG  IHDR szzôËIDATxÚµ•e˜ãH’†_�eK²« Å<ÌsÌŒ?ï~ß-33333333ó0Oõ<ÓÜÅe*Ù–lñFç¢gq(º‹”™ß÷F¤¬qã_sÎs–––ž ¼x&w0n—€û¿ÿo-Mӆᳲ,«åyÎôô4"¤wøð᯻EÀ#>ñoO�¢/*J†�ˆ Õjá8²F¥R‰MÓ|6ðÒ»DÀýÞ÷7Öüüü¤ØS€Iù�$IHÓ”~¿�ëºê§¸gee…jµJ³ÙD„ôEÐ+Nœ8ñ `t‡<ä#ÿô¸ÑhtÚ±-E‘¤~â8VE …sssŠDEÈ>%¨Ûí2•HùŠäܳ„˜!ÿƒûÙÿ6%ÁÓ,Ëzš´åw)º®ãyžrxöÙg³±±�¬)ü«««t:ÕŽR©¤„ÌÌÌàû>›››ª5" ñ/;ÿüó_DüZ˜OÿÖÿ”ó,tß ž>55åJ!Úí¶*>99‰ˆQÅlÛF¨�ú)…ÔúÞÞžr¼¸¸¨ÖŽ9‚ ¨$9ñÅåå•ggiö$s2©î[¯ú¿$pï×ýÃ͵e÷‰)‡Åù%š{v¶·UQq¯vz½žr»°° ÖÄ€j‡FZ¥žßtÓMjí /TÔ†ÁÓÑð‚ý�ˆ“—y›Àò/ ìõ\¯9¢±”Ðܾ Ë�ò¤‹‘›ø�¯° %FŠ+¬â€õõuE¢Ñhpå•Wª=ÿò/ÿB'œ:uŠQêS�ªpb½ÉáËö¸å‡Äc-¨Ï‰Ã¶ôìº=Ñ€óþñ ¬y£NÙEOõº\.#¨UâãÇ�³¶¶†Wîå™"µ¸°¨hËüÅY\þíëøöû¯£}8f䥸5×Ôò1ƒkh£„3ÿ}‰n»Çæ·÷8R"çT˜^Ž)M�ž×C†J;88P·4¹Æô|•®ßfëh‡ýõ Ö¿·Gs+Ärå̼…¦C~(aL@p£�5»Ìî­]Ò•s?'9ˆ8˜�Ho ¨ÎÚDA¿6 ^k(‡ûûûªp8 ±‡ý¯$%âúËnÁ;œ²þƒ ¶�6Y^žåÌKijsp¸�U4)¿à̵*- ù¹mº×÷VbœºM ƒÞ®‡*fûš‡~rŠ4„¿úû?gí¬Uì’� .NÃàºËoæ«o¿–o¿þ_}×å´[æÏi`Ï–îùh^B5)¡uA3´_~)m¯OÇüÝŸ/ã%¢¾Ó"égŒ¾0¢t�ÍÔßÔ·FÝÞä‡_¼��Cû,œ=ÅÚùKì êë¾tœ[/Û&ïj”rƒ¹…:Y˜‘ú ÝA‡¨¯c…&•I =Ô©Uìñ¬ÍÖ¹ìÐá ^L½ e§ÂÉ�éŸåìoµY,Ïpæ9Ë?¶!ƒu7–(ÛùI‹ØË˜ªÖ œáo$�”,KÕMHN�0 4W'Š ]'NÒqå3U—›Mò‚ÃüÒÓu§P$Ä„iB¹X`zÒeßóF ø¡3'gýDSó1&4tMcÒ)b:[í¶$Ù“äƒaÌH’\¼6ÃâT…õSM!–³0SÁ“µÍ¶‡ïçŒÌ8 ¬°GßíR’bNrDÊ”]²„ŽM§¹7.À •ÛjÅæ’3f¹æð6ëB£"‚’4¥>!}VÁÀ)ZŠV½â é!ƒQ"¢ ¦] ÞHÐÇ11£›rF„xr~g$‰ÏD�qMAÅ©*¸Ýî1-ˆÿî¢ [-O]›ÈHéô‚QDµl“¢fGíuÅeÕµhœFž1KŠ“gÌÖªô‚û%+Qm°K.ã×P +gâö”l<ešaã�bò ºö˻۔­ƒ@M3 ~†"~»3`(ûÃ$a¶ZFÀ�äº éCÕ !A.®„ƘAìÐí‚y¤^ÇwºÌTÒ$&vg™�µ±3Ÿãû=.\�ãVvØØmQt\�„9W*šL‰¸QœP,h$º%„N¯õ{.Frµ'Ë!’…1r/c/£MÙ¶¸áØ® ]Uƒ¨úìwB&ÌL9=¹Ód²1ƒáÖØÛÝ–ú¨a«–KTÜ…(¦ï‡RÜWTî„‹fäBÍW-0éá 8^¼:óqÁc�µØø! í6;TœzÒ9èfF ^@qrš¸P&èìÒ¥*©xT3!ç•ð4ËÔ~1.RŠ–¡ò…qòiïÿ‹i_¾0QÁðIðÜ Œ_4=Yz˜—ÙèÆdѱ@£bÓ‘6�(ìÒíÙëËzž)¼™°¡ºº–„ ®ÐÌ�8Îä÷â[…Ö3€¿:ãÑndQ¹<»òêùµ³˜®(¼£˜¦(wsµ yö‚ÕQš®)§=àùCüP¨ Ç.R0�× Q6^|\ÀmÞP4õiy£½Q†4õ‚‘º ¦Ks0 &n‘¢iª+l3“¶<·pê³iµâ¼‘<›¯òýŽ0ùýÑ¥ÁSlË|ÉâÔÄ£s`³y ˜«ê Y0uÁŸQø9zǶހn>í¶Eo¿€ñ€Ç ñ|yñ z"ȹÞ� ç ú�Þ:¿C˜ç9†Ï¯Õúj£ ±óP‰ì"•#á¯`¢2€à®ÿ„‘p³ÂrÊÌÈG¨i�ÙfÌ•`‚²‡„ç+¬Ã,_ÛàÊ{@3ʆͮRUœ‚¼�:PÆ¢%-&�Í[ âÆH|ƒYqy]Oïèù‹ ›ÿŒ»Æv'u§f Çw‹5©ØÐ«È¤©ø_`ÝÀVrŒ#:‚¸ß8¤#XЭØ?¶XжBRþ ‡¶—O.™Î ¶Œè–¨Àâ^Mås+¦�íã{‚Å�ª¥½@ÌŽïÝ)'÷Ü;°MÞÙ¿5;~x|O²ãoܧ—j�ÿ4–©Åm*È'控M�ƒ ÈíJ劇~ìâò‰`÷„®`D�"ò�êù¨´ ¶÷ovë 8�Cc:€eíB þƒ LiTVŽ1«q)PÚ�ã*OJ¼1“•C«©ø_ sùlrŒŽe- ¼¿”ñb|<�á5‹‚~•ò‚” 0R•ay>gU�þ• Я£ÿ%« bÿ)+TÈ‘ëX÷Ð<ß,áeÄœŸHðR`5Ç 5�Œàÿ–{ª­zr{)EŒ&ŽpH¼KÞ~L'(ä1Ùà}²öIÌ “ÍH áØ@ÜŒ°Mä®âEz4˜•FÐ ´DI$x>F²¹&f$ä —!(@/�ôÎFK˜T–âÇÑ/ QQ!Èšá+–äX‹•äif‚›q)zš¨"FzDô¾¨ºE7‰qDÃiæÍóÏâ½ ‚AIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/gear_legacy.png0000644000175100017510000000433615224505336026776 0ustar runnerrunner‰PNG  IHDR szzô¥IDATxÚ�—tÛXÓ†Ÿ‘dŠÃ\nËÌø33333Sû133/333—ÛP!I1ä€méÂüIÖ=ñ¶Yœs^‹õÌÕŒ^뢪oK€ ›ÞñðÃͯ/ìy»÷ x! ÜuWºîÅ“š‚®5unW]y¨»¡´b]í}­|ãámDðf@y×»‚Eñ®€�»ÓÍ­I{:ŽÎ ¿?’àÇBÑŸ@ù‘TÉ]ÐP?µ’GÞ•®©Ò|¼.CU—óâ‹QkíhÖäÂHg# KÓ.&¬É9wªsþL1¶S�ÉalDì¼7æ¨Úd³5敨T.zK¤SSå$žŽìûéDAß4Yi[”k˜NÔ×áD|’”Õ›�’9C½éšèý£ÿÀ)Xո܇¾Œ5C:—lÆõIÒˆ±¢ÎL†¥òÐt¤£°²¬7zª"ZÞâ3­jM�¨®êpI(ÞÇjœÅ¹j}M5jLc×zï2b]^¬oò·?å¼l­�ynÖ î¤¨¨.ß/~9Z¹®O/^W}¦á¿ð ßûAg욬+vÿcã-ß·æ}?] ÇÂm]_¿òÏZž9;­ñ)xÛû—�/\ü¾u�_‰s§ªµ—û²¹43§«ùÃ?ÈÈ|œPiÝ}[­¹n¯zvàYùÙ¦�ýȯåù!oDïœ>ÿÅËS»Nnf¦îxø¢*OäH9_ºïÈúC¿Õ²c}àT>±ç̽ÿ½ýœ!u¶O�¹;íä©™•'OTJQ�À»‚†ý?Ü�.Ï�ááìÏ7|âç-ÿð�P rJaŸsŒ†±1EBè\!´wÀšuõùã“S>:xÚðÿí:óyµînñæžR!<¨×]çN(�ÎÎ Dཌ˜æYoD«áÛîòÜüËàf¥¸¢Gøùó.Y#4ª0Üç~-Ü;ÑᙜŘkçdyx„¥Ð©Æ%5®ü¾ñ_ßrïìù/ƒßû)Ë3xNé~ñ<á¼ °¶jr�«�Umpúz¡£^™+¾ ÇÁ7öuùʾ GÔùiq®hÚœ�€€ðÛ¥¢\’Ó˜ãlÞΗF;OYù�Žá½ðã§ —tC” D•õÊ2J ù: Xøé¦Ñ¦È&ž$‰µTN›™B#G�æD¤ÂV®Kµôµ´I<×£Ž ÞÙÎÍ_éÖßðs…aåæO9N阇÷Vn\œB˜&Š Q%¡T%¡C eø³WÎÞûí=û4±{Åš|œëɧႮÊMk�� S ¤”Š¢*Õ~¦ù` Î­ÂØ ÕØŸœ¿2[ζó«¿z]Øør~µˆ¹\¼öޝúõMÕ&sßW­žùšW@—…°Fð‡Ñ¯l>{ 4.ü£Ž­‚9/ ] Å«*ý¾Ú°ÈÜxQ?Î>®ž/™ÎÒ+�´I:%‘8W3¯ìñ7vDi­g©ÖmÀ·�Û÷/»¿oï?ô]±ï+ûNÆ‚™CUÔ 'z†Ëc|³.°fc 41‚u�w.8þ ¡µñX£ à!p.RgC¬ Õi€c”ᱪ^N4,µ6ôÎ+ôCÄUóLÊâl,Îûã/è줪˙PhþìÔ-]qQƒŒ7ÑŸuì\Å4, ÆaE3ºŒ[ºDœ�A5&“Ñv¨K•fÕˈz?ÖÔ÷…O4îúÌ—Ÿ™éÚ‹SÚ;a¢¼ôÊù>Oy ‚ŒÊߟ±yÃ_lر&°*åž›„ÎŽ`)Ùi 7 7Çé›ÏËÜxæˆ:7¨Þ¿ÄÉmm>€M:Ó06#ÆÖnû½$fúÎÉSöbaMwÀT²ôcÏ:ú¶*3ÓP,ÂàÜ4½=BcS.À~ ·ŒÖÏáüA¼çïôјì!yÄŠ*=›LÉaŒÛ®q² gåèŃcI¾X_ �+,KI¬nÒEå¹—•ŸV¶ŽÁçœ%K¦´ËÌΦü ûêfÔšuzO0/Sd7Û·u>"•G÷%ö´úb ª>ñùRBú�©ž‘ßÈo>iÕ*˜‡¨ªÖ¯Ö¯gé•‹ª\ñ¨Â.§Ü8Ò`ŒqYW�5> ™d÷@ióÚÿ‚�à#QöõXWÿw��Ÿôkù-½Çš(ŸXÎ~«öìôð¸¯4üõš£™ßí˜hÀÙ6u¾5«Ašx�/"O„(Œ WRÈN تnŽrÙÊÕ*PP˜F=LÁ18B+³IˆÍx1À² lÔÈügb}®¨þcô‡¶û’KýuÓ '{'ú�C'�þx~_[gPÊPÔ äÄ÷|´˜r77™¿^}$:å··Øn¯ŸÁë!à`9ŒK<‚?1 ŸþÛbC*¿—²Ùጩÿ�‘+Ó6öÁð\Î|qô”ƒ{þîßjè[óSuÃí?Yw°±~ó�ÖÒÍçn©/ز­Ù|$jX™J¢쨟u}�‰g³«1@—ý*Þõ®\]]¼ÚÛäBŒ;cÛ±&ÔØÎŠIbWNzÄØUÅ‹n>»zä™{/Uëkb†ˆ“Œm[¼Îúâú˜(÷îb``VUýò=*7–fÞõ®‘úLÙ;ëÇñ®ë”iÛ4Æ-¨u‡+Ík@ÔÝ�:¿ÐpX€?(N·RƒÕð7Ÿ˜|æïÒu}­ËHF Ó†5Æ&¥ÿÖüˆÛKbj5±!&1¿G�¿W�û’™¸�Õršk^0(/Áß$™6m¶oŽE6›Mûñ.‰ýeX{–ÓLbµf£[±þqfûY¹²×,,Ð…x“©Ù›ÇÂ<�Á§rÙ™ Í·hLcDârªŒ•£âYt8Ï[�w4=ÿÕ_ ùÕÓÓóË4?Ù“áôÅõð�LÏÿ/4ä¬3”ïIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/dirt_legacy.png0000644000175100017510000000032315224505336027012 0ustar runnerrunner‰PNG  IHDR �TgÇPLTE¹…\–lJyU:Y=)‡‡‡llltXDZ(yIDATxmÈÑ€PÁQh…RX…(Dáüàå·¶bÅ1{AGŸ³èî…qÃp7/ô�ÎJÖ/Ð.:_�\éî8ì…6›ôlEWÔ ‘œÂy¡Ž;°éجÛÔÇ QÓ·\î—`{Aü)·³z!7çpü†•OE¹þ¹¥8Q¥HúŸIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/meat_legacy.png0000644000175100017510000000215115224505336026777 0ustar runnerrunner‰PNG  IHDR szzô0IDATxÚµ— u9…gmÛ¶mÛ¶mGkÛ¶mÛ¶m3Î*î~_þnŸîÓÝl’“ööNgN§§Š”RÄHIÜ£¤§cÔôFŒ–>ŒÑ…uÛüWììS½‚�œ ôRŒ•^ˆñÒó1Qz5&ÖmóŸ6ÚÖ%QÜ‘=c$ƒ¼“§·cÆôrÌE°ù„uÛü§�¶ö©C¢jä¦|LL˜^‹éñ] l˜^�-“°n›ÿ´ÑÖ>ix�#×Ñ{1‘#$ÈéÍØˆ û‚c¨Ÿ.¬Ûæ?m´µOÕÁ_�™Ò[±”£%Èa”—ènê� ë¶ù»-“¶ïÒ§‰"8çÑT–àoÄÖ9š ×QzÜñÆIͰÍÚhËT,]ƒD& ’?‰ñœOSꨲãÀ à‹ _ø7€Ú$ÂÔ»œTtÕÆ¦ÖÑÀ`⃹çNâ¾)§…v/bw£º¨"Ñou„Š£Ï¢[)‹ëRÊge?Ø}õ_HMCœÌ•—×qàžöÀ_ß{¶(ß�œ°¥èš u¤žÔ•úªÞ¬$àÖúqLB‡p² 8[� M gBM¨u¤žÔ•VgÕ>�10ž ƒ…é¼#åE:ÊŽKÊÛ_¹ß*¢“ˆÂT?êH=©+õ¥ÎœŠ�ÄÿKà ú?Ü;öƒÀÊÉ©x%Æ/Yf vZvš$ßh¾f¼Uš¸œÀtlšÏ�IdÉÂ`"¬Oσ‹ñ»;£_–�No 0zäà˰éG¨9ïÐÿ6ÊÙ‚�ð¿ YžÚÓ³™Dß�¨.ú~ žb S€¿u�fLM6ÆýWµ·â ç›\´k&ð+xÞu¦ü üQþý…hÎÀãð’SE úRª0ß�)è<+X„Ž«KDý$ÊK(o§tÉ~¤ðºÈ7ªc¼¡—ZoÖNAÿkù÷1VÎÆ”…Èë±*m›¡‘=ó]ñJ—ø´"õjæiɺǸ×зˆp ‡ ]"tž@"8š‡óC`SªPó¾ñ|ÕÕ \õÛ~8µ®&rLŒ¬qy¨<œð= Á—Áñ¶Ô�§tßxÔe›3r�—×|sê‹·Ï%ÐÐD̈‡Ø«1»i%ÀN”G€3]ó|ŸEyl¹¾œŒÍÐ~AÑùpˆDò{q윉9Ùò”*R‚mÏ÷6åãt9ò§ þyë+J§µI˜ ŒãÖª°(çN.Ûwb¡|¼÷yÂÕ"Pý€õ¨¥œØs_B–ƒ_=†{þ„õ'üôZÒ¼ÛIv½öï·ï«õ‚Å£9ß|ó ÖZŒ1`åÃǼ’¬ø—ÖäÄ +A~»-V¨²ô6aÊœ}%À;ì°az>ês[d'.l{WiOؘoV@jÝÆÁ>;ms®« ‚Û»òæ/c”ðªŠé"…‰Va'”M+ÚÄ™NÞÀôzmžš¨z¬“…aá1 \Ex À”á <ÕJŠ8¢pRZGnmYNÚi)N�AÎæ»tr‹ÂȦýëõ¡Ún‡Ý‰coú"Š" ¸P‡Ê:ou ±~Ÿ[…rê\pÁÈÏ1¦M›¶Là#â&`½ ×ÉÔ Ì;û˜ûG@²ítÊå”ð+®¸¢#?Ëz~ùå—>¿(Š í@qÛËî¼÷Þ{Ÿ¶X¢Z„ÀNõ:)P�k=ÕúŸÀM>ȇŽ<òH®¹æä÷_¹«~þùçŸþ§µvj–e}:…ð»_Öëõ™Æ˜ó€#$ÀnKà‚04µlh½¡/úðÃW¨_ÆIÙ7Å}2ÏLYù_yž/F�"¸º¨UµZ-’Äå™íìÛ‚ Ä´ÚR7Ôú5Ü+ð?�™ÀêÃ2Ï*�¯†uõ.xtÀX&ú���GÑU£îhë‹8]`?}š5ÓÒûGzØu޹¥3k ”·èVi½*:oÅÏ§Ž€LËñÑ\ËÆ,Vcµ;óV\-€w$9и%Ž/–É/ŽvÎítœ¬<"·\ž-×-/W«¢™MÕ0û[ o¹Ôsö‡rÃqÐ÷߯f«Âšì…Ý×g¨~YnPHŒÁ/d´ºª¿ƒ±“±‰‘–@Jµáp¾R^…¬ETŸk‰Š̶:èkŒ!ñÚ-N¾ï§w@¹ñxhm cµ1X=ͰÚëau6ÂéowI,K£˜±ú$Ì®¦‹1›ÄZ„ÓBźJOÕü=îœàÚ.ëáä??ÁŸH¢ã“‡à»í$¨‡n }å!mµ0ëã0êâ0æÀêhI8).‰5&çeâ=Æ&±æ•bÁîã‘À\µ?Áš›-5ЗïÃæ?¿>vûø9(u›5  ´B¯.‡^�1;=ƒ/IØs3áÌ+Ç*RކAM061ˆ5/ |xijE C.8Ÿ53âI«¯¾ÎÂ5|‰5_¹ÊÑ;B]IêZ[ -R -P,«:ßGƒÐe¥H4 ‰vf¢ ,GÆrC˜±ê8¬¾–$cƒXÄ$¶k6±X® ¬�êóÙ‘ßl©«¹|?¼?<ˆìÛOƒrضPǺ •‡ @-È�šŸ%?jQ>Ô’Bð{3ªbs3ÑgP„9Ñ‹ ÉI°E‰A,b¦°éßF<Sc¡}ÅH^gŸÛÒׯNëÃ÷èùP�Úš�Ñ*ÂPœ›%{”Ì™ðÍšá¾çwyÙ –Ŭ­‚ÕZ»·Îp§´h?2V…Þ”ø›Ä"&±="ZU°N”zÝÌ“±‡ÚÁ6ÓŽÜÚFËC¡©¥…”íÎÌ€ÏoÃk™ð9|~(Y³„@ôò0Œê )E5ض”Âí¦ØIb‹˜ÄNõ>•I?—õÛÊ–Ú™kMBßx%èƒm²«°¤8Ÿ;uwîwà5txU^M–iÀ7Ã/¥É…Î2TJ•°š°Å¼HÀYeöhˆA,bÛÃqªÎw «Å,~rF{ÄÝz`-Û s¨ †¤S� æAÉÉÓîµ-ØãõÂãó >Ët³P”-T�$êª\ŸX]üb“UáˆsƒXÄ$ö’h­£ânb¹Í °¦Z°Ø%�—E7ºFp—„¢0#n¤LZ©[ jÁœè†~Ж0·[Ž”aqK”À¢rûàôµI «EÕe,³°€Hv¦KBÓÎ ¸e˜)¢”î`{¦´ %0·\öõÇÁÞ|5­4%XL„ CõÒ^Íú9,…åf#\ÂÖ£(@sñ½dg¦d Š¥Q˜Ò‚�+�FÝã— w“U`´Ö¤ábmÈVaˤ² “�$¬–:öö’ ¶�"zðqÍäkØ¢$§1[] (þàHÌŽ'½Ų„IDATxÚmÐÀ0 Á§` ¡ ¥` ¢ �ݽÝý×"¶M´ú4eöÞ3¥Þ+˜$¯E®ak °³€HçÞ²UÃÙÛƒÇèKÛÚ»†½mAP@\-@­†½%\‚öW®SšQ[ƒXw5œãg¡V@Ø[KW@°7È9þÐÚX–±nÖõ3R¢„�?µ¯áIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/skeleton_legacy.png0000644000175100017510000000067215224505336027703 0ustar runnerrunner‰PNG  IHDR D¤ŠÆ¢PLTEÿÿÿUUUNNNOOOLLLKKK€€€JJJMMMMMMNNNNNN@@@333¡¡¡›››£££‘‘‘ˆˆˆ���˜˜˜ªªªÁÁÁ¾¾¾ÇÇÇÊÊÊØØØÏÏϺººÄÄ᝝ÁÃ÷¹¹ÇÉɧ©©||~~ÿÿ€€EEEIII§§§‚‚‚žžžAAA………”””u&tRNS -9:-˜ÃÅÂü”ÈìѹIDATx­Ó³\aàכּÌû?Sê [ã"íÚ¿1ž9‘8ßê:i$9û]W­N.Ôi4»ÖeKêF8mÖU4\iW ZPÓÞQQ‚W©@ŸƬ)^§bL¨ÙÐcKq¿‘¢ôÄt— t:aË áý‚Z€V"YîœUÙ=ï¼ãM>ƒT/ÍEƒ X�1ø·C€Åps%P›\ ¨ð‹Ÿ„¼Ê‹«ÀI÷ô®�÷*üÿÐ;–å÷4IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/steve_legacy.png0000644000175100017510000000136515224505336027205 0ustar runnerrunner‰PNG  IHDR szzô¼IDATxÚí׌$]àê±ÍFõضmÛþ¹¶mÛf´¶®-ÄX!ÂfÃÑ™—î;¶QÉW~ïž.—��0¨š`È(€b?QA³T<"B‰ 58 =l�8cÆÚÚ¤7h±Ø€svÖ•† €÷öVæx^Ÿ³²²Ã6!â±Má,-S{ s¦pRiDâØ s›�ZC€íØÛk#­‹¡…p¡-¯ûÙ™×®kÊ]ªKÄšÄÆL�Ø 5HËñ6¼Þ^x!Æ#P‚˜¢®2 À½TjŽ É¶¼Þ7Ø7ˆ5¯ÿ…OÂߨçÈ^n,ötˆÅ68YEú6A­û° ð€uYWg‰,-'²;ám£O¢œ H¸“1qáuHÓÀ¨ñ6!@{ϱx*pàŒ“Sú;j¼ƒ­xÀQìS1¤\[7‘ÜÝ<�<ؽ”ÜÝ0]×f’3³‹ˆ�­é4ÀX€S‹þ­“»²rÁ19»t98½„Ü^7�—Ò¦NŒhZ´é’ºŽ\]ðiZ×HÓ¥iøNŒ8øW\�ÜåUäúòéd|„G›î­�HŽ��'K³}H§Æl-‹®“Û]A"-µÈú”Ð6áÙRF–e’NŒ˜ëR'7)Ú‰ÄZé�(‰Áq›Vä“ùiÞ¤£Nœ¥å¤IžÕý ï5Þ ÀfúKFÿ E";\ü'$ÀååôDÏÚ¾€âu¾~ÎPã ¬’õz,ëT„B ž–,\Ü� ϱýŒíOøÕ~Â7ø`2kPÐ%ÉEYW䃋ÿãâNlOÁE¸ÔGÎÁa<ÈùØÆãY¡]S¥ÖŒ [Á ž�„› q\†myŸàùÌ›){ê-8SSMöÀ›4§ 쥰±ÑaA¬­%ØJÙͽ…yðìò¬ûÂ3ÍÞs(‚z23…NôŒ-cIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/squarecreeper_legacy.png0000644000175100017510000000255415224505336030726 0ustar runnerrunner‰PNG  IHDR üí£3IDATxµU�$Ë þªº{yvÃÌÌÌ “ (J�f„™™…Écff>¾y;º[†™Ù)—¿×–ÕÓ;û@x#Ëëµ]ŸËT�3ý Þö‘·DÄŒBÍ*ÕÊFP’tA ÍÜoR³!+éV×wÏ>UˆˆßþÓ7<:>@bê.wE“PN.Ÿ¦Z^ÙX­yÒôœç?§j:Ù;)*µfai^(’ÓÜ✠šnL×hâño¯8€ŒptJ¬ùÈÑMV#ÿ÷€ÆÑ[¥&2YJÐÑE‚‡Nó�®ÝÎ?æñ�u¸'<õ Ž^L•BCy¬<Ö5‡ŽJbzÉã^ e‘ããŽ?™Gyº @PTüîB9pY×»†ª‰æéaAÌš…J"W4C÷M£‹gíV�G2=ºájœ5)<ì­ôDÓ\gÞÑ¿øÊ/t"æ€2@!¾|ߢ†È¸±½áýÿÃÛ~6S 4` ˆçžýæ �ñÃß9òc/ôíwßá½ïüùH�(ðÆ«?爽îòR6ôåû{0 ï¼õ.æ¦ãùÆ[l7n¼k5ú|�²òFŽNQæ±L¶|rŠ|S„ã½­Ä„v¥•(#f"B%]ódŠª`IÒiÖo‘WüŠþ})`®¦ˆ" øÌhÀÉwÿj%a=cú‹/e"ÕöËÛû´'=�Âón¼°Ýd¡Œ·?í[¨2¢S¡1P†‰ÏHmZ¬*ì¤æî¢š Ëó@;¦ÔýèÒ¾S…Q›üdU`¦/°ª4è>HDÛƒíþ¶hÚîïx¤n¯ëaØÀaRhõ/É˽^4¿ûìô\Í¡ô{´oQ3ìíðP€ÁøA M$C7~à© l2~­TÔ±rM´�aD AÇ>tõ ÚMÍMÅQ-øt®Þ±¢Ùœ¤Á]øãG|øŽ:æpKß»‰ÑLë+kjѸÜíÑbèæÚ¦Nd@õ�ª4I8:…$`¯]+8ºf]a Âн«ÆÏcò±kГ£»·`¤` C¸­¬U_.ÓÃeû¢©¬®¯øÅýoüÿ§¼²+§V}o_ôÂÑLè~aQ3(zôÈqGâcŸXs žñÔg¸èÈ¥ ( Ý]ëg•ц0™i7næpmÏ")G#f»û÷( 2íd©}j6!õ…2Qc ^VU•wuý¾M’;ÛdÜp¤™P¬aŠZÞÙ¨*”a½€R3ú+C¨e ë™ ªu@? RTB§ÓYzÞÂsßþ,*@WNMMÃÎpsc“J(L£f•”h&–±¤’„ DÄÔ—pÏÍ÷Õ±-ícóÐ6 ‘DU €èï@�•BduŸ"D?£ 4gP”ɸ¡Š€lÞ¦i,„àrö3ÄÐ5Éö•hÿ¸ pM4]©%+à)gø÷!÷>ìÔZ�IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/netherstar_legacy.png0000644000175100017510000000226115224505336030232 0ustar runnerrunner‰PNG  IHDR szzôxIDATxÚµW�,I<Û¶m/Ì ëÛ¶×¶mÛ¶mÛ¶=Xá]åÅö,zû013ošU™OYÕ‘¼ö°��Á#0œË;�\ÀiiiO½^SS󬮮îuÜÃ3¥`¿‡óóóŸjllü’�žª®®Öª­­…�eçß ? J#/Àï‹Äbéúæ&ÁD‰´ªªJ�=û™Eä�P86ñã•••³ãu€o0àÍ­-‚á\"•.3< "ð(óú6ñ™é™™©Õµ5jîÄD² ¦ÌÒR½¡‰ "�ttww?©È{EEÅ lÒ?gææFÔ5'¢Ç�{äÖ�æ)€D­ÃylN%ÖÖRlU1pÃâââO0ößtB°ÏBxÁZîc–‚?عG€ó\´à“]‡–•Q3¼‹1ûo:ÁG/£ˆ�G„žcBv]°Ÿ€xü2Lví[XH,xc0sü“NìGû �PÉÓ³³³\]]%W¶ÁÅR)Í1°Þ‘Zlû™`8Ÿ[\¤âŽòÊÏ'–ûäd²ŒŽ&£� ÍȘÒ 8X³V[ØäŒ^ÓÖF+�¯¬š�·õ÷Ó,#‘×ÜL¦11¤ÃºCÅË‹î:9Ñ=;; ˆ‹ñt‚#€žEˆÀR,‘,VþþŸ—Ç…x8@7š#X^u5%QeK Í.,ÈÞ©�ÔTò`‘8«©I:66«ûuâoïQ$È$ªÖɪs*+1g{Àa[“¿ š€÷=™çƒtpc@ ŽXúøÐy55Ò¶±YÙ­ ðHIIÉ(è:À#Lȵ*ņ|#äðÀ´êN0œãž�×}E¢ñK#kQ¸kiIÇïÜ¡KZZ,`[áÆj¾$ÑØÁNܽË#ÀKÁúÆu §¡A„]–@ŽUll(2#ƒfH�•¯/]PWç¥àÀ"DÛ•55ñŠpv»°’ A�+Â=àÓóó¼"t §s,ÿ¼"jÃÁñqQÓÐå3mO¬«£p¦n~èsT9¼BÁqm¸<(.ŽݸA‡oÞ$äüôƒt‘u€wX˜ø€6¢ºîîùÔÆF‚¶Cb™Âù1Ïsšš@éAÁ!ç{ÀÏ©ªÒE äZf^aaKrëB.çr,l�À „R?0�î:: nHNÝ¿ Û�ÀöèDpjêü][[è>oKfÁ¤Ň‚Sä–Œ§~11"J4«|Ž€cP�g÷ÐjŠÞ”¨T;z½pÜSâ¶œ¯�T®Ïq®äa�€)ûÓLX'”ÿqªüÏó¿|î߈9^åIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/planks_legacy.png0000644000175100017510000000034215224505336027341 0ustar runnerrunner‰PNG  IHDR �TgÇPLTE¸”_¯�UÂ�b–tAŸ„M~b7gP,ûl ˆIDATxÚe�1 @Qk¡ž,Ôð/áç’¿•�Â-—JÖcþ= ${bÒRÒÀœr’@¦ÁìÎìÝ�^ð±dG ,9ÎÄ–x $–íáAÞ!Z’£�Ð3£4À‹9Ážið°ñšT  ¥>±DƒÄ¢~gZ¡dZ6pÂÉa²ÔX¯ÀËõW©‡ú€IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/iron_legacy.png0000644000175100017510000000030015224505336027012 0ustar runnerrunner‰PNG  IHDR üí£‡IDATxíÕ�À à™½ÿ³4@€{‚ C L`O°Ü Î¥ØåXŸ¡ðÿ®õp.ð¤c @ïÙi€îªD\(Võ@`‘|»1yÎf�ðŒ÷f-â¢Ö‘Á?ZékQªŸÁlQÿFÒ¾‹¨q̆î-1æ.ú4³rÂà÷ƈÉLÁIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/stone_legacy.png0000644000175100017510000000027115224505336027202 0ustar runnerrunner‰PNG  IHDR ’g PLTE���ttthhhDÒ×fhIDATxEÆ!Ã0 CQ“�žÎä‘^­ÄD$§)Ɇ&öªJ³ 3p¿ t¼=PM!#«ë"h@{?Át=—!ðÙÆCPku�õã >ã3#Ð ª¹5÷û¥‘ñhªÀ½Ä_w¹fùôߊåIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/diamond_legacy.png0000644000175100017510000000034415224505336027466 0ustar runnerrunner‰PNG  IHDR �TgÇPLTEKíæ=àåÂÆÕÿöÿÿÿžþëeõãpûðº½û´Ü„IDATxÚmÐÀ0 Á§` ¡ ¥` ¢ �ݽÝý×"¶M´ú4eöÞ3¥Þ+˜$¯E®ak °³€HçÞ²UÃÙÛƒÇèKÛÚ»†½mAP@\-@­†½%\‚öW®SšQ[ƒXw5œãg¡V@Ø[KW@°7È9þÐÚX–±nÖõ3R¢„�?µ¯áIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/magitech_legacy.png0000644000175100017510000000470715224505336027643 0ustar runnerrunner‰PNG  IHDR szzô ŽIDATx¥—|\ݺ‡Ÿwí=ÊÌd’6�6ª™öÞÏV®mÛ¶}ï±mÛ¶måS�¤š´�5“­µÖ{“9Js|Îÿ÷ÛÆójIT•ïD²"ô!ÂG0œê�½}ʳŽ*¯½×ïð‡ß‘ÂK^R¨ôÅ�f)­ù4ÊëRjd9µ>M–sÙŸn¤<¨<ä!ºªïÚ€6ð¡þøÈ¶|m©Ùk¼pËñ.I]§¶¢œÆi¢i<ÆrrDgZ3¾Ù ‚ ™65Zbûpªÿ÷žo ð‚GFžÊx1ý•¡tbî¿ìoÊ™ÏpYvƒxT—UÔ»¼f>qƒ§u4žQq%Ë|9.œi}üã ¹ñÆEFF¬®è[F  ?óîü¦°ÔÍrÔ«Îv’ªñIûV3O³u½¦vßüð¿ýVÁ*/ü£÷ërÜÒÖ²#NŠº5}œœtËÑçò™;5f&9w.Yo„ùzð�¾Ð£6Ûç�\㽿^³ô&o³›É²kÔi¿Z[^ Ç�¦Y%ß]ÞÛýC7ÿPihÇâü ¤Ù�›Ôýª�“{¨æûؽ;/+ú†0ò¼pÕs1n7ÞïÅù�gô<ógÿ¥çuÃÄéöbíûçþ÷Ýüîkž;¼Ž…Wþð§÷ßuSÏÖÎ z﹩VØØµC”ý¤éšf?8;¿™n¿n ¤çäÛ*bJ»¼ê5ÆkÿÓjOü‘_¯~tØg¢o™¼îð=•“;7Ò¬¬‡·7§`áøÒûg]OG_ûNmž½$ñ؅̵¢Ë8÷)T_ï|ø FGgTÕ­3ࡦvá‡jù¸uï¯}z×Sî×*þ¡«AÊBF?ÓÓŠ°¥W¨o†Ám†f­Ÿ¿®>‡#¯}'W<‰:Kr¶a}’^@y·yyÖJséR¤+2¬‘6[!x•FÚÝZõ|-üÈ»=o~ºeô~¥¸s·ðs×nºT¸pX:D-,`'&Ñ8F�’ßÚbLp�ù~ªA`®®�‡BN5‰4Ë’G^þ…£ïœ¿þþ/Ãßû4Ëg>àÙ¿Eø…ë…ëwÀÖ:t” Ôýuؽ-Ϲޟ¦ì<ÑåY|œBfr�åÂ6Ô]ŸóÁ í‚4ÂoÝœ KiÉ'.ôIæÊY+¸³pbOÛów9.žƒnÝa€@ üò94ó›hm¼�æ‰1’¥V®vus7¬òºAö{ØI‡/bx}®ÝôœÛGj‡|f{ÿ®ë×tk³²ÐP¾ðaåPŸ0Øm`¸%€Û 0èÁåBŽlÿŠÞð™7¼‹´ÙB�kÑÌ‚)•Š u¿ ÂN†1"��Ί·ÙNU¹dvã=…#{q0úYO97î\ÿѶH-´¼a¤÷ï¸Rÿx×™8}'m°ZΡÎbò9#P1H=ï´ƒ¥$„! ƒ 6ÐÔožÙþ[ÿ½¶“™¾ »6 a€k4„ì2úøwlŸui%Ã�üvÕnÿEîÏûyës_L4?Oû1X Dóy¤˜(©Ê ÂâXB>²I|O” ²¬¬6-­ïᦧ”ý;ÖäºGpýž¡ÙËÿ4÷c?ö#•ئ|èE¯à�Ïsó $ÆHÝP…jc-@´S1y’DB¾$M³@� Öw2b §ëKðPðj¸Âïrׯþ]· «rü�yé£Ç™£ÇhE „’ÏA΂VKè÷íÂLÎ�ó€êP`žRi&NmšHœêúnËAÚpð…2Ís™ÎýSçN™ÇüÅ_ËÃ~÷�åèg?ÏÒü"ª �€Úð|;ú ®Ù‹$·Q Äxo)ÔÀ1u¹¨©©÷i6Óuÿ“žUûÜ#_ñÉÙmã8¥¾fcÐ0ÇøM/dQ†yΟÀýÕ?1ò‰�±¸0OæbL{C ˜ö†Ô7PÿÁ›Ùß»¹Ý7øåXÛpa^„ˆbQ 0h½‹ ?|+?~Ë�èÔÑÌd™ ,Š2n n‰êˆE•ݓιBkñ°ÆÑƒ>Š&Ÿ}þšsSË¥¤³ ]½�Ú ŒÏOó®÷¼ƒé…©7ø\ŽvuV·°m„¬Ó» ùá[8ôÓÃÜP*ã�žÃÏ/¡ª©*S cIR\à#x  |ô|jÕ·h%HšU¢H ï¹²m ýýʸԹ<ÓX�/‘ZÐ „\ÉçÛÛ*XªeôÀvÌOßÅ]¿ýóüQï ÇN�qåsG°“³ª"  £¢œ%Ž›€†|YCGÅOÌIfkd¾ëÏ7�ìü­Í'p€UB™bzqŠDò!b=( F «Ò®ö޹•Ÿ¸û6þ¨¶‰ ss¼ýUoaþÌy<>=¯^¾à4¸Hc,ÕÖËzÑ, úƒÅ’q*X ñƒ8]Ä…9L¡€¬lf5ô�°w+æçî¡ÿ/~•þñãï»ëŒ/Ìó¬W¼‘Ë÷ÇG‘&‘à î¥Òšüգ᱃L-¤êÜ©]øï£×�=áÄP Î o{Ð,¦é‚Ë¥6\ŠE¤»†^»ÿ‹?È¡ßøYþgè7KÈ«å¿û,޽ýÃØé9U‘iVÁž�XoFyp"Ö]=!Yó7ùΚlu6½C£èn�“=ÿ»ó�=f}Ñ‘MYW½³Ú]ÊÕÆ�7Ä~{/î–ƒtÚÍþZÍé).r„ä DZg.¶=Wc¦QîŘ7;ÜÉUzäHúugÅ¿ünGÅ·ûdùvÇ·ÅýÅe]Ž;%IëÕ=;êñÅ+�ËøýÛñ›j˜8ÃÌ,â¦f‘KS¸ñ)U|LÒö\ßã|ð1TϯŸÖHA©ìŠšA|.°þã’ØwjæÞ¾²½SûI%—��j骒 YŽ1£ ô3‡±ŸQ}à¤w㱊Ÿ9Œð.T_ã>¸þ-WFÂüA¡Òš¨:²’Ÿ�‹¶ÕÚEý„dö&u¶¯U ‹‹²¤Â$È1†{mž¡XœçèÑì[/LÖ/BòYùP3ÄñÆP³ƒ^³ýÆù>…:J7ª9 FÌœ¨¿¤cbeÌjÐhWûƒ‘ªúïyqº²¼Ê13S&ªùÀ•Õù²†Q58ãDˆŒº¥DìqؤÑHÿÝ.N¿qTÀ0Œ¬Îfˆc!I„BA)•êˆò< úmþøÿQY¬Äq-8IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/ftb_logo_legacy.png0000644000175100017510000000257315224505336027654 0ustar runnerrunner‰PNG  IHDR szzôBIDATxí•c”,M†ŸÌªîÑÕ˜ŸmϬm[ÿÖ¶mÛ¶mï~¶m÷ŽU™¹ÝgâÇÜîµõœSЬÈ~CÕüŸ5ÿÇÝýŒGòð˜˜Ü}!m¿­}õ-Cô~â0¸Ÿ8ø*&¹Ê_À£ñîQ„t¡áûyö–„Zx·ïÉê¼ÛK%žpç} ñwÄÍóÀ+�øÕ}O§¿r(°ßìq,f�}`ì30=pÐa?½ÏQGüöþ—ã@…?ÏŸÉaß¾×& 2zõÇ.~;u|%¨8`K톚^ùÞsßR˜¬NV�ìï* H2Y»fùå·ýàšë€Û€›E„ö`ë‘7ŸÕzçKƒw�˜6Ž?AŽqû÷�Ø×£i/èÆ…Ø¯Ðä¶4[4 UÁˆ@ã§ss{ÜøÑòg«å¦‘=çæî090Pä)¹ÌûÛ‹‹—ò„¢Ñ/É?Þ9ÿ²K„ò£àrð;;ïïŽó£¾Ëð1 DUe=£•Êaã•Ê«ýÀÀŽÍ¿X”åå’Rµ%à0ßß·�Ò‡~züñ¯6Á]ø=Ÿðö]çÞô›4û†_^ìL´�‘ï±×á¯úá‘íµîûÌ;½@e—ïQ´Ž8ØT�™�ýù©·¿}úÀÞ{?˜²†œøÕᇿ·mo]g¥ïîm¬äªŠ–eŒ=€ßzî‘zÎûŸôв±rMßèŽwÀ:\UQQÖáJU/"\W¯UEÿ‰k¯ýž”eÌc|-Ð8°)!jé̶4€eilkl;ïè·öOïsWP¨jç¤kR�ê=e¹ù.~›ªoÙõšÀ d;e–¥\•¤ í¥æ¾/ýÞg.|÷#žuíO>øà$ëp_ªt HA5‰sÄ ¶/»W¸¼^?ØÌ›€í¼�ªUÁ¶ÞíYŸŸ‘QKÙ² À…î Pª&œcй¡WNMíxûVCæÞß…_µMõ[o¸îºï±c Œ&¨ÀàÎOýôÇ\¥·H>;Tš�“€Í–ÎçDº2@(KÔ{öÈó{ôdÙtT­*ܱ„S~±´ôíEÕš"]Q]�*QË…>óÔ×�=äUw¨ Ï<XïÈÝè±€>< �!FŽY]ýþçæç¬n�þwMM½õA6|u¯jõE¯¸ùæ €Ð5Du*BPÅFmèÖ½û )š™Õ ñ¡ééü!cçh©ËZèjöÆñïð„MãÚ΀AÄ™O¾î#óóW‘´“s¯÷{KФ ª *VæAü깿} ` Þ 7*js>½®Û3Q…µ& 6E‹€?·Ù\ÝZ–¿ØâýCŸ;8ø°×-,| (;2œŠ ªXªkÀBýüŸž�}ýXÃU».™€ +Eœˆ¬M�M‘ù."W‰½!ÜÓ2ìrÖQ4¡Ò« ÙQØÈ8@1©&N°2ÚšFÕ¤kÀüíŠôÇx¸ÆÈõª×ÙǨ–wLñZgÁs:7,ÍÞ9†$X`|º·wIéCIäN‹)�ò±²<Ê>fÞñˆ·bLf¥ûŽCI)¦ ÆË~}.?á6 ñgðá<kJéI6FXÍA€ªsn1¤té­)ù®ÏPàr`ÞaXŠû�`ÊFîjk¢ÀŸGnÿ!{Zi®V�ÌÆxÐ"¿ ¸ h®/�·EgNjuZý38Pµî÷ö£=Ö°«¶^©3sðf{1ðç‘™¸ŽÒEÛGìšÝtÚŽ?Lâß�ÿó;úÏF¬výËIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/creeper_legacy.png0000644000175100017510000000252115224505336027477 0ustar runnerrunner‰PNG  IHDR szzôIDATxÚ½VÐ,9î™Y<Û¶m©p¶¥ÒYå»Ò»òÙ¶ gÛ¶íŸ{ï·°šLrIOÞW•Ú�“ºª7=ÙNÍÌøJ)ÚÍš†h>OócšsšÕ¿Ä9¶Ûâ`B ÚSs-ýgÌ{îÆõøÇó øóšiÊ´Éuç^pviÕŽUã„_ª�„Ô¸«‘„šCjëlÓ{!…šgÍ�edÞÏíʱlÎ 6Xÿ/øÙ/ú] 6µ¼só‡ÙަŽéÓ^û_ƒ›´ßbv6ï³ñµç¿vÚÞÇí5oÔÄ ®� €3˜Ç3ôô YÀ™Ô˜`ÄÔ½ÆÏ;ôÁ=§�Ø0ø5†×˜Û³u¹hò”IuÏ|ûä´†]�žˆbÀ\S#FŽ pÀýL ûí]mþ¹ö'cƒX´x!ËR(ªù¥F½yÆGõ½�ý&çûD´Q3�µóÌ’$iÁ™GËŒ}°�åªYÎ9.IEмˆ¼y‡Î(QL�k�´jûŠq°6Œ#�NêYfP•ðŸ‘# ipAšgaØüaã^c{¶3éÙš'9MAÚ‡‘#gCú‘Œ—žGLJ]õÓµëêíB.Ys5¥ƒJý£ž?Š£gìzï!“¡Y¶Û•òmêƒÏ È!DªÜ²¤ªëW€›gKpi“20àlÔ÷bc7"7Í(S’¾ Îk¥ˆº©© Æcl ²šZ›àD[g;2�¤_è/j`Éõ9lI?)Š»iM"7¬¯„Œ3 Yr¨Jº#¥ˆ�Fò ;NòÊ «únúͳ¬t È$¥¤QcGÁ�kj®À×ßù�=o¦þÐ8B�òÞ)¨wÓ¯MD»å\jÿmûw,'ôæ2®a‰¨|pšÌ”8¤ÐlàvÏIMˆN�K&�ñtFOðŠn¸Š¨:¸ ”¢z ”¢@JR†µœkÊ!#÷ì?ƒ!¸Ûшv®qóŽ®VŽÜU2vðtu˜ñ>¨æ€�ŒÚ±�)çUE0æÈÅÈŽ`Â7@l‹*KÐÜÖl_¯m¤l͆ ŽÏ©“ž= ¯Ô¶Öv{É(Z0o�ùŸ†ì °Ú_jñâÑö§NžÊx¹ç_qèïèçÆ+wò$‡R.„0^è(@.w”5xìdQ–Q¦°G +²_Ùþ!YÁúœé&Êu ãç.Vêë)É8ºr14+GÞ©zX&£Ó•g6ÚX¢Bk‰ÏJãLWdtù¿¢*ÃbÅÔ(#l?g#dpÌf²0ÒÕÙ­µû±,BAÊÊ)ß:£¬Ì ©(€ét†×÷ŸüÈÍ@÷Ͻ|�˜ãƒéT{ù¼ÉL,‡)ï³q½ießó¡ïûFæØÜ�TB ºkúœîTÖ % Ù¿±'Ò‰QÁ{Š¸Ê±b3“&Mú?¹þ qÛÈ÷•SQ2IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/ftb_glow.png0000644000175100017510000000275315224505336026340 0ustar runnerrunner‰PNG  IHDR szzô²IDATx­–ståjÅwm¯S·ÏÆØFyǵÇ62¶mÛ¶mÛ¶ÍóÎÜÕfÝ®ŒþØq²ùÎÉ— "@FéédV©ÙétäIþUªPPL …’œ“käZ¹Gî•gȳ>WïÌ‹!ÓæÑÑ”?6–"بZLÌëš¼V鵨š\#×Ê=eËÞ±—g| „,Œø!æ,Oy`VÖFª[w/5hp’5ºFMš<¢æÍÉ ‰¢tÏ D×¼åU$q�¥¢(Æ> ŠBÆln%C*oÕ¹óQ²³Û@NNGÉÍí&yyQ®âû¨|õ º"éëÂýZ´pòWÄ LAj €ŠÉšÌU»v=ŸÓ\ oíY ÂZ4)f×ûÏpë®^;Œ€™B@š‹jtï~Ko^½:Qffî’óþíR†:÷ú«�ùPg�éÓ… „½Œ„ºšÒd={>Éñ† V´ eC·÷ÚÄÚÞ�”½=H9Ä:Ñ“”³½H¹Ü›”›½é¿�eÉt X÷ƒ—¿î ¨{B wïœC,ÆèdOèÂêïHåL˜æBXèFXíNØîI8äE8çM˜ãFn=¡ mŒðêåá¤ø3`JñU�Ù¼›�t"LeóÌ·±ùA6?Ëæ7| KÝ(oÄ•B±®þŸd ¯€ÝnÀL ¾àï®…qÖ„4B[*2§"a›oeóa΄ 5œ×ãl¹eÀÐþh0Ꙧ(0˜ دƒ¡žÐ�ï\ýÝ+gz¶°ù~/Â$šV¾>4ÈÏò)‡ª'4 ~vÊEØÇæ§¼ 3]iqj*uD4œ5›µ�usQÔ\ tà8êžø€k½ 'ÙüŠa‘›4¿ÉZ#Ç€d( s î õ¾ù8› g“|¬Þ, ù!îâ~�åžàcùÀc&`þI1ºWMËÆ�­øc“Î]{ýÓTyÖ—ð‚ÍÉOhd«¹À´fŰA%^×&>O€HJ"×”çs~ëËO�—©¥’üëÛ1æ_ËÁ®©Êë¾b®8Áªš;€èè{ó2¡HâÎß»äûÍd¨e‚Býr\eƒ› ”`{X˜ôƒˆ:J ú}ps»Ag)Z÷2®hòŽ_»ÿó«Ép³x5ÀÃlox1O©uŽš3HS†J•Q‘¯�·?NLÇ æNN‡¨B…} ð¢zñÔí¿ä@zµô%ÉbÓ €_x�g à´°�IÉ` éÙó±˜s(YKeʬ€¨‚IpêõÇ/j™¾@ÉêÔ¹LII‡).n+•+·�"bŸ,�µ8жoð�&#Mâô�¿“©8À\©u6ɾf Ô¡”G!¯Ë‹d;²Êý Ÿê÷÷0äŽQ&5OØ\þ€ƒœ)g—Á< ŽåÕª‘›û‰d»„îŒC`w�†[„`ªÉgˆól¾Ã“ÐÉQðŸ!‰ô#!å࿞¥H¶ÿLßm†ÞÖˆPô!ìô$©?Û;€�jA¢ö@X`<1ù‹¬‹ 4·'dØ’lT_0h†F€!`*iW§d>‰]’|$|P.óÀ]ÖÞ×ú‰‰¦$bKÔ œ?sæ“Ø%ÉÇÐgxƒuˆÕGów¬Ѐ�‘�´+�S2ŸÄ.0d¾–ÕV;�¨´Ë!Q[Ò®NÉ|ÙkܙՂU‡•ÈJ´#}6„ÔO&©§Ì"fÕæ5D²-Çäœf(%¢Ï`$u”z²�ç4ÀÄÆ!"Ù–crN+–ÿ³¢¶›1wVAIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/brick_legacy.png0000644000175100017510000000034715224505336027150 0ustar runnerrunner‰PNG  IHDR �TgÇPLTE|E6©”�¢†}ÆhQ±bM‹ng›VC�P?s?1 tßLJIDATxÚe�A À0 M¡B¡J¡L!{±2M“œG¯ºÏÉ8‡wï}š¡×™�$E�t�ªU•ÙŒJÀޱâöÅÐ…Šœ¤è Š¤®é" @i¨í_r>Ë…fk>Ån»Ðì¤"]`Ú©t#]ÌlÝÐßÙ{.4ÿŸvQÀÌéâKÆ\<*ìˆgN…žÎIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/tnt_legacy.png0000644000175100017510000000034415224505336026660 0ustar runnerrunner‰PNG  IHDR D¤ŠÆ3PLTEÛDÉ>·8§4Á<¨4‘-ÛÛÛÎÎί¯¯¶8 1‹+x%Ä<²7Öá‘lIDATxÚå’kÀ0 EObì…ýÿ¿Z[3®=ÐM»Ïrnåˆ"¦Ú^h B'¤’¿zŒîd ¶�®�Û„b±òÑù*hÌgÁáú\ܲÔÜ L‚Ÿ½xÕ…¹v´ó?®zÁXIŒÏ¬ ;ü»} »ÚmIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/32x32/instances/infinity_legacy.png0000644000175100017510000000244715224505336027712 0ustar runnerrunner‰PNG  IHDR üí£îIDATxÅ•wkiÇßOðjŒ:IOÊØvjÛˆYÛ¶}UãÚ¶mó›Ì3wÏjνŰ3Y¿µºÏÖÿqÿUt8Qr4 QxH‹ÐÍ V±ïS"2w0–Ý2DÍÙ8ä›–%ý‚a\#²w±Q}$„u�±õÅGBPÜq"Q¿?Ñx0‘,$# ‹"DÎ4bƒs}ç²0,h }@Ò¼"El½@÷ét4šºeAäÍò0eð;ö«`Ã’Öž‹G \ÈAŒ^Õ# š8ˆÈÚÉB€gëà�åãlÀy@�À{X"°aÁ.�ç!ÀÆ{¶^`Ä‚€°O4§! ]Û?8|‡ß& "§ˆþ |Š�7HçÎû-Ü%Ì>òÚu�°ë²ÿôÉ@»ŽˆïÅÍEŒ©#ÄÙk^Û¯»�Ÿ÷œ¸WWª’ÉÄïÿOñþ&,,,))IMóæÒܳºÖY¥}^Õ0¨]¹´ô’P¼ Ž/ «Ø> ªw¶ŠRÏ�d“”¯ˆ„Ó¨0¯WdËå~"0uÁ·ã 7§UÀ÷s+KiH�0bȬM’ù§7ó,Ó óNEÛI^é’L›èõÍÂÙ¤–,þ 8"YeC˜nPœîdõêÕ.�ùgG•1™Á:-<«P¬‹¤åIÌ;ä–�òÌz.á»ÿ1 ß™´ø µ‹Ò¸78«ŽçŒæü,á��…û4Òpb®Š‚/pÙ ¼æ9ï‚y¾qJ–Z͉àbТxÚÚhÝ'Hɵì0ÑuÆÏa…ûÔÑfŠ-œ¹®Àðlî™›}Fdœ�Æ¥ŽjñÚ±c+XËA¿C�‚â…XàT•˜g•*líµªió^xþCÕŠ$«�ŸÕÌ7ŒIÑiqT +“¸Ÿ�úÓá‡ÞºOqBbÈùZšk‰å;X¶E•i‡¼cœÖ×å_¯gÔëX%%„‰3nóÏÝ*wóuýbÝMÊñÐustÃý�¤x@ØÚBk²Ò«3ØÇÇ"�à~í1~š“ìdðÈ?–%ð?h6*²[ø¶yeÑ’¦ýŒxê6uúyüf@çiVÕ²ÆÜ'U‰ˆjºO4›*&èjÖ1¹mL^=£î<Äœ¸@ž¾‰Mœ§6ÎKòkø µ4::º,ŽëZ" OEQhHºV‘eBîœUÎiÌSò¬&^z#FLaš2¥°hƒ&mf*]_+4÷Hò{E†^±µWŠTcãƒBX­V[WW÷ùc8#YYJŠ4ØSÎðV°}¢y‚‹ýÍõíÖ©P&b¢8“Âc�¿`k«A�¼]œËld¤:p°F¿U’Áb—ÆgW—-` 6lllq±GyU:–lJ¹…\‡‡‡“FûäÛIDATx�’n0 C=fŠ&ï¯1óþocE7.ƒ_qXÑ`å ¾±¥%Ã*&Z9Ä'v†O8nù-vðƒ5Î ë»X×5±ÝÂ~´uŽWœ¶òyN€HÒ£¨66DDRéùœ�sJ9—"ÒkôujïõRê^rè&'©J™JáìZÕÑd9©Üw2ê‚Íe¡/©¼7ɳ»7ÙI|„qþN3oεz_“óé �LæÞ× “�ÄCÈÙnÍVç —â} zȰ»dé'Øo¨5«èvÞÃ>’í9¬IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/multimc.qrc0000644000175100017510000003420515224505336023435 0ustar runnerrunner index.theme scalable/reddit-alien.svg scalable/launcher.svg scalable/technic.svg scalable/atlauncher.svg scalable/atlauncher-placeholder.png scalable/instances/modrinth.svg scalable/proxy.svg scalable/language.svg scalable/java.svg 16x16/star.png 24x24/star.png 32x32/star.png 48x48/star.png 64x64/star.png 16x16/worlds.png 22x22/worlds.png 32x32/worlds.png 48x48/worlds.png 64x64/worlds.png 16x16/minecraft.png 24x24/minecraft.png 32x32/minecraft.png 48x48/minecraft.png 256x256/minecraft.png scalable/bug.svg 16x16/bug.png 22x22/bug.png 32x32/bug.png 48x48/bug.png 64x64/bug.png 16x16/screenshots.png 22x22/screenshots.png 32x32/screenshots.png 48x48/screenshots.png 64x64/screenshots.png scalable/screenshots.svg scalable/custom-commands.svg scalable/datapacks.svg 16x16/cat.png 22x22/cat.png 24x24/cat.png 32x32/cat.png 48x48/cat.png 64x64/cat.png scalable/centralmods.svg 16x16/centralmods.png 22x22/centralmods.png 32x32/centralmods.png 48x48/centralmods.png 64x64/centralmods.png 16x16/copy.png 22x22/copy.png 32x32/copy.png 48x48/copy.png 64x64/copy.png 16x16/help.png 22x22/help.png 32x32/help.png 48x48/help.png 64x64/help.png scalable/news.svg 16x16/news.png 22x22/news.png 32x32/news.png 48x48/news.png 64x64/news.png 16x16/status-bad.png 24x24/status-bad.png 22x22/status-bad.png 32x32/status-bad.png 48x48/status-bad.png 64x64/status-bad.png 16x16/status-good.png 24x24/status-good.png 22x22/status-good.png 32x32/status-good.png 48x48/status-good.png 64x64/status-good.png 16x16/status-yellow.png 24x24/status-yellow.png 22x22/status-yellow.png 32x32/status-yellow.png 48x48/status-yellow.png 64x64/status-yellow.png 16x16/status-running.png 24x24/status-running.png 22x22/status-running.png 32x32/status-running.png 48x48/status-running.png 64x64/status-running.png scalable/status-running.svg 16x16/loadermods.png 24x24/loadermods.png 32x32/loadermods.png 64x64/loadermods.png 16x16/jarmods.png 24x24/jarmods.png 32x32/jarmods.png 64x64/jarmods.png 16x16/coremods.png 24x24/coremods.png 32x32/coremods.png 64x64/coremods.png 16x16/resourcepacks.png 24x24/resourcepacks.png 32x32/resourcepacks.png 64x64/resourcepacks.png 128x128/shaderpacks.png 16x16/refresh.png 22x22/refresh.png 32x32/refresh.png 48x48/refresh.png 64x64/refresh.png 16x16/settings.png 22x22/settings.png 32x32/settings.png 48x48/settings.png 64x64/settings.png 16x16/instance-settings.png 22x22/instance-settings.png 32x32/instance-settings.png 48x48/instance-settings.png 64x64/instance-settings.png scalable/viewfolder.svg 16x16/viewfolder.png 22x22/viewfolder.png 32x32/viewfolder.png 48x48/viewfolder.png 64x64/viewfolder.png 8x8/noaccount.png 16x16/noaccount.png 24x24/noaccount.png 32x32/noaccount.png 48x48/noaccount.png 8x8/noaccount.png 16x16/noaccount.png 24x24/noaccount.png 32x32/noaccount.png 48x48/noaccount.png 16x16/log.png 24x24/log.png 32x32/log.png 48x48/log.png 64x64/log.png 128x128/unknown_server.png scalable/screenshot-placeholder.svg scalable/matrix.svg scalable/discord.svg scalable/instances/chicken.svg scalable/instances/creeper.svg scalable/instances/enderpearl.svg scalable/instances/ftb_logo.svg scalable/instances/flame.svg scalable/instances/gear.svg scalable/instances/herobrine.svg scalable/instances/magitech.svg scalable/instances/meat.svg scalable/instances/netherstar.svg scalable/instances/skeleton.svg scalable/instances/squarecreeper.svg scalable/instances/steve.svg scalable/instances/diamond.svg scalable/instances/dirt.svg scalable/instances/grass.svg scalable/instances/brick.svg scalable/instances/gold.svg scalable/instances/iron.svg scalable/instances/planks.svg scalable/instances/stone.svg scalable/instances/tnt.svg scalable/instances/enderman.svg scalable/instances/fox.svg scalable/instances/bee.svg 32x32/instances/chicken_legacy.png 128x128/instances/chicken_legacy.png 32x32/instances/creeper_legacy.png 128x128/instances/creeper_legacy.png 32x32/instances/enderpearl_legacy.png 128x128/instances/enderpearl_legacy.png 32x32/instances/ftb_glow.png 128x128/instances/ftb_glow.png 32x32/instances/ftb_logo_legacy.png 128x128/instances/ftb_logo_legacy.png 128x128/instances/flame_legacy.png 32x32/instances/gear_legacy.png 128x128/instances/gear_legacy.png 32x32/instances/herobrine_legacy.png 128x128/instances/herobrine_legacy.png 32x32/instances/magitech_legacy.png 128x128/instances/magitech_legacy.png 32x32/instances/meat_legacy.png 128x128/instances/meat_legacy.png 32x32/instances/netherstar_legacy.png 128x128/instances/netherstar_legacy.png 32x32/instances/skeleton_legacy.png 128x128/instances/skeleton_legacy.png 32x32/instances/squarecreeper_legacy.png 128x128/instances/squarecreeper_legacy.png 32x32/instances/steve_legacy.png 128x128/instances/steve_legacy.png 32x32/instances/brick_legacy.png 32x32/instances/diamond_legacy.png 32x32/instances/dirt_legacy.png 32x32/instances/gold_legacy.png 32x32/instances/grass_legacy.png 32x32/instances/iron_legacy.png 32x32/instances/planks_legacy.png 32x32/instances/stone_legacy.png 32x32/instances/tnt_legacy.png 50x50/instances/enderman_legacy.png scalable/instances/prismlauncher.svg scalable/instances/fox_legacy.svg scalable/instances/bee_legacy.svg scalable/delete.svg scalable/tag.svg scalable/rename.svg scalable/shortcut.svg scalable/export.svg scalable/launch.svg scalable/server.svg scalable/appearance.svg scalable/about.svg scalable/new.svg scalable/checkupdate.svg scalable/instances/quiltmc.svg scalable/instances/fabricmc.svg scalable/instances/neoforged.svg 128x128/instances/forge.png 128x128/instances/liteloader.png scalable/adoptium.svg scalable/azul.svg scalable/mojang.svg scalable/openj9_hex_custom.svg PrismLauncher-11.0.3/launcher/resources/multimc/50x50/0000755000175100017510000000000015224505336022031 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/multimc/50x50/instances/0000755000175100017510000000000015224505336024020 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/multimc/50x50/instances/enderman_legacy.png0000644000175100017510000000233615224505336027647 0ustar runnerrunner‰PNG  IHDR22)áxƒkPLTEàyúÙwòÙvòØuñ×uðÎsäÍräÌqãÊoáÉnàÈnßÌúJJJIIIHHHGGGFFFEEEDDDGAHCCCF@GBBBAAAD>E@@@???A>>===<<<;;;:::<7>999888;5=77766693;555444718333222111000///...---,,,+++***)))((('''&&&%%%$$$###"""!!!  ÈktRNS  '(*+-CQUWW€××ïïðóìLÄúIDATxÚÝ–±®UE×ÙçŽÛRD Aéih)‘€ÏàÓhÃðTüm *„Q^òž=÷îØWãÉäI¯£`ÙÛ–FZÚwk,køá€ÉËð4E€*Ÿù�Ñà*TGB×üV�c@ZÇ×o7"þŽÊ®ï6à÷Àƒ¤Ôw¿—ìŠôÃÛW¶�ˆçAÝ´ˆ €®� —dÒ÷¿ `ÆNYPkLiÔFfZD@\À (€)ÇcDÉǦl©œÁž›ôP¥"Ùƒi+ë-5¢Ö±YA3¨W"-bÆ +3õjD\>‚¨ã¦`f™y]SÙU=ך]¿™€Ü yhÆ}wµÆ!aTð _¸ìh>ì\¹j™ ’J­LU™ý›O·ôP#ˆË¢xdKNÔJ7aÄ,/o‹o±#¡8O5 ?¶+¸ÉýTÝ?—|ü¸Å"tÉZDŸÊB™q¿â’ùŠXÏ—@)^Š„¹¹, |B–™€-¶ðå¶Ï5_fÔjÍm’Gƒ=®½Ëþ4 /$ŠÀ0C¤ŒÑRàr€$ÉW÷EÓ⸄O†kÂ]ŸÎ7]ŽlîÈ¥Å,Ì,13­ïKb|€p›¯dIN�çkϾ í-œ™]>¡‘™3ëƒI,p]pHbÙ"Ø03LJ‹Ìl_nñßF0´ÜùŒÈwbÿg*Bgiy0iŠpwk‘Tä¾ñ !Yš ´P¬Kè†wÔ@‚u ê‚u|¤R®Š„Üm¹…{NAG⥑™Ø+S¾O‘B{Äò`BHô–ÁjøÐ[�Ìmµ1�Ð# U‹e·�ûÚa~-•÷§/¥ì»Ãá°ilu"3‡Áº¥íÖ².š[fµ_} $Äz‹9H9ÕµHÔ�°å}yíT"ï28@¤�‰o ÁrKZ¤L³)º=€%]É|#–?OAç&Ûù¯i;6?1°äæÿrº;?@"OçŸ�§ÈFЕ3&³'�Èó¬�ÝxʉøÏž”þÍ:˜v=w»óIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/8x8/0000755000175100017510000000000015224505336021677 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/multimc/8x8/noaccount.png0000644000175100017510000000021215224505336024371 0ustar runnerrunner‰PNG  IHDRádáWQIDATxc�“‘—“�‘f�‘‘QÒÒ’•d�™8}ñôÍÊ :zk¦•”0LøŸÜ7-ùCa×_ß©%Q ™ÙjÉÉši± ¾ÞjjššñaïªÞ©ËZ¾IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/0000755000175100017510000000000015224505336022035 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/multimc/16x16/news.png0000644000175100017510000000112615224505336023517 0ustar runnerrunner‰PNG  IHDRóÿaIDATxÚ�“¯]Q…k»?¥ŽÔ¨a-EµÝøÙ¶mÛ¶mÛ¶ïz3×ÒIÖñúöp€Žƒ¤K¤Ëzê{Ø+\Âz bìÐ0ƒ�‰,d´y`d*MCqØ[kBópü ï„vPL«¦£¾"MÕñl�‚·ãWòQD–ÿÆÍ[×qýÆ5ü·z‚ŠfªB1†¨¤E9AØ](@^š}>ý‚}*€�Â_0py-܉Mð”Ö{s/póPýÃQUèÑGÿž–˜†I°Ö†È‹Ë c¥ÅXÕÓç%S(‘¾ãÌßN*›Yû Ô‘au<‹IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/coremods.png0000644000175100017510000000121615224505336024356 0ustar runnerrunner‰PNG  IHDRóÿaUIDATxbÀœêy ÷{.]r­çÓÒëý?ÛNfœ�Ü``Ï@,X ]°ñÞ›ã®7Gñ3oæÚŸYÛ¶mwSw]®jÛÜßuwµm{S›—qÞ$“™N“ûä¹ú Îü!ÞhoÅã’'ââçKbò¡îwQ ‚Jô]× >QÈ?BÀ' Ò§gÿáÛÇ5œÃ×ñ¤äŽ\)<É’|s…ǹ ®ÎžTbDa»‹Ë‡íèg3 „$��VÍSa2³påÉXÌ€æª8òÌŠ Ró(áVJÚYÿB÷(õ¾‡Ÿ-ß“„>0å2àÆW�Æ h–‚Ì̬œr¿Ó¢ÏA6—ž›‘ 3 v„ æ B³ê¦“®KZ®%¦òOݺõ몬( 䊂"QUWªhðá�yœÐìú™Ãfœ½ò¯Ü?À¸“ËÏ&a„š„-‡ KÈ®¸Ž'wÕwßwóf6Óž[÷Èÿ“þ\½ߺ5Š"`2Uf/—*W †]8r"€A phóû]_|] €p)ó‘¸UtI¡"DT"�‚”èŽ$]ÉLk’•AOͰ®—Qí¹yûå×R‘ŸI‰ësäf¤ r IL›õ§òÍ'˜ÂI½õòN} 5·{ÁÜ”ßS5^gBÓ¦™¢"6±‡B(¾›Î-:Š "LÔnSçÛõÛ¥µ­w"â;Ç縼óÅBZé/ã.õT‡ÕZ5Ö ã<=ñ+E%>|+ÒËÿΉ„ˆ5º«””ËËÀ匠*?&Äü³*¼Wø¡lôhR0âIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/status-yellow.png0000644000175100017510000000061615224505336025402 0ustar runnerrunner‰PNG  IHDRóÿaUIDATx¤R3bQ=˘Ýd ³…”iƒ·ˆ©ÒĶí&¶mÛ6oÞù6‹k"âWu0N« +Ѱ[�“Í2œ¬–¡a±jª†¯¿—p_ó¦7Ï=�÷AÈÏ„^ —��¥rÜLÁ ˜à¦ÖS;äwB‹3�ás²U™.€å•à´æ]«f§éè³Q ™È·w‚}=×q n~V c�™bÜp'Ø)‡ºë¦!:8ÑãŽåCaCo˜K¢r¸’žèCßû>Èx°ZŠ“oGûI‰ rttèC߯q� 'X,Ž3Á\1ûãaºê´#ú%;—ÈSð$cÑ�qÊyF>è~ŠÕšÈ‰¾ŒñzåáIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/status-good.png0000644000175100017510000000074315224505336025020 0ustar runnerrunner‰PNG  IHDRóÿaªIDATxÚ…“CÚ+AFOÌêßž÷²�,áN¯m¯mÛ¶m{Áz–|ÿ÷TìÁÛ|ÏiT"ÒîM¹\߯¹‘—œÎqp(Ç®¡—lÜÆzã¶ôkøx5±–'co’§ˆðË/üÔ¼ 7âÂæ~�õýk_…«ÁO'^ónHøþÓ>_U¶_å+û^£’šàÖØZž 6òO³;!,sÖZ–SÃ.WF<~ºƒ¿‘ÔŒ”Ä÷Æ…Ï~a�ñXd\8:¼�ljžpzfZ2¹Œœ½r¶$9;[a¿þá·áZù‚Zàl>+Åb±&x–8/aÇ`Ž~-co<~Ðüî [ñ7¿¨ J-Ï]h‘Ó0•k’¹ú&‹“'"ƒ ðÕWÊC=>–OŸÜº˜êÃlãªhsÌKæšœÍ<=ž¯×ô^sò·å‘éŠ�²IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/centralmods.png0000644000175100017510000000105515224505336025057 0ustar runnerrunner‰PNG  IHDRóÿaôIDATxÚb 8¶J;Â( Ÿ™WÛ¶Ö¶m[±kÛfÜ jÛ^Û¶í}~§ÿ­­ý“orq¾›‰´(øžŠº{Àö&¼€o#¾°ýf ‡í“CF8nxåðæ 0D}Jƒ®ë°ÏìU?cS÷·ØØí ~µW×5 òüâMß�ç�M,è©�úû½ŒÁÆèdº£²Vq .ÆޱŸs.äu§£Ì{½•ÖÄ7´eyÐVäE›Ù“6‹¢XÕ9j–ü–ù.ûxÃé0ÅW7¨ß(WFG^£IxíG^X’S€’tAj5K„½0 þoœ‘Ûh*Ä“�Å:®ùyÁ£„—ÒeG?Z7Ò»›Ö”´¥¢5f Íé¼± /= ¤dÅSOÄñ~�å3OO¯¡ñyš½Ò^[Q–fÐø|vvõ7Yq1ýd¿æÔºE4>Íþ -E‘ Z"AÓ[ðìÆü>Íý…[ ´%÷ÀôûŸ®Õ²+Òžh4½ï.Õ^ʉóÑ…AQþÏWÙé¼B{u¥?Lj†*££òå~˜ç¼RþlqåÝ_gÅ}nÝÍÁF%IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/cat.png0000644000175100017510000000077515224505336023323 0ustar runnerrunner‰PNG  IHDRóÿaÄIDATxڕϬA†áïÌ ³¿¥­mÛ¶m;¬­¸ŽË˜U,5RÛ°¢Ú–Nçš[<É»ÂÿŠHŒÜq|nÙ¨�ÞäÞ ›ä“u§ @Ð{Òñìç°ygš÷h:h1þGBæN&oF¸nÓ4§nE¹eÝŽ‡ð/V�Û&&öž·ÁÐÍ_º!XhÄBR öâo [ݪG‹þ Ý`•»dv A–Œ\gNî;gÝt£¯ªÿüIUA�Xéßvä9Çr@mÝPi2°ÝèÓiA?³&´Ã2½ÿ’±Òÿ¬é<¼ËäÞÒµEÿ³eˆÓe×’ådcþ³%#×FP›>m‡Ùõ³MîWžQ�ࢠQ²¥|¢îÝfuÛZ¨MØ�±¨{\}ÙDÄ ŽÉøõ°°$ª ÁÐ}‘èé™Þƒ€ý¯"%S� ”˜—‚W@eY„¢9D7G„|¦ë&*"¡¢â„Ê4lŽz ÎÉ YDx° eb°5À˜ "'$Ùw\Sþôœ0g‡9ìD8jG_GÈ=Ÿ¡ènõ]Ó$l@vYN‘º„»%áMŽC.N@.S-V÷³Ò U?¶H#œ°¡k*õTÏŠZÝlYhIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/resourcepacks.png0000644000175100017510000000161215224505336025414 0ustar runnerrunner‰PNG  IHDRóÿaQIDATxu’p$M†ß™5æfÏf¬ª ζm›áÙ¶mÛ¶mÛˆQÉ"^õ÷÷o?UO±û3þ“�ǹÛLuÕéÕxsꦭëC°~R9ôñ¿ôî­Ä¨Q•´} Å=Ù5ÿåÇSÛ>Ì/^Ì¢z¯H¨+Í*ÑÊ4À -þņ °~ý©ðóö$%§Z9ôáÃ:{ö,ñ4oÞ<6¦Ñ²èÁØp‚»µú«Áñ;Dr´‡‡÷ÌÊa=L²$Ûívpáv»¡P(,4Pc*�D&”¿Pr�Cjïîï›tz¿yþ\°óOv�.I‚Ãh„¯¯/x0{Ráò­±ÎúõµÜ UC¯\à!Zá1NÅ *  -ÜܱÜáF#[¾œ, ™ÍfÊN¾FÏÎ ør²ð‚Q�±áÔ#¬�!B ΗBf›–—ZD«E¹À@4š2Ó^¿F›ÈHyöz¹|�¶‰Oà´½rq�&¨Ã‹!õ±ÒSÑ~!: @u™—sFþµï¥J9¼9< *Á|pe"+3 •¢JƒyrbYኙ‰±¸HÀ+öAw¼ŒZmرuëøØØXo‚²Ä²c ÕK�´yH�}‚œô—p:r�¥«%îx…ßZ¤Ð™òå‚3eÊ”5ñññÄo€s“ëmgJ¿PŒ^ïÒÑ»ã•éÃ�9´mÛÖ8 ¿ÓÄ`Pl0ƒãééYìäÉ“æd>P~Lä°<#˵JôåPzt:œNŸ>•Ѹqã*ø+mÚÙ©S¥ÈvíJv­\Yìß­ÛØ‹/Ò—/_(..ŽbŸÌ§—÷ö² 6Ü©]§¶~g0åF�:Éï""؇‘#éíˆìFõê¶QjõÊÜÛ±cGÞ�,S¦L¾Y¹rå�²,A÷ÇO†oÐh.î¾¹³}ûs jÔ8ÐQ’fêðE1 @(7„Ìõç–åÊ\ÅON¿‰NŽ–"'IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/jarmods.png0000644000175100017510000000111115224505336024174 0ustar runnerrunner‰PNG  IHDRóÿaIDATxÚbÀIJò>trZú²¤äÓ˲²ŸOÂÃÏßQUµGWÇ„Ëmvöd.'§(Nkk^v]]6 mÀh”`å ¢0|æÞy¶mÛ¶;��šaֶͰ¶m»�ꮽ;Ó3÷Ùû'ßâà?#B–�k°?""áPddRúÞj¥–G�À¡R)X_¾„�Fãï“QQÍ#"jwGDÔ­ $0Hwã⮤Î�[ÏŒFÀi ùûÓj�›L bÀ˜b&~‹øÙ»wöøI9u«tåÊ ¦×+EJ£Ù â?7zb‹ß[îÜYJû |% ü%Éǡш‚ÅC°ÙLœ)ÙÅ…\މY¼!,l[HRRœhDúMú§ãJPCL-h@º½¼:vL™ršFFX­ÂyÈ4ш ý†ïž<ùzÝh|€·”¾N¥Z@#)-•ƒ‚ìvQ,¦ iRŒ8!"ËpL§;¹E­ž±P ç’ùáCeº�("Ɔüö"Ä3ÃÕÕ#šR� Fã/Èvsëüœ“þÕ×ó/¥¥ü[só¸¼NHÐ>Š�ÿ‰W® •e@¹6xzΜäë»ñ\tô�¯|0_ÊËG âø¬y¡›[ôŠ nøÂžâjøh|Ê̇ÄDÞâé9 IÞ~—ÈÇãE|¼Â#op1…AÂ÷¯ ¥´ÿ0Œ”³pn‡a F’œ$ñþúÜ‹_ ÑÆÞIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/screenshots.png0000644000175100017510000000102515224505336025101 0ustar runnerrunner‰PNG  IHDRóÿaÜIDATx¥Ó3€]Àñÿ`­±mÛjb'uܧWŒ.¶mÛ¶m�}·Æì›ÛÉoølEJÉßP7ý×g©ð„æLè¥jýŸŽæ²|U‹òáóUØHÆù¿X¥\]˜†6}ÎrK(’�¢¨ü M³°uÃfRR2l:ïddŸûBãS«•£4Ÿrã§~5+ïð¡‚X\ý·y¡$üñ�ï^ÿ „à šq”�[–Rý?'ª¢ðÁ£H1tU%¦xðà§¤Ó +¨âÃOY^»½�m«Æ¢Ää×=¨ñ<”ÀeúùO/âÈÔî„ÿ/GØ_Dî‹sDÃ�šÖøvOw�ÂY²AYŒ,Ó˹ ûx�r›šá\Ò s0ÂIÜVV4P@ÿt5«—çüÅS8�v† î£2íñÚËröÜUþ÷uD³dâO?ŒU×P¾Õƒ{ž3 oJ–ø‡Õ{öríÔ# ŠÂ/®’ž„ÍÅW¾6Òâÿt»-BÏݱè°÷ø%<îò´ëVUS1…IaHʼnF+š4T(H9€Ž€ºµšÀ;�4ágV-=€2zhÓ|‡ÃáB`Š$‘ä˜oÂ)Í/w¸´¹Ëå(€¨üù×Çù%&•¹¯–~WIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/patreon.png0000644000175100017510000000042615224505336024215 0ustar runnerrunner‰PNG  IHDRóÿaÝIDATxc üwô6þïèóŒ|}P䜽ÿ;únøïäû(ÿì¿“ÏV zo4¼Ì� þƒ0P2.îäÝÔô"‡À 1 ž‚Ó¨Í@>X3nì�‰Û'Ÿk >> ö†ÿ¼$`N'ˆ|40 °óQ…ò c{_S,.pàzá;üýïì,Œ= }–6Àg+¾@”ò§fGßw ¯âM�°ð9‰Å€‹ÿ�ýô%$„¼ƒ¯P.袼ÿN^vÀ¨c$� jàm  †d&ze na¸M”×IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/settings.png0000644000175100017510000000147515224505336024412 0ustar runnerrunner‰PNG  IHDRóÿaIDATxÚ\Êpck@ás]µÝ¾n¯mûÙ¶mkŒgÛ¶mÛØÚˆs³i²É¿æ7s†G�2e&{˜äõzôz�¥†.“¥e)šbÈŠ¢nIW$EU,öíî{îýþ…]$Uö ËzëÚ5ëܺ^,=þÔŸ`±’bK°)‰€É‚)Ù´¶4MJ�^Õï÷³‡ß††úBèÙO¼W!dA®ª ¡$c_9gUmV[kÛ¥�]�çÉìAñ_OOÐÑVŽ­­ê¶ÔZ ŽzhÙR¥•µ«Ë™;g–ÔÔÜvT:�®—Ù[Âï�´fSe•a,æ8ÄS0c®eˆ&þßÿÝ?Éd²_fÑHì_CÏP[—ÑMa]áT qö}ít³™ÐTâ2ûõÿ7c¢ÕW y »Î1­#xí]¼Ðg'ZìÁåsÓÚêhÉ*³�¼ü’h_{Õíïâ.íâL—ŸõÕQJ+j¹|m Og¡BÑ?P$I¶¨ìcÅÚýOüó›4W¦´°—ö`=ü5šJmŒ7šÿðX0ÐÝÛëïîéxKˆ´_%n²Ó*pªß¿;ݽ_ ¦A×#�‘\¶ž/ÿÿ°¶¤ö�ʪFÅLvŀߨA:]‘ÙáàëWß5û€uEüð-Ÿ¿úýå�tÉ⊌®äÊ*,#Ãý™¯?{ïó?xý àG à2›õÆõ—^xé¤93´È3�óíï2tÜExO8«® ©9$R2ÑxšT2�4â›O_‹ÜxãÇ*WåÛ¯>òæë®ñMŸ¨Äï»�Ož‘×'ÌÌ8�>]ª²ä‘kè(J†L"Š’N`Ë’h¨±¡iŠñꫯæòÕ¡kLñæ“büÀ9âå"M¸©[αœæöL}æÌ3/ ½÷{XüöË_â�7ÞwÜqϦã�;)\[Ûð©$I˹£¥z¨s~«¸µ@ËTËò�€›ÝškkïX¼xù?N§ç+›­à~�NÚë±Ã"'¼“›×æb@iì€/z |õSIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/noaccount.png0000644000175100017510000000025615224505336024537 0ustar runnerrunner‰PNG  IHDR:˜ ½uIDATxÚmÈ! ÐwwØÍô�™¾éÚuEï›"™ Wš>AïÅtM6š"lÌ8t?¾—t”2Oo©Š�ÿhƒšW¯0¦jng« frU-‚XêÊí,̓˜hª[£ïèÄHj£ÀVáÄ@i� ½(¾¸-4Ñ=ÒŠ IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/star.png0000644000175100017510000000116415224505336023516 0ustar runnerrunner‰PNG  IHDRóÿa;IDATxœÒC K†á÷¯®1®mÛ7v²Î:ëlÏ&¶mÛ¶mÛ¶m »çLwÅvžÒê+ó"“Ú…ôäöaÍ›X3@®¬-¶üÊÚâ+·ÓA^׉)ᆩ õMúbsrJ¤ Ï¡ài[èÏ•¬§uËŠø¤t�ÝÃô—¯4ÁÜ6*½Õ3öy±¸±óÜnÑÏŠÄ´~§÷²ŽV˜'H¿Qÿ}¯ŠY–ªˆT4òÞÅßý¶È‡�h „» ä2)®Ýr9}åÔFûƹMžë.;pÚ[-ë;ù[}ýåFÑ÷¾K[ <°@�Lp·p/oH];ËñMS:k;ë®Í%¯x*ö¹…4ˆ�Ü’Ÿ®]3WW�Êñ÷Ž$!08…æÆ¶c8OýƒªE°|šQ >�HTünXü ü~>ŠXXOMŸ_ó¾qœ¬[‚Y½“µ¸ðónP>5Aa…ˆÎ8ÂæU’Þ˜Þkó׺ü>¬]×Ï—k‰”`åånKRc`œÅUÐïÄ�ÞdÅ Œ._cÁ ”KšÅ]œ¨‹|®rç<Ò"NÄIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/viewfolder.png0000644000175100017510000000047715224505336024721 0ustar runnerrunner‰PNG  IHDRóÿaIDATxÚ¥�%PEQ@wïw³ˆ»%^Æ�î¸Óé¿Ï éƒ[¥G¤îîî,wqçÙÌy~Î3�‘�„òšçn$ô‘â¶ù‰ÂæÙXUDuYhÌ~AÞ+Q…§9öï�'è=‹J„Í\Ÿ0sð>Ï€ÐÐ � sIû’ à6ƒ–‰ äÉ­ìZ~ýK a5#¦%ú!+… ¨†v9n»‰[€ª@xvØ…¦îuœº¤Þñ M°Ã®°YðN‡IVé°+V1¶utEv‹ài©Aж¯¤;j>Ùß žì݆­î†¤ P¹ù÷'»+„æàë΄¿ï’ºÂ k�ÍNž“¥þÃ\IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/loadermods.png0000644000175100017510000000114115224505336024671 0ustar runnerrunner‰PNG  IHDRóÿa(IDATx�R°]1ÝÜ>ë»¶mÛ6‡Ö¶5¨mÛ¶mÛ¶ù|™&{¿ùÎL°:{H e:nt™ÿxÓ¦cÝ›�§¬y§óØ[u T4èwuàÁKßè›Ï~úà•›ž¿û‹vwûFò<’Ø(ÛiS>"³h2€P»}“r3ZÔÈ Þ€�^{`ÍþGTÙ7[•Ue"ÿ²mY„¢(˜ÜLÞ§€�žhc}où: ÏfwïuøÈù‡óÝnWù=~Ê_ÿùµðõd ]°ÿø€¦œ©òæë®ñMŸ¨Äï»�Ož‘×'ÌÌ8�>]ª²ä‘kè(J†L"Š’N`Ë’h¨±¡iŠñꫯæòÕ¡kLñæ“büÀ9âå"M¸©[αœæöL}æÌ3/ ½÷{XüöË_â�7ÞwÜqϦã�;)\[Ûð©$I˹£¥z¨s~«¸µ@ËTËò�€›ÝškkïX¼xù?N§ç+›­à~�NÚë±Ã"'¼“›×æb@iì€/z |õSIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/status-bad.png0000644000175100017510000000062015224505336024610 0ustar runnerrunner‰PNG  IHDRóÿaWIDATx¥“ƒŽÄ`…ϼÊê•&ZÛ ×¶mÛ¶m›a¡qºç&ý»v“¯suNu†a¼¢ pi’ª€Ñ@Ë%iŒãX‹bïí¼0´�ƒ€¾Äò.¹"—d�ô‘öhÎ*�%¦p|Þ=~ ) !œU&�S]Ç_ ÞªøƒÚÉ%�Ôˆe€K# _šKÄËn7‚-±ÄR[2ó#A�/µ(’&ÕUL±®ëÆÆÒ’‰¥&=ëNZˆ�ˆl`tÃ, J ÇÆò² ¡2´ææˆ?µH´ )¾1QBe¤ÄŠSB ±<�ÿÇ ]üÃ#̪G’Úþð›‰�¼DùòIŽ~ùCÕg”e SDš,É)�ˆÆÚD6O`<™éá«|DÒˆïÛUV&tõô:–§É©É i$Aìù|ðgz…àB£$w`”¿šàÉØ‹5é½� Mó®jû ÷IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/refresh.png0000644000175100017510000000143715224505336024206 0ustar runnerrunner‰PNG  IHDRóÿaæIDATxl“¬\¹|åã8n>ç7æó÷ á¼±µÆ.:xۃù‰9fÆgÐJÇ1_,û‚_|0©S¿�’׆æïOã4ù#€R�<µã;ì¸ç–{[UQü.s¼øîóù;ß½-gîs¶¹þîë0¢8‹ 6ÙqÛ ¶·q7FºB­Vã‡e? ^X{am¢á€ÍÖÝÂn¶ö”Þ£ °þøäð…ë®±‘­5k¸Âñæ[oòÙWŸ8KT‰xþ•g™c»­·cdp†,ZïÊŸå¼Ï�–ÊR÷*Ý‚Ä&¸1‡_ ‚PïV:¿>1«Dn‘ÍdÜmQ» ó�Á Ðå:~:Tv¯lÆ “ FalÌh×Ògå‰Ûä¸ÀškFר.3k©DBªÙ «è¦ò@K8” £Ð¾Éš„ÙtAÕVè *Œ™*yÍy�M›¢+Å·I^¼×Ͳ�òVùMï“üàk ,Ãe:dÂ=™] ,ýa…02ÓFüâª^GØQ�(ƒñ�WzGõógÿ ¼Ôkç;OJßÂàTh‚Рû­‰”öXFÚïðÆ3Ò�†kougj?vß.ÐüÁ··iœ­*w˼3ƒC¡ø)(FJLM³òµ¶_öD«W[Ö½8pÿ÷Ϋg;k%[{�”Þ¿ ü´£@<ˆ#+äKzÔD4dIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/status-running.png0000644000175100017510000000073315224505336025547 0ustar runnerrunner‰PNG  IHDRóÿa¢IDATxÚ�“C¢�A„kžq´�¬"6ÖòlÛ¶5 G϶mÛÆµOº¯­¶ÎWõ£a“‚ÏÞ"ø¤¡[Û‘ |C‚ˆÕ-D-Ö"zî-¦�Ëg¼mGè !CH(Q*5¬ªÙXFȺ$D/¢¦Û?ùÌJõü ®7/ÒU“ýÊa)» 4¹‹ÈÁçf€“$ܪˆUH^s ágR· 1ã=zë{_~lR®T‘_d<}�¬$ 8Qâf˜“ѯ@Øv2ï-66v�T*¥ê­ô"ôwe É9á€z |}“½$€!Ÿ�Q`v5=J^´”Š 1c›@ÄŠUj‡ž4 ŽÏÐ�È2Bá�ሀÈ!*½€¨ÙM”J¼|„ÑM˜¬Cö©ç/1û˜=V ÄŽ~Aì,4|Âãè}h¹‹Ï8EÚXmŠîBÚ†û?RÚ&·ßcŠ|ŒØ¡u¤­œ8TfBÈžËÏÙrî.) <Ðì‚ CYy™)÷12)ôüõ…½¶ž{ÊŠ//–<© ŒÌçÏd %ëé½ýyÏnZh�´FRH¸ü(“ÓwÒJn§èI]C¥?T�nžž 3ýé½ß^\ÜsO夑cÚVñ“Û”cJ»ò™.pk&©—ö¦¦>¼ñ5�&\xrñà;Ù·f»”@Ò†£·Ó8÷ 4 )Ý"çÚ>óþ©mŸ›FÞ!Àà/”öÇʽ\­û´Ôús?W5—mQÕ—oWuVíPM×ïPíVý¨š _ž«PóC ÿCÂé LJÔl¹³j—IÏkY•[oøÚ¼:}f¦VhØñˆ7]Ttøœ@E]—�<þàx�?4YJGÏ’©þA>‹R/åÔáIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/worlds.png0000644000175100017510000000140015224505336024050 0ustar runnerrunner‰PNG  IHDRóÿaÇIDATx�‹CƒlW…¿}.Jm3¶mÛ“hšYfÑ(Î4¿"¶mÛj<[åꪾ]¼çì4¦Á[úFËð/ºé‘wØÉM�¼ûc¬zP?ðæ^~øªãø-‘«ïyN/»üJÔÅ<ßçãÏ¿Ö P@Ð5*‚ï‡ó/=xɱ~T-ÒhÔ˜ÙVÁ 8’â¼ÓO¨ NÝ*­uXÔY0>¿þöó1~­œ¥Z[¢S�06n_ÄÚ˜Rq/õÅâD XðBÁáìeö‡Ï�ƒÎ¿M�9í"vì-aDh5#®¾ødÎ>å$¢=%r»+JÙlÄÀx/§œs8‡Ÿxëgf>–›î{\ï¼ý¢f1l�ßMqã^®¸öDž{ívæ–˜ë¦Z¨$üt†Ü®OðÕKóÃ9l'@�¨Z'Èçžî'6†bË!é�?6¨k¤“IºM™d˜Æ¨ *k |CvÃ.ªË¥MÔ²\rñQ$ºÉ·-Í0d¡Ù!BhÚaŒ*X§«u+5ß}µ�¹m Têm|ߣ[Úê ;ƒt§i8ú„)]ýøªŠ³J¼\Ä9ÚmÇæ\Ä×쥮J¶a�ìÇ”[˜Ð§Óî a‚ áã‹F ÞrñH$Uyõûœsæ�üVlQí8Âþ ±'tÇifæv"Ù"~¶T壟çQg0Æ`ú’„"dÒßî¬ñè­'²½ÚáíÙ,;¶*a§Íž-Yz[UüÇ8þ¸£q6F¡uôÁ<ýøìÚSej¸ŸÈÂ@wÈM'Oð[ÊãÕWvc›ŽéÁÔêí�ZmK³µÜe.5cZžÏu7œÊx& α¾qÔPŠ.¶ï®hʸèü£Ð0~]9ý2âÎù€ j�U „==ÓÇ]ø—ŸuÂ�“½ÃÅÒÒâÌ_[~�ûú• µò®a꛿5™_¥LŒ7IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/log.png0000644000175100017510000000047015224505336023325 0ustar runnerrunner‰PNG  IHDRµú7êÿIDATxÚba`8¶ËÈPÓ\#† Q}»3WN™Ö'LŽæœ!¹�›0UîÌ ’vBÖ0þCîÜó 8[MÕ¨hZY{øëÆK èý4ëÌ¢k,”­çÛéÕi]•R‡óåî‹íæ Á`O ¥9Uqh,÷ß½•Aê„�Ñ¥ª)ÒrUdPmH©¨:ƒ°â‹ŠàÆ!K4Å…êÒ… ‰€kÕ�ÎÁ¿\̻¨!¨–T+z›Ö$ø:4׊ƒK'BªŠÞ•Ao�+óz3z¡wa¢°! VÍ¡ø8¤^ŬD'ŠÎ¦ÄHÀM!xõâýVJíÖ§úpGã7¯yt/‰4Þ¾IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/16x16/minecraft.png0000644000175100017510000000134515224505336024516 0ustar runnerrunner‰PNG  IHDRóÿa¬IDATxÚt��-G…¿A¯ÖÞØ¶ÍrlÛNÊVP®Ø¶mÛ¶µÞ¹ÆtÏLº’<¿=mü,„£oÚbûƒ¯ÚàÍ ß÷-=v`,‡³Úc« .IÓô )%�F!ÅbñáJ¥rùÕ‡¼úÞJ θ×�d,/ÎÈŽÃMòo±çyÄqüï:77G½Þ¸k|õÑ+.ÛëÉÏÌ‹ïÛóª�ÑÞÏû†zŽÖJDQ„išÿ !¨VkضÍê뎙ïö>=ëö}¯_Lðççógüôáµj '°(uð}Ÿ\.‡Rê_²b_Èz›­Ae¢ÍS×~ăW¼qòb‚ôãtú¯ª|ù̯|ÿéßÔËmÆVF&13Ñ$ƒ«w“Å‚oþœW®ù†ïž™DÌ™UÀŸ�X2eºÑ$jYÏÍ4^­‹N+á£~çûW&ˆÊuz{»éËòxFÊèX¥~‹VlC=Ïô—U …�Ÿ¾ùƒ_ѱ¾Jøý‹D#%�@þÝ$'\<ÏfI@ø.=cE† —ù—#ÜŸ�·êT¾lÐcºäAÞpè|„�ÑhKGP©AÜ®«O«¯×ïÑjJʳŠJ»‚­ !0E'Õ\IºÄA¥Ñ¦˜µ‹Y�³£:$¦¢g(Ô÷©‘hrES« Û¤Ka.!°L£r¾F_!`|d@ظÂB©„$3H‰kex~ ‰@I…mf…Å¥œw³+l~�˜£Vo"¥Â´ÃÕª1Ž”´ÃTI}î „MToß¶˜à¦ç>=q¸+Ø¿ôPœBµZ¥Úh‘w ê­“Qíϱ²-k›¯Ÿ=Ž•áŒƒ÷Øå m×xúÐ�×Ë.Ø›l‹5³õG»³¦£,»Í\[Ñž�X�èªïh©~ÚXEò´…º¤ .u‰ü0œ.’\ IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/24x24/0000755000175100017510000000000015224505336022033 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/multimc/24x24/coremods.png0000644000175100017510000000222515224505336024355 0ustar runnerrunner‰PNG  IHDRàw=ø\IDATxÚ¥U�+K=w2ÉFo7ß¶mÛ¶m«ðü^áÛ¥oÛ¶mÛö2Ù 2è龿§§ÞÖ¦æûŸÔ�s[çÜFUðOqè#«ÎØã«_5ùÅÝ~>çÍc“Ó^;Ì9îéÍß:øÑÕOØçΕ ÿ»\µ\ùàG×úò��W¹¶Ô/Þ¯üMû[õió3>ÿíi¼ïƒ«LÇŸÀÂ?@¨•îYªRl 5ÑŠZhi‡Ñ ‚ŽÚäôâ}â 4¿½ÿ»;Ùܤ͎hc$äwžåŸ_oߺΌÅÈ#×¹ÞÔE',°^ß³³WæíU¬âD%I‰*}‹4–Zd½··Jv™P˜„jîw¿C¿ûƒ Gš‘Š;RIÉ`êôGíýís‹ï0çRk�¸ü—mviÄ*6Bžpá%t®Û!E›9ŠLz.t‰Oß †W¯{{+èFÐÅ’ÕGŒ²Ÿ f¢*DdrÜr¨ŽThæDZ8T†Í| ‰`HPÎ@IÔìB é’/]3Y/¢Y‹£ÌŒ¢LœŒëœM´ DÄ`ÈDå ¬«Q/öÂKÚÚÄ3UgÕëHMx<ëàÀ°i#@±PDZ}3Dsf°ø~óçÛtöd æz»-æ©ÍO¶Ø)#ÄcBº/æ´mÄ ÇQ(Xl¥ÂbŠ%ƒ+²« —©º@ù¼Ö;䔹ksÑmKÞ̫γ&å˜ígbŦ®6KK@r‡óGhh$F½VàJ™(Q %Á¶6k,¸îÜGo¼ÈÆ¿ñ¤õ'£-FàI—SÑ8«t,O(ÖêÌAG9J à—ߢäý³¾=¹²HeùêbÕ•ÃþèKÿkÿmê]¾¶Á^oùò‘«ÅN2BFHiA’#î'mêH�rÓË7c€ÑM‰¿ðhóãϦ¹>@1íÙ êcRøÐy¾…P‘6Ȫ e‚#03Æ£ ¦› …È‘•¬˜#§ÂÈ; •7ä�àg縢 Œ•F&aØÈaÌ�2CÉ(ÏWYÌšP(V7™}®âüåyÂ/ü÷:¯µ˜ô³œwõKWúríeç¬[# ¶E(èøS]Î÷ÿ0,ðÎs¿>5ߪ³o2gmöÒë׿w¬ûÈà• ÔØrÎ˱ÈÌzÙ¶~ÄRóõÙsõÚf0  ã68Æ&aÅ š5/å*ï|OO<öý‹Ã—ÿ°!C_Ïb•­ �â‚ ²7ž¹ú¹sõ)B·`·gÌYW·™½öy«ýÃäO¤®»¬â"•M7¹p£'õ [Úý#Î>üÅÇwS>^rü Ê4”`W©":±gªË퀻�¨{WfØäæ[)Ìm窉•$.Á‹T®Z6 ù{f ×/ƒËV#o 9ùºõ+*= �?Îh,áñÚœ÷ñ#•6Êù·H˜ÝÞpŽ™¨XóÌZœ¦¹ä!ØÁkͳèOþF+l“çïïŸB~%4@øx'’„IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/24x24/status-yellow.png0000644000175100017510000000121015224505336025367 0ustar runnerrunner‰PNG  IHDRàw=øOIDATxÚ­Uƒ®œa=psߣ‘6®ÃÆETEí] k®mÛÆêÿkNgêoÍä¬çœ@DyqåFÝ©Omðî[±¶c�¾e†¾fÂÚªÞE#ž®6£®GÞ.]xrá@üÔ:aœyA×QÐ%ãÀ Z7ƒVŒ Å^Äçºñ¤d�„õ)ŒqH‹�>�‚¾OƒhFÅ÷)ÐûaÐQ´Ð šë�q‚m \»aHºq�ö1ñ�•­´lÍtã`¢†œâùµzT¼Ãòð•mv} Ia®,�3Œ×Þ|䥋,šAã]0*v<9¶ƒ> Ê«Cº�£èMtü*e¢ž’Ù6�!øpi®ö(”ñ0 9/&(ÒL Ž"ÁåE‚N†~W¹Ùy@‰ÅêD[àk±Š‹�›hÙîmx¬KŽ¿¤ ,).ÿoªn[�Í,@£ä°.GÐSõ·—±mÛz€ìÃm°Jž*¯’elÛÎÌt÷÷×µyÚ<%e^zzýÃ�7c_Å©CÆ J-kgÝâ­7f#èÇkËž}xx�± ªdI�Žjg¿hˆÀÔ³¶�YRyÏl8{òòé$Ê&-ƒªZ-m³J2ÒøÈŶ|Þš'Ì–+ö^ ]8e­UÔâ0¾N¢ÔæÖçߘU‰¾ÿx¦Æ›'/[ !¢`ó¥-m#�D&´Eói›GácazÌl”@À|&È©Œtu'•LAƺ%¦èwyk�5UÇlQTÜ<wÑSuóSB#HHEÐXèËriäÝQ¥’Ôú§d*B pªü)ÿv�E']fP †@ƒ†$MAßv¯âà ÷ùÖ&ÍÎjZGD1<æ  QçhüûÝðEñ%¹©ÈS�߆®¡T‡j#ëöÈj°U Kšä›Z”«i7‘X@+Ä´¥Æ_�O)á}EŸöÚõ ä1µvm¤¡áRÁgЦ@jX$˜ðbàAJ�¦á›ŠüoÒh%£èÇ -©@½IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/24x24/resourcepacks.png0000644000175100017510000000314315224505336025413 0ustar runnerrunner‰PNG  IHDRàw=ø*IDATxÚ}•p+k†Ÿo£Æ±ÚÛ¼îµmÛ¶mÛ¶mëØìé±Ê4i°›ì³3éÌõ3óÎ?ïû £gÂÿs0°#Ðà÷ò �6köŒ¬­š€‡€VþaÔïüuˆÜŽ eQͳÿÔÔªíë¶ÙK�ïÛÑj©5s´�ë—Ùïwó0‡¿p ýQªX·œy•_ŒðE½�è©û˜5Ó†Såd�ú¦&¿ª¼U|mm*Ø'Ù'üC¯9¾ï�  Ô&½pþ”@Ñ'S�ât€ò°Ù{Âcj»OütvvÒÜܬLÓÄãñ(Û¶º[làªGÕDEø@3ð.H“¸šB`�?#²C^Ò+ ¼2yò©3F>­´´MÓ$—ˉã8®ðz½`†ì¹÷î;EJ‚— ¢ç%Àp?ážÿL Ôª*¯×÷ÒèQ“—GR©”[=ét˲Èf³¸IòI™4iápXÛýÒ1ÓrHå°›ÿ4¢ùt1ÔÃóçVìu‘Öó·Y¶Š¹Á”ÂÉ�ÄÖ47 ¶m»ITmm-cÆŒQn¢ºñþ¢^û¨ÌúO¬Ÿ£–ù�¤€ð>BG#LÛië49ëÑÈq?üHG,&q /•±B!Œ=ÕÕ©íO9EFäƒëº.J)¤õ]Õ°|þÚçïJ=à»ÃÂû*ØÝ\ýí×ꀮäs;Å�éYž×z}1d—ôÊÆ`Ùo±ØºqãÆîóù$¿pqÁS™õ"éd³9QÙ¤4µeðz Qbç46¤'Êosf?º|ù5F×mëpiKgL0.\øÉ—_~ùù{ìQŸ78‚Á .ÒãBôö¯ÐSt#ƒG5Ó´¥‘hÎG"²=Ûbû—_~¹ ptþFÉ«2 Ñѱب¹MM‹FŽy‚aZ×Ù¢@L²í¿`Û9Ç¢µÓ‡Ù÷RZ’¥êå—_vƒ? óO~-ïbËF+Û«ººzŒkÛnÛ¶Å2j¡ý±3í’ñOÇSs»li3Ò/¼øÂÅùÎ呂ÁŸ˜¼ð¬Ê’ÚŠÊDϔϯëÉÀ¦Mn¢Ö…ß}wÝ AƒÊ»hÔuÔP(„‹ œŽ¥Å‰«Z~ÿù÷YŸ|òñ9ÀÏü ¹u+-½|ØI'ÕUU•‰ã r9Tþ›³m?ÿ|ݬ_}íóÑ£²qäeùN(++sGåžpvΜÙó>úèã‡ÞhkkKŠ?óÌþÔg¦é|_S£fš»ÿþάúzõCÞš?õû�;ayê»wïvæôéÓŸÚ¥~—çÆŽ{{49Ø Ø˜Œj�j hrTu‡Ã#áðð¼ßžêêt›×Ï¿¿‰��ë¶¶.nvœ5€è€VPªp…vAÀ*(÷}í¤±Ó�,IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/24x24/jarmods.png0000644000175100017510000000200015224505336024170 0ustar runnerrunner‰PNG  IHDRàw=øÇIDATx�T�,IÍêésu¶mÛ¶m+ôgû.§ÐÙ¶mûÖ6†Û³Í¼ÌŠ©Uï~eD–ë½—™Õ ËkÛl³zçöÛ?ÖsÈ!}ý§žêö�pB©k�=~¤õ+š×__À"¦/ø—k¯¥î³ìå—od¬·âä¤@ÇI¢eíXyç�ñ§Ÿ2´çJ˜¾¿5øþFZ<þÄø¶ X­‘AÕ²öYŒ@[‚o«Õ¿GJ¥ŽÊ{ï¡oš Š~¥~¡€ÕßÇ÷*•gYuÕÓX|x•UR¦ÓŸè iô<\×]Ï„×_�èî»k"BJ� õ²w€Ü!² š›äãÚV³e 8+�Þè–õÖknzðA"AêåXª·,$Ÿ&�©"cP“ ŽRØ--ð@këA� {žA*Á/•˜ÁŸšBªçœÇ¶�\«í‘!­s/E0Ö �Þ„ˆD€UòE¥˜À@ÍiÌjP(¢ÚšŒ˜\° º$ˆ ‘Õ’I "2+šÓ+"¥Võì =÷^±8Cpe.gŸL^Qòý†OLÓ ­²ŠTZUª T\eµ �À½rH=2®$Xß0Äz†qïfGu�ÞÔ$¶záŒlµƒª�L¦¨F"ç¼N@‚�ÐN?„R)ÔÒi™":‰:5Ù#‰‹ã{ï ^>�õK–° ~ë 0_1ƒ±Ë9¿$ÁVîîv/ºzÃpxóÍÃá­{\·å/ÛþIl‰ìñÚÎ;•9ï<$PVÆê•j9¦œËhjΠ¬ɹ—>ùÎÜ�f¹Á«ä× ¼ÚÚª^‰J…3!«^šñ™¼ç•ÂBØ6¢ÍÀjO7}¿â�Ž‚ÓÛËoVÆXÌ:†±^ZÓŒ½b±Æµ c•?-ë×OM±®úáZkµ¬»ñÆI~b2]!Ä �5§»žÏç?Ü;ß§1 _?6véKåò£Œ>&™¼îƆ†ë“š¦u;޳A*¥ó‹Rx*׳çóÖåÇÅh(—C"Ïvv~qûØØ\ûõJåÁ¿-ë/b^“Óö„aÜÃõXªëÂÑsše 71Œm\€èle!r�¾‹}ß]k­÷�T*¶"ÖœÏÃéê³Ö=vçιWB期¤5_Ö huÄã×Sx«Ìº¼‘"(}fšw.DÀk1r]å2Éò™G^ýY(³UÒmçIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/24x24/patreon.png0000644000175100017510000000057115224505336024214 0ustar runnerrunner‰PNG  IHDRàw=ø@IDATxíÔDQà%¢’H( ®B"ÎjíLk¯îÍ I€!@Â!áH@�N÷çy;ûÑðã}o훉~¥�ÊÈµŠ°ìz{kµAÐz ’-°ncÅeذÜ�EHº?öÄñ(XAúV|�¼�ô©L Õ�>‚Þ�<ƒe©€´9 –'°ÂÒ×þ…ìK¬¨˜Ž @êæ@Ò €ÔeåËX9µíp@;åÉe0@zoô áÀ•å'ï¬Gå@’Oƒô½úí¥‡dmÁ6$Ç/èÜ>hõúXn*þ€zc¼Ú.ʲ1°^Xž&–S†]äYë,×`í~9ô¤· ·ƒ(°oSO!ÏG@:‹¤9ßÿ:Okà©pàp› maÙ‹þR}Ôëo¡›IIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/24x24/noaccount.png0000644000175100017510000000027215224505336024533 0ustar runnerrunner‰PNG  IHDRÅb$�IDATxœÉ! aÐw‡Û?Ó7fú¦ÇëŠÞ7E2A¯4}‚Þ‹éšÞhŠ °1ã§×ïÕW È4xó¥¤É�@ô*¸ó‰Æ‚Š ®lC±$ñ¢Å�'„bÍŒ CG6¬B1§K›qæŠ)%{jp æŠ1™ÿnƒE™Á¤™ìùO9$Õ8.IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/24x24/star.png0000644000175100017510000000204615224505336023514 0ustar runnerrunner‰PNG  IHDRàw=øíIDATxb4àÅV6æ·5“�8åå6vbõ1«ðÿ?†Tf!½Y̳þþaH§ªë®eUz¶Séý÷¯�ÿ@øù.•@1Uªøàꦿ¿›¹U}ÿþadøó‡�[Í—ÿïo¦ÖÛKY˜ ég!¬€ÑŠUH/Œ�OÐH9@KrEQtßjclÛ¶Û¶mÛ¶±ÛNƶm}&}vwU½;¨Õ«Æ=§qnqŸGÈ(ádmµzžQ^0g0ŽC áÐbñû�d0ßhèM½Bñ8¾ÀI¥É›øæ"'S9´ËµnÉÆ=cIµ¤¡Žˆ ´rm½®§�¨Ö¤�øw£€ hiÁ:)ZðíduÝ÷]#ÓTµ°"¥é!÷¸º`ÖË�Æ–%§ŠÈ°@4Þ#^³E³X�F¡ªõÛI$QÉFQPÙãŠd*Ó”­ÒÊ­ù¦bë†<»¢d!ª�ÑŸlW×ËŒç#b5ëý\»Eßd²V3±ꨦ ŸuÁ�¤xnð]¢Ø©RÊ·nâÿ�ó+ËŠ7œ-Sž œY·U§o·= Á  XxµåÕ`ß.B÷8jP ‚ëÕ¢"yË´pÑßm[+X¹ltÕ �®Z£­J€¬T²iØâ�ϦwQÜ=NY:¥yóG�SÕŸ‚#5é1rùòéãÇvnŸ¨Ò AýÄâÈö½ú-É‚i×èÒq_¬vÓž2© ÀQO›‚¿î±O_8iô螣Ži�×%+Ä <7Y`ö\ŒtE &Ž/L•Tœ~ü &Ÿ…vÜ fů·¥Î\0aìß}�;¶Z@þ‹µÏ =—¤¶b¶çáü·œùKÊJJ·êY§¼¦Ë¹Ð~¸Anè0¤Í[-:öDB!�€Ë©SN šÚŽ–æ¡•6Z yÅKæê]g¼£¯v«pl�¼#-&±V$W%jD"xw ŠA”0‚ 8hi’É05ç^”I·,Ë � H$Š× Ô€‚:^‚“!™`Û4ŒDð’Zxw„P<ð+žKÔ‚hLÕ¶µQÎíÚµi�*¨¿~ÅB$ˆH‘H‘ þ=±8âdhœàd¨ .¨ë¹ëÂ’Ù‡O¢ï= & ޏþ/ǦޑtQÔÔFX»R˜5Ž¢â�úq$ÈîèùzÎ܆\Þç(iܦ l% ¨“!D.½~´\»à“Úfìãâ~x‹^;Š›wýj°Ÿ^=Šª»Î_õÞéÌúçQqUWß8ƺ‡\zõ(‰¾q\àä—‡Óã•aDÈ¡—†ze8�Þ<>xÒ«ÇXqöÓNW ÌJ#?0IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/24x24/loadermods.png0000644000175100017510000000210415224505336024667 0ustar runnerrunner‰PNG  IHDRàw=ø IDATxb Ä7_�Jj½8»bÚõ'�‹ïüiYpûSvÏ•Ó@ñlÿò3Œ¸ô1c¸UÚQ u ;X>¥4ZY*Ú]š9ÊUŠ'7DÞDO…o ;S%EJ©`á� ¼<Û¿YÛ¶mÛ¶1¨ÍQ5ªÝQmÛ¶ûì÷ÂÛÛîÞ$ÓÌË«wf'{ß%ß·¸‹N‚ ÁµU. �§06©adBÛ8QÖý/�áŸ_ÿôÇîð»Çßè§ Ò0!M„V€Þûj”¾ûì•»—ßéÖiÛT—ßéæúeWXý¹¶ÆŠ4”¤š´ï©Æ…3kæ­³B³ã»ŠA£¢„7µQ¿öÇÐ=§,6¤µ R$Þ¾á$þõ×îmK³V>p‰Íw;õËkO\VÅ @L¦Â%s`rY[�ö dˆ„4Ö,BAlÔW?MÀ�ܶ…7U ™ìó=ב¾’��¡!‡1‹j²{ŒI59&±$dß®xüWUš�1º¶ÒW åK&BÊdòTL¤„„Ý>×"ÀY‘K(£UªÀõª›êj<˜´Y—ÒóX*ÉZg×i¶§‘cvŸ«GD�HÁ`&0oíý®ew=NÇcmƒß?Ÿv4WØ2µrKH’eÞ†)#gÜŠ‚ë(ò±€‰yßvCGÊ«iY¤jZ^yØŽ Nêl©T÷=;‡øòämB’òÙ…(ïµ’þKØ@¢î¡DýÒAc�Oõ5®m™”»f�e�YvxÙY¶Ù ©T!Yÿ•¥R]8<渕‰Á8â|ÏÃdrb`$�ŸûBŠ6=qÙ$™ê¦y ¼ª¿yÞíwŽ÷|øþàwÏ‘bÕ®5yæ« Vn­ã“`³ö=%Ó+ý•ð÷¿…ðñÛO>Ý2Ã�Ú›++Þ~쪣»?¹ÿa©è\fçó–ßüìóùü;Ý}£éâ¹-U«Pìõ”u—¡ª*h®÷éÇžP½ðìc/}óü…›å/4Ö´.±eemûliÛÆûÞx…Ù¿ ¶¢@1ÖêýO¾ý྽g?tÙ¾oqÄ}O6Õy�›!ø×öå·}ðþý{/ö Š�#Ú3>mÆ”[”Ç’H±2·¢¶£ô/2˜ ¼3b)sÆKXAx Olÿk%2¨{ûúÁõk©ÿ!‚Å5e1c:ÉU)([Kë¢ÍÏg�Îü㲕±r'ôÿ8/�N@°jv/‹‹öoÆ�ìáヌÇ3@8IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/24x24/status-bad.png0000644000175100017510000000117015224505336024607 0ustar runnerrunner‰PNG  IHDRàw=ø?IDATxµ•Œ�A…O�X/¶½±Ê8y±kÛ¶mÛ¶mÛvn´1Ë¿ß)ofÍäÛÌ»8gîüR–e¥²UÊm–òë¤I+¤£‹¥Ây0‹õ4bÉ §¦,�ƒk¤û¤É»¤/˜d›`'�ƒ°fÂdGÍHÌzÒS!ƒ³RÁ1é±….ÁsøŸ>ÀØ£a=ƒé-Óà°Ôñ/§ ?µP¹Sa½ýÑ(Ñ`îû(¸èݹ±’¼…0�¾a’g¾�ðó}húèIJ1t̹�‰É胖¯I4˜¼‘å£D`´nÞ<Ñ´i�½v̹É®BÀ`’µµJÊ­d¬“ÉÎÝØ¹mÛ¬¨¨(›5mZ6ò�‰ñÚ1ç\³#™d=ôFrZ$å—x <ºwgÿ}ýúÕ‚ÞµñÚ1§2׸6Þw �§@[seQ2¦×Q(šÄX4Nûü9&Í�Ž®q"!EŒëxtiÿ ­ÉRáî�HMÒIÒ�GñÈ ­ñüÛQ›£camÑPiÒèš¾ÈáYÐ)?„wkø6íý÷6e‘Ãíˆ|ÐÖB¯¿›°Éä~¯ÔÀ«Â}â«Âÿüb‚Ç£øù¦/;÷Z£Wú²3�¥‚îŒ5?˜T÷,üs4=’×u4éÛ…‚aþšUBü2Œô±X¼´Nœc”­„Ûe‘s«- ?†2>™Ÿ&“á dÝa¬øÃèõ'Þ�š®•ùèG:H¹NR¾3p �׎9çš²4~gN�oÄïØYIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/24x24/status-running.png0000644000175100017510000000135515224505336025546 0ustar runnerrunner‰PNG  IHDRàw=ø´IDATxµUC˜lg¬8Ù3ö:ÞÄÜ+¶Í¶íîØXe÷l�mÛ¶í™óÎýÇÓÔ÷Uë?]uïÑED(»îg lO„ªµ ê¦ih˜µmÐV'B[a€®ô~Ä�ïFŸ‚| Š>‚¢›`àwÇ Á=Lð𻳗`n&h+ ºr‚¾8Æ‚§º²q d£ËP°˜k’ðçá? Í¿æ Þ6&+lò3,Õ×"$^¢«ðÝÐ1ÈF8x‚…—ƒ#Ùê †"fþXR¯‘﾿9-+‹ÿ»»ø:ÿ]&8›Ù €`ÌûÛ êzŠ>Þ.®H&ºã3±×°IÁ”÷VAW@ÑQuWP¾ï~ÇI_)4tËû?‰4Äœ.c3·LhCÓü· Á=|ï»Nêëë£Òò zó3]';›‰¯C2 è³fƒÔœ»?ÃHXXX Sé±7¾&xZ¢ÜÅœTf¶� êÓal‘ Ö1<.333N�º•™÷¹Ì�Ú©°”™™™9;Nì˜D«×’ù�ß9Gp¯Êmž÷�{ÞOUÕ�,..*[[[Ï~Ámþ/àÅ{äC$Iz›¢(�œN§xžG¿ßÿùp8ü¡eYo~r‡¯ÿÜS*ËòÛt]„aÄqŒã8Õúøøáâüã²,<›Íæ Ý&€Püpà=“Éäâf„JD!Æã1H£Ñ`ii €<Ï)ŠâabýÃ$I~¥iÚk€üGÀ¿ø´Ç~ø~Û¶ï"Äv‰iš¸®;�†ííí ØívY^^f."Ë2àþa~muíOk7,¿øÎ%àEzìW%Ezò]ïu ã aÇv¨µ¹Bvww™Ç´³³Sç€ÕÕÕÊÑÞÞ^%榛nFÕ%Œ†z÷ÞéÙ·;?Ÿ~xtèìNîác‡.õ�Këmâ �ƒ*uÍf³R*Þ ÎÎΪí9äüüœ»Üz¬ºITúLú‡ßòÇïž0ì÷½t \ä„#ŸÐQñ{1£•€Æ²A½í°¼º …D§ÓA¼EìððP8ñ¹û=ïÂÒfƒpœð·ïœóÃÏýÿ<§ÖÔihfq H~Z¢Ý°€ùà”á�FzÌò¯£ÝJˆ¶SV×—¨7èuÏðŸÖu î÷°‡0ê»üðSäÏß;Å=Œh^·ˆjy„ÃR¨q h6âC_ŽÈ 0�ð( -þ²ß!¼gƽt”šÄhz�?Žøþ'þÀß…êý?t1ë56nY¡t ô~ 3ÓQ¯ybÙ 7”mþÔ�õrR)ƹ¯…=4ùÓ/�éþ}ÀöÝ–ñ/ ~ûµý½ Š-ÝA­)LŽÇÄ}…º¬c7À¨éW€ÍåïÈG «µEÂæ˜n/d÷k‡ìÜeëD¢¿6bò³€“ï�1eƒ¥›[xûŠPœ'y”áè&Š^ ÊEY^&AÌZÓ¢µ¼BÏ�‰ýާ3ýõ”ÞŠ((¶„-Ù$Ô<ð±J•BË)“œÆ‚ÍbËdäEx³C»&"M•™øE¿‡!KÔZMZÛ&á˩ FSß;ð$-[RÙÙx,`ú7< Á@�Ÿ•¦­>ÝB?—½µVÎp¢^Ï|(Àr;øûÌ^P@?ÀñYñMßc«`ì ÎiÉ’ ÿè†otjßc˜[—=Sæ> ‰n!<†�ÀqŸŠwµå'›kìÌÔ5@¾s.ÀXgŒèóò¦-ìó”9ÙŠkúNÛ¶MP`ó-R[#ô\êÝÚé‹íuðû2nÏ€]5ü\V„´øW)€q°T} »c®„a7ŒºHý*ß×ñ½_çõgg0ÚÐóÌYP¨]¾Üaü×\3â|Ý8êbmøÁ†¬¢?×/|š®y¡¿ßï+5¨ê#UL©•Ëjüm_¾(úßyYÏ€%4ÿ@{§·ÁsLÄþÑ�0öl�Óö|ê=+ýVö£ºyô¥"2Ž�8T'ö>RwF_¥ïJ^Õ¯+êCO@ø[ˆ (ÌÖöÿ¥@ˆ ÈM Ò¼aÊLî£ÖºD5VÅ*/} ø\”=Ds¦*9ö'e¥ôÕ¤öLµ×')?c Î,Uj|„2’ú¨ºd´ÇQúä¤ö×_+›5¡5]™ƒÄ¿.©ŸãF'æåzú×WÄ(;¥Ÿ8æ|Yþp5×枀)c²5¦1EW�$LŸ+ÎÏœ˜¯Ú²(�oIÓ¸f33úlõ¢:·¶T�£ Éb<ú2ÞÊ5Z<»\c›RÕR› Æùeq�˜§­>Q´Ñ¹ž¶‰mé¡' ¡2Æ&Ê6{r� [0£T¿-mÐT;nªŽSMi¤¦�ËÑŒ yâ3ί˜_mQ‘9G«‹G3¦�ÊQ3kR�Í*SSU@mŽ,ALM6k©�”T-›Wɼeç²BO@eÑ(Â�p×òyUþvV€­(É9ŽqÌ�§�0ç?cx¥yÁþ ô…l•ä#í ƒbn" #õˆÆd^ú†ž€Äè•óy&&?q–ó¬ŽâŽûC”cÚHn py¡è)€KOì�‘*ÎJ*-¡þ0&㇞À"xl9…}·Òä%mnÕÜ5uåÑ\ã�€WúqŒH¦ÄýŒÈ1�æM+¦�ë^$ÆG~!ô�8ú‡œVu,¹Lž»s–·ù„/+Ä :aÄè‡ó„lP à %wcÒ—Ïî<Á1º‚P¢-DD{AÖ Ð€Ø J€ 9vçØê¦™æ bGˆúÎY*™xe!`¥ÇQÀB. 'Úµè ý¤cºvH@ÛêŒèq¹h>õLâ„ÿùìΪ„/áLN³å¹sDáOŠK¤ bHþ»•ÅXiÒhêX}Ð Ò�4à8ôà(Nâ,yйÜcßFÀœFÇ]zŒú%N’»G2Hã1da\˪“ï.ïéGšÍ�ZD_7n�Àd8Œòâ0޲rä9A‘ÀŠ ô¥�bˆc�£nõ!RÙîз›8b XÂ�sÎܤãöH!(œC Y�ùÓKIs^¿/kD¤èCNsžôðWz�l\„•k¸€üÿ{U cp æOæ^µ°�¡/Ä ”!'á1'rØï¤ivŒˆŸÇ›-�[ ò3áL!„ \ŠEÐpþ{å:ÈÁZíZÄ”j�•�=©€ëý;ÄË› …rYó¦q qœï™›!H ôKˆ¢„�%WEB•0&]XyÄ’•s•^°ÛV Š+Æ¡?d.˜Q!þ›"®ÃH1æþsE3ÿÑÌrÈQœÄ &d;‚@‚�@n’¬DA¤/ÅcxEØ0ú±å1&EÀ]ñÅä9c@¨«!Žë =ä0q³Ãê°$lˆΑŸwä°‚€â:"&XÍî�ç\‹ bLˆú�1!�1!ž¹È{ÚÜí8D…”‰•%ß(ˆ ‡ÈA4!£ Ç8&Ðι¦�1<ŽB ‘ÅX««™g™�áªA´…vWBã;zB™˜% ÉSò§ùL °Zçh§�å:úpìq%oªŽ×D»nŠõa'™=¹�vÄÎwG9”ù\RqE0ŸÛe¢oý}$FØ#¦ˆÑ@J`¤�Ûú8&JH#ÈÅ\4ц…”€_×kRGnw˜­`Ãÿu›�ñ™pu3ò[ÅŽúr(´BKÀ¸–,ýù¼¦¿Ø%ûqÔó�à("æ*@€�›-D‘…Ú<÷`ÒÜó"ˆhé·Ã0$ûEEV¼¨ÞIOØ�¼gW÷J€ ÎyÆÓ/ªò%HðÀn�€"f¤¹�€RíAb‡ þ£$:dt†š`Mð Ù‰f»t?Æ<¦“O>9À~áZ¯Ú€Æšò粋 ôT‚ˆ°Û�8æ6yM»À½ž_ˆ„!„?"H�@ÅèÍ…;aNíA1äj È¢ G8]ÉVM4@O¢u³ï7B;˜m¶Pì¸ü'¤‰ò�6ÄñC8Ïg¶A4B}ž&Ó¾dN7_Z:»HãêGM4 —™è~%®�û2Üs ¡£5‚EÉHÛ¾�8¥ÆE@†öþPMU‘Z85A+gÅþqèAûÃÀ'©¡$b–ï§²ø¿§ÙÖÞß w#WÝp>`×j×Þrap:ñ})1¸ïÇêßë}½ýÚcúse›´ªÉÀ×ø2_ -Ë2ð)Z0yÔo[n¹Ù“æ÷Ùfû™mGèƒ |aM@Ôð¯Ým-Qà¸ÁÖÇóÇÞ?¼£Þ~ÊÚš |­�/7ð…>[Zšª_çÆý}ñù'|n>_ìËûÀâ~&ö¸o�ÑJZ÷|�»¼*« F ú\Cû"­ª3ð¾È—÷iúkQ¼>}÷¡Dó÷ßž¿³'ïÃ�ž–í�[bW Rí¡}~zWÅÙƒ¤••F@±�Ïõ‹^nÒgæëm¾Šowïû=@À9ç�Áq€�mm�%À}á‚°ßSíQ RÕQê7ÕŸÜžhàK¤åy~Ñ›ÜYÓZ‡,7Ñ{Ì'zûûDo3—÷ëTrîY «O-Ï.�û³¾ûòUÆŸ}¢—%-MÑÔÖ¡€Ü÷BÄ�ÁE¯è“ü$¥±e?ã!€Š‘s}SŸ\'î ¾ q_¸Pñ…ë�~kÁ”¿èMk¹|ÓM7uà2Û¿;#z]ú¹<DD? ]1ªxà¹"‚ÇO÷l‘( -3ñg¿èMj¾ÊÀ?¾sŠ�ué…‰U‹ê ¡;ŒÇkŒ¹FCø‘­qú˜xDOcê.0ŸêÊÊwá•™ž5`l«KÐ_K2ÔQ=p. ‚à‘ó]æ/MA7F ¦f©½fÐ,ßVwVw‚ë׿ €ûþš¢M¾"çt³Pû®ƒß'{À7ß�fúÒq³í»|8¿:Ë<Ç™á›W*<Ò³;Á‡óËÓîèìèî²p{}~³`¯À;àÝ û Iîðg:~IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/coremods.png0000644000175100017510000001017215224505336024365 0ustar runnerrunner‰PNG  IHDR@@ªiqÞAIDATxÕšpãH·ý·[¦8ó,ÍÎ2|Lÿÿcffffffff~33.3 óLÀ±c[Rwß—R¹T«§¼ÍhóáIÝj«»çœÛW­ªÈ׿æv>B¨�~2ð)"ܪªWs€C`€p5÷+ò*à>È�¯}Õm|˜a�‘o‡ð£ »‚*^…È´Ö¢�ˆÔ‚' CÀcÌÛ…èG€wóA‚|Õ+náÈéÈÊKTøÿi€=�ëyîÎOáÐÜÍìiï§µðÁ‘†”Åøgû'yxé.ÞsöuôâsXÁ¡æ€ßgó@¾òå7Øœ·VÞ¨ÂÿKµÉ×ßð|üžOÇgN'Ä>ÉH»àðê P!’P^üøßòöSÿMÝ >Õ/þ•M"âѯWø}ßpÃ�ðq»?�~ºJâcœz¼úL §.k½zܸϫËùø}ŸÏ…áEYx›D¿ïÒðR`È&`ø0 €„T¿.UHµ³æü§²’öùÑØõ”´ãþ,3ˆCÂÂh�éæN†‚²Uÿà£^7 ÆÔåú4Ëé€;.ÞAìG9¹$'��‘Öü8û�dó†~À›N¿§|ª³ýs‰|Ô Ð;ŸˆKõ‚ <¿xÇ/pqx‘¢ãIÑyMÈEÑ¯Ž—}1Ýø5@ÏÝÓ»¿{rôÑ�ïþÓ²tdȱ·.½Ä LFÐMÎóïGþ�tì¶ËÈçÎçŽ'š‹Â+�sÏ¥÷3i!X>1zã…{û�ôÎÄÝø¤yì„ÕþýüoÇ]¤)жƒ‹÷J.BÁù$ϧYpÿÂÝ4E×ÔëÊû÷ø÷Ïnëä¾›A4 ›¨ë"¢dP”ü(CkkC0¦6wÍÄŽÅ#Ã÷n»yòЦ€×F .Kñ¬ÒãBÖâ5+ä¿ëÖRE€Q×ßqûÔ'Î^ÝÙ‰áÄ0•UVlͦJ ¿UQT5haH>ç_n¨L¾üÚï`ÏZ»œ.ã‚ˉ—Èçâ8î>ÿ]¸‹Ä�Öc© ð@@ @P0€‡QÕSÞé×»‘ ò©v˜Š°�íõ#jdŸ¬Ô°Æ2V¸Ð1†ÈÖ™om窙¸aË38<{sö \ˆ/‘„äI¤ "ú¸vú0‘XN÷�s²w„ ƒ3ô“zé «i?IÕÆ÷AD ñ Ýd JˆÃcÃÅô0àåð—l§¤6i×|ö¶¡�Z‡ß{Áß³¥¹-_·ã6sÍ`‰LDÓ¶P”8$ Ü*«i��ä�0&Z΂² V,Sõi&l›VÔÊŽ-ƒjÀ©{Rýpyû¯;þrN.=†xì_Ï6Ö¸¤rågm¡Ìh1�xöì9`ÇÌ!~÷yMßõÇ$BÖ ø¬õIÒ\§i6¶1ùuú´\#…û�Ÿ!0¾îxgé²>«Â]§ßÞ÷�MÌÝ4G®bô±FAAQ#eù| ›ß˜ð¿<8\¡o,Ò˜TØ€|( µ¾0a½þà™� AQÒUoÖ‚(Tã/ý³±U `êã”KÊ[pjL|Ü·ÁƒúÂS� øâ�'”çð Š*YøQÀ'*ªT�X±A„š­åïï²eÂe—ª9ÖŸ[¾vQ©º2ùr î–Çòߥ‚X ¸q[¡M¬ßÉB|ž{—Þ €‘,Pò"H²œ®/Àí¿tµ¬•Ú¤ 08³xÏŠœ~Ý‚©ÏÕö)`­a[s'ðŠ×´àZ‘P¹‚Ö¥”¾êÊc䢕ƶÉÖÆ–ÓXy/Šb,ªä Š”¸ñGštÙwZ[?|NÔ2�¨iVå“÷ÊöÞæ ´jMæ[96XÀ‘¯çrKî`ù¡×s—²»¾ÔÇ[v‚™úVÒ%ê}€ ²È¡yÈ @1®ùæ=vñîÞžýŸ³ýåÑ”½ÑÁZ‹ì•vïøÔÙ:¯œ¼¢u»áàôÕ€â4-¤zÉɼØIdÇ%wKs ð…˜¬M1a;,§y¸wGFÀ¡À_ÀŒQ%osö~Î6sñ½ÝÉk¾qÏ×g¢·tæø�Û~†ý�+yñÑä%�ý�CíÏ@¡V3¼`Ç'²âñ¹®D®Lº$PÁåRF”³˜­Í#",Äg95|” J+ÃQÀ¡ÞjKž¢ Å}€L_7)«'GÑÜ-Sßט«ßÖlFüè3�«¦sbø(ÿoï'0?1Ïß?ðWÄIÂsö<�goý8î_yOiý—ˆ�Å)ô•æ¸rŠÂeë»e'‰Ã�3ñãŒü%)—½UŸ Ó¨ -“E½.X+„� „sT=@HƘ¦1½#Ãεßsà»M$<ï ¸væ&îßAF™sû§÷óËÏÿ=\p\Ñ9ıáC C¿àl±€œ-+…–«ºC;šÎÄê¦׈?�’§9($–»žåžg´ä.J$2š°[úƒ@ µHˆ¬�eI¢ŒRex*~7BˆLËJ²ììì-�OÃÊl£axÞÎ�ã\|œ8 ;• 1¤G'šå¾Þ;é¹¥RZ\«— ¢”ñ’�#"´íd6gÕ­pbôiHÆnW¶¢ ‡J·çI��üæÑož›ºyê¶™çÌ|fç–©™š™P@U“Þ½½×œýÇÓ?…àBdF’¥4ÚùÉ[^(ͦpÕÔuœKŽâ4%P¬è“>ebåÊ\v»D>ÄšˆºÔ3‘~… £c$änE8«Ï(Q–>Ð}ùðÄð. ^¹kå5kñf,Íæ¾ÖAÛ²Û4 ƒø|ò˜_q�ž™0‰ë¹™†Èèl5÷4n�Ù‰iš¶Å( òªœ8Í¢ð{Ýu\è÷%QŒÖÔHCÈÜ]Nϳê—ñês¢VØ>Àp¤8Ý÷w_ ¬C2á©�Ž/(à€CF!4r«^€¨±½±G ´ëcòš�Ä•\-—«:!HuHìWYõ+ CÒÜV¬UB N• Ÿ�ïÁ2B‰ ÀŒCÆ?W€HŒÀÉœˆÐ¬5ÆdÓ²ãåwò¸? ¢¨zœŽHt”Õ�8 øîZôq£!†B5¨‚p^ !¤ñ¹ø’iX†>ã(]Z)‚q`"š­Ã¾[$ß®’µ¤!ÎÈ¥a-4Á…˜µ6#;ò«¤gó-ÝY�Ë'©”�”^ð¼'+L¦.†(Eèåü€ Aa9¾È#½÷ñÖÅe ”§†¬OUyzÐ rC£Š ¨¡#‘F*§R„"€„À(&�S\ðàM™ n,@ Ê&!ÿçu,ŠA‘¨¶£1’° uSMUU@]]ÓN¦I /—€êlZ�§Ê$%ÂR�,¨£¾·yÛà¾ÞÑöºØéÍC“m«‹iZÑ4¨ïyÂz €OŽN5ÚѾ8�åxjõ›&¥O%�² ‘“õc&ž5û¹ýw/¿$ÚÖpønz£XùlÙ*»|Ï-ø…ôŸ#¸àÍ@PÀŽïk_Ý~^8Þ}k-ª¡ô Zrh éæô¦[õˆøÚÉÏlÞØyArlpïÔgìý…ÖáÎW#b|PÖâ»ÿæäçHͼÅuÓ\ÁèLÞÜù”½ß}à_f'j\¹µÍÔZ+B”Ä+(Ô"ÁˆT ½ù,*w*#§œï±…˽ÕnˆC¯Ö‰ö´–¹v�Ý“ÛÑÐäŽ3�Ó¿0¸ûÂÏ=úÿ×O®@ 0�²ýŠ_9ôʉí�C»f[ìžiÒ¬ÆÈTì�{ kã-&ê�Š,K6… Ð�=�°�ûÏŸd)î2ÓŠ807Åí;obº1Íc‹GxÇÑ#œìvYú“ãŸì“·…¡O�PƒCœû§3¿¶û»üŹîȪ–ÉõÈ༲2rœ_‰é]Ö7?1¹Ö e(›‡V˜ÔªâôŸvèVZ¶“-‰¹Ö4‹ñYX|+ñ%ˆµk'?9~ùù÷ØùZ h(Šƒû{o¿ø_ç~}þówüÈÉ¥¡Y\M©YÁe�xbN�*Qê,ά©î{ ºyAjœÜÍL}žvhóD¯K?]#ÐnX¬j×´_DêÀOÞ½ÔQ¦€­7O}Òü—îü�h®¾WUð#ß]yÃ¥c$šûŒm_yóνtÚ¬t³.W‡^öEVS8»JoàF ?üàU¦]âˆ|)ÐܳòêÁ}+ï­ïk¶íhÎ}791|§®óñóŸá\ˆð!}z„õƒ+�n09sßT¤iw6¯ —’%MƒDä °¤úɱáÀŽÇ<ùU>(ŒœÇR' IÕ5[�Üæß°Bd»£qÈŸ‹ï"’±e<0“rÐr‹é¥ �ú@$m¾H)åª2UªAµš€ªJdU�©Ú~â`e:’ˆ2p€/îv0@¤N—‚BìuÛÆ%7´A+ÒÔ~&Æß 0WßXu*ÑeÜF ×IB7¨2L¦�ó òôSôÃR7‚*"‚LE™€DT�A5 qPpÁ™:>ä J%h…ý±R ZdÜoe°¨JDE¨W§™JM¸PÕÓª+ºÉºT´µ‹¯–Š‚¦¹ÔM3@aÓ¶>ô£G€ü#¯¬‰PDã°9q*å’~„EpãPáéCøØ…2ÆÿO2/U²PÔIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/status-yellow.png0000644000175100017510000000354315224505336025412 0ustar runnerrunner‰PNG  IHDR@@ªiqÞ*IDATxåšr[†ÿ¡âá ¯ ^�—2èâò &Åö0SËÌa®0JfšDfIf’™Qï¾ÿ·º+Ý2³Z>U߃¤áœ¯OýW¸ˆªBÖ@~Ñ_†ë±2"åx)CG_zK±ÐSŠŽîR<í.F £×;Kñ‹O¥È‚Ÿc¬׆+‘?T�Ø`Ìé/‡‰‘(¡#ú%˜ÒMºJ`:I‡(Fìc1òÉ5ø!Æ«ðC’7V…èh%Ì&”`ö–` (s Ž€pq’Oäc¢íEÈk+‘n1SŽoMÖ o¢ë`ÆÈh•K±x%Ú.äÿE0”°ÞJõ<'.;Ì |mª9$¯†™$$UÂøM˜ÙG0 Ïa–_콇٬…I4&Ùà¯òÏ^ÂÌ<ãö)¥Ê–àíG‚i+D¼µ97˜.#æo {æÂÓ50S$NܦîÂ,¾`¡!Ó|2Öƒ<ÎS˜hMJHi-„i.D¸¥ٸȘ«AîÜ $foÀÌ09·„E&œ¨WgËV»ê¡D¸» )�M…ÈÅy‡ù/¾11`v�i²øf»NÉž/ºu†îQ@ñç.h!Í0�XÍÌç‹wð}ý† X|ÄÂk•ÜŲ„‰ÝRx%°Þ4”àû8ˈßÂÏx•#*ØÍÚK%s¹L½X Á„Ë]DoNõLà+ÍÒ«-.Xü,‹ÿ¢A'LO¶êa>•}Ѐ°p’«B®Þç”`$A°µëʧg'´¥J8æ+’_oÙ��GÂú;�À̽ö ¨ Ñ`!ûÈŸ·È„Gõ9k³ôBöcO< á#}6�T!gØÉ�’™: ?é½áéS÷?äà èã‹C×ø�=Š£ ³ò¯€Õ º?pÙ_�< [‡„®þ#ÈßÄîzˆ¼½‹çdFÖ5^´lÕù_€†ÚMîÛÀÂzÝ¿÷˜T‰–!O“ýDfŸè™Áà}·€}º€ÅG5MåHخϛuÞÛ ÖBîˆpSSSQ" £·´cf®´Ø]Po¹&Z{Ë‘¯y¹ˆÍܳÌ0úXˆ- ô?äÉžRÄ43ëHØež€Õ ç9 Û Ew)²ˆé!½d F;d&í¥&X€,hF “÷?ÀMìÍÍ=¥Í˜'ƒyð'˜ûäÁÅbŸS9(å¤Ü”ãþù÷ßM`áÐR”æá»l 3ϰô>yB �PNÊí`cOÓâuEø!%¬“¤„BgdèoÖYCC�çê{FR»@¦v$�Ø]ÿ ˆÜñ´¿÷꧆µ€hK�9_¿•{½÷ê¼8ªÐ²6–Ñsÿºox¯¾jÃa¡ÐTxg[ÂÈcÿ¯œ½Oþ á��…lî� Æ1ûÚ?Å+ה⪠lj`¹!ílÓè“ç�rl*ô¯ÿÎÅI"hÁrKÐ`b«>½&×^æ-žù[8ièG†Áÿá�$8"ZKe9=¯¼·xqÊJ*þ�ïSB„G„^�³¯Òëžoô¶½ˆœÙ�¦ßý?û`Kp‹~œO{»pOñõÌgêJxCŒ[Dg�ÆÚ—Óò]7R gÿsyï3Á‚% "è€é»­ïî‹ù¶�ÜNiw×O9â¼ãý‘K bœŽˆÝÓðó|†´ý÷’ï÷ÚÝ…'ôÚÆEÆ; Ù,Ô-r=®”èjñVÐNÓåÒnºP§óÅs+þ™ÎýL§‹æèdQœNuÓ±ÂÓt´°‰â=ñÞuEßнÒÝ)}@·Kçéf© ë%‚®‚ËàR±  à'pœ)t œÇÁÑBA‡ÁÁ¢y:Xø€ö…"´Ÿéë˜ú:ŸúJ£ÔS6C�e‹Ô^&`‚ ;à¸á×.BøygN€cÁþ� ½¡EÚ]8C» £tß囤 * Ó@ù¿©¢{@èí~ܜ҄+àP³à,„Ÿ¶f�4áØö€]`‡ñoj †é/ønOc´l3�–ÏÒp¹ AÐz:dô@ÀX@пò=Êô,KЋ AoÓHàßOðØžÎÔ]s Θ dØ v² ‚ÚŒYj m¦5�û_ý@“_%hâ+#>@Ÿ4a$±üâ>Pj$Òa, ¹¤³ –,´DA‹‘ VãZ“xøõºÿu’¦ |ŒƒQ€,ÀP€èîj: p”×iȪ< c) Ø€6Ð š�$5†6�«ñàë °�Ίcà‘n w——øŽ›ù0Áš )¸@õF…+ÄÆXôý¯�bÀ#¤æ›L÷…[y†¡u>` ³ (¨4è1ç&HˆÇ§ )xJ!à‹ñ”Î\˜À˜Y@½>îŒ Ãe1ÌôHs™î`ª©˜ã½x“ñ,Ì Ai€Ì‚zP‡LXU ”Uðò6 FÀ(ÄO•š)ï/žf`‚Ô&C9 @¨ÕRœzK6`I[à¥Í4a¢DÐ;žèüÉ«4ì`B“j&Æí W‡¬ó]eI^Ïûäú>Œ¿¶¹ó~Ë„6]P ªõ$Õ~É>¡£,Á;¹nÀ&€i�ù÷à�äý Ï�e-Ï‚ &è³ânÙfÞ¾vÈý|7xTàñ,úˆK^ÉǬ¯»—c`fLÐ6zÉ»]:ËÓ„Ã_â` œ—Œ�„� § Ôa h›>ûñ¥ñFq˜�®·�iÂËL‰»€!ÙmLxšŽ¹@ J Ûˆÿ:gö±d¨æ?ñ;€NbãÆ� éü˜½ ó¤2 ªôSÕJõ„K(f\•çõà6x“î/ñ;�÷î]fÉ„�6&$Ò5ëÊ0?jÑ (™áJ�iÂD�¯Åÿúë¯Ìg™p3W™9 fhYÄPo»P¼ÈµºËÒÜ}?‹—ñy&¼DÔ«ó€¾H‘¼oŠ#t^*/‚c]ˆ·5a¯”}‘i€9 ‚2EÉ\œüIš0‘·NÄÛ˜`È%rZÉ‚ö Îúâ@íÙy˜ LP¯sfgæ¼øÔ xž®L„< æ©ÚéTñV.GŸgÁué�™â퇳7 L„¼l%:Ž.Ì Y“? ú Ü™y Þf%`.ç.7 ¬7Jϧ¹oš0’ëÞÎÌKñò| ¬ xusâ˜4a2ÇÝ�™Gâ™þ,Åž»‰…âÜ|`À£,wwf‰g&3-K¡'4æè�ìÀÓîï̼/GªÈ‚9BËégî½”&¼Mswgæ‘xæMš%ôŸ ý¶Ÿ¹åÄ&Øð¼c`W ;3óBŸ3žˆ·7`wh&6a?ˆg¬|÷§Áy`¤°33'ÆNÉÄÛ�]¡8Là¦#ñ s5Ø_¸Fp\¢y ÞvÜits£q—4a4{ÕCÀV@>�Ä›Ë`µuŒ†Nski'€ Ô›ëêší™xs#TmÝE�&j“]Öàv¾«ÏÄ«[ámêV¸ÅØÊ�Å(hÇ4×¶®¿ýöã…xfO@ †øHØlÌS‹bB<Ý5<ÿ̺Èã0Gcð÷ÖagCWŽó‡/Å[Ç¿ZáhF`·“¸»z"àü1ÖCñÌ¡u´”Ä"(ŠÖa„`#ZÀ«4çMðH<EkÕô·E9jƒ3\95M¸�ë|AÃñÌ�Üåé沸%j´(ÕÊ6R=hѹ©à¨ ˆg š:ûÛ4FÐ.‹þÍ•SiÚJÞÕò�â|žz÷í[chò k@­Ì„'éÞtsœ×Îc_¹û¬Ñ6Ð:ærD¾©/Xû~žSÏ·Þ}³=n›Ù©mÀ4âvÎÚvt�×l/XÛgE8˜ å›¶I†3Ýïé;ňåØËâ¡é³£ÒøKEr™ õš Çé.ÿªÃp�<ë+âA’5}QTh¨R[€bɈh€7Îÿ®ÇÁ Ïö€)Þ4`�ª %¥ø‡V#UÓ˜—�øe—ówÞ*žSV×bôO Qƒ¿‡üó›AÌOÖ´—âWûSY¹4"Æ�P�àl¸™ã½ø[<Û[î:;õciÓd›`5âP~ C¡MÎÑ|U¸ÏÄœo™À‚Õ ƒ3¹‚¦Ó\ÎßËUÓ]½ë T‰kt5°:@|’ °Qt)ù"Í�þ>»V“¢UáL’¯mMk*„'€°ÁT‚=HÏÛÙ\ŠZUë>c�2¾Â&Z‚¯i­b7Cø,¶fT€(œÏÁ„1=Y‚Æ3x Óë4ðo~¬7‹M£ Hñ±˜ fù< L6¸ˆ0Dÿ{3$ •+¡+hVÌ÷þ›¿“':¿Îظ¨(V‹ˆ^4ÅÛ›bcŒ xÍ"˜Qþ._ÇßPoûG ‚‹}±ó@¤È<>‹?s]jïÈŠ­ô½ ‚NCL7DÅñï9ð³dŽð?ǯÁkñ~¯Ëñ'mˆ#×ܸT×IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/centralmods.png0000644000175100017510000000702715224505336025072 0ustar runnerrunner‰PNG  IHDR@@ªiqÞ ÞIDATxÚí—p;׆Ik;v I.333333333333333c’6T‡ ñ®ô{kͧÖqÿ›ä¦dî;óŒvÏ{öˆ<þÑ?úGÿ¨H'=Ö¼Ó�o67Þòv³é&Á•¯4¿³ÿ]ÍóÑšúËfÓSòÏMÈ'aþ×uÓ›Mæ‰Ï{Çõo4ýï'áú×Í#Ÿôžk^kš°ß�ÿÃI¸öµFóàG ‹›Þ ñ_«�þéO6]EwuÕ+ æÞêûW¿Ú�£„$%è=¥ñ¬'g?5Eç1ý =³ÇÐóÉ�oaÛ«ÿ¨Ýõ¦q³°×­ãô^·Ž7ý‹qºØçö×üY+¥T-JJfFŠþI±O„l‘JÊ”’Š™‘3ZÙŸ°žŠ‘)™?hWJá°I˜ƒ(% ñ”ȃE`ÏÏ™o†tõÙ.•’­]³¶bŽàIˆ(M"’¡¦,ÉÐDÃò ,›NE$ETxоþ®õTDÞ{ØÒžRÌŒ’rŽýù¨§©Œ¦ž˜ÆB•°þà�Y½æcð;£*¦P]ÖJ™çã)ÑÇ )á3#¥TI%sITÌ�HÔ±HÅ�lª/`íEjXÐw¬aîd±?2OùjbI›ìßRs„лô”Ìtí¢Ï±eÏàx=óTŒfùÌÝTéZTõЍ�k0<õ‹è7Y ò7F–O¢*ÖJؔ껊}zJ¦e^­]‡¾ÅS‚ð�ÖĦ32>‰rïQ�úŽÄJ·¢ª%ºÀþDçÝ—��ï2Òû�¹ãR6�ŠHšˆ4}VÅ>¥”mRIÑÞuÀèˬCTùTDÛœ75,:žáͯ™{W¢soV‰¯BƆRÝðaÞ|-ÃâS¨Ž5Sæuâ)Ó7±teFRJ‚d¡¤ÁSq/Mu´‘Á±:F&ß@å+^8 ¯‚ò®$Ò2†ªôŸ ŠMe`¬žŠHé‡ïêƒxŠ)t�¤R‚Yè ó¢�€¨ÊåM´šdÆRÕü9‰¥OCÆGP¬Ø¼Û¶>åµS¡’„ÏTEÃ*ÈàI�Ï|±O)ER*)³%*@ØãÞ£ �ð:¨Ž61 ÚÈÐi/#s‘Xâ(f§ÊU®D¦›¨lþžÊÈtÂgÃ*ˆz9”Ò½‹¥´'; ™ìÒWÄß<©‰©lÞDKÞ|ƒR?OþLåJ!¤&(‰W³8ñEö'QûqÓBU¤9O’¸J±UÐÛ¸¤,F¶HOÑ¡$¢/ÇWúm„æ+UC&?CtØÄçß Lîÿ¥j¥3�(ª§¼MÂk'¬„ò° TX¦¯ÆáI:¤§D»72‚½GIMhXú•Þô|bÖ&6dYdl ‚;6BÛsak1Œ‰0,àŽ‚ SOvÚ÷dë S÷ éÚïÒ)RÑ7äÆ ;-£+âyÓ„@hmÔ – ׿›ýSJQn :sþPJ(FšÍs7ÂØ©^l*ؔؠe„ÀÌ0 HÛ cÑgÜb0h <”¸$—„.P8¯ƒUŽLý´�}ƒ¶1_Ò0d>ª>‘bE#^½6©c×�-$=de²»<€ŒDÿ"n·àó”H¹¸.P¸I@w`ÄÒ¨ƒŸ qLŠ×/º�\f)‡NcBLÊ’†ÒX“öxÆ3�"“*˜ ²u4ÿp/�xAbA8ðId¬ü/ã•nÄ“´Ì2º]’°E÷`¡µˆv?µ?4ðám×c‚&Ðí`3è ˜�öYá_çܳ&… üFhÿjo&3éQ^¾ä%rf(±ãžET é^¼ÂMƒ¡gp�ü×H)Ì _=A.»5U{žË˜÷gì—Ï[“¡‰6K+˜VDÚBŠÎµ#„ <�?RAãuÐü¤>Ò|ùÊOdRPyòÃÈÁóu3NëÍú =xv%˜öµ  !‚kéÿ:S¾ø˜W]L,�¶ó»òHœ„@§­†V’rì"Kæñ@(��/;hÓLÕ.gâÏ¿ô$ÔBj)x6i° P’¶t2¾Q~`è¹$•«íGôÓƒÑ�ß"Í`P¤oÉZ‘±ÛÞ˜ÆB0€Ûô $H({�a+Év´08ã–Û›ÎÆé£Œ€r�vWB·esBhcD*ë+­ ½Q$:¨°NÉŒÓ*gñ2À¡A—c*¸ó„Fû>ÂÚd5º3 '’R+#¤B/®í:Ää‰cRÙœmKec鬗Êdé ~GHO¸Ÿ4yta�K¿€r¸DÙë6Iˆ “èÀt4ô8¾Ð[è±3ïµvâØ¶#rè¶«Ö¦§ýù}L±ò¨1IoHøId „ðÀmƒ2�5æŒWˆïŒKí°ïóÊ<P´ô8¾Ð[ÞcY¦~ô‡n·ú°] £­¥íØ­—< X Äaé¶.Z[m´ÈÚrmÀ•w—D˜ (ÀgÜ% ‚ˆÀÛg¬ýÈ#?¿Ó=Kh~·6@;ðð*ðð<ðBOYt€h�V+Ðʾ›Â™MÌÜ\ÉÏú÷µÅI”H¬=RŒïa|Ï[^µ^ÛãQ�R@–«Ú#£lðÅ2ΔÔ¿`a Rƒv÷u‰$ðˆÕHªD?Ò3GA^Éz)˜±Êha§­p.DQU¸î€‘tQ¦h6Њ0Éhð7%é#}»èÀ ŽVD¬a‘EmLŸðñ-�<¹ŸÏ3¾ºOÓÑT´0(zVK¢U �ÓCþkðGœF ŒÄmtã@ †ºÑ¼xN3îÔÆwwçru_ë&ªÿøôª@ß³eš·.o§yr' J¿Ç(Âo耚ÿªÄBk„�­U¡•9¦4ðÎ5“xã”é´ü¶à|⸥W”s�X†e^€E—^I,6ïpqá´÷‚©/ÕÂG7O#9- RÛ%±²ma»ª}#øoÒ³kུ�LO{u”1_oaÌ�ë›`ôÒfêƒÍg')óÂF˜ç6¼º•Üöµ­PÌFol'ã/o)|vCñÓ‹c¾¦$è#à�m‡}=jƒø è¤ùÏfê>Í’®'+¤xZ•ɦ·ù_ìù†nèåõ�2®6õ3æhcÌ&‰¨kĺÂ<4ü&ûȘ[·zÛ?â¿&oî0lspwçô†¸Ÿjù^zòé‰'7-WG/e΃W?�,¨³zg˜mÊGU-Y9¹õ§†7{-3šÿ6=ºò™5QÌ�¼´âÝùøG}¥ÿ§ÁzÆòºÂèIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/cat.png0000644000175100017510000000562315224505336023326 0ustar runnerrunner‰PNG  IHDR@@ªiqÞ ZIDATxÔ˜´;WÆçn6¶ólÛ¶ýþ¶mÕ¶mÕ:j�jÛ¶Ýg{:ÙÓÔŠ³¿œ/Ö·wfvî€HàcÅà!*¹ZI7ç’ÖƒHQ“Þ‰²Å€›Ôå´3/¿O«Ô_k7FăaŒEÒÍ‚Iky¥·b�þ'kêw©"­±÷Öd·æ‚ˆI&Í“0%2ëNד›Z2Røú¦ýùë÷”®oÚ—·±ù@$«ê¶ÄÚ“^´Ã_‘“â:N•¤Ö\³©åÀòî²ÕO–g4ŒdÄæcBxƇ¥,¤De�&W¾cÑ;F\ïÏK,»ÄŒ”—ÅÐÍ´Ë/á)2‘L"ÇI„çþMMù½ÛAÌ0Ƭt3DBOÔQºbˆ©ÏÓP‘Ùt7üˆ€HK¬€éÁ•v€ö�Éа:˜þ€‚ÞÕ³;J—EhAà»þ¯J7µRƒX©ÌjÞðû]j5"Žà$~Œ5Ë£ÿ°Ú[…×&qlác,_'‚¦né¯ 1Ÿ.yú²(¥Ð—wK ·ÞkÆ?º^_~ïruΫ`›.w¤œèz‡ìúL»!'£)“vÆ‹¨Bõø°›´·�ý{]‚]î~rµ>§Ÿ^‡R2��1ð(rÒLÙìÖØ…gC$—îQ€÷:©ÛïzsÙçŽ×¥¸ÐùÄÒ›Æ4UÃóC€ªª7ïuöÝî«ÕæÛ¼ JHØ<”cé9(<>JJë÷^€íãí~ÃÛÀ69w€‰6ÄóQäR}Þ»ºý~�£f¡‹ËÛãÔ%Þf(�”Ó`)å¹5 ;#b ׂÀè] JÝ&ìÒF©$Þ&~´sö�Ä3‘ÅeH:ÖŸR×SbŒDo«aqI[/™V ])™wA)e˜üüPlBàœ~þ«Ÿ¶{fãCn´‡L›u zÓB ñŒ¶š³hÆŒ'ûRš’ÓµywŒy6Â.C¶(ø$�ei&sF§ÌêTÐë tBÅlT`ÒQæ…r¸Oû»Q��’ð0‘œ÷cà ÐÇFG¦,:ò´0j�˜Æ9Ä×-€M옎2ûI€y„cãOÒns§®>I`Cëð? /ÀŸ8h!ÒdE3Z0{´ôJL Ô´u¡´‘03, gÀ7»íz3M?à@a(€’Ó‘ÐÿßiøÄøX„y ›ì5Kb|åZ ®À­)sQŽ�ODøf³ºjGªÎùHˆ6QŸþÇø¡/�Ï§à„±(Mä FH/UÀ!耢¹‚ÒB8è]N…>Ù§ÝYÙ§{!VT}Žš ï …Òðw]òÒäOÀŸ}êÌp š›2éÈu¤„!^ª€ŒèE¡&à1øX�“¼Ùvãª5@±™�gôçø¼‚BØpptúéÙé×ÿÝ4íI¡/3`Ðb•!"ñe Sà†Z,dˆFP £n¿š@)™Ëû猛 ‹Ù!A�·M$ À6’˜¶3n_ý�ßÿé7ã¿no�|ôùÿoT5£!„zG‚‰ª—Ÿ€-få²q¯²p'´7Þƒ‡¬’/F±éºœè/�PW#Î?¦!†ÈÛ "Ïhs¥Êváüê7¦‡Œë1]N>¿º]n^þ­mGï„“Q†’r"9°'_îd(ÐQ±w¢Ê¦$¡ è>øRph vq:mI¤Ï ]Nܽº9\Ù9šŸÒße›6Ãù}¿7†’rÉO9×Ò�¯b¬¨B=\ýWcUuϪºŸ´{Ü<ÿ ç«»€‰„î�Ù6wîÜ�Ä›]€®…7†½é:p‰d'*ž†Nñb`63i!tœŸ ÂG‚%°šìèN/Xÿw¡ÔÀãÅ8Ï{o "*`»``�BÓ€]È¥#u©wÇõöŠn»]EÂÚà>WsÚßw ·�›À_„þZðm.ëû\í3UÌ¿¡)â)„%óq‚©à†ðÂè¸÷ 8/„nsÁ8S×l¿½eÿ‰AÿÚdì­†RBè BˆGØ,J)ØCR<ˆ @Cõ÷%Ó?teX î®�/„îßÜî2|.® ëÝmR OA<-‰Í˜Å¨�'‚ °p/°€¹ñ�`j£DþxCúŸ=ÝiG^W¼¤HXEâuM<Qÿ©!þݰ÷²„+à¸]jxßW¦^V»s6©Cð+  ÆuK]›8‚21Ì„¦à©a*èSp/C "4ÆãL9ì]Ê´àq嘆W (lx¨»>{µá¾ Oíž,`m€^¯�^VB÷�W0¼ß˶#í/Øt‰’�ÂÓA/:>"Tcf1S5&÷Ò‚GB}2F@;ˆFàÖ¨T@ 0OÁC² ;Ã^}‚· Þ!w�ûŒ›‚·‘°ìͪ»Å¾ËP„×(àÉ$Ô êØjê*Q"¸vo¦T"TàèU�q ¸Äò¸Tx‚Àñ#d€�"ȹ78 ïEH…Ò�RD�¡«©tyË®\³Ê(†˜ ॿg3*v8€Étj ˜÷5FÚb ¨:@�y‚!ð„Ø�€Á-¢¼£sÀ®�×!C ·TØ‘÷0…ǘ—àá%u¼¬‚tIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/resourcepacks.png0000644000175100017510000001344615224505336025432 0ustar runnerrunner‰PNG  IHDR@@ªiqÞíIDATxÚÕšp[G£…¿½ ´ qb»3:œ2æ/3cÚŸÒ×?íÏÌÌÌ)33SRfL_˜Í É’.ì»;òÎܱ£Ä…GgæÌÞ+ã9{–×à¿3ÞËÌ·?ÇÌw~ðëÌ|÷8f½a?˜É©?ð«çÃÿ#(áF xAÀG˜õΣaŸ]˜2úâ�Ä5©@ì/fqõ£aÎäêG‚Ï¿Ðæÿf½ûy%¶4ß½å€ãÞ® „ï~æncŽuõ”àR øÖÿ‡Ú¯ jùa-¶‡žøÖçn¼å¾²ûïy¬á?â›?ûÜsK~tÙ¢÷pɳïÍ?ò¡W�,¿þɽ¤a2Ÿ >i hP©¸´Î<ª¬îªsË¿òôÓO/r@E‡Gvi¤vÛRS>˜žjX2£¬á½™N_Ø¥ä„y?pàäƒTÑÃàÐÿÛHªûzâs̬ØÀ~£bÆ“¿ã¸'l�3aÐê†x<6Ù´L#(‰Ç”•¥ˆÇâØ–�ëz46í1Úüåµ çn=ì”ËkÇJ$¦Í >Aˆä¡oó ã¬L^|À2|1金!—�ÀؤՑÌçs 0Ïóð}‰ã8är9Òé4mmm¨²P( ˜ÏçÑÏêû***äŽWÇ¿·ìõæ[�|r�½úêþ/ ̆`%R> l¡G¿;kØ+ÿéÿë²Sª†~úDÆTijÉx,F, a˜ø¾�§Ä+vwwÓÑÑA{{;ê] S™6ª~fÇ ÷4Ÿ¼D¼‹s�)@Ø ¼ ¬ù$ ˆßfÑnÒ0þ ,ø¹[uà�³ç].~úå€E‘{fŒ²ˆüú4 < (qÞ'aÀUÀ ”†wZ4úÛ/qÉØÃHé[•$âI”xUƒ:ÚŠÚUê÷ÎÎNt Â&¨R½Ï˜1ƒ–––{W¼¸už¹¢.%7Tþ¨¤4T:od°Ø?j€ãØ&%“•?9âˆEN!•,K�L$±íB¤”h4Ôgúkáw˲Pì ý}p 'Ýqã‚ ‡¤?sH^‹/�Ó€'€=gh(ù}RráСCž˜;÷ðjdJup啨Ȇ¡¨(ú” ú]›`Û6ª ט8q¢êKR—οè{{2‡³˜Àì�; ¦Ø|ŸïMš4î/3gN—…œUQ^N2Y†­(*q:þªý«R¦©ÍІ¨Ÿ7Mý5�)S¦�ÇQ_?ùä“ÏK�5FK$ý@õÇ5 ioâ>mÚ„/Ž3>“I‹òT9–e‡£­ëNO?kÃ56D7UêïQïJ4ÙleŽà¤ïNŸ'c±´\–™°øOž<á²!ƒÇvt´“H$¡…h‘ZüÞ '£¤ J¨þü¨£ŽRMCý®žáÔ`ÒÑ둬óñÙ$ðÞÇ5 +<ïVÝæÏª­2ÔÈ1-ÍMª£Óµ¤{yM-^S›ÒÛ„>Iб×ýÇÈ‘#™3gêwª$(c4Oýþì±y£+Bi<ìø$¦ÂO 6Œ&F"ÉßÏš5uõªUBS‹Ò¢ÃÃ\´ð° šáèwe@MM g�uúw¨ÚWéÐìà Ëίæá úâMàêOl-pØî�O]=n\å†Õ«ÌL6«&.áÙœÝÂì#:œ„T*ÅÙgŸ­žÃâµÅä×ròwfÙ9`ÉËfÜ_¬÷ñ^ùKà×@�ý@ð¥Càë_ò£g¼àœ“Ïþ÷ªÕ«0 “H :ÑÔ„·g¹ÆFÜîne@Ï$†h3aVT`ªÙ`P– J"¨U{À´)aÓ´‘Jì‰'žTüû˜$6|§üTn~dû•O­þ�çCÂBözœÔ"ñMߨ\ÿNÍ3gú÷V¿ÿòñ'pÞz‹ìŽ4N ºûø\Ú6#FPŒëÕãÆ1`ìXŒ²2”°êêjŽ8âݨt^FûÌ ™ÅvÖ!wý�K.~è3=2ï& \�üó½ÏˆAŽ“`‚Ø, ¯o¢‘ÀägÀxzaNçœÚ¿oþÂ�M_ù ^:ÝO¡ý7Ç‚šÀŒ ùK>XÅ[ƒZ|Ú™—0o ß¶”üÀ+X´tè£�Ü}ËÅzÄúÞ³œlb̬¾#ƒ|¸À@ÃdþÞÄ#aÔž¡Õéo+oC¿h©²ŸLÆbœså•L˜5‹²2=™ª)”è¸òÄ�¬ÄHìä즘{äÌS»}kîÜØqù»§Î21®(1Õ Î x2h ª€cØ|(ß�‰;--(ýÚsfÎ�Ë/fú™gR__¯úUëjÖ§LØ+Ë¢YÌÄPŒÄpÌÀ+’Änü·ñõo~ëÇ‹W½Q1¨#y Ø'äÅai‡Æf©©D››õ¥þ�!4¨=šãüc†í] wª“ëêêR}¦ÉaÄín"‰!H#‡ét �v|Ŷ§˜8òÂÙÓf|¥¿NŽ4ØD¹€q ‚¥`ÂæútG»öJ›ðS0¤¡�ó®½–K{ŒÑÇˈ 3Ôó‚ÊÊJTû×M@—ÑN,‰°k0bõñ¡X‰acïþ+]uÙ7["é(ýC¥6jǾªkùÐU;÷LV Œ~Æ=bÛL8ã N¾õVN¹÷^F�t‚N¯¼¼µ Jµqç5µ –¿›˜Ø ~w‘F©Á L0ãC°’C± «(«X^ûôБyÁþѪ Xl¥rr¹¥Gˆ]†Z�E£„a”HA$aH0‡?ø÷¿ç˜çž£áW¿bôqÇ1~üxTÌÕÆ‡êäÔs¸–Ã&èÒʯ&š_^éef¸3‰ˆ ˆÖõ˜0ŒHã¿våÔ-UÝÉ}­%4\gp=ð. ÿ,9s2áé)[–YAMņ uàN ¤zÆ êƒikíWP÷Ç?2õ…õ·¿Q}úéÌ<ì0%\ V{ªƒSqï³ùÑÇ!ñ[ž€ŽEøžž7`ðŒŸa ¬rŒhÑ+>ÛÈóŸ°+¿<~l–¼QZ?7^øV?vÓ Vž•Ó·&ÿ£+iŒTV¢S`êS$ï»��‹aýë_Äô#F}÷» i³=µ�QWW§b®fyª¶û/e€_h&½êû8��ã;i¤¢«LÐìé"Œª(š«CõÑÖ;¨?H}Ç(¿²o p$\¼ }ÇÉÀ€iÀ0ÀÖô4ºË£ïØUU§5C›2…±ÿþ7©@”Š; £¬×êsÕ¾õçý„¤}óƒlzû ¨°HUÖâÛeønáÆ Tb(Ñ‚qÂL`Ø•˜� ¾2!×L´óV#òóOEŸúìƒÎ�ͪ'‹a{æFãùp%‹ëÇ�áCàï3'7|i÷ÓúÇ?Ä[M\õ&ÆÇ�×½�Žÿüí[Ÿ¢%[M¬¬†šAµDS�¨@˜« DĈT£ « „ ~•?· 7½§s%ùŽ5´×ýK~û»›w¢œp K´îþ£Êb\yYʼn]ëÖÉä7¿)¢É¤^›‡öò>øy]° Z§ Ðü0&Héâ7ÞG~Å|œ¦§‘~‰Ä.yÇ¢àÙÄ£¦) ïm }”RŠø9Dvѳ­ßœMŸÞ¾n©ÖQr? ~ËöÑ©�[þ"àï¿ð‡ÀMÞ‚¶/›ŸO'ºËsY<:þö·¿ý&èØ¤jëšjB#¥ìŸð¦aù)ø›~ˆpš1 “0-Ó#ÍPȧIgÒx…N|§ _ÓM£;C%X ‘ÂDC)„=ÕdÊÚ¯fþe—þ`ìûkîÿÜKO]ÿ/Þ}ö‚E×™} hع{œ„ßMLÃâXç°®Ÿw p ž:Æ‚öºuϽöÚkO‡×ñŠjêº_xY¼­¿Cæ·b˜&ajÌ€q«›¨‘¡³+C®[ ï,ÒíBºáQ!‡ô�€ˆ �¾QŽgT‘M$âoG/½tTê,ßÿŒ?½ò™ÙhNjiÀWÄ(…ˆg_˜?ÆDvAnîÀ�­×.\øÛl6›×âõ®Ð~RP»‡~ß÷0„Ñ#Ü ¨�(>[†$ÉàÒtu¥qó�x:!|7‹¯b/ýb³0bÅI’]‰kTSÖyò‚3†m©©II@À4àâp&CÙ"C™ ]@*&2™%=ôÐmz[,´!º_ŒAç@¢‰,ŠW}H¸˜EƬq;�:3ÌfÒèè¦à9i �N„i@ÒÎ ¼4�]iœœJA‘®2ÀU¦ ð½<àTgiÆ1TÚì*âéÇñ�œ\»aìØjâôª�þÀ«ªŠJHQ„šém¼ùæ›ÿ¥Sðaš‚Q6ž‹/}]ë!è4DÌÉHšl¶‹t:­šBAS5�‚¾ç€”€ FÌ®@˜1’é{ŶyóÆ·€àƒ^ ín¤ºš(Ä�îŋ߰}ûöõÚÍþ4sè7�F¡÷íLƒ„�Å&Cggš\w'n¾(Þs{Ràeöô6˜±€I°*p¬‘x“'Wl¬««ü¼Ûµ%,c?°í׬Р,Àرcw-\¸ð÷úÈ[‰ï蘒†+1ø«ª�öˆ¶t_ÐgXLØJ|:0¡K 0�çdfU Š}0Š£‚ˆã›5tEŽBh2¤(ö;AÂÄþc”QzX �"( uˆN!MG�'0À-F„SàSàzÊx‰”‚,Ãè�Ñ'S¿mkû+°KF{O. xeÀË�Ï�2Ù‹¨¥° †íû•@~ öµ_|ñAuŠÛ»ST|_0ÊgÀS÷:, ¡i±âV5"d2J|¸(fq�î€êoº82N£8Züž={vålûÍý�9ÀöÞ7+¢iÛ¦‰BBˆ2z!Øßë¸ñÆÿ�   ›C‡FsØwð‰…‡Åá° gˆLFºÁËÐÞ™¡�ë § ({Lp=ö¸‡Rpm”xÅW_}õÑTEåî�tOÐñ¥ë». Dè lx¬¼ÿþû¯W)PÔ&ôgTP{|¢þ*Ââ0z¨šFOlS¢šB&Sœ!ª~@à(ñùL‘�áÍþ�d�$ú¨~×®];—,y!�ûHä¡MŸøR–ªÎÌ£�>ú¯ÆÆÆ]ÚmD¿šBݧ‘Ñèa¡ão€¡Ì°P†$"–T)(vˆN^uŠE§@gäSd½Z-^Q>øÐƒ ‡¾ò#ß­·¬Nm@ ¤”ª)l fˆQ¢ƒÖ@/#öÕŠkþá?B‹J,¡(ªgË2HFS»³ttfP”®G®âlrÔ>|}þùçïìè¸ p>²N.×í´·£�W”Fþ­·Þº}Ó¦M˔轙>ï £â0¨:‰ u‚V@mD‘ñ¨MжÀ€t¦ �²þ+Œa(ñšË—/[úì¢g¨·ÿ?²í®»¹mÛ¶L'4 !vSjrÔtÓM7©Õ¢Ô&„¨ú…’w†ÅÐïâé©u�Ý$ôn�J�‡›Ï’3Ça�ù-®Qƒ®yU® pË-·|kjÃÔeŸÄeéôªmÛŽØ‘LÎ2ìn0ä<³dÉ’ÅÚ€^Ôc*c”Ð犱$(%E3"¶Iý”Ë©™ý ~%\sÅŠå+o½íÖ+ÇŽ·¸Ôž Å‡Çú‰aƵÞ{ソ�0aÂÑ€Mº¦õ¥'½•¦J Y5«éadaWO­û=âý¢‘A$&þÊf¡Eëç­·ßz'øÛßž0~üË€O ü÷Bx7˜Ý©k]­ëÃTé­ ‹Ïšä}5,*tÍKa#j/Æšú ^|zøÒµú�Î�wÝyW€ù�ø—´øÿ-:Ÿxâ‰?'Bí!áê$XQ=ëµÃ^ïe¬9¸ñ9x>HŒ@ð4Ü‘×àÖ~‰\Á@O¸”‘Û¶mkúÃ~ÿë•+W~Xªcÿ¿m**Ö&,T Ðâu©N�T©'LaêýÆÎòËÉYèôsºjG·®…ëKÖÎc�=ú̯~ý«Ï¿vÑOˆ—õ+ö³­£c´ŒÇëã±ØøTyy¥kÑ-��©¨ï'»}?3¢¶¶‹B!ß\ãv}½L§w”G"zGïÒoqåW¾òbmmípu¬©Î uiÛvïöÓç �bèª\¶|Ùê{î¹ç&§P¸ØÆ‡„ø{*E­­=bÔܹ'Æ8à°º!C .-E @H)€”„){J¿H麮ؾm[ggkë«î»ï…–ÖÖ‡�5o¥Röž�Õ_žwɼ?¤R)¡E÷fø‚µ¦¯©„/_±|u�ª‡6nØø@MmÍRÀøð€‚Õ WDáKã++ÇMN}Ô²7,PQ† ðýð{ŸïÍïÚÅ–n Õóü5ðÜšdò[O9Nó §ŸöðìÙsf‡D«‘@Q=ësÆ>(¦3éì’÷—,åÕWž]¿~ýC–i->Äo¡ÆƒÇ¦Â€‹Q6y2ñaÃC–ÕW¤~VІtÚÚÈnÚDfÍÔg ØÙoÁ±¹X,~ì±ÇþjöìÙ³‚3E;l€º¤J}OX±+@0—Ù¾lÙÒUo¼ñæ³-­-K³™ìÀBøŠ<ÀQ¿K]–4àpËáp™ 4Ô½µ¦Ra(ªZ2Œ¢ Žƒ§¦¶]]ñêÜ€¾ :Á]·ÀjÃ0ªê¨;*ØD9bØÐ¡#*+«¢‘ˆ-"‘¨ézn¶£½£»­­µ-¾6˜N¯Éf»w�ÝN)e�½èiÑ!º½èô*õÏ ¾ ÿ8®2A†a ’`••)ñEÔÍÏde„è9 •ž§vBð²YÔB70ÂíèÀ èe2t9ŽüÖÝÿÜ o0z j;ˆ¯BK€åK鸎Óåº^ðõ÷÷*5¼^B5 ºT,õ™øDòð«9°`$ ÓÄ®¬D]iU»ÀªÔTMÂR—™cð}ÜÎNÜövTô3MM´îÞ-wïÚå,kn^ûA:ýÞ®»¨V~XHÉçpÙ—aHÅpí—¨q§„1о˜µ6œiÁq£„˜Z‹ M¤RQ’IÜxÜsc1ǵm'ošù6ÏëlqœÎ‚ã‚á§ÐšN7ïìêÚÞšÍnorÝ5í°Á‡N@†ha%H ¡²7K´{mD¨ÔìÛˆù¦ }a:–Ù.D™gY^e<î,+“Aìeðî„p RúÏ“yÇ!§Î s9±#�AäEÁ÷… Âúˆ,]jÈReÿÙüú�TÏÇF^4IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/jarmods.png0000644000175100017510000000704215224505336024213 0ustar runnerrunner‰PNG  IHDR@@ªiqÞ éIDATxÕZpÜÈÁ’9à8ŒÇÌŒŸ™™™™™™™�™™™ù.̉c{cZü÷tê-]W6.¹üm_Wu v4š÷¦g´–™"Y{Øaž}®å8‡†¾¿ÎgB-hZ2¶½ÑÁ£8zɲ{îyØL°X“ü�CûN?�€&ÒlÖ€#BŒ©ÕŒž1�¡Ÿ]vß}·š ×L¢¬ÙÿN΃žb<ϸ šüQG™ì>ûwî\cår ðüÝ/�¿u«©=ñ„)ßy§ ††N´,ë†5øÉå>ø‹§“¼ce2WÃÚ'óºãMo2…“q ë‡Õª ëuj<º††Àb}3rÑE¦|#œÀuC<ó†Ož¹¹\vŽ/¼T ãÁjºòq}_¦³·XN'¸ä€õ3âòŠ$r.ý6%P®¹Ø {'î§!€ƒ´Px8ì�.ÇÙ±XWPÀ'­ž¼ÇkíþIðÐAœxz£%tã1yO\ç�UÈè™`×yDIžSïTE‘Å6i °¼]®-Çáv×MÁkpr®ÀkrƯgíQ2U*I•¬øñvá’óq2ñ$_¨ãy¬s•¹›Y°ét«óJ²=+O&ìÚ#�<O�&(¦ ëà\®3z�ã�€dñÓtJÒ€“íy_Úêø×¡¥Ûè„§ï[ííÆ�1Ãx6°êl”Ð~œFÓ„À>Ù,k»Óqæ±;±Ú-x@Ž:‰éûº�¯CM{“³ÓÙIÐ «Gá꺆-©”~ßß5§Í›g¨ÕeÛtnh°r²N²{g±tÈÊJ<€²;ðÉù�Ò4Dt^“•ì×Îç£-3® jkÖðž”Ê�u ßHÜUð›ž{‡ï·ÏuÝOáò¥­HzXf>·ùW1.Þ#›=@âŸ,{[¶h7o  IÉÚ|òºYˆÅ9®1ê«W7À2L“ê a�¾6k–sC©´ð=]]Îpœùcí•Í.ðç[(\¼_6{8§@wñâf–Ô®Û$A©YBUrMÁ'î4“[|íZ¹ßTÄD¼$ïèì´/mûÊìÙçÌøìät}àÐÑ‹/6C—\b°©ñB–ÛR…£�æ‹È1,¯¦CeùÝ_'îÑÍ™ƒ‚�ãmÛõT*\”qã„¡@b4à†ÚŽÈåXã»'´´||žãÖ†Îg|üã&³lYônc3Þ�ÓN3UÄ\þ˜cLîàƒénÚ-ÇLfzvÐS]32¸ô%(æîpy+Bð5„"7„­ÉƒzÉ ),ظ¯=9 Dƒ®Ê,€ûÁjµýûÝÝ¡{pfÏ= °±:Ë,Ybfå+Ñ9½Âß¾=z±^™i�Úzº�>&"ã˜ñÍkŸKîþþ)(>"Hò³úŽ ªXÖlÞ™ÁoôV7¶zÏVð̪ZíVÞâ¹Û‚ð°ë?àg´ÀmòGÁNà)_ u¼÷šRéb.W e¡ £É¬ª×ûâ„Ɖ¿F’JaXç=— 6^êº ÿü©jò¦ (M‹¹–EÀÉÚŸ^$_€ø›´± ©$hýÕúzý~©„‡¼««IîCCq.6r~¦c/MÁS’ûp<2óÊ ±æå´à©´¾k½ûÎ>,ˉê­ÿÐ( ¹ä´Â�Sô"F’¡d~Ù ¦JÛq‰7†ÈîÁSGðf3@(ÖРÇú2d7hоÃUîºKúšƒ©ó™<©í–,×SŠÄ1+Ü&#©é-3eâ.vÝY˜çû³ ¤–Âp.4¤âá�cÚ˜�év¢ ˜H¯¢õsP f±Ãî,—W/t]®bÍ!ù¼ÅsNóU¼Ubø³bQàT€êhSG>¿˜±ÌiI%7%ã%fâ eÙN9µPxÙµ¥Òyóã-¿¢ïsZÉ|×��<×ßëy羪½ýnxºwîÈH˜ þÓ¢V{ðÀ\î8õõëå£ÇÄy x.ŒÚúúhñ–Ë�€/W|¼£ã›GäóoÜÔ™âÃ?,_ �¹î \Î{ Z eŠp¡íGæóÏýnw÷g¢Ó™sæ4V\"ºæ·‡¿O9ôX.Ø6â¸Ý÷Qßc)¿�žA,�س <60ÀZá¾÷oß~òá¹Üè]Õª/E ´çïsç^¼$“Ùnc²óçG+)çNKe*ÈÏ�MS-òÁ•ÆØºu+W„ðnçšÆÆ²žk—-=dP+˜¯÷÷?§ÏónÀ~`myÁM”ˆ¥Ÿ‹ßÿvw÷·yž3ËË–À \�õ#A¢~ŽÏ"–®\sO±Z[#a�b ŒÆk›¡ gî 2©3h8ü©â9ÿ©Voëvœú 1O%àžjõÆ?íÜùƒwuu}lÙ…Þ^“‰÷ÑF¡U(“g`ù/ šx÷O߆{ÜžãbŽÊ"’"¹¢ÊYy1ÙxÍH!äC+ÐÁ³GFþðÛñÿ›OÎuÝE2u ‡áà¹ÃÃçÚÿæÎÎ7IN@¯Óª`¢µ©º�‚r0¦Æƒ`²<Œ9Bn¢\¬C‡yq{¥réí۶ݾ<“ÙÉd&Ë ²ë*º/jm}!ËÏäw·©“‡$¿ “Y�i±˜Q_†‚x©8“1²¦^ïÅÑI¬¢Ü‘ Ø]¸ãº\ÑMmA”BÎT�{oñ¼{q-h!á¨ÕTSMUãÍÑ@™¬òÙóRN8TŠKP.;9ÛÖPIÑWËIꢨ(²3£Äc††RZyêe¶ëÎ¥qý1þ ª#%(#’å"̲i-?ùa’èsŻݴc€å«�Œ®ËA៖å²!€›¿ã%@êS“rXÇbz€“2‰-"Ö­$ÀKI@%p"–0æFfûrü?“€©½Ý0pÇãÕ�ÅR�Zz÷à'*d�iw`úØ3(ËàZL:J�¾=½Vr@= *³@ZñâθʪMAÑ3^a4y.CÆ5 $BÁ<¶n�TW-ìDÄ©[úu ~¼øXOoD1±˜1±S$˜@+Ó³Ò>CÏ-Aoô8Ô±Õ¿þKæ/c:éV/WfòFÛòŸ¡¡ï‡cBSIÚí¡Fç¼7ýE¯xéÛ-œ§%€í³ÐÖ|êÈoÍ_çXÓQ1Ƭk“¬sÀ:ç�XçˆïÿÊOro€ÿ&b­€Ž“6ÁqIˆ[>´6âîc,­[>+\îß}ŒÔ«Ï³¢ï�К ZY×~ôwÿuÖöºÍïž9ù`ŒáCBË—Œß=V�ÿ¿à‘G>á·€/µÑZDQ@.× êVë�V .mm€ 3b̓�ïín·’£}Tüö·þµsùÖôû !†ìåÁ¸b÷nyVlÚrŒÁh�Fƒ6h£I|Fc^mÚ`ÐÉ\cVÏ[Ñê¤]á[ö£ïšÓ¡ÕCÒ®kÙʱ;è8æã>é“Ù±ýK„Q7�¶[RA!cøú¯û*^�œ?}šþ>ÅèX¥ýW Œš¼~1¨(lÿ€ ^¯@©hµ†Ö'D©�×3Q§ˆÂÕ)0:~š©™Ëÿß,¹ï“D@gÔj¼tCû)v‘Ílÿ?o=ÅÝÉý:Nzu D�u¨ox~’z¡ö^ÛN†üëå^~े×dôbš¿}!äg_Úµìû“WrËçÿëw–h‡R÷(‚ñ=j€çy¼·„aÕ1Í\¥Ënr¿ÌŠMœ“OR¥«¹Æ`Rlc$óT›*hˆ¢{¥@Ø Ùl–Fv5ºèDܽ�ªèn?ǘú4S·èv|î‡*«¯½`ú¸a#* •ÿ�6tª­k‡0??Çæ‚æºuŒsÖSLÉmÄÂAVXÎÑ\—G¸f§*»ïø“cª²'éƒ!¬ÍrȺˆ¬Ù4Ê[ÿê»øñŸÿZæf�ßa!­] ZkÌÂUŽæÆÙ–^@uQä†nÉCI_@’B§åû0&vQ=lÍ<’¾ÄqïîiG{}>fÃMœòFF†yãsÌôÔ$Æ@ÜÐ÷—Æ­�jÇ”ØÆ)ù¾ ‹},ø`Wn0^áý²§y$?BÁ “ˆ0ˆBÜáýógÙ�*ÓT$ `‹º@¥RFÖÇyØ»ÊÝ<³¡Â‡t_dG¡ÉÇoÆ+�a|| ß÷ŠF¥AXQ ÑÚB�@�ëÓ“µvŽqÚz_nêí\«‰Ë#ìå<»¼9¹½TÓÛÁrYØr¹ÄæèOzg’ó´òžfVl`Æ—+Æveæ§Ï37?GoõÃ#몹H>á!*Ј&÷6÷A)ÊPF9–º ]›xqq˜b;¶Šè�&ÒW°m››òiªÖ¶:‹ÄQ@Õ;H1&¨—éIÏS"´˜�»™`7‚·}=IžÅå"ÇŠ“gÞÁÌÜ8'O¿¬”$­$íPQ¸v„ŒZûUÐ[ž[‘2]¢JÆõpB'yCÂWU4×PuEl ½zœŒãS’ƒL†EöäË\©vÑŽ[ÖvÅgIÙLó[ø#\¾|TS'ײ= © ø¦ch¯-6[<•:ÁÞÜ"X4}HX¶’éÁo„LÄ …‡% *û8ež¢ªÒ` �„«Ýœ¦Ï?Kéo?Ÿwþâgà‰ ÿ© ¿ý'?É7|Í3|í7|gO¾Ls&Ÿ %ƒhBc!"5¡«iG¤Bt¬Ú hOYy”š}ê6G Ólñ*¸–F –mÁÚ̬µ�ëöcZþ›öC,È� $¾ì!›¸}™“ïygÏœ`ò…?Jæ a>÷üþw ñûß]äì;~šú¥åÒ»þ†ælˆ®hÐ�°IõØÈ 4ø R’„(†9?±e.�úsfÍqàsñ¹(­‰0eÒ ÒdzÉögÐÉ"_áxAYÑ,©$2ý.*Љ*ì´ W§¯'×Euî"Ì]BJAcôŸ)W|zw<Éhnª¾À�Ü v¼)¯HÊb‹ó£œyñošuνûÏ©—Ç üˆY5�T‚'¶mÇ¥Á¨€Ûå9ƒ*y0kÂZ„´6ÄJc �PàøYº³9*óÓ mÞÝa!Ô!DbíqSilÇfv¶ÄÀ@‘ ¦H§\JÃ/a;'°¤dúÚ0'f¾ƒÔæöÿX¶î{’�»ŸàoþêÇø×¿ûi¢fH܈±‹m[¸9‹”eÑ•NS®Õ(G�Ét116AàiœŒE¤C2 ‡8#‰çIM`ÉH!èïêehã&®W* t÷ÞG h-…ïIÝoríú8só%æë¤R’ù… 3s‹è8¦Z«cu9pt®®sýù?å~ù‹øÁ/=ÌoýÔ×pù]'øœù;fÁ5'óz� E‘ÆFÒ0Š(l›ǵñ”CnÁÁ]”ضÄ‘—¸E+\ °,½})-òù婱Ž) ×š½=y¶m „‹9²™ †—ü2Kýón-ùÒÔê 6objj�üÒ˜±åÜ¥&f‡¹ñ„M_PëéIIµÒ 'ç�Ò–Ô¢�¦IGQæ}²ø‹!Þ`š0VÄaRù ª *€c Ö†éÉ)¤—g®4Ú¹*ÕAA[¦gŸc ¿HDÌÏM3:>Moož\6·clÛ:°$J¶%Ù¼¹Ÿ-K6<|�'�ㇾé àCÛß%yû{|¦¯Ð‚®¼áè¡Ë¼ðü i]¡jòݶH±sG™ž®1.ß¾M¨.„LJ C0&1ffn!I·Lu.‚Z­-jµ&s %êõ:ž—&—õxèØnúûŠHÁR'W®�±Xªâׂ(bjz1™spÿV£5BJ?VãÙÇ.\=Ïoþyƒ¼“fçÐ�c{xñìŽdž’àû3ÚAùè:}§ÑÍç.MÐ4Qòö¥€¨©°„Í|ÔèXµZcܳkGõ°qCÙŒC¹\çÌù[¼óÝgI¥lòù, ?dp¨Èþ½[Ùµs…|!FÇIžjm(<„ƒ»—ÈeLMÏqâÔ$×oŒQ*U“õ__Ž£‡wpèà6¼´—TZŒ!R¶ë,3ËõÛ7Ÿ%ò=´ iÚu\Ëe §Ÿñêc•¥&…®`óÞ»þ0µÚ£hc¸Ñ�&ÂÄ!Q}ŽTf�?�Ž}Buò]."%;/…W§€ÜËLLLsâäUKuªµi׿ƒ>à;·o\!O:í%áëØ)¢¨‰”Çvéï)òGÿ|�@�©º”Gç‰E€iÆL-ø îîAø |Mu¾‹ 61Á¥ø!„tPA€´w#–½£}Œ Èõm Žçɺ¿O£"ÏÈΟAµÆ0¿Per|ŒÙù ¥R�Ý;7²oßf¶mÙH>ïàÚ.ˆ”h£±m!àåËW ê‚É+Sl$´5z6 ŒcT3BN7ðÒiUH`KÒ >*“Ë>Oµú$Ò²ÑJ%‚&aeÑ¡‹›ïIBZ„�ù³h5E—¹Àj@�Ð!îQšMÅèøóóe{t?‡î ¯·€c ²™ä)ìߊç¹ "ÕLBß±Ó Ï�|…š_G)�Ä8ÙQl’eo*r»zRKf»6é\Ûs‘– “Ká-™íÈ$м¥±ze3ªüùtå¹^KäÄ×2ݺ¿ÛxWöWï%†”c'×XÁ|ý‡ýóÊ`üEŸ<¨×“}òGŸ¶ßQëÛ�סm—$ðkÀǯC{ð�“¬sð@€uÎXçüƉ6Ï#V0½IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/patreon.png0000644000175100017510000000142115224505336024217 0ustar runnerrunner‰PNG  IHDR@@ªiqÞØIDATxÚ훌eK†GÑ z¶mK§k¤ª3϶?[Ö¶­mÛ¶®qÿíu‡Ã¾§nÒ•|ñE}me…"Ä™€á02ºVˆlòߎ¢É?0Ò$Cadˆ�âÖ ùQu ‹‹ü YcA­<%•Mò{YYÙ(®)I7ï�áT~?e9h âwpçgŽ7q’W,Kœ¤Ênù QT˜ðdÕ02ÍI¼©Ø‚b~^µn9xÁJµtqš…Èz!†Û9¥î#³PöÌ…É p“'î ¤#ËU]œ¼âÖ ABÌAYYAb�ÓS$†‘>‰€‰oÉ> &…(~+<$PÂn?s^ÚÀTÇNÕ×Â?é@2ÓeìÅ•xSó�3ÞëÂÄ_ù@Ü$Ð /LƒÙ V€á¯¾Ö›PåuNõW ¿ãO€‘AÕ˜¸…ÇÀ?€Êé³´Ô_d‘Ç Ý-Ð ¯÷Yzê ^´�~K} øúû€q>û€7,PN{쎯³ ÔIÄŸù�•• ’]ª§Â¶�|¯ºY ”u'eü 0Õåj×Åü—ÿÕ`VVHVêÀGð¤\“ž¡HÞSX ºùßs�º‰*J~¢§¯Nó®påc 9ª¢ç'þ&™ƒâß”þ„ÇsÉ8Ñ!œäÂÇ9LèlðβOL ùíx²úÇãQTÃÃÓ¹æw’OZ€;2È¿~‡GNÁÈxÇé½"c? ’u^†:åtxŠ/IÙ+,0òHv7Áúþ0H:£„¯Ì¼kre';È@ñD�­ßØ.+aäW?? `”bI!@ Qlãû»u¹’n)³$Øe¤Šä9%%M’*©ÄKJFÄ(i�H»®d—1·öõ¼ÛÝ>½¿¿½øÄäíû{ÀññÑAŠëy^¯j­³C„QWɘç¥ô…K©Çqr’‚é)LÓ$ÉJµb’’¦Ô}$Ðëm`úÊOûfÝ6–…áO’eHì0S“rÊLK�efffffffffæi§ÌMÚ†Évâ8&î{kõølgfq²¬sþó@I«ÿ¿÷Ýwß•‚låXB>¿pß¾Ãů}íc�››‹îf÷Žðq'\ýýýohhþì¶mwoêSCGG‹´®|X_YiEmEhyßÜ›÷úr€"[ヒã ÉË9pĤmÛ€"û®7çpáÂh ©¿Ÿ`H6wpÝ)˜¦±~Æ]í»vÝ›æævÚÚš/Qº ùp’„÷À²ïo¾ÌN“�¤MÓÆû9,Ë*ÿŽ„ìƒ‚e9¨ªK  ¾¾^©©©_4~ °˜ȇ8“J%Ü|>§ÌÎ&éîn&�ÎsàÀiŠÅ®+abFùaMË–Ä Ÿ/€‹; E¤ †‰ëJ’`Šª`[EQ0Äœ*æ$y Eµ¨ëÜÿþ¡ª*Dmm„ººFI¾,¶Ò¢§æçg‹Žc…Òé¡P€#GNðñ�¿ Çéŵ—Ë€Š@Áq¥‹ûD«¡Š9Ÿ€†$ë8:¨rNÞW‘¤mÇ®˜Ó|Ø–‚œ“¿‹£¡Z96®;ÁÞ½{�&)€ð‚Æ:O?À¢ P(”&S©ä´e˲ق´ŠŒúôõµ“œQ,Ý"= «àÓ Z@SÁ¯�O­ôe«k (²/ ª*à­%Ûŵ©<¸Ît�‰I“ùD‚žž>!@� ©VUµZDZ‹.@<Íww÷œK§“ËJ¥òù"‘H]]­ôtíäÜD´Ê„ü>IÒç‘•"HxâhŠD™´"…Às3\$érßrʰmh ‘އ‰Åæ—P(Hkk{XìHMÙl*(w´¨Ü9—+Üûl*5@*• ­­�îžj03eBÚET,�„¯‚²0žHº×t9;÷C:†fff©™Ü~[ZZüµµõí@u…çâ Ýþ´X²%“) ÐÐPÇÒeõàfAQ+–Ö%$)�ˆ$¸~IX è¿¿2¬�Bá&§‹—§ººšp¸Zéë[¶ý�¼ÿÛï}ï{?ˆÚ¢ îÉ……yá&2TWX¶¼EY›J ¨À³ø¥"xäýû~ðÆ‘¼ß•¢„�.1|á ¿ÿý¯˜œ<Î3žñÄÍ/zÑËÚÙÙõ¹ÞÞ%êeQ(•Œ ÉäüœëÚBi ™�­^ÝG(”Ó­�®x‚Í#sþ üçt•X‚ªÑÑ1M]8Æé3Çèéiã™Ï|÷¼çƒhl\ÊÆ wmìë[ú>]××Õ‹"@*µ�. çs¹ Å¢!1Qèêj££]‡Roí{¨ˆQ „>� $\AeN´hÂË8Æ›Zä“}O{úóÙ¸q‰D‰±±33sLNO‰q¢(ÜsP³(Äã3¶pÿ!Ùl€–æzûª¡äBU‘¸}!|>I¬ÒVWúÞ} I^u³Ü§}šg?û¹tv-%™Ì ¤Ð´òáÌÙÓ>tmæìÙ³G]×-Ö¢॰CÙlÆËðŠÔÕÕÓÝ] V�¸vQÙW¼±Z‚úí÷ý^?oS5y�Í+:Ѓaòù’ÐI# �ÏsöìA††Îš¦9 ©E@(<�ɤ䖈|€@ÀO__¨Yˆ ”,Ï*Ë¡,Ä%èé`B² øRyîÍù9ªòQZ[‘ç™VË ´ªÊO:SàÈá �OMMNc@iÑ(•J"%Nd ÌåJض‹O/B=˜†œ SÉ2YÈ`:xGÄK„ð•[(”@X“‘)í4¤ jýcÜ{լ𰥜;7Í�ƒƒÜ|Ë)öß*Û#ÂúH§ 3Â0€A ½(™`%&¦Óéô¤p·•Å¢ÀF‡—/í¢¡Î$fj »P²ËdKà÷¶±P ²õ©^ l;`š`Ûe8.(BtªGxš(OzÌ“P|�¸õ(ñÙ9, R™ãc'WGèì\<5 ,Ö¢ 044X\µjÓ`:�ZÙÔÔˆÁï÷ÑÔÔIJ%UÄÎA¯Ð@Û`�y=ÏÜããÁ~É4ÜrÕå\uÓ Ìg‚>‹®æ*úz{éëëgÿþc5×^«ê®ë¨‹*àš¦qVðyZËåŠÔÔTÓÐØÌŠ%ÕÜ2X_-èJEŸ'DåL€'@e×Püø�{¸šš–® Ü|ã-üägßäg£ÎTß§sDêÀçâ*~Fâyp-dPWW£µ´tv@XT’ÉÄÈÂB2nÛ¶L�ñÊW¬ZÞCm0‹`ç�/M‹5]Å'ú]bébÊemêr^¼&ÆsŸötL¥ž+ÿ~üësy¼™©–û£tn(o“eÏòW1:gáÚ%4M¥¦&L}}K P ø]€'nÍ”S✬ç X¸@sS#K;‚`—ÀwɱØ'ÉkHò~ABñ 9)v¥Æ«÷ÖñˆG<…©hž_ÿüüôÚk¹ÅÙ@nÉ}©j_Zþy<1 V36›×Âu!®¢±±¹R[lÇ0̳2ÈJ°Ü jëX½¤ Ì,¨^Ð*)qÙ!T\=H}q„û–~Éëµ�í{Èñ“øÙO¾Î¯��çLÝe¨ËïEUS²Üîút\5®¦ ùQìé}䊆,› BÈêPE€E‚^•XIÓ€ë àâ…XÓ[ ¼C‘z1V‘�NÑÊin{ê ­äÉ�zÁp'7Ýt+W]÷;¥ª˜ëxZýlËBÉLRkg ÚjÔ"]­:=u:ËjMº„¦‡ä. ‹¤²>ñûƒ †Q þ³8$<À²,Ûg6š$ lÞ°õ§'qT?¨6håt×Õý¨¬J^ΓV—xÈCžF&§ñ»ßýŽoü Cù Ý�lí*Ò¦­Z¡­6HKC-ÍõÝ4Ô†Ñ4ThL ²ÙŽcÊà1ÜmïƒdâÂWþšÑ‘ôõ-gIÏ �j†Çg…™DÙ:ŠÌ0%9q_¤ñÞ6¹x›çÚk;û÷߿ǫ1/êxÍk>q¿õë—?<™œE>@[]=N>Ïý#~¶øƒyï»Iijõ‰wgùÞsíÕWñȇ<ˆþ5Û9&‚Ü•Wý’|nN,ƒGÐÜÜFmm�ÜÊÉÞ+6äþ.ûétY~Ëå ¢ŸÖN;‰DÒž™‰‰¹|"OŸ9shxhèôpˆÖ¢ ðæ7^õùÔ7íØ±F½õÖcÈ@ØQBµL—õÑòä§³˜}ñ ¹å«ßâ|,ɃŸò "U�ìß”nø ‘H€‡?üQ¬Y³]÷“Je±œ :'Hæ‘$…°v4-ˆd^äYÑfR©¹………ÄB2Ÿ/òiÓ,- LÃ@ °M€B¡¸cÛ¶þ»Ô×G¨«¯a|&ÉØÑCæb4=é1±¡uÙüÈG‘øþˆ�ü!´ÁS<þþ÷fÝ“Ÿ†[ß@îÜ9Jñ˜ .¥8;K ®Žõcc#C ßp-Kîv!”xͦ ·mÞðeoý¦¹®E å÷6‡ªÆ]ÃöU}Üõ¥fËòñ8A¾�•ãüÔ4ù™ ³s(.¬‰F9 Ÿ!ÕÞÉö=œ5ýd»7�ÞG”ØÎyîîü[ ðê¦&µè¸/qNìÓÒÚ`à^/y>½�{"†«�¦(ˆ³’ü,…™i2ƒB ÑZ¹X.:V¹F ÑÐÐ@{k}è#•ãÇ>Zð{ æ­{>ûÙŸJ¥b­ã¸}¶m.·KT¢ÏÝtÓ•7_sͯçëŸ&ÀËÖ ëÃMNñ~—Ý{Û^ö*ü+úÉOŒQšœÄã”䚟ä''IŸ8†KPð©ÌƒŠFдHXËW°LxL]mù|Ž•+Wq¯{=¨kåÊ™7nÚ´ó°(¯7èz Çu­îpuU›ª*AŸ,[¶C$R3¾uë]~?99úíï}ï ‡½¨ï.ÚWb/««¾RÌ¿gsKmÃ}^ú"Ú÷TJùEau#ŘóÈÇãeËŸ:‰9;ÏH¸†�†ŒæVlMÇÊf°òYª7ïfí=HCm=Þ;4UÅqlTÑútŽ÷±�êó£éATÇ6qK9t äÁkz&.œnòúéé‰/~ÿû_¸Ò  Î�ö•Ø+"‘Ëq?Öh{ïËö°ã­ïÄ·j-…ÑÌ™)Œx¬lyÑJŠÑrƒ˜‰yÎUÕr¤½· m«×S_߀Ћ9'¯úXº|µ÷˜,§ûÑA|þ@™°/H¨:‚ ƒ¢¡jrNÃ0]2éâ“8ùU…|®ÀèØ¸=>>rõÐÐÀ‡¯¼òç·yÀý‡xE8r/×0>ßV—?HX½ó…¯Ä2-J£C˜‚°)ˆ‹Ö v’|”üМd’s�0›ºßã~,Û~Úšš‘t¿ùQT™ù ’aªQUÓŔ޿÷¡*`y•qËÛp¼o%$„9ægFð jÃfçæ9yòTvxxðË?þñW?ÌÖß%À«ªª_¦•Šïß»ª#xÏŒÀÞûcLN`NO`ýÑêRÏòùÂØ(Nj�óþ0GZzi~ëýïÏŠ¥ò�±$© ’>|BŸV&gI’f…`å•@yÎq.{BH(åùtr�tô­5.òT¸oߎ;rý•WþìÅâ|p(þM¼2z‘¯Xøôý×vqÙW¿ [vS:? ˆG±¤Õ%ùxã"ù˜ ?1�›œ'®¸6ÒLqÓ^6=ìi<ü¡—ÑÔ¨â¸`žeðP!ˆ7¶+De[{Þpqì ¦*àÓËíÜÐQjCµuuüêW¿BÜôÛßýF€Ò_%À+üþ·ÕÆÛrß�ÊÖO|·«säÖ\¬BÞ[÷F¬L¾8=�+,Wtn5â¾{=�öÖ.JE—êH˜%K:hïl"XåöÀ”–·*DœK¬m9ioŒJ¥(ˆØèª�QÈQʧÁ1(K†Egg™L–ïÿÇî•Wþæ‡ßôq`Rp·ïP€7êºRpÜO´9ÖKýôG²ôŸÂ‘Q[¬w�<žå}>&�Š6 Ù,QÕÏ ÁFì�÷`Ù}ʪ«Ñ}²Z\È�E Í磶¾†®îN›@�’ù§ÞàR!«k€+a�m�M'ÉeRÈ¢K6“Gž[Zêhni@AÅð#·I×-—ÆNž<ç>õ¹©Ÿÿü›Ïö î w(À+õ“]Šó’ǽü™t¼ýÃØ™ öÄöEËÏyA¯BéJ±È¤âç:½ŽüÆ�¬øãY³z-®ã òÞÿ#!ÊÒyÙJÕá*:»ÛiioUAuÁ´lä6g%A4�¬4çsyÒ©,† >_ŠV�ã’J�RaŽukj¨©‰ y©ªâ}K¬‰T#øÀ>¶ð³Ÿ}ã­À/ÅÏŒùn7à)Ê[Úq^ò¤W>‹–·~H�žÅšžÀ™“Vü±�®�bÆÄ\"�Ï4тܤո´à”n«Õ ãSõA=HWW§°B•÷‰«ÒFªNuuH´*¦i25>Ij~ŽHM5¦aa‰¹RÉDÞ—Þ2<4Žü²µ½C�—–Õ±m)”�k§iZQ�¼ï‰ìU‹üÈþ™3çÄø©{úôÑ €(·›½FQqÝ_<íiÑz>øyì…$öÌöl¬,ÄEË{ä¥Ë[²nÙ (Uܨ†�S©3‡³Ù}š¦Mô÷o^ÕÕÕ·{Ù²•=«V­ò­^½’ææf$)ï{`)†´”†a”_¦x�߯{C9éIÞ7H º®K ˯͑¿#+F�€œÓÈçóœ??,+Ëâˆ}]æäɃgGGÏÝ\í-�äŸðE‰pø‘Û–¯Øù�ï!«·ÖdÙí¥õMÏÌXc6ŠŒúN.‹ãÀQ'ÄÍTY×§SÇOçr7û€SžÒ=B„===ËïÕÛ»|íš5kÂ6¬géÒ^¹6åR(“óÜUŠ!å¶"�êõUdßMŠ  "—’\&££Ââƒ=zÒ9qâxrllhllìÜIÃ(�¼�%ˆ î–*W½" ¬ØòÔ'ãúØÃçpiIÞ–­t™ðH«'fQ‹Eä¡ç7Ä~Û_¸6=x¤XÜH•Q`lrrä¬Àå§N\òäÊûÞ|sïöµk×·õ÷¯RÖ¯_CWW’ŒeYžW ‰zbhÒÚY²*½E–¿§¦fcxxœ��Asddx!�ŠÅb“#‰Dl˜JIì0 ¤û6‡!¹¼)ˆÞÒ‚5>Œ�¾(€$/!ÈÛÉytÃ`�k­ %yuf~ÿœinöž¥RóJWDÓé…ó'OØ'Ð;0pü^MM»Ä'l+·lÙÞµkýý+ˆDÂÒÒ’°h%QÓ´�o›b±8ÓÓ1A:Êøø¤;::nÄã3éÙÙxraa.šHD'²Ùô4¦Ÿ�7M#é[’T�ò €uÛçøË§ÁÈSõs[«´'=i‰éŠfÑ@îÃi ÎT4Ž”œø-ÅÂá˜eóÈõ\­Äßé@hº€N 0Ìxniñyýå?›+zmÖsEÕ›Ëz·ÿÿ§³ÿà¿ãúßMìÛ8IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/star.png0000644000175100017510000000741315224505336023527 0ustar runnerrunner‰PNG  IHDR@@ªiqÞÒIDATxÚíšt$_Æ÷Uµ£™Iƶ¥FXÛ¶mÛúÛ¶mÛ¶•±�IÒ]ïÞ­UŸl�Úô(ë{Î×]~ïûÕ½å�ÿÇÿã_®â6\7Øý↑W°îÊÆæõ×”&o¸º4yÝ5M-ë¯êò/ˆ:ýu—g3Þ·�¤ð«Âà¯Ü™ü…»œæ‡mµö²\æ¿ÀÚK2�š ¡bûi §c,>d;ÈŒá«.Îÿ•V]˜•zSá È|¥WÈáêѨu²”ˆ¯‚4¯Œ—ý¯ ¢‰‚E ŸV…ú)?ÆêžÆŠ�Q?íg¨‚ Uï^ƒ·Ò€åçg‚ŠÃÍÛ�É翃 YÀwÆ*ãzµSöAÔ$‹Ú-t#—�†ÿ5 ¥lóŽñ.7˜âØù˜îŒÅ_ÕI~ô\‚âTa�öY3׫õìŒðŸÏžf_8;» õ¬ì–Öss¶½õgÖÞöËTíXù;‹—±Ö³³;âå_óü9™Üt¯&Ò8ö�›@m×ä7Ñkü»Q…XŸ&’ñ�Ÿdþ#d }&½ó›ÙÝ0ݯûVÜøfþNݵꣳ€íÿ–pÿ¡¡ÜDPTd*ÊϼI¾aÈr …ô4·t¡U‘ÉG4�~Þ$Tµ©¸Ù÷éê:|ße‚°ñÀÁ¡óf!"yÉÐ+T™-ðC dj¦Ô�a¯ü2ÂÖ½kIšX~ë1tlyñö¬!?SÑ[œ±Q°voRQ›ù…Šõ(€{ bNDBCr˜Õ9'½Äd´{¥ÁKf™“0SìͰ—�0ܶ÷¨ ”Þ,»í°B+˜©(�·™É-öˆ76i…¡£ÝЊ™xÀf})²=p÷~�g¡ˆd1Jâ\“š pØt¦ LF©£D\ 4�˜Oß ¯ÃézÛ»|ëºzØŸ ÏÜÌÆ§.ÅWÊÕyN­ xÖàIàCTôy�Mæm‡ˆu(R™óeïk¸óΉ�ŤÎ9é‹ÈgÌA˜ŒGh"®º¦@&_¢®ÿTêûO§Ô2'í%Ò~/3 ®a3mž§mÝ£l_}?�Û×aÚu93Q¶O÷x¸SM×8³-jt¾èkêIvëö?¸‚˜Ìx‹ÕI@D Èå(öA±y<Å^ÃÈ×÷'Ì7`å-X´¥º5‘Ó»ú63J¸l3ê#:¶­¡}ëJvn|’ö OÓÙ¶…®PDm»À=¦\a�µúHÚ^ü opÇo]½à:Ç�T˜ŽÄሠ6Q?` ¥æ±š‘Í÷Á¢v¬²´\ͺ–®k€ØÉ#9Ý!a#’©ÃGíÄ@hÛô<;Ö<gÌ ÔWKf™?óªç‚l‘;íB7W„‹ÍIïl©HËø4öŸL.ߌ–·AÔ–V8iö: jïý.ãݸ’mÂ[';6·²ö±ËÙ¹a5¨u ¼Ó«^!·ýZòBøE~ÓЯ?£_þIèl_!5$©$€½Ï‚Ú{?i:©”íˆÃ5 gù#g³ö‘› ŒÊbß“Û~%Y³ðý*[¨Ï2ñïG¬¾–ùt’²ï2 Bmu� e&�_õ ¶­YG_ñGÈÍ?w.—1¹B� 1c }ú¿Ì¥š—tóÈ?,‡ä7,m¼ûT·tãX‚äšØ^ÙÌ—‚ó¶V�-³Gà†Ÿ»zgÁ§Ôì·aÞÉäW,%—ÈßwÖÕQã˜P»v«æ“J¢€hóh:ï«t3~è�?xÁ÷më_üÄ95¸½ð憖æ¾§Í]Œ$ ì"ÙGdzÝ6^vý§ñøµ?fSëj³ËMø¬ˆ-›÷CÕjW®ý‘dÀMqÆ*2¦yxc&¿|âÒˆ«a<½jD÷ioÝAPHBš'òìýG²ê¡GqjÏ™ð{`Á�µL²;×þPЍ›œ NZOìÇðQKA Ò½ù4ìf&X:ë.ýõCp�#XÙz9ÏÝ|#âmÆG] W/ø©íü‡Éxí÷¤Á,x³b‡HiÔ¬! ´4 N@Øu¤wë,P•¥šO7Ž‚«ÌúM÷ðÄ•£kä NôœE¿Ð­tW�WljšõqTã7.”츗�¢oóKÁ²oÒ��A¦Ó!ìÅQ?aغL“B_¶v¾ÀCœŽï´Š~ è±�°qÉ/ÍjÞŸ]ù-ƒ1÷Yo|?ÈI0eá8zÕϤùYQûXPBíZGÁ\iÛ;ç�óO¡¼Ã,pò5=0p¶né¯Mwùvø²oˆé/&ßPãK™’Èô¥“©ÏM ªæÒ3Bj\&×:õ%¥‰L�’�m|†/àÁûÖѾ G"ö 3[ýÚßY�ÛátAÙàйG*.4 ³_;›¼ŒCD A\z9$‚µK #5íòá£Ýð8~ÙUTJ<ð”cûZÈœ©~Xö†ýÌïÑCÑ?Q»øË²²Ù/BGsÛ&^ÿð�÷3kI‰ 2Dº')]ÿ`’+Z:éÂqHvV©ÀΕøç�À¶¯Ô O®pl[¡³ëÊ‘ý0–¿~ó{ýHìâ/’Q‘‰992™2tz‘I³AT—Øû‰’¨q<Ø¥ó¿«× :6amËÑ�wÁÎõ˜<˜¼Ððô]à°gLx¯Eúà›¡¼Ïž žÿY ˆ›'Âé„Òôâ·�¦©8ÄUÍ'$. ’Ò2 ×Ì ¼9Vl´í9lÇÓà­jºj<<´×;n¿V¨ì´6>h^¯xËaìÜçO…Ïý,}î{‘ÊW†ÏÊ3eî"ÐB@â á’qEpy°NÐ6Ј¶b• Ðù<í¥ª¿3%† ^hxì&;±é7�Èú·n¶ï|ŠŒš¼Lœ\SjvÁ‚wLƒ¶&8gàœ!±L*�XteÐMà7‚(T#å´¦€’f¼ú/¸÷©�U�a ïôÞ.|ÇQ”{ä½À™ŸÀ™ÉäL(7º‚4-}³!B�Xt®þK‚D–$ï’9U]ŒWD]J  Üz‡cs«©�ìâìÎw�ï‘7C¦ j ‹U5 ‰ºw AÂ|ÐÐßà„‰é±’Ó¥ -1Ѷzð†šõà«1ïÁ<¡yɹ„ÄžtIU TAÄJ¯ª+0I��&1#ÌA!(Yõ õˆªd½·l˜1K¿NªÿUI˜PÂt^Õp¢|ÌC¦êçh±×s£¹\‘ _/X%íYa¢“ÕŽ'K"™ ÉrIOO} %AÔ3@µgÀS(4eK?¯ �4Ÿ¢k¤×}�û u†¨§ŸAÐcP‚(¢�*ë‹èBê2}Ow!Ý|— (‚ƒ+žL�f€bfä‹J2jdDÕ\ú0L1NÂt2 gDë­= À+™J…ÞfP,šòüÝÒ $äÒJ a¼ö3ÄjÛ¹H" R#×cLÉ:¡?ùpäÏñ‡/ _}�ñ奯Ï? åXÕê0HßFõߺ¶]6r  * ^Éû¨¨"'4¹ X—N¥ª Â2ÂʵgÇ&ü’réÑÊ“÷Û·b/߯�‹\Ä–gîRÎý¥çŒ_ÃsÏXÁ�Tï “€«Ó]hxo¢ŠÃ÷¯ ‘CºûÖ¤Ù±¶põ™pÞ/€ç�Žˆ›:<·»ˆc…Oôϳ(ñ“lÀã+V»ì÷ž ~o<Û �d$i¼ú/¼ά>”Tż'RG{y§Õkà0ñ@R´Å±l¹ðÈEž5O(¾Œ«+r†¯p�xVDÂÿµ«0¨Ft°”�Záé¼pª‹aï_ó¤½xõ“>h L\ä3 ŠeE7Zõa‰d¡}+8°(¢,AOð´©²:È•úVŠJvH'Ô Û„çŸ0ž>QÙºÚÃ!·Ç:Ù—¹Þ`µí_º¦ûäüòUx`ûþKi#beä¹, ˜ëC[VÚÂ[Ž÷¥{aølÇèY†™È ¡H{›CdÛ*lŒžˆ?, ñà%|ÙoÇÚã7ΰK~Z´£ßƒÅ{Î~?/þ_̦ƒ–pú�‹xãþ ú‡ùX„c#ÞžÄëçöŸOÿýòʃâ¶cµÆmiëóÍ7\úÓÑ!¯ }lvÕA‹¹"îØW7�éûÏ£%^&�}ÖîB$Þf¸ßšb¨ããñÄmž·ýÔa¯:.ýɰò9_é{wlþ¥,qYz2~}6»ßB›�kÎo^Áøß½’~ûÍ£w2 ‡c¿ù¸X¹?Ì£÷o_ÁÈ8¦ý.ÖïçË�ƒß˜ÍѳQ͉i»ßdžãÇ¿(âl�?,Œ�,÷I؃ø#´…øÄÐcIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/viewfolder.png0000644000175100017510000000262615224505336024725 0ustar runnerrunner‰PNG  IHDR@@ªiqÞ]IDATxíÚŒmíÅñÿzÞwŸ3øîwñ±¶m»16jã¤vPÄ j5Ã6fmÛv/Çž½ßwõìœäÔsOÝÙ�ß8œµ²žááÿÕ‰'Nœ8q℞õÎ?ò�àFàóé€+ÀÇPæ xðíÌç@ð-à-Àw�{7/’xÜb#$®ªT8h}_àÀ‹�oç®3ܦÿ ;�Xæh‡­ùåù–Kå1ý Ž[ ™C‚q#0Gë˺÷íl¸¼9+áÕÀïq�tÀ%`ïªØÐUÓV�¹ª…‘¸çm3Æ\Ù¬�6| غV `h™~^¯ö�, Wª)ÅÌ«_ÌÝoÍH�¶ö|‹à®ÕpØ›g~ô€R§+3¯Q_Âm2]g®’ØoÍÏÿÜqÐz 䫞†éø/™FY\+$(pdš-À” æø+Õó˜òì ˆãÉ€€RÁÍ[€=m­+‚�,ÀÌhŽ ç4Íæ^À¬cs¬IP*ÿ`Î@WÂŽXò߇qüo'ЉA˜.@šûj5e@€˜™ï—!@ú0Ï HÂ=`ï°Á Ô &,ÉÌ@þÑΟëºz¦+Å·9wúN7\¿<••­]ª+C ›%P4«[;wúËÊFÍ))çXϵúÃÀ=bâÒúv³¶½w€®°qì rìܦ-åýÑÚÔRýK=äß(ę冠Ÿ€¶T6÷JÍ‘bÿÌR^|ö“oÛÞ|zìjc“m'FR‘èlë÷—vGŸøÆåýœ"Ö+Z\ߣ�骅«Š•˜$Ú¶5Ÿ_o›j­çZé ·ýùù¶»¼�l«ÚÙfÌÀH”ˆ8ØÙ;àwvÕ¯äœÓ†•�Z‹(6=Ob8Œ$K”�íRW¶:Pìå&¥�R+B‘Rƒ]=ÁÐH²"…Bäˆ�,éBа&š”1Æ5dW†F Eš˜fÖ…œ"Vª�„š,@ÔZ±cˆ!$„´’GMÞm»RCÄ( µJµ28!"EÔ&§ÝœB«5¢“ˆ&£�\ª(zâø3S)4�4- ë³gI—"¢4£,"DWDªfh"DHb¢í³ç~ÓÑ£&CJ"$•*†¦Ï&�„rêO vó¸I»Õ´H‹9IM–…HÃ,@ÕI)EÛgÏ’ÖRĞЙœÐ¸*Eˆá0�“è¦ DŸ¹Ïž‘Ö§)è ˆ"¸D‡¤i}ö¼<Îí.å@h”E­(Äàä$•b Ñä8X§6�Giÿ õ¶&BhÔˆZ¥®2¸hì·„&RÄvŸ=—ÊŽë€ÔdÑA1C« ' Ð’Öûì¹IÑ5™=Jµ 4Á°„J%¤ ÏÜg�Ó×åÒ?Œ¥6b€lè3Fˆ>sŸ=ŸZÌíêf]�0¥"Û2ÈdêŠ#EÒjŸ=€NÒZRP*aöP¥’"‚>3ÐÀÂ(í690’� b€Œe+5)ÜgÈ·œI[¥¸[+ÛmWeIK#¥ÛœM%%mÍ ¸óMiE¨kíXÛ9Ìû‡˜A1!ÝxÝH·;ÛT{ CåìÂáÎòòbwXXjrIm—ÅEˆ›¯Ï,f•í�½zùkßõÐÇ>ééo=}úôÄpØØØøáW>ÿ©½áUÏýNô¦×<ÿÂÇï~ß÷œ>}æaµv§l'1 –T"òÖÆæú·ÿËŸ^$¦�;wn–�4 1(Àpøð`OL h€åé @ÎC$fì;ÓZÀâiö2Lž½œ8Ñ㯕6®G !4IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/loadermods.png0000644000175100017510000000746615224505336024717 0ustar runnerrunner‰PNG  IHDR@@ªiqÞýIDATxÜ–�d1†wt¶mÛ¶mÛ¶mÛ¶mÛXÛ¶mLߟ¾ÊÙž›ª¯òX›ïï~Éêý­ß ¥fÐlºÜü1FâA˜8²ÌLãA0”Öû~—˜ª 3xÂ9õ_lJ�¥«,$|Žë~<5uV¾Ï“ô¸q–š½Ã–.>ò%[× ‹Œ§Øø$&2:�Ï]½£è¹E0ºæA£×XRß…FÆqº(¯‚üYÑ;ŸÀÂ,G>�±äáMn¾Ñ,øטíç\©×|„¶Ûl£ºÀP!Þsž1]î/ÅYZàéòÎ^QdçÉ]aåNNádlFF¶¡díA ÷ÚsÀ!¤ÔyE÷¹ÆÏ0Fnñ °x)þ¦ê,΢°¸¾u½° ¡§æÁ´é” užiÈ!tœaØB'ÀDUhÛ0Qý–“ôù»þ êïÉ˪ذ€êï¨Ñ®¢j­'ëóä!ù^Õ_3w “ò²ê,ÿÐ8�¦mµ¢8mÙö»Ê–í°çß W¹žŠR­6ªK¶Ú°ˆÉ³ÄòCŽUÝÔþMÕYþ‰‹Ó}£@±ð{í§¤jnßÎ]¡_²ÂõfüÛTésYQ®ã^UÊ r¶˜øÒ kK`Gଜ¿Xu–¿¥@3·ÛŠ–çAXšl¥Ë–j½YS®ÓÁ¿ª €Œ­¨Ä(Pˆk’.³^Óxì e•¾WSd/Õ©b…®G�‰êá:‰ÑÑ3JT�:)�ªKyÞ*o¾ðç]cÞn;þöEõF>7/PcìÄC7«9ôI “Ý‘÷•Ÿ£ÝT�Ïþ\`øC L¢ Dì!Ÿ"AðF®À‚6ÀXâ9wH‚jXÛäYœ:Ùò€«.å¯<õ£ }hþ{’Ÿ‘r-Hq ]" bAHnphŒ,¤®‚„DIq /÷f>ò_ûÿ¤�V´õ¬+¯äXý¹ú�gq åùþ«êÍ:’5 Â;žXkÛö³mÛ¶mÛ¶mÛ¶mÛÖö«¯snöÏ¿=“t°:§ÎdÔÝU×·g!q—< TȼámŸG_ú^軜üzCûlíµ�ç�#ô$@°ØöO¾=iý;S±ÉËíØ'�!qj¸µ®_ÿ0½›³.²¼>ÿ¦ž8Dpyˆû.oäoü«°M†øõ~\sÿçÁ•÷|Æ#ïóy¾Ïq€…Ñ bšqœíOx�<¢÷¤-²ýçÝ-žô¿Q²Óà¥Ë-ŽQò_}vs„°µ²�µ®ÒÄ»ïò–è ’¿ö�zò—ßýYpÙ]Ÿ—Üùihýóoý$8ûÆ�‚3oø(l‘ñ.š¤®|?ÄqW¼zÈá½rþÛaÉ#ä©&]F–H„d,º�\=Y×ájÈ23–7âFšìî¶²'Ë[SC–÷Éûñn䯺7šü97 qЈø±—½×@üÐ êÉ“;¼\Jªz—uµf<ºŽX…rVGòAÌqº9Èû}¼ou+qÇE�<Ä}ò¸¼KâçÝò1ä,êµ']5ÝêG]òî ä÷=ë­F¨™ÒŽeªL©Ø”ÖèLÖEN‚ë±:qé’ÇêÉãöï.yš Ÿ<Ä]òXò4X\0ò`ÏÓßh`ñž D§¼ËÐåã  /¤Õ€ô!›"'Åý£¬î76>y‹wˆßòè—FÞu{Ÿ¼Å<ñ^ŒIϬÏgøÍ Шê8`±tóé'Ó‰ÒºAY%ÂÕiI‚Ä,®ï’7ân¦÷É;ñy§Îû–�&ïǽ¹>ä]×çu¾Ëß[ó �!0K @uMŸy›/€È÷œ°É¸Í‰ûBäýæÆMvâ~ÂFÞu}ÜÞ%�U!Ìú�ç;à�x_áý/ä+{Ô PÛw�”†ƒ:¹÷÷¸;Š3æ’älŠã‘’‡0ìô±¾KâÀ-u~“cäýŒoä�KžG{°2g Üa} CbÓþñkyð7ZÚ„nY exñœ÷´£|„$(Ιbž(ï4,ÛeØŠëÒ P߉k&<#oeXÙóš¢ä±¾‘÷ÇZ¿Þã& "ØPãÃÄÀ­¹nå­?Kjú¯¢D=A¡¼Ù …¼I›ãdíÀ Ïü5d±ÃoÊ–uMe×cªƒTHê�R%À³´À¤Y`¶ÇÒ.qo†/NÞlüZïOwüÍkü�Õ-Ã7<€µÖákDª¿ÐCè% ì�H�TﲜÚ÷M4Ä­•ÎUMÔë}pz>—He)úâ1«^ú8ñO¼ß6Ɉû #ï··>y·ÞCÒ€x–'䬡‰⟹ëÖö™oCñè(”  ŠçLñB7yWyIN¨_†H•4Š(6>¦MDU<À�á½½ˆêí#û{׌0‰ÍÊY½… ôãríûÙðŒ“ÅËE >�ã1+䄼xŽÁ§ïÕÝñb'%‘_I¸KMˆ»äýR|òâÖÝá⸴•°¶�‰Åúß"ßKn]yŒ1rP¨‹âè€íŽõ‡— µ­dnJ•ui­žÑ¸N¶¼ 6“Ö¿ý{îÂ¥ó5X9Ö¦×è¦æà܉¸‚¸�dV¡©ø§&_QÚ&op×?ò€òæ¼Å@_Ì£žÉ/–Éê…N:«@óãà вvt¶¬K>™ÊÅ ô5q0ê*I+ê„ÔÞÖ‚ÚÝV ÝÅkuí�Öõ¦²åejo“ZÚ$hí.t@jÈâG¦™qúͽs²�YJ„&ÀǨ§ÜF"SǹX*G\تº5 Ò¶ãƒ9äFf|Mx9 �ÓÊ{b¯‰›¬þàܾsïtT÷1ëLÕ}�Œ^Oø�¡ è¶×™ÔS¶½xlOÐË·”A ÆpCü¯ˆ/›ÎUôé‹ô|šîJã¼LXûÆßµZLž’©è:Ö!¬›5U=&¯I=¥dnæàþ4…â¶\ào>ÓVàx-Cí;Æ¿æ5?ŽYåâO ¬¶—É�„BŒ\þ¬dðJ �¶ÁÄF¹0@_~“1Y1a¹'‚4ÓÓ Ý¹‚×[ â¸5°M OV]„¥‘·4\”j¼�×´Ø dýÅrÝs*™ §QHäI„r¡�t€™˜ aˆrzn’¤~ïÃDÅÌÀÉgൔOú*9�xâÐ0A�q£ B"4JŠ„A„¶¾‡Ë]þ#1!�z¸ê*®þQçÅN/i‹‹§¡i ¸z“àzU›ìuŒˆWOXçæ`Ø’Ç>Å.@¤ýv1G2†W÷œ²ƒÖ_7@�V_Z—] lz ¯!ÊÚ‰g ×$¸N$\·¾Èzh ÎAÜÏ„" –&SUS7RÌì¢Ò²©’Ç‚z}ÞÎC–;‚ìÊ‚Ï& ŠƒÉs ÐÞ`~Í ¥ŒÃQ" L5Ý¡Ð_D‚ú"Z�íH–µDÉÁ› r‰�ü2S�„1h¶™:Æ ‰Ù¡ ñ�7”Q!tÒöh]b‰ÑÙn6uº@i‹’8ýÂØÕ¯t—ûPŒ¬U@ªh“ ð g¾®Ö†e²)ýe¬ÈI)E³ \Ô&ŸK"”¤ãL ˆP™¯ê5ïøµ®£BP_q-~�åƒ×Û�h1lqŠýæÙõVqé,21HThâš„ôÌÎIÈ´m .¼MÀÌ@£„Zÿ?Fß#²±Pù§’ÈŠ1•6´UðôØ^ i3ì!ÀàE{‘{AJ„¹Xƒ³P¡Ä1jÜWÓ±½iÖ‰› ºO@»Ý èFo o¿'.½“é’|l¤ÚpT¤©`÷"X»Å‡¹¿5`02F,wú—âÒO”Ä@*568plPAZ zû8 ¥§@¦Â_émÔÜ•¶HÝC˜F)¤àÀÅÁ,Ñæ ¡Åƒœ®þ¢Á“eq(7è¨h-Qµ=ÀŶ\›8Í8ðí>÷â P†rúß¡$¹!Š�ϸÀ› ¬UÚ_ø€`Qè¿ÙÖ:þ xˆRl(!{ê·‚Ûék|ÝøàwcAÿÏÇ5Ó %ég\?<Â�xÌ¡V&0.'¬<‡`%aYa®ìCã `{Dè# † `D ŸMéÁXžë‡‡w)–(G8�D*P³\/×�‰ _ˆ9ñÿy„J+ÑìºIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/copy.png0000644000175100017510000000403015224505336023520 0ustar runnerrunner‰PNG  IHDR@@ªiqÞßIDATxÚbá`0"±Ù�XˆÙ†ˆÛñ[ þI‰!,Hla^^^7===NNN&¼ºþÿG0écŒŒ ß¿ÿwéÒ¥;Ÿ?ÞzF­`y~ÆŒ‰BBB¼ÿþýû´ #™ÀÅHw9‚IY0¾yóæsFFÆü£G� 4@Y�9 ˜æáàäðbF¹NÃ@µCBx” ðÁۢ⣋èþÂgõjµèHáÈÊ»,,;-´9'w’)ŸK))·Smš+„töýã­3î š¦©}¿1¥qÕjjðm6P‡Ýî)ÃGŽÀÈfï–U­5Å��7°N�{e% $€ƒþ�8�¯ƒ$ çGFmŽÀáð]P¬�÷¸ïKè§Àeðýþ="�ð¼êBô)AÝ3m]H&e+¥Ö:Ýn·/�íWI´˜ù|Ï„UV5 ^¯×Ÿ—Ëåû·×ך£ýèð‚ Æ[‘ÀÞ íÿü„ùôò²>:×õt:QœÖ?ìˆÁ\LB½4Pž=Eøëf–j›µú¿õ³Ϻ?Z‡“À‘y8ÏhÿаO(ˆÙ‘�“ÑคxÿÐ ÀË%@1qàþçµy¼(�ut‹ïÓú½BÍí@tGÆÀ¿#Yê$ç :¼zÕ /�m˜Ð?è7AŠ�á¹ ÐñÀyHýƒ"vãð:x¼ÚÔú‡H|컬x<øµªöÀ‡�CË"ô¡'€]_>š3Ûû Â�é9(�‘‹Ãë¯�z ð 3þèÿ+}è�ëÞ÷ÔØ›"Ë¢­ÿ²úч�að ¤G쳙âîÇÁGAÌl| P‚µUˆ~\F|  ÷ ½Þ‹þPx3Ã~¬83Ó0êYV¯OÊ×6yÌ+=MSªf¡þáØo³ø/;á˜ÐµÖíüK€ƒO9§óœJ­ú÷Äïj”ãðZJI÷û½­>» hæeIo÷’,+ às_7«ÁüíwûVÁã6Dß2£ Ìm’2ãÿ;f•™™™—!̰´ß7Ö�T´âÚ9ìH#£v¿7øìŒóyÚ²8 Þ›UUU�$PtÕTÐå}=à x.—S·¤¯GßÇÆàëµuõðx½hh¨Gss³á·MÕexU¥é OÀY³Ù,ïsD°ª÷’Ô×ס±© ¡P±H›6m¢ŸÅ`L´‹ ©¼�Á³7°$I˜˜˜PÏÑ5¬æzu5ZZšÑÔÔLÞV¯9�.D¬_¿‹-¢ŸÈÀb>*Sø³‡Ù©T £££óIp ç¹j’|>‡D" ¯×-Ô‹•+WÂjµ¢¶¶ÖÐñZÜTï¿^øH3™ )Í'¡±±ñõ4¡Ôàãðx=ðù|Xºt)l6êêêøïêãÅRˆÐkœ_Ñ6‚žÈàbÆàyK é<-ðÏżH�8¼>BÁ,Âë6« Þ Kºº€YùOBÀXÙ²,«!O9Ïž§sTÈóáp‹Z(ì ¼AþP€¸ªmCp5À¿žì-K¡OF 0²,a||\‰D`Yd¥°§œ7ÌÒ’¬ “E ˜×K¡½´p õ)S¦¨ç%)ƒ1Þív!‹Áj±1x3øƒò²«Ëípؽ2ZÐo œŸõõÅutt¨ž§nàt9Õí’ÅKa±X¼)üÁavÿöóOG‚�À]ãF `8 X(ü«««Ô>·Û‡ŸˆcÙ²åÜçMჃƒÑ;vzúôé)v¡y£`~­q^{þ�î£ö×ÚÚ¦n‡øT*�Õ«VcÁ‚Äþàt9âûöï?tîܹƒ†„æ4j€ àƒ€x_S´�󬇿ꭾ¾>5Ö­[�yóæ1xÃüÁåveN�:ytßÞ}Ûôx-À 䞬1#¬ïQ™p«£~\)ö'éœq�Ç#®U«Ü~öìÙt�1þ þ'òäÎ]½zõôî]{~÷ö1xMÐ]4MѺ!%¦B%Í퉅×V¼e�×"GVP "–/@&I9ƒ!´67aË–-˜>}:ƒ7Î<îÂ�»wÏíß·ÿ{ÑMz^¯i€ Ò�’•ˆŸŒÖ&î_Es{MMMuï¦ ?\©� €C˜6u =ØP$OšÂÜ÷äÇ.ÿ·@' p%DÅ[ü�ØŸœÅX2SÅîù�½»÷Á+BúÁ›ûø?ýõ.¿y©%<® �ÎäìïñøQE�ümøë?¹%oÇ…&…´ö'ù$»ÅŒ²×@EIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/bug.png0000644000175100017510000000734215224505336023334 0ustar runnerrunner‰PNG  IHDR@@ªiqÞ©IDATxÜÖ¬dÉÅñoÕ½mŽmÛ¶m{¢?Ö¶mkÛF¬µmÛzFïIR™tn¿éô¸wNòiFçW·*es¹»“b‚ô;åʲcŒ±€/‰ºwßý^–±»±|h�Z©’ŽÒV2vCØ?` 3\ºA$Äeÿ@ñ –¾ÒNââíìpƒù·  •´�¤„Äìþ3¥¼_<1åz¦Xö\ŒSÖgŠeÏÇäÛ^vþL)ï' ¸‡óE$,!D3ðÅ ˜ÒÏ”2Àˆ#Nkâ·Ír¸Ü"oßÙÚ{å±V|)ï<ÚŠçiÅ3µä>¹á®–œ|U–Sb´sã&W^÷éS]q<Ï#�F©®®¦¦¦†ªª**++y.”jf 9ϸe4"¾ä}¶IK¯¶+ŽKƒÁÙ˜�ø¸-åxbvõawµ¼ Æ÷}Tœx<¾mnÔÖÖòs]=ïÚX^Ùâ< É„(œ™EÃ`ùIi¦ö÷éd%)wv˜í÷€P±{„ÝÕò‚ ÒµkWZ·nM:�&•J¡¡xÕO·â°ýa�µD§Fva–ÓOÍphúí$íá‰Á•wÅÏ’%ÒQ:5u�°»ºòZa´ÒÔÕÕ¡ïË_Æs¥¥°¨ãþ É!l � ³îšܵ>Π§´‘„ønÕîüqr²Œ’‘2(x�°bŠdäÈ‘§»=�ÞQi´úÔ××£ïT‹uÅròhlü 6È£•�£‰!8ÉûÞÌÒN8㜠ç{0è,YWîl9Zp¥—ÈЦî¶ØÝÛ‰!Ç?Ë�ö¸ÕG@'=¥¦Âúx®È“*êoðN­ÈÉúüDxùC>ˆXãÈÀSµ-.HyŒzÉyr¤ägš{Jš»ò>.¶Èݻҩ@^}õÕ^yå„×^{m[y ‚RS›ëŠÜ]AAîú»Hñ¼ÒÇéîÑÿÜ4ge §ÿ§0¿ˆ•F×±1…Òƒçyè±w«_z:P�q+hh"EVß`À8VÚ[:žœffÔÌûò–T¸Aü&ÕÒ¸S‡ µß÷û¾´tqÖ')ÈÖT^y'¿°¸Â‚#ÆéìÁãåß”?äkùT¾sÃhÈ)eg†°3éä5`B! °4ç·€Áa‰À¥-aU²‰½^¤pã"0/º­üÛò—+ÿž|$?Jµä‚O@D:ÊL9XÎ�så�ùüƶgZU]™WÛµùÅÞ‰ÊêÞz½³:_dæ4Ïïàž<Ó�8Ü"r½w4áߪRà8°Ø $F?Ѓ×ˆÜ:`-°FWô¹HtnM²–Y¸’'!”s­¤ú�wà8íU�Ò{FóÏ@¦ô?"uÀÊßÂ$èô ‡”>O�Gýzöß¡ßg `K ¾\!T«@ô±=ËôowÕà½ÇàÇóÙÁò«mN”—�äÏZÞ²Q1@pBÔ#µfÀsµ¿·uΓ ‚)%¸r_©âMa%O®Ü]‡oh^ؘÁº`q�Žì5#ø�&k˜¤ qð—¡�›,` ê�[žzú/NÙœ@ÇÆÂÇ&î¼¹cüŸO™”Ô­Í!xȳ!Á–+¸J5w^ ¼ä�û(ؘš0ñÅUþ˜ h×@¹-€"ˆh@áí¬ÿ>ºÿïÞµóéÞÿì>Úø“¬Ÿ ÞB¤S‚?ÆùÿIœùägΟ^áý“ªåÙåR2cæLÆMžJÍXú• Me$˜r!Ä,‚sÐIþ½Ê_k›4e�æèÐB k@Œðaüÿ×»G½»÷Ÿì>À×»ö†ÏÕ¹o7Žøø“|¹RK>VM«ÿ–¸jâlÉ‹wg §C¨[O#BÀ&9„XùõÝc)Ø[ÇÈyA�ÚÚ_–™3&a*0˜ªñ¾\’uº ᥄Pšh²òkŸšÛ¦Ã6w:¿Z¬ž§@;%Bè÷ä T¶ZEÎã€×�†ïN†¨ÂßWáá•£ãï‚D{UKTþ§Ì_Sa²^˜F#;à]ž˜šA²–þâeÆòÕã)v7ÈLÀšÁ[!Ì8 ‚ó4<±’ ®3!)ÁFÃÕÓàÊ5Weå ç‘ dºŸUæ…À×�;5 uéíqVGÔºôCðD­2pU?ìr°Ó.è6<ÜÝÈ#ï¬@˜F „1±¨hDXAˆÙ IŒ¼ DåGbÑ´èxà¡;O1–dlaÍbUÖJ-¬zÂØ}-å žÐ< λÇubä÷ƒdÃìöþøÛŽ÷)ì‘ÓQ@hŸ2X'§‡€`ŒaZ„@ÍúXê ¾B!¤¡ÐF•`R‰V›ŒKZœ1Ø>ঌúâå 60�ÑtÔ(Ý[!Ó¼ Bf˜:«åtC‚‰r!ø‚`<”ÂWzàëݰ$ÓR>À䄇€[�+�³õRt<î Qwlk‡<8ŠðUxÿ(øÓr®�ކÿ¡(!`ÁÚÁe-™ ÆØ›-<’Â}°ÓÈÁeuX™ûCA=lÔx¿!!Zà f€Ã^ä‰P ðßøÒøêXøÿ*S•¦“YL¥~Sc#!ÄÓAcVŠ\ XÔ `gÅþPà0°EK’Í‚0„ð3@QF÷’oy¶‚ 'c9=ë>¿ ¤81öÈô嚪‚ë¾`�b³Ö§íŽÇ€@ü„â>@j*ú¸¡@ ç#ˆ<ê.wÚX«Ò°ñÙHÖ0}Ö駃Ê0!¼r${ñˆâépÌSœ|Ì$ÛÙ>@Ú wø€W¤qí@©x/-63ˆÑó/g�šëÍL°z�*� ¨ò’ûøø³r®�ÎKÒ#å�p]�4.Ec{èl íq/A·ÁrXÃ]—v„Y,�Ž-Â9e8×B=F?ʶdÄóªìEÕ¸øÈ äxËsfâÄØß„àãfÉÆL(œD»SH�^•v`†@v=ì}Ƴ¦X÷Åû-ûFÂ'¹Þ7þ3ˆ™`"„ü>‡`MKcÔR¥n, ¯#‚�÷„8&ƒаY) 8®°q-><ûö#Ž—õíÓß¶”À”áS£àÓ£á?+NÿÀ¶@ÈŒm}Ž ÔÇkblØÁ5c%6Æu–-§=rþ�ô¾ó•Xñuù»À·=ðˆ§ýèXüX|˜öÊ¢ÓºÆÈ«) Ôm! :ÈO‡}IåðÃ3ªûýÀPý À0œÿ²ƒžðLJv”bô­vƒ« ü¬ÇòÓS)Kû4¬úCFë©r?e*ý.?2eÂvÞP³«€n9¿ØôþcdxÎG[îyj¯ç`̂§¤œunNaŸ� ÜØgXÝ×@Mç†x„jXjö„®Ì¦{2{ta-[}uoã¡~8¤¨ov(úiŒ~0lç�'l›çYÌsBp õA´ÒR°eýñ4O‡æ ”^P3 ®éMZÓ0O»� šf7«þë:û †ëüJ­²…ô>Ïâž&„åbô)ÞK„öG.™Q#T)¨Èb�à`_­°ª÷Ý×é�fkç®ð(°¤5óµT(èªÁÖ;=ß<éYÝZß2äó–)n-ËDGcˆ¯ÒÊŠµ'NíR�ó»€Íqxç;�¼x°Fk¥¥@¦´:©Z{¶ÖÜøÈ�À¹&àé‚ÓíJäxCf—šÊïÿ!"„T27 ›?oëé;´ôd÷.9zTŸc|Ò‚kçüpN�%ÀÛ�ËTS{��À¥Úf�8p�çÇ›¯¶¹BÇo/rªæú§B,¥» (ò»jõÃ×:¶FÎö B½õa(î öËáõÀZ]7h�ЫAãÔR˜w�çÍ߯¹¶©ßŒzN ž·¦·¾éŠÝ�ÖC8¦ßi°K™œ.€!öOBì²ÄZs€Ñ™{`‰çŠ?? `”bI!@ Qlãû»u¹’n)³$Øe¤Šä9%%M’*©ÄKJFÄ(i�H»®d—1·öõ¼ÛÝ>½¿¿½øÄäíû{ÀññÑAŠëy^¯j­³C„QWɘç¥ô…K©Çqr’‚é)LÓ$ÉJµb’’¦Ô}$Ðëm`úÊOûfÝ6–…áO’eHì0S“rÊLK�efffffffffæi§ÌMÚ†Évâ8&î{kõølgfq²¬sþó@I«ÿ¿÷Ýwß•‚låXB>¿pß¾Ãů}íc�››‹îf÷Žðq'\ýýýohhþì¶mwoêSCGG‹´®|X_YiEmEhyßÜ›÷úr€"[ヒã ÉË9pĤmÛ€"û®7çpáÂh ©¿Ÿ`H6wpÝ)˜¦±~Æ]í»vÝ›æævÚÚš/Qº ùp’„÷À²ïo¾ÌN“�¤MÓÆû9,Ë*ÿŽ„ìƒ‚e9¨ªK  ¾¾^©©©_4~ °˜ȇ8“J%Ü|>§ÌÎ&éîn&�ÎsàÀiŠÅ®+abFùaMË–Ä Ÿ/€‹; E¤ †‰ëJ’`Šª`[EQ0Äœ*æ$y Eµ¨ëÜÿþ¡ª*Dmm„ººFI¾,¶Ò¢§æçg‹Žc…Òé¡P€#GNðñ�¿ Çéŵ—Ë€Š@Áq¥‹ûD«¡Š9Ÿ€†$ë8:¨rNÞW‘¤mÇ®˜Ó|Ø–‚œ“¿‹£¡Z96®;ÁÞ½{�&)€ð‚Æ:O?À¢ P(”&S©ä´e˲ق´ŠŒúôõµ“œQ,Ý"= «àÓ Z@SÁ¯�O­ôe«k (²/ ª*à­%Ûŵ©<¸Ît�‰I“ùD‚žž>!@� ©VUµZDZ‹.@<Íww÷œK§“ËJ¥òù"‘H]]­ôtíäÜD´Ê„ü>IÒç‘•"HxâhŠD™´"…Às3\$érßrʰmh ‘އ‰Åæ—P(Hkk{XìHMÙl*(w´¨Ü9—+Üûl*5@*• ­­�îžj03eBÚET,�„¯‚²0žHº×t9;÷C:†fff©™Ü~[ZZüµµõí@u…çâ Ýþ´X²%“) ÐÐPÇÒeõàfAQ+–Ö%$)�ˆ$¸~IX è¿¿2¬�Bá&§‹—§ººšp¸Zéë[¶ý�¼ÿÛï}ï{?ˆÚ¢ îÉ……yá&2TWX¶¼EY›J ¨À³ø¥"xäýû~ðÆ‘¼ß•¢„�.1|á ¿ÿý¯˜œ<Î3žñÄÍ/zÑËÚÙÙõ¹ÞÞ%êeQ(•Œ ÉäüœëÚBi ™�­^ÝG(”Ó­�®x‚Í#sþ üçt•X‚ªÑÑ1M]8Æé3Çèéiã™Ï|÷¼çƒhl\ÊÆ wmìë[ú>]××Õ‹"@*µ�. çs¹ Å¢!1Qèêj££]‡Roí{¨ˆQ „>� $\AeN´hÂË8Æ›Zä“}O{úóÙ¸q‰D‰±±33sLNO‰q¢(ÜsP³(Äã3¶pÿ!Ùl€–æzûª¡äBU‘¸}!|>I¬ÒVWúÞ} I^u³Ü§}šg?û¹tv-%™Ì ¤Ð´òáÌÙÓ>tmæìÙ³G]×-Ö¢॰CÙlÆËðŠÔÕÕÓÝ] V�¸vQÙW¼±Z‚úí÷ý^?oS5y�Í+:Ѓaòù’ÐI# �ÏsöìA††Îš¦9 ©E@(<�ɤ䖈|€@ÀO__¨Yˆ ”,Ï*Ë¡,Ä%èé`B² øRyîÍù9ªòQZ[‘ç™VË ´ªÊO:SàÈá �OMMNc@iÑ(•J"%Nd ÌåJض‹O/B=˜†œ SÉ2YÈ`:xGÄK„ð•[(”@X“‘)í4¤ jýcÜ{լ𰥜;7Í�ƒƒÜ|Ë)öß*Û#ÂúH§ 3Â0€A ½(™`%&¦Óéô¤p·•Å¢ÀF‡—/í¢¡Î$fj »P²ËdKà÷¶±P ²õ©^ l;`š`Ûe8.(BtªGxš(OzÌ“P|�¸õ(ñÙ9, R™ãc'WGèì\<5 ,Ö¢ 044X\µjÓ`:�ZÙÔÔˆÁï÷ÑÔÔIJ%UÄÎA¯Ð@Û`�y=ÏÜããÁ~É4ÜrÕå\uÓ Ìg‚>‹®æ*úz{éëëgÿþc5×^«ê®ë¨‹*àš¦qVðyZËåŠÔÔTÓÐØÌŠ%ÕÜ2X_-èJEŸ'DåL€'@e×Püø�{¸šš–® Ü|ã-üägßäg£ÎTß§sDêÀçâ*~Fâyp-dPWW£µ´tv@XT’ÉÄÈÂB2nÛ¶L�ñÊW¬ZÞCm0‹`ç�/M‹5]Å'ú]bébÊemêr^¼&ÆsŸötL¥ž+ÿ~üësy¼™©–û£tn(o“eÏòW1:gáÚ%4M¥¦&L}}K P ø]€'nÍ”S✬ç X¸@sS#K;‚`—ÀwɱØ'ÉkHò~ABñ 9)v¥Æ«÷ÖñˆG<…©hž_ÿüüôÚk¹ÅÙ@nÉ}©j_Zþy<1 V36›×Âu!®¢±±¹R[lÇ0̳2ÈJ°Ü jëX½¤ Ì,¨^Ð*)qÙ!T\=H}q„û–~Éëµ�í{Èñ“øÙO¾Î¯��çLÝe¨ËïEUS²Üîút\5®¦ ùQìé}䊆,› BÈêPE€E‚^•XIÓ€ë àâ…XÓ[ ¼C‘z1V‘�NÑÊin{ê ­äÉ�zÁp'7Ýt+W]÷;¥ª˜ëxZýlËBÉLRkg ÚjÔ"]­:=u:ËjMº„¦‡ä. ‹¤²>ñûƒ †Q þ³8$<À²,Ûg6š$ lÞ°õ§'qT?¨6håt×Õý¨¬J^ΓV—xÈCžF&§ñ»ßýŽoü Cù Ý�lí*Ò¦­Z¡­6HKC-ÍõÝ4Ô†Ñ4ThL ²ÙŽcÊà1ÜmïƒdâÂWþšÑ‘ôõ-gIÏ �j†Çg…™DÙ:ŠÌ0%9q_¤ñÞ6¹x›çÚk;û÷߿ǫ1/êxÍk>q¿õë—?<™œE>@[]=N>Ïý#~¶øƒyï»Iijõ‰wgùÞsíÕWñȇ<ˆþ5Û9&‚Ü•Wý’|nN,ƒGÐÜÜFmm�ÜÊÉÞ+6äþ.ûétY~Ëå ¢ŸÖN;‰DÒž™‰‰¹|"OŸ9shxhèôpˆÖ¢ ðæ7^õùÔ7íØ±F½õÖcÈ@ØQBµL—õÑòä§³˜}ñ ¹å«ßâ|,ɃŸò "U�ìß”nø ‘H€‡?üQ¬Y³]÷“Je±œ :'Hæ‘$…°v4-ˆd^äYÑfR©¹………ÄB2Ÿ/òiÓ,- LÃ@ °M€B¡¸cÛ¶þ»Ô×G¨«¯a|&ÉØÑCæb4=é1±¡uÙüÈG‘øþˆ�ü!´ÁS<þþ÷fÝ“Ÿ†[ß@îÜ9Jñ˜ .¥8;K ®Žõcc#C ßp-Kîv!”xͦ ·mÞðeoý¦¹®E å÷6‡ªÆ]ÃöU}Üõ¥fËòñ8A¾�•ãüÔ4ù™ ³s(.¬‰F9 Ÿ!ÕÞÉö=œ5ýd»7�ÞG”ØÎyîîü[ ðê¦&µè¸/qNìÓÒÚ`à^/y>½�{"†«�¦(ˆ³’ü,…™i2ƒB ÑZ¹X.:V¹F ÑÐÐ@{k}è#•ãÇ>Zð{ æ­{>ûÙŸJ¥b­ã¸}¶m.·KT¢ÏÝtÓ•7_sͯçëŸ&ÀËÖ ëÃMNñ~—Ý{Û^ö*ü+úÉOŒQšœÄã”䚟ä''IŸ8†KPð©ÌƒŠFдHXËW°LxL]mù|Ž•+Wq¯{=¨kåÊ™7nÚ´ó°(¯7èz Çu­îpuU›ª*AŸ,[¶C$R3¾uë]~?99úíï}ï ‡½¨ï.ÚWb/««¾RÌ¿gsKmÃ}^ú"Ú÷TJùEau#ŘóÈÇãeËŸ:‰9;ÏH¸†�†ŒæVlMÇÊf°òYª7ïfí=HCm=Þ;4UÅqlTÑútŽ÷±�êó£éATÇ6qK9t äÁkz&.œnòúéé‰/~ÿû_¸Ò  Î�ö•Ø+"‘Ëq?Öh{ïËö°ã­ïÄ·j-…ÑÌ™)Œx¬lyÑJŠÑrƒ˜‰yÎUÕr¤½· m«×S_߀Ћ9'¯úXº|µ÷˜,§ûÑA|þ@™°/H¨:‚ ƒ¢¡jrNÃ0]2éâ“8ùU…|®ÀèØ¸=>>rõÐÐÀ‡¯¼òç·yÀý‡xE8r/×0>ßV—?HX½ó…¯Ä2-J£C˜‚°)ˆ‹Ö v’|”üМd’s�0›ºßã~,Û~Úšš‘t¿ùQT™ù ’aªQUÓŔ޿÷¡*`y•qËÛp¼o%$„9ægFð jÃfçæ9yòTvxxðË?þñW?ÌÖß%À«ªª_¦•Šïß»ª#xÏŒÀÞûcLN`NO`ýÑêRÏòùÂØ(Nj�óþ0GZzi~ëýïÏŠ¥ò�±$© ’>|BŸV&gI’f…`å•@yÎq.{BH(åùtr�tô­5.òT¸oߎ;rý•WþìÅâ|p(þM¼2z‘¯Xøôý×vqÙW¿ [vS:? ˆG±¤Õ%ùxã"ù˜ ?1�›œ'®¸6ÒLqÓ^6=ìi<ü¡—ÑÔ¨â¸`žeðP!ˆ7¶+De[{Þpqì ¦*àÓËíÜÐQjCµuuüêW¿BÜôÛßýF€Ò_%À+üþ·ÕÆÛrß�ÊÖO|·«säÖ\¬BÞ[÷F¬L¾8=�+,Wtn5â¾{=�öÖ.JE—êH˜%K:hïl"XåöÀ”–·*DœK¬m9ioŒJ¥(ˆØèª�QÈQʧÁ1(K†Egg™L–ïÿÇî•Wþæ‡ßôq`Rp·ïP€7êºRpÜO´9ÖKýôG²ôŸÂ‘Q[¬w�<žå}>&�Š6 Ù,QÕÏ ÁFì�÷`Ù}ʪ«Ñ}²Z\È�E Í磶¾†®îN›@�’ù§ÞàR!«k€+a�m�M'ÉeRÈ¢K6“Gž[Zêhni@AÅð#·I×-—ÆNž<ç>õ¹©Ÿÿü›Ïö î w(À+õ“]Šó’ǽü™t¼ýÃØ™ öÄöEËÏyA¯BéJ±È¤âç:½ŽüÆ�¬øãY³z-®ã òÞÿ#!ÊÒyÙJÕá*:»ÛiioUAuÁ´lä6g%A4�¬4çsyÒ©,† >_ŠV�ã’J�RaŽukj¨©‰ y©ªâ}K¬‰T#øÀ>¶ð³Ÿ}ã­À/ÅÏŒùn7à)Ê[Úq^ò¤W>‹–·~H�žÅšžÀ™“Vü±�®�bÆÄ\"�Ï4тܤո´à”n«Õ ãSõA=HWW§°B•÷‰«ÒFªNuuH´*¦i25>Ij~ŽHM5¦aa‰¹RÉDÞ—Þ2<4Žü²µ½C�—–Õ±m)”�k§iZQ�¼ï‰ìU‹üÈþ™3çÄø©{úôÑ €(·›½FQqÝ_<íiÑz>øyì…$öÌöl¬,ÄEË{ä¥Ë[²nÙ (Uܨ†�S©3‡³Ù}š¦Mô÷o^ÕÕÕ·{Ù²•=«V­ò­^½’ææf$)ï{`)†´”†a”_¦x�߯{C9éIÞ7H º®K ˯͑¿#+F�€œÓÈçóœ??,+Ëâˆ}]æäɃgGGÏÝ\í-�äŸðE‰pø‘Û–¯Øù�ï!«·ÖdÙí¥õMÏÌXc6ŠŒúN.‹ãÀQ'ÄÍTY×§SÇOçr7û€SžÒ=B„===ËïÕÛ»|íš5kÂ6¬géÒ^¹6åR(“óÜUŠ!å¶"�êõUdßMŠ  "—’\&££Ââƒ=zÒ9qâxrllhllìÜIÃ(�¼�%ˆ î–*W½" ¬ØòÔ'ãúØÃçpiIÞ–­t™ðH«'fQ‹Eä¡ç7Ä~Û_¸6=x¤XÜH•Q`lrrä¬Àå§N\òäÊûÞ|sïöµk×·õ÷¯RÖ¯_CWW’ŒeYžW ‰zbhÒÚY²*½E–¿§¦fcxxœ��Asddx!�ŠÅb“#‰Dl˜JIì0 ¤û6‡!¹¼)ˆÞÒ‚5>Œ�¾(€$/!ÈÛÉytÃ`�k­ %yuf~ÿœinöž¥RóJWDÓé…ó'OØ'Ð;0pü^MM»Ä'l+·lÙÞµkýý+ˆDÂÒÒ’°h%QÓ´�o›b±8ÓÓ1A:Êøø¤;::nÄã3éÙÙxraa.šHD'²Ùô4¦Ÿ�7M#é[’T�ò €uÛçøË§ÁÈSõs[«´'=i‰éŠfÑ@îÃi ÎT4Ž”œø-ÅÂá˜eóÈõ\­Äßé@hº€N 0Ìxniñyýå?›+zmÖsEÕ›Ëz·ÿÿ§³ÿà¿ãúßMìÛ8IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/status-bad.png0000644000175100017510000000354015224505336024622 0ustar runnerrunner‰PNG  IHDR@@ªiqÞ'IDATxåÛläHÆñÄ'Ö‰�O¬c-†‡™™™™™³-Sh)pKCÇ Ã�Ú¿¥gɪ$ãî8qèI?uËêöÔûb»Ú0o[¥~¯¾Mú¡¤¯ãó�¸ gï!ü)à"¯·¤7%Õã’ú±Þ¶MýR_ ‘Ÿà§Žæy}r‘÷1˯‹ì8ïOHúcŸpB}Vï¿$3Ðß1àoòú6Ód–Ëšœ´ï=ÒjIWú$€ýJ\ïx›tƒ›†ÏgÑP’å”sÒI·pX‰êǘÇ`¾q§A¿Å{ðn{ ‰ºf®šËø3þLÝ�‘t²×œR¯ê+·¥mNú^×ÁÙ‘Ã'ð^õ®.á".à/¸�‚~ìmÒ=’ÎgÀe]ßî”ãS®›Æ?ƒÏâ�êÛºŽ6¼„݇ðwüRÒ“YpZYÕýXƒwû� ŸÃ»Ô¿uÍhÇíHæËŠ$mV_Ö~éÇ¥Å'%wÊœÄqÔ£gSÖ‚Õ�ZÔ  (g¬¿aÌ}Öüéè1køDÄó8;À[5¨F%ÊQƘû$„ƒ¤ü#GpÇp/áì ñ(ÆE¨@J»’Ô>éþý’;ˆC–öép“dš1=º %ô ÞÔnéÛ{¤ë„àÂNâ5œ¤Ú1 •ˆp½˜^”Mí’¾²Cú!¸=؇ChËp ozÎúúq=MÙ JPD/¼~E™Ö6éQp;†Ð’áÞÀ«h5¯Ú²³¡ÖSïmÅ(¤'eR›¥o‘Ü6l·žÈbÐmxÛ ïYfƒOq=û£»ŠPHo±SÞF©•\ÂÉ,ý(æâcf®-³Á§³³4º €Þî85®“r7Hn#6c+Z³ô,|?üá¼gYtð)¬'Ô„Šèn€|zì©ù÷ã�pag4¿fÍš@üà“®'ƆHEȧG¼_~­�jWKn Öa:4ÿÿÿÿ?�Ñà“¯§g¯ *º �^»  a¥äÂJؼUìà“¯'>„]‘l7hP´K_X&u‚[i¼3?¿Š'1·çAǾÕXóY¯g®�áÕ˜ß í(Cä8ÐÉë¢.•Ü2,Çž ~ ´b>Öàã?u†÷,Ë~=³1´Æ˜ ÀvÞZ-”N‚[b!<’,€ŒC¨6ñÍ'à 9œ–¨©ÒH×Á…!¼‚³Éw�Ø> !þ»‰vó‚w ̧çbz×<é§p °p6áô—M#½m>«éÐL‰‘Gïš-UΑÜ<ÌÇQœM1„Tš7›½ré]3¤ù„àÂÀÙ”B¸téR •æ¢30_°s¦­xÃÙCH¥ysÆ �Þ5MªŸ.¹0„çp6ÕRhÞ<Þu*¬×T©cš]Nš^ëKBVs{ìo†¤Í›f/€|z×déS$7Ó“^ò²¶¢5Þ4gû{ìq!:MÖØºZ“4o^íºüïíÞ–‘W㥎‰’›„Éxq¤íã¤zBp0ÏŒ´ƒàXi'Ü8 ááa> ùÓ`­4´äÂŽçB^˜/¨e÷ׯ`×0þ)¼É(ˆþ®–~ZcwXGaÞ8*ˆž §„UÒµêH/ÏÓao°ÓaQÒiBpÕ¨ÁÑáyAÄöÿè«r©°Ân'UaÑ0¼$6§ë À[+Þ}¡Lê$W�j´£‹¢m(‰nþþEQQ| �\ÂîatY|‡·ùçÒ«ü*–jƒ”JQ†j¼2LnŒTt=ú×ʯàvú#A¸0ˆ ÃàÖØZï¯ïßóCÈ >†P†¦!|s´Ñ¿ 䪧 nóÁÖBûR1 áÛã ý¿~ÜíqQyÒ�óí a{†à{üæô¦L*Wz4ϾT`+zx="ó0Š»6ÿ¨2­é+„ð·håhI5¢Âk zÊö¡àoó¥ëá Ơ g©6Œ‹4o\Ç·Õ›º—Âå b aó�Ý5�Ó‰Ôÿ–sÝ-}�& ìi^¯ÁõÒ5[Ga°N Å ®½ß#ý”&*1;iªž×üÏtز�ö™Êà;ÁwÕÏõÿäÊdˆm”IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/refresh.png0000644000175100017510000001175715224505336024222 0ustar runnerrunner‰PNG  IHDR@@ªiqÞ¶IDATxí›p\Ƕ®¿Õ†D#4ÇÎI…©rƒ‡™™™™á233ß{0t˜™^˜Ù'>NÙ²%[š‘f4°w÷zÖt%³Ë’m•éáWõ×Ú=ÆÿïÕ i†ÿ×Uå x øj¾ùÐ7ã‡w>¼ýWÞÞ`%«£LÈÑEžú'O->Ðxàùå¨|IO®gm9_^9X)� —†KÆ>zÝG»ë»«Ó�é©J³²³Ú¬nÛ[o-�~á†wß0w4ƒÞÏÑ`ЈyqA ÏŒ/ë+Žôް×0#={kÏ0 ãrOA¨Ô+LÍM1U›bwmwGSõ)&k“í=Í=7Ôµ~­sîj`âÈðaŽ$—Ñà·±\$N¾|CÅ!®8å ®8õ úŠ}ÄQL…�EØÔ’&)6±¤6¥Z¯ò“~Â÷îý;çvRmUQÔr¿|™CàYW>«pFùŒ S�©ûÞÇ‘àQùSIxº*äÈñ‚s^ÀSÎx «FVÑ�;æw�¸„åHÀÊâJúM?Ó3ÓüäþŸð™>ÃLkÐ�ÿ¡±~¸žeÒ»®tñïýÊï笱³‹W?xõo¿q$:`EàÌ�ðJ¬†¹>þ¬Orê†S™N¦™lL¢(‡ËHa„‘x„û·ÞÇó�˜¬O‚•X¿ôv‚ØÌ°%ó[üê¿>õß)FE¾ô‹ëÖHü±€Cäù´øµôå$ÇûŸüÎ>ñ¶Ío£e[ "±¡´�ÍÛ6ó'ßú#fÚ3H@["}?ðw,�”ÍïhÀ'ã0æóϺš«y×l¾ @$ÿ��Cà7HùU æ5翞§œùT¶7·SOê a��¥�|õÖ/óé›þ ÐXþ®•OÞ 8<ƒø÷4Ô�GaÄ\úÇôõöóÖï¾íÀëX>�Ï•âÏëžk‚�_þoÒ3ÔËÔüÿ+XXrõ™:ôíߣE "ý~!—¾˜mIüýh†üÖe¿Ï¼iPΕùÈÏÞ‡Ãu(¾«È2Y%­ä;jå´�Þ~õ%¿É4Ó4Òÿ+‰ƒ˜Õñjþô;Ķ٭Êf~$†·„Qȧ.ýMêfž¶m3Zå7oú)i7ÞÍrÈõÔâ[\ÊN\}o}î;olÇ©ã˜!x4;öˆ'ö�À§þŸÜ´ãz Œ">vÙ¯Ro`¸8ÌÜò¤A&€Þ׿X_sm}æÆ•yà ÞÊöúöckz™¬í[Ëü\�ñ™G9mÍéŒ7Æ7�ÂPaˆ¿¼ëIƒ@–u6†?tužÙ[èååO{ �Ì>Âá=QI;!Mý¥gaõ÷öcB³øØ”凲uf+F å¡!6W7“¥FdTÒå}-FòªtF?hÄð¦ç½�‰æÄ!�ëùšõ&ÛvlcûŽíÜõ‹»˜ḵ/…\�þþ~N9þ6­ßD©·„ÅÌøb£i;;~œ$I0€¨tó}S‰ý0ÖªØ-ÚÖÒ«ŸÿÌ`Hb–�o¹É�“üàÆ°eûŠàe¼FG9íäÓ8é„“@öÓÊbtéÚ—ë㪭ÿNbÚÝ= üÊûá ¶¢/¹àì‹8íÜ3˜kͱ\ÊùrçxúêO¾Êøä8Ë!kœ0]•ûÊœuÚYlX·dŸ�P½– # 7îå+Û>K´º¬xkKpjs{z§X¾æµo¢âª,“ÎQsÃm7ðÓÛÊþ'ã ØÀጢ‘B@Wa&�±á1®8ï rQ.kö€¦qÝ×KQ‰ï$hv7Á´jY‚¿ÓD /¸˜™¤‚UËÁ„ÑÂ(W}ã*™X¼QÖ&{Õ6˜D@÷9Û\¨¤}–¤Wq9‡¢®ú.¾~ã×¹òœ+é)ô°=°› " �!coêg®l?Úþ^D,Ï{åK©¦U–Ãh~”ûÒ¿QoÔɨ�k…„­¬¢ÄЖXª3#Šs)e×Ö,91@ ¤ý–æªWT0€àä—œ~ C=CtpK^üz!(pCå;¤¦Õí­&ìÃï‘"g^tÓÍi]ÖfwÕ·¯Zd>ïB ­µ€¨�FÃïJ‰OOŽ×¿¤dyfå–žç%u}]2mŸÏ…anKÈüš„Öš �Ór-~þÐÏyÒéO"b�%–€[\Û´ EPÉtÀª·¬"C±¹­Rqó=õeÏ¡I“ƒÑ—ïãú›¯ç¡ñ‡ÈÒãbâ4DÅd<�_ü˜e ©ž›T“ÏÛšn’Ph�¥ÔOnƒÉµ°²%ço8?kri©¯±ÄÜ7=.jw;€ÌWp¯¦M´bÕ*ê¶~Ы®ˆ°uëÖEæ{ɑӵù•á§W–æÞ¤,Ÿ[n={þĽñ§í)÷Ξ(J*–öZ êÍã`b~‚­Õ­¬é_ã�Ê~öç¥(Bö7†4lö÷¿„úW”ýeâ ”‚×ßu=YŠ&¢H„*®°*z>ðU ¼? Oµû”°ah—,Øn¤pïÔ½,ÜRKai‘ñ¥'ˆÍ°ƒNÆŒÞPº«ô ÷‘&)ˆáÁ‡ĺn€†^“Gœ’?íÍ:FƒÏµw§O‘Hhž–B�7�U¤séœ?OÆ4¶[­ZHA„ÅWáõ»žSk%¥ž¾~lè°©å@pß/ï#K�ÄS¶Ç½Ãoä0Xý@ø…Ödú `ú™ó¸ êM'^9›cma-c…1Œ5$i²¯ù®¬—XASÐ¥°óz±Kù�"­v‹ƒ±kjY‚À�·!NÔÆÃá+€”Cd¢â®jN$/–P˜ye{Š{ÜxœÆ¬ŽV3�bƒKκ¬ùnµ@Ú5?½g¢6¢n‰ÚºZ­âŒ’Úô ›ßÔž) {c‹m€S%*šŸ :$¶o·ŸyÌ|õ ìé ¢$b+5Þ¸k;œq xeM[ß¡¤0S™aff†ÉÝ“�†uƒD�É`é�àV8 „†$I8 fê3{óÏ…(@Q®áQxI¾9ûŠvl©|´�;[ Ó�17ưFÁ&Ö/OoÚËBLŒÁPkÔ¨ÍÖ¨T+T*Hðr^Î*6ÑLíÇ<é¨sŠBš¦ˆ¹ù9¼ùna"h¨®mãÿæ8Ær=Ÿ®­¯óùu7ã®T‚v@¹]F[Êîdw'i ¦e:UÚþÙ5]ççÕjZ@_S/¬W„�TBüe7€ôñÊê¼k-K"@ ×È€Ap(&�0Ç!ÐnÌ ¬ŽrqĺO1õgUh™9Z€d¾(_ý �0hŠHb"0 &¤ Ö*Æ¿Ú  I‡ëS ñ;êRæC_ÛÒ΀¤RáÐÙ²m¢ú¡Ù]íw·ö8UÐC@C¦œ*±„4IÁdŒÆ¾fEØ­6VòÃvÖI.°¯?Zml†Å0îòÆÕT�m;EP@Å8% #É‹šÜ‚€Ø³ŒtÂâ(H²Ø$Ñ~B[RÊkŠ4H°©{ÁÑ ÀÎi�ÁûÏËÙæzk¥&Ž9uu2+ÎÕÔ¹ºÓ4±j�:— ´R5± 9oó{€Gæ ¦ "£‚Ù"Ж‹Ì@øbàjŽ0nNÇÊ�]ßL“ÈZ7 Öõ©sª©ªºÐ6ÖÀåZ¡+Ìgšâ¤-ÎXqÖª6’pº(“×jÀÇB ²æ—"öU0F0"ÌJ“‘KÌ>Ò×t÷M}˜å!}¬5S2T8%Hª9M�J¤b$U»B創¶nÚ–ÁbIpcùÌàUÍÐe ÈÂÔä¤Ú9Û5ë g+Þ¼Áˆ—CC’I‡E5îàÓÂQKvº †‡Kqýœ4¶¡F" â§Çû 0 ÈH‘‘|If§[¢!n©0ÃæÒV%=al¤‡J®¹Ä¬û*ácæÅËx55eíÊ2µé’˜�¦'˜nå0°#io{›}EO!Ÿ/_œ�gËÍ!|ÂŽ ð’Ñ cÞÔ¢™­· ' Ø%ׯUôù£}=ìî©ï·õ#1]óâ%@9Ö–û¨Í´…Ï’HÎéÕkº©/ŸI©­o�Ûw÷äs«/ïËï\UˉHN�‘X ˆ:U2_™ˆ˜lCÅ’I÷Xi¥Ö�£¹ßH׺LÓ|¸Æa2`Ib»(c¤€7O° ³ ƒÃaŠkÊÌδĤrR:oÞ“æù0Á2xhºÖS™¨}ÊNº7ì5_Þpi¹°cÕl~Á<â!‰éîTBàŸ1 ¡ Jp\i@v>\3ˆFÁ²€ FÌ“çö´×Ÿ°rˆÉ|-k :f}Ùü˜Àx%Î’ä-'­¡^mcSÍ›fðsr…�Nçƒ @É0“„åJÃ�QÝÝ~…Ýiÿ‘„SÖ­(®º°/¿cÅloY½¡ß÷„ràNvf²§n¬8#"{Õ1,ÀBQxüYðˆ"‚ x¤7ÎI±3¹§&ÄÌ»î$ý™ÖyGÞ†aa,¢¶0ÆìwæÃn‹k`@ š´¨š&ÚïÚÐÃèÆkOì§ÿ„r<ÌŽ¶˜Ø«b³sw1HǼÙ+o#t=¨—7¯‚*UôÌ¡•ÜuëNœQ(˜{€örß,½Mô¿Ú¹çMWž²‰]¦†<¶Ó Ùã�Àøjº3ïÃéæ�ÿƒÌ›”º­âŒ¢¢`é6°¨`œ±D +FcÕˆ àœÃ§ Œ*NÑÀ‰ª€ŠèñƒlÿÅ "¶5– `¬åG4xÛžÉFá‚õë—J·ý33¼t,î ÁEÚ‘]P¨~*6Pta¼P x©¨Ðiÿ@�1""€*(ˆâÁ�ýë}¹+“^îx`Úäø! ËÀӦ̭zúÒ´æ‚×�0©µ%Íg.‚ .ö¦m6€®¼ùŽ|#�Bƒ àÍ+hƽøx„ ×éwòj€ß<Е<àÀla�ÚlµõÔa)R)PÕ&¦k¸û˜EÆCooÚùÙÏÈ…êkàp!>CWt$¢ˆì3ó¨ú`.[¯?ùÙV™o%JÀ»‰œèã¸]»kg�>8†öAC²æ³Ï^BøXÛÇÞ¼ »!¸0c>t™èš§k�ˆ Æ8 ѬyPA¸tlƒþô§[™œ©+†m”ø ‡ü�¯p:WnÝ1sÜåU”ËyflsIóþY�Àà2kÝFÎö5Ó]©¼yÿŒ™ÓM£bP },ƒèe+7ê·¿¿™�{jJÈ#Xþ °„€år ×¢\´uçôú‘ ÄikV0‘ÌúvÏܽ .dñ†9Ò}Bp Ên€Þ¼G@Ô›Å?;ãLgh.Œ¸|t£~ñ›÷éät �ó›@pä€ßá¿8�¡‰™¹ó³)Oõá‰Jø´ãO` X`OR÷í/ èÞg½ï£l84| Hfæ3r^9.ÙÈÖ»§¹î‡÷’X§ÿI��­cñ©±'àþJ½qÕ¿~ç–µ'®æÉçœÀvfy´]A…n;‹¢FqÆW Ô² ØuªŠv ©†ŽŸ@qV1�ý9Qâ(àÂÞu»Eþë ·™Ý³uL£¼øÁ±þØÜ Ô8‰ïßüèî�îUßy›Öð+g®g*®±Y§H¤»®1ø ćáÅ.(r�gc0ˆSL hÆx_”ç’ÜzZ�X®½æ&gj €ðBÞìþ_õ¹Áð{4ù'B~õæÇß~ËæñèäU£\ô„µôŽå™6óÜiv2Ã< :a¤±óïL½aA8!æt»‚R3bêÑyþãÛ·2Wo�à€ë€ßîä0^Ê‘dÂ'€çƒk†û¹äŒõ�o›K \^qEe®Ðdw±Ž‰ å¤@©4Ì^ Rî½s’[ïg®Ñ�¾€åw�û8B—s4ä„ç"�0Žcz 1ý%k™©5˜o%,ÁNà;ß¾ Lq„žÇ±àL”sVâXÙ©<®ØÕ‘ì•ëÔí~̹ Pþ?ÿŸ£Æÿ_+š2æ�ã¡IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/status-running.png0000644000175100017510000000455015224505336025556 0ustar runnerrunner‰PNG  IHDR@@ªiqÞ /IDATxÚå›tkY†÷ضmÛ¶mÛªô¢gÛ-al ImK©•ÔL;{þ¹{Í}é™[!˜¾½Öß6¸½çÿÎ>NȧqoCa•§Sxù³Q¶úœ"J3(²¤–"‹ÝYôEþ#7Eå×RT^Eç}NÑ9Ë)*ûYŠÉ:�îû`ú_ÅõPhͳ^õ%…WvPxS¸“)¢*ƒJ bM€À@ùPSt.”eCY4-ãKŠÉøÈ”aã­)¬þZ «ý€Âj<SX5T B„1€†Á8” e PtÆ�4-í>²ýº-8$ÅCž¤Ð†R ­‡é:¨– @N#£Ì”Î¥–RLÊ“�k"!î›)¤)�B›˜B¡‚ÏÏifZÜ䲇i­‡iÃÓÆ¿Dø›Öö1­êbZÒÂ4ï·RtÞf1† ”<2%ÝL~‹¨–½è-÷z` qA €P‡¦ãõ¥Ý0ƒ›x<0Ë[™f9aŒ@ÉLæ¤(:u?òi„º/¥·Zké­o†‘ø=¯ƒiý€˜L­ïgZȦÜ!! jñûR™oyÆ{!&@4¿kµ=άXT³™F D™"'Ù¼kº˜m†y¿uV´ëµRã~Õº>4�²ÁL:&sÂüI2ß4“�â’êîÍ €=R˜@j™ †ÓÕ,€!~!M(Â"¥ƒ� Š@XÕ8ÓªÖ ùY²Ô,¦¸ÈqÖ|ý P¿Ö»$"›•”­ëe²åeÁYân£1Exý1Ï[ Cš m ¢h7n$æƒR<€�=€@h%“ã]ðV˜Äü¨Mf BTRMÒ>¨µ¾2Õ,€â~Ó¼�U/fp€À:„pXåùæk»™Z!5H«;a8Y€¿c_ a#²dÌÝÝÚôU ˆuŽòÆLÀL‡žÎñ{`!,­×xApSdòÃÔ¾Ó,˜ÒŽ�¼xá…ø€dºàaÀ«„9EYg²öÀMX¹A!…_Û?f+V¬à•+Wò�wÞÉ;î±/Ó½ó¥—ö{€{Z’½@±-õç^=Ù[²Tu²aAÛ¨n¤Ø´i÷ôôðüÁ6›�Ï=÷\ÞêÀã™ÞøÚÿW “ži�þ¥é²NU)KÕqø7\.ôÑGüÒK/ñAÄtú­LóJý@Ö–T5 2•%nÑYÀ"�@ó[Çt#€ÅÅżnÝ:­Yì´û^L·[eÔð„EÕ*&›ãL/ų´ÝHvjÊeI;i$úúúØápðÌ™3ù /ä­ö9œéé÷ý3A2'`qÌ"="‹²5U"šQ/N5Ün7üñÇüÆoðá‡ÂI×àž9> ‘›@vÒ–³+6%ûds²X´¬ÃgÔf±aÃ~衇x×=Ð,®y�iU›o,w±Ð!x(¾QDáu„ÍH@`�P"‡ÏHx<NHHàyóæñe—]Æ[ï}¨4 é€'Oú š�ý:¢èü× D€`­” | @�¶¶6þæ›o8$$„�:ê(¦®�9ýdB˜™=€Éña z¥lEç „y�� ‡ÓéäwÞyGk»í)Í‚V¶L€Nµ#\AØÿKZ@b``€SRRxÑ¢E|ýõ×ó¶{ìÏôà24‹� îÕ{€ß@N °aeWÀ¨ÑÞÞ®5‹¨¨(>ñÄ™Ž<—)&~üå[Õ¦ˆÍ€l' 0 ˆÖö€•••üÞ{ïñÓO?Í{ï³/ÓE�1-mÇ®Q�  ²\€À€ ÚÐTÔf±dÉ­Yl·Ç~Ò,ÆRÞ @á´¥X‡€á'˜¨ÑÙÙÉß~û-ÇÄÄðÉ'ŸÌtÄÙ(wì¨Êo*€^ Ä:„ÿ€s& Ã1 @Y[bÈpÎÙtk{·°NpZZ¶´ªcK³ å{@`H ,uM݉ÐRƒ‰¬”ÃÅT�0¿zêN…–L…§%¿¬C˜Q0…CY‹!SÒµ€À¢4éYý·Öv‰æÎ�ëûå°%AÌëâ®%²%ï ½„SUĊ橸!¢v€}öý.¤…)Ñ „9%þß›™ëã-±µýÿIz˜âgCÁŒ,Xï™*›¢âEM‹}é“tªxAXT3…¶Å«Ôô‡þ<ƒ$ô,H‚¬©2îNÂÁȇ~؃k² •ÔÐŽ‹ð&€Ø aqÍ„ŽÆ~ùå¶Z­^GcßàhL«}¥÷�}ƒÔÐŽ�q|,odN’ùó„GÈq8jMd^=U³À±Â�ÂXh‹–}÷Û�éÂG|<ž¯¤¾Vûf2lñ»ãM.‚€�9ôÐag0Eþ(ã¢%µÿM}xÓ<û3dvÈB†�5£ûˆÌÃ+ƒà#2(«EI}ÑÓ£û�”Ùþ½@ðaKQúƒ ʈ²˜wü¢yU˜~? ™Ð †0#µÛ¬æ¥lÓÓ�Ì7kžÆ87ƒ<‹W6LOåîà¬ù†æPþ[h\aù3b�à–¬ö oó(“‘y&“#”&`ƒXáFKkm^Ž», Ææ-Žy4á‘‚˜“'途ǽã“k^…ðÔeŠ›.®dÚØï‡Ž®_¦·ÖCãPÌ?G> ³ã"¯Ò¨0¦Ç3-©ôÅH!SéE•2µêq†ñ*­Œ> Û¯{’õÏõ0ý×� ¬(ÌÜ\¦M™ɵ˛$Õ-†µ-²hú€¢ö!¿…Íq= g)ŒaÌJgZXŠ«ŽiU« £0·ùksynU«¼ga®ÉÓÞFUÓÒ³´²ì£û#0[`Àˆ�ÞRMzK½¶zD+C€C@Øþ¼&?€<�Èð ³ýG\{_ð~©:ú�ýPÀ§°åô)~·Bqü8ÇÕóÌVE9ìøåogï˜{ïø£&;egŒwÅŸ£ÇS¦;¢nú-uêÊ/¿×™é¹½ÿT7‡fz +[¶¬&ݧI(ô}^L·éÚÄ$·�K�vþú׌�´³îa9�‚s -ü³™�W¿#�i­KbýV%²þ«ÙÀ5IlÄÆ¦»-�MÝ�^hì‘zÇd{ôþIK"¢úNv±¯^§a/(®ò.*¢<7×fš=´F-òÝáæ'Û7#<ï‘mP.›ê‘ÉÆlNaCÖ%²AkÙ€5 lÀêÖmik9ç_Ö‚ ž·šÍÚÎ�a]é³Á¤ ‰;Ó M˜€kÉÌ8™:g¿?„[��¢ˆ ÁóËdr×1ÙVÛ}ç–:�ZôæÁ²âÛ_l5Út–ãtÙ÷ˆüÂ)»3ØÐõœLÂæIæ/��' H`½WÄ3ÍùŠ Xšdp #Ïô‡“—þ]«nZ£i `‰o¼¦¶í²y³CÓÎ:†ä�u€æö¹é ìK¾/�—ËÊxÖqQ¬¨Uk `™Q`°þ'Y«>cÆÓ|B o|øßX» g†¦_B�µ)Y|ÑØ/ <¤/) ;wu•Ðiq¬ðkÄqùÖC¶í×£l¹À î ˜}�¼  I²kÿ¬Gz®›W"UG,xÑÈ_w„¡«Õ숬ë^™R–W"ÓãbćB&’éÿ’}±4Àî?{›ý˜y�yýuœ­ùþ [ñͶñ§Ã,LvŠX¿.¿F×î&½†‚ ‚¢4>)ÀjAë<²Å‘_!ûÝ—Å!ˆR0¤æË’`?ÂÒ„ªkþuF¹ÛÌϾ5Õ#C`¿ôò–ƒG¦‡ç’5@0!$+4M’ & ˆÞöTöÏžËL_¦žƒµ4±ÿoIöI$öqO|”ŠêÔÁãïÔú�›÷E�Gf‰ÕÊ)8û‘®Ãʹh£©ª4jÞºŸ›oL6ü7R¼TäÀì•��ÃÒ``T<¿M?ÏÄ&;�ï#¨Êþÿó€2mƒr˜É² /$Eê,‡Õ;ô9vFpÊ)ŸluÊ[<˜LŸƒ'™ÇäÁ"XkN¡yì8%NÒ¸}ÿ ¥ÎûpºFû±%ØÇƒàšþ9ÌfÓ7ßòICÕô·æ ñ†3Ã2¯Qš‰!èADöEæEð.²ðÏq­-…1”.‹ÉSÜ�«Œ¸FIäØ`ì“ÅœvÿW­ZÕ®èg¨ª€ÚTüØÍ ϾC•OoÕö®<¤Ô '²³íO÷2óÍbÿR¾ðTð}¤Ð(½y¿@L|JgŸ+sQHàÜ|¢3)!WåàP“9®3ÃsLØ‘àBÄWô”û=�ÏQW8PÍ�zôz±ÈæÚ}¶ä«ýønV‘}Yû�ÿÅ&Œ‹OÜžzMÛ TO¦óÜg„ç<O �<%~�/W5âCðÙPOÝdy¶'Ž£ï²…_ìGÜ!°XÄ´7V%ö¥¢Ì ð•í«×¬­Rb•“ aƳ�g„å܇\¥ æK zKžù=^¤ûÙQ„Î=uK,”ðJ)b^\÷Û«Ç>V-Š+YÌÕ76¿^ÓÖ`*Ç*‚¬g†eÝF è/dz*=<‰¾ =âºqØ~̺P,àe�¸A “\|ØF· Pèû]Eö— ì¯zÖ}‚™Pg†otzý†Mz£³¥ªjô­?Õ=8ýò4ÏL0yñ ÇÁK;s¨ tåö³œÿàù;Ì%,¯h» …Ok‰ýùÅÙï¢$òK ä/ÈcœvýúOÅ å;«º `Tk×Sk¤[@Ò1hP`_LvÔ z˜4ÖÐ"‹H8ͤqçÁ<` L£à�(d¿³Èþ²ÒÙGæŠô›6›¿þ{êD•kÖ©ßËÑã¯dôþqS\½ '¬õHW1yLZw[*K:|�I#ƒ:@¨¡ 0/6;•³/SÈ>Š7d•ÓiËÎd‘Ï6Þ)¯N/@Ó|ÝçŸ;‡æÁ””§¹Ë1/±ð”ñqöÐó;tá“Æ'‰gänûö;*a¿ŸÀ>îAklvdî}m«®êö ©F“fl]åžûû~}TËô8óŠÁ÷!ÚT]FﻂåNîû‹i½(°^û�Ôb? ý ù 0'4ít�!zºêöqa­¾:&nAi— <3_,è ¦/60‹kÆ�¦�Ù£C„e«=ÿüU°‹Eë@þïêñû¿ÕªUïö"%Uª×ªÛËfÇïñvA¹Ð.4®^ИçMM´³ ‰‹TTŠ\Sz³SèöHì÷)Å÷Á¾Å™YQùó=7ñCW^d/°Ù”¹ž[ÜÂò Ð�ÖUMs»ð*OÀ„¶6”�I£LEÝ�> z~`Kž’fgéì¯!ð|éÃÆ� AsÃ3.ôÓ6škÍ_-7è1bÊ$gÿäÓ¦>Ù¤q%AO¨îÀ|À!û¸ë½Tò¢ñ‘rä:CÕ‰ÿƒJ4;žD•Èö±y㾇9ïøéçÊ•+w�ù¿Ìvx{ÓÕŸ}†Õ`ÄÆd0 dzÅÐ…Ðq!@ÛZÂ..ØcŸ%�eâÀ¦ÉzÊŽJÕÙ_]’}T€ó¢rïèÚ,vǶž’åOya¤5ÙÎŒzkaVÐ~±Úž'8˜°¦¢�MAøŒ¡É)”À›> pļ’f'|_ûÔÆŸîõgLæ-¤=�²/»3ÜÁpEd„SH^!¾ ó˜¦4ïÔ*ßÑÅûp ¼ÙŸìen>(Öû7õÍÂgJ}¿·öM)s�™sc¬å|×—Þ!«ÃNƒôÆ›ïŠ9„òë: ¢Ò!'< X›µ‡E%œa�1'™•6?+T:û=”D~(•+”ë°å›¯ªT­ ߯ò²ì‹]âæ#í×.³ðI¿«·-M<Ù¥²ˆ+Xå™%žSìxeÍNÅìC°Lì_Î J<Ð{Ĥ íU•�«U©V³ûÄ%‘ŸÑ^~"LÿÅ”€Aêñó]5›�"ûÃi.6�9lndöõq+æò°Æë8[»yçþ# 7þoî—�” ^ølOQÚ«f³Sb À~æ2ï“ü‡+‚½Ê—+×A|¯ç TûAã'lþ#§E u,A<ß#YAÛ` xÌaNTþc‹5Q‘uê7î¥Þ©��ÛÕ74Øüg´w@Æ'Cå xqö¡|§¥À?2[óégu·ê ýþõ+AkâTÃM¿&#&èmOW‡×Â>i/ªIûà<ŸwÏbuD�îû&Á‹Jhتû�18³ké›ñ-h´ÏkP¾êÍNZè8håN‰Ô¼8mή µêÔýMƒ'uj7lÖ{´ó– f»d'É ÑHÅ„¹"DQľâ�®”àðw¬ñ`}Vdþc'�¿’†ºÙU(_N:2_îmÿX“hÕe„¾þÄeŸ~mî•r�bެ!H"¡`A ÿJgûxÉ+v{Ÿp@�›LÝ#”ã3"ò \ýŽèÏ÷ÚÖ²CÏÁ<Ë«ü®ü¸ª,/œêjT©Ö¹÷x(ÂÌ#ᜥ_ÖSl°NÜ™�CVbðk°\¬­ ÿ†‰cOà°5O?µyèä·oÚB?�.ƒ´õøi�|EzçF9ž~ÖÓ¨T¹cÇÁ“¦h»ïÚBGÚcÌvÇŸ¶ôM¿Oio¡%¥¾~9ò�[Ë€†YØRó\ç�ܧ.A·}d-7~ó�ŽÃªù­:÷àÜ×5°þN*¢ŸpÓª5jwoÛwŒÞ@£¹nÚÓwm¡BE­ûögÃõ?Fnø)ÖdÛ­ÿúû)‹Cé€ãªASœ¬Zvê3¬¢†F{äÜÅ*¾ûÀ»Fî«5xg¶1âI{úI\ç²åÊ¡héÄ÷ï›Ã·ù’VMüå‡ôëÑòÂ/H+qÑà WÀ5o ðÇñq|ÿ)mmýž˜§IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/worlds.png0000644000175100017510000001514415224505336024070 0ustar runnerrunner‰PNG  IHDR@@ªiqÞ+IDATxìÖC¼tMwðÿ®súQlÛö$¶íd{ÛÉ,6‡±íIlÛÎëçyï½ÝçT­ ëû½ýû4Žê¢ú쪭޽Öjÿ·×ÿ¯ÿ_õ_ô ßx:Þ|xÒ5T” ¡5Æ X{hUÆ ”vûÞçá³ýZõIßðóyë7yCú×¢YŠˆ„*³áC+U´¢”eážk‘}Ë÷ý”ÿAkÝ®žðÿþ°ãq“РX0TF¸ÕØ·×ò†¯ô¼¶½ûŸ¶Ö?ýó¿ð’¯÷Úd³ñÙ|æs¡ÑCÃÍqxôþ�;wï¤5QM!€2X. 5_ë84„ž(ç=hEp™£ÕʶÅRÔŒ“âPl=�ܾux ü>¬÷ïßPéú¾«‚¨*§T10 ú !#zìÆ½æËW4·WZ•¥æÞH+k+­�m­Ìb›VQÕÎ÷[³4ZÍ»Îûòt¾jÆG;¬� H¢�8î‘êÏ÷�?ù{?�€zÃ÷ùT€¼þÛ¨ëãµÃÂ$…  Fh�>¨Š±GIfò®U!XŠ=aúí(T±V™ç £h0. hæ­™»3p(:­¨Ðƒâ9ïÞñ<ý¡` @•d7z×}”iMŒ”µq,E0:Uô1,�Ó޵춭$"„#愌KãØ¹³pÕ9,dìaC5j0=¤ØÃ�•ãŽb¹Eyd¶B¢EÀÁWNòÚ_ ¾¾Ê~ó�Ü~ã¤0`5kÍ./¬qáü*/>’¥ùEi¨ÅdËM>ô—�óô^&Ka(19í¹ÍÔX‚@"ÈK|þO¼ÇÞ|ÍÕ¨,ójÉ««Ü€p_{ùŠˆ~ÑÈ%Ÿ¤@’Ÿ„5ÿ¿<(‡=T]¾°Ì�§rý•SÄňb¥ÄòRƒµFB7Ñ4[ Iªét�Dy¥é„”g.ÃÔÖ ‚@ðúK§˜Ù9Á5w¼ a�eøq‘§úã|=¯º óÔargÅø#‚Ÿä)<­ä ’cƒ78­Eƒg=—§pýlA&‡Ÿ9HŸÕ|êÝ;­30T¥Z+qß½/³ÚHH2ëìî×ÎQ,˜­ÑW/ÒXk!¬Ÿü”$ŒC²4ã²k·°´Ðâ=¿÷aîú’›¨×Êhc¼ºõh™ !Ü zšYæRØ]¡~…ñå…‡o,¿¹yT†ïwKn 8ÊÌï�ÄM‚—Þ÷Wm`ËÖg_×úʔʭ„nªÙ¶kœ™k0µiŒ�ÛÇÙqÅfúgF‘åKkd"A–)„ Ü驱ïªm|ðOaöÜq(ñléq!AáÅx m{jKãú¾‚ÀS›33ñÈn=� bòÑ×ãQ Iµ§Fÿûû‰ž}‚Ϲ{ÊZ÷Âó -D QÚ¸“¸ïÊM”keº™¡ïõ4¼Îgå|�ŒYCÖÇjÇ�\`ëD�Å•.ÝDÑéf,®uøÖ¯½ž‰‘ ók)ó« VX÷á£8äÓîÞÅ%{&©UKÔëeöìg]Á‹‡yáð‚cˆk/�âÄ…�®FD!a’*áýg¸8× ÙL°ÂÐÖYŽé,ñE°ë–ÏÿééñqÀ£yn€øqÔ ~¨Iâ{þ7¹µís@ –‚tßóúB8tú¡¸ò²i÷òK�„j­È ×ldóæV©Ãe¡V) q‰³=»wŽ»S9»Ôdn¹å>çÂJ›‘� åRÄ�S+Ž2ñÎrX”2\rÅ6¤„Öz—Ñé§Êý5ÚK)ÅLü6БÞ,ß¿Þ Æ{k^õõ@±« ‘ÀÛXùì­¼O�ÐQ–È눬› ›]Ú�”õ®rÿcßîqן�Dá;ŒbÒNë­Ì-š•^O#C>òÌ ÚŽ1"ίt˜[O±�$,„qï2"$ˆCN�˜£›6ìc|fˆ™í=k®P`q)úZpø'ˆÃÈ›•Ppœmw ¥ÿ¢qd�>f ¥$ðN°ÿm „ÁXIŒÁ· ]ä̓óì¸dÒ�ݶ-Ã<óÚyG]Îpõîÿz;ÃáZ«R ™¨Xifnçï{ä(£#u.,¶Ýx[*GΆÆ�f ¬ˆH¸ uyµå«Ñ±Ò$) £€­»¦í3>ý9À¯…ÇæV¹øÄ+QC�ßq<ÀåÏ_ùà“ÏáÈ|Çäñº�¬¿ùw/[ä£�ã[¾þzž}õ{÷Lpüì*Q Û åMÔ¾JD;5ã¤`y½Í‰³‹ÌŒWxåø2RJ„ìµ£ -F @˜Þ`DÏ$�ŒEœÐ3NF·ÒL‹�Ѿ+.νVû !õ*XƒÂÇGRÓ;¾:B�4þYx;=Ñ^ �÷4Äqà4½’;oÞÆoüÙ lÜ8Ä. ‹�ÄyôD’Û¡( (’YœpyóÄq0=V¥¿^âô|ƒ“ó-¤w˜„´Xgÿh„ˆ(À9Ö C�!àSoÚÈ{>vœv7£R+NïÛ‹wXðqS>ÿKzÈî X€Âa�ÈH@æSÖýŠkK-öí›â…7.P¯˜]n3»Ôâü|Ó�]Nœ›o2=V§¿V`i-áùý("„ߌþ¾€�¾aG�çÛX¬ðI…Ÿ_Œð©µ‚¡ñ7\é$«­” “iJÕ2Öw†ÞóæGˆÆ]~º#7.%ÖËæ^Ÿ“÷ˆ°Âÿo¬à5‚v+¥XŒXo'TúÊBòÜþY®zÛ$/šãæËføýÞbÏÆ>œYq¸{Ó0 K¾çü˜�`5ÕxCÂm s§é-9mÑ"¤Tì-^+Ñ4š‰3KtÒµ–8ŽÆBm,�T»£âíünûU÷®ˆßáÄÛVÞÁA‚Gðü ¼«ëq!*Hf¦úÖ*w:܇êjúJn‡*å�3‹-.Ù>ˆT`�õnF±�ïyí­¥Ž2´´%.„® l¦1¬õX€³Ò‚PÓé¦ÔëæVº¬¬6‚04Z%Åpï¦1®ºôgˆ8~ZåŽ�ÏäÈcf?ð@WûÅÀßd~b}¯>}ŒsG–¨Å!2‚(Aè~öko™[¯˜d׿A¦Fj( ËíÌ�Ýb[ñı‡3Ce&û‹=ç*Œ0ô¼t«�HøÅ·Æ«ØH²Úè°uÓ‹‹ '�ŒÉ(W"’È‚ˆ“ðâr“¹•6J)2íx1Ÿ÷Á›–Iosù9Þjƒ²P�%@øÞ�®ñ ‘…Xzc5Üß Œ†À@­ñ‘Ïq× ›øâŸ{˜ïøü}ÜtÉôÑÃÄqÈ¡¹u:šéÑ*×N®&œ^OQÚ:ÞÚ"¼‰w~]“Kƒ%Ò„$IJ%¶,JI\(ðÜcoqã ;1Ê „]{ÜãêZ$H�”õö³ÏØ´7~hR>°ð´©=N‰…0€(„NbA¹¢|šä‹|À’‚�Z�åVÊ/Ýû&_ÝÈøÂ›·ò®gOqb9áB3a¨Ztøáh>D0é["Ÿ#d£3f&ùàÇٽkš7^8Ê`½ÀÚÊ�¥.FµŽH§ÿ½»»Ú5ÝÓíFƒ@�:�óö·w€}›S’½»ðßrß�4…Ð šž×_­£•Æú�ð”‚äÃ/]àöË&‰#ÉŸ|ìÏ�ã™ãK\\ï`‚€ù®¦i,ZˆÈ_aþ.¥u<ïÍS�ÆlžâÂR›f«ËsOpctµ²8¿ÎÊZ7ëv/œ {Zß ”vÇÙ©@‰«Äû€Ä9Á=‡8�ÐÌ Ì¹Ÿ®‚BÚ@èÓ)™arÓ0V¢¿Z@…k™¡à0Ö:)“ƒeºÇW)TJüÎcgù´+&Ù?×âÎ=£aÀJ'ãØR› k �®BØÞîk%¢€#„ɰ*É`5æÄ©e/ñ]šã†«¶Ú#/Ÿ~è„ÚB’$ÒÐMA¸E€Ìz·Ä÷0AX¬ÉÒ NŠzÁãÌÒvê��@xÀ´¤JPŸ V YYiT ŒŒõ‘JÁôh�®”¼çÙ3|ÞÍ›xÿ[ ¡äCæÙ9ÝÇå3uš™!ÑEvOÔݽïÀ"‡æÓÉÀ‚´À­O=nÚ;Âûž<‰ëp«±6éQ¶(Z¤iú @h�A']:Ö¸•¶™Ÿ½ÉÙq>¬v,µP¢ð^¹ ÒÆ?Z@èOƒp#3„±dfû0gϯ‘¦šùù&ÃSuV›]l!æ²MƒÜú+F¸!hßT_/ñÅÅa>k”|êîáO\#¼y¡ÁÇÍ ãœa|þµ3¼ãÑ“”B0iÖs£tˆÑš-9xà<Ûö¾½7ßôy߇qœ¤™P™r&…°iš¡´�¡Ò ”¢›f(Õû~æž•‹¬2•‘f:KIÒŒNš�Vd*s¿×è¦Ø,CCŽ/‘ AXˆœh¦`CIÛÀÉå._zÓ^8Ûp¶÷÷ܶÙM~á@ÙÖoWçv~$ÜItŸóË®çþ§NªÌEò&µ˜DaSMÚ͸|{¿=vòtü @xþø›¿?60öFDRRsJJÐІë-o+IbÜÝåXï àv®I·Ò¥°÷»ø@µ¥ ¬sh§6Œ°¿tŠþ8°-Â¥D•­”HüÑã§ùê[7òøé¦ÛõÈ�Äx¥%{ 厴e¤sǶATª0®œ*ócïxe´ °Za”­1Æ09Z°‡�® žûµCÏÿ�løÊ£ïüÝW>ö®6BL"EBb­Dxˆ¶H°²7g"z?7½g ^îøð•s]^ž9£x¬:1þù·DÃU[,Æ"M´Û«4Ò´�‡-³uªÎÛߘãÓw�¸„Z#Pл ‰�YCj%µbÄt5âݯ̳°Ò"ôÔf2÷{tJ’¶Oz²•½ø€�S\ú•¿([§#t†C £z—N…EA¦ýŒ©ÜÏ­UëfOÜ úCÚ{6x‰/†LÍšR�þˆî¦ßÕéfTêE¬Ž…l¦±‘fv¥ëZ )–;™s|«R:0ÎÜ \¬¾ÖÕ ÷‡¬µ3^ŸëòÈkg ñÒ8UH­ÂB±ˆi´ìêÜÊ™V%ût`_¡¿§þúߪïž,_u|¥}c\-’…QR÷NÂѹ»‹�œ^ã–Íýlˆ\¿kÛ3Kú£€=!G—Sά¦œº¸ŠÈ2Ë0J!�ÅD‘ÛyÝH¬^i¨4m}°yIþ�êÑüèí×í<4Û7Ù…(#ȺŠ@kNÌ·©‡–jdÑô ‡r$ØT(p|5s'g82 Šj 5È0tT•b§Â²…‘4[÷‡ùG%à þ«ð¹_ö=O•'ú¯zi®}âj‡ˆB…˜$¸ló òÉYN �Ì2ßéålÝTñÆéE¤²¨Dñö'Ï Tæt¾mwÑËk69·’5ïžàŸ©�ÿÛJ€«Ÿúð[¿wãí»¾y`sÝVGkB .ð ˜*13XääzFìcº,3,¶fWÚg‰°ÜH@@€A5;Ö.® ¹Ü8¶¶8û©À1þ…’üÿ¨oyþÉýw½ïo^¼øŽ¿|‘w¼o¿ýë�wêÐfŠãs-7¢Ï·Rž<¾J»Ý¦*4#é0CdŠÇ^›%]iY–Z 5š¢Únþò�¥ƒ{ÿµ—ùÿSSúüÂ7Ç�öG#õ� VùÝû:öö+¦DÖN à <Ña½�ƒTŠÙÙuF›Mª­uÕ\mþå“/ú)à<ÿŽ£;šÿ§u}T,YXŒ>9ªW¶ª°ÀÖMN5ž�m ”�$£»¾º¦;�Tbß�$÷Mþ%ˆùÿ^Žn(–¶ì ãÁiDPcŒZ_JºgO5û‰«{èòŸ¨¿é \(%lÍIEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/64x64/log.png0000644000175100017510000000433115224505336023333 0ustar runnerrunner‰PNG  IHDR@@`¹U IDATx½Ø…n$ÙzÀñ_WWW³ínÓ˜=°t™1ÌÌ‚° œgÈ„!$³ ÌL˼;Ì`Ɔ:•«#idi4mïZs?cáù÷ÿûª¤ÿ^ûùî¯V2§ˆ>›7rþ�N)ÀϵþéWZ™SÆþDñ;ïüøêï(Þ3À‹�JÕûŽB!(”¥éÊo]²xb„@V8 @¿ ÕÒ3¿uÍü RŠS4O¿š´•ÎýÖ¥á¹?PœàôQ<„¨·•.ÿú?¯ù»/#ÁPª�­æçþìß¿Ëߟ@À–žÔ´ ·ëº‚ú2‰Š�–íØÜšy}…²šÂPIh~þÏÿ-"¼'—<0°lÓ¸±é-‰ºOÚòÀeM‰š’q¹W]r¨éc"Ž"üGD8yºàiC<­¢§fKêPۖ¤ÌSÄ<ïi $ó1¹ž 9‚�7?Nlà²ë*ºv”�¹mÕz2 m{ªîû�»öœqKÕ¬7�÷RAÀ�#_Bø¯ˆp¢x֢ؒ2žÁ9A�!HeËRUÏ*+{V”CÀÑD|:Z8‘�È<º· Jr<Šð¹ˆàørUЗÙS‘a(ÑWÃP (=üJä<Šñ/áï`ÄûR-==›Î;P2ô”ÿ0®fט�}uu»ôíÙòŒ®’iÿhJqʉ��¬ù…?ÿ§ˆ0`EnFfJ¢¬âСš2uŸ�* $ªÑÊ�Š>±¹eSEü ÑCfÿKµð'ŸóêÈÜu_!µfVOðAÏ«ª)9ïßpÎ=‰�†Üž\[Çê¼ã91 j2A®0Œ(Yv½¹óC~A> nJ¦fÅ$>k¨ˆºŸ“hšr ¢§Š\SKIÝY‡ÚŸÒ‡/&H-뾑Žز¡f[Sn¨bܤ‹¦Ý÷@.¨Z³î‚W�‰¯#scEdºþ[×P߬‹‚TÃŽI÷TíJ|TYR–Œ¬�¹(õPŽž² xζóØÖL ÑFö¡9u=CuA‚r©Y™\Ù¸º1›ÆT”$ÑЀ¦ër}ûºÞUÕ Ì»…ªC÷·‰U7Ôöu”¼nÚ¼më†jú Û©º7�µ úÒÑ–4”ж¨¢iO*3£'Õ’bˆDI#jKáÀ9‰†Vtv߬€CUegµ¥` ;à¾}X·!±¬dGê¦9oyκ;>ìU‰/I½¥¬aÎ…²+ÆTõ°gÜ=›&�qÛ™2#wVåx€iC%Ûñäm™¦ Ìêø –«+ubBDÝ5 ÓÌITtH]SÑRæ#šÚjRŽ«�C‡òX6÷ìXtͬª7cE¯k[·è kR}¹²Mƒx¤åMu-=C¹–ª»ª>¢¬`´�¶D/ܶ€ªy™1AÙ zYÇÓ cêr‰= AÛ�,VBÉ Ô”œ‘0:0¦ŒsX\MG·0 h¢9bÐFxrN>$[wO]IÉ0–NÉtìZ¨YyŸÍA1  °c «'‘KTmèÊMêKí(Þçœ 'MAE×¾�7lëȬI\·hÆ-WmšÐñŽi=Á˜!ž›+«y dÁºmúÖ,:㿵�óН�ã6 %¹=U ·4lkÛÕ¢���=ãî+!öúR=׬ÚаaKÏ´¾ÃèoǺžBü=(í`†ë–m¹ì>œˆ´ìöÊ5ÔÀ}r›.hZvÉ’u±Ó’»ï¬·$>¤8‰�=Mëªvm©¨ÅßC-nZÒ³£a`߬-cÖäªþÛL۵㮊ž‰8kqKSê’g„ãž‚ŽÍ(3wh2ŠßW—vt�9P±ë·mD˜ºžà›°f!âv#ÚYoY4a߸- Šã 캕‹Êz,,ê‚¡DË5={f\UWõŽT0çm]»6â§­h¸æ™Ø9ezñÌ7ÄÙ£püS°d°¾˜Ï΃`3`|+ `tN£ïq¼knšr �8%1fIÌ÷ã‚¶ª®G£PÅñ½á†-Uë.k++ìxÖÿȬÚÔ´o=bÌë¾çÆ!Œ~P8çÂ#}»”G|¯�C8®(%%$’c–¦œÞ÷lH*éXÓ”špDz;Hí«éK̹a aÊPà CL*K=‰Š ¤Ö´]F¸‡ãîèK¥¦Ü0á†5}™ðÐÈ«Æ=0nèœ kƽ¤¥£ãm½ˆ¿ì¶OzÛ–ŽÛ.èרZ‘©£gÊ5ã2e5sžµoÇt4´«ìk5]6ÔÖÖpAÏg=P‘E;3´}Ä}O—ØÒVœ$;^W×Pvˇ¬¹ç®Ôm-λá™’š-5ÏÅþâm÷͹mYâŽ}™`ÛŽ¦LzMÝ´¾5ß’÷@ǧ%Æ3:R³>é¶®}©Ì”ì#î[BÍPâi‰Ì¤® µiLÝëf­HÑ—žÌÀ-1Ÿ‚Ì Ú^ñ÷=oÎUm+6üŸºTOÅJLAjOÝžEw|Üo +æ«­[·&Q‘Úð'Y¤š3§cGlèêj> QÁôT� Žn�×nyÁ¼u >åy™ª oHe>e ) d•YŒÜ&pܼà>€%À'@ßi£8ݼ sÚˆŽ–ïIA[Ð3í®YkêNÇ?®ù_«n;cÓ8ÎÙõÏúžñ)žt ŠGjøðä È=ÉÇx²Q Œ�OÜ@o@É“�З©€“x)FŸñȾ]=yÞ,@ø�×ç?Ð=²ÌLü{d+<Ü›‹g! G�½æávîÝþKÏ/�˜ßù¥_úËïÉêž\—^\ÿ¯� ð��[ÿò% O.rw¦.Ë`ø­7ÞºSJe €‚÷ý?�ípU{�“-&ñzx“c¹|vÂÝ��¨Î€O` \Y0J�©ME¸x ¡èƒ„�·p›óBh_gÁ×°ž…o#S»ª£-Ïì àXx»k X*–\¥Z‘¾Bþ^:~Ÿ¹^ƒ�Úñ ÀÎ,h7Á PÖA495é¦g¦eòU«Õp`{Šûè_Õyð!Ü /À—šÆôß‘�X¨Þã}.÷À½PŽz<®xºÉÉIñÌýýýaÄ%ÏÔc)1:¾àýv¢›0ªÈ…v¦W¶°-ÀqÜß;??3u iâÂhþJ$@½´1vÒ&$QŸ7bÿy}e[û húçu"¼û÷Q­V“Á¥�ç­&�ìß�>úh™ü¤“ç«kšÞ“L|»SN9EöþØ^À³p;°®‚4˜þÙWûŽÜ™ˆgïV*•\.—s ‚.¼ÏË!^VmTÁZàVÝþè�¿ºhHž‡~¸c�‘|xÚª‡:>�ÇáIžýHþ a:Ó_'ÛØà8Ê|Û›È7ëâ¥åJX䞉/ öB¼¤Úâñ�†LzÊ_ÏLBw&ÿ6Ò½¸qãÆÏGFFV)Ó¶¶0ý �ï3v0ïdrdý�àO]ñÜ×2ø@íb leð² Ëaáè証§|-Ã[HÉ_lvïÞ}vggçÇ|–ÜÌä¿Á™þ�,°àx®ƒ¢¾×ú‘¿ôYÔã¸@<‚ÔOª}"hlÄ^Ÿ˜¯†÷*"ñÈGq„žH:õ¦”#íííí•C?~C�´¶ÂKðÉ¿Xðß_l8‰ËÇàJÈŤÓÁ„2q¨·öE\ýt!�kÈ]P¼þ ½’/Ñ„.a;%Bèîî–kÊb´“r_†Ïøz°h €-¶ø¡þýØÜÈß<¬«èA�îói‹²ßò¿ŽÏ†×ʺÛ=SàPPöøîöÎAÙ–d ÃûÁkÛ½Èµí¸¶mÛB÷Uƶ±çXÊoöú"r";WícuET¬szwu¡óÏJUöý÷ß`±/,m¾ãýûÑ(;˜ÀÌ^õQ? 6kèu P"ØȾ& �±ïÚµË9ÐŽùÒ.�¸íèÛâ¾-ôÉÿyo0æë³:àóžøU¹9.5êÿÐ4.603€™¼˜?êÇô¡·kYEžïs¼îx #ð9öXÙµ{»8m•lÇxiC•½LÙ2±Ëò3igÛ.¿ÀuQ¿Í¡£øÛÖ]&q¡1€™Ì À�ÝO«¯YÉàtÇ·të$QjŒ“¨öØl Þ¾2A´/À_¢Ž°.ª¼ÖÑ1�æyuÔïG_‹q¹�$€Ù 8wü?ÄK½:jâ2K£RÏÖŠß”bÜ“P ümØ'÷Ч±¤öYÛñëX©ŠòÔ¦diâÕ´?@aSšËùîœ%€çs,7êû£b¥³t Ê dI "ÂÓÀv÷*ÒëAuLã¥-¿ÞgTúa->m_»KV¢c÷=ÒŽ:edôú-êH”«îÿóøóM˜U€™¼4ú$<öÝëØíuÃ)¶SÄÊš-Īk޾µ‡phÃ= ŒDL‡@>ë��ßÍ;ƒZw"u Å.‘€–�_Æ:a#øIÔ?2t2�« `f3xqòãñwtümöÛ•lhóŒ~]þxú@ÃÜù—”Ì\?À8T-(ø–A›Â”l uåšß÷¢ß2ôs�Ì `f/3GŒ£±óü:–‰]àáWçh.ú6Dƒ‹NKýqÒN⤑u¬!»øæ™/ë¡ ô› „ºòÜYÀ‡i0/UÁ¬z²Ìˆ™¥j‘VLR°ÝšÜ‡{Î6˜ÀÌ^õã_wž}«Cî˜îžø˜ÄdÐ ãÎŒÀ±À(=c×;  �v«««+?ü0ŒD‰ ˆÝx¾ŸÃGV‚Äü?÷ üÂÐR|€¶ŠlW˜¾m²:QÆ“îóìB§|/êâž=g‚Ì `fYÿ~q´ûtüÿƒ°s"¦óoÀËÙzOç1÷Ž(iǺÀØmaŒÙ5+cŒ‰Ð6ïø�Ø]ÎóŸmK‰RfCÿ‚:Ÿ2’²—Cp Êu1Æï¨þôô§?ýèéd3˜€¢þçãå¿#ê6ÛŽæl€‹A1 ðÛõä^ÚCO]Ö†ûè@0ŠÀZ2ÖœW€µñ4 Qze�K´à†�+Û¶oó^û*m¯GŽwÑ’ç`¤0Æ›b}~×ÿ€ÃãT3€™Ì à…‹C:ï…./«ÈI¯èÎø¨¾Ô·­Ë€ìoVŠH�æKU}@ð~ÇÊßÊËP¯‡�Fót¼a8ô ö)#„ð> !%§A±ï[¢ïâî¹ðÀÌ^¼Èaÿn?oÁð’'âáóX 6«À÷þv®FîñÒ}¾ñðÕ^u|v`BQë¶8(´¥œÏÏk_Bv]#$<ŒÃ뮹^IãÐÁC+[¶nQW¢ߨª± ^£oCާÖ6g+ÎŒ`õÑU’˜8¶u1ONŽŠL8>›þ#�¼ðÀÌÐñ¿õí¨ØM Š"¬ÏLTyY“ÊÀ³E”-ÆOÔÆÉ"€/³q-Kû”T¡ƒzé;÷~#!kPR5„jhe|D52?e)½Ê6]ßÛâç±>ÿŽqï>ÿÀÌ^ÏÁ�÷¶ž®§ÃÒpÕ‡ûÖÄ—Ô JËÀÇÍÜÑaK¤ÝèŒ�Æ3J›{¯ÎÓ<ªö×Ù2d†05ª„¢º±#½Ûµ4‹0UÕ`™-C°ÑÕd(ËAV¿dPæ,¬tZÛ9F~a߉úOb¡Î}03€W-²ì¾,ËõyY&ÁH¾gÛd±º\×}ç&ÄqãôËKUDÏžy¦À'òŽk6EÅ_‰»$áÔjn„Zð'ˆ†{Ê·d&ŠïÜê��;óô¨¬·m}oH-ŒgbÎÞ[þm"’ä>,ë«T³fS@:Y{—0�=»×Á†�œƒLžñࡨãºM„4ËH]kª‘… ëϨç˜Dѽ<þm¬~ÙŠ¦mõWý²¾Puì¼ã ü‘Á~øÕªo䞀iÆœCv%,¹Í3@Éá¹¶•2øhÏ×í‡÷WÀ/AM2@®A¸%•÷ÝïÒ~ZLÉIf§~å CŸµè¦eÜ¥ÿ´^™�NÙ¸oYÄü!Ú<û `f¯ Nÿ¥ ¢·DÛÍë Ÿ57¾@)c“ ÀJ¿ óI_íõz×Fƒͧ¹ˆÀÃ-g¶Þ¥Ñw2?îÜúRv| …Ôêǯý™Ñˆ~ùRï’vƒµ)¶�qŠ°Ê”•&²‡F gä€�x°h½ Wß±N?Œû×öœa03�å.élÝ´q“�Ý.®¾©•\Ö¤”=�æ]´UõõFðoŸÓÎÓ1Â`þ ^ð‚•'?éÉ+»÷ì^¹õÖ[Wn¾ùfˆD›C1L©^¬óJEô­À§Ov{*„ʵ¢;O=¦`À ˆ:�l%;p¯àŽo„fÇ`³D„Q …y²fôoTfM�Ö‘û¬# Éþ¥ÕëãO?�úkš�~03€W/ÜyoM¢~Ö+tâ"Ú ±Ò°ÓÖàó ð[âè“y(êÛ'µ0¥ ƒnþ _øB@Ìõlj¸H.·Ür Ð4ž“�OÂ¥Žl W§–H¨Ý†yýdš9¬˜öŒ�HA~•ŽrÈ´¡Â™qæg:Ƽ»R�dÔfa” ÏðÌ“ùf† $€ÇF`žÄÜ·}–ÃXMva< �ÞõÄÄßvŸZ03�ÿ)t|¦<'³Î T?~ßζ½m`,ê3—Ña—BX;tÏ{ÞóŒ¢øŽ[7%Á@)Ìå1qû¦›n¢O t숂eiÖÕ  ÇÀHUšvÃ’QK¾€íb$\S™•ój=,^W?—‰Àب<Çy¤Râ“Y‘Œ9húìâ,”ŽJ»¦Ü};žó¯X£óiÀ“/¯Yÿ}ëmà¢JšóôS-�L¢kDÂ!ðs¶ïÆéÎŽk�nÂø¿)‰Arh-`Ñ‹^dŸCP,¯žžã)=�Ør­%>wüGW]¹ëî»JACÃãÇÆHHè�ÄvE­ÑX¨Mƒ±.É”ìQe@¿AÕˆª>J0}âÓ:æçGÿ¿ŽûH‡Dð7݇ÇWæÓ€—,ÜyïqÇ€>£¨Š}úËuUåïâU¿ÂdI@\D}ˆ¥÷¡¢/Dð÷oúî¾.,“ë"€Øu1¢óæþJ4#5¿ûp¬ÄÒ?ûÙÏæW›�ëdh/;>}òËs IŽ—PÞò¾}'.-I)v Ÿeÿª 90cUÚqÝ]3Þ5Ò j@”ÂÈ;{„¶ªs’ajÏð¾T¼fÍkqÛ"Äø÷Q÷Î*À˜¼2þÿ€?üÞ aBtnˆ¢,Ì&ùÁ�£ÒF«íŒ°C¯f\èØŽÛL@€ÉS¶MÏÈŒ€ê.\’%¹5©F–lA>ßâ}ŒÕ Æ÷"ðÙõ]Ÿ%@òoJ$5 Y•*³ë·}fbü½xþ_ÂÆ²÷¥/}ib”™¼6Œ[_Ž{Þÿßõ„€ïõ¦ÈÁ®É;†™i²(jÞ<€¤ÑËgv%Ͱӿìe/ƒx%ü¶�Æ2w5NÉDž¥.]Je´`¿mçŽÌéAl·Ýv€bŽ“`ª» áÆ�ÿïB¡ PmI‡‹¨m»¸„ú‚ýðßwß}ãhÊš™:Ôñ3ÀeÊùjƒ¢õ®`Z? Õ ¯Áî™DžEν·“zKwP¶›£½ ²"ê[ºqI`¹ŸÁü½‡~̤£� ©¥Xø‚±½ó`ìÝ÷î|–žÝx¹®O-e7îìŽUÁØ#À ¦ £c®•Ùõ"°jL‰ò›±LU‘ž>kž…¾d�FY÷ÖÞ£:lnÄ Ôyʰ”–¹†U'˜£±˜×�Ñþgñç_ñª.FpÉÂ�÷š%¢Ï'Úr¨j&,jÙñ›þ}nQEW�ÏK.±ë) ˆVNî¥(CÁïÜrVtYcÕ½>õý¾’ü3©^/ãnØZ€Ò  p¬IHŒ~̳¨ºoÎ`œ×Îû7s÷€�íUád¬±ªZg(”N”8ݲ ÀuíÔµŽ~óßœg±¸®êü¬+ïšûì{QîY$&ùS´Ûq10€�UÿÝ¿ õKeDŽގ€ß�Ѫ‡ïD=Á§[ŒÊ} œ3 E‡•HÀƒ8èKà3çn—W4Îórm©Ç³“¹ã;�Ó–g`Éla ùu,…`+Õ§Oe]I³f¾EsÖˆÍDå<¥èõ®�€¬:~-y—Ï4YKuúÎ`n¼W*s—)5å¶hGdáßc�W/Dp _Ò‰úîãý˜d˜©·èû×è•Ågçƒ*=°jü½>nw®Q2 %ú„¾à¡þËßM/†ŽÓ ¯R‰A‘BðBð,™Î`¹¨èÛüz}=búB æ4ác]6�@m öìzÖ~ͽjs<ÅÈ^cÞÆX�¶¯Ü{f�·kœí,0T\º¬+kÌ5ŸÙ”œñŽ ÙÆ8 1>ïÀe4‰ú.þì$¬K XÙ¥%¹˜ÞÓíŒ&ÌôØkó1�ÖÒ¯¸Ú… ÛÎ{ü¼6ã…°I°aX*ãð].  û~_Ít0æ¸síº¬LÒ:ÈmÈX­I;r6êh§S_Å#Á8J0ÐÄéÜ}>2€W/Ro½“!l%É…/8M¸ÄqS£ÕÇ~+ê×oÐà'âíÎÐg6^Qs $2C-ă[ʱR$ârú¬öéšlä©»œ£02«ê%ßëù=(õJçRí õdFzÀµøEmóÙ´÷ë>Œ×¶ ÃìÂéýÛ·ÌÓñ¨6Ö(̾èšø¸gOþ¦ƒÒÌ„w‰äcˆ±Ò\´uǧïò.하 Ú4AQw,l†œ àÒ¨_޾¡�'±¸8בFB@¯î�wΑ¾æŽÔì¶Ã$ëÙ1e,ŒÂaœøcu„ñz‘�SG!ÑŽU=Þ:9?×F«|¾Ö”€pÕ!l@=:i™S–ÓcÄÝÉ\�{×·íTô˜Tà�w|�¿,’R¦Îßy—0^ò0VéÝcϾ§A¹;êÏñDÝs.2€WkÕ'õÖ€ë+´GP!š†P‹Ø­ØÊ"Btì²Iä_æÎ3v€ÿwÁ£oç­÷D ;·nÃð'#÷ Måײð» ‘ŸÚ|½0é ¤¿Ñ9 ûÑà “¢Í(t¶çõ¢¶"ÿ7`±¶;–õÄP ì.2r¥’Á»ä>™1>Æ�¿¿ØI�¶c°ÿñ†WÕcŠ~õ7˜¹ÎpiÔ/E}s«ôÀÏÄå/ÀÔ—�›“ma–u,AÓx®Á2¾XEÜœœ²•D-9ôL|9ñIï21Ý„ìLŠ‚–AHªëS‹S ÃÔ‰¶"Õ8ÖSuÅù§¹37€O•` “öÙO2´jmß £*†H†fc*ÌÎ}ô­TÞ�cïNXæâŽ/ðé—’ÝÍÕV¤ø¾aaÜ]ËÒìdž1Õ��¿ixV•2žÂØ‹ÒdËz†}ï"1ÉŸV6YÝ~o0€ÛŸpfÀ§ÿÿº•£[_qäàQÜyïX‡˜?õ!Í.Í´º²n?ÿnŽÀ//>}ÄË-é‹2}¹UN‹Ñ€ ­ckJŽÚƒ€éŸ_U“>L¸¦ûÊÀ§v‡d<éèük?Õè¡Õ�Ìà$ØÎ°&�±"ѰÞÕW_Kþ»aÂ]Êð|’ÓOœ»öô?pçf ‚9Š»sŠ�:V¿þT?ÈR%'˜}¨Â�Qw0²m_ÿJêjܾÏößÑXŠÆŽÇŽÆ6¢‰f¢¢±mÛ~B_ÛRwÎ9Sß«ûKVf÷ÙUwžwr.ªêhïµ¾%l’šxÿš-ΓF²>2'ä‘V§ùSWïû{ZûÏoøúÜ~ÃàýS¿QmýPo­¥é¨PU.wF". Ë18ZRqÎw$ÄÜðB›€Nž<éà BËêܹ0 n�®mC59›Ƨ�Ï3½êÊŒØm—ä e3ç|˜ü޶a— âÍ÷ï¬iyŽ¢ê�<“ÃDþè£�²ÁJÎÇ@þ€çÕk ¨ç€.ahJží¨£9(k“;{ßÂÄïŒcºnPMiPs*5‰LÆ‚ Ÿç¨GƒõŽš�¦Î>{]Ïþñ‰?û‹_ù¯OÔŽÖ÷ÿ÷ëNƯÿãOàÔ³—?£Õhª7ì¨3hŠæ@ÐÈjØQɶØ$`6ÛW.w52�Ùr/˜·¡¾yë¦7…Ä'Àr×2!E$·�jb&›GUâ sm¾ZlC¼uш(E¹Wt`ñw­ Ë–cGK3'ÀÉóÅMºypß4>í’lûüøñã˜Y±ïBã#ÉI”"ŠáwKú÷Ž(‹¶à5‰mÐÓý”óMø:Èèü]Ô2 � Š‘jei] &Tœ7¾Ï�´�áIפ’Mi°ÑS§ÛÔéW®è_~sKÿú'/ëüÅ‹ýoüªïøá7~æwô½ãÑôso\ØÑö•±TJ½a{¥'¤ÖÕ+ž…À®5A»Ž%?ýù™` €P¯m"3Z›ùq´¸UØ´œj2�`ÆkÄhÙpÓ×Eöupj‘Ò›T˜�Ñ4Ø– Éq;KÏ`f `!…+#Á&Z@L‘ö°ªH\Ü!mÇDÏ[�ÓI@�Ù‡I ã[ò‡*D¼ÙÑë"ãã Z`ñœa‚p.sOú± 8Äüœ«‰ƒ2€gÔwÓ ×Ò+ïv‰ŽNGîÍÜó1Ç#ú¢"€$NæH1Ës²¦«–ÌgÝAGggŒÿo¿{Pÿö;töÈUù³~¯wö«¿ä›~ä 7>óÛ?èó[íÆÏû™‹i©ª¨4Xëjóá¡Öïë«·ÒQ1©äïxÏŒ—³À„™ìøšéj#™‰ˆµûœ:I …Â:Êf›SàÔ��üŒ¾�Á&M€‰ÃÒºŒ1x\¹bF¬'H¶ÞZ›§~ŸüŸ‡s‚«ó˜1ŸO&\]/ÃÄŽÆ$Á“óôÈ}óül”!ÇÚ 4¥e­"$– ÿ‘ˆ„30Ë©6 ±ÙIÜù8«íùj<–*u7ý4¤Í{Ö4®êÂá›zåOëпŸÕd4Qµ«V·iRóØzùoμû €Ïþ®vÿÏ+Œéx.ú]m>4ÔÆ}uúmÊiµ¨´ÒÙW1‡»ÎN÷aæµäµtòïcòP6¿›ºwçÏ�×ÅK–�b7›9~øaöžG²‚ú¹Â>&p´‘E%«±TÖ¦Ð21çØ<$6 ÉÕDBÅ‚V€$Ëfo’Òë9u³ 3ð /¼à¿‘‚¼Ïž iBß×Ü”I"&™Áü…½—VèÀóI”äùêh–õfmSÓ ¿* Õ^⽤™�UwЖšÒÙWuø_ÏëÜ+7UMê­Z#HhýM€t‚ ©,¤þj[­hóþuWÚ*&¥ÊÂ÷Q]_}…í¦’l4w iµ[lYíÄãc^Dæá¹b¸Æê³U`Å@bÕÖ¶&’ž†ù,%ˆùFBò9VSM´1Et¯êE®‰v“(¥m´b¹*f•ež»P`’¤4£9$©±HÝp`¶˜ñ=¿þL˜k˜b¦óÿ=§î^ @‘Bë~…>¬I°Þ0 ‹fƒm¿÷±Le\Ôè³�×ôÚ{cñ“¤Ä4M…Uº'c,ZKZ ’ýbDŒûÍÎô²F]Úô:x]‡þå¼N<I»Ûc­ï_ÑÊp%>ßÛ"ñ6æ _Vs x` Í‡êöÛ24Ô¬ÝPƒE7³V& ˆƯõʲHÑÎçÚÖ,Ì8ØðLzôÐVï숺ì&©º^XbÖ0t¦l9!€¶O+kýÙg…¦c«}Iþ¼™ÁÄi›¸µ‰|ÆB5óµŒú–â1×;¶ÚNzÜ“çM<b vwb7#É)BùÂUId Gä~nvû5DÓ 3¼R®>ó;SqF581Ûè‘0ï#°ÿöVãíäžQ­öû�>K>®Ã’ È™0åΨú¾o®:pÄNŒM?¾Žç8^?ÉW zÑÚ aKæ*š1œg'žÛ6þ¡>?€+Ú¹¹3—ö½6¿g@ã݉¬î¬ntÿ“›ºï‰ýŽX#pBŒoÇž=û¨ú¨¯þ>––F ¨ÛEûß„jÕ„‹äÚ^4šmR˜�š€|ž™‹vÚ>Ÿj0$wÚz|qƒ¹’æ"¡Fi 8™HyÖX™–JñTjyØÙ‡Ï™_Ò¯cÊ*ZQœ”Rààß\ ¨$v% 5}¦LÖ3ÓËo/óÆGâÑŠQ|gÆ�mÝc�BB¨žã˜±JHÌXŒïHågˆUê¬XÈ5tæÀ5ü§³:ñÜe�w¦ZÙèÚ°°ä]煮}à»?�’s— øÊR¸�ýÃ�®|æî™V°_E5Ñ™ÓçtñüE×»'ª~ šèŒ‰‰3¹iœ^D¶“Šíµý›$q'äœÓ¹Çç“ÅVÓ0ÛŠ‹wX¤¦£à”#&×§’™• ?³œ:êÏPÉqlÆ=~˜p±s1·ÈžôuÌôT£d̋ĖöFÎÖ‡a N±8ÆÏmGÆßhue¹DHö¡•ù „ƒ÷ “ÒçÀ€¸¶EíVGçÎÿ_Îéø³e Ø u­NÓÜ㦪æ�;þ­7R¸3‰7jHsäëí¨ìíÊáÃf£¥ªÈ¦\;6±“°¨/1Ml�šØéAPKl‰÷îw¿Û÷öyKu†é9 Â̈¿Å7a"3sâ=ÏÍ3Ò™í¾¶E�tÉÒ~É•³Õy€¿ 99`Žæ—ïgIêçµôÎù344�×,™ãLæ§…ÉF¬å¢† h¢”'ÛÊGÞl¶j÷šf ]9±­Sÿs]'gw{¢Þªûú<îQ ¬;ŽìÈüo)$/ .YÕ$¿æx<Ñhg¤îj[ýͦºëR£c¢xGR%Ù²Úö9ê˜?7Ñz1Ù\ÃG]õÌN‹pˆÿÆûÏCºø~¤%Clw¹Þt1ϼ.öŒäb+,K?/É=´â"­7ÎoÒl›6¶OO}(â�ý.IÕ1Y×´Pˆº‰hÿîÙ8¶³utÇ÷E…78Ç J}0|4²ÐzË¿¡ÜÖ Þ+±ññ×øŠgÇù—]K4ªjÝk8sϯ|ùø¶Nü÷U�{éºìÜkšj¶â9y¨*|0žÏN\ƒ·”ET¡@0µ²ÎÅ|j5Èá�þ¾†ºR£]i2ž:|hÆGB ÖÇ,-êÑÉ$ãú0~LËDÅŒBKX TÈŠ3Óû -™ëƆ &¸$­„ñKà=‰Tð܉óŠª5lؤ{@ܱQ‰‡lì]޽¨²„ð�.½³»ãkâ͇áu_#Õ´ËnZžÌ= ûÆta3¾£&qSQª¤˜{lw²éeH©rô‹ Ùý7áZr!0˜·xññšzÝš­f4³ÎG®ásº+Ö`«™Äßѱÿ¸¢ ‡nÊŒoí¶Ý1ýM‘úð}ýY²A-ãmx•�w?wè5ýÐäÝc å@ÏÔÄÓPg¥¡Ö°˜9 ÷ëÉgW·ÝÓx4™‡Ó5ð=½^`3‡ÓŒoÆ`�s•€þMbžš|ÿ}ôÔ,Ê £°'iI½(´F²ûù“˜S·6HUßËfŒësÓ0W¾!†“^w$%£&`a³�4‚ÀîÆ®+ÀÙXkzÅ“ünÞPÅ;êXø¼˜ÂFÔ°¨æ³†—¶ÞZÜT–Ú�,ãGU¶;˜ÓßÕã3ÆÿÏk:à†�{·!œçazÏÀdî4ÿÀøÁLx›ŒR:ÄæÅx½:*oçÆ7yƒ 0ìp:ñÆÝC=øÔL-|tŸú«=“ÂZA]Ñę´LØÐ#dó%©Å l`˜ õrQêmL˜!\µÔîF0 ñw¤Hu�Œ“�JBßÓ€&‘šN‹;㤒?Ž4‰0§.ÌOR:N µóÈ4¤Þ¾nSÖ�mÂ�²íû-ã¨e½iä0¢©íiã«y[Õ/¤KGoɪþÅC·4�TêôZjw±ñ•†µÉšS—ç�¦Ó;܄ޞ'5LÊ©5ÿ~!ð½¥¾£ëw õð3÷È`0XíÝ+–IxâJb1asOlÁØ4mRªhB€Ñqè#Ù¸"†«°ñ£4åPZè«MØq3Ѝnó¬I 1!²x / &©×^ÑÌðºÙ$ˆa²˜ŸDéd ·Óo©ÑjèòQÛø×tn릦;…zkž¿„ñ3àu"Ü m%þ¡˜·¶^ú«7!à3¿ëƒ^3ñ"`n›Üîi³·®íb¤Ýb¬iUX0d ‚m1¶Ô)´¶o Gßs¯îân­¬õ´»3»æ6 -ªÝq!ó -Ò�j}@Ø9 ³ü«§?Úø9âÄ$À<©SÙ¹@»þ˜é©eØ«�•­Ä¿m'�TnW[¯üÛ |Þ÷~ôçOªéÏ�ʉä‡Ws)´{Úß[7ÖÉÿ�ª±Šyª F»c`á˜ìº»5‚U=üô=Ú÷ÀŠÑ˜êÃ,3Eéc¦À¹ç£f$-Ö@@™*�I×[6M[§{ÖßÉ& vœš¡�ô~þ„@có Ì‚ƒÒˆg]&ÅØ÷@}ŽÌ”+ÏEâÙAÓA«È…V'bùÌO¶1(s`°1lmmaFð‰ôi´äpž%­nœëôó7uvÆøNb³Ó¯Âyy@Í÷³{næÌ�³•÷Z~µ›*wJ—¦š^šn=wõŽ ­;–äUÕÕ¸œj·Ë@àá—É ?·_ÐŒë%¶{êW]�ª‰¶Ë]9Êž~4‹Ü°7ÕvëÊlñþý¤lÜóèšö?¼¦Þ ãä ª#3.4Ò›üoK´° ÉCú†Í@`¶åçÉEn¶¹Š�ë×÷Ê ˆÕyfz¬: ¥q,†¡5•=çÑ£}h6~?Wï]¼xÉÍLpŒúëòøŸ„*æ»¶§œŸÉν§žzÊÏæ÷e³ª3ã¹h>¬‰8˜T1]ÜÇžŒïõ¶?Áš˜¯mmÃK./§­W´ù»Ã¦ß@×N�tæ¹›:¿uKB–øý5³´Ô€W"húXj ñÛR¹S©83Vqi¢j\©Ñ�oÞ@@š{9z­¶ºÍ¶ÿ­–UІd^häÞƒë4´ÒìªÛi©9­´SŒløóÅ@àyèµä*´�›c{þ¢.œ¸®»Y×Ý3 èö;røÔ‡ž„/pL�…€_¯( ¤ N+*å�äPh;Æ`ãÇú|t�IÊHy¦ÉÄÅ$WgL;ÏiÀöÏ5áÀ܈»'yþ²3̇3;·äÃÁ&öÌ›ñ(my÷¸Û/À…ÍÎ{DÓ'vGŠñu€ Æw÷'_›-×}} íœ4xù�|ol|÷p¸|é²vwv-‘Iï¦õzÜttÏ´åXàãgµæáÏÝ‚Îxúô™Ùç•Ö6‡2±^;9ÒégoèÒ‘m�wJµûMõ7:„x—~>'úmî˜[!›ž™ª¸2U5*Õð|÷³½Ñ&À—þÀ'D'àœQ+i\ÍùÆMM…š�¦Ê[eâØ×[ó$ü_±å™Q1™Ê¿³`ûU κq[z5©ßŸ² 0ÜìÉG› 5šlëÊ%—ºŽë2‘üôÄ7���K|ëÂx±¡#É;±Ý¢ÒU˜ˆØ5f÷Ì©ø‰ý�Q?a€ÀÏj»ÙÉEì/è#·eÙh‘±aüE>ƒ‰AÖÙ”ÖHi,|ใp®~¯¶úê7»#mãD#XFê[×Gºyu¬Ý+¥Öîíiõž®:ý¦Š‰ÙD Ò,©xCõ%éš$¼„ì:3¡Ï%9Ç}øüù¢=hif;Øö¨¥]’ ù˜Ü  ‘f‚X)‡çÜŒO,ߪ/NQÿmð�QŠ‘`(Ú„×&B€[9ïÿᇙ½ï“²)€ó�p Œj›œäþ̪:šYÌZŒkâ�–B+3®ã{Yóñû“häÄ�•õ¹�cëß�ë_þðE½ð�‡5º5Õʾ¾[oÏéöÎlüy¶_dü;·iÔt&Ó½CŽ>ÀÆÛpP}W[²z2>>Ödv´êªóÞžú÷w Z8PãTÉì>ì¬hÐékw:ÖötW“ÒR� ÿ<®°jUM�· ]8tK×Ïîjýþ¾†wwÕî5 Î.¤÷Ž#Í5›J¹¸¹E”�´¿öoHí%u #¶¬²-jÆâ´Lƒà£ím5׿K˜×`”8ÈÐ^`nÚR‡´f‰I’ ˆÝw89‡¨Â±cGuâÄq¹Ç¢£~všn:»Æ'¤é�S�²\œ²>‡ž7-Óé!yï=÷ê�ÐÝlÊi·/ÿë1ýû¿¤çþþ°&£©†mÜãsl*3Ò¡µ(5ägÍqiÖ$4Ó›ùÑ|¤ãͲÎЩ9lÊ¿›œk÷ضzÞV÷#ïÒεÿX¬”…ŒÒ«Ý¾úí®llÛ4(çêvvŽÁßvCÍÙaÎÅÃs Ø÷Ð@÷=¶_«ëw.rfanqI°1Á‘Pƒ= &ž|œˆx¬éDœ…ua1³5�Âî{ÖeÀáìòï(‹¥3��#1å´²Å×JZ¸×�ëç¥ý¶ÏeÔ¤OSñg磓w¥«6yÝ&I©2àÀ1ÿËÝY8Ç‘eéþwnR‘À² �Ó8¸ A�èŸ|Ì̼L4=ͰÓhjw›$‹U˜pï‹:QçEE…­º%ïóx_nät·¶TªÊÌïàw¾£¢Ý¡ 5�p:9æÖç=®ÿd‡�ÿè+Fƒ ½Í¶‚?Xt¸ä1_¨WÝwÅâ à x{&œ“•ABx:Àe .êqM¨ÈÙ�t…ú8 ôz]\ãÐ]èu§þjÀÖ²�´¥ÑÀ #‡x!ŒÃRc�dˆò³n�©OOظRªnaÞJiJµ¼�Užµ ¶E‹¢‹FÀR4jšf†N½žæÁ–§>b,×DGõwg×H�‰µØ# n¡­-W±ÿžß³LdÓŠg6áwÖŽyí;£ÆžíóˆmˆË¸sÇYrâ¦ìkD ãý?r)KÞMRö¾ðÓw¾V!ŽzÜÐÙ(ØØîEƒ×½Žzý眳ˆõøsßÇÒL¯üpÑ9‡·H5yJmÀo~ã6�çº\øá&ùF®†ÀOüäAH¬çœ’&‰=\Úö"€QL…³Ù€Ö&ìÚi‹ƒÏ�í”üj¡-?öË�Xê4=Ÿ” ŽÆÞ ræWÛäEНµF`­5›³l :³¿§}þ9�¹>þñl§\iUk[`:§jêÍGÎ9›fÓ÷Ÿ[Tb¯±‡{qÆÈ‹ûM­g>byœz‘¥ù,Ò�–(ø”x9ì‹À×Ïcu ‹–ìóÎO!Ú1GÂ2=y¶"æ´¥85æ«!OÈŠLs|ßöo¸ýþ;³!�v»…ôâúðFÒÑT,+H$¡ 5HXÍû&‚FÑ!Øäß#U€íñSç´�ž¥ÉÓ1Ç7N9¹yÊÑ—GlýÜ›ßß ßÌ# V˜…Wa–[g3=½M ‡'qŤH¨¾œ0zë˜î/ö(¾×!¹�ªUĽÈa2l¸÷ÅG÷ÓÖ¡F$AÃÂr\ª•}„,×LAg T�:´R=·µ(»F®Ñ‡tj´0ešøæõC�âäëïZ˜='ÓeáãYž}¾—>¿ 5jHj¾¥i^xžÔäœ[¶a^¯`~ü£# Áœ�O�¥Ñ¨í;ÛïÎH§5�äì�Ž9>9RžÛòàË-îk)­µ 5 gUá àñB‘æYNæ2MA'U‰ ‘ÀGÁÏ(PQáÅã›yy|aiêÈS¾uÒžŽÈ73h <)¹ûw9øó}¶~t�K¿v—BSƒ|ÔrËvQÐà¹ô�nÖV¾€q–3 �nBx~ØgòÕˆü�¶‚übFÙa²¬X˜ *^yZs÷è�ã#íH»&ÉE ‰ÁsVø;ß»7åžGíÍ3ïgy¶†­£ÑH‡^Ì“ž±oo^²zj(Ôˆ˜xÈ#Ò‘ÇŽÊÚŒ‚I—g�›12ÂŽþÓ á’–¥Í÷O{ÿj$í{Ú5[r˜Ã˜¦Oj(-z\ ¹C€ºQm=ZÝ ?òîþ†;ŸîÑT(ƒ´½™E;«Æ{—tÅ>Ï3ÄH[ .óH’Ó¾ò*ùæeÊ£]Êã=|9Æö‡/±[ ¬@PPÙ”zõ²ŽÎ ,7 † é鬶ÞDÉs•#¯›?ëïHTËpà+?’cG²îY¿ÒãÂÅ5—QWêéGG52� Oðç7?.BT¯mº… ÂözÀ™òìÆy„dÙ£„0̰Ì+ÙàÔ’¹z5@6Q§5 óÒ&ŵ�þYÝÀÄP,Š™�(ÎT/>9=¦ cÆÍß|åùðw¯óÓ?½ÅdXÒ]oÑ]3ñL9ó=ƒUõ2—’»ÌŒ±þ<¬Ð† Ò,#ñŽÉ×#ü ArgÀ�#eèsëO<þ À‰Ÿ¥fÀ³d—€Ô x—´¯¾Fqá A¾Á¥)ùzF=©ñåÙù“ $" T4´’\ÏÂeK AÀÞ û,‡U%ll½$A_ õÄÓŠ0±q5h^I)‹íCó¼–wkÛÏòå3X�‹é…‚c¶�C�miÅcÀ¨¯�yuïÜ6ä³èÉVWP° QDŒXó8�~óìÓϧÀ7¦�E*À2¥]Ëçíó-Ù¨èn´¸÷åðÇ?åö'{ˆOtßDÑ΢ûââ4'##O2�2â{êÖ¹ò!�J¢ïÑ.ZøIÃIãQà'+ Q€~ 9jÇ ½=Èì¡ý ¥ÏŒ¦E´šI[l¼ñCÊÁ·~ë:—ñyŠ õ°Ö0mÙR�?&M©gî2ÚI¡¤ ‡wÌ3øtxhÎ ùÈyNÍR†§cúŸ Ùÿ6gkÚ>´Ý‡•WM [©ªg mX a*¾6†kpõ–'''j,¢˜ÛjlàÑs�]¦`LÓ”E ëy%žá`Èp4\œö³°ßþŽµäŒ°4 õ•Û`ëÉìw·éçA…3�1?p5¿—Ï¢„¹y|¡ÕË”«ÿÚ!ŸÿÉ]î|ª 5(ºíNs ç£*+’\Ûz!ćúÂÆ{- ¶²™ÁÙ}4ÂÜò÷IÐCŽ œª‚DÀq›/t¸ôÒ&­nA]ÖÔ•GäÌå ¶[ÀV—YXý(Y*š×4*xðà�r |Kxìçs+´ øÆWX*¡e]ƒþð‡ºxµª*�FBöÅy =­“`¯l»3P�÷Ü¿6þ¾þxW…8òNFw«…¢>ø@UV "%ç#Úk®Ÿ¤ä2›ŒÄu¹É AvgÀ·®�ðÔÇŠG+KÙÞìðÝ—¶yå¹MÚEÊd Ħ!,ñžÞµ3Z[-&G¾þí[|ú¯>åÁ»pâpkR!âÐ׋@D3$FÊJë嬟 H±† s:2:Ôú�þôO¾æî—iêF=ŒKÝ£ä¾LàÓöòMe¸”oª4 ÈGÏLGÁBPO¼½½­¿3·ˆä¬1Y«¾Ïó "$Í2ŒxôùçŸóî»ïêç°t庈zš“�Z[atQ±Y±‘ˆ’tÚë…†úò¯¾àÿѧÜ|wGÿÝÍBµõ‰÷Ø*#?šE9óQ—œ#¡1ƒhEÂxÊ®'�æ î!ðu€SfŽð?+@Ýxh@•T¶z\Üèrp2dg÷Á¥Îø i'%m§ô÷œüÖ)Ååœôµœä• é9ÂÈCuvD HL´ÏËž-7Qmÿº¦ÓíF¯”Ao.™OO¸þÞ]îÝØ×½‡—^Z§½VÐTa¦PTÚRQ[5es6±¦Þ@mº6lc•ù9�ÙæãXD`ඨBÿiÕ¤–áâ4¤ý|qŽESc55BÓ1Ýi4 é‰õÿç¶í<2Ç·ïki‰÷AÁ|àÞ\gGCýzÜÐZËél‹ 5"Ö±yš&Øïéß�=Ì8–èñÇÖž’„"OÈœ£?(�™œg®ÿÙèhÛ¨l´&py³Ëf7gÔÐ�BZñÍRt*�  þ4P~0ÁݪH_ËH¾“#k¥‚æ\7@�s iï1aËö”¶Úé�ÎižÍÚÒÜ!yÂè¤äÖG÷Ùýú�ç^ßâÂókT~LÿD7Åà »ç€“”º©§ AÍñ766¦Ä"‹©õš7Ÿ¤ýÜèÁf`lΞT$KäÖ,%Xh9.„´è73ZÓÏ:ýœ:ßÐétìï›`Èc[�½µi!ú¿ùl—¯ÞzÀ�?ß×ëSt3Š ) Ôµ'„˜çÀôÜb³CdµgÆã{nV;æÅ@²$¥Ð "`T^<ÏÄ‘>aû_¿ÐÄ7$iN× ¨¯Cg‹º¸@“µ!4D(� …€€?õ”ïŒq7JÒ7s’—r$ÅtW1PšŸ§YJíæ‰2IšjØv2Ó™›>ìÝnG 9º¹%‚¾™ £þ„ëÜ£ø2£³�°~¥EÑËtú0xã£EŽ€‚Útæzùg)ÞN …ꙃ�ÔVpEq_ßBt[/nzfEk¶êÿ”©7=§)É´3 5 ë <"TIw€;_ìóñïßäÖ;zMZë9ýœÄ‚Þ>ÏâºñÈ–`0ðÏŠy 5 ¹¸ø43(CV£ç„¢•Sä)&@󴎧ÏÞë%Óo¼O29¢)6¨‹-B{ƒ 31ÉC� ¾˜¼5"½UÓý[ë„ m³!‚(X2Œ#¯çPÏ2ºÍ‚5›,¨š1MÄ©²ÊaÍäëŠÑ^MïJAïr¡ÑBh‚> MÝØÔi[M+ìV× ˆ‚Z�¬“nªq¨µ„Å�j0fcÃÓ4B9Û’£¿oJ¿N�WCc¿gâ(ó»í-ŒÜâ åíß§gšÆL ÐÔhã½×"Âl:Ïsçó=>úÝ›Üúøå¨VƒÐêµV›§æ &+zmÁ)ð=Uhhhfˆ#A�ƒ$OÀÃpwÈðÁ€v–©Òû¢‰µHDºñì� tï �ŽöI'Çøæ²²ðÊ&�Ễ-ã÷çâð§ ‰OQ]�¦Öð.±fÀZOÊZß$ 0Y^Iv"ʨ¶äÊÚEÝkÐ/G:žœˆC–Íp v-Â�Æ7ôNtèhíJâµÈõ‹/ðÂ󪊻ÐZSc6ÕÔ(L�¬„›8ÌóBÇ[ øs›lM-húžÓß™T–B¶8ÕŠŽ‹iŽiêëæ �MC.êXÅß{¯gggGSƒç_xž‹—/ðÜýB�Ï�î«~c÷B‹n¡�(ð‡VŒSòŽ å<Õü ‚¶�ŠÚÚ!H4òÝãà ƒ{}†{#$Àúf›"°ŒS�8'úï ϦpÎ ,yP’CðpzŸ‹EÁöKWxpxJTâÐö!!‚$�àTKÀûD½–G%–W¢jZ÷ 饌o ñ“Šä;ˆF>""p(1)ˆ “´’‚Q=Vq5…èö!³¡£‡×ªG°ýò¿öW�ï¼ú£á˜AÈÂaýt3bkÏè»öº…‚˜í3°â¢mç�UОÁW˜7ZÕ7íÁGôñMÑG÷þÞN÷è}v‘‡Ÿ�ùâ�o•²Ûêå¬o·‰ŒÐç ¡ 2—’¿rQN�fšã�×¹þx—bó"Êar4¦oÀx„F¸Y¢ŸO�“²}ýZ§`c*¬;œp2«!ˆ=€§4\6žL-•=0Ë õ^�yÂ¥Í.k½‚ƒ“ Æ}åëË2â•É7 Î¥x^ü¹Š—’¢CåO†¤o$of¸Ë)!Í’PÍ„I�Sh§…î4”c�:Ü!L"z6#ÏÃë}Þ|ÊÎxõ/²¶ÞÕmI��*[ôì)œ¥úcÿm»øl&A#ó\¥þL¹mK¬#a�Œy1HrQ†Üh7ãÎ;Cv¯}A9òºZkm«çHÜlÈ«¤Ñ¢¤9¡Ø‡~Q…y&nŸWâ…z-åKP>Bÿ›#Æc|”즟ËÇéYÔ�z|zíœn;'SN„bŠØÃÏjyšêû<px<м¸ÓÊiÎAÓø¨;©pu«Ëöz‡ƒÓ¡‚“á'B‘%„H .NÈ2½hÔaÅ€)0›Îr4ßÔ4wj’çÔ\Í�‡a‰/m4" �´(H9÷•­˜HÔ¦#%IìÞÙçÎÍ|þþM¾÷K¯òÚ^dýBOwÖU³¨ªc˼8§Ù w‹S& B˜÷ÒzÚìÀüÀ‘�K�¦Z0´…U¥†`&½ýû ×JnL¨&�r@òVdaN@rG¨Õ½ ÚCßΠñ«±ö„†y¹v‹Šlƒu$ðª¦Öˆat0bò`¤­ì$]­ï,½NÎZ§E®u‚0 û]D@�>Uêu[t[¹âàiÕ” \öGŒÆ�VF«HѰ 1 ôÈöa��^›“Á˜½£‡§#´zªŒ<ËìhX†Ð%7E‚j Òš; þAƒ»Z“|/#ô¡Ž/­gŒÄ�ö�¼Í r:èã�åï£ûåsÝht¼7àíßþ„kÝæ{¿ô ¯þèEºkm-üMF"�ôÎ�Ré5ïb‘lñ÷Ô«/ò,×_<¥ÆU¥g½Ç¤é<Áɽ’ƒk‡·¦À÷h$°æ ĉΒ xO½WQ?¬t÷]ú\ à#�–€$ª©­¸4OçiÙ±Bv­”è6©K½Ÿd‚¤¢VÃ�ws­E+ÏðA#b{}–:}§È´ì½þüéõb† frZ“�Öº…‚ÖæBTû°Ö/õüö:?üÎ%î<<áæ½ö�‡*u”ãžh2ѵ’.ÑQ€¤T×&¤Á±þw6žµrmÞ2.$kÔˆ]謫náéx@¿RÖÄ ÐêäôùñoÌçÜäõŸ{‰7~áe�Fƒ±Fg <™àüÀ¥K:ç—”˜v@U�ýýßPtRr×bòMàîOŽyðù ªÀÓqä�È%! ¹ R_3àûÓr¡F!â=fÀÏ„0 4*®)Þ,�TÔ Ç®p¸à”Î=nJ}Ö!àÄ6K˪i·éûkèßøyCU+Pä…µ¶_‹…�×ógÖ°\¬ªŽNGLªïƒæ4ŽY� ¨ÃIÅf¯àõç¶xq{�osëÁ!û{T†IX�´™ Õƒ’ìÅ·žÆ^ŠeÔAéd‚® ë­©˜LÆ”¦¹IQÏ/Â…îk­.§“¡¦Íh‘ô|2‡Œ£ý~ü›òÕÇ_óÝ_xE Ao£CYV”ãjžd+¸æi¯ó^û¬V³UÝÛ¨áýâ~«/ˆŠN¢ùïÞ×}n¿w¨’ëUéÁ y/²Kcµ�DðG “�1 |A ½o1÷[£Æ2àïÕª ÆA�$ 1Ñ¢Csy<”{Æuk‚¯-’h¤ë錄i H$^ñ7q¢5‹�^‹�n¡¤¨ºñOa šO¯í&eÃþÉP=øhR¡·|›�€R63Î7ž¿È_ÿùWø•ï>ÏF§ ´)Vú¨êýÇ_ 9ú{Œ?‚ÛH�³†.dQ¶Ú“ç™AûÿY¦?{qTȦ�Øêl°Öî*1 qÑa¨Îä· ºëmÔüÖüæ¿þS>þ³/u òÚf�¼Èm}ִ߯E½ð?ªÀgy¾ Jš•ÕÚ’SÍŸ5UIt%öþ7C>üowyïßËÝO�5ä.:Ž$“øe™hˆN§hã÷êƒJ�/Y¼ÕW ÕAS¸æF‰ßiÀ‹¾I\¬ï §gu\Ñÿê„þ§ÇT'%’ªq�~0öë’vi[P ÉB;O5Üoe üÏž"ö@iÑ‹qYëYd Ýv¡ÿt²ìnš@„5(?|í2/\ÞàÇïÜ£z³ ý\ ½ÑÚ²‹Oæð' §rÌèó!­ï¶)^o“®¥zc‰0¢æ5u;ÎlspEC�¶8c夃 dy®Þ¨n*�T_!‚¼¢=ûV®`?xxÄŸþï=¾úðßÿ•×xþõK„´a0å<œ5·¸µwÞ»?FQX(òœõ‹Ú™à›Ïw¹ýþ>;×ú4OÖNiod³Ô.Îã‹@–dä.�u�Iø«…Ô©r9hnT0ö�9ÈíæÅ<#‰FÕQÉäÞˆò Dž8\¶ ðk$Iɺ¤yê>Á¯Æa·—:A‡ì²$±. õ ?;A�ø/‘˜ZnÕèvÞ,u\º°†“8ÀZíOJõ*õ�1ƒ¯Ni~°FëûÒ‹4�à—4rHZÐ×ôß:a|}L÷—{ }xôˆ6Ú®ø‰²Æ IÉÄ:�¡§¹œõ •÷ßh˜Mv³—]_5­VÎáÁ ü¿Þ£·Yðú/¼Ì+ßžb#×´ ®šÇMÚ¢MÛr4OZØ1 œ¸sÂî�cÒùæÏ÷QyínJ¾ž`¹ b›@æ*Žx›£‡•7ê¸4¡4„*轎�Ò-�iN+†÷'T‡B£�ýy(C„(H@Eoœ#í¬“´z¸¬¾†Ê¯„A4¬w"ôÚ…Ö !º-(O)Ð0_½V”xˆÓs8®9ê.÷•‡y|ÐJ="ŽÑ' éOÿøHe“’^BHb ‚úzTqò;‡Œ?¬œV`b%5§a¬gMƒ ÿu£¬%¨!PŠsx ¿ãsçV§¥ºû»Ç¼õ[Ÿð{ÿþm®}ø�ªuÖÚ8-Èb‹A¦©Á”�§Ë9mx~�‘Œ¬uÙ^ËÈ;©ÿ­s�ßû{?åÆOvpˆ.ÍT±ÍH•Ý@PcÙN åM¤8|lE~‘³/¦´ä`^Y'^H#¹²*éß:¥|0BRMËk’ÌòóÎÅ…«dkÛH’«A 4QßEÜ,êO*£u ¦úVhQ>-pi£«úqUc…‰PR±Ï'0ׄñgŽþ÷ý?=&ñBãâÛ Òr¸ŽSåàó«lT¡áÔ�é{e®LEõÁ«‘×e®Ø¶´rî´F@»W°·{ÌO~ûþà?½Ëµo|P�¼Á°?¾NõöÖŸ_;v|t¬¯MZ(Yçá­S~òo¯óÇÿä n¼³ƒKœŽåJ"+)äæ’*ð[I®)›�_å=N’•¸þaø^é˺ ­ÏdSà'±Ì_}~¨<¾ïÈ/\!_ÛÒÐ?4 |‰û. ü&0*k†ãJÛŒçc׳j<ƒIõt Àë/lñ½—/ñâ¥MÒDó}š'@vŒÓ-Ääl‚[OñNß?慬ǯo›0‰‚ˆšÏ™±H¬7zjD0c œG¡=š™¬vm}{‰ä¥ Ú>,:9û»Güø7?â�þëû|þÁ-îÝ{À¤‘å©!ñ}õçYÇé¸òןîðãýô�>ãÚOvÔÈ´×s’ììE˜÷øh§í rMÐß=§€K0 ºsM¬šÇŸ”Æå„ºnLf.Ö‚(ðõ}v+­'5{�¤ÕR�¼_m‚V¹0µv¿t²T ÌDÉðûãRϪOMD©À/]ZW€Ý£{ÇÔ4YÕ úžýÑíoŠCI&Ëž÷D�^¢TÚíõ.k­B…I KœÓI¾Õ­¢Únš—ºßÄ1O‡=Þ’…°ºÚƒ=”MUáç´@¤®A«­bœìÝ?ÄO½ÍéJôŒî¶SÖað_»ŽÚ« ~;âÎGÇì^ï£b›�”ÞÛŒl[4ÔÏ\ª÷Òd°~†‡*=5µ–Ö#6JÏ@DSGßxê�’f¯Æ•„Žt„fec„ÀxR[‹'²¢1sŒëвjÌÙÍÚÊO¥hì±@ݠŬ—/ohZ°w2äöƒLïXI€cÿdD?¯Xk+à "6°è= êù pek�­õ޶! ´™$n¥‹ìDf}ûÉl çÌ(-™bñóŠ0ÏV4 Ç‚¨ÊR �E–QͼMLÇ@«ýíDS€jè9øzDÿaI÷âÔä¤-§>D8üfÄ��ؽ> 6½DÅ6 1¡>AH]ªQ!„•WV ÿ/!Ì Êñ­6'àÕnE}¯Ä÷=)©YÒ¢ŒªöËʿԄ Þ¾,k¬~ð3ãza|yâxãù-._è))è\\Ué­Õƒïõ5§qÊ/�¨ êü!èñ£W®pukMÃ¥Fi«ñ’Ï2†Õ�'€-�ïf XDâñúÞ+Eª2”Ûê°xp›\=«aÃÁ×c|ÖçäÞXûøŸü·û¼÷ïîpçÏ�hm¤ˆâ�HIh»œ–d8Ëý£ ½Ö OR$ ÁÆ%꽚òú?ö |I%²ÐKZ=RVEn§×ì™ YËNC�Ÿ¨}(¤J(ª5¢Ø9h‘$hb&™3ë½[6{m”~Ùøå;Õ0¿XI2Nû§Œ'cõ$i/SŽaExk�+Ý-ZYN=CˆzDÍó›l¸J”!bÛ–£´]*¤-QÕ�ã;¾ø­‡ò'ɬªù€+¼gÀoÍr|±Â˜hà[ÚT6ÃzBƒ_Y‚K2§Àür4 ™Ä=ø¢icc¼ß¿ð»“§­ <öA +~øD„Âp\ª!Øì¶Yï<µ÷KD̓‡Y±Ö:9—.t¹°f† >"0ÑMݯ7rôàˆ“� d›úpÄçÈ:(t¹{‘KSC�ñÁÜftÝw†S…a$F› Pt$²�gàt8Ú‰Îåñƒ*ð”:F­óød%à‹‚¿9¬tZPäOó²ßqÄ?Iu+VyòPϯ'¸bŠøÌ€%€l¼‚r•œ¨´6Mm­IV¿[øªÀgÒÔq°·cFù•Bð ü1“¯FT•x$sÑO»MxÏÓܨ º²û<žZðüÃ)ðOfíå,öš[D§µ¡v‘ͦüôZýÿe¬�ëCPFàÞÉPE@|Òp4~¦€’eZí¿´Ñшb~8"ÞÀ|D�ÍRƒ&ø¸0º“ †à bïww¹ÿï¾åäƒCTÚ;– P‡Ýf|å ©þt¬Ã,Ò=!†éL H•‹766TÊ܆zä\à·]†5^£Š•6éÌ÷ÙuœvP�ÕóÂê¬D´7¹6õø#êýZÓ—»h¹ €ßóø›5þn“Õà °iFŠg|p>h$`­�Hàë³Û)LO#r1é³o–©ì:Tw0fÿdÀéh¼Ò&ž0EUÈœÊ0s¾v¦4"Ð'Z,t �„¥ÛZ¬°‘´Ò�”j¿dï÷wxpÿ!§~¶qÇÅ‚<äÊe§ù¶¢ú“1Õ[ÔôœFøØïÔ  )ÁÚú:p®‡J¯MmŸxÀJ˜y|Q�Ï ëR×@Xðù«åûõqMµ£Óyš‰}onßÓÜjw=Läi|ƒAµûÜl:¯�“JÐçX�/+ 0ðõL•ç•ÿ?ûªÀ!Dé> NHÐþ?'ý±Š„v[�™%ôqUx¢ž¬ÕdtӌӺdtÐà.&Pã%é�×Np”€3<"«Õè5*uÓA$@ºhn×4ßT¸Räû-²+9Ò¨ÆBœ!˜-A˜—[aýÕjýêùû5öªœƒÆww<ÑaÑG.+yk­%S£êi†~öÀ*ŠÒ ÀÏ[6�'Ìþ˹:•-�Í�•Äè2Œn+×ݹ'ôŽÐ Aæñã»Fz�׺-^¾²ÉK—7u3O­úŠ ¢EA 0©k†“RUµCX-ú°z‹÷þ逇ÓJý¨‚Y_$~™ˆ'h‘ðG¯\âÍ·u3JY5jD"¸ðMM(Ǩ!xþû´_ÿu&!£*'³Ï"Ñ⥔:t¤z£qm©‚VÏ�¾ßàÚi-郇¬›)Ð\ƒ²¯lö4‡D‚‚•£ëV^G!wúy+ô¼„ªoK&_Œ¨ïV3Í=‰Ÿ}˜)û†�§¾UR_/Uö[dõHReÊu[Æ–;�œØ“óï5ªõ^sò«[ë¼zõëíœÆë3¼Ú<¨#ŽJ&þ¬»³ ðÁû†QYéÜÌÓ0ú‡û#v�¨!À¡´^U#мn‚2ð¾ûÒ6´ ÝT5q|ð�FiÑeX;öŽN9•úþfV‰Æ fræ5âVgÐjUüø”ÁŸœèü¸[K4g®" Î�C‹HIš(!i{£«^…àiÄC „ø\ÒK VŒÞ:¥º[©xŠ´¯yx<:­ÈÆòÛ‰¦Š¬èñ5§vOl lŽ¥]dÓû¢†ºS¤Ôuô3;_Ï¥ªFã’I]aû.eљǫœX­Ÿã©¥ê=�è—Ø?ÑÙ}n�9˜HF@Ã�Æsy³Ãþoj�ë–JÅ1A–F‰h¸ÄI„¶³=r©‹¾¨ó†`÷¨¯­H»%+�ÆŸ 9ù_‡ |Š?UC ^>øå™¥Òo½ª.±µÖæòÅ5Ú!ÃOtGôŠ-I…êÖ„Áo1üƒcê� :‚ë$æ<þlãM!™Nùe.@R¢=~R$z–GSÐOP}þC‰?z[®Í0ôt‚³§Eä4Mfãà!~lÖ@2Q�!r¾ƒÐ*25B—7»JÛµU\áœÕòª®gD­ø¨Õ´1š™€èp\iTmá)MàÂé—Ù?ª!—ÕJ¬¯0“÷ÁëþþË—xóùmºZ#Ðb¡ÍË/cäε'ìÍ A4zCüô¡sT•??c-1ñR6äô7�þ¸¯©I3�”è=pÅZBç$Cþ¸Fnz  XDÎîb¸5¹£úºdð{' ÿð„z·Äu! P¸”–ËÉ\†EjÈ’*æ�?>³ûç»ìº¯_œ?îš9Mp‰L‹hú,lÎF·ëU'6Åá´í;WšŸ·Ÿ¥Éô­¶²T�PÓ„¿J|¼sÒ´”²ªÕ1�ƪiEòŸ9ØÄ<§àWš¸óUvK_#À%m¶98ñ`ïˆAåiQÕ~Û8£E¾4Mthh0žP7¦¾¦9YNg–ˆéµµêÀèã>å7còÍ-Õ¨ÇLjK•G¾ìЊràø0 ·«žèeœQb6OPÞœPÝ))^)È2‡´ZdâLº;®øHæï�9½Êð¡.%!m¥+yCW8Õ'œÜÓm2Ö¶ š @[P&LSjX–0&#´ZÙôÔ÷dÅýÉŽ@¢é‹h¤<—JhK�ÓÈ(y¶fàd¶ý¬ýþ,gÓN*íÓ«Õ½¸ÖÒ¼|¿ÑäÄeˆ¯XBª·¶Šþs³×¢ÛÎôÇÕÌÈÝ$[ð‘,›Ž @"$ë ¡?!»t‘ö›o0yx‡òè!õ¸*&‘-¿Àèq ò�‡›À+/ ´aÆh¡·„ª¼–h^?¹6Æ}§EÒU{ß .0>™p|ë¾ÊŽ%¶Õ'vŸþÝrg‚êî�T¬mwíÚ® ‡oÀ÷š>†U˜ˆ lc ‘> ’ù¶ô<8Rµí‰_•ƒƒåþåœôR'Ús'Ѳ^>ˆ’‰zýÛä‡_‘T}|R\¾LΔj4\Jœæ‹IªªZûU Bz.®w´ Ô„@¹Ur‡÷%ø@qñyz/ÿ€ÎÕWH‹ÍdˆK–Ë™“Ý™!ø(À[ Íõ ’€¬9Hb"­„¿ó.øšl­G5JÙyÿƒ��9m¥q÷Õk'A_[î–Œ¿PÞFi'e»Åçø�Z´í�*m¥ð%ò^ÖÊÜ‹P²ŽhçAåëú£‰>»+K1˜ mÓèšü½“¡ €Ú¼ "„¿¬ãÀYê8N¸·w"JâXö¼X±°¬=A îÓ>ø’ÖÑu\yŠw¹ž1OŒmtm÷Z‹íõŽ ‡¶ŠT#ƒº‰Ó¢ z%�lohn¨ ² žÂÇ‘5¼ÇOÆ„ââ‹ô¾óót®¾®­Íh}y‹:À‰§z{Bù{cš¯JD4"X^, @lÇÅ{\V�o\¢µõˆv'Pà#QŽªñºWÀïÕ ~:þ?òj%“¨ûèÄzÞhŽßO4b4ƒñ̽Æu ­‹ªxZÂ=z6’ýßy,ÓÓŽtÖ7 Óbü!äïáþµM¹¹»N¦”ÓîéœN.%c÷å´[ì<ž‡ˆ¯y–;٭ų½T¼‘48Ôúà¹PÛçl‹Gü9±ËQÔZ¾Î˜/Àl•¹ýûŸïç•^ƒ±w;SY®Ê áÏ„=å;øìRÃãÏ¡!Hä_þ�W®ÉŸøÎ}yëîR1þ ° #ŽÁ×jŽ ïŽÀð³wDš‹ÏààRïbÄ…»ì ’ç÷Œæáîæró�|*nøÒjîqcøÏ~v ûïîËÙãA`q¥x{ßk–^ßÂð;føüµ×ÿä’eÃÏÏÂMÔφpÈ•)ôÍð­Æ÷ñâE^µ§ú£I΄éßç4Uéò#ám 8Ê_‚)Õc�…û'Ö›RyF`†¢µâ‰>d¤àôX+?4Ÿñݦ4ø3ßUÜ»Šn ‡§�€±OU‰|YÅØd8,jj‘ò‡1ˆ·›ç¸}KúÝ ™”ìaLö,uÍ{FZ�IÐkG ŽGrð“}yÚxÎ�åÅ'K-ÿMÏ�v爯‚áóÙ¼{`6ºŽî….Í©¾>ÙÇÝk9â×Zr 3 ßš{#3ü ŽøN r½ƒncø¦5`ïìR)!›´³¹KŠ—J[¶:k„ˆÇQÞí‡ÑUÆ@,ŒåeÁ«7xí ŸŽ„}|ß&#øö]¥È3•’.4? w§´±&zø…œíŽà. i8&‹ Pªßh`Ó·¯n"‘n Yµ€‚o“�zšÙÉÖªœ~|"�÷ ?g½<¦ÕBÇd�]² 2/€WÆ”+;œjÏEDÅéš<Ü\›©&š{=Òü¦Ù¸EÄ÷@U 0úVË{ÄÓpÄøâ$�½„z�¯Âïµ²Ô�¡=‡JÊ‹/’ähÀ¡X_éÑø ,àPkV‚#€yøùÁ±@@JbóÐÂî¨Âù—>f›?›ß÷^».®ÉÞ¸µkŸx/ ’gƒ#9úà÷äøÃwe|üTªVÇAA´­3‚ÿ~£I¯ÜÝk"ÜVãbF3µ¾D—­:'ã\W{Ä/X¶™Zü§cõÉ*:†Õ¯‹#¾˜æž`ød$d�ö±ó6µÞ˽æ]Pç¯/ñËÙyœ+Û[€C%èdÃwMÃ�áónóT ŽðE¦*ÊûÀð¹9÷�ºì8gO‘*ã…‰6Ð~;¦#À]žTB*¹<`õ7H¾ Œ(˜�–8n*Šv9# �|ãÖóÙØ�‘ò©O­®Tí%ŸÈñ'?““�*ã£}0¬‘AÕXjÆX }ËõêÃκ•A9ex6Ð4<”å4’­õUéD±ú"nXý.ôÞlø)•nõ’Aùóù"s{�_&²lg�á¶¼W—“@Ä· NÒἤ¸ÍðŸôM0z“ ÿí9½0@ óÎV§¢Øé´ éð#…jûÊö"®ypt*O›¬xdšñ˜‡ÑÈb’ƒãe£”¢C³�^…åŒñËo<ÑÔdǽÛd?–éðDÏXÚ12L` UèR‹ø‰ßar*íáSi ŸI¥¹&žÖ1v=zDP|{¾à¸Š35[„Zcøh ‚Çh­L @ˆ>>†?ÌͽZ¬ÆO x£åŽd4�³ó\9«ßÆâËK|nÔ©_xþzFU¹QÛcxüƾ+ê÷»j©ÝÁ1â'xÛ¹ L'ͳü¸AþâÓ§Ìîq–,ty#ªàÊ‘¿¹GÏÊøx_*²€À¥H™ßúӯɔ”³- °/­Ñ‘$@@U»˜“l–1.",·+JMË…8üO£Ç€Ñ#™…– J¹‘ø¼Þ"þÑ`,i¨-ü˜']ŒF°óLÈjÿ`ħWE)>a‰RJ#ßI|ß"ÁàÒâìû8¸æð$;vE²0dÑg8/âÇþÑ©C|çÉ Ÿ><8’Ÿ|ôp”Î=&jS®Ý~e“Ô87èj-2œ@{*íÑa–JwÈØÈRйÃÁ è¥å‚ÄýåŸM*”`¼[ÞµÝÃ×\ã{Wßùøá]|)G~2B+JˆZÄϺ;«LM0|k«%‰©†lºcÁ /�(•þV•g�#øìøŒ”’†aps0e1"5$8@I?ûø±ÇŠ„¨Íªù¾ye,A3ßäù8tyvŸÒ"{Q*þÀ£yƒòÞöš,·jQ¤ØM<ðyføŒÐ²á댈iij�$�eI­q"Ö`æ?‹ÑƒvÎP›šçyso±º©¼\PQ땈íÎXÆÃ½NåjC%ÎÙÞMnXŽM„÷kßé…p:ël/` RúÃÓ�LmwZi6='§Ôc ÖJá½O›¹øÑ"’§¬eWf12º¹{u£Q:ºÒtì7øçiÙ~6Té{�Ns?'»OYzk‚á»ô–lZWŸGՔ‘‘Ï<ŒI³ùßåÆF“4�_Œž�¾�o´Ì(³á‹ä%3'nò¿ísbj@ Bûy~>Ëdò-â ß±ëŦ…áóŸÁ(àâ3Ue-"‰ ú<,Ê 9½˜2…£JW·á„{y©›õâq/>oÏͨí•f4u}{µ)5Näç=¦Ì û ʰ8…å³h~ò ˽%œÕ9ib«‘Œžtkµ'Ýn[¼q›š…#*I)Qÿ †o©µFqtÄ�Ó $³<±èHæÁ¨´*Ã^ù^JF‹}[A¾¼”JÂÙÁøA½Ö6æ+Oͦ™&ÞêJgm7kj=–ªe™ò%§ÇÓ\›wâžÒ#t¹@²…¥Y’œŠI³ðá³cyrt&ÃIÊNxvJz w÷6å?¸-ߺw•ïC]ì³êT´C1‘]Ð÷€/Ntôý‰ªó*tXàÓ�N»xœ—Šðó¥á®–V¥³}S¦UW†Ã¡wã#[ 4‰•”¹ÉŒ¶ goRDÇøÉ~Ð!¤1ÇÄbŒ½×ø{¡òbËêÂ’ 鸑0zm­îJg놴Öv„>QºœÛ�K©‹å‡�¿ÐŒE­ùw¡Ï:ó,\8¥á(§á.勔ͯ�|’�ß{=³ßûâ@à@àh¥ÂÆ¥˜ü7Ú…šjGñi'RiM¥(*™a/*L�ágYùÔ]–vCªÞJÎNìw2'5½$"¾kU–m?KŒbîŽ!QŸß˜ßl^–¿†o,ÆßACÖ¬nJ«¿FôÏd¬ÉˆŒ`Ö€^¬ Àš¥/ÄRº } ÔA(N¤� ”è"£> \4EáTôãN bL‰óݦQø§¿{_ÜÙã/:�9"^zl3lÄI˜‹×>^*ŽTÁ®¾äús”ëûè¯ ñXuW¤³qC:›×¥ZZ­�qhïÈgÛ¶ì¬xnž2Yjg}E¨Ï—ûrÑ‚3Z„ÓÀ¹„P„êüæÝtwoK{5G|�ž+�wù/ƒÚz�nÛ1ò‹ÌkÆ�Q–o:Í€¢lÜEÏb &JÁ é8ztV”ÍÇ�}HFðê5ù3�# 匌rx^s`D-Þ-Q'ËV+Û…?4IfÆyY£ÎŸ1…Up3�¾j6é~Õ[1™±EçŸ6% oÒa|F}ŽNÀì^KY´ù\¨Á’’�óòˆ’’·Ý‚á›BT1ªñ’÷T8œ¾X¡×µƒÊ—œ»¥•YwmDÐHÔ˜Né3Øâ/o:Òs¦3qtš›&Ý¥˜¢s9ûp8¢æÖ(œ-1¦Zã\y¿Üüw8&”©aƒ­  ¹‡ákl%¹�óÈ|è¥ †SI�/‘ˆ„þ.œóy¶0¹®~žÍ—îü÷Æd ²4£9ãP”gÔšˆYú~­I¬œÃËíTpøjÊ.‡>;‚‰mVÕ°ÔØ‡ÔÒŸíÓýå³fTx´`q‹hL:�ú0‡b6Ê]&ØÈ¨ :sóY'2ÂXªH¸¢1®ñ×èj�}ˆñ“ @C¦Ñ7FøËï÷«üzë¼[DBWߤ·RŠÍÎ=¥�çMM='¢1€á':èd�a'`ß1;33|žg<ó�DÎ,#=Ùê㠙DZªòN …áǨƕ=;g†3kj×/ÊÀtTiŠ!œð„ƒÊ—ˆh°ûJå“GÏäÝ÷¾À€Ç¦ëj�ž Hð…¡8ö 9¹q¦r65 G–UWp8Õ"WR‰P«Éªœ%÷Æ­+akµO�Œsúuà"~ŽŽv0ðVwªÖëàr0yCôæÆj†/yso¿ß‘¥¥Nx?¾ý™¦œ~& 2\›³ƒMàû˜3ÃY'K³†oÙW±ŠqÊ+äÌðs6·;³KÁpWxÞþ¸²Ô–…rX7V{8O]¿ærÏ ]——‹êðÏ>x$5�ÃÞmG�…ÉÁÁ!ìóy؇³=ÖŒ±5‰ñ!cŹ—¦ÀSX_éó‚CŽ€¿QNœëë7wåÍæoÎ:±T4× n$ªb;{í“XtóYDÜÀ8OEo1ÿÀø�þÈûñS.EÂ.Ž�8f’Îdߤ“Üðí; Ç|§b5 4+'¦YÅØD%¢#˜òø} Ð3ŒÃÊJ‰KÑLUNƒ�N¢Aî>?õ/Ôá·n_‘7š{s¥ÏáÅ“…ˆü8”�>“¿ÿ…À7µ•æÅ)}n äàøËìÃä«MÎɼ#ÍøGðø8ã€G~4•ŒƒØX]Ê C—©¡³#(¹ÔšŽ˜­5{¿�#àýÒá`sÀiÐÍ�–6hÍØÌHpêÁˆ”2Á±’JIÑ+â;‡ ßaüù†Ïohß§Ébè[XÄ÷ïȼ”bdk©>5¾Jd¨m�jÉtt&ÓƒÏdxôTFS <ËE:ÉM�gǺãx;NŸ5Ö Œ†ƒ:š{{­'oš#ðˆUš±Œ-:¥¶çYx¦æsm|Vp¸u&#ø û0‰ƒfRd|x:ÊŽàÙ™•¥A²=�°ÖLÙv}¿%Wú)mYóù>ìF à]`(¤“&G(b$#7|)=˜ª™ÉÈöQ¶ ¥sò…tÓDªvÛ ?ZV*¿�‹¹ª©çKÏCústNÍ`g"~”`iÄ£!<„˜€¨E|iîzt*ãƒÏeÒ=8  #¿± <‡áðx€zwc*"’(ÔÆ|PQ›y“â7·ÑÒóa™# ZÃsÒêBü'�³c�; °§&J2¢ÁækÌ,Bd)÷ŒýÄzÌ¡y7œ4WÆ©¬¯öý‘*‡jc5­þÚ£wÐÑ«ñ¹ :Xx™ˆø�lø£CiŸ=’öð Ã^Åëü0€sfß¿“–÷-¬a™Vu0‹ùªzSÞP»á§˜òx “g_˜ág‡Àú8I/ 2‘¦Q˜—¬W\ü’‰X8zÎ@³ûZk�…Œýˆè™óÚœ‡íÞõ-yûîºìžb¸èY$åf!rg�Oh\–Ь#Èzqd4œ$óë¸Lù4 ðdŒÃZÜð»§Ùð“šÆ@ªž Á,.ûVË‰×øÏ+½žS9ù¼³?ýX”½‘†B�½(YðÊðç©Ø b›½kš“\ªpœÁÎZ_Þ¼I³p½A<¾­JÖéˆÍäŒÛ+6ŸQÌÝ3x$¯\ß¡ž&%·•“"ÌVëˆPdÝÑ9ȹdØ/þJåâ¾z�òei| ­³§1Ã�ƒrbkÂpÀA¦ ÷ó¿’èxl§]ôÍjÍßáÂ2@3#¼€&[)JÈ%D}ëôsFpë n€aØeJÂ�„›MŸGàœíTpØxéL*€à¾vsGØ#€CÓe'u^T‹Øp|L‹á.bX3°ïlø{ÛëÂ( ‹¦J¸/ÁHõmÕ™e^”^¿/.€R9áG2A”„düÅ8ÒèýÃ3küÀx™ûRï¥pêŽ`*L ö¶Öd›%�@ŒëªËÄ>yôµ•Ì…§¼ÀLˉà좧`ZôÛÌ®1 hû¢•)|°³úÒ`(SÑsqòªêŽQÊž-4 ^Þ°M¼“æ÷Œ´Œ®£9„‹Ö×ç‰ö&lª NsæP'Âqdzç¯*@ÅV47Ä5>åèõ�5&`â¨ímÎ=DäÓ�0�¿ê0D:k[¿]&²›_„mb ½‚ �5 Œ‹—æýëd[+¤÷QL8}‰€läJ¦¤ëŽ6ô\ÏŽ0‰íÌ:3•øêj3‘~§Í{Äæj²5_V‚47†ÏØ‘?gMæÂwçÙâ™ð±œm˜Ú¼}s5;³ v^X$’…ñ Q=yvæý“9Ê_»vïÆ–Üº²�á“=^8Ðæü*8`´Gg‚´«ÜÕ.½Ì! uùçÔÓ‘aí“NÉlŽ íŽ€Ælä•EÄ¢̳C®™÷Ò‚ŽsîÌÁ�·€£lEE6}à€ìó~‘_;Áxé�óHÕý2w&kYo!Ej|‹ø«fø!ÉðÅ›oIfy@‡áäƒ)˜/â[ßbk¥/W·V³á“ÅVÎ9Êt4fŒœ›á£Q¤áëï—gÚZmRýÛ¾v�žgÿ›¤ ã1ÁþI&Ť´8ƒÙ?:iFvÇMF0öƒZ8Z6™¥ZØ�Fª–àæÎºéÊ•5è4 Ê< jŠï5HIÁŸ3†D삲ÃÙ‡ûÁœ#ÏûÙ?>�@FÀ!ʆoãDÒnÑó5 µVkÀÚZ0‹øíÊ ?.n2òó]j !“ w]}ÕXÄįƒÁïm®Rò|¡ï$JñOœgR¥ì"•šêî:¿:‚mÚ¾ÅK5ä‹©¸ô7�ÈãÒßå©RKˆt`¾à žŽÜúMç\ÝZ“?ÕPmÿè·î’¾IAõÍ-ë¯3Q9³œd¨ÀÇÍwâ$1vd¥fG{€*�¥’�Ùy5³l¨3ü÷µþ’ÔK!q²©"â7Ñq{�¦,ßÅ"U,âO�€œ!î‚ "‰ÍДnÜd=¾P£°\àÙ-Õ_æ;Í6†ãúü*¼ßæüžX‰TÙïg'¨dß"¿ÍÕí¼o‘Œfl.]J@²®6†‘‘o€o˜¹“fÚ…tÿÌAÅ �…žÞ!€¢jY¨ç>�)çL„”é�>¸ÓHrÝ¢¾uQÅ ð{D¿¢vy°!d4ÉØ¢ùÙhöpœû�l=ZòŒ ^Ž¥Ûmùº­bœ†Am‰¼ª±�<è$A¶ Óâœ~©wÝyC~'阨h�j<È0g">ÑßjÄ~ÏTåˆ�&�kœÊÌh8~Fh´ÊʇÁ–WM ÇJ/Ʋ�FÄSzNÖsî§G¤àЀá¨GÀ7^`0¾9˜®¶5wDÏë„+FBÔkÁ†üÉïÜ“?Ú,ûÜ\ëAà ½X3u‹äͳ àP«;¥ÂìÁŸSÙo¾ˆ@"É8RzՆꙸe‡Å÷0>�8‘fw>*Û|¹ä€ÎSãÓE·ˆïXý™Ã_ޱçïÇ‘çe#¶ 5¾$„ç§\¸nlT“ðZ°ÔììíL”– ªÙ©ÓÚÛ\£yi¬N}a7ùœÛ+2?ÝÒE;¨å`ŒÆR«Ó&#KRÛ ‹O_ˆùMO†Y)–%’oÞÞÀD]#A6>É™5Þ‘M0Z1lDÞt4æ;åHŽs˜o|(D@©+ðb—£¶9×äÁÞ £MRkš…–ÉÍ‘ê«b¬Î΋lÑ­’•]’p|Ä3ž#¶¹×³(«ÏFk‹²ü†÷Â%)#^¡,ã�=² Ïd.ÞÓ5ÓGß`æn�âpF�d:™ ]ÕÏŸ�²xQÍL³Ô²ñ!?>YEo©ëÑêpÐ)™Š±� 4uyvü±Ý‡©€ë^Э}Çóa�7Á4þ‰|?3ü™›óÙk‰°óÌØŒ KóÊÒÜbgêÌ`-0zËæ²ás§ˆ&eMY˜…NøO®š[ëЦ"%ä<>@ísÄ$ÒŒxäKIí+{¿9£Êòvƒá%p…�M®•>;ó‰äÔÕ´Bѯym{UØá}g]¸†ÆâJRªo :�ÑlY³% }åužŠâò‚“2ëSkŸK¾îz«=2‚¾—AÑù7; ú=7üâÏhåôÚÙyG¦*¿°^’M—†°(‘úb$n„�mM\n$ÀÊUŰ©F>+Äq4Å7[ð©Œpz6ömÄŃš†È‰�„÷Ë™¹%@)¹ÆE…1 Î€ÆØ¸\JËTlrYÀh©í·²ÐµÆ¨&^(ŠžV�â¢ËçµlŽªËùnh"ø8ÂX¿â9Ò8K¿“MóÒ"¤Ø„�&§ɉEü$©(M7;œ5|¨ÓôHˆ¶¹, 1b™�³üT=ãŠEüš~CVà0(ú,’—��Sž5÷4¶�8‰~îi™3ó1oú†›€¶Û¼åéštì’ñÒÆ!Æ™ÌCÅ,ûÕë[òν=!#¨Lö¹pyÆ ÿ»b\Çü–ôÌ7Çt YMuj�Eõr]iÑño2mÅÉMDÀ.ï`ÆS|Yvp2Ìç€wÏ9 Ÿ¼a™XòJù„ÄaØð§fø¦1`Ñ7ñs&Ê3¬öÙv•3ª$2gÄ cözj†Y¦c£í¾ÕuµÍâ 3‚M½Œ,4G üÿ%Ø•…¨ïñ´Ðu‰¿úô)o‘ÿ¯HGfÙc:¦ܤYî”T#ìÃÚz§¤~v€.õC¹Mk„P1ZŒÅ³�9IΜÌ�˜`Ji„µˆ/4õ‚奯փ�$qÃŽqFL�|j[3§Þ<]¶JTÚÌžå`&œgW)]Â1 Ýx„;Ð÷{ýö.u5Q5géëë\�Šâµ±à1©ÒŒbî/?ݶþ~¶$JGGP’�f¡OËNÆDrF섌HFÀ:iîçmÿ•¥¹é‚©À0!k¡¯æ£²º}n—2 ±Tã?eÃ÷²±»ÔŸÀïë™@ 4AÔ;é>FŸ#þØË \žm°Ã‘ç!~÷PtFÚÌš§G®lÏy©�LRi<Éì¸WÜ2²^(ûhy³ÃLJ*5¥AÞÒZEˆœ=°H“HþÞç�#øð‘|öôH¸h<#ÿ Ž a‰ÃqÿúÔV—ØRÑE±îñÙ³=Ê34É84Œ4ãw8”E`2èì'Ci–˜§’±X¡üïGq£)õn‚�§y[þ³AšCOUN]e·–´8}�g í3¨óh57O³${Žøj_äÅ©‰pƒûÞˆ(¡-.•�G’ËIÇ„ „£ÂáGðT~ïý‡rpr#-¥äJ¹;}y«‘ÚFi—1ähdŽ`±©0?x„eæõ÷>}"?ýð1˜„ I£¿ñBû죋ÑrGÖ„ ž)PËx¥ÍÆ]îÖR_†u.=fåĞÆ!·XQ¶¹B©`2zEÍÓ‹wj^Šÿ,¦0j6Âýì…ÇH¦2,¼áæ�0o]Kv)˜× P`šëdñRv ¹O–.ÞUŒ5¾Ù®Â"p!è7­éVQGKò�~ùÉÓÆ<qŽ€¦lÁÔ@´‹hT3`"›Š†µÚÎC¾ëõ�U2K%ï#Š~gI–6®ÈÒæU©SÅ_ˆQ­Sz.¦ï%�úÀ�CÙîÅ;€Í5S¤•b¢ÝPÁðƒ�¡C¢açã£9.orÚÅk» 5‰o�?ųhD“ˆæ§‰—îÒqQÓH $¹ue]4åöÞ&ïŠ÷sþá¶š“iG·��Û{Mãóç?5Éçä&¤‡Oùµx©à»†¶+`ZÇÇ™€�l\üÊõm …À‘qª%”!ÕgYÕ~w}Wº›W¤ÕËKI“–9¤äß);3î9³wfk}X””ËywâLsi©4O¤ôvºåNI%Xø Ç „ú˦]2{™ñCFÝöùþ‘°¿<3>ÔÓ·z<Ä2Ï÷ôš ¾Ý�~3}Øt×ȦuQ³0™À‘GvgoC¾}ÿªÜ½ºÅ÷ñŒ „m€# ‹@•™ Ù€móýRÉ£_í€ÛâfÌ—ÝÔ3éØÚÊÑ�Ǹb�¡ù£¼LÄ¥¯b—j"ßÃg'�#Èd’#ªÚˆ\ ¢Coç–p!ѬZk´M,#”ôúÍ+²ÖÏræÜ"$ŸáÔ�<Ãb|;gö®Êxà) ÆÒÜl:ÂÑ’q�rpÂl¿ƒóñ™3gI:Á´8e¶ãéÐŒÄ ¿ÈV�Àg1Æ[]êúøv:å»kwË3u×v¤Õ[~G�N#À_Ý~fkÑkµï$á˃$cÊV$]¶& ISs€LJkÕé®í€à…-ëx.¤#1:��ÎòfZk²”þ ¨Ý‘•¯ËÚÝw¤·}M†9Âh‰ ˆ)µLewñR6ñà–B"` 0ÞN•3‚oÝ»Ú8‚ 6!“1å‘™¤²Ý‡l:Ê»³#@¢ÝVSó~NG†ÕÍ ÷rü,æ2Œm0Æ?�m¯1Ép~#Æ´™q<[’�ÐæWp0(4µ¿èÒ\Dü3ã ÔZð�‚¢«v]R”,ÂcsÀíeÊ®«-–Ž‹¡ù,­/s¢òøðX`Ò­g6<È�ú‚Wn±Ž)uûÒ¿öªàú;×3åtÂè«  20É&JTŒ™ˆ0fŠ\Ô¹+ n_ÙÀ¡àøßüÿÎùÏ£éM¢�Œe “¡ÑrÇv ¦úٱе¶Z^JD¤·øci„m¬ôe©Ó!h؆�u[%cÈœêó^ŒWŠøQ'ñâ;»,uËÿyx’Wi¡ *Ô¤s�3ŒFЍˆ§…)Ÿ$#¹±½&ïÜÙk(À[Òïv1š‚=üɬo,õh ©Ó—þõ×GÐ^Û%•Œ‰—Ž�€RÍÏúÃPxv4ïîmâP…¤¡™ÅQ‹EI7‡Û Û¡ëdµÑÔ6ǽc5¾-·¤¹‡Ž ÿ™ ¿.¦åÚ"ÚÊG°i¡†¥Zßbìì¼'éX/g<ß ¸\n€+Üð"ŽNòV êóìB|•Ñ õj¨AƒP'c™f¡I•163G°`…ó,{7|§ÌOH=4˜Î8è9Õ˜¶P ôC…ÚrÖ"�Gü”\lsFe—Ù|‰PœÚææÜØdzDp9LüY ‹7üöäDÆÃ¡$ê@£v;/>„L$ð)¹3£ñËyÊ™àXÜ&<—S šÙÍwÀn>¤¿qµ•…RZD}:ûp~öÑ£ðòÏO]Ë¡(žÝ=)%p ŸCC¦ápI |ã C‘Û>œ/�c’Ùã‹ýcú¥ê±–¨É™ ™så,o5«ºvLÆžT¦)Iú4å–º`1¼5‰G|ÓÜ[F¤•Èo†oÏVè5ƒ‰ eCŽøs¼ÃÇè&§Ò<‘öè�~OHá‡ïd›¡1Z~7cçI¤WàÙ0 ØGÏN`ŸòyAµh™¥ _fA�ä"Xh²xo¶ ã|¬zCÆWc¸CìòÅÓcjl+òç¥Ñ¾Ì“ðÉQ&QËbx…H/wp6¹x©9½*�Fr�ö1:�c؇æRÉÁW¶%`?óæ*¬lëÑú*ŸgpÃO½­ÕT{ªyí%i™ØfIv¢Ö-lj<ã}þ$+*Wvð‹iÆ ÃW3üýÆð�¤Ò©9„‚ìò.�Qç™äp¬~ EŒÝ fLexߌ~%•ÌD³•‘æ.±ðK³¡š#@ãþóÆ€Ï,*m¥r*/dd§ìžÉ`8õº\ƒâ¥ �)ó"A¬±œQÁ ‹Ê™'qñRsµ{ú"ßæNƒæÎUeAÛV”G¦2dMvp˶ò¬°4P5@Qs«•…]“ OH¾ÙZ°êOp6ø ˜ðüW·W}Õ{¤�äßäÌ ÿP’NB» Íð ²Kßbü‹Mâ«¿ù}ij?$Û#:㸌âój¿ùŒlˆw~YJ€øìïGè­;{ ßxA>>K…ésË‘…™»:Ë5HAÁÙDŽ÷¥wü‰TÓ‘hÕmîv¨Ü™dñR~ &!V6Ø$DÏõæ„ÆTãN€Óò×!…"5­ŒoG°Þ\Cnº1¦óä´4¬zƒ8)ª¶›+¾gEõ°š±ÑhÄðYZÂvÕüÃO)7÷ƒ¡´ˆøõÄjÿªýgB¯[æÌXy–$ñûZ9—øŠ5 %¤6e:ô²pÎQ6|sˆ—© ]‰žÉ@Dô»W7åíÆÜÜÝ0Í¿IA ›ÿº�·àžâ p.fÜÅÇäœUÚg�¥wø¾tO>—j:”ºÕµ¨¡… 8’Àxb NˆêæÅÇ�#°ÒàÈ2‚2± ÒˆL½4è¡ÃhŽÀêüÅ.¯‡YÁµâª71]}žÅ²šø>ŸmÎJ"Ä£¯ˆmæ®eÀØ ßÀ²‘kÛk”T6Š¢¼°äzdÊà†Ïw";=hÎÀ4 ˜uÙ€_S›swÛÙ¼Ó ßní5ŽÀ–‡–F£Y®U­Slõzy¬1øDlÐ8‚gïÉÒñ§RÕ#ÑV§´¶÷ôŽùòÃÓÜà›õúцÑÙx¦4˜ R\Ž-H%#¸y˜€àxWóL \î*‰þ¬d¸õ $…xÏ82gÿšrÎ<¥G`†¿Ð8ï¸yžÊ²1Ãê_Ì¥¾IÊEn3()OK†¹€< 4Ãåî7Ž€ý~Ë]ÃÔ�ÅŒ¼,>ëQ“pŸ³Æ\!T9׊UG¸:gO¤ø�´OŸ-Ü dÏ’¬†×¹aÖñås¬G`Ë&GãZÒt,:‰ ²ÒìÄq¶÷+¨ÏI÷P­Ò €'I©áó³*»İÏ)îu|Ø„?çŸãû8/‹™ÚI½PÈ®ª=PÊpr…ðŸ€!_R ž>sJÆ[¶w¿Ûn£1HZȼ{¦«-’ÊÔ|0›³ †s2šX—YKÑM9òËTªÓ‡ðì™j ÉÁB—ºg/©0¼Yxxt,ã$ý{ß“öê60eXŒç2ú“Po»5G9 9“ �©Þ$7üÃ7*wíͱ ´<�xT !\º3n$nøÈt›áó,-àjD:2+žÅˆG!h±^Z@ê På�>¸‹V=iÿ_¹&òÌ–íoÛ`¼•ùÓ“ü†$ÖŒ%XUÆ�áÙŠìÂýx,'ìO}bSƒÃïÂÎíj@ôÊѬ8³¨l¼º¶'ý;ߓޭoIkyCt<ÏÒsèŠú6Þòš× ß–„Œ0ØT¦l›f _EmwÞª°;¯Ûªˆús}Ï”Å]LsOó8/°aHua¿ïm§“K)ÑÜ?™c•üÖE9€‡bW?~ïúV³›ï¾üèí[Â�GT”iø"KjèMÃ�¯QÔ’¥–S€>lžÚ.z…Ô±Vw†¢š÷>ËãC+ ÂQ³F³�ù.[�hdùr“ˆâ/ŸFFðôYÞkP[FP.í”AL`Ñßÿl_~òá#&Vk·ÏgB¦,C]�Nõ›öÆ5Y¹ÿCé\{Sè\'áy.Pf*e§œ7éŒ[a0ñ£\áÜ�½Qb0Ž"Lam¾ù�„­¹¹‚‡nÆÍî[¤´Åð—hž&ZE¯ßkî¿ÙÜo6÷ÿºÈàÝæþÍýFsÿ'¾[éÁÀkƒ›&í¦É÷g~ð £?¢ƒ¯­Žl!OT pJ_ÒéìC-ûn6ßâ¶×8dm?8¡­@IGðØJ×CÔ�@pš?ïÚèû}ü˜®¿‰P”BxkÑñ™$ú ëWåàl"ûLj’Ä:Èó_Y\$oîÍMÊ䊿¡}‰ÃÉ„³Bɘeåêò¾�ªPyË6q&Q6zbü–9§ø¦¢N‹i‡ �øö¥Øõ³æþ;Íý-·¿o¨ øóôoÊ &{C~\j8ÐUŒ¶ÀÔ'³K!´xO \?órŒOlIgª¤}†H¿×&Õœ[£ÐKƒ NÈ|…xic‰y¸;xŸ=>’vHû?I¶–¡àN3Z’uä6mcÑÀÑ ü®Ç¶úÛ,¡¥™6ú"ê3µÉU©@‰¾5Ô04x bõzµqäJ`F§rúÞoÈèÉÇÄÒ]æA 8G±1œI†CY¦97湬-›Ý�Çû?:µu^ÞP ìYà �€S˜e[Ÿ}h©íÈB�'pþ�æþ6ͽ˭wׯ‡áT†ƒðïÌ xk-Ï�©yÑö!ŸE‰ñÑÃgòqs«f¯�… pÚ¶‰ˆñƒãÚ qs:×L˜ ÇF\Å‘ÙHêá± ?yWÎÞû¿8‚l°Ý~ÎTŽÈSÃÇs›ÂrøsÔTvã,:/ílîroäkèÀ9¸8ŒX&‡Dç5ä½Ù À:ÏTÍ›éÿÙ#þ áâ_ôï[Fðß‚µ°uThªloÀ5ïãÂA»�)»>ÜVi}öä� !{ïª*!k¸XÆÏØ 5>; ¨e5è#ím¯ÈÖj)pç= D@\êöG0hÁéû¿)ãýO%Qî´;s ²Ö™—JÑÖd‡ŒÍkùÉ$ãÚù,�7â£æŠ�e#ˆ¬&)Ø9PÞ©×ðâÙ$¤úýÅÆyDü¿G` Œó.©ˆgÕ<Þ¿�|qUs*ˆ‡Ò€e�KG pFÛa¬|~ ?n†~òG Dâ¢�@± ŽàÎÞ& 88Fn!LºH^¶÷|kíKŽ àŤvWªî’ÔÃ#|ôÛròþoÉéé|¨jë¯øNfø±ô=ùB ÛÄ8ϲ§,êÚ ÿ,oR‘rÈ®ý>Fëyþš—ó�ó~ÛÇyœÿZ|ñ©�fÇÿ9cŽqði±Yuk½?‹+0à<1 c 6ýæ$øÉIíƒ^pOáÍ[»òzsƒ0ÙV U-F9r­AÙÝ\†ë{÷Ë�…IȪNO¦ÃˆKMvr&¤ÌiV$Eô\‡drm�z8ÿ'Îo0¹Ê®£<µü]ªf é~v¤ÖH-ÃæFïÀV“=8ô\ÕÕ/¬9þ]çM/ï^€‹ÏþòŒGÔpi FÙZ¶öv˜ŒG0äB›ƒO‡™s½ÒÌ(‹¨‰ŠÌ[·¯°˜¤ùïË@r�ÖY,Ÿ-"(ãtÛS >#Ä>¬ZiµÛ26…¢G¶Ó_L¼ôë¬)°×2•^·%ÌÞiÎM‚ºú|'�ø#²¯ñ1æÒ–¥½![SÂ÷ö÷¸Ê²®x�ÿöW!ô/ÀìõS2ƒ8þ‡h-7™ZFÐïÊîú’¬^¹%Õʶ±ã†3­ýG�eÈç½,¸ZäŸÁ›Õ`;dó]cÄ©TÝùñ'ƒ¡ЦQ1V©\¼´D†ƒ#XdnïÝrnÔòR×qíÉHù”&ó�@#â�'føö!Zñ¹)hÌ>}v ¢Ð“@rÑ”mÛT`±óLªï5~þb/@‘ÇüÛs8—À’éD–6ö¤ï‡Ò»ù@Z½u©GææEÀ‰h‚QÁbÅ·ÙR×Oµn”m�d0˜:`©è2ð‘±Mvè|që”›íoÜ?<ã=wÑÏf*8J&3½nw¶V, bj;¬‹g¥\–úN)Ž0ʹcõGÓ¼ø„,ÇŒ?ÂÎcì¼´ÔvÒØœ8 ÿ[–êûõÒÄÙN8‚oG±Ïj›}E§ÒÞº!ý†&Û¿õŽT½5Ññ@¦ã±D/[�A}nĦÒ�Øæå É]zF¼4È>4’èGB³þÙµwJå#5Õy&Y €™7½“ªjy#ìk…ÀtÆ o®ÉF0|Ûeéû#Êì›Ï9ó÷[C.ú%v^eN5~ýØ ÿ- ß#þK°ðõ»(z®AhÁE�ÙqªSélß�å»ß“¥¤Ý_–Ñ8ϵC8÷”pìn.ËzG`˜üødMGŒ�:‘OxÔºaEå6‘<¯ ëîžÜ¿¶-ÝnGÎŒ@“ òû;/g3ð+¦F´ €f¸\àÙ‘A´O†³ G�Úú ¶.œñä€em{!J9_áãw¿ÄΛùÅê¿tñÒà¯àac¥� gŒùKoß•«÷Þ�×olâ€xd„¤ò1$�yp(Ó ¶[%Åð Š_�fòáÔ¿ξm¢±ò ¹oB\î]Ûf¼éKP9Ôá&ŸkØWÆÎcp®Ï-2†c#w‚¢lÊ—3Ϧ"½‚dhÄ3£Û‚â�Nâì<Åð™€Gaçý�‹kî½tÞ\ñAPAµÎ«¿ë©ì6ÆKsî•k[€:PÚ¥QfŠA�úêrÊ+¹6V‘L1‰sl)±(ÍðÅc×ìîC´Unî¬1}@Y±“,ÆÁM¥tvn�<¥¬ÀÐè¨cl‹o’e jß¹LöÌaÈ8hG#ñ#ÛˆÅDKÍðûFÒI ÷æ�øÿÐØyÿÞÇy/À7Ò#ø‹3¤#-ÅÞóÓzóã#/ƸîÕÛÔ¶Ö¸+²×®ït*�«ð~*zÿ‹>?×È%K+Sú:�¹‚´4ùžÀ¡‰UÜh2‚oß¿*ß}õ:ãQh¶¾�bƼ¾ à1]}"þ© ·[9U°pde`"Fª81„¼ç(ÉdÞ¦¶[ÒØyK ±ó~2SŠþ+¹€ë¥(o¾ü� ‰ÂE&hÌaïÔõo6ŽàöÕ¼˜d:)&èdÍN%# ˆñ'�9ÐÔ—” s�Ûl7£Å!Uñ%¨8:F�â»÷åû¯Ý »�·ØzFÀí]w‹²@vÀëÆ{”Ÿs¼˜<Õ·±àˆt÷猬!GÂ5oEò†¥cõuΌӅ8Ær�×K×HûnÐpà q¦²³¶,»ëËÂ~»¶# ë‚TÓEK-Œ¬ÉöåÝYø \“þŽž=Œrc¥/îî�,¤D᳋¤¿IÛ‰äôpl²qß{õš¬,µ…¨Zç€Ìô cõGcƒìÚø,€Õ§Ù:´¥ñ2Ájp›–`øÜ8¥‘-?!C`�×îVæ‹Ð[™ÓðG¿t—èúmsߎÎaUÅ¡»PDq[8‚v›»,#ÐLe&ò“¼×8‚ß{ÿ 5b.…Ý͵^³$e—úžC�q04,Àù+Ä•uª$,ZùSßE¾ÿúuæyh%\6´ÞÚ"²cõ)ò{‹¾32q²LÆÓè®Å‰ñT,BÍ;$ÖzŽ3¨5ŽL5ÃÃŒó^:€K8>d.û½æþŸó°U2²2†Ç¡ŠlºåïUv «Î6…/CÒ̃$ôÆíyãæ.ë¿]·dÙÊØ¨Ð ìÜÙÈCùÞh-.È:e¬~Vüµ2 &½…� qtÍyÎcš³ñÕ•%¹²…á÷É´\•ÓŒ…/(7ô\F"ä(2vþx’$‘]ý^.õ–ºdOÀ¨ ETà©s�¹«³}S:kW²$Útâ-ÖÕwÏ¿�¯—àÅŸÐ#ø¯ó°Õ¢ð.; Gpá[¸”ÙîÃ6Û��ãŒ}T¼©þ{×À¬Û÷|f,•b[(#6‹±þR,”b³„gÛ¶-œÍé¹½·N¼³O3·6yöôÎÎLwKY}ÆÉ=IZÇ•gê(Di5à´äÙÔ+ŽX° tãs¯s”ö¹«7»®V$~­ºÐœåíïó”7~HüOCsÏÀSƒ§ŠsTXõ=�ÿcá†3û£®>·+,Ä)ý ·$< 4â¥HÒ^§�›_nz�ÂðRd±åÆ?³8í¦¨3I·¸,�9šµšŒ-í®ËªÍpã§Œó(½õï cÓ¨Ã~Å5¡1ªThЩÇüI…$ö‘Ó*â`‹ÎŠ€ìÃÿ™©ãã0Œ± åñá½ôGÎ*nýv£Î‚�ïpªÑªU4÷Z²Àƒ¤×ßø,õia÷±f�ÇÀ*‚gX|s”|µU ¯r"c6Ž=‚Õ›vↅ%9Z¯Z˜­ìźp5ûOùp¯¬N°¹uŒÛM&~ yHüAymˆ‹ |¹Okî½P*õ“‘ÙÀ*‚GX:~–®7pº¤�ÊûXH`ŒÜ–®Ûâf-['@�ç"I[Öà‡|ñ»;LóLMø 2Äå<ìêïÆæžH†{µœVø¶Ínü&š…éì<ªN“-ú¾•úG“†üŸß9}Ðआ-5™á7¨bk .ÀCVŒ×o-¸ÜÚ-»ÈÒSØ�ÓæKI$×$ô#¸«}~‘ Ï)2¢“âÆiŠOCÀ,#NRôoü—‚ïıçØÓà!ÁçÚ†^uBP èÃ× ,¯Ä%c½&â¥R,XµÑ-]»U­×Ÿ¡Me/"ã$@Œ9¸«O@ˆç\x÷ì<ùòT›ìà3ñ®³8Ž`@_ƒ“LJÅG·#³q:)6 Y ‰°†ë©”s,ýú@ÒÙ;(Ú™«¶§?§õ96Kì¼T>~ØÜûèD5÷ ,¦ñip } Ô+ÆŒát„ÝþšØ�Ëü[q“gG}Ó0+­ Ù||Š–�,÷ëãt`ïAy.HS­&Ór—bWŸ¥þ'C¥¹g`1‰¾×%U¢á'ìC˜�Шª ´½È9Ïó Ó}@*–²��tõÁ·Û{:‚âDU‡“ÊÇ¿»úCýÆ7°…¢›µÍBáÂÒQ A§D::\ÿÏ6Ùdçy'´h•¹*V5èQPÉç óyIUHßÜ{%°ó†Ë�o`1™Í«5*ÆY‰8ãWŒa†f¡†}ˆPÑrñ”¨‚툎<]ƒâË|ªIyr·…¡‘ä~Pá'…´õ½#ßΉo`\ƒ'Ø,ü1¥GàCE ìḀ̂†÷ )ÃÕ§†�;èõ—´ò#˜‚Ð ¤š2,Ù.<÷t*�ªcIq^æ�ÿáHçXL-ÎýÜ,Ô› r_š…E‚�,AP»É“0Às_à"‰9†Ëø}%ñ3‡]ÿ"ñOsç�~²kàמ úͽç¹Àó޳¡`1�Í› ª`�@Æh§ÉøP4 »íÊà¡nþ¨Àg�Ü-ÌHN-¿t‘';,`”¥�t.MÍ=‹)‚[õ¾žìC~>ÞíðÖ“¦vT?–Wž*‘ Î:{¨LÚçȲҮþÇ£µÔ7°˜H_> tq˜Ûƒ×m*„@¶îÚ+#Å*™€Ç$¸t„Å ùùÓx.â®¾Ýø¥0°§Ám)ÍÂ`±µgï1Ý 8œãËqIª©¼ö��æÞeÿ¾Àca`1�ÍÂë44d:r—œƒ»a.„�z½*#:ïÝAÓcK}6÷bQÃÂÀŠa³ð'íî>]rEG`ѪMnÉš­òLh5ªaÆ�£Gý8ï ”úéo| [(º¯87¡"Ј7Hòõ[vºy+6·FŸ ÞbŸ@¡ü¡êê¿xtÇySXÜ¢Zˆ·5Á.7o9(Ä[ ÍÿY¾ñß;6Í= ‹I‚;c¹žÉŒ ×nÞ忝Ø;tZ•%DzPê?! ‹±àh5 =Ÿ¨ò À™V, ã<–úœÅ Ó,¼£8?;Eÿ=¯/õ_ÇèŒó, ,Æç^Ž¿�¦f™†�ÿ"åµßº¥¾…€�äBѯÿƒ± <¯òÆoø$¾…€-ÝM®Á °¤´¹÷–³¶a`\ƒX|‘üKKBqž…€U˜Üõ/\ƒ�Þb©ÿáÈ4Í´0°C®Á-Þûß‹ƒÄ‘ã¼·Gß8Ïâ\ÿô›Ôw8IEND®B`‚PrismLauncher-11.0.3/launcher/resources/multimc/scalable/0000755000175100017510000000000015224505336023016 5ustar runnerrunnerPrismLauncher-11.0.3/launcher/resources/multimc/scalable/shortcut.svg0000644000175100017510000001270015224505336025412 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/multimc/scalable/screenshots.svg0000644000175100017510000024130015224505336026077 0ustar runnerrunner Golden Picture Frame image/svg+xml Open Clip Art Library Golden Picture Frame 2012-05-24T10:08:07 Golden picture frame, Landscape http://openclipart.org/detail/170182/golden-picture-frame-by-tasper tasper clip art clipart frame golden landscape photo picture edited by Paul Sherman PrismLauncher-11.0.3/launcher/resources/multimc/scalable/openj9_hex_custom.svg.license0000644000175100017510000000021415224505336030617 0ustar runnerrunnerSPDX-FileCopyrightText: 2017-2026 Ronald Servant SPDX-FileCopyrightText: 2026 Ludgie SPDX-License-Identifier: Apache-2.0 PrismLauncher-11.0.3/launcher/resources/multimc/scalable/screenshot-placeholder.svg0000644000175100017510000000730715224505336030203 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/multimc/scalable/datapacks.svg0000644000175100017510000003470115224505336025477 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/multimc/scalable/status-good.svg0000644000175100017510000001471215224505336026015 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/multimc/scalable/openj9_hex_custom.svg0000644000175100017510000000766215224505336027214 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/multimc/scalable/atlauncher.svg0000644000175100017510000000207315224505336025667 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/multimc/scalable/status-yellow.svg0000644000175100017510000001316415224505336026400 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/multimc/scalable/java.svg0000644000175100017510000010764615224505336024476 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/multimc/scalable/reddit-alien.svg0000644000175100017510000002114315224505336026101 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/multimc/scalable/bug.svg0000644000175100017510000003221115224505336024313 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/multimc/scalable/custom-commands.svg0000644000175100017510000034246715224505336026670 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/multimc/scalable/checkupdate.svg0000644000175100017510000016226115224505336026027 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/multimc/scalable/export.svg0000644000175100017510000003732715224505336025074 0ustar runnerrunner unsorted Open Clip Art Library, Source: Oxygen Icons, Source: Oxygen Icons, Source: Oxygen Icons, Source: Oxygen Icons image/svg+xml en PrismLauncher-11.0.3/launcher/resources/multimc/scalable/launcher.svg0000644000175100017510000000662615224505336025352 0ustar runnerrunner Prism Launcher Logo Prism Launcher Logo 19/10/2022 Prism Launcher AutiOne, Boba, ely, Fulmine, gon sawa, Pankakes, tobimori, Zeke https://github.com/PrismLauncher/PrismLauncher Prism Launcher PrismLauncher-11.0.3/launcher/resources/multimc/scalable/new.svg.license0000644000175100017510000000012715224505336025751 0ustar runnerrunnerSPDX-FileCopyrightText: 2007 KDE Community SPDX-License-Identifier: LGPL-3.0-or-later PrismLauncher-11.0.3/launcher/resources/multimc/scalable/matrix.svg0000644000175100017510000000503315224505336025044 0ustar runnerrunner PrismLauncher-11.0.3/launcher/resources/multimc/scalable/language.svg0000644000175100017510000002047315224505336025330 0ustar runnerrunner image/svg+xmlPrismLauncher-11.0.3/launcher/resources/multimc/scalable/centralmods.svg0000644000175100017510000027064215224505336026065 0ustar runnerrunner unsorted Open Clip Art Library, Source: Oxygen Icons, Source: Oxygen Icons, Source: Oxygen Icons, Source: Oxygen Icons image/svg+xml en PrismLauncher-11.0.3/launcher/resources/multimc/scalable/news.svg0000644000175100017510000003577015224505336024527 0ustar runnerrunner image/svg+xml Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce convallis mauris ullamcorper mauris viverra molestie. Donec ultricies faucibus laoreet. Donec convallis congue neque consequat vehicula. Morbi condimentum tempor nulla et rhoncus. Etiam auctor, augue eu pharetra congue, elit justo lacinia risus, non lacinia est justo sed erat. Ut risus urna, viverra id interdum in, molestie non sem. Morbi leo orci, gravida auctor tempor vel, varius et enim. Nulla sem enim, ultricies vel laoreet ac, semper vel mauris. Ut adipiscing sapien sed leo pretium id vulputate erat gravida. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras tempor leo sit amet velit molestie commodo eget tincidunt leo. Cras dictum metus non ante pulvinar pellentesque. Morbi id elit ullamcorper mi vulputate lobortis. Cras ac vehicula felis. Phasellus dictum, tellus at molestie pellentesque, purus purus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce convallis mauris ullamcorper mauris viverra molestie. Donec ultricies faucibus laoreet. Donec convallis congue neque consequat vehicula. Morbi condimentum tempor nulla et rhoncus. Etiam auctor, augue eu pharetra congue, elit justo lacinia risus, non lacinia est justo sed erat. Ut risus urna, PrismLauncher-11.0.3/launcher/resources/multimc/scalable/technic.svg0000644000175100017510000000512515224505336025157 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/multimc/scalable/new.svg0000644000175100017510000004331715224505336024340 0ustar runnerrunner image/svg+xml PrismLauncher-11.0.3/launcher/resources/multimc/scalable/server.svg0000644000175100017510000360147515224505336025065 0ustar runnerrunner image/svg+xml eJzsvXd+6kzWPzgb0B5wwAkbSwIEOCvhnHGOGLCNjQETnu6n/5j1zD5mY3MqSVVCEiL0r/t95159 zLWRVPFbp06u+NTZ5Ypeab5VV1JJOSbF42a7Wuo222sx/G1sv17vdbpt9NXCxWJMUZMyPKTv517o g9fVdqfWbKzhW/hmAb29YJX+qlVi17WPRrNRW4wtXO/vnpye7Mcs/XrfWoTHirVuvQoPfjcbv71q u9lWlWSptsiaAGVapS48oOZW5PyKKstaTEmtyVl4wGj2GpVa48No/nMttpLS4CcbU7QM/KTh9l7t otrxPJNJZtCTSfwM/kwlM/Cs1Sz3fqqN7lm7Wa52Omaz3mx31mLm36VG7Lj0AXdKsbtqvd78R8yo l8rfEvQ981Ko1avQzZ9SN5ZDfdb3FfXF6NXqlZPez1sV+q/KCvo69YJLvOpAUVAq+h19nX3Z/4Fv LqvdLjQR6kPjdrFrmDDWzR/8GHyHr4WHi+pHDaag+bRIS2w3Wz+l9jd6DfUKfShqDn9mUafQQ8Xq T6sO44e7j3uMOuz+Rp+CfpABUtU0fOS1WCqjxtIZ2nh3dKp/1ar/WIudNBtVMgR6u3tZ+1cVzXsO /ZBvL3r1avuqUetC4zT0VZ4MwHGzUq3Ds867hXoJ9xtfivtJHiiW2h/VLsxis97rYnTlZHoLBvio 9HcVTZJCKjhtVRvF5jVu34qWlqH5eShMSUPDcko2pmq4aC2WTdF6FPwFbQx6Hb3MSkUAO4PJOW3X PmqNNdqm7Mtuu1ZxJyylAoToJ254Msf95NkPaSF0ttutNmiLASrmMTf1cvL4Euq0GxWz+YNGu4Mg DpPeAFDWmx/knvM7vgOv91qk/fjvF5iYs3atgcqUTvCd3MtZvQe3dtvNXmu/8d6UFsiSPit1PwHM 1UalA+sSPbRSa9Dl/t5Dixmt1wL5LQZrqPRTK39WYXG2FkMLLrZLZag/dvr2VS13oYyLWrlULzfh N5j/Xr0Za5MvBhd1WUYj1o4Z7V7nM1ZsNutcU8+qjQZaktC2d9LgKvxKegCPXXbbGLTNWMt9cCab s3Xtv7hiwEgb5gbfRMOP/sRD+F4HuhRlyP7+eWvWa50f1JIa/t1tGfl7cCFnuPeN0wYBid8gkAed HrbIG/7dL7VR92X1v7hioEWDKz1zy+0CLa5hTP9PrNYs1eu1j3ap9Vkr+9fsFFqmj75DtQNQXi6h +jPpsPpRX99rjQqsrcterYtKwjtorNz8aTU7sM2yCiitqNZLb8126V94lXVnspqih1ZAOmhV36FU dx2xbu03/oK9pNwWuuJ+CRNSBzSVIgC1XmqU2jH8ff+E1brdXrvEiGYJPeD5yqkh8wJUnafbKytS NmY0OKq+2y5VajDUwCVdNeD1aiX2Qb+KKYuS35ewGaVjRkV6kDakgvjPxpdVMAsGXHohX8jBpeEr DZcKl1KQ4SEbfiy4TNuAS7fzds7O2lnJ1uyMnbZTtmortmwV4Bn0z4RLt/JWDq6spVkZK22lLNVS 4JLNgmmjB0wDrjxcObiypmZmzLRkpk3VVEzZlKFJtmHBM4ahG3m4ckYWLs3IGGkjZahwKYasF3Rb t3RTh4f0vJ7Ts7qmZ+BK6yld1RVJl/PQr7ydt6AqVI6ez+dz+WxegysNVyqvwqXkZei7jRoMzdFz +Rz6l81puQxc6Vwqp+YUuGQpW8iirltZE5oD1WXz2VwW/dOymWwarlRWzSpwyZqtQd81U4NGazrU B49ompaBK62lNBUuRZMlTc7AIGag+xkDtTyTR1Vmsui5TCaThgtYMLhkNCdpOw2DmTbTMAppHXUA 1ZnW0GPpdDqVTklpFS45VYALZiZlpWC4UjAcqXwK+pGCBqa0FJSZSsHTKnyqcMloslWYRxXmSYVJ QAOswhCqeQkGCLoP7c2iZqDS0Rsq+qeoilIA1hamH02uYuILZkbR4crjC42Ghq8MXCklJcGHii8Z XXIBXza+THoZ+NKdK0evvJxll7SFIK3CmwSeCJwImDlAZRp6ImNEIiTmAItZjEHURIQ/0wTMYNxl zBQBHQacLQHmDMCJDnjTMNIQ1hDOChhnOsUZQ5kKjVMwwEyAGIJXDgNMy8M8wuyoEkYYoAt+EL4M D740jC8VI0wG7rSAwYXgpQPAELgQvFxoqRLCFqCrAPhCCEPYMgBdOYwwDSMsjRGmEIQBwAoYYrAO McgMDLI8BhnATMpkOJylYJJk/IPwZgHeENZMwJoBWMthvOUcvCHEYbilFRdyEmDOTpkUdwbgTsRe FrBHEJhOpRj+ABcyQiH82IBEy0EiDL9EgMjhEK2ejAeNKUCUqsrwKQMiCxiX8NmHTfiRKEB1ClC0 thlMswBSTUljsKbx/ymK15Si4P/JPwe78GlLADYEYouC2MIXgbJFwWxiIJPf83AxaOfdC0MaDSUM pnftop88Hkf2kxV+NO6HjDD5LSvRXzP4JpsE9xv3YY1ODPvJOr8jwpLBn/C/hClHmlIQ8X+Xpri0 RcE/aH4VPL+E0hSc/y2JTjaadBNPugXTTsgQ+STfMKJkABJcPGTxZw7+yuK/AB0SJlUEJBoAhAAF N4YCJu0ARoANJWcMNg54JIwdy6FuJkCHUDnyk3eoXY5+Zh3Kx6ifCyz4lAR8pTG6GFVUMM5kB2UK wphAJxnQCMww4CQHbwRrpoM1A/+Wx//nPYjL4R90IZqSc4mrrEmyhq+sjGgB+T2Nf8e0gV7o9xS+ 3G9Tzl24MKTx7Bl4xrJ0StJ0/5Cd4S4442vScSUjmcMLlI0i3kckOnZkzFJ01GQyXmRFcjuKRReh 6QwGPwi4+5Lbb/xDLrGrpGvkUvGPeyneS0INmeQ/abLF/SlwjGIQpOF/BcMgjVdHFi8lHaMMrcgC RqOK13YGL/4cJhIGJSQ2ojOU/iD6hElWDtM1A1M7G5FKvC2qmKhmMIXO4Y3AgM3VwptsAe+8KlDf NOzHGuYF0R4Nu7UEmzbavgsYxQre2tOw0Wt418/B/q8DJ2ACT4B4gwLgHfELKeAcEOepAS+BOAod cxcm8Bm2pBXwElGAxKYwP5IBjjYL/Amw05hbMTHnYmcLiJPBRDkFvE0GczlZ4HeALwLeB3FAiM+2 JcQUwaUAXUhhZjwDfBMqDfFQwGYBP4W4Kgu4qwLeKxXEB8B2mMbcl4bYb+DF8rqO+TJTAgbNAoEA mDVY6YhAq5iBSyOeB9g5JEDksDChG+ifCcyeBVwfSD9AMNHMoK0G8YNp4Auh1xIwiFlgE5GYghhG w0T/kPBiAxtZwORXwTsXmg/EmSMmS0OjgIUf1HhoEaoFC0WWBNIRkqRA6sLUCe0viOtGuz3MFZoJ xNUhCQNzsjngaPMgcOlY8DKAxzWxIGbZNuZ6C5JdINtCAVNRsqkWMCeGZTh0ZfClOVfWuXLsIlSa YM9Fn8Khz8WfHwJdDBIUIgzmJcw0mj44ZEjUHCQyLFoUiwyNBI8awaNEIWkwSMJVwIgkmExRTBJU 5igqOVx6kJmWOHDmKDgdeKKBwegk+ExhfLoI5TFqYowCSiUKUwXD1AWqhoHqQtXASEVYtTFWRbSm MVoxXiXUIAxYBlkTQ5aBth+2GLgUtzxyDYJcCUGIA68LXx7A/RDmQEwxTFEsURi7QHah7ILZC2ce 0DykbQxpdAmwZsBm0HbBzeDtBbmGIY1xgyiZS8sQdBzwcCSNAciFkAsiBiNTwjhiSJKxTgBhCaMJ 6woInFxqxyDFQGU7RA/BCouHjPZlsFLCiy6GL4owhxIyWohAxsFMcpDGY42gjeJNQByPOYI6Ricp pZQo8hj2RPTx+Et5MCii0MGhZJocGeXR6OLRJagEkS4mCSoFXEoOND3gpESWxyePUB6jDKUYpxIF KoOqCFYRrrZdEPDaj1jArBQKW82XNnsoNKXS6Vj8xWhLclJWs8gqB7+A1J5Po1+UDIBPAi4nmQcu G+gv/ALEGLHy7Bv6+obEcfrsyjtKFna56hfDuUznomNLOi1RjkehMkyKXhl8MQEoS4UkInsblBsy sXhOJolwRcAXSZg1YgJbhopzRM7LY32A4exWsF9h1QESMZGomeL2LCIEw74lwUJDsrSzccFksI3L 3bpylH1yd64C3rkUrG1IYTUJ5aEkWO46VqeITBRjo9DGRZiofN9+xe9WDgcl0a2KkBdGWggD5e5P JtmbOL6J7UqEZhh0NwI6IdF9KI2JA9AEwrnmfS7dc+U54RFdrk7OVWxQ+VOiwij6kwqoVAxjAqzK AUOhQluawiNNVS0uRDSJis9oCHQHLwwtBhYTkVTuyOhIhMSipIo5atUDm4yENUZIN4A0BkhzgLQI poMfE6shCIYYz82UGCmMI4akHOGAJMwCESzxeGKsUFpAFGWHBEzx/FA6k5IwrLIYWHnKEpkeZKlY aexl0NG2RrBlUXQh9bMqOeAi8NLJ1Kd9rpQosAsCPS//as43TB2AdQQZR3DWHEVCltPXMhmbydyG QFIYrgimbInCi0MTU7LRS8F4IsIVjylGdIhawMGVRBXPSH2Qo5o+lwblqfrGcFWDAj2yKMIKLsIk B2IqViRlKMzQlcW6yayDtDzVUxkcvTKofstimJOITgyDTk5RmkdVbWkq+Lnw0wB+WU4HSPSCTNOK tmBLopC0qD62wOTVif77U+B/psA+TRPVQOE1K3GLGS9UYiPJ9V1eNiPLsSAuY+IyK4x9wfyMhO2C 6CIrlDJAlB2yqKXQpEwTYaDy1DSD7YaU48pg42HaTknYgqhiK6JC7TYFXA6zJiIGT8c2xTy2KWYx P5jBlkViW6TWRWJflKiJ0cRyjy6YGbGhEa4U5lpdWyOxNvrbG9MSNgMpjsnRNTrq2BzEDI9pZnjE 1M7f7ogNQ8QsRMyOxPBowt5ODENZanhkZkeZGoVEiyOxNxJro6wVJLwDmNTUmNOoeUzzuTJ9V1q4 UuJ0UzswviQ26QHTblEO2eibfP/pz0gEAfjqh4AIAi8M/ICQkjxYKOAGETD0w4HYnRkgUhgQIiQs CWPCRUUOo4LiwrVEC8AwKTCIRVq0SauIK3Ot0gQfuoMPgpA0FgsxQBzLtMlBJOdABEAiOSihOAla 9UFrPxsKEQoLaRA2HAHICw1bgIZDF6QAdAj4oIKaFyBeiGCQSB6UiDgRkYKxwkPFDywSRosXL17E 8JgRCYmB1TYcMZEwbjKOXdklKSJ2XPQQwuLihxAXB0ASpTAOhlwR8UFSkMNhOi+r8D+S/vAvShZE QuKgqGDhEH5Jx9RYKpdMA6MUy6lJFUQOVMalFH8Zqwyj4wiqMSaoxpigil8nYmmMCaruN2pM05KI yY3l8tAAOccaNLkCJ9K6fDopw/KbXOtYgbh1moZKQ6ObzaVTqFQV3svkcal54LfRLRkIBZqVdA6e Rg9rUFxGjgHj7DZr7JKgPUALmZsSc0SK6pvk75yEvZNU5p1E2RvXosLsKTYWJVVqSQ== ce0olteGIhpQJMpUI10AYZ6Z3toWrCaOxYRTVLvmkqwjjVlSxqZ66bRgIWHCfYEzjvQbRphZxFUY ZiRfy4ireFY55aDGmUbygmLQVQsqEmchSTuK52CFoKgMFNWBWCEoOfpAVyPoVUb7q6NVRx1IFIIZ rA7MSdBmoom2qe0khVV8WazSQ8q8LRcKrnktzMAmmthccLjqJBDamD5A8zWz+RnZHLD4GNlyUghi tD7zWsFjXmNKac6WITkI8jew6RRHpqCAVnhjhmhokyieTKZ15owZqqBydhXOokGDIoupmyUBWaJR I88ZNVzzG0GTq1jmlMoAHGDSEVpSGBk5QCQCRQFDIo11vflBUBDBwNtbRYurwVtcJYFgiGAgMnmB 0wAxSIiWV5sHheSiQgCFFxYiMEKUhhIzdwkGWJ7E8PpD0d7ltXhhoiP5GCp4q1deMFK4YAmEi8Th pc9AEWABE+1fHAECqKhIvMpQ4ywiK0BOMBlBpAPbX//3qDD85HYmrhNwZ6naDIZR6kO4S+w4Qsft ggTSOlYz2RjIbPtD8DWRax5SeyNShlSTOlFHAkxTeK/LAzAtgKQCYETaa6K5trFWEW1qOcCXmbUB TCkAUTanS4AZG1qbAmxkAQ4mAECBaUfqZh0muAA0IA0zi+bVhgWv4hnNw2JH9F+ltD8Pu4hl2UCx UxKsCWSE0YETt7E1JQXkExlCdGQmKhBtFvZeJp7LxG85i4WWNJZLmM8ycQll3sp+rsomlhRARpA8 fsrMS9mk8qLjoUxlRK97ck5wTQaWXuI8k3XOM9lRD3DKAV41kOb0AoJWQApVCviqBLwaAeoamqKO x9gr1KJux3nqcpyhbqAKdv+0qauxjl0/iZsxcfkk7p421n8bxL9Toh6Eaaw5V7Cjn01dOHWsD81i h7w01p0q2O/Lxsp84utFfLwyWH+L3bkkbEywsIZYx1rkLNY3p7HzlUIdcf5A4Q8U/kDhDxR4KDgq ENkJKFWwqJlBfzCh2O8eFrthQHNqCoVuZuQ8koyz6O8MDUuVfcTryG+MJ0arvmK0isTonCBGM3aZ l5pEp0TCQvQzEBmOhXDEaJnnvxSZiOkq/clQxjxDjYE5+pN3/I2J661FbXoOMyPhjy3ksTDc4o2w dK2shJauSxZsrCxkoU4s2CnraDvTjlJTcVSYthD0pFMtZVZClUPJiutOEeqo6QoLvKQQIDziFlPL JnFuznAijUYN40zOZT/MO50aK4kLBZpRZ3gjkrEoRAy1kDffuGp7qo91rTZ+NhuPxUYcSD/pmboD pgS1C6d4Ic6LKerXr1FXEWbsNwS3RlfAg/lyBkcIJBMJcyBZxrU6WmPXwKDTMfVamxzjgthhxwuN Sf28zG9QXT6GFmHkMzTcw1VRIJMv60jfbiHalUijHU21RafZY/ISG0hlSaxuII2xMIW3qWmZVOzE MaEYOfSUo+kG3IgFYkkUSaFMV8ErKniPy3SQXwv1BKaOLZLjBMwUELajgGDevxoVLl2/X+bzSxwn M46zWt7UJeYtSbpbwEuXLd+0s4DdRYwXMtoaAxxcdbyZUjRLjjKIQNpVBjliPtqmsciVwls3wkMe i16GK9Q70rxGeAAYSHcyxmYDRC5AwmzAGEyAlweQGBNAoOJvW9W5lagJ5g8+ptNdkw6zJDkrQAzr JOsgLwTepbD+TLCeYQWKENcpseg7cTsx6Wbl3UzSdNNi25Z3I8mz+FlCRCVq3/KjoQbdz1wKKtJP au+m5JySH4nRH5cTgkVvca6WiuC17vVZJ86+xJuSd1Xn3dQ1iTpL5ql3JPGLtB1PSOYBydweXVdH 5taYx0On4x0Z2iURh030PvuHhxsmzMAkwvWeJQTCVUih2UcEAuGB6MIReSCKyixVTxJ9U8FQJKqV JBpJosw2qQJboSprDesocthf1SK6CqyaJC6pWDmJdRYmmlbiAI2GBmku0NT++9RVTBFK+DqqHKJc nepwCYw7wDpwygukKGeXdfSdhLOjPkcp7Oim/R/Ss4kqMNUJqxIVYUwNZniU/inJE1QDfA/hAYS4 ZSc80A0X9YQuc8FnbogoF7wsCYGhxKcs7YSbYe9GLijPjb9zw8tyjmscFlEk6m7HQsZkKrg6IeMs qNcNGs85xFkIG3dItCXEjWeJuCZxYeMKFzhuOiSbEW1GtgnhZqSbEW9Gvh0pjtFwoOKElRhSAB4k /0ocpxVB/B0s/Ur9+94A+VCRk7IWIiOS+1hOVIiQh77AtluZ5P1hht88sqw676mpZCbrSowjvIvr TEFPMug9hf6C39Pw86gAYmym76WySS3j1jnCu7jONOzZKFdSElmKszn6Xob1IIva7LQ1gzIeOVUO /yqpEVmg0TioMPOKxl6j1vMsar/TUE1NZhSuxqFfxTUC2YFGcXZx8hqZBnhPmI+sknQhMsKrRMvA zO0ZfjYU5iKQZvb5LH6QvJ3LJ1PchI5eBLQAu3ZgJKCmK0pWw9DAzh2kHBg/gJ0ziV61x+gFjKcF yftqQfJ/nAn+OBP8cSb440zwx5ngjzPBH2eCP84Ef5wJ/pgN/1iQ/0DhDxT+QOG/3Zkgm0+qwAQN 6VDA3hpLnPb1KVCFvKGY13CZZq9LAe9QQNgIMbtRn5gUZC7nJaRUqLHcIxpJnKlYlIxEkTrAxsrM m268vSSYJrmAe16MIKIEi7ZkokSORjlzWmtOwuRzQjmChSQkhzLdoGNnCFOOgOEOpc5kTk7upGMq OZkAveKn5cgargiac8QN0x1o0ZgpOePtCqN4yEXHEAUvGtVxDeFDyzMBTiI550dn2dkkvB5NGh5u 0U/iP2IzDpZLOKE4MeB8HHiKxoCnURS46EKRddT6efp7znGmQHTCdBwrLOpYQSLC0Y+JEtVJNDkg l2USh4jL2Ab7Z/PgNg9+M3VHhB8TZ1S4cXFHhh8bZ3QkboAcjx9hjNxRcsfJHSl3rOhoSZw7hOsM 4Ubr8QZqPsIzMPRX8on9xUPY75LU75TEuyUxxyRFcEzyuCZJTgwli5/MOHGTYlBtxKhqyYmQDA6l 9Q+NDAiMdAfYExnJG6kHWnl4bJleM73LmNDky6aEORPCm7h5cVWsIgkzoKUdFsVjPZNwFlzCqWSF LLiKkHSZGc90mrKV5VzN0CyqKcrAKJJv3mXRgskSL7v2S9eCSWyYtmPBpJlKiRmTT3LL0tw6OTRZ NmYu8ajq6hPcWF7JSfTjZmdmuWDEHDH85U0lxHKBoLzNPrmHhAuvEMo4MtaRMY/MDSVNGEjKQpL5 MfD+laespGvfRPtcSmIzhFPrsgTFrpGTJSh20xO780RS2LrJifF0SThZLUkiowszRhLRujbnDM03 y+WadbYNZnuGT8kxQVtchtm8M4M65hLYLGZpPlmNMhIatU6zC2qSuIl1U6Vw2Yv51CpcMieWVdZN ZGyR/KmSLwBMmtnYmykqKLUQl3zVPxeRNyEy2Vmd3dZyssWyDdnmhpFt14ZjymefLEdvTnIS9TI+ gM/+zBaHu0jY3yxXb9r5pJl7mb9mQOJePoEvTU1bcLxh+xL5kpRKbuJod7D5BNLst7wz7Gywxaw7 9C+JS8nD1hn6nSkJyd+acGW4dL8Zmv3HYeUk+rU3eVBayAPMLm8qIf4eyxPsm5tITCQsM3dE28uv c+y6f4Ysrc+TUMiR5WbJ8qQUFfwJFWqDYB6F/dlEcQZ5ZrvyZmUMziVq+6RjJKnvYBeTfNOJirkY xVR3bqI7N80dl+RO4nIxBmdjdPIxBqS341IySlxaCP+8jJm+DBP9qe64lHfBySv4fKWR0sUOMhtw GTglx8iU9jEy+WXf5HMfOklj3ayHkpB8k5vswOn2y77JzbnkTLoz7T45DvszHIo5DnPu/Et9OQ69 WQ7dPIcOFDgw8HBQWG4gb6pOLzCCIOILGP8UNt48n84sBduS/WbLnS9PtlSpb85Gz1KJ50/iFi0/ g3QOQzJV+s8k9b8UJlPIWNk/n6Ezahek4EmNPKfCDEqBKz9s9QekvoxKFfgElzmPy0eq39iFs7oF uHxgpYqjpJJEpw/qFe6qUFxrrZ8BX4xmwCoqiUsQ6ef00a+j6t/1BBWV1Lfl+bnOB2SEZEE6pke9 ojqqFNcMzjvN8LnMHXWVRNlezfE25QZV8JIQdYC8WZzXBSqSr7ZKzC2tePxsOF8bZ+iJ170z+Brn d2NiRbaQQzE4hWKKpkPU6IEBeaoT5cOdTJrIsOCMp+KwiIo7qhJVnbIf5nbiehuYdKgtpnOiyQhV OtwqDXwhY56VBEVh3pFNDDoFThwMFWgKNGmqmHkwg3+wOCThtINZmm4wz6kU2aebBvNPzkH2zz9L n+phe4UkfWPqCEUVoTS+jlBUEUrj6whFFeFIYSosq12G04vJjkbMFOIr+rSGVPllCkEVKErQiano 1xnmhZETx67/gDJPukBvQEte0LSK48iPJD+WeDQl31SBeTEPXFAauCxFVkbUJXKaRKZv5TWumie8 j6oPJTqE/rEpGVFtKATHBaRilAbkYtQ9Wffc7Gl87jTLTcgo+Sbew6n3uLyMIXnTfGx5uXRSUzLp EGsfe4I4EzMP3JSGvJGRKyz5gk+ulUGJuJCjclZVMhrKAYbO7M3CV30pzyZU3lgZz6CPeS2ZTuez 4yc748r6X5XnLOVnS00tYnvt/1Fb6mAulTBNPm6mfrZU0T05jE/14VIDbamR+FTGqSK5nepjJC68 k6limIxO5HP/0E4d+4Wa5PQWx2E4hYwdJACOD35joW8Kje3CQW804o3EvDkRb1y8G411k5wwt7wb 4Iaj2/jINsonBJmRGTR0qgz0NyOn/c3IjnzD4ceVczTBjGzwPCE9rQ0DSqKpp7Ncsmn+5BcSF8vQ 5XqtuvZ6gjPCmKtimvMc9g/sP4OIJDln1ntXOnJxxyJj4QEJh8YyN1XRKz6Dd0aCPbRfYuzhkxgJ 9jIYfVmMPBNHD9pEHUiCB4lTMoGdhYFXAMylsAtiRowcxDsXczDVaNygTTyQJRisDHZCzgGyTLSF wlaqAp7QJuuJFXQt6ETGYKwiU8VmKTx4uzkDCZM78KfknEBIcUO0zjR/usJhiAkihOxkHGsCM4tT gU/CufiZfdzgUOZKgcwkbjvSoOwRUlIUg2mUk59ILBrNmq45oXosdzo71U93sqi7fg/sHEDLEWmw PabAfK6pfGMzGYceDqE4Cf5l53gjmR4boTp/KTTAjf6qOMIRS9HObEIp55wA8j/zwOi/j38kSqaJ YYkcLaBRWUv7Pxh9+b+zQMX5Hx++x8tcKmdu4M0VfecZcAss51hMsPlENG8xtxTLsYwWOK0JW2Ru Cg83soT6oUg0+ijrHHXhZvNAy8r+44L4xwXRxxt1PAeadHQHGhcghegONC5W9AEONH4CccbfgcYn wYOLHwFBfvnzOaFY8knvoHFoUvsisHUOUzyqqPOIxHmP5DlsZTzeI2LkdY7DmIMygg== MwkDjc+a4WIt5cRc9/kO6Q4gxIRGbhaK/pRGXC4KifMbygh+Q+JpDJaP3C9K/hQ4EpeaQkxOIWbH 8fcY8kmkLnkUAf2qgP5E6u75DGIifow0SVAJuAlF3IT8LCW/izt2ord7ZLyLP1uiKUYIIWO6GTfR iKuhoefIU8rGzvp2UUlP/JYooWMHfzunymOIeqLY/7ORvKO4RY8uYmf9ROwsErEdd2VL4Sx07umG Kc5a51rsmNVO42x3Hgue5DkEMdCKF2iH9RzrKdniuZ7MNuefPYUZ1sTcKXz2FEOi8qV7cbbaoH/E hs4OFHSPFGRXSjhckIhDxD8i68QBk3hNNxbYkLBZ3XPcIDWw84HBTlCwc+ggHxHMbLjYncKm5lvX nYIYbYm5Fj/HHclK5p7MdQbHg2VxTJiOpS98BCsR6wfZzLM4mQuZpv9FIYPB/1Rq1srSzDI28CZU I6VhBtWk9tKUY9rzC3v+/5UU4znxW/HV7LkpeogMkKXaPZK2RibHlJIML8xFNUUzvajUXZPkeylg ZtZ23FaJe6RB3VcZEwKXhP1Ys6REypEQn9YUzQpDuRPqQUk8XG3MqRBexSQLFJ+EiI/yJYwL8Xtl jpXM+zVNzypTaRIZhfp4FRx/WLIgTcrdwCU5zrHE8dJ1kdXogXtpwQGTOMv+EYoiC0W8LwSldu6R OezQHNty/5nc6TniCTp5uhWyjVGTHD9x/jCdlLPfAgUm2YH8TtpyDlbysfCIAgN/rhJm6CSfY5WY GY2OLM2BhxZVaEKGkHQMQbkYgnJTcgFHJDWm/1He/ht+hC1f8uz5rjrZSZfGT3bwhh/xnycwQfJI F312RM7+Kh7/5R7ulHfclLIkfSo744k/6ck97ck98Yk/98k9+8m5yHj3u7BxnozceVTek6jcp3Xe 9in5GT8F4zETmMWYiQy3ElLuSrBkliVUjJ8wKeuiO3EUxDRKpCMmITEpiRw6RVwFsNIwRTzSqe5B x07nyNFcxZ7RBvYW17DDsoJ9jk3sGqwBWVb/PVvhv6dAkg7Xxu7weadLKnKppp3KC67JKSG/muPm /m9r38QLpBwBC8wIuORhL4n9ohZGvmz+ksQ/B1xW2EVOah/e+y3UrChF8H/LCllj+42KOd6oKPlY FXG6WJaCiLMppgWDomNOZFk0/4tk92SOWcU1FdnwqbyOznNjjg1ZIW9dxOdJ2ShkGQUxZ7PMXYEm KCPnxyVzJPUcTf7GVTLkiyNnMxtzFCPoMl5Omo2zdq3RrTU+pJOW8yj//cpK+At5ckdvdy+7f9er HWn1sNH8RwP/EVuTFh6s6nupV+8+LcZWT0o/1diytHpZ+2nVq+wROXbq0dPcluCLiwjx6be6JLt/ /A1/HMAvX/DVP2Lp2HHs4UmOVeDb2wsJl1uRVs9K0PzYuhRbhYbB/7gL0Ge3AwNH4axUr3a7Vdzo s7cIzVx4uID3Ot1282kRd/L2XxL/nXRWHsPRBEZr4aTX/KsZK5d+WrVmoxorN+vNdjWm4JLPDKdL rOXDzOgRNJKfxr4Rww+MAh1FJrcu//55a9ZRMf8X/RoK8nwZUDhtq9Us934A21apW4IFs8r+BnSh v2rlLgxLqf03+fv2+OikWan63lyPLfzzp96A2yulbrdde+t1q51FAC08qrfbpf8zRUygfO6p8met XmlXG+QZNba6DyPj3EUf3b9bVXJ3Ya7Refmr1O6sw3q9hNIbH+Kjf5XqPfYs+r4T8BzSgpLHaEs6 wl//Q0enAasrwsDUm+XvaiXKyLAnl/+z/XqrNSrQUCVC3wAcl9XuCe7E4P7xT09o+sccCSV0JCL1 v1Z6q1ejAH/grP5PWuhrf0Ve6ujR/zCiUffKvU63+fOfpWT/PhyudUqIjUJ7HiyxqHD8t68LaMt/ UVP+N6zSzvs//ot34//RG7ocW72oluohgxppQP/T1G5gN/6O0o2//9PdWFW0TApEJwVknf7n+P50 6rVydd+K0ivn0f9s3xQ1N2iSPqu1j88oxNN58r++S/+oVbqfUXpEH/wv2FcxXv7TOxhuxP/wvesP bf1voq1/6E9U+vMf7FBs1Wg2Q7rz1uyCQHNUfe+etmsftUaUnvW/899CZC+bvXa5ajR7jcp/XHoB Hvs/3YSfardUAWFu3Hbkx2zHTIVqT6Ogi3tYQLC+ryixs3a1U23/VY0Vq//sxuxKrVt6q9Vr3b8d yoobip91VLYgtPVasaNS46NX+qjGzpqtXqu/cDn2XgfgVhvVdqzFqmn+VW23kH64E/5CuV5rxcpN JD3/M9aufsD67jhN8n2j2evWa41qDOnrv6sRH+5Cr2lDZK6v/KPtUqdbba/8VS13m+3YW6leapQZ M+yhPCmNEbtWqVLxtOCn1Pn29LnTanY9T5XqNdr0LCNElVYtSb5K02/KzXrbmV59P6b3us3YBW5n 7V8MIw/H1UqtFGvXOs1671/I5vDEocVvQgtOnzEqujFXJygLoGGAin03muVvGMvYR7vJIBDwKGpG qVuNgcyP9Zh0KDLO3q3v52PH1c6n05ESGtrYRRW1H/1K3kjJwhunvW4L6g9/h2tSPtYqtaCHndpP r15yH1Gdyc/Huu1So9MqwWot/w09q1Xg6X9VRQqCniu1u2/NUrtCbDnkPorcyAU+E1Oh/2wVD3z0 o11lBGPgs22modYymVQm+EmFa8DAR7kGDHzWacACOrsK/Yspapb+ygHv8nr3xWw3W0bzn7d39A3g EpJyyIM37s4PpZMnY7L/K9e16j+GKXuP45McnHzr+2elNqAfQNU5swomECIAC08ZGWCER28+a+XP s3bzvVavHlb/5igLQazw8GXvDRZZoQm08AIh0XkBjbXmV7z981at9BXv22jUYljEHQfg66gN5Wa7 Uq30067Y6kmzK9xW+CXTaLp0O1ZrYLrc7NS6kUmNGgMqFUJe+F0AP2si8m9S8n/Bk/++R08pKb8M ovvCU0WH4GddOJNHCA1ZvSaE3uAJvUCmyNPMVh8rNSqUboVRKvLSEWqC3iYv4R3X+5LiadSpZ7+M Qtbts8uog00ejjba5NmBwy0+5j/e5JnIA04eH3bEyVtDDTltu2fMV40aWyb6pbm/n8tYsFQqyE8i nt4+nXtIbNxszq+XbpcPUnOnK8ZOe/fnc+2jMXVQmFpemDdrpWRnVrvas7WZtZ2r3a3j9Pba0eP8 8U67V84WbPU4F1fS6RlZ7lhf1seyPLuz/pxc2tlYbnV2OofqqhTfWT+aarOHDrrGx9750c5Gunpp 1ja3ylYyOf/RV9VR5Q7qy1qF+Fr2frdrfT0Z6fuVZf2nedTR9y+7n4ktbaZXsNKzN8ZXff5Gilvv 8sGbb2Gz2fx79vr84VEvmsnr4Er559aedja+C087a53kT8JajvcKC7uVdymOB6vw+nLas96fbrJG fad+u/ZufHbNz+y9IgzH65xVVo5+dza2529IOdDkjvn88dyE3+Z+rf3K/pSxkvua1S9XZhqkDbel Sk+K578WEmW7nDlfMD/TL+sbejw1lzBOll8TO+b8VcGs9pa2rg9mPtfL5dI3+q2WsN+PPknNirxa yrZrs69rteeDilGPb8+vtBOPPf3ocu4XtX9xZ/3gMyXFtfXrpx29UZ7/SWwer69mfx43a9nsauc9 pbfL+0rie01xSixbB51rGLbsfDV7k5IrazVztQTzqxxvLqwsV4169uyH9ODuKL5j7m/M3NjL+UwH 5mX/QZvZyprN58TGdeVhTX2becLFbjXi0KEtbWkGTcmDdqOdN9A4bRnfi9oKheZ15UhWnmaOrdXS xlxhKnHfRrVo6MYzLgU/IsXlt+n9NP49sVXYoL9t3NiH5HFz2X4lhal36j5A91ZObG3Zy6q1/bFJ y7nZ3FivfJ0845l0GgzlnRoZWgs8ZBw4DXhyG6AsbF6gh6pp/F1myrBe8FBb1c52WrvXvsp60fpK WO+rh792qTQ/a2hvV+cbe9bLll78LHf1s9nysV5UUzD7evbpbgbeqdzbt6/bPWeICGoFmD5/u4Xl 6sn2Hpuwu6b1fmlX8HhCsaXFxNbM+g2ZIVSyFLdflMS1kb49KOy0259X6bXjm208Q7lMra3B5C0t J4xm/tk7lGLH+XFn40QmFhUlxdcTm72FglVXDPkgswYfu3KNlLOlvTV31ovdab140O31D6VnJrlx ZxN/255C3xWBjv0ma7p3nHoX+WphYa61aH5qF/f2m7yxZFXb7WW5era57jSEDIczGEe7+suBgtG2 sfJaQCv1MGntf2VLZO2TCc1d/v4c6qfPxnHBfD/IycrhW6VgVn7uMPH0mYNdo65t3rhla92jzXtj rziz6WmDFIdWVE+t3e94Fao630AUJiW/r900+1vrfa4Mvy13d1vTb/l8Yj194hmR9f1O892sdWoa opYrD2fq4sz+ntur9era3A+s5ItFBK/jxObB/QGr9OMR6FgK7saPlna7L5Uf/fL1wFqtpadtUsD7 /J6mF4+bHzvXxf1Swc6d30rxteOEyhWB5iDDtpaLQuFlceqTvi2/ynYZxEdMKDeW77Z39ezzVEeX t3pZ9zn7bfVhVj9b3n4gLUSEWYpj0szfX6kfbyzP1G71YnFumdt9FPmtZ883zh6ceflJ2Ne3e2xD WZqz3vVUlaP8zl2YfXrf/rQO2kuv/W8vP2k3ycOs9V5sa9bB4u3lbnz3IC0XHndS6G4T9q6PXkEv P7zDAq/8wiOndwCfbe4u9GVNv5jGXxQW8tqivbL8nraM47kFh0itbsy/3HxmL54qeQDxziZ8FEz4 MNHiKujow2B/ZtEjZgF9nKMbW+i3S6CWwpPo28Kp8+Ql+rhgf+Ii8MPcjUvnxrZQvXnmfBi4FtIA XSiM/IkfN53qTfZiwfApe8upWRer13Et3oZuOYVZ7E9y12TlkIaYTp/PhBHbtIS+FEzPiBV2nHYb rD34O1LsjjNY5+JEuKO447TGecS8ILU4JZKmbIgvbgsl7qC7m+i7TfRI9sS5oQuDwSOC1CLOWyTY DAUaby2BM7jl9j+wyT4YwsNGkOze4ibUnaEdsZwLp0mXAkD4ifAbMRcn4dgh3zkY64NSIJBILS6U juTo68D9k3TI6B9AUrPh6Qv+DU8jmUt3xM7ECsRecQvJ7YHzYZ5xtbj3/cASOL9nzmu7TtdM1iu6 zNwR8yw0Muiogk1nvRRclDh3STl+xIwbaLcWt2XZHaE9wePpvubi5cKpgEMOh2SHkIYDrVV02q07 Ay2SaO4GbpfppTAnhHHa/uz0yC7dPdo/3Wl34xf6ZS8+LW5vTZA7Ogva/Y/9g1i2g9XPZlyTD+xf E21LS1bZNH9AQDC+gYepx2sZThhS8iAMHRraPLCVi+ccC1U86CX4DZp/7gpY24y/RIdYO7TvE+Zu 857bkR1ZDTUqgQUWJLguA4txvyF0SJ7ZuZ5fMKxK/eipYGXuVU8t2fX7owJw45md1Q== K2tvoTGjH95dNoT7pUetfb53ubOxkp22DhIzGUE+BVEYsYM8ywYiLD8sxtuHVV2yQbIQ+sqxRgkQ n4q/+tn+4qP1BiKzTxFEWHf5eyz7ZTqNC4Pwwd1W8jyxfV36leKM886VJsJ5o6IugVM6Nbv2fSX+ ob5ubFuAMczyIP5/43WjYI0vSjj4xF3DyIF5OVX2Hq1SY/cG8eDfBVsBufVSy6uy0pyTs58zVRgT TVtyuDmvHOQWpp8eaUZhw35MOlxWEouCUjxQGIwqCqLCstmrp+qh9X7euVytnT0YdNGgPqfUaZCS wqXbSLLtzVeKIuIq/k0VJoI4I8U3Eq3118LC+2FSlzeK9+ri7PMGY0/5cbI+Vha3CUDOU79N/fDm dhaWT+K3b9j6BSApzotAXFNAVinoVtVZ3Rd0NvB43myuuAsg/109NEGUqCQKry9becs4uvv2NBNq oULVx0Nh8ay+jtevq5NYO1BXFjzFutKPKPus0kp1+cGu5MrP8sF3aU99XV86IxKfvFb5qctALZUD TLj8VhZbDMXszvrh3TRIYJ9TzuyvIRXMqX65W/kCjG221Z2rqTxZHsuzqzD7mws9tQrC4Nw7ueXA HUv0yvKCmReFVK/Evi5vr78a9ca1Kb/nzX2q6OjdwRppGXtGumfIUlxZvuo4q/YpCXTz4VnPrx+u ODeKTIzees1mi0tV+WBveh3GO5lYf1v7zEaoGY1Yf92j10wB6VEIAYVR779nzM/HqbXE1u7Li1B2 8tD4Pk7MJrbO3tPiHDwb3+rmlHvD0dKoVnXq/MSoVw3FrE09zANBNc5hVcqzn7pd/v6dw7OR/2rn C4Xnd3u+oJ/vA4HfPSdSmaxq+hylzrcHBshsb/nV44tHHZWdcvWS/XjRFSnOnmTrVyiMV0eErN69 FWcH9F29WDtK12++Xmi+R1Fg8A3p00gou+Zh3VMe0OSzvfo1v9BWjZr5+WDCOteOX/uL7Z65qzex dfe0RB/Z+gUW4/hjaud3u1xlkzjf03NvrQ8pnlT2rpcREopIq3BnvSenMmRENw/aHXn/YXfD2aLs tYPSW5JQ0C3tRIaqiom9zce0yrEd6t7jvF40Ty+t5EVlZWft5LsGSHZ2LBd5RPs7t3dxi0T4B3u5 edzT86v1JbcwqrJEmoaF00YR7devsEu9XUHZZdXduRmSFaMJk3OZerUOD5vr5uu3kQZ2Qju19suJ c/iuqFBegFa/bH58FlaAPVuYzl2ut+7tt1X5Az7ua2snH+s1+6049ysyMhpZlQ+54tzsZeFlZu6i 8HKmd5Fm/c2/8R/xb5i//DTS1e5BiXbGqGfPExxzQzYjbWNhp72S7+nnS9Y7cH0ra9WWWy1R9yq5 qZPC4t1ZF7gnpeLcOEpsHp5UrMpPftmtGfq3sADbxNwNLOGNtHBj/nP1s/r8yngYbn/FBGdvdwpW ZeV559dQ1oEgpWbslamk5u2a8NzW2rt+eOj3CPCW7KF97Xd1Xe17qH4ZL7x8w1o8ezh4tMvbcxnr cH/qMn8W/yrsdA6OvvBzlML0Y8isTc/CvNBluGZDK4zWsgcdzKSy+rlz/X5loElu8dwhLSyx+5Rd XNUze0/7IoeqUR1sLlczSvZzQb3Sz9dv4hwTTKcxv2AddM4asLq15G589/FFb+xelQobu8qCpzAK uWRlJ//5qNdhCVvH+kVxB3hLnvOmLVsF5vVoSc8+rps7aze/texNKlXVi3qzD3Jq5vvXyCxq93pj b+m3sFEodziobG1qKUcDjx5nDOYR+ngUcLK97w8QbWYa2lD92NGas7fGebyprTSWiw4ztZnSi9fb 3zsbW60ToGNXq4cb9tt8JvCha9gIFjtoN9QdcoSGcm+mYOpPH/Cx8lKwzo7V/gI6yzvNleIe1HK+ tP7pXRZOX709dUwYfDkP1iPwFJkTGOitT3eagCYfFs3PTPcM8ZavrSXjq/tT48u+2UzDHnHRsRNL 2jvPnMPHSuvFeN65nu12heX62sufHdw/cd1FJDgVL/8Akt2hdrT/R5jg6pfdh5r1PrdXz2Xa6zfY eLRefT779MGLhjawfSA4S3MF08gvIUbtBLg6vW2XXh8W+FpktbzTsz5mHu9A/Fgo22VtcUOXtw5+ PIBdr16pZevg+OoWaOneCmD6YVdcPmtYaUwYrPLtyTvW9O52gbe8fbUONlOq/Xz69mS9FxtJt1hk uNnGwiVsBGsH1FQHokI/jXzN66fNcit/knk5gClpXACraReRlSd/8SGuxS/CBsFvnw6LhYqY+dZT 3SVLv2gcW/bb+8tmfy3wSHohfwo7iXxul292cniFCSMmd5bvtfuLpRIwMvO3nj0C90qbLhzPoTkw rYOpd9m3lsx979RTgEuToYitq7StZ7bfjwoLh7t5TnIKWamRYM/sL9RUcF81a5vbOWyf4e1rK1Pf ztQuARPRWbH295H8smrUrerG87R+dn5/AlyRccxveXn9B1iDG+CUqChBTaf3+uVb650YsNTN6pXw Dqes2FYL68vTjlIj77CNZFiy1/WjIlL2z9ovLWRHxqPDbwqEazirIfvEIxKZFeCjDovQ3etNz77A j8ObFgfwvS4W5h7mL0Be2tWs/Z96Cr/Bz77wTq6++3TfnF87eX77hZ39aL5vLeLWgPSm55avfpDN 4tvHGKWxednS7Dlrv564LOi9j7bncYdTZDOtnQP1Oi4sYtYgsX2TeM5evM+X1aV270Rd1L62VHtj r6C+Tq3oauFJv1QLxirQMfU1lTfV0uHyKblPbhXXL1TrRzawlKQWDjPn+E/Vqizp5Dlr72dLTcjX 635tWK/my9fUHAwrFXGwCB0u5jEN2T+9+QD+8OMFP6ltFh43sVUbmTSRio2ZNIUljGpJzdnlaQ0a Wp9qF+zcxR6GBb9esr97F0fYhrv+/jM3BxV8aX67uMO8HVvveTsJLGJyMXTHvZHiduJ0PgHz+7wS 9uQWEgEOZ6HmQjK05pudjXphcQBv+eCSQi83g/Z488TOv+rNz9OlxFbnPu/uZ7hDmwvni3fG+dnO yWptZm3DVZOQtZ8qvNRXNeCjtfpO9vbD1nO7nS5yoFgpLBysXJvK1U/HtWHyNfvJE5StJEIF3tQo d0Eli/PSHpCCprz+3qu2qe+Es0M2l6xVbiMU9mZN+1Xyl7AWLwuFnY+dOvDqxQodFu30GuuUlnpI G3SxtvIxleKYZaTII8Q1Ln8/INFtAbbO1ozWPexuQu+Tq9zDtedlQKXVWcquPScuMYfuDjnjlFKL jRRQ9HRJT8/PN/PKzeEqP2z7pdudzuLttHV48PRLyKOwd6WBkXlvwcbzuKqtvZ3Oed6V4qFvw853 tLdXeOk+rRR25qtF/aLe/OVZPyb0MleQs/ts8aXX0C9nzE3rI6NNbyz3HkEW07Z+l2wva+hsGc5u wfYXurV0qHR3NpfY3MlOW9ghBbXVzF5Wmhu7rdnnFVedK8XXKpVycU0tLZ0Z9RWluHt7f9uAaeoa rg6APAJ0s1d4fep9IheeeUzlKJvnmX25W9JzK8lp2H1WbgAHV8DD6I23j45rW+dLfFZ/sEGYc9wR ir3WLzPPPZjkuzgwb+c98e6NXrzKFvHYAdeHRi//vfN1BD3/uREU32iUX2+UnfWW3NHljZMWLwqj wTLmN4rWUm21vLF8d2jAHrCPZmOuRhauwyxyml7PyLPJcYTwj4ZR1k8P90pYYkcjoQpNOp+2y3fV h/XSTfPLWrW/4gU736zZz7efVyC/FOdW7nwef9WuTh++CO6KR4XHwLILi931JmIRM8bx4mIt29g/ T3jcyfAuRjaF+d14YfbeXrnLfRjpm9QKN/vOktN+D0vbm49WFQSkzoYi1ueUsrS5/rb2vWk91e5m OE6JKwfzBbBj/wQVkTo3P3cqwMPUzRu9qb8v8HN1/jgDfISR08/ul5tu70VZDNCB/C7m7XJvugbS 3VMGS/QuoRRqvs8BJd5DvhErTaY1uc/v3Jwn5jDr7sqfhCavAWf+taw3tqfOCq8LTypaDI+Ye+Sa R8t+eYaddO9CW7u9eoO+VAvA4Ty97sYBaqiodaDi8gfSsS5CBY8rPAcra2szT5H81TL5l4R9nMjD uksswxoqduzy1h4UdjXXms1f2Q+z+Y+bNrRrL4043WfifQUt/L+3WESCosYOmm+x0xbysuzELBwy E83rGLtirroBT6tfzbdk57vWequXGt9iVIr3sXb1r2q7U0Xltb2hNN5nW6WParvU+KiGF1lu1lHY AhdtgYNmkCM09HC/8d6MoS5R59yzdrVSfa81at1mTK8036qxM6uQhHKaZCS8XtH8ILkOujk1neLd gPmnLp1IhBGGEpUGDXq57JYalVK70udrKz4cKR7Db+BYPcV2qdVicQFhDaI+7fuNcr2HXNbPmvVa mfq3L8CgXTVqyOHWby5ZESQQxGw2KjXUuP1KtdGtvdcYEMJqhwXTrTVw9/iKA3o2IIwo+ktCoFKE 94JDosI6R8YFbsN40GFGSBthhLlhEgKD/KpWyKySoC+93G6+lbpHpb9hdXoidv3eM+rVagVFQ0Z9 1sDxk6FjwRVc48jIgIIv3NgQNz7M72k3lgOHcgweG+qfX+RCjaI2qthsDe6q48POrfHAh00Uw2M1 /9Eg6Wq8Szwd+vJxs9F0393/AbqqvzX/ovhQ1Uz0ivtezmiDXi4Cnl2aGPb4brv0NxcVc1hrVAa/ hOvwfSu8bagysWlhQ3hR/Tgutb/Z2kiiMQsGQbFd+0GP33CRS1rYC6fv7wic7eaP3u7+o9n+5mE9 ROfPeyU3/ik9ABLlT6iv6jt0YXW6r7qwcN8cCGKj1A6nic7s+BU/6J2gsQgfRPIODH0Eign4RzxF FNoKa7vaRjgoOoHD4YtN7LV3raVC6ZvftHjJxHAY5Gl2WM0M7Z1RKnE3keHec0lsKOkTRzQorihs a9yvA8PTbZe6zTaKvYa91yy1SAR2reqEfF24wV6xHxT+9TRwx+Y3GbhPgsCibdsF4Pduqm8okDIi jfBbS+qANeEJpwxdeoj9ASa8+Nn7eWuUavVOX7iXl3GNGhYcyDZddap4iQERd4KVF857wAJVYzUU /dgtofmoxTqwMmK/vWq9Xo1VarEKycEK3wCL3Yx14IFS/a9SrNeIIdYrxk044uWqToA0LzDErjoo VPrXpzpabqvawI9AjT/NCvC5ZeAhYo1e868SKgxeqpP6ag2+ymX2fgXWPTwFg1Gv/etfpXa9iZ7s NeaBYQfGsESwBqXXS3+j+G7c1H1k36x9NGJNVkx9npbQhNoaJTQEVVrx//v/xDplGJtOudbrNpOR Wcxyu9Zyp8hJaovawnVkwAJAUlu1LHKq2QDaGDHWP4zHphtlA0mf40gvfVz6UGXQZMV/D/2iR3Ry ZF24R+iG6QxnJNHTd1WWW612kk9KI6Sdjj6JvoQclV1DG1rybYAogJ6ka5Q+lAsvsA== zsshvioCzGa/0RQp4f3nUtj4lYYeIQkxBxX02cRbFk0OkE+H96EthLgHtY0v0n9GOt06bV+rFcKR oUF5q3V/SojxEShuNq8GtfRTjMPvKxS3EWdmCHmm3k4COaRYyYcPSpft8cF9hV6i4upuOhVNCewA N7d+wEPlUeA5gzfoOcLjhbWy9fHzTacE0YwQaKEHqw2UcjNk5srtSrJTbtVDhVH0ULP9kQwDMqqt gzKP96VA6K8Oto2wskh1KPC7FL51o9kvYyLcRYlVQuQAUm+50QnDGzzzXq+1Ppvtf0UYDJpN12+P QY/gdAkDW+8kWXirl8ohuk5UYpdjTAYO718DkU6g1uAZRB+Yo2l9QzoIoBUCqQ1BJ1CDbq1bj15u PVzzIz7srOJAGLbJtjgAPGQhtQeWhiuO3Lp2OE0jlSLm4c2RW4Okf/wsEAakOhy0jaBHkX67FipK vje6yUq91X5vNsIWC3nMJav+GzCq8geJaV5kBNXc6b11wmQzscT2AFWH+LQDigjPhu/vrU6yUf0A 7PwVxkX+s5UUeMWAkupKGQs+oQ8BBeviDPZ0Pnza30kijrVR7YTLQfBc57NUqbarIVsmrtGTKibg OWAOxHoDnnvD2TJo0wJGovpXtR5GOdqVdsfLCvkNe7NV6YUMFS6GsfdhzcZFdUKmDz9QDllNpKpe oxy8kqAMtj9h1f+Awgbu1m554fYH9BzsZGEaDXjkw8us+WuE4Mm298ngnREaVy+1Qrf3nyRLDtfs fjJuZ2CJoTsaHr5So9EM29/dess/f38HKy3gQbQ31hohIwzPuARyQIoez2A2QnUyjnzRdNm7BQub GKmIXopV2kCd2gE1IM7UrSLYHDq4GRUs+3Pb6ILdg/2yGtu/PI2ZzVIXpL/C6e6FrmaDBDo05M1w PtXpcKlDRAhHGuUsrzp7nrO9OimILq93b0seCwl8h2wzRT5jrPN0sfrTcm283Cs43RZRIYnwQPca IFU7DJjs3rCKllOQUwVK0HcI7BW5k3afxqfzFPuzi8Kts493304cNcsiL+PWghRISFbutEpetQV9 mdipunzDuXH4Z/e0cVZirEXKffWsXS3XhFxiXpnfPaVJPOgHzRo5AAgVz+5JWLfHf+ObXCmxlbG+ 09unr1tyZe5yB/+5ld+b/XJvpIxcKqfdaz/3ONkI9rDgXtt7qZrt9lapsVu//nozXo/PdX25oTyt byvXOWum0rOluGXuPT4pS3q2kZkyF8/TndQsijF6W5VXE8dqeuN8bT21Xewa1nt+93tv5mKzZL3L d1vOXTWxeaF9Ts23ir9Ty59fe1OJcjI+tfxSeZhaVguXUwt7HegL+uIumTrKTyW2NltxWstnN7XV WtxoQZMPfmiTSxcG/e31YBv3JbnayfzCb5etvkegf51coVFUF/J3s1IcxknB3Th1W9Z+7HzkoOZc L7G1OzWX7qidY1Zsbjd1sz7zDn/u1uHdO4t1/KjTbq937ttP62en8mr6cgG3FVcKtZBqd5XH9O3n 0bJvpU8fxl5gpZr6tToVVOlb+3k1cQO1iNXSSk+MxZmrVv3Ir9LOzLNmBFW6t3WebVx7KkW14GrT i7eJ9fLpsV+l7d7r2sJSfGPq1a9SuSBvbwRUqs3M5tbf8xjJPn1N3z3KhaJx7tvT6UJrLX5aO77w rXR3pnnkqZSuF1zt/MFR+jhogK/aj2X1AFW62De8u9M3qXimOw+vpZt9c7o2C7NPqz2bn/fMarqY 3a3jSmE1vdlipU/tp+u3i4BK114yV5WPVbdSKc5V+7xSPAmsNLv6fjnvX+nG1GK7s5bo+Fd6ln2G Wih++/ramd+6UwIqzXwuzhvVXf9K04tPiY31H66nOEmcO6ulzMyv1jv2q1QuHJ4bAZVqM/FMLrMd UOndC3IlbBaLvn2d3p3dnDuqfl75Vrp7nrsOGt69uZWp5Cep1H78LgDG+AGeXuzM7yzjAV7qq3Tv 5Sfzu9SSodJsy1vp0f7JE630bmXB01Mpns0kk/dutUJf7w356Oc861/p/nQvd3RXzvlWelqv7rmV wryI1R4mf1vLAZU+JOTLo9+ef6WHqeejQmF7yq9SmJfifm0/sK+Xx1rxNahSS76Wn/P+lR6tzBQr L0vruFIp7u3r9fNmJ7DS64XqSyuo0mP5ZmXb8KtUikO1duJmJ9c2fQf4frn4HFjp13TxyAio9FGT n0qvCVwpwpinrydXte+1+Mmyb6XPz7cvgZU2q9sLH36VSnFU7bH8ah9Z/gNcuFKmb7vn+36Vttsn KzO00rfUomfRJPJLdgpXKsWV0nR3V6RKa+1eypRRpct9lZ5uLvw+27c7UOlm21NpYq15t0wr/c4v uZUCTUbVxt8fpslWrhp3yr5IIC5k++RhF1W60r+nnqzMNlLrZ1Cp2fUOr21/JXGlaF4W7GUPKZyv JChVSs2umYciKawmNnMnD6jS1f5Ktfj05t3eHlR6MOVWCrVgdko1Tj9JX7fXzpOeAf5qbtrfpNLt q6MjcXhhYr8+m3hPBRbqzOTvqt2fKXWt8Ua5i/77vbmpVOWn5X83vQiLZitTDbrbBjAc19y7IrVM 32/KRzspFd/vJ+H32/LRxXY66K4uH1UONb+7GMn3pnw8dZMLetuWT8v7F0F3X+RL87sbcPdhWb68 npmiI+ZzPykXd44Xgu5m5Kup36T/3Y0pud2bytK7/ftL+mFPvr6a2iT3vQsp/XAgX3+sbAfdPZJv ZvO63108Yg8n8o1mm0Fvn8n3M9mnoLtf8tPt/VLA3ces/PT1vsxGrP9+Xn6+Wk8F3d2RX7SXfMDd l7aSXDrMuHc9I1ZaUPKv6nHA228zijH3XAi6e6kcnk8fBI5YuamcfKu1gLcri8rd1+Gc/93Mc6O4 vtG997+rds6m5hcPTuiIqfNbC3vifX0qubezRe56aZvarU9tpA++uLvmytIFL2EtVIoJo9k5YRSG SGDp5QtEdwxAXtXwEy6p+LitxHdWu/NmobidvbPuC3dF697eSMJ3csE0kmXTNFYPE7x89hqvk76k CCF16k5szaTmsbCH6RiSZB5c2rZ6nGvMy6ubtz20Nh6A/L1vOJLozGpt820R1tC03cmdr515eMv2 tDq/ebZCNgokyXB0nK8084kkmaZ/pem7W79KYRfD1U4XKiId5yvFkkxApcC8giRTCqr0FVfqIlno 6/TuSp6rtDI3N+NWivl7p9KUZ3gRd7/OKt2t40rRvJABnl7g+5q+nHUrBYlPSQZWivn7gEq1GcTd P7q8Ja3W6etDYKUwvD9qYKWYu/dUiiU+Wi3i7ytBlVaDK82dnN8GV4p4Bo639A4w4hpegio974PS zNoyrR7/RnG+5jP7/k+uRyox/XgU+JwUF55cPCNPEnqhHmmi3kdYuIUeyNLzD6argsE8GiEubGRT jMLAizvK8pWy7Hw88OI4jDca1Ba/ml4TDVTEhdOGcxDI5pqocaar/3KrR6noMnHygSb0hmdzaQWn TjcsJN0V8CMe7dJWfvfqDP6ci9OP0i3HEZPZP3eXLjz+YCn2U2eXI3Bck7fsOP1YPm6S0SF8OSPH bg8AgSYZQG723ZE3Vj+rVhx9ACS35X23UX5Nch4JaNKyjTWK8B8bUcVP4YcHvRc46O6Q4w/aPyw3 s/45NJn08GyKh4h///DHxYD5Az742J0/pIXzm8GX3hJrPJFa/PrXdCZ5wPxJ8YEzuJ4YcrD6i0J9 oYUtRygsEth371qecWc6paGRJb8r03eDVg4bd6yDDRn5QTCNtnLwjrwujzFYIulJNvtIz91yC9YL X4HT/yFnw0bd2OUK4JT5MIASozuvi3MYl/5jd7fcDW8Npuz4g44dVoz6rkr78XrKj3AHr0oiuvh0 bVs5PBJWpdi5aF1bmYkw0HL1LDmHrQVUQ9I3yrtnP4N7NUd65Q/2Z1uudr5vqO7Cb7YizlVL3CFS /jjfPZwjGLvwJS72o7U61MAEDMtLm9Gx/oHZkavdq1WKHQfJRELxLeytE1gUzP7AwsR1p7T61t2b +hveZynquisgNdF+4LpDNGseeL3jZYcmLbiw4LWj8HipoD71jMNBE4o+aOOJorkfG9A/lwYSmiwU tmws0EbxLUsEALZUkN/N5ENAJ0/PMfuFUzfyXFjYlKR/+6bkO9cOp7R4xilv6Ud83MehV7edIIYg Asd4yu1iu1666t2YwuZXGMXKrlKand8TOFgf/ikS9/Sdnxo0WJtvC7hJvI3Pr1GqcZ8/8N8rndYM bhLi+r7zM4GNcveNaPP3Pes3f4Tnx2R2JnwGufkTWboI88dRfu9gydXSy9VkwIDo2Ff1ejKFuRuK pyigycMX9j7/ezuxERvA1w03Yu9rC/cTGjEPRRt2xKhei+lhulu/HsFVNW6fA4kQ8JZDcMcfe9FF QWGD5ujYHrKh7EVksH05BejQ9/SEVuUesnIcDiMoLwZi7GMvNbt+eDT86PCtYTTC4WBHGJ2XZFRR IaQvg4hCpIbIrpQ0UGoJbMgAKsAaEsJboqaokcWn4IbAoqfeHRFkQ6hq7dd/1+xudUJ2O0CJ1C8b YsOj6EbE3lnBTXLV2dCar32gAiU7HIuSUIGg1uDG7qnTiKDUcInHomcAqacKbpSH8RgkDwc3qTsz QN6PSgD2hyIAhIfx9tDtX3QCENY/Ka4+dRPz4ww6h4MgTsHZX7yoDeDltxesrrJkfBWG6p8UD5jB z+gLMkwmB4ln9/FXlPjGGKzQfd3RXUQbrPAlHggGSpPFJZ7sX+I/B+ISDxDSArRLvIYkNbs2PT2m LuHnQNR1pVyPu2GFim3lcC6KIsDRMiIKE7CUoGvK/NhdS21fF485G1+wgiNwC0rN5n/lCL2SwhUc B1DLdXPsDiEOVtxVh9f7bCsHPZGee4dFijowmeE0j6K+hqP8B6EaGz9SIGgaRFZ6e+2s67LSVGud 2r6KR1AmDmalW4d+OjNxvQweu+0rJVDXFbwN+moUD70boT8ieE44sGt4GxxzvWxfbS5GwLkUD0N6 69C7/Y2A87XzKaSB53e+0ToUT0fS9YUSgEO8341DAPC+DwPj2ep8B2YwL4tGR9zoApEciZedh5pn 5jlTLPxmwncLvspuTuKLuO5uL8PtE36IcHgYd853z3789rtRVGioKKRtHbxeIuhboTCRBx2JJqNF o4yjiyfzguZtcWFcnT4uZcmvFEqThyknMXxrBG9bVs7yRHq14pYiypXRdj6xsGQk7iKapWJxsZ/f vC1GtTCA/DJI7wWFjbvduHIlatnui69zRrCNL3AoK3PqaqR54c3cYRQNRJfMooeiwXdrHBvn9R8b iqJdRaVoUjzEigds5WQoGhoxVNiELEhQlB9FG5JTQnrp1ATWPjLE+9Kioda+x/chDGMDy1mJ2hok I4eUE4V8DG7NqseLQDC9fDYTmwxF/ryAOGGJQAkaa6440+GqL5dCHb3oUkESz5LXqQ== C74LJuXD+VyZcGM/AhHycmYe2+vt9TiGXG9RDpENt+9H4OpRYYL3g19R0kBngd27Fpamx2GlifS6 LLo1jVAOLiWQsZfiw5UT0QXCUwqvhyHlhIrUUVvjcTzysfCGseTewiIqsqRIu2Fpejfh3Q1L00cR ZJoZzo4cuBveDM/f94MUaRQnxt8//gbuhSLGouyGj7/h/L0fQMQYKzyrynQUncsAel+aPp7ILgbl RN59wnYxKGdsDyJcihzJF25wOUr4XijFI++GMGErYQ4SIXshk169u+HawnLfbri2EGUi/PbCPk4J mvLQjeSH5bphBPZ+bUHxWBL5lgmMhbsgg6xvICqEr21pmNUNhY3G6fppFV7akVZ3BLysLWghIzaU cAkzeT2Ay5QG209vb0NV11E88zhNL25UsIoiAqvp2ZZU4/ZuBW9LQi1e34mRhbQ7z7bk8R2lvmuh nmunQrs+A9XQ3FCKXmqBYDGTA2Sj6N62qLBIW0uQQ71YlDYpDYlq3E3PRZhJbt8P8kJEcxnJ+ZbA IlBGfut4vaWCEBGtSX1qYQ7JEZZXi18W8Ju8WnjxhTOdAZJs5fRTiu/Wr0svOL9KIT+781wobp3Z w8fQhUfQ0ajnsWPoWOP9I+jYiI0bQxceQUejnseOofOvlEXQuRLfeDF04RF0NFpw7Bi68Ag6R34Z M4YuERpB50QLjhlDFzi8OIIuOFpwuBi68OdQDO8kYujCI+h8I7lGiKEL94fm7JVjxdB5HJI9+7VH D/NgRXJ24fi64DigX88uFr1R3iYN8J8CXof59Q2ImBJVTKP5r2JL4oMl8r+DxylItj2b9/Ul5608 UcdJVDEFj5M7Sr42catvo+9zLZoRRK6wIDzkrCPo+UfHUzMcmpJvDwP61xc5F7V/ok4JNSoiOAc2 iTflED3/qIM+SHMVul6iBM0NYpsFuAZ62z7bI2oP3fqg6addMbo2mjOIzxzYEWxzUkS18bM9vL7K 7RqLFbUfH2fG8XmgwW4BriCiJ+TgYLfRXEE8MYnW2AYXewDPH31gQlxB/MWQECRHiNAKkWkEFyy0 Kt/UpoeJGKwsjkYe3tTeIA1J1DghaNLLAAdKKWqQaSHUlj1Acdan6UVaSDWamiRAcSaqDRGnu0y8 O1z9/S4aGHtsdoKEpjmLiyGZV3NGjwNDcXzdQZzgEHF8z73wbXKYOL5BvNcSr7QKblLVGwTDy/sR ggu5JoX4wJ9eeAI0B8XxDfKBHyaOL9iYPNT67Ndg+vKW0QsbEGDjKQpre4ILW51MJ5HmavN6QJaA YTrpa70YdcQGePsPN2KpcTrpKn4Jb0mSQAquTh97g6JnIvGye1jnGxox5C72oIiwAdFyrIBgu9je YIKDV7cykDp/7KHIlMkIe93t4LXNWRMWAlWoqIi5qKJ+EIXpbnXkAclRnJkOGpPwQDkpHmmSPZaR EIknwCMChbgFmxujjedWJ1yEkyINx8DQ2KVwAxalMKhD6agdGuTKv2C1PVwkQnLnozuE7BsY6WMH h8D3ITk8/Gg4CdphIL221/3JqHdwk9z17sOPDbPiYZyiLFfO/hISZjeUeseH0SYc7P6w6p0hI+M8 0U+DGsWaNJRGJt10cnb5NGo0PPU3yZtTZfRxGhz3GtIoUSOz2fVqZFBIUgSNDIexIBXiz8HYGhkp nppdW5gNl1QjcugHgRoZj+4igkbm52ACHkTQtfTC2GFoHo3MSPmUcBjaMBoZ/4hUFIY2goOwd5oc jUywFi7SwEQMznH9lILCc2B0/MJzhgrO4TS9q/3McutwUBxqJGb5EK39cT33ttfOB3huSZGlltT2 VTZC6KivSse7Ix9G8J0d3LVFT9cEv4toCD0cPm+YX05IFFcX7LoQPa7OJbI+uaGix9VF9mUMlpIO I4aQBISOCmOD/JODlaADg+s8jnpAI+e9dlT4bnGQj2I0W9mgeLiI+frGjIcTbUksIm7S8XAjY2yo eLgQD9UJxsNNwEM1QjxclIjU8ePhuGjBKNEsI8bD+VPLScfDuVFpw4VqDBcPF56HZFLxcGxexIi4 4PkdLR6O1uKJiBvZrLN73ZxAZD3a3gb02Y+P8OctUWGRyEcEn0goSp2Ed/qSfhnJYhXKqKLQwzHT kGL5BZczdloLXIooPAfFiQ8uZ5QYe28k11V0U95AiuZmAPbPpjW8xzMw4ltL3kAc+9FsDfIiiLYM 76IkhZUixDINyJoSgZ1nNgsoLNgWOqRr8nyftDgCN24O79jgx42jkMHxswHjUugiHEd6xeX4L8Oh cnfgcsZLdYFLwRibDGuPm/Tmm0wxOBNFiF4a9a/Pqwh9FyRQ83nhokSklqYvIoQvDKJjN5OLSL2Z ZETqzWQiUh9/JxKRqkyvTCAiFUqZSEQqKmcSEamonPEjUlH0migeihLf4FhuzwIJSprqcTIKiubw LsOXdv8yfGlH1WsN8LadUCics4uxYLh/SyjcGLmghwiFkwYL7hMIheNGTJuMXOkXChcmVwYQrhFC 4ZyzBf0bNaFQOCIlccFwXC0DQ+EiMoZvHehLMGkaLiM8CnwSHSREy8jwcXXf4WKR12KDuXFfmw0q 7Dfc/BPVJ9DE9v1ueKq3qDzMnTcReYAtKUIeWxzCFiG/mpAm2Cca3UyuhphwhtscoEko07fgdTOS D6rLsBs4s4dPfY7HHT7EcuFiNovOBL+cAn7paGolt/IytXS4rkwtWxc3U8vXz5foWPDi1FJR19Bv Z+g5c2rl8CUjr95+Z+lmtNn85hu8N/tFdUpisNtCSLDb2arMj7EQ7NaZn23yB5yKEXaZz4W32Yvv gLizxcewYLfnlcBK5YKhnQqekGI0lnC0mDfY7Tks2G0q41cpi7Db3W3fOX31RmOFxJ1tTD2FBICd 71wInJIn2G29fnUSUGnmc2nnZqkVFAB2FxJhBwP8wc+qN9jtaS84wi7+vXr9FlRpKTTCbldeC6y0 3fk4mAmsdOpxRrsKPI9vajnsaLz9Oc+souW6gqvHv7FIvF7F7zlMk8UnT2eqUUqcPt2civBcu/fy HRfy9KJe97GiTFkMby8veLbTMJGKd7kNyqXm4WDP5n68bvJefWqYz8aAc6zEMJdRDv2iUhI0auc3 aqNCmxR8Rouvz9VYJ8n5cb8+2RvGPElOmDp6jlzwSXkRx2l+ejifq5Bz0QaeOCL49YWe+zb+IXJO Ud4j5Pq48ag4GHzYCNc/4qsQ2KiB7uHRmhTlnJEBg+40KdgvfMj1Ir8fpR8jNYnXebZErm8S0XSB 1oSJRtP5cdtEbznJaDo/hZefpne8aDq/WLrgzJOjRtP5OYAE+CiOEU0ndIjG0g3KdTN8NN3wWutR oulCkDzBaDq/WDqsVZhoNJ3fDDgUZmLRdH5ys28E91jRdP3tOg6zvY4YTecXSzcgZmSEaLo+5oY7 IXdy0XR+s9sn748dTccPFuOig85LGj2azkUJr0+edDSd3/y5PiSTiqbzi6Xz4ZTGjKbzK8rJCTmx aLoQC+8Eo+n8YunGGrGBwTnDjNhw0XQDRmxC0XR+sXR4F5toNJ3fuuJOyptQNF2wl9oko+n8Yul8 /JTGjKbzi/3ykV7HjKbzi6XzsyWNF03nN0NeX+vxo+n8YulC5MrA4YgcfhNkeZ9ANJ1fLF2UHERB 5gPUpGgCoNcPVvSoXOv0Be8sWANZjP4wOj8p6Wt/0DGVUQOfGLWIyF0McV6dH8MThbsY7rw6P/cJ /jy+gdxFtHHyOdnW6wcbcZw+B27fAgQC85Cgc+8CTqIduklenj8SLn2bFHoErRgpPKhRQ4XGLgZS GNSoYFeK4cbJm6sz2g4iSkSp2fz3qigRHXizi/TbK6OpwUY85s4zYv4H3Y3AkovH3EU8Z2TMY+7C dRf0oLtxlDH4mLvx/ZOjHHMXwT8ZBdKNecwdojADD7qLODDBYUoBJ00EuCmMesyds1eGHXTnBFWF H3MXMS8c7Awz44LhcIJxFq3DoSKLsEYxKJp57XzsWJ5DvHUG+ipED6SL4Hk4yNcazfnYEbA4ijOa 92/YwkXH04l75ShRafhsugHEOsyJTDgjFUYnO4YTmSeeaMEVlLlzrC4Dt7qhNjqkmvcGdo3inQ4N TQ/QDUf3hsI8/6S8oXD0/7ie4ZeBjlBDRj6Oc8akI+/jcsaNqcWl+Gx5IrWMWk7YCox+TuKEDpok RQGvOphaRg2s/aou9AXWflUH2pKiav1QYQOCV6QhAr++qkuRiBmnLvKcAMIP5mUjUowyL0iGelBf N4klUfShvm5OJDeA6fqNjBWZcjXJEwyvJnmC4dX4CQLwSXk+/PsIkY+J4QMZ+r06UTljG3dJKeNn CSDlRGTiifwSXE6Iy9AQvqr0nMSooQzRAhnuWp5FSPmxCS3DoBPuhouvHPWEu4DYt0mdW01PuBuT G494wl2UyMfxT7ijJ+WNvwxDT7gb9qS80WTpvpPyBnnuDYwP4YpyUuSEZjmLGFg7+IQ7X6+biPET j79R3ZvC6BgwdwNC0qOyOST2LZLkGCWwVpn2PSV9yLOeYbyrEWIvIkQ+TiCw9sajzRrR5wqXE120 DtSN43LGD6y9GZAXbsj49r5E1aKP4ggnevU716BQqavgSGApPsQyfGmPGMTku4vdBoYxjRDENL/5 NiWFBwtF9bBBhX0GR6hzWc2jCO5Q2HeEVRmotxRHLFR6H0KufGljAT6yXBlAUNcWkhGCmHCW5ghh TNCoEPl7EGOI6JiXNXzrs7ji78ZgDD0n5VV6QVM77HGPZpIjBX2WkWFjXN86ESw2zBduUIwrFDZG aipPnqu3zqSOezST2ZARGy7G1bh9iXI2hRQe43o3fIxrSA4i1KiI5xiEbQ7u6t3KHDfc+gQfEjoH R512W+3O0iC9g/wdiu0roo+dqUQ5eYDC+kwc25dMHeVnnamb8zSO/vbSnsZr34mdUuJtfn2K57BN q2v5gGPupucC4+HavdfkiiiJiwfdbagfwSfOhZytl76791RKZt8J2PpSAiuVC+8nl4GVzikHL+Wg SitSPOwcNuOSq1QMTevMfFaD4uFye1u/sz9OT5HWWoxy9A3DYwO8GXbiXF4OivzTYMRmL37U56Aw vJCAw+lCJx1c6e7y67VbKVr7QrXz1YXsZ1CUYzKs0qP5wEqleLtztTUV2NepnaeVojCr1TyrHv9G J2LRvK/8BD6H6Rh78rn30xhYojbz+2zfng58LvNJcQebJFn7KEjmUfewnUwjs/DTt3UWeu1gcuRs dO5u13eigcitcvYgRkEfrOHdJoNOMTubLwSomHw1JCEH0CGeN/QUs6infA10xJTiEUxdMGsF33w8 UTldjoexxnatcsfJ41gV5gkZPk4+rlWBBsEBUWlLgeLVsFFpA3w0h8BToJfWkL49qH8DfdL7++cn WaAQtzA/rWGaxEXXjjfoEby0oq6X9ZBT6Ulr+n1jvRlCfNJhPtsT0THb/WemjKCDvQ== W+5GUG5FOvsJGnod7gozQPzlKIw9ASvP3YqvQ81w+jF7RL2WRz+GogDHNlmjGEBRk+JDLSNFAUZK UuoMi+9eaU8uZaNNc92MUZjoNKJ4bTso6u53sLU6CoUpFSYnI7+lpsdRBgvnJJamjyeW7AoGy6vn HzbzkEen8p1reRwIPApG/9NLo0e3BfNtUXJEiKGAg1ziB3mtOnbk79yAPAdDhEW9TXmYN+/5laI2 Nji6LTjPQSRuXGjUZ6Cz/nDH6mHfUYHejxHKGXjmjZsTMnIoZ9sTqBMBDEER3KiwQWJK9HahDCHh h24PUVgg4kcZsYGxPMOM2Ig5R/xHbOAZx9ELCzzcTYgUZoX58od+UYBRYwCl+GD+MDgKMGoMID5V duQowKgxgETPP2oU4Gj65GGjAKPGAPqfwB5QxMgn6rneUKNEAfbNUEAMIOepMkIUYNTxdPbKkaIA o8YABsnI0aIAB4ujwda38CjA0yv/XoUdysfikf+9h/L5Izk8amv4Q9R8JYuJH8oXqoWLGDg8+FA+ KfI4jXMoH8f1/RsP5RuohZvIoXyhMSMTO5TPJ6v5v+FQvoB84yHjVA1cvTukNYNzQ418rl+E3FAT ONcv/FS/4XJDBZ/rN3xuqFHO9evvGn+q34h+Sn3n+oVrhYLOrxz2XL+gqLvU+LmhDqI6TA2Kr5xM LASNSRz7XD/nDd9T/dCITeJcv0mcLzb4XL9wnYMnkmvkc/28XRPF+9HOr+w/128EveUI5/r145M/ 1S/8dIbo5/qN7KE61Ll+4af6DfCFi3yuX3jEjIPkMc/1GxQxNJlz/aJHpU0o3N3nVL8gPX+EhDfC uX6je0EPc65f+Kl+EzqPb2nQ7E/mXL/I5/GNda6fU4rvqX59FqsRz/ULd3Pznpsw6rl+4ZJasK/1 cOf6hYanFCOfZDTgXD8ylEGn+vVbEkc7148F7vmf6uerhwl1uPY/12+EqLQRzvULCgVTovGWEc/1 m8Daj3CuXzgb4J7HN37cQ/CpfsOfxzdKSgG/8/jGj3vwnurntfGNeq6fr+3KsXBKUXn5Aef6BXWc LEJ3FxvvXL8oUWnjn+vnxG/5rpxQOjbEuX4jcOMjnOvnAzTuVL+xz+OLdLhmhPP4xs7sQc/jm8C5 fuH6Ly4yZaxz/UbKqDP0uX7hArU3w+Go5/qFn+rnI72OdK5fOJsjTehcv4FZmyZyrl/4qX7Dnsc3 mjar/zy+cemv36l+o/hc+ZzrNyAYHmNsAuf6hXvnOGcMjXmuX6hey8QW3gmc6+eEj/lKonQXG/tc v3CxHc/LBM71CxfbqfwyqZingFP9RpEr/c71C5YrgzTwo5zrF36qX9Rs89ECYoNO9RsUKxr1XL/w gFjiETH+uX7hAbH+/Njw5/oFB8SiU/2iaBQjBcSGnuo3HA8TfK5fuHHBLzvQKOf69a1P4VS/Af6W kc/1C0cEyaAbMVdKtc/OiL8L3hzovsDOSwpW7b5fra16VbvwXYgnq4/jvXC+mCdwUcBTy6PDgsk5 M91lLyi8Eg1+CO5WFrhIYeRt2zDpiLLC6IsvVbPd3rqo7ax2Nw50JXdzqc5vTVv4ERRPtZconpXa U/HHxNwUUgRNzT/vfU4lN7/0xPrWL4rk2li/uEsUa99N2ba/VmX7a2VNLhyeW3KhWTuSd8/z/19x 36Ed1bFt+wX9D02QkZDUqhzICoAwIoNAYIKQRDhGElbA94w33v32t+ZctbtbARtf+75zzjDqvXrv 2hVWmCtU9axZuXP/rVnZ/vDRPPiy9ck8ueereXrn86p5tv/pg1k1B1/M6purh+b57OqkeTnzbMq8 fn/2gXnz5sUH8/axPxDN/86/uGDe3Zt8tLe3d3Nub/+X3ct7h3Znde/wbZncv5B/AiB4fICdnRdm P28uP1i+Xz/cWH398uOZiz9NvHh4vlz+sjDx8Mntn3/69OvE2bN17v7kuS8bE3dC/enB+3+9WLpy sf0e397huzOHM1/D/XdcEt32Nn/r6dMJc35rU2gPd0/VIW1duLt0fx/bSVfOzC6shbGfgNQddtOX v1xa+M5kXQoyHYe/mXdX3kzt7d2fnf6DkYapF9NX/IUb5tbC3QVz68PGz+b2g/vb++fepPfcX1nP tN8tvPbbzemr5f4rM3fr7RnsSXxkbq7mt/yZPzP3YGvmmDQdFZ8jP9n3bnJ7FG1FhGQ4uJH9GZ+J g5/OTH3+cu3MjLu1cmZ6Y/bRmemXk0tnzi+VB9iA+3P7Sc15//DM7NyF+/jiJebuBX5X8+aZmXsv lnsTZy7+dk5Gvf5bafytP6XppqFBz9uLi/by4uf1OYvxvb1xeeXMHpfp1k13r8inJ1/tzMeP1+TT 6m908M3cr2cHfNafr9sHxkwO5lQnn4d6nGyf/rV1UZ5ZntL3fbjw2zQup9vlpclZXM62y5Uwp4+J Pji4ufHrb9XMxTtmfnt3ZX/+7vPnr0UfHIi8tI5ey1Ojr8ZHcO3q9NgX6+cXr3ZfLM6OvnALL59d 7764Mzf6QjDYx/nRWx7Y4VdvZPEmvpi521cvjmjjb769ODP2xdibb98ZyGzPXBSj9su0tHJuyr3e /9chVv/2Yyuf3xt//dmZQzP38Mao7beEi0K7NYNnLgre3lqgXoFSLKJVV4oopgeysA8fzCHGPEMT LJfPLJ/g4uAtD1/5uXtPfvXy9FOZ/nNXzuH7aXnBYNvMra2MJuZd95Z3svDp5ezS3PqVny593Lnw 7NaVm+FfY8pTVevtF4+G3uuRgH1nh1dqpzz/Yountce45UrtNCxbnPq5PKrl9q0bF7aeLt3ZbD9T KaN6YTv+feqml2YmDm9NLt+546a+vD3X2GttPQwHvk724oJhxt4/mMUUzci05h25fDbXGP/9C2Pf r9yZkk+vrOLfufdv3LV6x8lavV/3/HTUAi6OmzzhofHanjGp/KsK4JehArgO2ZdPX8LKMQUgsg8V 0BTA8d/SnfsJg5xsnoUo6zagB1sXKYtieJ9c5DxgX+8XnGWO3/mMhg34689vrt96O/UKFREi9gOI 86T+5ObW/q8odl6+2KTbnp1zS7OvpkWmr8506wLZF+ggtMUBoMoL9arln6mGmOOtuRGs6h3JB4lu 6JgF8Y4u6CEyGyY5KrM5OXmpk+NLHMakvfnm0pWOdoNyBXTx+De5vDUgM/QmVEGIVP668OXszi07 83XRLP18Zdec1AfihOsiilKwggygI+9Pc/3G0RrRCrDscInh8V39OoIYFJV4PMR2XDY67KHA4uqT m5tmc2JnaW/v6sbR34UFKLly93nbLcjb3eLT92EExUjDD/3e65p4N3G8icP3D38TM1gO9Wecf3rp fh6z16Sdubq2zWooQof9Z0cOQwD/hrWH403E3Vsj8wczqXvjqRSFGe47BOnBFsvT+DQY0uYY51eq sN/ME1myg8k9lWSxPy/HzG035k8XJ66ceTd2PkHDI/pbzr+IYgq35Qs7GA2cPMZd7YoQ3ozBgCtL +086BMCjEOq5U37XuPvNWZnU/bO3NuemR7/lPDzloFV0a0Ji+DPQwxMPXh09UuLi2IBe7fn33YAu HTla4l347Viv2289nzjb4uDab7rmh+9u+OnRDzDLZO2O/QSbCMBg1MDwDAgcx1DPtoFvPLjXo744 e+J4iOl7g+/0my9tCzE1ef3V69MWQgZ043A0oCN47IcXAqdIdk38Mt7A5e3HwwZejBogtjzWBA+O /BvMoBHv8T4M1+U7vTjZxNu9vzQMNnAkNt4OOfo7w/h0eFoD9F+2j/7i8i/fmbFjbPjw7XGGHY3q yH1b+0MNo8ettDsF+F95eOTO3TPfbfEHBgmpfLh3dviq7bFXLd9b2zjiIP4y5Ky1X8a71H5t/Guz hQfXp6HZ00g7NZ1stg5uFArfyKCMUIqgx+1faFrFOq1FNcYLL97SYs02Mxl/2cflXGe+ly8MP6Hu AhYbdvjiUTtMszzTLhcHNNqD4YNz0zdXXyyjN7/466tfFzpbGSbHgPjQsGJdxLSO4e2j9nUMb1+4 vnKt+6JZ2mZnD9/c6L5YGYP7o9cTJ1+aoo0YN+q3b0yP4e2xN9++NQK0b7jxVGgrcw3c3H5ggGGm /flLAHS3nzmCZUZHGwIUxLw4TUTd8N/DO7OdXTy82GzXw5WBgu4LV1+IcD18bKAyAec3cflCm/XX n+6oq+Smzg+mehMNR67d4VpOjzsQaw8GfItfKL5c3noz9WHxU7r1YOFLOVfGUAEXFohSmzqexh3l XqXFx3P/TItde8/MqL3eRHp29dLi/LOfLr9b/JTvfp1/Or/zXBH6wquLb5R/pzZefO5cyqd+yF7v xtnr/crMGBBXr+T94wGRZ28CO9inFI2vn11VXD5CpuLJ+WWgqzVhpSt3ul+dv7czwtvEGc3VnS8X GthQjn+429WQCDqe7gTtzb66uJ/PPvxp6OLud17pQCPGzT+LYYoFPtLNG7i8NN0hQRGzoTTJE6uG si/Shpg3KrAHxK329pvBSxW+0+Oky4Yx0WHm8qfui7uTlGM7Y3bedXN8l+ddkLo48X5InWm0ZxYS f3cwpmtmPl6d6/DtijHx/V2klu7asVtuL/yWhFU+PgC+FV2ytjsgUjo3pacDjcIfu/99rVesy/3i YuzPPT78srX3YO/zx887/Zne5d7c/B1rn+1s7t7a29p6uvVfB0u7G4fbWzsH/Uv9ufkni3fulLi0 tbG7udWfOfJbSWMxzSMROHJiepm2X16ZfXdrNz/3ZvNkfG595/aX1X+9X3h379H8/MyOfX35ul0t S+c2D8VDWlpc/uU1Bf2UCCsl5LJI8cHC0od6+9flc4+vri99MGvXxuSH2PbC16e/wfFaxiFo4na9 3XwFp+3Jmcnl/ZkeCWtqKRT5H01GIMoyCnseS/IRrF9LE08mbtXzZ58uvv753PJ0jV8fLry7m5/d +PD06+Wbzxfu/dzD94tv55fTu/vzy/7To6Xr00+eyle/rd6YO7i4JF+cec8wzlHhmLTTv2YcfrX2 pmOYezujQMRIaMVAfmkrP9NZobW9ptIOJsFabw+anhUx6T7ZScQ/JpsyjZmSi+OA73ylQ8hLkZBX e/QFhxIp7t/QVzTjHHvh0zBMdXdq/It6sD78Ynr8i5XpjeEXs6MvEBd/k7eGX82NP/N18ePwi2PS cFEFYYz2bHIobPdnxr/4GDZ7E8OvBlS2oiFuGNVIy7O3gIfvWxXQ5euPcflovO33H6cxs4+aDG+c z5ea3hbvWQPUG7NXLeXl0Rz9D7tx/Q6m7VFrduPRMzbrx6I/y2/3Jpplmg2IxT2dHY+TXL9xecj7 jxe+DD5enH+48WFF5OXunTNPRszJpR0qqBPnnnaqYcFNLd+6clqLp7XXKm3/rMVHK1dH8hkPHp7f uv36Zfk4/+Tw/OebLzcfGozKjvh3HG2svfEiL12IKIwNfel6HjLLc41PuqVHi3Dlng== z3Vs/1zafv8aZbLPbWdMnjOIs4tPXj8dr4U5LTLcKYChVP5VBbB0uH55uTdBFbAw9WXv4Y25/e1r Sy8Xt199RwEMdc2PWceRbcSu+v8t6ziyjb2J/z3rOLKNI6n8563jyDYerYD+o4jgiLFPVDyekLX7 Y4bn/M03cSwG8+hEDMbtz47HYDbK3RNhnIPJ/WEDD08GcS7eHoTleUR6WrBo9+LJeNPsvbFo02Rd 80eiTencmYmF80vHAlYd74tDpJFIWQYivsuz/qfLr6zYiKUZsIAmQ0kDswyU9nr/racj1Mnx24Mj 6b5zF74bwei8cMQwvhfBuPA3IxjTOz/oua7N7PxxxEjjRTw/f8xvbwP65ehpoNP3pn8sYpT3fsRR 1+3o0rnehDYxihjdeH5xXdZq+hbPV6W+GM7Jl+O/XXmy32/GQ0niinMZ5C0nF2Luby7E1HgMDUHB Ua7yh8KCunPkL/fh2P7dqZm/E5rU/SKnNMB1+eEm/qdRudEv/U6Z7zRx5ATY5XczP8iGM2PT0pv4 wxaHnRfZyI+n7ncnsn40R++z3+8hBiny8mccM+NGr1I+11fdvrt45L65i2Nd2vGXH3Zd2nbDOoXJ zaeiuG/sK1QbHqeq+una7eearxgzKGOxjAdbO82wvvh11r1d3pxBxmhWcRvyMYrGBy0j8+wsTc+F Lnsmn1C/RjusoHtohxV0a/Ij3pjVy9f7W4rG/fkrV97Rgzcf3tz4OGYrxzt6zLSOfXHU9xz74pnd HH5xzM5+GHvLONyfPX/n0xBAazbZ3l45O0T698eRvtiFIXi7Pzv+xddLYNz7c8Pq1HjfKFheTitY 7vuaGLPv30wM2340rbe8/2ohfI9mhzrpvLtwbRKFWI8GDXKnRbTyyFBliobZWH5AQmt24+Ur20V/ bk0PE7KKPdxUujScmKcDfYuZOl+M+/Jk4uZMnRLXzKyeP4IKJre73xZTUHqi5HBYGvN07kdbPK29 3mktmrEW7dy5/SvTzy7vpWvPwv35/HpzsiH0l0/OdNnWV26UZh6xl5s63PyVA6f/8nxmHIgvX4XE Px80DP7yjlXGX9p40IKiS1+eufbp8NUbYit3c2r9bVd5tJKPJYa63ULi7GqdgkjEPCXwYscWT762 W0TQOgc3TI4c3KFPekGTKVv/2mJlw5SmflDooJGe6SEWFEG7ceZ1k6dFopmHMwxYzX1+cTBQ3Pp5 5tI0tYU/f/nmz6ftz/DXV+/fVR7CP71uv8Xk5rOG5Y/VRdDhPlYSQa/72uKsZj+PFkIM8a1IRl5d Xnz367x4r+OxutuXpsAs9zi3olyKLgkc4KIgWGHs/H9f613uTSDe8/bmzuZ4rKc3MSGUJ1sHh19x Q3y7sPXx887K+r+39nq2r/838n/8m2vfutJHMMn0I6gr73uT+98+Wm/yVH9FdPnbufm9g6XPGwef d3fW9/7dvwTSi3srz+4s9S/1R/de7k9Kb8xbuVu+mkKAiffdR7SJD53SyHe/lNb+a/vLjnw9u35w sPf5/eHB1n5rdH5vb/3EXRufPn/Z3Nva0Xtcf+7OzsHoW/xz8O+vW/rt5KeDg6+X5uZ+//33we9+ sLv3cc4Z8UBkLFP9uSfyup2PR5/9tv7lsHsY9P1L3793Z3273dq6126e+fvDsn84rD/v+pPV22+f fvq8f/PLFjjlRwZw4hHS5b+e6c/Lfy9+7x3Knwc9M8jOxlT7ZpBCNpEfTC7By4dY5bssH2wF7ZQP L9alFTNwZE1p9t9y+bN8+Jfw6e/90L/Xf/Xa9Dfxxsc9HwfWW9+3Ng1CDam/DZLxxZKUQ7R9HwbV +SKEPDBRbl7rCSmZUkkqNfW9H9RgglyXQQwiC0KQ75VQgin9jZ53A+czmqnyzlD63g68qZYE453r exlecNpIMlaesXYQbI59a9KgmAJBK9JR6Zp8SkHuMH1XBtnqv9IDko18UevAVQgnpJK34BOf4Yc0 cCknIcoIZL77SV5asyMhOWv72WDYnvfnQfTG97Md5FgL75HhlX52g+iCDNOhV14eknE79txFWcYa QYloNQxKkg9ymU3ltQm8PwYb+4vygJc5dXwgRIfu+oGNMiQheJnt/moPM5MMptXIZNYc+lW+qwbD CBgb/mz0oqxGlFfjKsh7ZJXks8y28E7/xIov9j4ow8QRx8j6xupxkzBD9hlsEWS1bCGpRHkX1ryk onxiXAJbyOByUrZwKZIvvLeWhGyMLLEb5CTtKeOIsiRf2Jo9l91UQ76ImQwoBOcDCCbF2lopkdMr zFQzGcN64UALxjBJZrqtcd8JAyQZefeXX2BpqnCDLx7q2419LZ8bd2RyhwPfyboW+QvmcEavk3wv vFGs9Ah3J2FWeVB4w2MUcu2y53UpDrwi8+VkwoVXnA2h8YYnMzhIC5dtIEIjlylj1uXSBaPfpyF3 2ApmcAORHSy4fIghgmBzyY07vINAGSvdNxncEWIkKyl3eOWOVDg23w/CftID+SzrHxJl/ejCd9zh jzCHSbhFGvLSyDbWPcmLhCTjiIG8YUJV3vDRR/CGExURle+SF3UCVnDWkVCicJ8Qgi+RhBpkcZU3 YmyqBzMlrFACdYQQsFJgFlU0SdlSpldejlmzJg5Clje4fhFhlM7JJ97hsOrFSr/0r9Kd8oYVOe1D YtPwLnxuvJHAG1bnTxY2Z/BGCMHzWuajgDmGXBqbTIsuKUXvCbKk4A4XnCPBUU2IpjMlt4cgKhli wLWRSbUyCUIQ0VFC8PpMLI09hHWS13ax8hD3GPje6l1jDoOJM2IoguiBCr2EWXJOecMpbwRLdeLA G6bGrOq5yriOr/uQNcYUB/qWfKGEiAqHPQFvZelSksk0wlErPcpxFfaQ2Y4xt75QvFRIqUGj8Iew Aa8CmV0VQPQ0L6I25Vr4I4kdWiSnQK+DD4wRFpTXSFdCreQDiqQHH9QqM+d1OT1MjpfvR3/xBaSk ihUovNO60df43BghUkl4q9LbmZDgqbmPTQJm6lT4ZxoMAd7rb+xuf9093Nns739a/7rV3xYAMQYS bH/+a880xNnmWl4ZOcOWM2KlvxU8L39p6prZFU0j3DO8JeOpjZ6oh6T36i1HGKFrxpJH7fDW9s6N 3sL7nmldWfgknZt8tgO0s9n/uLe++RmJUjul30NOwc/jaFke+tgjjBHjJNLb/Z2NVMLyhoVtctfC AtDR154+7LqBm0EQ80dra2Rht8neAdyZoWa86oMUCq2n3K9wxojYgFBsLUQXYtOwkjmCBNYW9SJW DryKLq+RVaMRICFra6MoxhXlZ8huNqqBlSQGyqlYgzGFECMMhbClS/0TPV7EBJ42a15nbbaakxMm ZBmD92JhZ4XzKxZDZg7/szQI2q0A/dR/sd2b5Zde1M1scSHLA+WPHiyCwQbC5TL5G3/tXQt/+V3d dMqb2jKHxp1YSVkLLKoXtIPmsXTOyuICcpALRQXFHGm8c4VetWK/irxcFIGgEdpAUZLBVlmdIGtR YGXFpkXwiHRPlHKlZpNVgXoMwgE+K0kQtwVuSoLf2B1q3RP92/izJbTllDWcBTQCtGhz4kYfpOFa 8lj3BORxCFjKYnMj834xCCcfLA6oMgKk6aMB8/s/eePCD75xOP/DBzdGYjumqqIImhc7ETP+YmmH 6jqfVNdpqK7TUF1HVdf2qLom6A1j6jqourZj6jp26jofV9f1uLq2nbo+1t+VH1B3bqpPGZCu80OU hsgCMMG2wFk6xgnJ6kvarI596L5JYmkT0Sh6MVKJh212O31oZBKz2FEvywE4s90TiyrwTRZKDF6A bwBsJVMhgmmKwHBh6gCAA31nITYy+wW4UtwDWthEybV8d4KkyTAEivWrCJqIuTxBSmkUvcGJZ4NL OB68tu1a3L7FHv1IIkBwqVicJPKYIk2OIoxSRaPCBpkCu45uiaRbqPEKSBwTgY3gpMq5gRfJwYOA wdcELKCkoBSrd+ADp6d0hLH5ojaW2RcPEjpAMLARbp8V7ZYxJOgW6bn7rqVzzdRhUQbihQOSzMKj Kt2HLOPCp5qS+L4y76JniIKFQ4DMkjCo8ESgWsQoRdGlRA1aqtgN+ULWK2HUAE4JENGOccSfBZe+ rh98cjKqt+7t6QGmSUM3qNaxUMbCwvzGxuH2492DdTTboivjL+nP3d89eLy1sbu3KROCrzn+3Ebf DT50Qz994HOPt9a/3FuXt/4Xgi+Ti/N3bre5ffphd29bvxoGUuY3d99vvZ2/UzGAJwf//rL1dtQn vSs3XIWYyxOgqafD2E6LuNj+i81Oqk9daPcnC33CNssqpP7kVP/Fc2GSpR7UbGnfi4wWYVd4XLBL IgQwb0mUjEMIQoTUQhqFHVNGCMIgFmEYt/Byg4hFKA6cLa6BqNsAHEsPQDSXWC25ttKCsK6DkRIp zjCCBp6BiFYlLpL3VxFLyHcSGYiIcgDpgNPg4YpwGg9UJAIrYq2OZSnaYQg4fWDgcSEIhHGqLj3d D+MHmLYiklOEUuEiVf6t6GaB5wB8Zbz6QYuYD9EerTWYciFkhAxAgOyfmLDFH8GcVrWwuEUe6DL4 HKEkraxvMNKYGDSofrBlokl0yVUYNJEnmZVZL98LYoOXlSw0mCwLoKrMLyMQXpYM8R76RqLoI7Sb zIVMo6ylhXzC1vxVwcz+f10wdejHRv6H4/5Pi2Vb51MF8c/W+Q+E8+IhvdY4kk3T/0ibEWETECqA 975NUnaijRFZQmhwhaTqLUlOjA8oRaOZRaCCiXoTWFpmVZwoL8ayL+AkakDCQjsGCLSsqEavQqKh ExKhA0gRoZNSBKUgdGYQchKfvCCIifipYbwNRrsUjTjwnmDYeeerCg2FGIqCTjulCmZbKBpdAyVU 6pImaB5BkipeD4UTyMjAXXEA0YVBSI08FoQ9SKLTw2EJ6AcpaDQDd3FWSKKWwF1ifZOSvA6NgMtq WzkimAiSY+vilDJOiedCSEoaxrd88EVJorItwxoFEXI2JegikyTG1utdAob1riBLKUhZuseJA+8Y eADFKgAiKciwCvrrdXiEqCQUfb04kA6TaRvCkA6VwmdSIoYFsGLcqyBIo604BAilN7CFRV+eXIID iXYEJfWrAJJcvVICQhxCScHbdo/wV23QTUcF5kE8k0MqwkG4KlGW9Rsmwsrig1LFh4VG5eKgBad9 Q1hF7mUcxrT5tN6yTZkw2yYdKk2Y2Cedc1lusUfQ7ikZpxRBkdT3zhPMF4xWFL1QGPwkpRjhwMrY j8dUCRtVXBfRpKvarMx7BUYVY4yJ85HXwjZZp5JMVSE+qehUwrMTgnhkVgkBwSbMrYMYk0SQB1IG dNW7EIXDoohHiJayQE/X2M3kBA7MMk8lcP5Srk5vYvgdswP2IaXUxNkRmNPuiUXHFaphOxgheil/ ncNcZNjFyKeS15Yh5HpLZkghi1RCgOVVgJF6C8ExXmUjbXzUtwjjfGMLISgFXCHXTA== OGAF8PY1NiALDwraYYtMMFQGVyHlAoT0raLxileCaDWGiozwo1KsB86X1orhQ7DcmKekIUwEuiEn FSkPkQ55syytEeGsfJatWPTbkWKpWhE6QUJGKNHpu22bSYEmNVuleOAMRKeSLhuMAJ4SVWtLo1Rr GVP0YCxSLPSUUJwKF0ORIusV8Q4XlSKGT55qoi2Uou6iUBg2EorMm5F3VEhaTnpPsZhz0VXi5DFs xCCcE80o4/2Gdp0rpGSrIdCa2Lcg/LOG71OwpAgbedwRZdI4L7lrgbknofgiksl3iNHDtS9YV4Qd RVXSRYpUw9JTseAVsyKwFj6wcDAEJ6h6geMV9C0Z4aqNnlCC1Rv4QFCcVhnPr/Q3EgaKBmLQB6x4 lKTUdgcyYlgai1wQPBV4e+gDblgBxSGOLY8kUdqgCPiMhc3WTPlNyHiQLQSkFCUYJYjy4DNQsfK0 zFhJlFN4y0gxCiULWyjFlkAesJVWKIqGiDHp2omcRpFpeTWMKqV0rQcKwHdBnBxPCPYWqYBdL1xs xAJgdAoyD5GUpN0WMOCp4+QWgYmwqNGQZYQQo2f+oesIUokisDCLIcejFOtpWOXVTsYGhV0M1bJQ MkI4HWrmgMiwJajXSwqVeIG4UPMlBCo9oXUzl0IpXp4XpV5LbZNnoGVL8wNIcR4+hixXoUrDGhio bEROTWyUEgjsYwrtqWSrIpBEa42QA+IDQhFL4AGRabgAsMAQ33AHlxBgCooZix+kfbmOVcXDafBW KAkefSLA4zVN0CoXn65H1jnD0iOHVlqmQK4DRoplFa0kNjtZNZQFCyyzg34hgygILELF8a0xy4JW 08WmAO8j4BRTh0V7GhnckIlBalYoTCqKuiB6SE2ga0sBsFVx6fkeIwiTnhfuAD8VjA0OD/graQR7 EQ6iQ7K0IPltKLbVA49mNaprEHSmuoQSgmmin43lfIiIN4oDtMIMIf++AtktIbNdcRKpEAMDtt2r U1B9gRcLRsTwgsbKsBDIj0lnS+RaFm8BJRLy2cKHQJMi79om01a4J9juLXA6hCISZJXiRNGQRyzN CxqGagQfxSFFdCF5zarahaRE8qPzrt3DFDSwX0mNwkgo72mKRQSGfJ5MW1PhlNJXENRUS8jgWDFg WaWlJjEBuHa5SZR0jjiPeQJKnTjkfMbA9SVFrB5lTJioSSahJdzgyt5EBJ4zHWZjTZP5hEAPgDCy onJdQ+YYPUzGN0p8LQR6go9CH/JuMoEUBG6NNxSsEeoqKLjSSBCV2AdMcrUpMdHKBRSkz3EJ+AAY gzVaxSMGGlgoYrcc7sgsVKhYDzgH0INoLNfhaJDtR3lEZYoRl8DkGcIi07fKR8BHGVEsWTAoziqi kIumieWaWV1cA3dsQLWKkpcnMuJGvIP5J1zDuq/hjoAJEgpzh9INYaIqoiYUa7igEdnW2ic0UgL8 Lc9mZVq0W0hi0ENBv4WHce3hSiyi36YWUizmOMqd0TGYUsAyawgUi9PHso6I4psIhm5FHEgKr+IO rR0BHpEpjrR1CShJtI/VNlIUJJXpTETcUaDSgJFi1iYIgIRgnSxwRM5Y9AASUILt0ETU8phslIM5 UqsZSOHKivkjKBQoBO+f0ydIoPIJF5uNiajuyYgUoBso5eGlgLCKS/FDWJMgTgTLTFBuYFgEkxlQ QueR4BKcTwMLaIuJqEzMCHvSg8xelxDsity+TG4QVK0mWEwZs0pVRL2fAO7AaaIvHLJxME0G2XMx p/IOyKcHk0QF36uQTxmex4qL4lbtX4mFhWPVfAR9ng4rjUHVFggqYAsK0ujAOQjEA6DoNTXlIkGM Q2IvgteJlEK7g0xPTUg9jgXPRNXARrCxYKNqO91YrXIaMTQI7Bl4QVVy1MQjKN41CqAV+tK0IMIe nIzcaXFrIbtxaA1QHiTaOI+0MgqURMQwBbmz07lgRAlMbjVVgBFBaoTLVtXaOwqfbTFD2rrMKh+r YI/aFRPtPdEgC0KQWI2Gd0SmMjLUuo0NTYfC5ffNPCQmEjBLkZ58yohqJPJQyY1CbZhDp9Bg72BB MEI4aXgGKApvRowBiUYWRkR4kfDZU1EfB5SKRAIyfoZMQtRGQJ0gZ7mFYnANH17G61GowjsK51X0 SGGSEfdgbTP8NUZMmEMWlZBL59CAUqhamV9ILWEB19Nqs1WzNdCcGRMMBscSF9UM9KQCqmiKUU2J yWJXAWaoTSBe1IGlcFYhkOxo561ZhQNCSaa5PiUiUQ73zbV+elT1YTWzIoY6TJRXr9iyUDll6NzQ CFFAA0UHhgNj4WilP8g8rcEPK+hDpsik5uGZSh4gC0PXIGAGYUKdnXi1LR5BLnFM7wsAVAawCYsF Sk4sjLLJsFUUWqWi1Xq+jVkTfA4gJTQvEQASyq4qI2UUEFILyzhqx2yBaUFHFI4MIm8RXO1Du8Wi YQw2hsaPyhkoaVC0wog7tbWwbm76ICcWEAiELyqVdMiEEm3zlaw1Zbx/gONw+6U3OTYAwyQRRmVL 83sYhcac4F7q7ECzgNrINbXUXhfHdnYCAL7jPBJiVT/TArjSimarJRDG50YhEEVysCgE0HJG0UDS tRiUSaFuCxU3LBg8Iyhw8Thp4qLaAFZ+0oI5JLhgA8TVoY2jDSqwcN/4PZLC8IQqDBrcSsM7vGJh gohETOMyFxnumLitMH7R0jGU9xGTytNWfHi8v4jbwNbwzCodRSTOASaoP5CKhT1DRKRUxfFE5RFZ 2aEbi+oVvE+DGUzTGfaFGc625sBTST0wUmiE8FTzdSGBlfewmha6TdRrTBqtWAW7GUQHsGiIH6cW 44koZk1cD64i4EBGsBcP0JZHZJDYYkUZSoT9capxiJ4iIG8XCUke02hZYoVL+Jmx+TUMSNAFEAqD h8r7coeIN8JLuC5oUqREfGxUGhmNIguFATUGPYR9QtXMErshIhey8o0qqSKKIKAGxTQKIxIBPjFl Jged0QAlYpVAAxbgAoZ2Cx3WAJCjFCTOpAtC4YIDOgjOxnUyZICMBLgwWDDK49CkgmDQKh3cNcQF ufAoStNIOMExSkayRjrkWgSmsvsyYqUg8BKQVjctTGiN4RwwN61xSoT1ZV75lxTnMZNe0Yg8g+Rv 9FpIt8pWETMDBT2SnhWveNohis6QqahwiqINCE1YZhQJpOQdjLqGDKxlNc7ICHYcZtdxib9wsKBL N/AAHVq4ZVm9DfFtUDcnopTAU7BvqIilU5a4MqXpC6/+CCnkO7rJNDHA/rGgzhJG1nLewdu49lyY 3Jg+qK8IfMLaXOTCabexmIYlC7ExjVMRAwNyRrPXsHQ2KmlkkQCfFBYulayGJZKrDQMMhFeG2o+r mxsEge6DT7PG+C7CK4Q4to2OfgNdYNM4wnBOEPtq4XD4SnDFoQjhhCLIDSuEpWYOhmVGMLRV0x9e q7OgmWF0NAOTSKA7At+xUgBLlwBgGBAS6Yr6lkhB0MpEestcXFyXGDQrG72Wtgmwo+dLbUgzVKx6 woFGyCR9QqQP9eWgICkM9hCVjWvqMGVBIjSkURJdT4aF8Q7PwmWE4xF2yEhKFrqrskjsF7PbFLlI i+kUPgJlIVXFAunYuINimpGe8p6uJhwwoPBIvZ2bLQEggskDU/J+VjJRmLjvARSU8pdmr/FO2CzN PxBsydjUy2JSo7CVYDWTg1pKLQdPjsyAiEjQQm0WkAklaRkbK3xZEYvkIarh8VTRxc6qzBDyRgCU lJIUNWTY8BWk/8TFIrKoGg5HQrA4rg/jjqRUzoPRGnqkHqHeO55ZRebRQ3ChlYFya9PbwDbwhRAS R8EGjU1CRThiXKgq7iB8depTgJkN1U516tOlrEVBK6BQacLgWEZXGBI3yp3CpqAgQOyq7rygn4TC YK00sEhv1s7BQbotIOyGQDGUABFeZFScMf5MRuUT6mUIW2QDNxRZCZbjw2WJTNI6KldsvwiMXxv1 UsmdLIXtHHhW5yMVgugk7/BAHkiNIUKFFa5EiszRFAVrluqtikrIOhSWHJJC2G21sGoFO1ayV20V US6Ea4bWKxgXLFqr1rgjqwA3i8XcQByAZgldAcUP/R5UniJZSa0hnczcpYHkLKvLggYgNctbqMCK yhuzvGQKBExiy8KiPAP+lY0sjUVGM3h1uTRnwuQZsthUWijlN1AtvOb2j9We1p/DeYcL1RJ1QB4E Gt62MThVSy5ZLVn3SJxhYqBBtBVfMwvrc8i2y6Xy2tIYYAjal9r5X7prhr2pKWgu26O+H33JQJmk IJeADiOcDEJClpzPZK10pMjTvSssIzZO45ogcdcD7kEQExWGNeic+2ZGWrxQywQKnSqrAsekeXK6 iSixDM6qpQevZsd6OGNb2CvQPnFILR4hnJDbwEUr0GfiBp0VfbvVgIP4lZlLblPW0FfkwDvZCyg0 0zFonEoIpSiBYAMEGHMdeMTz8M5y0fpRsg1jPEanT6CxFl2nmlqtEGOeIDG9LoPT8IlRXkwtytGC cKxLwJ3sCbkeRTaZURub6Fpy3ej5tLSUTi9tPGJBxeqMF1bnQLN7lhpFzUhBadekTI0ATPIkmVJa QwINmD838HDxmIB7Emps70eILjEdw+057f3wlZE16UjyAFxshLK7gpKolgw1Jq69D0tpeVfRRC3q O1jTAFtSiGRYJ2I1BB+cFqajmgRIHPF1h7pKEjQjhIz6N70lKSnnoLdkZnsAVUrhWHKbFTBL6Mpe qCQLlqARkC8EPqi6p0PmlsuKgHXuZjsiN1taXYmSEjLOaAcKS1vKqNxmwN/XRjpSirPyve0TP1wb 1aqLPvYCkHnSFBPyids91Kqz3CMr5FnpBVSbM9OBfCAqw+HQMynlkZEVCuvJQcGIUSrOXKJcQ+Gs 4Q7RM5oIDAyyhKR1AlgKwQdwdQT+6lKBFb+hH5qgwOoXVnwK02sbiACsnej7Pzgt2Egjpod1bAQp 20oCukBMFK4RCIr0Ge9d5R26NwJ5PCgjELBhj0FG7qdwtN+pbV5b1GfAtiCltoVHwUSrVlk72Zl/ cqCmalaTUU9Be9tkSgUiTndsssgL8s2MpVX1HYfBUaZDVL4ZiWCeMzbB1dyf1agxCBw+THyi2Yoa nUQOEpWf8mqG5VIrRMDgreoIZstR34wZQxQczhMCaW3is96jBdQgFM5hqr5dM4jjNaCmEy+ASO8p NnGvi4Z14JTB1KHuz1iuHuslFjlbdGAxRu5xMy2VhSVHsOXEfH53q9P/bK24WTTCrwuVa1U1bYHw TOHWENHlhTmTtkfZ6O48pl2q7iGWN8unGHTLCdqIWQkRLssab/HA1rFtilshqbjCuwi7MV/MusC5 xl8QIpQygjxIAut8demqZK3qN5BiI7GAriqbxchKtxOj3PgHtR1qaSqT0tRQ26QgyU2ALvMY4N0W 1sbI0idu+gNHVRotLCfiINBYRNrCN4EpEVbcsOAjeE3N18SQ0iJeweBDBQaM3EnDag== f1zDIwxw+VFAVHQpN7D1R0uVkBEy7FSyRYFfqcSsuKWhxVSYOkY/UtWbWmFdcBqS0DI/Qa1CSNXU ITpExKYavYEROw6v1Kzgy6NOyhs1UbUVf2LvN4wpqqYMc0Yuafi3thItFmUbVrEwKYwtnShYRE0P ak74BFmv+hbexT4dAA54TpkhVdfySaUq8BQKdp5pjQHjayvYNCMzykpQ3ZDpNCZW2v6GVWzEDSUE 3Qudqu6/Rr0HHCcaEZ8UbiPjaxms8m2XNaJLGv/3GcWLgElMZPmsLiRCFi7BZcQj2LeJ2A/qu33V RAEDDgShgU6kFoQzKI+gXKvkiATjwStSh4OKygKwDVLSKHbHcFfBSCxVFwrdZfCutXRQGcQ8zt3/ pOBUlApaGGGGErexM4MAM6CYkghMTDpza6GoC7oCsy9DYUyPm/JD0Ix9wEajGJWNqSVD7SLSQmH+ FYXkgUVuARvHZdqj1eifbmCDBYjD4jSPXjBGS+xEAksiEcfViJNHYF8Dt5wtUiySgKAUIh0fldcj zA/jlD7orvVodfckKRn7eRDeRWyRFKBKvpyRS5wGIb4lm4lEdb7B4OjUTVsBZ6JGCoPSGKn3GraT dmNm3TTYO3MmWLlBAvxAIQR9sVVvF6vDNoHjbeYNmRVZrmpINkK90e92rQ4mqutHgjg4zMO37cjY sS9dQupeq0kcN/biEYYAHHeHcHipMFyN3dZAYthKj6iqc1rZifICT3+vO8wBSW5shgIQ8JqtQOng GuQ9YnNtrAp7HQAhxlIY819DPzlhVZ1sHFNRC/MfdO4cImCi4nA/wLoomaJpr9jgzPgd3WX7GgiG DzDoFDPzyjJ3AS4lqiIySywwv4LNuFcE2XF5IKJkAYPC3sg1bQJRHaPGYIW9QFITlWYw0NKGVRBZ s85d1Zg8UtMwyzh/AfksFBBlWmNQbGFEWVxG7tG22O2EeAUcT96RMDnIiAcmW71pGf5WAI8jI3JJ Gr9IpQ3GB0ZYgzAcTxvIuhOVKU2eUJBqYHiAaSPldI1DMlZPgqkauxRBz8rGnV/PUnycahBs1kJO S53cCqgZh6i8g4XUCGpV7Rpi8Lrj32F3GIeTIUmpZaPI+5oARYSwvbjibAeGFcnHvlUTYMdzZYgJ Z30wHIjAL30rULDvEr609U14vU3M25bKIhmPPIoWRfCMAh0kfCMUPSAM7K1iArjW0XgdQkasBhuP cJqGroiPmmemX+tb7T12laIaQhYxMsVcbFZW0o1KOB7CNlZibhkVPtZ2kspIABkxqSHNLjFb0LQc T2FI7Am3nZPiWV0ET1nNL5RC0NoKwV4Qz5A95zIl+iA41SN6hrVZ8OdSK4HACTwsRqeVN7rH31FJ aLzZaZyMKoA1XmjUMUHlcGqIbkmnn0CKbVUyDQdge1zV+E4rTbZA2I7xkhYnddxURw5iwZOlY8gb cGyGOAgo6wpaIg6L6ZCMLpwlqtY1NMo0Wh4WDlqyGvMbTFjKM8IyrBzgwTrOtqR11RisDBBRWc/a A6qoFVC43wvPRFW/TjdGaACf64NdIwg9YY8bwyLYtMYqq9ol+3B4BauqoCvIPM42tDA0DtirLp4H KAx0kpItEERLMmt/YtFqPR0UcreFwAXukXMajC5OixdX2UYL/zApi/5j+yvzM7WNmqFNhD40SI0t dqh0RlADBgy8YmorzGOFtmO6mckRGnunlTzIcwRVeUnP6chVjwkgqzNGiSyFDc22BaA95DGARKBI URXKOwwys1SURZ9BaxAgo9MYXWz6mmXlzCTSs3YAV1oBKH98ew+KsgtYnelNl7UMBTtQNJrtUAqA WlZMC9kHKrnNtU/0fHCaExJ6pcUe1fQGrdvMoZPDrCXL2TNS6ZKamq5gGrwhSIyhoDS0rJDv0rZD cJUTYAGqVRFatQSyWlSKO8jtuqpRdQy53fvMzU8W0AUb2pE5R/wuYWNQUQ8bCTFAa/HtijolhWfq JNzB3U/cGFW4iAZJDybNgkJECCpCBFjo7Jp4V6tLryclySImxdoVO1K+UTIBzHWzi8cdES44uYml VBR3zbw2jIZDC1h8207UWkFvWViBvngNGDa3jKHZiB2MhNswsgA12IPaagthp2W82LkDt9SrJlyB ViloXSi0KnxL9C4rRaOuUXuLCpFI+2KjKkb4v841SsEmPVaaKINZPWxI2jFw2jHmgiIPp9gQ+gvy CoAJRMbN5xbnoIGCVcE15iI6TdrhuiiGY5GAblfHoVhEtplbZyPSfQDVGnqBlKJoDZUPjqoXfhty JyhcQHYJ4gM3CqUNVTGFKNwEF4A1A7CLNRDeJ2PaiViQpdAOmRLbX3CkRkhaNUjQEbEHGpQAsIPY l0hNgD9ImcUzIlDiTDSQi0dQlh7anjlSGHaAA9yED3xceXAOs/U+avLcN1iyBo+B7gtcCc3Je+wc g9NYNDDkq8b7fEuByDNV/XEcuqSQPBgttIRoq5OKWgtAFlA8uSS0bQidiaSLwypHuMFA5cHpnkTI NtYGZ20g0SPX+HqRfhM0uENYItD1o3+Lg7KQMaZnRZMnFNZN07Pihhksa2LFDU80EmHnqUyWwQ5W lhPvM33GduHCZ6gfthJQchpB4QSq44r4DRA5ysNIoRpzRXOHfAqOPgBzYaQWriDiLFSu6j4C8Gce ZYE0IMLMKGgDFqSFDq0kCQwRKeUIiwAHe27MJsuw6Nc3fbNGlxRVBAChCMqFrPvU/LAGL7TNY143 WQeesyCdSBrJ5Q2YUN8OcGM/WTrkUSYZ6GxhtygYCQpyrRcRO5MuB+6RBqvznC+MmEApGo2mhqRC vQIKvWocaoBNYCsn/O1/MOILDW6otHLJiPfCHqHqAb5p9C1nz6x7pyxKy8NFq9ujWCVDVxBVUNhI ijoIeG1wZ+EDsy7C00OnolrkRkpwENxmOFl4B4vMDWwyAQXTnaHZXz7B0kYoD68bOjlJ4BjrdLdm gjZCCMJrsYEn8AtZi7xJ0GIlZGm1agCRn0hWoJ4hhTFORCnikMKwADVOaa4Bxgq4vMpdvWCnaBU0 4g7Uk9CnZoyHpe9OC2M22F5T3zz9D3ud2oyzbmJNF0XL5dt6H1umfzTkH7S8hDVMGvFHlb4WErnI fCK3NIOQomZHmRRDiTWyl5qEjEWdUFEvSZOQ3AmDXJ7VzCXTdij6EUvXpTpD57haxhjpQKcWIWT2 VjfPEchrfpfRUZR/+6z5e2aKmJwPscuPY5Lg3BYGNXDYpdNzC1uhG0oF4NZhh1DW2CorMXB6nfFM BRqN+4Hi2/5Yandu9ms7YWEh2f0aW+9YN4liLx6Yhe3asJksRo+6y1sLB+DOa+YBiXgsNuvBou4q 93gaOYPYErosTOq2iC1yicS+aw5Iexda8WBLyjD/EpmRSS23hIQ6sh2sT2f+M+hGstTW6jgr/IMR Q6ZIgQGQ8zc8FREkrbVBzMYFPXeG6RRgR6eFDFnDYKkokmZCNmsJBc3XWkvaspZGa4xIYGFX7qof UDmK0EXWMIhmelkjk9reGuaQwYxZg5Ka1KI885aiSa2o5VtgJB81hUWPpDsH6JvmwsTR4vl3Ner5 ejwpEPe0/LixWo5KDbOh+fFW0uq905UocO7H2bqlwkFCcT/P9tQtFowZ6kyQGyB0iPwxo1+U/+iv rTGdF1DanaLaNhI4W1FLHL/xHoIfCF6I2k4rHaT88BqIBARglo2Wu8Y+RqxG0ESOHRJqbu+u3nAm mNvgQ17r+oSd6Zvj3TDxCYmFrO8ihtX6atvaYXoLhSmmau3AMf76BzUktiMzJMfNGXp6lIdBoSag /11R/JRYIuQjzwXgXu7CoXIzH7IdRleYpQKrfAZxpJR0oLVtPeFc8JyJYy/+RxPa2PWvR/byXJtt JUH/dOV7zBVaPZnVWIpIbYf0Ot1doRk9WlKSXEvfISzPfVA4Y8xoVS6uPQ/pYcIRGT0ezRo141hT K1fkkZ4gRF7n2r1GNxp53TYAQneeIbc3aCrWR91tlfQhAPXEVyfNbAJQZbZik3KWx0ZPFEG2NLBp p9chBoWYI+s2Cqvb9Gg9Hr2lW96qD+0Rp8Ey6hjeAfSO+GC2mi/vtlWxNklP4CIH5S7J2R1aAKGM +kxA1AJFSqElT7NuyOVpTLSOeTgcbn8BgSdOcC9dy1BjL2xwwzpO7b9tsbzQSZNWTlr1EY4QwLOr 7Z623Y3HHGPhq+4Q8bXVzVXVFrgnqZstS52q7lyhJ6gJXz0K2mgYVe/KNM9VM85KotJAPRiWCNlk LWpCahIJSQtnXreAYN/dIksDAvZGICiJwmrmqJGz5L6ZrIQjXP9PZuMpLYp1GUzWbDxBLQJs1Xaj 18NG2vQzQQ+YgfNJrNHyBbjzQXeyxqQiRNCAzboaPGVdATQocGGpuh70ekAAkP2m1Qn4jlXLSYXG ceOtHsqNlWiJS3pqKmdaImW706YqbCvPX8naE54fwjLmHNsjrAjHPZlOLwUc7gqiK6Xjg24zcYSU khAa7kdO+8T0/UNLwzVwLLsS/i9tVbh9i4cFtFqVVjZR2rmZjeR1U3+2bbYYCYIPEa1OzXjL3z2y NB85/S6K+dTDQN3wb8tge+4DQPwgM7CCo5lq0c6CSwyA7yxj1dhkPYsd61B1CF1Zp8cCxiry82K7 J0oxI2iIM72kl6c/OJszKzq8nirJAs6FDRZSIbiIBwx0+Y+8c2EbCVTUcGFQGVHUUx5DMY1pR7gl cafwGCpvkPjhLhjsrT7tdazbZe1C0eOYFn5wjCjsYTRDnxQ2H53B+Q/w18Vnf7OVPz8v8K9wzH/y TLLTDiLLP3Yg4Oj0YxyGip2X6kHyCPNtpZh23pZlCgTJ5ty8MNYQhrZ9GqcRJYYcuZOvtp11iwjU IOCCcn0UxSK4YrVukUUiCGY0FWAjT+9GJjipTdZNHa7BiXaIMTQ2a6yDZt0WuQUBNVHcI4XzZawq OZyU5DKripgxRFog81huIsnM8laLM19Q8FK0qIXnCvFQM9b1B/q/xbaz+eE04shASEAZui+CRY/P HtXSLDI8PDofETixNrMWG5oCD7oTacLpIn9wZOBIfQmW0JNYHWwuStNNUrasSDLNFu7cxglfPncV XbLYOJccmfJZlFzjRDO4MDjUw3GrKLOEPKWkaMHAn5wPOIlT+sQjrvlvSxdCd0dG88dj+U+f+Xf6 un3vCMAfEb3gtH4WPoYlbCHF+yGFlTHcuIGaBB/0PHRso6tauIy6k1hZoceNtos9nntsNZoH4OWt Oj0op0NwNCrXB6O5oRbArl5/4sSiOiIhnYUfCdGMPU/rRRJNDyBGLrTw5w4YgudRbmJNkL5HCJBn oYSkJ9zWosf6Fbj7SBPgQEskslidVLWIAikoZB64N5cYHRjRawDLIRWF0Rl1MFn+cHzi2iG5AmRp Oh3q+cV0Mj5teMa3TAUOpf2OnNUjMKGyHATNUNy8L0CNswh2ibTPOsHAuqEfyqrtjg== lqVFZhg7mnE4OX7FAPEOZMtQg4d4LCKh6HvWWpb/f3KGAR0dzp8M5j8uaact3KmCVn9M0J716olP Kwv6w1k3dzb5s1mzs7j2+kNai3u7X/d7h/tbe5sykv4cvtjZBfXe+t6v+/1fd3Z/3+nv7B70/8/4 TyTh+M4l+e9xD95Pwf5zfDBEWBnXcXj0+OSrx/Km/YO93ddT7PiLl0cPJu9+GgnngZ74caRZgX5Q lyiHnxXRaxcrvSc9/J2t/IYfoUH4xWjsh/o8fzxo2IBedS1YPqZNWBRzn2hCVPNYGxZxr7E2rCtj rbSrP2injUWbGY5Fn2ujaRenjGf06f/2P3/QdZR11VWcmHi4/nHr6d765y9be72P++vftvrrOzuQ oa2v8o2w09b+we7eVn//0+7voMgj3e0TEzcf3Or9P39xpbo=