pax_global_header00006660000000000000000000000064141325670040014514gustar00rootroot0000000000000052 comment=b975030ecff95f934170e653f6c9b6e35009b083 mupen64plus-qt-1.15/000077500000000000000000000000001413256700400142665ustar00rootroot00000000000000mupen64plus-qt-1.15/.gitignore000066400000000000000000000000101413256700400162450ustar00rootroot00000000000000*.user* mupen64plus-qt-1.15/.travis.yml000066400000000000000000000053531413256700400164050ustar00rootroot00000000000000language: cpp sudo: required dist: bionic env: matrix: - BUILD=windows - BUILD=native os: - linux - osx matrix: exclude: - os: osx env: BUILD=windows install: - "./build-scripts/travis-ci.sh setup_qt" - "./build-scripts/travis-ci.sh get_quazip" script: - "./build-scripts/travis-ci.sh build" before_deploy: - "./build-scripts/travis-ci.sh package" deploy: - provider: s3 access_key_id: secure: FLIdybRxjnMyhP7wfvWmQywYms61ZnX5yfABZs6cBE33MdDrJVnL9PqauBuyQ+1VZu81uz1VGvnVyQlnTYIncwlyxLGFPJ4Mi5yPsrVKh/OQ6kgiHHZHwLJB5S8RKF8Rtwp9p5DhxCBzuqCuuMKkaQmkefGGR0TRdEzcoxQx6uLhx+VCaCLoXEFJx7izV4lbDdBb8j3g1z+Se7kZSroMJRWB/ph//PpUQXiGHM3vsNDGXDtkn0s5gd40Ua89VL/kqfkQhNrmsmRlq3z5eUSzbRF5r1W1xBkaphVDTEmpFwv3HQZt1jbmPUckB1TSw1eG0kxqZ/zZPwgY3MwgXuS7dgxRN8sCfUmrWRyQjIv/pJ55clcKnt4Ql1kzKP2lkTWF2nInMl8DhHmFehgxLqLZg4+Ro6fHsrMZoM6nbGUfbR2eMDvbFVUz9yLaIp4mikcAYozdRQ1zlrD7226QzfjblLKuxfUlO8OF2OhF3jIPGAY4WjLq95jLX7MrWiOwXsv2fRNmAC4x40HT+Etufgz3f6BRZ4EUTqmfUTU3eNV7URiScBynqtVkRswy/FL1r1HnSatMDwF4Gfr79mMVHbVnPAApBrJPtlFsqNLqYit0kYqz2INLd54VmwELYVfGuR2woFMAgylyydRW99bDvr8USRVIHpfU3bV7QqRIJpqz08Q= secret_access_key: secure: WcQgb6f5iJWfoGKE9+URj62a7QvHekbE1RLIOaqXm0rQDnfkFcWc1I/oRLNmMel9yd+L4VwM+G4bTdwDYcdLU5KnEh808UK7Nq49K4zimHFeQdUyj0K1wSPMOP1jfImSCyTOU4e1x7JYNBlRrP0hGrVFDGqm3aoI7Qpcfj67jN5/psWoPZkOtQJ8Nse138v6x2OdlF7x+TmRKi7g7WD8eSIth+lJ5Tda7TMv7k42VgMr8z+RKUCfvOUEymSSYEODRPqUrulFYWugEM8pUlc+cyr2PgzBIYz4SYirbeRdIZLI3iRmGRqKq9Tz8szEsXnfJ/egvD3VmDHSal4EA6iFRp3/mDNJoupQP7dpKT71ti9ZhFm2Sfk6NE9wKYZG1NpAeobGCyysxhFVMQ1PA1x/GmmH9dWdUIjIAi10ud+T7L0g09uQe836MDhXexjU4dRx2/oXHfMC5RYLi+ebGUPlWmEreXIN0t2YxGKjykjPQf+l6TDG6ICeFBjMA/1xxToQbtKGBXIUghDYr9HDmpTiso05ML9/S3weo7+YVoqruUb57Jl11v7Mokejt4eqtLYKM9kiQsEL9QyYQj1lf1QEI3Y5/CkAPzq5Z52cQ1s84AVtUR8b8IOPhuhhqRRbvltTapHYarN1fgVNApFTsn+5uFedMv0lzKRQHEfZlEBuwkg= bucket: dh4 skip_cleanup: true local_dir: build upload-dir: mupen64plus-qt on: all_branches: true condition: $TRAVIS_PULL_REQUEST = false - provider: releases api_key: secure: grCc4CqvrdWgAOgEubIpoCKrSHTYnERysvAgJaQd7BuGaW1BXU2i+pYUv+Wcz4E33L3lFP/cpezJtMmnZ1ibPIo+bS+M9a/phvyTpiplgMSYg0TQTfs/OTaq8uJolLfZ5VELAn3yoAoIm9kdGZV4mzuF+dX8pCLkuEerTzQksTHDUoN6ixyBoLevShNdlGgj6KVa8qj39H+eVH0aGswvMlpeIdoJDIut5KSnuj4FdN7NTsVVetdppZ55K4fkOVIt7hPsrzA41RYDhCSI8svuTpQ91uDH5FiEwCK2QKtagzJQ7XIX4D1hLUKxl1VlxA6dG+sAWr2HeoaojvDYlzNi44sgWxoEfS4P4nVyC/PZcTUifSIhgSQ3NkayFncVHhlJ5+wlGSF9DPB4Rs6/VqH0ofz1qEL7/NkIpLVjAIyHqoxDjCHOvzDgPiJIIFMNaNIlcaK7CXEaMh73daia8wn41EsRURyPS7TA/QHA1XKzH8CpLhllbWa5hTOoVJtte4yXyCNxUOJmRjR5wwpGJY4uyK3d0Fk+hR8zbEyGR8Di5tfMr3/oVRnzE3w5EWkJ1QZnFieBmKdOvd40B7b772AXvo88QTMzE6srmF6wtrgAicAeW/Ag/7znBPekZ7y3j8vMabNm4GKWBmqw4yK43IElDtmUQcFfEnEil7oFZGeD/xo= skip_cleanup: true file_glob: true file: build/*/mupen64plus-qt_* on: tags: true mupen64plus-qt-1.15/LICENSE000066400000000000000000000027231413256700400152770ustar00rootroot00000000000000Copyright (c) 2013, Dan Hasting All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the organization 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 HOLDER 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. mupen64plus-qt-1.15/README.md000066400000000000000000000361541413256700400155560ustar00rootroot00000000000000# Mupen64Plus-Qt [![Build Status](https://travis-ci.com/dh4/mupen64plus-qt.svg?branch=master)](https://travis-ci.com/dh4/mupen64plus-qt) A customizable cross-platform launcher. This was adapted from CEN64-Qt to work with [Mupen64Plus](https://mupen64plus.org/). A discussion thread can be found [here](http://www.emutalk.net/threads/54976-Mupen64Plus-Qt). ![Mupen64Plus-Qt](https://raw.githubusercontent.com/dh4/mupen64plus-qt/master/resources/demos/main.jpg) ## Table of Contents * [Getting Mupen64Plus-Qt](#getting-mupen64plus-qt) * [Stable Builds](#stable-builds) * [Development Builds](#development-builds) * [Building (Linux)](#building-linux) * [First Launch and Setup](#first-launch-and-setup) * [Linux](#linux) * [Windows](#windows) * [OSX](#osx) * [ROM Collection](#rom-collection) * [Views](#views) * [Game Information from TheGamesDB](#game-information-from-thegamesdb) * [Updating Game Information](#updating-game-information) * [Deleting Game Information](#deleting-game-information) * [Manually Updating Information](#manually-updating-information) * [Editor For mupen64plus.cfg](#editor-for-mupen64pluscfg) * [Mupen64Plus Log Viewer](#mupen64plus-log-viewer) * [Configuration File](#configuration-file) * [Comparison to Console UI](#comparison-to-console-ui) * [Additional Parameters (Cheat Support)](#additional-parameters-cheat-support) * [Linux: GLideN64 Workaround](#linux-gliden64-workaround) ## Getting Mupen64Plus-Qt ### Stable Builds Stable releases for Linux, Windows and OSX can be found on the [releases](https://github.com/dh4/mupen64plus-qt/releases) page. Note the Linux release is just a tar.gz archive containing an executable. It's a 64-bit executable that is linked to Qt5, so you'll need Qt5 installed to run it. If you have issues with it, see the section below on building. ##### Linux and Unix Distributions _Debian/Ubuntu:_ Mupen64Plus-Qt is in the repositories: `apt-get install mupen64plus-qt`. Alternately, see the section below on [creating a .deb package](#creating-a-deb-package-debianubuntu). _Arch Linux:_ [https://aur.archlinux.org/packages/mupen64plus-qt/](https://aur.archlinux.org/packages/mupen64plus-qt/) _Redhat/Fedora:_ See the section below on [creating a .rpm package](#creating-a-rpm-package-redhatfedora). _FreeBSD:_ Mupen64Plus-Qt is in the ports tree: `cd /usr/ports/emulators/mupen64plus-qt && make install clean`. ### Development Builds Automatic builds of the latest git commit can be downloaded here: Linux: [mupen64plus-qt_linux_git-latest.tar.gz](https://s3.amazonaws.com/dh4/mupen64plus-qt/master/mupen64plus-qt_linux_git-latest.tar.gz) Windows: [mupen64plus-qt_win_git-latest.zip](https://s3.amazonaws.com/dh4/mupen64plus-qt/master/mupen64plus-qt_win_git-latest.zip) OSX: [mupen64plus-qt_osx_git-latest.dmg](https://s3.amazonaws.com/dh4/mupen64plus-qt/master/mupen64plus-qt_osx_git-latest.dmg) Be aware that these may contain bugs not present in the stable releases. ### Building (Linux) First, obtain the source code for Mupen64Plus-Qt. You can either clone the repository with git or download an archive of the source code for a specific release from the [releases](https://github.com/dh4/mupen64plus-qt/releases) page. You'll need to make sure you have qmake, g++, the Qt development libraries and the QuaZIP development files installed. On Debian/Ubuntu, this can be accomplished by: ``` # apt-get install qt5-qmake g++ qtbase5-dev libquazip5-dev libqt5sql5-sqlite ``` You may need to replace `libquazip5-dev` with `libquazip-qt5-dev` depending on your OS version. Once the needed packages are installed, create the Makefile with qmake and then build with make. Run the following commands from the directory that contains mupen64plus-qt.pro: ``` $ qmake $ make ``` If qmake returns "qmake: could not find a Qt installation of ''", you can try running `QT_SELECT=qt5 qmake` or `/usr/lib/x86_64-linux-gnu/qt5/bin/qmake`. Some distributions also contain a `qmake-qt5` symlink. ##### Compiling QuaZIP statically You also have the option to compile QuaZIP statically. Download the QuaZIP sources from Sourceforge. Place the contents of `quazip-/quazip/` in `quazip5/` within the project directory. Then run: ``` $ qmake CONFIG+=linux_quazip_static $ make ``` You will see warnings after the qmake step if the QuaZIP sources are in the wrong place. #### Creating a .deb package (Debian/Ubuntu) You can build and install a debian package using the files found in dist/debian: ``` $ sudo apt-get install build-essential devscripts debhelper qt5-qmake qtbase5-dev libquazip5-dev libqt5sql5-sqlite $ ./dist/debian/deb-build.sh $ sudo dpkg -i mupen64plus-qt_-1..deb ``` You may need to replace `libquazip5-dev` with `libquazip-qt5-dev` depending on your OS version. #### Creating a .rpm package (Redhat/Fedora) You can build and install an RPM package using the spec file found in dist/redhat: ``` $ sudo dnf install rpmdevtools qt5-qtbase-devel quazip-qt5-devel zlib-devel gcc-c++ $ ./dist/redhat/rpm-build.sh $ sudo rpm -i ~/rpmbuild/RPMS//mupen64plus-qt-rpm-..rpm ``` Note the resulting package is named mupen64plus-qt-rpm to avoid conflicts with some third-party mupen64plus RPMs. ## First Launch and Setup Mupen64Plus-Qt contains code that attempts to auto-detect a Mupen64Plus installation. These settings are located at Settings->Configure->Paths. See below for a per-OS description of this and what to do if Mupen64Plus is not auto-detected. #### Linux Mupen64Plus-Qt looks within common locations distributions place the mupen64plus binary, plugin libraries and data files. If any of these aren't found, obtain the Mupen64Plus package for you distribution and peek inside to see where the files are placed (or use a utility such as find or mlocate). The plugin directory should contain mupen64plus-video-rice.so, mupen64plus-video-glide64mk2.so, etc. The data directory should contain mupen64plus.ini. You should be able to find the executable by running `which mupen64plus`. #### Windows It's best to place mupen64plus-qt.exe within the same folder as mupen64plus-ui-console.exe (mupen64plus.exe before version 2.5) as Mupen64Plus-Qt looks in the same directory it's running from to try and find the executable, plugins and data files. If you place it somewhere else, set the plugin directory and data directory to the same folder containing mupen64plus-ui-console.exe. #### OSX Mupen64Plus-Qt looks for mupen64plus.app within the same directory where Mupen64Plus-Qt.app is placed. If you place it somewhere else, simply select mupen64plus.app as the executable and it will autofill the plugin and data directory settings for you (these are within mupen64plus.app). ## ROM Collection You can specify ROM directories under Settings->Configure->Paths. Mupen64Plus-Qt will search for any .z64, .v64 or .n64 files within these directories and also check within .zip files and subdirectories (one level deep). After adding or removing directories to this list, it will automatically refresh your ROM list. You can manually refresh your list with File->Refresh List. #### Views Mupen64Plus-Qt contains three options for displaying your ROMs: a table view, grid view and list view. The view can be changed under Settings->Layout. These can be configured under Settings->Configure.

Table View Grid View List View

In each of the views, you can simply double click a game to launch it, or single click to select it and use Emulation->Start. ## Game Information from TheGamesDB Mupen64Plus-Qt supports downloading game information and cover images from [thegamesdb.net](http://thegamesdb.net/). This can be enabled under Settings->Configure->Other. It's recommended you have the data directory set (under Paths) before using this. Once enabled, you'll need to refresh your ROMs list to download the information. Afterwards, images and other information about the game can be added to the views. ***Note:*** TheGamesDB shut off version one of their API which is used in Mupen64Plus-Qt 1.11 and below. You will need version 1.12 to download game information. #### Updating Game Information If a game is not found or is incorrect, Mupen64Plus-Qt supports refreshing information for a single ROM. Just select the ROM and go to File->Download/Update Info. From here you can enter a different search or the specific ID of the game (from the URL of the game on thegamesdb.net). #### Deleting Game Information You can delete the game information fetched from TheGamesDB by using File->Delete Current Info. Note that if you are deleting a game's information because the game doesn't exist on TheGamesDB and Mupen64Plus-Qt pulled the information for different game, it's better to create an account on TheGamesDB and add the game so other users can benefit as well. This will cause Mupen64Plus-Qt to not update the information for this game until you force it with File->Download/Update Info #### Manually Updating Information If desired, you can also manually update the information from TheGamesDB. Note that if any information is incorrect, it's best to create an account on TheGamesDB and fix it there so all users can benefit. The information cache can be found here: Linux: /home/\/.local/share/mupen64plus-qt/cache_v2/\/ Windows: cache_v2 folder in same directory as executable -> MD5 of game OSX: /Users/\/Library/Application Support/mupen64plus-qt/cache_v2/\/ You can find the MD5 of a game by using the table or list view and adding "MD5" to the current information. Edit data.json with a text editor and replace any information you want to change. You can also replace boxart-front.{jpg,png} with an image of your choosing. #### Extending the network timeout to TheGamesDB If you are getting timeout messages due to an issue with slow responses from TheGamesDB API, you can extend the timeout by editing the [configuration file](#configuration-file) and adding `networktimeout` under [Other]: ``` [Other] ... networktimeout=30 ``` This example shows 30 seconds. The default is 10 seconds. ## Editor For mupen64plus.cfg Table View Mupen64Plus-Qt contains an editor with syntax highlighting for mupen64plus.cfg. To use it, make sure you have your config directory set under Settings->Configure->Paths. Mupen64Plus-Qt should auto-detect this setting for you. If it doesn't, the default location is: Linux: /home/\/.config/mupen64plus/ Windows: C:/Users/\/AppData/Roaming/Mupen64Plus/ OSX: /Users/\/.config/mupen64plus/ ## Mupen64Plus Log Viewer Mupen64Plus-Qt allows you to view the output from the Mupen64Plus console UI. Select Emulation->View Log after Mupen64Plus has quit to see the output from the last run. ## Configuration File Mupen64Plus-Qt saves its settings to a text file. If for some reason you would like to modify this, it's contained at the following locations: Linux: /home/\/.config/mupen64plus/mupen64plus-qt.conf Windows: mupen64plus-qt.ini within the same directory as the executable OSX: /Users/\/Library/Preferences/com.mupen64plus.mupen64plus-qt.plist ## Comparison to Console UI Mupen64Plus-Qt utilizes the console UI to launch games. It contains support for most of the command line parameters. These can be viewed by running Mupen64Plus from a terminal with the --help parameter or [here](https://mupen64plus.org/wiki/index.php?title=UIConsoleUsage). See below for how each parameter is used within Mupen64Plus-Qt: ##### --noosd/--osd One or the other is sent to Mupen64Plus based on whether the "On Screen Display" checkbox is checked under the graphics tab. ##### --fullscreen/--windowed Similarly, one or the other is sent based on the "Fullscreen" checkbox underneath the graphics tab. ##### --resolution This can be configured with the "Resolution" combo box underneath the graphics tab. The list is generated based on common resolutions smaller than your current desktop resolution. If you require a resolution not in the list, just set the "default" option which will not send the --resolution parameter to Mupen64Plus (Mupen64Plus will read from whatever setting is within its configuration file in this case). ##### --configdir This is configured under the paths tab. It's entirely optional, but allows you to specify a different directory to load the mupen64plus.cfg file from (useful if you want to say, load different input settings at different times). You can also specify the config directory on a per-game basis under Settings->Configure Game. ##### --datadir This is set underneath the paths tab. Mupen64Plus-Qt will also make use of the data directory setting to load the GoodName text and other information from the mupen64plus.ini file, so make sure this directory contains that file. It's usually /usr/share/mupen64plus on Linux or the same directory as the executable on Windows. ##### --plugindir The plugin directory is usually /usr/lib/mupen64plus or within the same directory as the executable on Windows. Mupen64Plus-Qt also makes use of this directory setting to populate the options on the plugins tab (you must click Ok first before they will populate). ##### --gfx/--audio/--input/--rsp These are configured under the plugins tab once the plugin directory has been set. You can also configure plugins on a per-game basis under Settings->Configure Game. ##### --emumode This is configured under the emulation tab. ##### --saveoptions/--nosaveoptions One or the other is sent to Mupen64Plus based on the "Save Options" checkbox underneath the other tab. If --saveoptions is set, then Mupen64Plus will save whatever settings are passed to it into its configuration file (mupen64plus.cfg) #### Additional Parameters (Cheat Support) Mupen64Plus-Qt also supports specifying additional parameters both globally under Settings->Configure->Other->Additional Parameters and on a per-game basis under Settings->Configure Game->Other Parameters. You can specify any other parameters that the console UI supports here. The main use of this is specifying cheats. For example, filling this with `--cheats 'list'` will allow you to double click on a ROM and then view the log for a list of cheats for that game (remove it to be able to launch the game again). You can then specify what cheats you want with `--cheats 1,2-1,3`, etc. Be aware that if this contains anything that Mupen64Plus doesn't support, the emulator will not be able to launch. ## Linux: GLideN64 Workaround Mupen64Plus-Qt contains support for [this](https://github.com/gonetz/GLideN64/issues/454#issuecomment-126853972) workaround for GLideN64 on Linux. There is no graphical option for this as it's more of a hack and should be fixed on GLideN64's end. Edit your configuration file at ~/.config/mupen64plus/mupen64plus-qt.conf and add "forcegl33=true" under [Other]. mupen64plus-qt-1.15/VERSION000066400000000000000000000000051413256700400153310ustar00rootroot000000000000001.15 mupen64plus-qt-1.15/build-scripts/000077500000000000000000000000001413256700400170525ustar00rootroot00000000000000mupen64plus-qt-1.15/build-scripts/platform/000077500000000000000000000000001413256700400206765ustar00rootroot00000000000000mupen64plus-qt-1.15/build-scripts/platform/linux.sh000077500000000000000000000015621413256700400224000ustar00rootroot00000000000000#!/bin/bash [[ -z $WORKING_DIR ]] && WORKING_DIR=$(pwd) [[ -z $VERSION ]] && VERSION=$(git log --oneline -n 1 | awk '{print $1}') [[ -z $ARCH ]] && ARCH=".$(uname -m)" [[ $ARCH == ".x86_64" ]] && ARCH="" case "$1" in 'setup_qt') sudo apt-get update -qq sudo apt-get -y install qt5-qmake qtbase5-dev libqt5sql5-sqlite zlib1g-dev ;; 'get_quazip') wget http://downloads.sourceforge.net/quazip/quazip-0.7.3.tar.gz tar -xvzf quazip-0.7.3.tar.gz > /dev/null mv quazip-0.7.3/quazip quazip5 ;; 'build') ./build-scripts/revision.sh qmake -qt=qt5 CONFIG+=linux_quazip_static make ;; 'package') mkdir -p "build/$TRAVIS_BRANCH" mv resources/README.txt . tar -cvzpf "build/$TRAVIS_BRANCH/mupen64plus-qt_linux_$VERSION$ARCH.tar.gz" mupen64plus-qt README.txt ;; esac mupen64plus-qt-1.15/build-scripts/platform/osx.sh000077500000000000000000000035411413256700400220510ustar00rootroot00000000000000#!/bin/bash [[ -z $WORKING_DIR ]] && WORKING_DIR=$(pwd) [[ -z $VERSION ]] && VERSION=$(git log --oneline -n 1 | awk '{print $1}') [[ -z $ARCH ]] && ARCH=".$(uname -m)" [[ $ARCH == ".x86_64" ]] && ARCH="" case "$1" in 'setup_qt') mkdir "$WORKING_DIR/../osx" cd "$WORKING_DIR/../osx" if [[ $BUILD_OSX_QT ]]; then # Build Qt # Perform commands in /Users/travis/build/osx/ if building for travis git clone --branch v5.10.0 --depth 1 https://code.qt.io/qt/qtbase.git cd qtbase ./configure -release -static -sql-sqlite -opensource -confirm-license make sub-src else # Fetch pre-build Qt wget https://www.dropbox.com/s/mfw6qwfir62fy9p/macosx-qt5.tar.gz tar -xvzf macosx-qt5.tar.gz >& /dev/null fi cd "$WORKING_DIR/mupen64plus-qt" ;; 'get_quazip') wget http://downloads.sourceforge.net/quazip/quazip-0.7.3.tar.gz tar -xvzf quazip-0.7.3.tar.gz >& /dev/null mv quazip-0.7.3/quazip quazip5 ;; 'build') ./build-scripts/revision.sh $WORKING_DIR/../osx/qtbase/bin/qmake -config release LIBS+="-dead_strip" make ;; 'package') mkdir -p "build/$TRAVIS_BRANCH" hdiutil create -megabytes 20 -fs HFS+ -volname Mupen64Plus-Qt "mupen64plus-qt_osx_$VERSION$ARCH" hdiutil attach "mupen64plus-qt_osx_$VERSION$ARCH.dmg" cp -r Mupen64Plus-Qt.app /Volumes/Mupen64Plus-Qt/Mupen64Plus-Qt.app cp resources/README.txt /Volumes/Mupen64Plus-Qt/README.txt hdiutil detach /Volumes/Mupen64Plus-Qt hdiutil convert -format UDZO -o "mupen64plus-qt_osx_$VERSION$ARCH.dmg" \ -ov "mupen64plus-qt_osx_$VERSION$ARCH.dmg" mv "mupen64plus-qt_osx_$VERSION$ARCH.dmg" "build/$TRAVIS_BRANCH/" ;; esac mupen64plus-qt-1.15/build-scripts/platform/windows.sh000077500000000000000000000032501413256700400227270ustar00rootroot00000000000000#!/bin/bash [[ -z $WORKING_DIR ]] && WORKING_DIR=$(pwd) [[ -z $VERSION ]] && VERSION=$(git log --oneline -n 1 | awk '{print $1}') [[ -z $ARCH ]] && ARCH=".$(uname -m)" [[ $ARCH == ".x86_64" ]] && ARCH="" case "$1" in 'setup_qt') cd "$WORKING_DIR/../" if [[ $BUILD_MXE_QT ]]; then # Build Qt with mxe sudo apt-get update -qq sudo apt-get install \ autoconf automake autopoint bash bison bzip2 cmake flex \ gettext git g++ gperf intltool libffi-dev libtool \ libltdl-dev libssl-dev libxml-parser-perl make openssl \ p7zip-full patch perl pkg-config python ruby scons sed \ unzip wget xz-utils libtool-bin lzip libgdk-pixbuf2.0-dev git clone https://github.com/mxe/mxe.git cd mxe make qtbase else # Fetch pre-built mxe Qt wget https://www.dropbox.com/s/jr6l4lnixizqtln/travis-mxe-qt5.tar.gz tar -xvzf travis-mxe-qt5.tar.gz > /dev/null fi cd "$WORKING_DIR/mupen64plus-qt" ;; 'get_quazip') wget http://downloads.sourceforge.net/quazip/quazip-0.7.3.tar.gz tar -xvzf quazip-0.7.3.tar.gz > /dev/null mv quazip-0.7.3/quazip quazip5 ;; 'build') export PATH="$PATH:$WORKING_DIR/../mxe/usr/bin" ./build-scripts/revision.sh i686-w64-mingw32.static-qmake-qt5 make ;; 'package') mkdir -p "build/$TRAVIS_BRANCH" mv release/mupen64plus-qt.exe resources/README.txt . zip "build/$TRAVIS_BRANCH/mupen64plus-qt_win_$VERSION$ARCH.zip" mupen64plus-qt.exe README.txt ;; esac mupen64plus-qt-1.15/build-scripts/revision.sh000077500000000000000000000004041413256700400212450ustar00rootroot00000000000000#!/bin/bash git show-ref --tags | grep "`git log -n 1 --pretty='%H'`" > /dev/null 2>&1 if [[ $? == 1 ]]; then REVISION=$(git log -n 1 --pretty='%h' 2> /dev/null) [[ $REVISION ]] && sed -i -e "s/\([0-9.]*\)/\1~git:$REVISION/g" VERSION fi cat VERSION mupen64plus-qt-1.15/build-scripts/travis-ci.sh000077500000000000000000000007721413256700400213200ustar00rootroot00000000000000#!/bin/bash export GITHUB_USER=$(echo $TRAVIS_REPO_SLUG | cut -d "/" -f1) export WORKING_DIR=$HOME/build/$GITHUB_USER if [[ $1 == 'package' ]]; then VERSION=$(echo $TRAVIS_TAG | sed -e 's/v//g') [[ -z $VERSION ]] && VERSION='git-latest' export VERSION=$VERSION fi case "$TRAVIS_OS_NAME-$BUILD" in 'linux-windows') ./build-scripts/platform/windows.sh $1 ;; 'linux-native') ./build-scripts/platform/linux.sh $1 ;; 'osx-native') ./build-scripts/platform/osx.sh $1 ;; esac mupen64plus-qt-1.15/dist/000077500000000000000000000000001413256700400152315ustar00rootroot00000000000000mupen64plus-qt-1.15/dist/debian/000077500000000000000000000000001413256700400164535ustar00rootroot00000000000000mupen64plus-qt-1.15/dist/debian/control000066400000000000000000000013731413256700400200620ustar00rootroot00000000000000Source: mupen64plus-qt Maintainer: Dan Hasting Section: games Priority: optional Rules-Requires-Root: no Build-Depends: debhelper-compat (= 12), libqt5sql5-sqlite, libquazip5-dev, qt5-qmake, qtbase5-dev Standards-Version: 4.5.0 Homepage: https://github.com/dh4/mupen64plus-qt Package: mupen64plus-qt Architecture: any-i386 any-amd64 Depends: mupen64plus-ui-console, libqt5sql5-sqlite, ${misc:Depends}, ${shlibs:Depends} Description: customizable launcher for Mupen64Plus Mupen64Plus-Qt is a customizable launcher for the standard console UI frontend of Mupen64Plus. It supports downloading game information and cover images from thegamesdb.net. mupen64plus-qt-1.15/dist/debian/copyright000066400000000000000000000027231413256700400204120ustar00rootroot00000000000000Copyright (c) 2013, Dan Hasting All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the organization 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 HOLDER 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. mupen64plus-qt-1.15/dist/debian/deb-build.sh000077500000000000000000000006751413256700400206510ustar00rootroot00000000000000#!/bin/bash cd `dirname $0`/../../ VERSION=`cat VERSION` mkdir deb-build cd deb-build wget https://github.com/dh4/mupen64plus-qt/archive/$VERSION.tar.gz if [[ $? == 0 ]]; then tar -xvzf $VERSION.tar.gz cp -r ../dist/debian mupen64plus-qt-$VERSION cd mupen64plus-qt-$VERSION debchange --create -v $VERSION-1 --package mupen64plus-qt "Version $VERSION" debuild -i -us -uc -b mv ../mupen64plus-qt_$VERSION*.deb ../.. fimupen64plus-qt-1.15/dist/debian/docs000066400000000000000000000000121413256700400173170ustar00rootroot00000000000000README.md mupen64plus-qt-1.15/dist/debian/install000066400000000000000000000003521413256700400200440ustar00rootroot00000000000000mupen64plus-qt usr/games resources/mupen64plus-qt.desktop usr/share/applications resources/mupen64plus-qt.6 usr/share/man/man6 resources/images/mupen64plus-qt.png usr/share/icons/hicolor/128x128/apps/ mupen64plus-qt-1.15/dist/debian/rules000077500000000000000000000001511413256700400175300ustar00rootroot00000000000000#!/usr/bin/make -f export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed export QT_SELECT = qt5 %: dh $@ mupen64plus-qt-1.15/dist/debian/source/000077500000000000000000000000001413256700400177535ustar00rootroot00000000000000mupen64plus-qt-1.15/dist/debian/source/format000066400000000000000000000000141413256700400211610ustar00rootroot000000000000003.0 (quilt) mupen64plus-qt-1.15/dist/macosx/000077500000000000000000000000001413256700400165235ustar00rootroot00000000000000mupen64plus-qt-1.15/dist/macosx/mupen64plus.icns000066400000000000000000001474461413256700400216230ustar00rootroot00000000000000icns&TOC 8is32s8mkil32 l8mkit32|t8mk@is32  8M%B1%}%%'f06ؾإ12oЇ;2OV˯0ʸ2ٯM.Ę߉w 3.R[v ^~]Z}uA a]hR`B < QM" G18 8L&D .$Ыx$6?08R$!.(]W*rCJA&&<#-(".beB 2Q;&/PaPAS8(&U\=/G;#7=5B;' 6 .5s8mk "p  1,h!g\v!qsrsntxU&2.6 F)QL il32 ˋ  BuxV 5faZɽRƼF 漦+DLq}bPrRuz]_qHq~bxotGOfG_}mt.]g5>ѩLDɛʰ9Қf๳iҺ<,ԉ]IYÿbUv@^qƼ;ˢ9,鞀K9}ܵ;2CȲ;̙׋ַŰ;ɬ笒;᰹ü夏;ƾĽק;ѽϺԜ;~°نugԚyz];B_lsqEw½2c wjAp/^2ropR ,Ĕ;Y&ӣy|l-B[ѻ܆&zzuqeO:FZVͻ~,ƋgZNe]ol_Ǫɒ[Bj;Z|?S'>p #jFLu4 ,w'TW"5 XN\(  BuwV 5gbZʻQƼF ,C! H{r}bNrStsR~\`oH~s|\ÂkuߒA^z:HbYI\CYcZp9_HV 1<HC"1P6}; 2=G\: l;U YHu1=F5 \ 1J E/< 2<DN3 4I2 1<FYj/GOs2<GNI;B2EC (UR%4<G]Za1ChP4<F]c\2F R{o1-^K.>JXDTN3FY2Y ]!HF\=4P3FVL UFaV`7M3F S3J3z0Q-a7nF_;F!3GG2v2DH!4GI0ʽgE&2FE4⾯uL"8L UZNkCZ'8_,AMVhS+Oo}T? (l^+= ]Wc)  BuwV 5gaZʼQƼF ,C! H{r}bNrStvU~\`oH~s|\ÁkuߒA^|<>cuBc}kx,U@XhB*L6\I?E̫~R-Prcf0;K zjE5 y"!HZ|65>KdYI\G[cZr>`GW4<K B"0P6~>6=K`:qC^^Hx 5=I:a 2M K3@6<FP9 :N  ;5<H^o  3M Rw6<I RL?G8K J *YU+7<Ib`!g7InQ8<Gag\#8LTzr4/aN/>L[ETN$9LZ4W]R(H F ` =4P#9LXEv.h?#CdYd7M#8LV;NG BÂXUdeba bedVVG !Äjiuusr suukj) o껗 zͰ#cֿn%Ͽ%fr· kǩľw)!)pĽ}+るƾ$+upq u.-hqw|մ纷}xre) (~z!xڌ4.zUkmtzۚݍ§zsnlV,&$|wk9/!OY_ejpv|ٝ{upkd`\K.2os؂+d+q4'(.a=t@CIOTY`ejsnߎ݉kske_[TOIE@=)VDyNL[\YXXY\ZKO}ޏ= Jˌb]kmjiEjll`]ܘ[Eݮ036;AEKQV[bjd݉jia]WQLGA;830NUÅ__lmji jml^`ҐN  jl~{?~qfO)*/49=CILR[Sߏ݉Q\SNHB?:3/+)Ego~{ ~nh. 0ݿC""(,06:?DJQL݊SRJD@:52,(""Լ =ʭEs$(.27BGMRX^bp)'ҊJXOIE@:51+'#'{챇2'"',14:@CINWJ)3щbHHA;83-)% ! $ä3) ! %(-26;AGI[)3ч1C93/+&! %ε3+!&*/49B0)mІO13,'#%ȼ+"(,13I¿)?І.$ $), $.),τA% *-8ÿ)*ς%+-y)+΁3 &+. )¿)+΁{(,1s),̯,(˭),~ħ-Ȫ筫)-}ڪ-ޯ)-{.୧).z۬.¿⫥).yè/⪥)/x⮯/¿ᨤ)/vǧ0ᨢ~)0v䰮0¿ߦ})0t˩0ߤ{)1s贮0ÿटz)1rά1¿࢝x)2r궯1ÿߡx)2pʿϮ2¿ޠw)7n뺱2ÿޟu)mnҰ¿ٷޞu)mnĿ츳ݞu)mm¾ز﮽Ԯڙt)mmȳ添衛t)mn~пĵc?G]кտ˲{zxuyt)Wm~νd$1_ѲV  Pt)Xm~~̺` +7]ΐbLyz|z{~ {zB\t)Vm~~̹qo~!)5u|fΐN͉ Ht)m~ʷ@')5 (ˎJ➬ Gt)m~ȴK !)5 7ˌJ ډ Gt)m~ƴI!)5 4ɊH ޏ Gt)m~űI!)5 4ɇH ݎ Gt)m~ůI!)5 4ɆF ݎ Gt)m~îI!)5 4DŽFݎ Gt)m~I!)5 4łEݎGs)m~}I!)5 4ȃEݎHu)n~}H3)56~Dݎ Fs, f}~{uL!)5@9!&1ގ )";= tz~z0J)5 8RMNLEEJIJDx֐nFJIJEEMNMR2E괇tz~yST)4HĐ{*Q췉tyxe')߸4kǃ&~vANvzza ')߶4_𵊕'}ywvmd`]ZWTROMN4<In^ ')ߵ4a)}ywurpnmjhec`^[YZB=FYT(ݴʽ4a}ywurpljhfb`^[XVTRS:=BVT(ݲȼ4a󽔞}ywurpljhfb`^[XVTRPLN4=EXT(ܱǻ4a󼑗}ywurpljhfb`^[XVTRPLJHJ/= T(ܯƹ4a󸊑}ywurpljhfb`^[XVTRPLJHFDE+=T(ۯŸ4a򳄌}ywurpljhfb`^[XVTRPLJHFDB>A%=T(ۭö4a~{yvsrnkgfb`^[XVTQPNLJHEC@><>#=T(ۭµ4`u}zvtqnlifdec`^[XVTRPMJDA?=:86323=T(٫4a|}{xvsspc]\XVTRPLKEJVQMKIGFCAD'=T(٩4_ӎMYTRPLJHH=8'(ب¿4$w>VPLJHFEC8 d!(ا4 ε9QJHFDB?=1#(ئ¿4 Ѵ3LFDB><;9,!(Ӣ4Ҳ.HB><;875) !(觩5ү)D<;87431% !|˳ýҭ#?87420/- Wжaӫ;420.,+) UӺſ^ ө70.,*('% QؿZԧ3,*(&$#! MWԧ3,*(&$#" JRԗ   GP 䚫 ^fxɂȼj   qqqq   /2 $x*(ۆ0/i)&dމ43q91CD26lߏ98yGCSURQUSDFu> BÂXUdeba bedVVG !Äjiuusr suukj) o껗 zͰ#cֿn%Ͽ%fr· kǩľw)!)pĽ}+るƾ$+upýq u.-hqw|uT躷}xre) (~z!xڌ4.zUkmtzƴ zsnlV,&$|wk9/!OY_ejpv|R͸Hۜ{upkd`\K.2os؂+d+q4'(.a=t@CIOTY`ejsnȲ kske_[TOIE@=)VDyNL[\YXXY\ZKO}ޏ= Jˌb]kmjiEjll`]ܘ[Eݮ036;AEKQV[bjdMȲBhia]WQLGA;830NUÅ__lmji jml^`яN  jl~{?~qfO)*/49=CILR[S ȲQ\SNHB?:3/+)Ego~{ ~nh. 0齛C""(,06:?DJQKGȲ>RRJD@:52,(""Լ =îbǪEr$(.27BGMRX^bl'χ[IXOIE@:51+'#'w|&"',14:@CINWIj'χ]IHA;83-)% !xxx" ! %(-26;AGIX'χT0C93/+&! L u˱y(A% !&*/49B0f'χCJ13,'#AqĻv!;&"(,13D'χAO.$ =ot7% $.`'χ<9ns5&3׿'χK6jq2'yZ'χ . 3 $/) %ջ'χ F}"0  +,rU'χFg  /DZ )#׶aP'χI F.Ư &УSE'χ| 1Ư&'χ:8~2Ư*:2'χƯ 'χ֞?Ư 6ɨ'χ <Ư @˩'χ ]?Ư 8v ܥ'χ &ȦƯ ڥ'χ 0fDƯ <{$צ'χ , Ư !צ'χ *^GƯ @sצ'χ * *>Ư52 צ'χ *XJƯ Dlצ'χ *wƯs צ'χ *SP {Ư kIgצ'χ*  BSxƯ j[2צ'χ*LooBpƯeGffbԤ'χ* ? xƯp! = 'χ*xD"e8{Ư"tC^'Bބ 'χ*pte zƯs/ aw|% 'χ*e` 'zƯ,s5^rR}~wy}I'χ*dpk{!%zƯs3rwasʼn 'χ*c@ %zư s3 't# 'χ*cL %zưs3  6s 'χ*cJ%zưs3 3s! 'χ*cJ%zưs3 3s! 'χ*cJ%zưs3 3s! 'χ*cJ%zưs3 3s! 'χ*cJ%zưs3 3r! 'χ*dJ%zưs3 3t! 'Έ*bJ%zưs35r % *}*dJ%zưs3C!B  a*W-%zư/s38RLMLDHMMPHwmHPMNIDLMLR3Ai*]Q%zư9s3I*Pm*`f%zưs3 k$BLr0 da %zư s3]&=Gw#l]%zư s3`(>CsX%zư s3`*>?U%zư9s3`>BT%zư9s3`> %zư9s3`|>%zư9s3_u>%zư9s3_q=%zư9s3^~zzd=%zư9s3bn>%zưs3`ȧ 8%zư s3$wݚ疍 d%zưs3~ಎ%|ưu3"܋yݸ%q ư h3ل{r۸  %էư3~~zxvlٸ !` ư`v~zwuspfָ Wd ư dbpzwuspmi`Ժ ShƱ h]hzuspmigdZѺ OmƱlXbvpmifda^UϺ Kj1ưnT^rkifca^\Qͻ  GO̽DYSOMJFDA7ù  DN! ]ey¹j   qqqq   /2 $x*(ۆ0/i)&dމ43q91CD26lߏ98yGCSURQUSDFu> BÂXUdeba bedVVG !Äjiuusr suukj) o껗 zͰ#cֿn%Ͽ%fr· kǩľw)!)pĽ}+るƾ$+upžq u.-hqw|vW躷}xre) (~z!xڌ4.zUkmtz ƵzsnlV,&$|wk9/!OY_ejpv|XθPۜ{upkd`\K.2os؂+d+q4'(.a=t@CIOTY`ejsnɴ kske_[TOIE@=)VDyNL[\YXXY\ZKO}ޏ= Jˌb]kmjiEjll`]ܘ[Eݮ036;AEKQV[bjdTɴIiia]WQLGA;830NUÅ__lmji jml^`яN  jl~{?~qfO)*/49=CILR[SɴQ\SNHB?:3/+)Ego~{ ~nh. 0轜C""(,06:?DJQKO ɴ DRRJD@:52,(""Լ =ïcǪEr$(.27 %? Ladjot{~ 'Ή K~xqlfaZXFh{  9i+(d>  nrCWZ`elqv|( 'Ή I kzsnib\WRLL2* 9p:1CD27l? "ǵ3ILRV]bgntzz  'Ή Izwpkd_ZSMJDA0Q =yGCSURQUSDGuA q^-BDHNSX_djpv{# 'Ή !gdpfa\UQKE@;65 HÃXUdeba!bedVVNϤ57:@FJOU\`fpdu  'Ή ra^WRMGC=83/-@ӱpˆihuusrsuuji{L-.29>BGMRX^bl 'Ή  cIXOIE@:51+'#'x|&"',14:@CINWIq  'Ή ]IHA;83-)% !x򿠑||# ! %(-26;AGIX 'Ή  ]0C93/+&! U)u˱{0J% !&*/49B/l  'Ή CK13,'#I#qĻw* A&"(,13E 'Ή A Y.$ E  mt&  >& $.g  'Ή *=?   lq#  :'4 'Ή  T;  io   7(xc  'Ή   . 8  .*  3) %  'Ή  O}"6    1,r^  'Ή Pl  6  ȶ  .$׾gY 'Ή  O  N4  Ǵ ,Ϥ^  K 'Ή    4  Ǵ  , 'Ή  @  C=  Ǵ :F  7 'Ή   Ǵ   'Ή  ֡G  Ǵ  =ˬ 'Ή  D¨ ǴF̬ 'Ή  cH  Ǵ >{ܩ 'Ή  )̪  Ǵ ۩ 'Ή  4mL  Ǵ C)ة 'Ή   1ů   Ǵ  !'ة 'Ή   /gQ   Ǵ  Hy$ة 'Ή   /  2E  Ǵ =:  $ة 'Ή   /aU    Ǵ  Lr $ة 'Ή  /   {  Ǵ w   $ة 'Ή  / \Z   ~  Ǵ o  Qm $ة 'Ή /    JZ{  Ǵ ma9#  $ة 'Ή / Xs  pIs  Ǵ hLg$  jh$է 'Ή / @ #z  Ǵ s촻)? & 'Ή /zF(e9~   Ǵ, wC^-Dއ$  "  'Ή /!rse }   Ǵ v/ aw)  'Ή / i` '}  Ǵ, v5^v QI 'Ή / fpk|!%}  Ǵ v3rwav  'Ή / fA %}  Ǵ  v3 'v Wnjkjk۶ 'Ή / fL %}  Ǵ v3 6v Ѳ0LGHI>Ž  'Ή / fJ%}  Ǵ v3 3v ѷ:UQRRI˼  'Ή / fJ%} Ǵ v3 3v Ѷ8SOPPGʼ  'Ή / fJ%} Ǵ v3 3v Ѷ8SOPPGʼ  'Ή / fJ%}  Ǵ v3 3u Ѷ8SOPPGʼ  'Ή / fJ%}  Ǵ v3 3t Ѷ8SOPPFʼ  'Ή / gJ%} Ǵ v3 3v Ѷ8SO NCɼ  '͊ / dJ%} Ǵ v35s !Ҷ8SONMKJAƾ *π / jK%} Ǵ v3A ˷8SONMJJIH=Ǵ A  a / W.%} Ǵ/ v3 8RMNMFINNPIs9RMJJHGGF?kIQNOJEMOMS2Bl /  _Q%} Ǵ9 v3H<;=>4*(%%$"! "<Gz& p]%} Ǵ9 v3bEURSRQPNMKKHEDB@@>>=;:;::98764425=CsX%}  Ǵ9 v3bBRONMJJHGGEDB@@>>=;:976643200..,/=?U%}  Ǵ9 v3bBQMJJHGGEDB@@>>=;:976643200..,**-=BU%} Ǵ: v3b@MJHGGEDB@@>>=;:976643200..,**((+= %} Ǵ: v3b>=;:976643200..,**((&%) =%} Ǵ: v3b:JEDB@@>>=;:976643200..,**((&%$$' =%} Ǵ; v3b9JDBB@?>=<;76643200..,+,*)('&&$$' =%} Ǵ; v3bw0?;99865423543200..,*+'#! ~=%} Ǵ9 v3a?PMLJIHFEGD5100..,**)%+953210/.-1<%} Ǵ v3^p"2.,**(sـ7%}  Ǵ v3$w 1**((&󛔘 c%}  Ǵ v3 Ң/((&%$$#% Ǵ x3բ -&%$$""!%tǴk3 ա +$$""   %ը Ǵ  3ՠ)""  !c Ǵ  b֟&  Wh Ǵ gb ֞% SmǴ l]֝# Op ǵpX ֝! Ko5dz#sU ם#  GP֏  DN  ᒥ ]exʄɼj   qqt8mk@1{86|π=:~ӃA@؈FC܋KHO=E?I!ao#dr&iw*mz!.p}&hn6$30lj+-ẅ́@s:{32yօD'7>~86|ۊJwCρ=<ߎN+;GԃA@S{L؈EC|ۄۉIv lt                                                  ֶ c,/] a \ ri   )  % % % % %  %xٝe$o\~+(72 _n. ^i/Ze/Vb/Sa/P^ /J~W////05١-DFEEEEEEJ??JEEEEEEG@ wd p nQ\nRZ nbgn _dn ]a~mY^ oV[{bSʘX0 24)y~xyyyyyyyyyzn!icnV C mupen64plus-qt-1.15/dist/redhat/000077500000000000000000000000001413256700400165005ustar00rootroot00000000000000mupen64plus-qt-1.15/dist/redhat/mupen64plus-qt.spec.in000066400000000000000000000030031413256700400226010ustar00rootroot00000000000000%define _name mupen64plus-qt Name: mupen64plus-qt-rpm Version: @@VERSION@@ Release: 1 Summary: A customizable launcher for Mupen64Plus Group: Applications/Emulators License: BSD URL: https://github.com/dh4/mupen64plus-qt Source0: https://github.com/dh4/%{_name}/archive/%{version}.tar.gz BuildRequires: qt5-qtbase-devel, quazip-qt5-devel, gcc-c++ Requires: qt5-qtbase, quazip-qt5 %description Mupen64Plus-Qt is a customizable launcher for the mupen64plus-ui-console frontend. It was adapted from CEN64-Qt to work with Mupen64Plus. See the README at https://www.github.com/dh4/mupen64plus-qt for a detailed description of its features and usage. %global debug_package %{nil} %prep %autosetup -n %{_name}-%{version} %build qmake-qt5 make %{?_smp_mflags} %install install -Dm755 "mupen64plus-qt" "%{buildroot}%{_bindir}/mupen64plus-qt" install -Dm644 "resources/mupen64plus-qt.desktop" "%{buildroot}%{_datadir}/applications/mupen64plus-qt.desktop" install -Dm644 "resources/images/mupen64plus.png" "%{buildroot}%{_datadir}/icons/hicolor/128x128/apps/mupen64plus-qt.png" install -Dm644 "resources/mupen64plus-qt.6" "%{buildroot}%{_mandir}/man6/mupen64plus-qt.6" %files %license LICENSE %doc README.md %{_bindir}/mupen64plus-qt %{_datadir}/applications/mupen64plus-qt.desktop %{_datadir}/icons/hicolor/128x128/apps/mupen64plus-qt.png %{_mandir}/man6/mupen64plus-qt.6* %changelog * Tue Oct 06 2015 Dan Hasting - initial spec file mupen64plus-qt-1.15/dist/redhat/rpm-build.sh000077500000000000000000000003331413256700400207310ustar00rootroot00000000000000#!/bin/bash cd `dirname $0` VERSION=`cat ../../VERSION` sed "s/@@VERSION@@/$VERSION/g" mupen64plus-qt.spec.in > mupen64plus-qt.spec rpmdev-setuptree spectool -g -R mupen64plus-qt.spec rpmbuild -ba mupen64plus-qt.spec mupen64plus-qt-1.15/dist/windows/000077500000000000000000000000001413256700400167235ustar00rootroot00000000000000mupen64plus-qt-1.15/dist/windows/icon.rc000066400000000000000000000000551413256700400202010ustar00rootroot00000000000000IDI_ICON1 ICON DISCARDABLE "mupen64plus.ico" mupen64plus-qt-1.15/dist/windows/mupen64plus.ico000066400000000000000000002020621413256700400216230ustar00rootroot00000000000000 (   X^$jr cccfffffffffffffffffffffffffffffffff]]] pxxx z sssQQQ w nnn|||xxx.>-~;+|9*z6)w4'u2&r0%o-$l+#j(KOZZZ~ uuuxxx"~5!{3y0u-s+p(m&k#h!e=~DZZZyyy66hhKK11xxx$:#8"~5!{3y0u-s+p(m&k#@HZZZ11hhKK++xxx'?&<$:#8"~5!{3y0u-s+p(BLZZZ^++hhKK''rxxx*D)B'?&<$:#8"~5!{3y0u-DPZZZ''hhKK!!#xxx-I+G*D)B'?&<$:#8"~5!{3GUZZZGvvv""hhKKYxxx/N.L-I+G*D)B'?&<$:#8IYZZZK킂hhKKZxxx2T1Q/N.L-I+G*D)B'?&j=hj=hj=hj=hj=hj=hj=hj=hj=hj=hj=h00''' K킂hhKKZO^\[ZXWVTSQXސ&777++eee[K킂hhKKZ0iiia_^\[ZXWVTqMMM <sss%%iK킂hhKKZ xxxaaa_^\[ZXWtZZZ {<sssiK킂hhKKZ 33{xxx~xxxaaaaa_^\[ZuZZZzzzzlllo33<sssiK킂hhKKZZxxxaaaaaaa_^\xZZZK<sssiK킂hhKKZZxxxaaaaaaaaa_zZZZK<sssiK킂hhKKZZxxxaaaaaaaaaa}ZZZK<sssiK킂hhKKZZxxxaaaaaaaaaa}ZZZK<sssiK킂hhKKZZxxxaaaaaaaaaa}ZZZK<sssiK킂hhKKZZxxxaaaaaaaaaa}ZZZK<sssiK킂hhKKZZxxxaaaaaaaaaa}ZZZK<sss{K킂hhKKZ oxxxaaaaaaaaaa}ZZZK<sssK킂hhKKZ~xxxZZZK<sssg킂hhKKoTTT444K<sss99hhKKDDO ]K<sss>>rr %%%hhKK mmIIhjkmnDD| eZYWVVvK<sssll hhKKii<<K<sssnnhhKKdd<<K<ssshhKKqq((<<K<ssshhKKzz<<K<sss hhKK..<<K<sss hhKK<<K<sss$$ hhKK||33<<K<ssshhKK <<K<sss))hhKKvv::<<K<ssshhKK <<K<sss..||hhKKoo@@<<K<ssshhKK<<K<sss44uuhhKKhhGG<<K<sss hhKK<<K<sssoohhKKbb<<K<sss hhKK&&<<K<sssii,,hhKK!!\\<<K<sss||66hhKK%%>>K<sss~~!!!77 hhKK((---mm**K<sss 66677 hhKK,,)))))) K<sss99 hhKK00((( !!K<sssEEE == 33''' 999K<sss,,,&&& @@ 88''' &&&+++%%K<sssVVV555///***$$$ EE##<<((($$$)))///555KKKK<sssDDD>>>999333---'''"""!!!JJ  **@@'''!!!'''---222888>>>DDD**K<sssgggMMMHHHBBB<<<666111+++%%% !!!OO''00EE(((%%%+++000666<<>>888222,,,'''!!!%%%***!!!&&&,,,222888===CCCIIINNNTTTZZZ___eeetttK<sss%%uuuoooiiiccc^^^XXXRRRMMMGGGAAA;;;666000***PPP^^^***000555;;;AAAGGGLLLRRRXXX]]]ccciiinnnttt66K<sss~~~xxxrrrmmmgggaaa\\\VVVPPPJJJEEE???999444mm~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~vv 333999???DDDJJJPPPVVV[[[aaaggglllrrrxxx}}}K<sss))|||vvvpppkkkeee___YYYTTTNNNHHHCCChhh55\\nnnlllllllllllllllllllllllllllllllllmmmcc$$uuuBBBHHHNNNSSSYYY___dddjjjpppvvv{{{;;K<ssszzztttnnnhhhccc]]]WWWRRRLLLUU^^^[[[[[[[[[[[[[[[[[[[[[]]]__MMMQQQWWW]]]bbbhhhnnnsssyyyK<sss..}}}wwwrrrlllfffaaa[[[..SSOOOJJJJJJJJJMMM\\[[[```ffflllqqqwww}}}AAK<sss{{{uuupppjjjdddRREEE]]eeejjjooouuu{{{K<sss55yyysss))ffXXddsssssyyy~~~HHK<sss|||ssbbOO~~~K<sss;;##yyjjKKNNK<sss~~hhjjKKVVK<sssAAjjKK UUK<sss  nnjjKK\\ K<sss  !!!&&& jjKK)))!!! K<sss $$$***000uujjKKcc000***%%%}}}K<sss"""(((---333999??? jjKK???999444...((("""{{{K<sss &&&+++111777<<>>DDDIIIOOOUUUZZZ```jjKKppaaa[[[UUUOOOJJJDDD>>>999333---'''"""pppK<홙ppp%%%+++000666<<>>vvv^^^iiiuuutttttttttttttttttttttttttttttttttttttttuuu___RPPPgggcccccccccccccccccccccccccccdddkkkJJJLLLRRRXXX]]]ccciiinnntttzzzjjKK||zzzuuuoooiiiccc^^^XXXRRRMMMHHHaaadddccccccccccccccccccccccccccceeeYYYZKIIIXXXRRRRRRRRRRRRRRRSSSccch:GGGUUU[[[aaaggglllrrrxxx}}}jjKK~~~xxxrrrmmmgggaaa\\\VVVXXXR]\\\UUURRRRRRRRRRRRRRRVVVRRRRGDDDJJJAAACCC]]]bddddddjjjpppvvv{{{jjKK|||vvvpppkkkeeeaaaVUUUEEEAAAGGGKKKN?===XXXZ5@@@nnnsssyyyjjKKzzztttnnnRRRLPNNNGGGH:888PPPR|||}}}jjKK}}}yyyIIII???A3K/:::bbLLLFD</G=6*444EEE?%...>>>9 )))8883$$$222. ---(mmm~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~uuu$8\\\nnnlllllllllllllllllllllllllllllllllmmmcccHXUUU^^^[[[[[[[[[[[[[[[[[[[[[]]]___bPPPPOOOJJJJJJJJJMMMXXX\KIIIEEERRRUDBBBKKKN?===FFFG8A)Z,~~>>>>>>>>?>>>>|>|>|~||||||||?>???mupen64plus-qt-1.15/mupen64plus-qt.pro000066400000000000000000000046721413256700400176450ustar00rootroot00000000000000QT += core network xml sql lessThan(QT_MAJOR_VERSION, 5) { QT += gui } else { QT += widgets } macx { TARGET = Mupen64Plus-Qt } else { TARGET = mupen64plus-qt } TEMPLATE = app macx:ICON = dist/macosx/mupen64plus.icns win32:RC_FILE = dist/windows/icon.rc SOURCES += src/main.cpp \ src/common.cpp \ src/dialogs/controlinfodialog.cpp \ src/dialogs/keycodesdialog.cpp \ src/mainwindow.cpp \ src/dialogs/aboutdialog.cpp \ src/dialogs/configeditor.cpp \ src/dialogs/downloaddialog.cpp \ src/dialogs/gamesettingsdialog.cpp \ src/dialogs/logdialog.cpp \ src/dialogs/settingsdialog.cpp \ src/emulation/emulatorhandler.cpp \ src/roms/romcollection.cpp \ src/roms/thegamesdbscraper.cpp \ src/views/gridview.cpp \ src/views/listview.cpp \ src/views/tableview.cpp \ src/views/widgets/clickablewidget.cpp \ src/views/widgets/treewidgetitem.cpp HEADERS += src/global.h \ src/common.h \ src/dialogs/controlinfodialog.h \ src/dialogs/keycodesdialog.h \ src/mainwindow.h \ src/dialogs/aboutdialog.h \ src/dialogs/configeditor.h \ src/dialogs/downloaddialog.h \ src/dialogs/gamesettingsdialog.h \ src/dialogs/logdialog.h \ src/dialogs/settingsdialog.h \ src/emulation/emulatorhandler.h \ src/roms/romcollection.h \ src/roms/thegamesdbscraper.h \ src/views/gridview.h \ src/views/listview.h \ src/views/tableview.h \ src/views/widgets/clickablewidget.h \ src/views/widgets/treewidgetitem.h RESOURCES += resources/mupen64plusqt.qrc FORMS += src/dialogs/gamesettingsdialog.ui \ src/dialogs/settingsdialog.ui TRANSLATIONS += resources/locale/mupen64plus-qt_fr.ts \ resources/locale/mupen64plus-qt_ru.ts win32|macx|linux_quazip_static { CONFIG += staticlib DEFINES += QUAZIP_STATIC LIBS += -lz #Download quazip source and copy the quazip directory to project as quazip5 SOURCES += quazip5/*.cpp SOURCES += quazip5/*.c HEADERS += quazip5/*.h } else { lessThan(QT_MAJOR_VERSION, 5) { LIBS += -lquazip } else { # Debian distributions use a different library name for Qt5 quazip system("which dpkg > /dev/null 2>&1") { system("dpkg -l | grep libquazip-qt5-dev | grep ^ii > /dev/null") { LIBS += -lquazip-qt5 } else { LIBS += -lquazip5 } } else { LIBS += -lquazip5 } } } mupen64plus-qt-1.15/resources/000077500000000000000000000000001413256700400163005ustar00rootroot00000000000000mupen64plus-qt-1.15/resources/README.txt000066400000000000000000000005341413256700400200000ustar00rootroot00000000000000Github Repository: https://www.github.com/dh4/mupen64plus-qt Qt is licensed under the LGPL v2.1: https://www.gnu.org/licenses/lgpl-2.1.html Qt's source code can be viewed here: https://qt.gitorious.org/qt See the README at https://www.github.com/dh4/mupen64plus-qt for a detailed description of Mupen64Plus-Qt's features and usage. mupen64plus-qt-1.15/resources/demos/000077500000000000000000000000001413256700400174075ustar00rootroot00000000000000mupen64plus-qt-1.15/resources/demos/config-editor.jpg000066400000000000000000001641711413256700400226540ustar00rootroot00000000000000JFIF``C  !"$"$Cr"X !1"AQVa236RUq#4BST5Ersu$bdt%7&Cc3Rq!123AQa"bB ?. $W˞|*흟$-!meeMd~68wV]6}EG}7﯉}$L1)'e]lK}G}7﯉}$LX'g>ܦěpozoQ_{qIϲ)'g>ܦěpozoQ_{qxIϲ)#g>ܦěpoz#%3egSۋG}M6#%3fgW)3?rm;G}|I#g~ܧ)3?rm;G}|Ig~ܧ)3?rm;G}|Ig~ܯnS;f~6$ۇۛw|ۃ?OnS;f~6$ۇۛw|ۃ?OnS;f~6$ۇۛw|ۃ?OnS;f~6$ۇۛw|ۃ?OnS;f~6$ۇۛw|ۃ?OmS;f~6$ۇۛw|?OmS;v~6$ۇۛw|?OmS;v~6$ۇۛw|?OmS;v~6$ۇۛw|?OmS;v~6$ۇۛw|?OmS;f~6$ۇۛw|7OmS;f~6$ۇۛw|7OmS;f~6$ۇۛw|7OmS;f~6$ۇۛw|7OmS;f~6$ۇۛwo+?mS;v~=#yLؓnno)Gx#yLg~ܦěpw~M;|Eg~ܧo)3?rm;7o)3?rwlIwߣW^w{oxF߳3)&>ߣS~_{oxF߳3+7M6w7OmRf~6$ۇۛwo+?mRf~=#yJٙؓnno)Gx#yJٙ+fgSbM}Gx#+fgS7}M6w7}OmRV~6$ۇۛwo+?nRV~=<#yJYؓnno)Gx=<#JY+egSbM}Gx8m[\U]-Q{ 5hB"̾IG4& 2)&>K'AƹMۮSԃ!Ԩ8R0R'83]ZWJPx|KPi7_m?f%O߿q_sSep-ѐ2aqB$xQPBךO0wan3Q'8ꌊa{JJ ɐi;"3MsZRsǍym ذhuAkbDvh hq*HV;q mo5Fr+a㎡jJiGc*`Te6~ۏMr) !XCJ% $,9݊j/lnML%%ma>(ZJz+ bVSV]1EII%cVq\1KtH+<+jl}\/ ;5M,E pҊSL{܍%7E<Z 82G$5*Ap&AV%+R@;1S|Fve؉~{xVR2wË@H궡qP~?Dtl[X&sH@PRRӄq< 'IDRshHb<^OHEF8O|NJsKd "M P!HIQ^s=n mv=zs/9% ۉZiH$U݁.on5XLvJ'P8ϴo}RJǶc)j0JZI'sDYwˮnW ߀6.GS#JRٮO[,Y2Xr+?f/٭ꣶ&k._l_~bOLܨ9Gu!m8e!i<8+瓳4h_l_~bO] MW'gzhͦc_7lYU}/?T}~NAV|vwt+瓳4k1ܶ[G,*}tؿ' SWBy;;Gm:ޚ?i[-}_kr>{_l_~ի_<6 Mk->9]=/?TЯNN|vwmf5{hEWbO}ujW'gzhͧBy;;Gm6erϢ}~NAOkr>t+瓳4ӡ_<6Y|9gUؿ' S맵9]Zޚ?iЯNMƾorm_l_~bO] MW'gzhͦc_7lYU}/?T}~NAV|vwt+瓳4k1ܶ[G,*}tؿ' SWBy;;Gm:ޚ?i[-}_kr>{_l_~ի_<6 Mk->9]=/?TЯNN|vwmf5{hEWbO}ujW'gzhͧBy;;Gm6erϢ}~NAOkr>t+瓳4ӡ_<6Y|9gUؿ' S맵9]Zޚ?iЯNMƾorm_l_~bO] MW'gzhͦc_7lYU}/?T}~NAV|vwt+瓳4k1ܶ[G,*}tؿ' SWBy;;Gm:ޚ?i[-}_kr>{_l_~ի_<6 Mk->9]=/?TЯNN|vwmf5{hEWbO}ujW'gzhͧBy;;Gm6erϢ}~NAOkr>t+瓳4ӡ_<6Y|9gUؿ' S맵9]Zޚ?iЯNMƾorm_l_~bO] MW'gzhͦc_7lYU}/?T}~NAV|vwt+瓳4k1ܶ[G,*}tؿ' SWBy;;Gm:ޚ?i[-}_kr>{_l_~ի_<6 Mk->9]=/?TЯNN|vwmf5{hEWbO}ujW'gzhͧBy;;Gm6erϢ}~NAOkr>t+瓳4ӡ_<6Y|9gUؿ' S맵9]Zޚ?iЯNMƾorm_l_~bO] MW'gzhͦc_7lYU}/?T}~NAV|vwt+瓳4k1ܶ[G,*}tؿ' SWBy;;Gm:ޚ?i[-}_kr>{_l_~ի_<6o&S%D~$)tR@A‡>`MyOvNZf#b;o5f nZ=F8'6r[RHH!#9 =VN$KO94]\~aHa;s)مpeMyO%-GpZ[eZߜ"D7)IAL ҜZދzfrb0}K-  GW<-%PZJB 8#)ט䥁%.egxFu5hl.Lyf@+.x* pmCHR2\HN;A<:;xWH1刮RR'Rryd֭݋ qZu8%C3 qy֩-14ZRHH: vfoC5i t3C* ^c'#>h"Q)1ي*[ @ ψl-.+b ?VmpĉTpUedGF\1K)䖟R* \ҽsTqq_ nqe22p2RA'V0>?g"m9/B3Ʈ{O~9ޯW67kxn_?`SW?J+[}?5|}ymR\fo٪%k%\ٴx<{&Ju%Gt3ː gFd\) pAGblf= :U=Rc8Tȉ,6Ts$I?I5loVC/hnՙ[.%l:PԕjX:O#EkL~oKv<[ Oqp>j|5yKJRIIP#WY~Q,,0ƕIυIf\Bהz|J [4x:HfAy;lfNhv`B9O\Jm,7ZuԘ.TPHS:NpyY%Zdq;INǙ)xs5erڗ>ˑP[Bѧ*AӔ )Æ(0F oj%6xNI +X$p8y>rC~Uqv Wqnry ӥă{Iƴ6}%ҥcR(Q zjIb%Z i{sTi"6kcINJh4b_/FRM gJ19~F4vF2R*T :| Vۖb̋!Ԃ" RCZW 㫌c̀lȶaΗ ZpN{1WmHD(+;a+GVݮ vpS@vX.VfDOY'A3cu0vi.:\$gkKq(ň|=CN=]*ِq2m8s_,='}$!T q83##8ƂE- mx8q5JkFq h ǀzm MH`VV@2p4W SLĠAԥ%E,B+&C:R)H1+iu N$D9siG=DmBKȫO Р@99yp&Df#:@ZsfZp:GTrI8|jو#Ej;eE !(IW23stR/CjK)R-!N8#mn ^7M1z4˖8?d礮L/d);TnG ׵al [Ԡw g>*Q@ V:|*)y 4L/i(Rc98 j̪,L09+$I$$q֑~1gDKG< TQJ2Os g&D\ Yeg B3kܫTi.u:I)V4ԥ%I ?@ Kߔs9aIsn[eJy-%[H uyۤե =$J[ tQۚr#k34{[[i!e$ZJGܙ,[+BV<2;F9j}lmZ<^2 R#xr_v,yki;6W !ì4#Vlד򚄋sQԤRUøΤk@J^)ҽ#9I#^]T%1Do@r8<֕(1-5$Bm,$ yI88aj:,=\&,quw KmV2s0Insvm&" Q(q'H8qt o}nsW@~j+-RTKlڽfgKadρ_?%jn=V2'Z=(k3lTxЭ-)yթ8BUUuLG<U`Q1heikt->à# urbdwIu-VR gHSfonRYvd[zuSji' TmT=*o \+_.цJYNBI4UUbm8uy/eO ~pjV誙(*; $d)8gx .Ȱ"meFc q:R tGaWLڨkĢmU3}({YĖ#K (nBRNG4DsU3LO|9{)X)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@_WJPWqU1 Ga?*6R [,OnsC y5swW@pJPcP:{5C+Ka"h?~1_ ,[,$̓TѺ+!҇! H$]oc@;ed{MJ6NД'#WgmutXZ;{'|m5&Ŧvݦ x:\QqRc; R q5/eoJݟfE52DhGw\KZS(%#*8#}EJgꉾ1JovϜ : i~fcY_tZ*JOqG0kv Xo\.B0]C 7 BTUc;17qO&{mn~Zmڶa)l-@RN3ը]U_ 6Y&ȋ=wx@1!JA5jqG OiQM111"bb'=vv˴w}mI[ݻĻ2 M9.KY``qg+ژ!@//ژLx Rs~n5%*IJRHp56j1bJNjUOӺ$$9oܫ~! BB#WykۮjɍbU_fJRe)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)JR(W U/‡w0`|?PqU1 Gxj bx(wWA~jw??tWB~j+-RT9ߎm_T9ߎmd]ecijZl<Zfo`%:ҒQqu2 .nhBWQ栣݂1Vo2FOR62vZdqǙJ+:(yݥ4;$!.-фn(wcJrK0)JĀݟ5%,f.Gӈm©.Pe@ gԨv.`mpb7V N9ckJwzTH]ܡM6px{ $PYiUY7{!ECI@=$q[)9"{thp9Đ B ɠRaN[[B yrRjR*FJXwGm8ڝ{CiӞxA5JfP@+HWQzǸ=C%ʏp$0pu ixNR3e]f9F<JcP) qƬ=*Z%nhuzciNcGHRX%nRea͆r+9)9,o$+(aڝYBSˇ8կ ]Nދ|ʐɒtVsORQrZ-$6^VPR415sv[SJY<ǜqA%JֺLLJ J)J HI"6;L!,i2 Ai<$PyGHpoKji@s㻇vt-Bh% Je|PiRH9َ *6K3tS_sRuJAN:lOҫ.7hmeԷ{!SC {V.0G*a\`{;'iQeob:.- ” t\n5%%NJ@9%J=×koѓqǙy%Ǵ)9 9Hϛ+ (<}yj6e$!%LF[IuN8?Qw%h~讅mmh~讅m8V[t(1fs~ھ?fs~l'f3r˅A *)B`Z dD*RRIʹI$m øDl6*S]iu% G̣UEv :e,m՛c3!D*K:0Q q[ ѣt~QUCJh p~c&SG%JB%RNRxg#ާc1vvɶ)֢V]tԜ#7}˵A.El61:T6ۭ4oi 8F2sJDn옳. \f`A} T!/-mxIQӃU"b1.L`⒧[lC2rG9UGj.Fx;[Qݺo]MPr1GUͨG>5Ff~ژtvg7 F*ZR\֣!a-q A8 w+g<9rXjBV\y֐\SQ)-WXH$N+A=Fѷ:F:6mN*H9Ղ8OıOÉmc8TJI=enVF0wϨ|euWKstHt|6ᬋ;i5 JRYZTFH\1@'5էqLGLiJyO)PT`$cπgUP6)BF@}wf:=x7_i)KAF )ɭ{%S2ѻqs3d[;Q+E\^2$][n.0RCEHJBsV:qYjSSk)}4PiJ{PU;jJk@qpc#Qlݡ*^)N O sRM>Ji! $q謍h3{-ANwe\4A*4n,*P-1`v1TJQ%J'MgDo]?R3s)*<0 B:G:ڥcVdt)I%: gy8ַ}X:C6[‡P'Ĩ.B4f͡rkZHu+ z9P0@,ID)¤GLp ғysP`ZA{{ >c6- ƞN`3RЇiBRJˊ RÞGfGj%2VD%MFNR5pQߥ|bfOe -ސH OmG\-owOGj[lmIqE${GH=J@X!,됥RPֲu`N3?J6K*KD~N%#uVu- kiLg _-QKq g]S -7'tTpuc#Sr!-`iZ2<5O@YQ.XPқwru )Uh֒8ԋRV:^RJSǐ5QUZ#]nع,! ֭pP3$VFz,UgSL,+-'Yۑ΂~[7:yimBdf/!۔EˊIeѡJ4XWaADtiCl!,JjF8 8Q0x *s=ɪVL4L%SkR\rG{+zSR E2\[ .Q'HM@Oi!_u/.2@p`jCfFKH.92~!:OvpjĴFu(?#HfKj[ ֔dCqD REF$j!"1oy.uAbcԸgHu1wt-8$t~QZmx^8;はp4vZkSp`HVmN($dN<ڂ OabRtvQtUhZsO0'?EZ r[pr3;ڔ(AjI q84ˆ.LU2v5N糟~(.4 *Y#,%,s:+jqp(r"䄬@¸,^k)yN(/9XJqKn5kӮA[)Rs a CכARRr3A0 SRVqVZf 8Y!RMP ˚PRr2I)J)J)J)J)J)J)J)JR(W U/‡w0`|?PqU1 Gxj bx(˿OvxO\4Ο-🚧 n7wxWj;W鴲Wn YKmzQ\e%N, Iɞn4ݵ6_ȁ"Q9R_K+*l ~ NF*EL}l/ )lt'V_"uًnѱuk!3Qp,$*(e_kKbV-#2[RJ`uo"k%U .52BIZPABrT{j>GFLK썾f)Vx2b.0P`Zˆ)A&mWİ{=qv}DHvXr!V @(½sY1 ĭb)QtV%7dϲ{=黬Hv.,n\efdPBBy-ݱm^667ɲTr CͥÄTQS.xd)m.苫L Dc";EeaX1d׫Dž7Urٻ϶ܸ33qbErÊ #Jp[-ktb]8Z=$@/wIgJ[bZa孈oiq$TԒ!9ҥuFBno7{Njkfc"~웗G )o@6Q΄I@U7}sf|Ka5p\U GAdĖ*-/}~eXƺF&uG}Rq-z!(+GW 6ixYgl?:;v'-ZC0LG}#ڜu!#%D@Nji.bٳca(+m G3$ G\t}TTtnRxh6tgЙI'S,\>|P\2%Dȓ ;m7 Kҭ ^Cvv7I>轃nRwiqN&XkRAz6ǍONwHҺPR8UJikk7cg:5SlLFKa ûEj#Q;<ݵrTeĦ&ůUx/os~o%ltKq two.De8,ArINjJ3GǁiOݣ)- 6[IRdnνJu5bF-wwfy_$]\L&y4XC [߻Tg|-Ѭ3 U\ SJ r(Bܺzz rq8? ڍweUȵ=yD|F³jcx/YKr&&2d4x(tuGXpn|U%ұ*r^ n-p"Y]Oy6Z$=rJu™mYX/}9VX ZЭqʋ0e(HH+J2'?7;"ۥ)_B [xUSg8#H=;-Ga 0M%  R R R R R R R R R R PWt_A aSO~L?ci?(ջѿQ-🚹-🚧 n9j7Wz5C+Ka-jhyiMJJ Dg!Ǹϱg/(R ^Gvz٘{+.t(:{t qh12XlII'*I$~jR_Ѻ?M zc*\OT+KPl=%.%mY8'??eoNb%=l$fEO2IJ/${rAi{J#@b{ECҹ.}Jz<émM4SB$qXbkB& :wi ]cPi_KRZʃqaҤJAN8*95a8VdIi:jHJԌi' JPFøK>C-m1Y[ =Prs#1ESR 7Iq(x !]\p NG>2IiKku()  <#Y-Wg$IX~Q)++Z99'--LNYICcQ!k=GL3.J}JX@)JQ*QIӞ$đ@N7xB y'HbkR?Qw%M讉kM讉k8V[(1T9?m_${Ѫ_[Cgv^kzދ꒢^ SJ,8RA,yM$lMZJb֤ȄܩZ[o7$`n*B0SKu}kvt'm4XI%@ƽX$lhv{PD3 6å:J7ԫ|4n ;i.mi0` )xLOSpVlS$H۫NqA98'XWun2Bɋo0'Vƽ!\xm:I )}uw ؛\Y S @f3O Rx 9˲](p WY2Y,>&lٙ؍n!Ժ4ژ :)tFEc{` h=rڇLKBu<Dž7;u`-m6Q + C%Vڒ F+J*[ݰ]z0a saA;kCVDx!@JpV˱@qף6֖TRA$!]dq>\G#ƹ^x\7Qh"5&D)CtʳCO.gFvCѢ˒[mW'RGJR8Hg)N,DvM>?iǫڳݟeemeSiIRU6Ct9unն1|/JTYcl¸*j4NKPKJ)ZHRs'&VX"]"PEFTCn+Rq$p^c?ļaѳvU362)7^%bvȻH[spKOjCKJcKjla:x9=tnzem.C+Ju%i2HV8&rӳ?h U$s#RR98֮İZO;cijAQ.PǻߛL-]xQT=֟vU7TL^S{|2*dtwJ.=RߍlWP+)yN(/9XJq{)@)@b!qZzuhVH8wq·tV@IDW\I$ $gβC1*K4neTPnNN(,VҜc5JW.ih^T[9n?ADؕ:Ҳ RP@0Vz)J)J)X)\m!q *J)pDB?U{R>ia9Jxs+F'%˓ wq%Jӥi$F <)J+ٸ*tp$~JSr\iX1/Pt\

'(/6Fح7dۜd aVNT%H9c}JF/@uyf ⑜dviz'N7KR5:I3Udl){AMزq1-8\x zyw 26{j4ܖyMi;-k}(MGpd EIeh}#R䎀SBIO'8 p Jx+S*+ɂBOa!YqnxUၞ/;aR}܉ZeMHR)l{db.^06훈lvKt=)ODu!iZ(`(K+D= nj u'V༝$QmxESdYFnR7n[-r$tH%h׹iNi3N3A]nQԩ*KjK!ǹ_fAB e)#lφk}M1#+f@f% c,TZ_ь%i8V [ͅf( YK)֒BPu&f}Ђkxgfad+=SqQF } BQmV6jb1M7NC AodH kX_/+fٛRɅӕr]GB҂&9Za$d8;Rm6Uinyv~\qvYi>H 0 t[ 4 -.7 ? 8pl^.-zu$C.(ÕxxC~yV cH̥t΃#9Rx1TNoo{vb'yJa,`-37IKJB~/lp_ln)JZvT'<+Zԇ/}xmN`oQjqmeJރ'8lvb@ܸiHK-J\I=QgUoQ#:#F0~-^ Fmqn4'ԍpH/Èh: $ $< ҂uVYR]HKGI?ԻjքJ (#6]niLT)' OܩDGu{F* !ЧOS!Ǐڸoo<7AXNÏ*5]%SK䧀|cD%%G8<MV12#N8D e jp9Ւ&e &l'CrqJܰ!ɀ!K`:QE*>aRE:LIHKYv[-drJIr+ aJO7vJA#͚҂ޑ&ܘ1)7`8=ZwK{8|[G'o8%IP G{G ҁU+{2NF}0]7qdkp9ݶ9o4iLɏcv1RR{CHQឩ+rnWͽpAmQSF2yԭH[+0+v,ASZ $r$ miME*Tf?$HըÁZV]KKDvCn2(JRi"Mf# ;\ZsxU YSO[Oߊ4$)+Q!D')AVyHDJ}S$K#_% 38 „!?Qw%N;讋k`N讍k8V[(1T)=m_d{٪_[I/mX˕Igcy0.\mvz-۵GзKtIJ/\.ݭT]omF*;X.;XK@j:O]lЧZE23 ojҔajAuE 'y VF:H -ĒCR_[h‡o1ʷe߶~t-;&Sje&cj*R`O>L ST@* #sb$y1-  s)HTGٜf+{/G KJzxAKA$$ꩵ+E ClGjߵoF[E!ףϸꔵ4`k[oTlrUh4MmY.O7pzAL /gl w dĆu*p%$)JBq`ZEf"l씛]{Ҟ=#o#C.AK u}СwP8wmm:AH\ݳw !MI* 㳐<9ď[mوvȶ)V[O8cL%GPJT 5pٻV[N_"0ҀsJ'JQ#ſL>l:€SG O} kLdKiv; S%杔R!up<8\xT!lm.޷۶HK;hEQ8Vu# =g3ᑭElەmn % uw}NAN4o0yDq-HAuE3pvԽ8>۶:gهxI3z}O7!I1Bt88X;qhVS/b"E5nJd*9p1˺?t)_ICĔe+hle~ZobePLP:< o ~T%2NP8 u*A1v#?eD7jfߺVGHΝYg:19{2nې.!-KRR4$q^^nPa ڔxvryMbb]Vm)%yKk$=rv_i{f/۳^d݆UMĽ7CZӞڱ˾JczUwK.25eH* !@Se&Cm% +H?b`QS?t\3w9{d[hŇJa96cζKj JAIJus m|/NmT05c P $ J=tqqZ mC@ ݿ !dJ[*=U+NR@=_oEuaq%ԒT H^?fpꚩ=3~1=.g%Q7{5߭[uLvo]fFxF kZ[=Rpt:=F-wwƄ[[<ԕx9\Q[bywUZr%LMʕ$6NV{+hNEB-4(A*R+* Yc>]H[;$Լs5˗ѣ0FC/B4-I%Zx8 T"/t91%$Tm(kʂ9OS94)i \[wXV$e# ZWql6[ gIڊ{u1t8m',sbY`t|og9ۣiCʝw;S$2aRp0xsAaCF-IRUA! 8I `[V?)r&<4J17Q[Dm1AZ0 y,G 2e-| g p![ɎJTe,җ5)P v(vwnRof;gqx4d;9εZK1d8N:JRڴ %\"rU}-yPQќʶ_@iMa8##蠒E15:l^lu1B+Jw2N=t4y*)-W9}bHxSOeD 6&4# áAJӂ8(\G * u=LeQ'} nQď@9Լ%\tSM6m±~ ԭ 瘖8S"C[V)BtI+Pmk(xsxp󠚥C;6j#%ʔrdI'&_ƺG7ĽVtww2 8=S<#T ﳑl=%ػ,%( ڥYƤ*}JF> Œ,.2F RJN~XnV溗LXil@ԅc `ݬv3)ݺ ٖ›Mj%+^HKhVRIvL\  tl%JYmli#)R@u5[jbmH}-:%XӤr<ՖT5}d8Z @ê9`vg`5 !.)-#/VR0{,׈KZ,⳷63`H@ H4CTpA fW1xrƿa[DGfd*)Jwiyuuc,W/fB[PqH nrbEuj qIi\oK)ueMґY}ܞd`sw1ex9oOmf[\## xxۭa>ijLqv>nt'_FƬ$z- Udȍ ZV2qT pz)2俼9.,A@ U[[>ń+j6-YQC%Lj'(xg#R -WY˒AqLzHܾJPYnK wwy*O6kR]`})E3}iRgmJVPx 9?YnvK5„'vn ]-Dݾ -|֥I(䄀H5mwV7ºEM[He.>jSaQ Z xpNX_hOeTvdGdd^&mnؾ%ʖN<1a&-0a)!.uJW,۱cf!7gqJzRb(禍 $gחy{[_16踖Va6[m:%Dxog!P eI)(l9)'<>lT+;GFa>̕ Z*Zg<9uVf/ttzNN`szP*-#S&J(S:qT(#5"L-T3:ԕY㨞|~E1K/7*V#m)U2MJR-#ijc/͗)wv$H)H ط^TJTiNt5J K yTá@<+]6f2[uKά=01{6A +xĭT $g8ζdEmQ-K )))9 gK3%Gި-) U.DEr J<J XJEc?  ''JҐҔ0e>qGLYn:[-+o4sWъލ$J} YT!)$VzPj!"YiaXyKnF` qRWLmJUR19Gf*J$H"$ $*_ġq`20y?=H҃DZ ѻӞg[ iD*Le,Ҋt'X9䜞}1bT뫏0+{Zte@G @;ǕyUy.d6 o 2'#H.g&iA3<9I'<1OCaq .KBucjJ ;i~3%.F*Ƥ8$7ctfޓ,4o$zfRˑ)ڧt Nd%:Vxg Y cVVԔȓ}J*p+Wfx'kb,ƘH{C+S'I@Qÿjۥ]emPZ1>q#R.WV|(W U/‡w0`|?PqU1 Gxj bx(?Ӯi?+?5s_Ӯi?+?5N)J r= oxWj7ǿW{g!ŸxG0c-MZ2I$ @=ߊFbSL-mC)Qm28DdTf%mi 6㐢iRHrsr+{c@믭eoI )B{0qVo2FjN$hvVr/T-HД:J @+Ul[,HS oECE $a+Y Ԭ&6 z%#8 %\1Q Om*n"$,j=Pt$fV`C 4eB@@l%I $_-hTaB[iB=Trg"5*(KH qE9Z$s.!NJ>Ң.: KV2GX(Qc"ycp$A^I')8kI@+C[Mĕd@Ϝf.QC}hq=6B]Iէx/'IgvPz}lhRPVcG;vh3MrDؑ䥧PaA3I yZ;I{;њv U冗mې+r<,Im-1q ZZ}c!XFS:s8ibƞoL- mᲓ$,lYQkژ̭Hmkִ'HRW p':‹]'zI-FGRQԎ**<95pN[Զ#(8Txg6kRE[T?1iBX88Nn,f.JhDGH.Z5P \md6K1.m ̅1Sp*.usǟD[%2c*+eJѬj HᜎusR ju3",MpEGu%"7hDi1Ã8Z0u`=ly l͕hSsDiWK'++9s2eBJT2Bt$uI\ֶ4筷ŠmH0QNu}@MճȸX6RgS(yv{,- @|(vm·ď+C a g8 Ɯ{g4N< ÕD((2C!%]R7Kx9vqlfvoiݣً]lqԅ3* 8Y>g.kT{aO# rKjPpDiܤAL])L^pvs~,D&m^$WZ3LS͢߿ݖ͏7}Ӎ:y[^"CP1!Km$.c:T8 -z!IBTQ*ӎETj\iqj-pB8 ΰZSs1- TL8ƷȊnZiH( >=#TZӁpD^mÀ qô #H~mm RTr(qfFoeLuYJm[D#O,PK&t艔0[ʒ@!\ 9VU$V͂0m\DBRxI\qǎ8V_% Z2fͽG 7o99 rO[T PWt_A aSO~L?ci?(ջѿQ=W7<Ο-~🚧 r9j7ǿWz5B+Kan5> ctct4ikY,(U}Z- e- ݯƎR2CV?󊔊Osvɲ\FHrRԔ#ZVR玕qAqpK{~–b1앫ٳ)e1TʛB)Ns]vc؛."\R̗ʈ'AGz٨6z="ywÉB*KhР 5b- hK %h%$R+Q[^mٷA\6}mتUaЦ<5+0a|ȯM})RaCL,8[NJdp<(17S66"{;&{kemp< Ez*ە(;1yF(0F ѲmķbLxͷĐ&j{y΁A+I֣P+{[MݵTfdgb!-g I #Wf|-{il'˕*ˮ6hJAZX t=? Q5l_4Ţn4+k(1.;+ԕ#.% (URR.;2B5 FYiJ)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)JR(W U/‡w0`|?PqU1 Gxj bx(x_]" wOxOSeJRz5B+OLmk%\ٴx<{&Ju%Gt3ː gFd\) pAGblf= :U=Rc8Tȉ,6Ts$I?I5loVC/hvJ6!–HVRVx2qצo-VZ1*10gfD$2% :QV~z?aJCvըxRY|z,ŒRQ/:J.$É99gK?!SXm\R4)0sXelW*8) ?J}rljyV?rjwemjPO#Vee[x2==ΠFs5Kjl#}#v2}Cv#gg㊒MU(nujJηz(c.\bbZ|xʇ,G>(+BՔ; j rq/N% iԇaɒj=+rKku6aJl$?6GfdD6/iGwU#ƃxjJ^LT27Qp}:RqY< %kB~k mrcIR]l(f1KC- a(Bp|E76_ƽYˆ1n^]Dך0٘U;-( VYnvV':"K!%%$SF2yc=Tu[ܥ` F×i6kͺmHR@Q:Gp1ս\cmM(i8n+hХm ?\kB*4x[{8p09 = ImJ}m} Q R T}fsm4%eYHj^Đ>m啖UBTNI3ZٙDB2TX[ #I AJRf6O20ZTRJݎ`qWxԶa%/@\- LjF,de9^IPTvw׹S-m:ގYE:< s*EVVŮ"p%=Rr9Zе9iJaJ'$#yVv$j-.nTÎ%I0z@{0msq)b:R cƒ~99V\[HKMzP8ȹL%qHNtj;iuiiZP%G' [ۦS(+$W[Im[N+mi)RT20Ej´!=o4mR>!J 5Br@.9ӝwㅯ[!BP܎edGSI} h~Z PG59}N=ΐ1Àn {vw žҧeI>#Bƽp;f)/8mZ[{BR<6m 1Cѐ]J)X#lhǍ*[+vTJ) qv ,VP.-)K G\%-!6ݤ XePZ)HryϞtV`gjVxc9u%IWN:I7wxΝ\3l*mۦ )i)/dO0rxb5m S̔kp)ݕ$N5ZTޕ"T%im )IMmlEq棩JTt'*;FlI[ԅ(Vwj)F;հ(\n%TP%'8^,Hr|4odž1h?nݓh0T6 !]%* O0x[7/Gٗ!n~!HWwl6̦uJ4jRtöI[M--% RƜ1ی=\gblXqb!$6[tipy}S!j f<('sR@ C_}%K5H1PnniD. FBwQ=Q(,:#5qͥԖ)JZ jHҐ1Z ͹^KN3d` 1˄d6CP;O b;JЄ6K)CR``h[ :ڕ2[4i 4!j*$H'+<<`SeRكW̓Au~ יf!%-eD.u2ҧLē=-oa օ)4-ɋ%+BP\{NBSU v=zAM0܇e%ikޟ%qAn!JJRFI0{kb+\\<NKG|2m:pA F8D#f[ȑ^_kVru y;DYE"!=06YҀS?[Cny_ Ɍꔂn9j3񷿼W»j?L:;}1ZXdapV*Oʨ :VO&=b (BZVZp!5-CJH5n)cj|XZn$G JVP,IRBqhyw=^oCZJ]V3uMWUP7GAT6cev p-vM8էId h{pxP8{Og-XiZq ~f\sڋ^.oYHl]د]wcvll&4Z,YHZҝBSq51hfI3pb24棄(Ҕp#qΦ;llldU]]z2mdYÍ8A=bعYlXYNо>*JZ%Y. uA[fڝ68{9 $Y8"JQB9'c*No.Ve(.hHBu`qR39 g͹+&*OlvCΜűjX!jeAJNyzsfػ!Ҙm*m ;1)%@qeOo] XݢmRNU%D/O]Fsk]N q YiєHi& +,|ͪzn[aܡ8Ar<je*+aًgF[ Z aKMtJFq^SfoߞsoNxNvGy*SJu:R9V9[t V&d2-80Fc@5JRJV\␕ Ԯ>} 4T(i3uGmũI*Ayi$`vmڗ[KA-`#+v;oMc t ILԦ۩ԍC#AIS|XTPFA*VI)J)ZKaxWBI pA1ƃjrd}{C*(SNq=\b\סqRN?UeR"mzHB+|+˳њ WZC9B zRJuť@*RpɠJַ΍=9R1m-"5a!N2^KYR Tke/VPҊzQq#3*)J)J+i ,PCm\’pG@cQb%ip"N.!HO% @dפݠISd%[d*`qޥa)mYiI6(8PP)^p 8楄@~?]*C_PBl?˜T0>?}S#J<5n7K6# IҒQ* ش&;CnPX [97fK %XYvnvh!Y6RΨ0zI.Nv:LeicӒ;3A ͱNSԻ҂xVO<))-]Y\JC \ucSE*O"8` 5kNm&~i`*tG6Twݑ%TL00PNGmcSSf:[ iYӨ35Go.;vF)SBN VaGjzMAn5HL$!M+C|%Dp%S| urm-@J0%Y@R]\㭖ZJ1ȧ<1© ]?cb6ch] %xtNH^J;'̹@FR6umv3jTs%7+[0L+MB= 2T7(JSs$v֍"Ci:}7n 4JY{+1cÝG췄FoեǁTG<.@_] Y D{նKͺDWXqm4jSDd$\y _ !yRR"/A:Rs~\E[2 KdBD/)\e'y5Y=_|Io9Zc_.H66 eH poO.wmT4D_P5P9>pYsو˄}RȻE֋(Е9JR3[ /H7+-V͹3,^%[A.%Q)QʃDŒ*+ ZTҐ\X^WQѐT[#H+[Z8ۂ`:(!*9k~ylw;ΔDh G$Z{))'Zo]OH2VtN3X< [pm6el4)EGmȱ󎔄xmd9Wȱ6<[ L'*Y='eb؛{Omr&ZVP-HX 5$C|N~̼^fKd_H -ɍ\ F6p,(TUmǶ#n`(BJ0OVJPSIؔÔуj[/YSd hHP\RxMܷEۭF@Xz#@JHԭ(0\Z\|:VJBOq Pr@bQz+:CjN Gi^^j҂dIpViel)hݥ@XVvS%6Ȑ̕** )n',-)Q⡞(+W\7dDe ,;O*8 #4Eq g$845 i-uaE+Kw:T8GuIYqX]^ZCk STjS{ȷBn,y, ($'BCB!qڂP4Qʊ2 U۔ | NZҢP%)P8ˏ^#Az[L6┆ n6NĒ8sNlG!S*݀zz`eZ)ARq!Y[wRT)Sdˆ{9WaL0 >M z@>>M[$Ԙy:u NHH+R0(+3"MŇ>IR8!C 5,od2ʛK&9d i s$ (1BJV!$+%)Ay$۶bb68㈈ê)) JH $v&I0_Y=! =H#.:+>gGq) V(*qNDVYRVV98G<3^.H-|h6iQTr@䐐HI 㧎O:҂7gwR[ix>ţHcrpݩ&Vb) t_]Yg5dMJ] YRJ!e0Iƞج]tDbH\4%rB2#uxZPU![D̶é =PV'v5C_Uҩ~?&)ULm?0`|?TV8KF)BBΟm^🚹?/|]& piJPcFS?{~ھT)=md5v1[*d< j,6{y눉)rN4+JCo%)Vuo\ٴx<{&Ju%Gt3ː gFd\) pAGb*(e1{ejz{pǼ\z4riZt4YqD )#*Hէ<3%uoi!CٶnZgCva%mۡp1nYGoeFm919+U;A"$Hm5lՉ/@Q ]C9"l>[5:B`3%´ژJZ3p5p V7{vڿz ;'Ori[WeI맳u%u RP݄h焓_goES4& h St #I@1'iiNS6V5:m-#cMF(KR%K/)%FRW^!>i#cGI1X8Ӟ{qXJࡤHSe[쒦F9Un^vv52—,-BuRUnm .*FE: @i.Z D`c:yf1 Qԓ@Ucch2@j;ƔmjuZP5c9ß C^ __Xܚ1ܺ0K!4Bu$@pWoM`kgey,YJ}P|$-M0BR 8;*2+,>ԴJU$Ԣ^{3ÖN*BqnO 6 SkݸFGP0AV݃W+vl{"d硶jBҒˤ+BOm%+ 7;&*.IZZQR҅Jϕo\g:ĘK_J֔ JSpO5$rt+.m;E%)Zx͜s.goOTG-&p} `GTh> ,GnkSpvcF{yU#v&)82^!D q=㶂M[jBBq$tm['kvC{'Mqx޸8]) 8B呎 PvЭ^[Eͪjܕ1iZU )r>b+$:d@'*N8g8)dnr`%/7PH#Eu=xP{;,qn]uH"S,4хRGo y3TNvmntq՜nFs{1 {Vs^Kx$q#GƿJq v2DY¹1A&\e@AyD"ԖCn)GVN0pDFgCl{yP% RH8֏ 6%Co6TH^=ΐNq5~NtEږ 25$ OkVbg0vb7mm[VZ'[MBJRIp$hItNҳc**d)  89+Խ*?;E[XAH ) F0O[FZC428;[Vx ThعKi%S8- u 8y^K*,!9)ZR~rJ&twd`M})9 Ӹ.8s28A .\ a:Ovs+-.Bڂょ1n}{' N1ϊ~}T4Yw`FйqoP `q<+AMKK. YBR 'v(%iQid2z?Nv',ۣhe\ VK ώt*T5P:Tw IB:{xY%䰲uRq2RQv^pK%A#8J@|Ɇu ̬ 5*>!KZ\е%$8uH{rIu! NPLR>C'bDR@}ŽC8H@u7 nݟjn9oxC=PrGq#砒@۶aPt s)ю!''8z%4Xq#bIOrǟ: T"v ;qy1 R!AcR)E#I琯,m} FBc-I iD;9i W9'iQ{2$NtzD`Y.3fcpLҝ!NJHxxD*-=<㻰O[nlBV@ԐrW\H 2›.&9t=YHXP)ǹ=*mHn G@-- s)hTGٜfvg&$D5 keVٔGf<,~8"eEJPH)O<*5?SK&46*t2ˆc]#,$E8z >з[SXC!)Hssc6+JvUc:mCo)A`i9 cAgyhIt@)@)@)@)@_WJPWqU1 Ga?*6R [,O  :tWI{~jEtW'²ݥ)AOMqW>j7ǿmd3{ee GOnX0|-2"FK ))$\$OMTѴ ze9E|i{SnFBJJ5{cS.k%ufdz1وYf*R ڮ7ԫ|4nFޙRxC5m2$"KrdE}((3*I9 sdU:7zSW !F1ؒŠ^`b.[)6^"ȫVs9pyWxӏq0\qN2™#RI<97mTLfT^[)Y8r4 qSlݬm3[ |ɲ=|>[jv/G#Bը h.[C23%4.:JweGz>eoylR@r.髃R84Tz:hEhk'-fC0a)PxY9V c[-vwĥ\zXԴ! @$d8t.KQ*~ vݥD]\%A~qb0dpRJsn<l^lJim L͵z Z–ZIƀHegPmֈ8`OX}>"⊆Jzn^|jl˳lIQX :l(:Rɨܛwgm ;l`ih$eأ[ɔ- (Ɠ$pڣ޴ kj-ْr:trYCZS'xzI8=҅z00E3;Sʷ2/8ڑz9'ː[uM(Fp0BB1$+ZqաXⓂ9G-[o&<[u[HRy`jFiGI\T*N9dp,cH%/Ɛv:@[9V 1V(0[R TrHk)~c!e*F[I xe#AbuM2Ғ H%>8$(l%wH㠬~  )(lvt82MoRٙjpzLӭ8TS0ڂBx98N#Nm)qjmz:Ƒy(CkIqj?k]mqJcNX]$ 0PFγG!VJ_i Yp##lHg5˭BiÉ:Uxdv`j$c"GI u'@Zn\*BNR̩1ֽ;ԶSͲ8o# % ~V:V]Q ]eYp+Tg܈ۚm ?^G$ٕ&MѰNBR0<('ylU5=]30)'}=tOPݾ箺ϵv{QloJܯ2?I=\Mi<}]='7o9뮳E]+r[7uҷ)L>I=\Mi<}]='7o9뮳E]+r[7uҷ)L>I=\Mi<}]='7o9뮳E]+r[7uҷ)L>I=\Mi<}]='7o9뮳E]+r[7uҷ)L>I=\Mi<}]='7o9뮳E]+r[7uҷ)L>I=\Mi<}]='7o9뮳E]+r[7uҷ)L>I=\Mi<}]='7o9뮳E]+r[7uҷ)L>I=\kcڽMŖ\^m+W>&7|7ӫV;흄+pr+q6R^-?M%Ŋbm|#+`8^oݾS맍)E]?v-?MUJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^xN]]}ZHi"D߮AJ9^sH)C7צ*Е)kTA/f-Ԅz8oWZmPHm%)T`|?Wz*ЭtNAjxj bx(H^]t"|"{OvTYnҔ'ލP8z5C5Fjr kQ%JS $q^|Oh*W]6& >Jtz{GP~ΏUoRJtz{GP~ΏUoR>&u|>'|Sޥ:|MS:#4|Oh*꧉%A:=UJttGhTO>Jtzz5O=?gG'|V)j'W!{GP~ΏUJtz{GP~ΏUoR>&u|>'|Sޥ:|MS:#4|Oh*꧉%A:=UJttGhTO>Jtzz5O=?gG'|V)j'W!{GP~ΏUJtz{GP~ΏUoR>&u|>'|Sޥ:|MS:#4|Oh*꧉%A:=UJttGhTO>Jtzz5O=?gG'|V)j'W!{GP~ΏUJtz{GP~ΏU[CkiwS6=~:lMS:#*'|Sk)zSY%-Hq HQ>Є<4$INToW!{GP~ΏUJtz{GP~ΏUINxNttGFxTO>JtzOP?jwSP?jwS&u|'|VXѢ[NJ[@H'U S~h" [VA*PzY9@vbcnr#}%'6pX_~@ X/uzYZ@(- nyxFO1Շc3qnS}@ڱ ۢթNwn!E N{pgPom=e f \Z"L=( QI:FN3ԾŪyiԷ]#Rq-;Od]h #<+BR N'I=#/ml*e[\T~:-Dl5<[ KiPԢ<*{[[|6 j7-e╇hiu*Ԟ V%qy!yahιojU㢖^Z-!@@f؅:9SN2Hi9%+Np5#PnɟmeiM"[!/jRӤ/_5^9jy1{4t9n]> ܯH(:7D6diӽm+Ӝ#8';B?}UG6PHNv?}T~=EtODΟ-?ķiJPcFs{f}wlKar)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)@)Amۣ[G}֚JB=ZI__KJ"BU=ZI__KJ"BU=ZI__KJ"BU=ZI__KJ"BU=ZI__K`NF2 ȬMLfIN4$v nR`~qﳯ0?8k=(0x:t~2U( >-euo)}Ud >--<@KV[_PVO#E,][:T:Ud>(ibLL'|Z>)bMM|QOc.t_Q^鱾:Uc>)yRΝѫNѫV?cǑE,]\Ѿ:TѾ:Uc<<ť:7WW:7WW~ǑE=XӢuz5zjU>(|AKW:|_FU:|_FUX {GusT8FUX)uRޟjS?8FUXRDTO+ѫV/cAOcAKW|ajW"~q^^{o.'꧌"~q^^{oW|AKWajS08FUX}7=,]^?+ѫOUa66u{08FUh>n@gC_-+AuNRҿ%KMcw[im]4( * Z7꜎igj:v n:Tj/Ev4RlCʀ# yKIjZWä-+AUm}l\nj{2҅6;TwA*'Pjrwn7907!ЖqXmoL qRwʐV~Դ?RKX ZX%PC=^X3 Ę,#+0BdMo EԹH6VkN yoChґȱС-zc LH6QBw8;6*}{Vj[}Ijj7+ibKiKJpsq)Q1%$5kAFI9);tV,o*V'nđ'AZ4ߵo1|jm!}#*)$}($pi>Nv#:M:Nq'WIУ҆DFz $B +{Go8b㦏q'Wtژl <*çT+Go8d9 .?8T, T3{!tgHoXߓJpcW*?3i[7i$H[/O4 ?3d%οTݛO4s(.bl/U΄4 , *oV mT}`[Y^B:D%Z_'*[N1N9u`(REOKKD-5%~N?4SKM?i6!u"4֒đ'G OAJKsTkI~Ӵ$_zOiƎ"N3^is1B\RX, WڴO"OlG)&;gEGO?T-/Et{%%~N3CE(0"3<%UXt#:%4Oذ"vb)s1 sUB%T7=.Gu&9K~#u::$S yS1Gc+Y%i-.F8Ti'gGilБ'R ^3d%οQ%Nđ'^5㴷$_ԎkO *pq*ޑË:mst_ՕhJ6M)sUM%~NreIi(i+"g,6wGKADO1Hݣt$_шe \}cH~4,o+ƏıУ{2K'0ڴ~$_b+i( WZOt813:ONkL酧"NcʞBPGg.yUaZcLNLn#: Piao!+Zm:j 2ڑl-hRTRA r*! #Gꦐ43? \)Kwǀz(i!^njƝ?F&{?UK=]QX)M3jIB)ғ쇀n!U6hۄ4St@Hl=O>B8 hot^-$bD} f2vRZ$ݷ{+^e{٭0J԰J2Im}3Ӟ\nw3('huEܥF "|xm!JV#  tvLY% -sv2i!UHBZ[FB`Iғ4bgFiv&Ch GA 0HUCnG\>EOxT(HsGꛕVL#cU=s+᱙G4<O'ti4OLQ4UPrJ+چ-D*ŮͬDwl#99yV.M~PfJ.Z ,,€8 M[v]d˄oʛ@i*y-`q댩.]߸B[I~2'ސTWCx9f؇l|Z1-a9Z8tO'ttud%Nu%Lvإ+)I ߺ/)4UNc:#]XQ9Ҷ6 %SUv);.vSuZruB^Sdw Q*(P*$Jfѿ\?j{@qqLq- j4mFWP!!<ߔ|PRIN} M72sa7V@8I_SRo'XZɸBVQTMﱽu¡i2b[ʀ\[R\2J7d+p:b.[8Zв[ +(XXE<4~4t4OM'>QMOIL;#Y*<Op8>79ITݹN5╏ ZV#28Ѷ.\Y]d̉O7:\`K>%g;SZ>Zy-U F[ j[[ّٝ}w!iMaJR63{*vDMQݮʇ-\*J'oښFAIN|ί_ρxtΰ_ρ90mmֶEM$B]-QVx$$)g˃**㔾"JJP$'%$[?;+j~wNNY+|4NMA'؃l-S*K8r@UYZ.؛SwH5k6%w{lAZnrBUZKY/~>u:GY%Y:b| }ij .3%m1QcrR63C<85?t71.qnxmy ۘڐTI8)yETEyΪѾ){΁ F{*5<hb3D_[o3ݴJp)$`>I)z|?R$?*l& ̑ې$F){ߞ zp0IbIjC/_C_[8tI<qkd5%ׯڭHWz޿?JGT^j!q]ׯڭHRQvvToW{I{5zz޿.i.'~b'ׯڭHW{5zT]<o{I{5zz޿.i.'uR^z޿w^j!qKEKFwԗ^j!q]ׯڭHRQvpQ]u%ׯڭHW{5zT]<o{I{5zz޿.i.'b ԑz޿z#^j!qKEKF=ԙ{ҷ!qL% ]*.]N w9o㮕 __?j!~qKEKQkԟ_?j!q^{5z*.]N 04ԡ|^$þ~/_BJIQ2&9Ob~o_B0o[8Ң%ѥT&0/[9o ޿.i./e"Ԃb_G])z/ ni?GמhPo_BDAZV$/itI<pkɣU دr!~qKEOPDsD&%[8/_C⛤OI 4`R$?)A!qQvpC0 8ӭz)*(} EW!#s-q>H} QzN$?*KVPbuw^SNy+yCn>Җ̝?uL^Z{l.GVsguuSjw!q^ԯn>}8(/ΨK·,ZZ̼3CJ>գu{o?+8b6X 'Vo7Nr"*Dycq)-(N^.2cj]6ElsaCA[IZڗ $"e|E>bkCE /~ ۖҚt%IH(bcE4 Sc \Srq+VʆHGHEaalw^m. }%߷q9:P06VɰM3t{rpy$Qo<-m3y|,wZNG7f~mjp- UI:(:[W~&s5m߃.ϻGyw6#98ѻcm(\~V_S^\ #$EP%.X[Dam%#'luYKx!--;:!_%lkl r 4&مjǡ!bƧIf5Pz<J ս[\@LjRjJ%ܜr{ipwSiF]RA)2II dvP.lJp=ND!#}qe)@$u"mm$Q*ۨ㬾9IAl*}#p)@_~ԦjZu曻z5l>ium6 J Q A1!""TY]"{*m)AXH*d+6_fRh=LLV}|Hl-9 `+qO_y"MG`[#6! clVմ Rr 4[{}~U#Mop0Yoȏ-Z\mĞ*{ޯ5]i,_"e )k}2;S .cm` .ZӞƠ#[؛뎣fq9; so:4d v\ɨm)V!K2ߌ=߳=_ʿj+% V#76*[N!זBA#rpr8Q-+Vlb޶aK[ʵ4#J|8c'rJBR҃τ^w_wZT~i_c^kjɑ&RFD|X R'm 3i%OTA ĵ6.Gi}s5DGzSiͩm[B$/jQȭ'AXfYln3pQ2T_n)%^n  |-ޢ@oxVϕW[W~ ޯ5+}AwU-r[W~W頻RFMwz]wuKa\~V_o_h.껺ԮQ+}]ޯ4u]RjW(ߕW[W~ l5+oz|MWwT7o_kV_[ Jޯ5+}AwU-r[W~W頻RFMwz]wuKa\~V_o_h.껺ԮQ+}]ޯ4u]RjW(ߕW[W~ l5+oz|MWwT7o_kV_[ Jޯ5+}AwU-r[W~W頻RFMwz]wuKa\~V_o_h.껺ԮQ+}]ޯ4u]RjW(ߕW[W~ l5+oz|MWwT7o_jRLN6ܖyFwUzFەJЙ`_fMkw?F*,[#ZjhZj@SS@8cZiRR8\zXD<ٵAm{;L g=y{yr7BrxmZRrƹQ+4nF» 68hZ>(?f(QlnqM{e3h![R+hRd20? us~pS`r_N7(c9?K NhYך:P#FzR ǛCS}A.d9RY<KnOVOޥI\o$]%RPOOlH88#*av)84픅 ?d+cZRXb~uzP.NN~*' J@P';FOBT fkUj0_JY)J}m0R'sH5U= y)ڵ yg)= w8"{rrTofSP6@gҽTV%[a1ZXqR{ԩ$Pvc9YTLhvQ&nTFo" \Skq%09.]-1v}%JVd$ڔnq pO)OE&ɫ>8qLďhLFC2 OyӶ2t-$>orA|:F/Xa- ٺZ`DK&2vHsQIZs~Z6K"VkIc ӱvu*Qyh{꺼x}8U#/V1 ʛڶڕv9ZezPOVQJU I q?CW~CTvAnX^ԜgӺOZPעKFylUuFps#~S)8;!OWZX<5En 49 N({S |9~T_S~Qu$cPߌy=}:W Ev)'P}< EI. SKPsUBʝzďթo-dd%# qӕL '=S֗/ n%1[qI}uԻseA=‘w'9cQ *cĞzp:{ҝ)<ԏ?}!ԏHBJ=sv\Ba+ڰv:d zeN3N+\T!QT(( 8BJPT>JBrM7u=.eJi{Qm!5%k![p {W(kNG APJJTA8RyH DbU7'PvU#"sJN0YZZǞP qWrC[-v6'2 Fn}TTH;O? jr ="5M T'Le#!үR{7 WރN[nSA 188MH 89Gi.eNwGJ$D}ON!%>4_)Wz?QP08VVc=J-ҏD:N6@DrV׏wVQIsL ]8Tʵ۪T Tde6H cx ,OQ2MGvʟC{ZxN}y=<;N1@%G cfvʏDԷe!)'մU06|ywC=N?>ԑxe^i:IRWJq׻k0J7BHQ^{p30s&=(;*auri : ܑ%!?swGqdu2.~M(djmOu$Qs;O#C46уMHu=3#kw$R-jW!]m9"_8-GB wq `u S@p z~vqLq:қp{uUgk0K!i)pI\{lَ{$g~ ~J8i=?mEY;k\G􃭤m޿BWc)&S΅\#Edj idY"kҞA\RGt[Jsd6HjUrD)qWC\<6Jkw?F~7sjNU3'bZir; 54U̦ ~>ؒw|k+G(|97#^\N=E*hWy_!T'~f˦:D%cFwmyq|ǕI/! N @ ni 8L ڞ;2:eC062{ϝNTsOQL5rHH de%^W)#e@($8ᦢp2N9IySW9Q$FFRF:P $$888֋@27'T~:0Ay1)599nqhՠ'?%C($!=s4c8s9;Tۍ˹>E8sҔ;8*1^۩t *8~o9CO?ХIݒ<@$<7qz{)ƪ.-R<2| 8PV E!_m'P3AtN-rqOIO{CGQ>!E:R1:Td>\܎e[h0)H9U((P9'v; i*}IIYE(ciJ)>kT9*l ')\Yq$@si㎔H/vҙ>~J>zP@|X{;I7T,V0 ')d8DT =xpZUQ?WZNIϠ)BM$ qdA p|N 8[RpGNa# U N}B\P N'4@|= (9bS sqD uUGT$$V?Sx'K#=x灂:3Sn Ny_Z)|9*9P*H(( >]i88`Fhi YGHj'(,MˤMǝ;- rA(d@dGTk!O}8C)J>&a&Ahϖ@sQ*PyN t ӭ!qyRS*GREpʆH9R}xRIc8RwFpyۊJP_^GdQH>^r)흊$}Rrvj B@<#az4CʄsW g朞9$y2rcʹx UI?IIA>CkJXan#l!G){cFao~]_0PZ=R3uQX.]Rw8[hQ߳b6XY n2?v*K_͂"ck@pg?aH oT,_>5>Z?:||mτݞhr|=|m (l $t\;!(> =GTy38Rbu|4 mI>~T Ы>u9P8ӽBgl0nC;xҾ^MF>jJ >iZndp:Ze;mNIV Z ~(ݲ Z *\ $i֮w'PJh6!te]BH=9 ڦiە s1wx@t6> ^@36FW_R }um@Oq..4;Ş1Pqc8⻴ҝe4~'U(ߛr iݭuVq)Q0yۭbnIcզ\e%$V qGl2T~38;!&5!򍁹.õ,x -ŷDwjiȲ㨥6GI@\W$ܶR8d *@v$Yc.z&}Yϣ*?Q/Ia:bTys-)u.9+rqҚ6ޙ= 8szxsө򨿓@%$6$\|sGLBTāmE9֓U+h'}֯ߓtKy%CC2{ҟi)cwD2QNf6qvۂ9ܨV>kmq+ L/P%gzI45\5z; m<%pxy1JMǿגmHqiEJ*Y!]LDe,x/j)HSjJAbN03g*7VEyt5s mƿ[mFP/P?y|hˊ5U*J0Bg;ah$uv6-M֪*TZl0H꘎ p=ˏ6GNFRHL@L&5Mt/|'*GV+u5r0^Mx5nkϡ8VO%തɐ\u $d6< ͊4*$`s^@DE['k͈f1fFy2~_2|;,&u _f[oqRN#4wf"H˸hlx&N WPrTBy?;ȣ0#SAI(pxEZHN~|8P'>J9*  Nz))"[J> ֓آ"F6#;Y]v׭i݋geqѴoȠq˻*Skw ۍ5HMt_<g(ԯa5u^J+\V*C͢𱽠3Co*3©7NO+7y _2+JxhƈZprNr=Zܤ3bݬF@ytGDJڒ@*GPܔ 5Pq9S5dx*4Sn3oR򔶓ڈEXLmEٮckOdIp(gq9IC%EuhՈb1&*m-Ge '#8Y"<.ɬuYu#i9*wzzC;SKaxKG su_nM6eq2mͩЬ Q|S }D]#]ͯN;&.B}53w27'5zQ!$ gRbMbyC()O)X>}qӏ:ؿ.𐷭fu)(=<* %"[֯$Zteղ[qj 6ssӡL)Ɓ#G)>!){EXԺ}X *N䐵q9]e찪tL}Z[sjw+jPI>I>gAarnӈ*V6~w]iGZe+\ZBT<9N1A4R[ݐ HP ?jnȸvM>Uі_Kk `"N9=F` 6dM1 K^wqR{f2Oy;5ZDk4PۄdrTd`uNOB4 8c $chܟ>cuW?E@(5ݒZ;6iVDH^ xR۸gܜU՟, NVF 5.mEd@0WJ 4ϋ+1^3ƹ{1ik9𣍩ˡxru>v};:VcL~q[/*SIT`+( ?> 6ȹɹ{ Fh'Pcaֻ+^w+۔'8R?dp9sU M3vnm~{qm:%HB H#j{o '@Z.Q*-$8*BIYY3plI%(ke GR}j/80`>7!kqfxKE6Ёhcz?oǶ-Чl6qUp  [rT-IPe4kZfk:eW'ԮR=+R #=3*+]ް($F4;[-Xk1?e ( Acnjntj޴Svڤ vzg>-ʶ:ӷKR47:Zڜq{+'VE3R[ֶe&saS!HQ5@ m/> ۸7׹mb0o)ZR*p8ڤ7 qC"\Rv0}8QJ}ٔ;kn&d5$Uݬ$s"]ʒ4:Ǽ shjr[I݌?bR؃/$+cQ/:⏒RDrX8>yMKhn$W/}Pa.Qa{_3߯x-8t^~RzIՉÌ.7)ꗐҰ/>M7]((9NO^h <͡X;Ԑ1HOUV9SحqV{n2PFEma3kō;i@%>~'婹9dHR2;GSq3>k$uw#t#;Ke,d$2B"@ Hsxn>tOi L4(vFIhg@Vcr(`=O'C:BJs9EcSIH?7#nU2:|ii ci`TNI8RER qZWc<}\f =>; qh?0?@bw-4T.n"954*Qs{LβgE֮} hOyT4WHmpA:*6>i趑s`U$q﬜~70EZw>igleiA`|Cn)*Hp7Jw`?\]&h-@+5$Q9yU_֊?'? lpݕ$h"C:\>Ĵdn>>H7MtI}Os-V w/(+gVsfsja H&d%cA67XT4fOg+ڝʐg>qiWOyDGI\#ڶ[$4!Iy8m\gr:e0 vLJ'SEmMrqk!2 9\6nڰ6*xvC;[pQt+6֛jYscI z*.gٔkI[6@JzUnS~q5wN@:'>t4E6`oeZ2AJz/5BX2& ҦM7;Eq[ ètf6봇J!ۓ >1y.lE%;RTR-_O-5~nťmy77WJMEePB}0ʀH$30L ]#na(YmI ߼_HǞTa]ۼz L!ү 9JU3꺙9 6X [wWQquV8ޒF,9dU<&m7%GQez9-$DES 3V9p3wu6n0CqdKHJI,e?Tp). ˫ W !Iqi#@1bG򆨦E;թHQ HG/ 烟.(`{H6NON^.F`#wT언0\ulC^Q]jڣ̸RBق~?Z.{u7L-̼L)E)w9Cc<:qΗc:Ԑݪv2k]˔(f>MB#ev_J\CNf?40$'CNe7 VdzY^~@pn.Box%L *#NsoYch))gRm8pgs. wsͻrϒ]u(VvxN}0£{+m^I+G?"J& !Dv#(-׷VfqeJA#ǚ9Wo?KDi A@{AgZZіǂ 48yv/`<]u7,Ҁo*{Ol` @9+ l))λLo35FhBcde ;E9Ǟ*?YZ㻥݄+ ݀ }4ꄄ6#u3 V8JA *94Qb]e`27l; d:uK%wV<[M5 !wK.c`yu3o{dT9<r@ :`'g#$YGгB$..E`cնǢZvreiJYPP8$vu딴&#yl{ "ikZAceGP]b2;ⷴO(Ԑd/R@H'׭b$ҏ[6IڭF輔F _qm5n3TEGH;%ChW<W)*JܣuUQO5D%u2= EђnCoC@ >}Z5RvPV u5󜫭BNT ϯJ՗Jm[Í(/x2cĞ-#{-qYQ% ]VdBҭF3xG:s٦ ͐"!Җx(NR6PLd6{s)ԩs'G=~2^;O_z%;6t{W3]5*,7;%%@G-1fh=@S=[RE|js&ECE.lANӜ(ʪM\ JIK$yfVe;G%)$?t1XesW'UIçX*I $!JHV2py_O*_X wdJ@k.s-7'gA%`(3҄IB%XN8]ܨI% >wJp01VjgIF<ꁸ.`GJJH3e8c$֋طμyׇk7A!Dg#o>dsZWc6뎛Q@bw-0T>ot$950* U:Y@];l۬F/)Rhũ!$.xsKMp[WSR `@4cgԚ$$$P|ߢ'g:K{px)G:dc"_vV:W%ve@3R*20qQg h tzӔkvR\UZ8N9~u‚T ˭Y\*j=Oimsly}}iSmD$%Y${`UO\hjSQ.LHf[ۜ>R#`@XFL8E,EǍ%a)J]GTg?5#nCaK=I($3M,-|!lEU&N}3#k$- #$<{Rt>;ٹڛok$0Bpw}!wjwIIP)f3N6TNǸЖɱ,i})@ⳍQ]CMgn;Q~ i? EB8)J^҇'+qw =U5𕌭I8g.<͙{>x}$@3 :pY \sLn\xN:Fs[ 2:'mCZ9dIN6X0 ]rv'3Hԛf HRvXE $}RPNjsUl_~V8HO?Jxod6aJrZ6oYX[j:" )q xua=+vnj cwӊIB~hԺ,1ZPص\s}ƷOjqݜIv2BKrwiK{:uHi" ߻4 N[r;zU-PՒk$`c*@oRs?P ۷!>Y风9._M]anR_uMnV8ڒz\t٤]?TM?}u*Cˇ%)$ }ՏKum,[Jw1Usp]<2RW|$ޠqG \,o|܁M!}H ;*@6 #>k-'GG'}N4Tqc=Ghۭ00_. 6h.'+ ֶ-^eb$faIKl6\) ޭ;2:sZѸ2ĜϕfqLt;^\RS?Po|-n;\^*Srd6v>yW\]aiv~jX;aؐ *PIIJr2?5hSi %U->O]N9V0<`Gi]-zjZ LNRF1*xv'3 y .<6E qTz~u 滃LIam!БAx+P>ur&#*/"@RY4\ ?9kĐfɐ[/*$:x#T!0CkN ]&]_S#rhcȌy^Ǭ4tܵ% |-R; m%,j5Bv~/ḹj=G[w~=B|8B+Ixy'-]˸ 4= _+ ٗgQoQlG,mHi=:V:V!GӃξg BOw2ޑ $QBzU |~/db{hA^31@%dc#j:GRRcBw)^xе7n7i, M;DwOOҊ dy;*I'9]f,[U\A;[[$|ܑ|[4^:t eD$TdN%pqz }lKe0ť*9Y$-M~kѹwC_c2u\=AүsZ![96YRB@OAVF\~BȊڦ]F~cP-$uX.4lڗPN*3m.$bY+XV3Ga1֧+q**zap$r]$&5xG>ĭ%GI( I>]zZwYh5i`aRUC(HG.;?{U+Q,V#c I)Tڒqp8-mh R*"!7G%eVS֊.D`k=-hƙ,W{Ff+  )C8$=F+CK12DԀ[Z[O' BRrO5}ͣP1kiH \]jdlw!'0ݮH|NqͲ2/{f>!VDۦ~pCIQ}JK$)$JT jMݴiެZթo66;O!XӶ״-'gr:*8Fս܉2RF )[|'8WLuubd騥 b-߬n7x-qGi4K{T%Jzt5-\:1~Y=*61Wa^I*gEF曫1nJХe!\dJ^f+ pwX*eS&*dy)qĔ29<4vBBB Z@PAQzVNVTͺ;V%I+p!k9\>U oYJDV·Dy$~m[Esm\CN3u>PmeJS@x0N}ďYg!9v5$u8#9TV9>\Ch\e;""4m$rȣHʂOzP/mSK*$36BюTr$ n8 P'8ϸ qwsL% ʴnžm?g C8'OuFZ0?Y L^tUU{Y#]D]piz|i4Xg(L!_z5&X:TVIT _OAՊ*yC9 Sߢ/GکTYNq^o@@{*820)P$m:Ȑ1=JF 9j+Fwdu)qiPKpq4AJVE_j<ag (*Z*Ho a9=|ԭІ\*[J ªم]_[P,Qyn(IRJTsҨyaYpx]4⭶:џqzꝪ#ùP!%M$Iqx"2=2Jc))%D9P? U7<N-%!EkN}ee$s'㚌mƑt^ƭkxPUO8ԨlD܇Am#Y&kcυ0xqdJzCyTUwQ_#qs{Pߴ}ξERze+!W5Cn(-8?48sF:Ha5207rw#+#xx"-M%םRG=UIXoGir%ҵEJzA1VwWi755k+Sc³[8l%c#eq{pYuqURx.<((,Ri)@?.0fZZk) BN҅eIV=P)u.JwϝR֤"?vۈ[}!nSKiHu5.esۓw=r,KuE`g ִ#E;v3<)xy/NNT>螦Nμ:b %PXAZ#qCPJWD4 s F6c^'*#43uYT-=jaO>:V "#NinCaa~q RBB?i_a{?*6)kw嚺^eT,d j]j/cq]yk%#*'@5E8qq{Ď^vxD=Ee T*X"z&w^k6,7DBJFGKΰAYL.C ms.~־GpF"0Ň'b ARy$V4[5ۥGTGT:|:*X[^=@Qui}MV Uj>M-V#S5-';F ӊh Ae^/=&Jrܥ`p<{*bZf̏5(g!)鞤q_2{c"Ay) dpO_nɸ9JAqCSzV=pUȮL^X=­%ם ia=QǙ+(bռaߜ?\|R>:Nlj]elf>YhtRꚏ,=z$sBDUBA)INV@U=Qm>w3jOԓǴ1C7#rovp'ԊmCK}͆O hTMTggC֦‰9*#v\$gCW3'=hBVv-b,h)N p񻎂^Cd\"BO@u|O>*f5xx JIwZDϟRSxq]Nݙ*ǭ%=IssZ7c?+3?{<,@N~51QeYw-4U{Z~#VUiOMuiz|h~Qϳ?]D B5D$jMYKbFdP#㓑Ug5c}N)xpT*:䖵?ꘖ~aǠ)Ry {ȧen0ޟA+y1"A$x;R<) tJx2GA\ztVR>$RQ^3pN>T.Q‹*cqP3BbQ@t+>?ASQ*ɢRm[d+j\*)$ ,u}."\0(݅Ǘ|*SN_Ŗ+ڭ>}k8֗W4ꊐNw?E_C䌁๹FEYR{2Sq8wntg!yMJzG5e6aȩ[ΫpuG% yVmyV˧A]F7j)lѩL%1rJЮ=*YC|F?nכ'ۧ*tեJi +PNG+6~KCgšHJ\ (+j-\ssURA#Y|eo^(f_͈ݻKu`ӶLA]\x!eJpI=I+h>\qLxт;U[vp[J Wّ֒Fx;O#Pé$dt Pq=쵏u6JӓIKQM)-H'һKVkbD9ݜz+Gr2ҬqyhIz KOT~KNaJJQ'!*V=dÇ򮎫igCIZFkS%ڀ@R*=8Um^B 8H>y溺&˲% g #*b&ǀN*#Rg4K`_q*<qԷ+rg$K[6IyXA%xʔ*zo!Zt5j1JT‰i+QRO/<,LNM7ds 6$ۚJt1'>."\.)PHj+Xny*]}͵VH&Sc}⤔:A݃ҢRFܸr6EdF~S͹=$Y-ˌ91q[Ǔu5keIte$a8XQI$G\DB^B?"layi۟B)rvoPL2_->!S2^+ 2̇G8fUJ+Sޒ!u)Ĥ,QFdNXyIFzJ~? :GAzUuY-Gxw8:}B9Ccf`>MR[.w{glz:ͱAQܯ quJ 2)\u+8R>}7k\x*#T{9 ĒJ2 u G8]n+L ohu] #cuUWLc{ -PR!g#:zdUIWPx"3*<}{2]gf>] #!}14#OI`֍iX|sPk ?㛛1HT+uJ ڤZB$ +讥mÜ#B=*mPjK%M8F7Ei==3'i÷[Ӷh.ۆ{2Iv[ԟhiݚhqrGl2V VKVm7oȺZ R$T#b'[cGˇgKf(mDY+᚜Z>Mƹ__3eI&[=c13RW>?8WI_=apqgs: iidkK6$DRv % D+IGTmUn ;b:ɗ%HNVBOd1smo|U2̅Nvݘu٫/}l}BO 6Y3\13d1 +B$~rs =*oAt~f]dINl$8y#4]YMcȇ!Z^S>-4ϔHhM>(*7VVDhviq<$kgvx$tNJCX/sXA-% =@j+]Wڑ>,FiЄaI)$A=9dt#RI\6Iuy z+>bY#?[ C $IkΪ,X,8sM(|SȬT\CRQ$^ApJH[cG?+(Rd)i$9Y&d<VIެgV1Λo({ΨKNVTïwIwJ |sL&eˆ!mt+%.+$sVMR,0em)N!$0ܐ1TfZ5K:O4t)hNB3sHbr޻l"&Qkm{d:.RcM[YmSq-4QIQ-{Rhb fXh+s w)N!)Oh~tD'4[D]lFFZˉZN2p:Gjp<-eclugA<_+qMhgdq6le$$qy3Z X% q\.0ڻ;3`W  1 rAʾOl񴫞:t@ INO+)'IZpVIA>dRP;|X RGTAcZ7b?u:ێ=kFUYUܸfai⫺w _ך}r++?;]]g~QTCD$jMO[3Plv! 5&@Ť@O~IkS~R9c>>p%=<ҕ7?) <ފF~ F`s? &'>.:RYlI c|kBVB+:i IH!DTiIZB88NqƚlH*R3&Lݦwyv ӁFK-sSkqzWM?/&],!M!( m(OA) #Bs9SU"0.3.GsWxÂ%<\D7,Lg3pR͊:=uXQl𩓄JO%Tҥ?u|ac#.5lT QV0+Iq Z%dNˏf%2;Aq:Ӯ6P Bpq-i#όQmsLh* WgAE(:t!];~ݸiM\Z!G80}aL` Kޒ r%'6GfhۋyNhfz:uw,a1 ^~" w9%^{fvv]1R%3)б -V @I>%}9‫c cb*-O'~BwڗQ8#$1B?9IK@'tpx4KzvBSԩ'} Ǩװ ⠙5hBNR^i.)$w{1ӑ5Q;BPPJR@=j9z%¢ErtZ}. -vۈU=*Dqe!ǖ< +S-y6tf3?/WS# CvJ5ˊ"҂Gz?GxFrr3TDufXz1N*`GJ)7*Vʘڜ@A J|Ցnn1ˏn(PܡWEhLM4:lSj;R}q[TqG˵YB:(JHB]_Ҏ}T$gO=)> iQ6tz q !_ӡUBN R JYP9)}S>_&.x*2JJN@ VơD5t|ԆB~!_./,7j #r~46ę$!iܤm;[g_tF*={ 8c`xUXOyڽzC(i^.F3MjJl(݅(g>GmF ]~2|h_NU8m');@CWoxM3X6-K*#KP0e*t) ~@#}5T[H6$nA98#POV1Ҥm:,Î#䤑|O,Qq]5.%^7Ϗ~kooC70;w$Q=8V+Es{=T{PR%Hmpzp*/M۟J4ņ3X}MYtֻw8o)Ң\Z:GkU˶l CjXs<˰~(+b”QDGW۸X& rC> o׌zzV$Q9(<]ӛq L)/*JQuH[gj3E3(@kIY|T1vj0@mi`[PȪZ ~ 8>ǖ ?4}I~1lCIg9'99΄~NXozw f`XGnݗ5i$$fYh2ZBOhKb1.4=PK`$y?UikC,n{v;IPO Y\~jAmGt '̚}|%3y'?lՑE\'B6նMO4aȏ%%wKdZpH~V>J1y xZm39 w`SiGTG#:S?1a0~!:rQUbX'g3R0=9nSJ 0jLxW8)*L钓W0g*QA20I9;:C>2cf!Dz3Nrq9~R9'~t.$NMޓppOJѻ|Q;VϻwFΣh?0?Y M]ֿ4UUwZ#]<>.Z^_9߬4j;]D vLj' Rj^ѻWNgb 5&*v98I>~!@rO~HUa?*==BBH;E6ܬJ#7wgE#iDvG^k]O^Fw%kG1LexY A$䎹R0w`38⣎t߻9  !a2I(x%G)+VYFIkJ̅d:oJKh(I ҞVDX CJ@D]j',ֹ1P!9!*HKn9~4՛KmĜTp6GvH=oRqGՓRu11['# ATe9eYdw0~7XRxB]7zc {5CzM}=ݨ EF^l:Z \gR6[>LyLŏoeh kPdA)yʒPAqL9 5M{_)QKEiK^'ϙNJjm-m}P$ RӍQ?rJ^)%LAP̾]ﳕ;wcΘ^7cj+h~້SHRHuH eGÏNL ie&_Db`gsAE[H )' Zwtˠ`S*ڠ@Z1 :'Jtq^)).~vkJg?എG<)(JR8NNFx׏J1tĥ-qSY $dm> E cOΦSm }j>Dħ;9>T8S;Tdܔ ۞}zS*JT>ܧp RxDvӓ3źId۔Zӝx|hy⤮VX8'/ے܏jBTs΢dk̶dK0_2!qZBTNy9jE˵n-(bqjSDG$tӊr`M>[I>O+y{IQIHAgO )eXtz4I}{pO>Tlh*G#tJZqr2mv~U5 O'ΩIt?}ul.Z^XٝG?F3Xhg(D!ay{GRܞ!8<z?DV PI=~T*:䆵_ꝐP.R (%)8)R%OI!`a9dUt'?r<4@)>#Eڍ8qґA)$L)8x$)B61F+։)?ۚ!YK lWH8H'x QR)/e0:sNy#by)q=ND[r~g(I@sI70dUzѰ0rHϼ~: !~=# *)xqN@_7*OZB Nۻꥼp S Rz1L!')GAFNagjHQVʔRI=MH}91RRb AS,Nx^.|$gSj*ڴus׊ FAJik^R@#רA䎤bZʤI4bY╔Sda$㟍>Ғ9wn%X>) )%#Rkt8=?0iR;BH_Y Ǡ󦲱n1[@O qD 3* h)+9ǗU"JSxtR8xg3jψKF8 $isy./qY)^ @P2F(HHRPC~q?z)ʼ\W) 'vsҠuV zi#9#hRJT!g#!C*#p}zQSR-~IH'A+R %9$Vly$ZNq}: "6ZF>ePɣR?Jpx@?EGXH A)7/!=8}4ܲQO:e0{Jm9 ' (r=`ccI=9m9I<^'k<kVH'WHwZuWCH 8=iqBXcy'y9IN&(x;s%p|בg5 S!׏:*FrCUj=A'ű+°3revu .)I>Er U$)E*8)S%Dx ϥ*Jʔ;] +^pi.Vm#}94_DāHԢrAV~qmvR2c 'R%![Um;GصmdRd|&D8?z͓Jৣ(H$d:giiL*+QuVi_lˉ*Wt=7m~j1֝s4?L=SD,0:<8SG_*PNmŵ):0$:uݑK)֬R*KCו՜<<SjQAYu+w Xqi 4jt% *8[ 6.tWWH.):Xpw G:%Ly+Ngo֢SN2Up=>qM-uT7VkR6҇H8'=(&(1YGmg38*S t́@#nD*)l8rҌB%GpP@I t2kZ ee }*{f)óniTsnODŒ(PUQz)&ɫ\J9_k*b;)P;TъAwT]=;go1 aY#>58NN{D>` WyԽqzZl6V@D=g'B]iۀ1v)+p끓BڊuŚ \iG7A#'D70P(r>sc}4nS~7{9*(9 Oul7u/%{[y㌊i$H֥WJmiU|4;{J}gIFx&jT,G#‘{` /tޮ~'1Fxڡ'σB>Fw:c>Ի+dz̀GqLVܲ:r~eRHH8Y=N 9OӘWAg<%\度u0<قX쎀B@)!@ Rea\9)(j&*a@c5";Ӳ96vM rҥ0gߪDIaJϊ0KR R}<݇z(8Y-āʼjJ;uRFY6zqBv l#oc/ڤgToH޲cG4{ӈ&aޅY]@HExlhޓncǡ׷%r=@OwSG R{py5od¶ οMa?17`z+$d5i3V7\u0{0ĤNI[+ :Ձ\j!Gx.FiE"Byʩyo}UGs_SPdpJuDtGS8;oB~LH %w(*y> ۂFrZ aqzh 9W™w +NG)V|x>Mtw |f@j^2sϠ`Jc^$0y#RR܋ 5zVFkc,U dGUv;MO4U(qI?ἍA RQU0%I7$,/QU!B*q#J 6K+篳;]]g~QTC&( 5 J9>SGg4ͿYOE+ˎ+?Sߢ3~N y;N VEӴ|Nё#qZSc'ϧW(A4y}_U'#xURW2;GyF3)YskIVO0j2s®eҐ,nR%X)^a.{mfpHէ`S:[7x賰3k:o~L {].⇋ Ũ@`IRz8)0+ܾJ~jYJO#?j֏{Uߌ1-ɸZi=E&?fp!OFOop %J%Hۀq:Vns͛x[夎}.x㿹i'Y5l{Tr[*ktŖ 21@\ufL KbuDz|=1owU&sv8$$UJA3@St6D I)yY5^Ĩ%;Nt$>@lZz $tl->#FӔǟ*pf.-O|v64Iߑ>b!ů sKPhT\ kjMbCn4͐7TS0'=+S  8sgZh NǺ[fK3nz$SkA-\=nδMvM635N][jbdԀRIJO:^)_P7>h0j;WMWcΔ%%0R:{4>3-aMy3В$N!̶ϼfE)؈g.m~4̇ W$*:,-$}P.v[e nBp̕ rBZKޙ*d_8<ɩ˔)NVhrC.G=r 9ǑQZ8H:L9wZ3 <fc暆~o-.<㭣'vj\75PbPP$IeIVq$ jJʸ;=^pEJBR^U ;zj"vNCg'GC@]Tu4rT U+7D5[NyM[(ڴR9'C&C/-l  }J[}HpBNeZPǨrT;km!r(gӁlNLf1 pJBJIPݓ'ĦyJQD`RzdOJC#P]RJ%بJr{X q!G.cF'[lIg]Z DDw>Z Зfs@ci6J8'Szen;F;W!c/$u<ͪ;`\ ՟W#Vg%.kIWAǾQAMO=!#[;k`32=-\dW_b*&z*dQ[ilN: PO'֙yKdH RҤgΥ`'kO\W6S9ُ":\ ui6_%XvY #Ӟ,O[2˅kRJJVahWܚ$2q{ (Xy?8hk02gjB kܫ9֕GTxAvI1ۥ%V%%TzA?AnƸs`LfBIڎc}j?v`b6W=ZvvтEKmUsm?HV;Cː! k`g>kGdd1oLƌ}M, rI9<`f=e']Zu1q\t5-rA3һ*Yd{ e\1n#w)LIa Z~Ү co8|"1ǷUPXݴzyW!H,%m5ś{MGQ1R~Cwc4ADB3^;Br1F$* 1_eE@zh?jJ?)ܡ::u)HsdzsZ/bgy\lqg\u]hΣ(?0?@W]_0UkOEXW5ĭ?>95ٝG?F}d~]D vAC$kM1kB8IV|hɇH3ؚIlV)Dg$uTh?(_C[T°OST 4SI;Pd|+]H=xI S?k HN[Po櫌1J_H)$ }ɒ7'ۃ=N8욎%ʇpR'+>DU{{谩Yc#4NK6ElZn#r,"lIL|a VR}< TJ v3-ۮ`zU^EztJCϮ~PˈJ9|_EHd;MVgo"{6U`rwd`ԥdz [p*arZ;x*Bw6@Li.Ȑ]}@SH䎪?GZZL[}y)+} +%=5M1/Gy7NDӶgPtezDibM 63֙wmb q,Jj|%e#$>Z6"4{T3dc*qk9HtYCpVМHB  Cnȋ)n{:Y5qa%Kcy `rzѬh0&mW,wŒ0rH I>%W+ZA[0O'{`ՁϓY^)\T0a+>ѓT}a22Y263,l[y2+uM8Yʹ`89q3P.{$HB\ܩx9|=h=+zWp2ᵅW‡y72wܺaeÅVوjj4:RQ"4lJ3xSQS..TjRN }u+:zEb}גVujϕ^t׺r0\P)?YPGJ9[v>n6߈$gpBBM{)<+%K$ņ?_IGTXT/Hm Zw$b#sQXln-eN1G`(%Jg|hU5R?lTF,A9pF4Ojt9F[F>TdTXGPAnSxKIyFIʱJjSiN-nuVVc5:/a@{5\EnmwhtteY{=CjaMiIbLYb2dqՌ4Ek4m CndrA$`_,Ã!r/2ԭ¥|e]uL͓k%; 6\FyX:;{2ݞj$5Bvr~u#R㸴4RP0 >o7-MWD»d +sZzODP-)BPGMZI.U*\dc8u3kFmeq6nʶҕ5 a$?2IaW$][O LrG|^RT߉XI8>\yԱՍflw8R !D$uk0ӗ?X/O=> Е#!O - 8 I$dFA0]¼\NmK cn:7\~+K,5{].me)&Z=)ҠNON95or/Q}+H9궭}}da9>L/V)<`,HW;jǑ~3UQ# R˶#DhÅ;h,l|Um8 ~V8x#v܃+Թ” >8A] a:*8I}5F*_hH*w zt8k\7[3.l[ A*1mR=ˤ[ex@uW@+Ǭ▰┮OޒfkRC YӮ$d-$ tf(,.>='a ^ 0VCza(Sk|0ꓐہq@`_lqBb3ǺfJ(*^ ڤ· -BEeh0:DugyE m$!\}b2x5{}-ܧKT )OTۻw|a9 35nQ01h5ppnqKo qm'brGO?Jk΂녮ݦg-+ai;w)e_5InRH n.nWI ŹQTWs!Qm Յp6 Ir\UUmu%%-#jA5Eʘ̔C,R'<5gJi͝bDR‡BDv-{<{+!pGVgGP) {5="D{AYaߕ1Ԥj};".*,<ʜNZSeIMZ!qiÎ'C SCT' Hc2nyV<ӫ?pAQ $b' Z-,ְ7.7'R@+?2BJF{W95 -Xqm<қZ)>A\ԶƠ; 2:I%`ch9`=ܵRŕ[V)*CFIKxPy8 &Zm843=n!7YKXm,E4mle@  ;2碯RbC_/oږ̀V6 -=X)?UhvFD+ݶr`|SAӚY)isf "V)֧}SsINHH'?IQm#)SA8xE0U'ErfQ9eEI<#8,'G\J q.BS'4/䁅9y[!\bejmd-*JA{e*AFBGΣ:I>P6ă_[ET!MYy}6կ ڰ%IBU>5w d!jYmj8Ko(q|Fj;JA, 0m]/mA%kX<]4F9klH/ejb7+IjZطwD7>!}XtͧB[_*>! JrSԂHbL@,Z1B(Cj%D|M};gnpd䘍oOvN3TJAk]ڨ4񵅀ޫ"ѥqlGܾnR'}ZRz^uXNaMŀJC|'\Ap2J X!?#Ն"P8Vp2 N‡)?S=1ٔR)3"+F;=}4t.-h`'|SBաD; eH@$Wp>UڅU2!3C.2@JKH I*#hv3Ž[RBr;U.9o쫴+ ]*k$ qD@I ia#rt1Ccۼ zձ7Im H';#΃Zff -շ2yg$P=j۬$Esiǔ/:{Dv75sjKÌ0KC):yw]I4nNc-ŠVZVRNj=5w3߳ E8#jݮD(ν,ۻ%^J$4ḹN+IBĀcִ͌H[!l[oR{v `Z5פ%mqW80ͽ9F[ztkF[qCrBrFGgv~*b.cmD?ir+ڑ %K8l(pU:1['hW{yjOZ|] s|q98@U@St]k`/|Z8hvt RӻtW_ע;RJRJS6'kmMc‡|K0弖q݂@ PE} dK:R2r1\BBԢW\ZTp"O/qUuR(jr 3'Ya?0>up]\ul}ҚSVF)??)]_줟cw/xUi@bxidsFib!'[ 4;5Vw\=]Ȋ{ŹgH<j)*4myZ^z',tfF8N\oümS{m2*+nHIS4 8GjkHل[T͂JLg;a3uA⻍MLIf*@e8YP%x*G&ȕ6\oZy\ f6l=ԑn֫+|nGyV;̣B\iED%O)1nTǂ)P ?Eb:OP/LHxH3d)%>gDGwp/F1ߎI:Y999*@=*zH"#/K36. kHR ŠIRN3Msck-TB1?]/SS q+eev))RNJuc >-Tb` ([OUunHf !% -uҬyV 4X K FqUB̶}:"iQ[hQ# N2(k6܏eH6Dr. ('UɌІd{'juYϲ Resxǝ{J24j|+`R[s҉>}+ssIK_ #7' o8H>`er{:o(?0? ajz;iG&wr l"ݣ:]N}߬4j&xU 6&37x  bj&ف!)NjL*@d֞@HP9~Sd)P Xhl YHbH>| e Dr2=B9Ix8QH$$;8xyTj^`ύQk2܇ ҞQUU&3ʉխqc0O$<ƾ)[dMC. YI76AYxiV 9xh\٫enq8Y\#kk?=I%{ŔcƗRFGNj==z1t{)-!$L!eqP 8SYr٩$;^[TXeTtBكˇga;皞f=Mg#rJX8-@>Ǿn0ԦSmF>iUChPڻ0r~)m@'qh{k0glyLܤ8̷-dI'jDviZ4Prtu8g 8OåOqܕq[shE2HܧWRݗ50|S>/@oYOn-mš)X[Uy3X{ݛ+njp ӧ5knΘS̱Nt 8@)?OQ+'T9IJm]Qlsyǟ*6DunKQJmwdFyTL;䔠iº3VLv"AK8[v\[!ۋ%-$$ $KC4_,d7%JkHJs{Jʊ.~Q HBF%XjAJO\\ 9{%`gi=Úx) 1ԁ9OxH"{h }R3Y{?TwݷW][=Q!>ڗZ5cv /|c $O]r/kؑ{^#Ĭ~q*G3sr3xЁ@=zP.U"Ec>̙IGuBq- J8j"rfc )ҫIp,Ѓ9Wꞷ[W2xj&?wޥJˤ$$2k 4mc 㵞\o޴c{/A x_Rf-Mϑee*vcTJEY PVyW_I OB1j3*@ p͏mȅc/n 8瞕m)TFytM|#Tʈ55[RL;[L9[eeѐRiK9K bBI'Zrm1ҭ =,d2i xXچJ8hy RJFzdzqJ )Xܠ}k px+*U^Ғ\\wX*?k_By&) r\@NVyuvl{]Uױ숸NrN8Gr8ϟJ5vT N#;1G$^hz9b?q!IT:(ǯO:E3)(Q%/yyhXJLfCGY8qlM/\w-&q=ik񮶫U3%Ω>pӐ~p5;HM>J zRXt[[SM@_Ӓ.W%Yw -x=v 21%`ۍ/v;>h){oncobP_>X~4zι,-|ԫ<3Lh<* Q搴KIJR>x5ej;KVޤO_.}MV́(A%aJV8#$qK\ci= o{i(!(xOj*uYNяUk+ppzղ9;g֧ Gp;"+|q^=Nx#jF |Mki;*4 jQ! p7R@I1tN p9QO>t,Z`ܛo<;Yr\+֡7ͽ`#єoY_0UkOEXWu?>9ٝG?F3Xhg(L!aM /AEIFTwa?H3ؚ+$'Xw)tP&t@(;Z 8^)Q9 Ȥ8UvcDBvAOdWVq$(FzJ0xv&sP[ҢڒR:sKdsZ% 6$-')ή]=NȉYm\ Țl9F.2~&V~e|#&Sr^ܭsr|@ue]sVycIz2M',{nY`CjSJ8:Q|Mwy @Ilb }BipFl o"ѕ+Åp=:l7-:!_v:OA]r_cveHY8'<^R5ww_ì{Rd QKW+*[ g*W5+nBq%s-RV-ZsЏXƭҧp:ޮ6ݣ2qq-wKmQ8 lP]δ{OĚ#< (%jI PzTAN,`mK~^'+h={;ij;r!n#  Q n&Yz3iR5G SRF?7 ']&v]XZ}+?Uhk 9p{҄'`c*Q~Bt8Z^mrsﲲZ*T4Y6<.[#5>Գf;oLvݜ9qq4>qNC{m,6e@rpx=sT>sˏ:r1>6lHGWҴȨ>$4S8s?%b0.q߂YH\wHJVJ<>֪J0~Ma PZnQN2QpeA'PP8VA*cՓe,*~WY ^2U*l(]l唆rpy j4-jF mMy/z\A/f183r4>Z)Jpdł[~ ㊏TW!N%D()yucId8Fs+>i}bbnnOH3Rzt}d)*v3t2qĐGJ2d}N*:0hOkN2PqǘcRGQ G*m;G̋T8^ĸ4c#IyV 6_"5VY&rܗ!#Zm3Pi+]C8Jܧ`A GiPS2IM<̶B/Fݤc?4ԟfO9hstc!ZE[ADnफ8Lņ7DK~/m2bНE)OY? ᭩j@.]h6# {2}~ǻn7#Ri%/um6@ BUDa,v'hz=W~ǽQo޴ޅ›[8 @sj7=jթ ! !IU>\l,MhǁqiIP!`*+#=3PPf3EԔ)]8QHO\̴SUN.駦kNYLr_6}SvsØL9*cZmJs=@A1^Qqh|Ӷ1%Z{NnWqx9UXCo'Etxu4Uigȴ?pku44]='#RntC]CI+BH)?s{!7h\~YA|Ke8‚zU-v2g#L|і]9zD*QX-g>z5O,pwݒ Ƚ7|!qG>#Iθ?|rKD섬8C`SǙR;4ܭ2f\Aۊe }œ\HH 9k ~qlIA\Hk94G53مՕ]d%M1uAX3xrFo[jB<`'t>:d4jPv$D7 x}&:jsyZOVӈd)=s>AYm܊"0>ΐ(hI[K)#n@H=3] 7݄xܡ:c_f&>EL㯧AG2o9""Bp.IE[W>F*FX%c :QVf5m9thU8;2쟁Z`}qTW(OL!Q3 VFOqQ_TzM-s5ssT#5wrrOƳI5n*y;:q4-ZeJB ʿEvTͤf%Gճ |X] \ ל Zޝd%H)A 8`<ɭ'mjX͎j +.8 ܌ X]3tPmPnq HX'jTDĒuߪız, ,7o:R}jƎ}ͰJzGUnV}AvwͲ`7u-NntG >x581 X '?Z6|\*j*3M ,Q1opVH;O_w&Yb0%$ӂQ=+a3jdlwÃWwd7MECm  MZwP\]ֽp7BFԄLLyChOiۄ豖! .}IJ>5;.EN+Ţ[fV%SrpGǝ=[¶a(7)*ǠAE} >Gzl#R)D`uJEN/'86! ݽM@ۚ;q=:.]?58$6:P6c@+#TK4$(gv9b]ֿ4WO Wfw a}WQ3¨݅?/A 8=y$mMWa&!%c.3Vw)4ܒ֧}WgIGW9JH!C$:pxq65ʕٍ$3By5di'y$gΖO=28iN68$]B#I!amj$%@)? Cj \, )j̀Jsw{pFZrHh:z5O!2 s [sQIE-$Ki 618sUUcXBN/']EcR_BTPG ~s**ǵ4p/|hmyZwf}۴k}Cm:]t-TRr֔PqLj[tEj nHAh%(e!yIJg$5uCj ÷M7fuo.'bPF)Wy,>LtrӲ]ti؞}.2 H#0_]K5U=?D/rJvw)'^[ϢB*mS!Kt%ߩJǃ탩CbäkƛH-ǏyGUsOQEx衻2ilK[y-%,H*PcՃˋXU1{RYKꕌ) \PnkjVԽ[HwjarǾh61i3[p^p%7?I=7z\<Ѵ7b;f1OT:y]kXjHD_lmԻ6HTxZٵNekH[z\4\D )%HPJxơ7n.zEx=QXܔyՃVB'D5+0eB#mEݖ)S\sO=ZwWh᪠ԷKx"N#xhVnN9-W;70 OhIOާ}Zլho^o6kNH[YSGv< zR\,254Z5;*WG)IO28?f `V쓲1[%hVy[ҙ}lj+H uUɷYQd.Km) r|9 h\Bd-(?8;;Wi\.:S@)$, Ꚛ26y'7UN[J%0SJVH8>@Dy-vmJ3zy[,qba4iG (CF TMR녉q&,: #iW=Z9KKQ-8l"na+gϥcj 8T]K5ˆu.MILH#UW[#p\jj,;wgU zc>Oea gPѻ;ukTBrTN V& Hnƴwdq1c,Ti8yt;g IwK%*uIIp yȖ$͉$KVRTTՁ t״KOgR>!X%Tvsmg]6(`s'lf[4 [a+6h6nK`lNN8؂&_.zpmo{7uݍ H*>pR8JhKj;qK KeiTs|'$4KQrP4,N9ȡ +0!q@f[R]p*YO'-U $>YMfJ..vF ?ԕg uIRzfܬ{۲聾vP+%8O~/o/5$d0³AKjgZKR ;ֆXI;$8:ds8im۞CVMZJC,'I$ʜs)É|7mMuOQqtJT'ǧ4{vN-- #zPJ}qK=D<ܷ*AUGb:n' 8PUds8A3oNyiV2$!.(u窲ʑ 2l1E׼wwTZnPZ-0ʸc]wqw*.1+|Is88mX5 gfXEREJsVl K\} Ihʸ'hE.nPb[%ibe Jb[)kh6X͊GR6b?T-$T4hLe&uDWжPVGsiJn6W"?!O3hLpTRj5t%QPBR$NqX1aqE{O?(g["@ 2MSm 'R;JN8tn,: hV},)xK]Ni-p'ju k` OUGŮvh˥B Uj۬qHj "R-Ja4VAGzw8Q]G Dw{rqpP}C2Yci6K]uA 8HQdtsy*n&8{7d$*XZ[D0x^V()T>Iآ 'O=k(VN+pSUr; d|sVX:H3ښ[i$$R#%Os< RJr,U;/"Uv4RAnurART`-E)O8_ZH39T $r S :ꓸqH}4O Sy{H7 UNcc16~S2a)_5QZ㐞:R)Scqҗ䫒9g>UwTrq^A`qTӬƐ4+QF&a RB޸ K p ~BSypqCN7'7#Zo%ZB+I 0XI ۖf446 +'`c֗mѳ},s-nef+W܎[E=ڈ#@w{]ْ됄uN]N[ߔ<*V Mpo]*AQ98;r~ĉu\",%[;p65{d+mYˁ.Lu$E27|h[n-£ϓ3e$3o|eƒRչ;l2F봴z(-PZc{F$3[yiBl֏ES~4_NZ!MƲ'#Lc8RCk-$dg*Ͳ7L\9-2@ZՃ'_R#_Q;-w2ÓrUSS*WQqRP"a:psrjYA$$`jHp+')3%VE6G:ԜO9|pI=9I' 9'ꨒAgJYPLA#(6Fխ]s8P.%y-gmD)^ | uO4U8{O3m2wKҼ >Tm[b-Jʖ[IO2 d篝taDrkBc˟kpm"WsZ4&(hqH$g^R[8Q䏜wyUpGaԩIQ*uJTTXFR)8Ϫ1zUBqW {HW= e$\S!u i@H*9vN1WwTzD"6ԯdmBYORPă9:gʅkX^Y;\۲l~2:$"kiMC%*BBT`%)yzA@n͝ECK y/QGIc |h!?z(SO/raN4HiJQ;)W*_F9^s0 .{3M.LynJRP#03t.w[;K-u1-!BvyzI3 ;VP崓{z+<}P7(=v)gm\nKn\Z2[ZlG%[Gtv{nW81"L|H^;$YԊ+Eb>'ԯV< VNswۃLVЦU9,|R!8K=w+%y8pT6OZJǸ"!dmu.}Ǿ i DP*( #4U EϝcΐT@OĒy\n@#;G\ ())sړ,c!Ǯ(Iurp$$z UOFDKYSqq!#X#994$Dޡ>_z<Z*>Ldܑ?thi>ԡvgJ.7Ens S2b8V6o*!#NU5Z4ݦD=RFzPrP|u؞yDw(ttJ|q8?HK'l8\sg,i }SfCSG-N0T<4@$\8kNVPzԨ"ʒN$|)ߓfZI+Ehk\mEuiӌ1w vh IQוm$-1:KSho|K$)>UysH!Pn,&zOR@v $ŧW)ߵO&c1Ar C*Ypg*#p cN"ʘn)a.l^է I̧3Lrreqq 9RRc-KmB< a- 'ϥj+ ~%/w 3SmxԒ | y;@"kn=BRhakr8 wd ;ʚqh6ږ'H$^>^U@˓(#㻟[ RCe8F7Sj7GzLU2v"yVTw$(-,.7(fؒJD!d942$8q-%ku{RiXh9J4FBA#=kǔq)Js)%$q僗1DŽx‘P կ <0ǧRyZr|Ou!E]I' 2u rҤ`8 gx4˧t2X O'ݚE:&P6w`6ࡂU#''{y4$!L([zBݷ" ;yyY}JS}-pR^NآQu "yI N1UW @GG^R&Pi6lFNO^ LF=sMoB;ń9q4|Tt?z+/EIRP2K̲L~e wŀ;eC djUQ-6v;pCe+[Z7,~ȚIݔH>U@wFxB!C眎x45HO 8TVB$օP=bDGZV)@ ysN&Q1N;=N܁y4ܵ$7t.B H8;|qH&[G( A0NQcECtwjRFrNAEDW(Jto2v8r @b'Zq}R|H;9CZ MªHvmF1)eCkaa`$9=Un-HnLĖsX7EfJ˜(H$}")!Y8}? .I'įO)tB> 5E ۟i/\av@ZwVQ\GxK^պ❊Ci)[O<* ::Jr=A6:ҟDѳ3ņ]ڝu$wa$ Fࣣ/_KLMuHBrӻ 8qM7|!+LvXmMJR '$3CyWMsDG8Z O[itLq.Z %(,J$Z7jbrq!5;=7XV zfdʱ:DqM?ii9#O*dd'0qaIa\)F೜y(di Q/#2}8+[DDϸPnD":қVI馑+- zNҰׯ?s6~&RSݵXjcŠ`"ǹ2߷,/[yG"BwrR a}WW}߬4j&xU?o g5Rad9utB$mMRZ) =fH<>CZq\\6(G^}љ^tq*9u(UЂ=W)?>^T4O1*x-$=yE""9ݴ^t02IP=h\:>XO'$>Q+ /Hߓ@Ps84J8UnُqBIڧZDytI:>{0Ix p~*K$ 'R!^,pیI3NAÄ9u ;qӥ%%%;wJZ(1Y#%CNcgOnP:dw {(c' 9wcYc dOO_dI(ydM  ]1殔A Q$4[ rzg?Sp>[=ʹx4d$!p09U(43J𡑔GtI/'ٔzs.A*0Of!=VJIO\>$$e%)h0:wO:rvH? % ǡivl))$'I1@\/nKAR`>P7//(7 I*Ixa)'$ LАzׯ-AIIglhQI|$tŒ䧎@'40]^P2 ;);w'?L%IG< gpK1Zmp:O*y̔A$?_%M >tCJ 5 &3cF'IIp=ʖPr IB+)W![ FziǮGe$%]vǿu5 )H>м J:g^PR $+$(g]2T;)}4ԏH[Y=Ť7`>4D<Rh[il~d8T2| Zb#2RF=PNHH^NOE2tK# VzsAOߟ.Mʇvhe( ݓ nLO܃ӁC9HHJ>XR{I$O.4ˀlZHϗ"/X+觕iq SH8>hV<1pG9b=$缌2߷,/ZyG"BwrR a}WW}߬4j&xUVƐSTtp7^3Gښ4r3YZ}R\#ۉʑ fc !kdtT{ug:s\;vtD`XQ0TIѿ%1Քg{^Urz-:~W8Fj({\ $9p) 2}GR&>GL(UX--"/ڑu61 O>5-eJR&ЯL<NS-l6rGz˭Bq؎ei =NGUN"P,%܉+J}1^_WTS2JgHˈ9; ]k ڷiԺ꣱$@|GYJIRNG+;6IrMts@Z}K aI9.: I&IT)))?5*+Ufw}xiH/qҤC)>Hs_A- l$A Xi|sZۭ'=;OQSHHd`bC C+N?}gK2'+H]tH͸%uw>)"a\5HtZ,vܯmhi 8ISD^EOݒ=V1$"궝uҡ9=O};Sb֙6ڲ5mQT@ׄDkCn}ե# }Tv *7 8s^}5Bq%?Tנ-6&ZIt.Tq_>3؎m$t%N5H}VOq󞴎a_7Ci)@뗿%\;r\mHW 4 ~#?>r:EA I负4kϺ߲#E'[i7uT+k>̴z$]-papÁ@RO9Q=-}9#bEޔl P ʉ$1܆&Ď-a$( ~h,B@܈'8ŤI_LF0{S඼2{c$~mm=&d^P<:VTӊayhΥ cqY.dp}֎OlˢSnrn;ǁ8a Gtp85JJOޚv KkEm˅S#%=|>jDᗈG t'bv13䔓E`@.+3TOVkVУ,룛Zc׽htCҼZ.mpFqz+c ֒IY܄cv=jQԎ6$n!VGpY )lNF[_zZRG?k- Ylwn>@8=M sp@o܎p$+#i)pi7n_Uck+S?V}%P䜏اW5eGlI#8^k])nԢ ĺ)Ϻ$H>e5?)պOy6 rVCT'GZ3**p62_iğxG<\H ;Z|m}`⇢1+|'Mdyv}Q0!<뚦jˀO$E9p)=F:08..͒m*4w]%<P(S*3-X7 $z I<4F.GޞGjSgu9_"..a'p 4~C*Z*iۢی[#iWjNOG^T[ԜD$"#Jp0yӮ(JJU(wCh9O}1xN>=E{ akw~]TءFd\m2(At"6q֜CkBРRaH :rR a}WW}߬4j&xU$mMP#!CW^n~ Ieˤ)ɷm,#9RI%98MCA46%:8 lXF,5NѸ {R IY!hʈq-JNO6_+I_+/nn[dpzm@ 'Y?PqԜ~/Sv_fQՐ2y3Ƞ5R0~zx\餥/ e gOʺ+d/P\x_ق=VII*A휫F0'vQ P! 懶[{)USMSJgVRx $SPkE|;ecx$/okTy=kCt4+5^Rrxy*6~f9R:Fd׀_rc < ~(ooi1}|7@n2>*㧮wŜ1t!+N$%X gU"$t!BpZM:RHVThZ]qO/ޢF rLfy%  x/4Ֆ 9̘ 0H`)r:tA-wΎRk֮1>Ďmxy#Ү-*^=sFmM{7wgdBlܡ\)(g0:e^fXr{chmkF˸H"[nfۨ5j[mMtnTˀ C$$yRYykT"hi̿l:UfNߜݿ99I^n׀AlG,= sV vӜHn7_%kVZ[ʒ{HI,YEU{/ ]ז^HI_5 rRsCkY ";Xe:˪Jmr)+)H.`bffAE̿% A ȦR fO$O/$8:ݷ4É\ pW>`\>t'=8gԙHRz t8s]IG59/ee眤ij /|73sENqTj.0]Q7Ѐ|sҰ=U~U?.Vjd)L$(%KPs@,#@x$ٹLBA=xU|~V=n+X^ y6 io ]˦vʣsVlY~ }iSWnHB8KuWS䒵wI5QTTbs.{ml FIX99VkL9 &TgԒ2!D gie7M ȪY4Avh ve˂`TsnZܒcooZ}* rC_`ݻ'WiuMOzJujK$/П=l{*w:F HI|mn ^ÜS魥w)j-!!OrV:zJQgVv} *xU(gym)HRzx]88#7asNk(쯴!w;KQ^+A@Jm*j<?>AsY̩H8*ذ6|USbPJ/A+wׅ|өVN}B S,zι֡էEokzƼM.!?KoyyOZ ɯNdw2ZIұX7Sh7DwKotGAY!##ʳ[3 eޖofn `M`NQ9?]&ؗ+&?ڜJjyK*JBD%A#T+ ʭn_M;펧kpe)::ޟj:_*_R4LoWϖU}BbnDB;!NTG;r`t?nnhcBh[MJOgH~M~g*у0e~+L]; F\n~\$2$!J$!=$1qP4 1C-,z{|ZR*S u,6n61Ӽ$}4*V[`a a̩]GOyV;~ِ^^:d@W_,j癳{:BBVR_I8Ö*|ǥ}{=Z(%W;e_I.Og:m?7L>$Y`HnxMb2@py `ҷ3֜8Ιt'dz6ALs/NȢG ctny*vu@Oq'Ome~R~^K.XOQy_~A&ACO4so-rcq>b7c8?YI"̧+mC-!I:T2ƃDioRmٯ߬4?*r,>CqP'%XtQJx:l^+ 7  ƛT2d` ոeU *$ko4FJD|H~%j"__hw\*Ie L,Z]m#O+4hߩGFJ>}ZZWv.|v$0ۋPXNnG [1~ RJd-X9X)T ohkFJD|H߉~(j"\l7mԐ'4E#"OneKA+|}pL/DK6nߎwZMš@!XCn@/.{[N?@:'FM(?4hߩI6[u].ˇ+O+a!+)@y)G┓͌eIľLtƷ6T^z:M/!@+FD+3ttni"i)#K~&GJoV,2W2)p rPsjQoi Y5V'ɹ ]wJZVrR1)6H:_4oԬJ$ȝ-7Rkt=k}j";N?8@ < U1,E2@#΋#DoѿR:3E|J߉~NkUY3+K&tvFJ*i&t WuPG迉z[4oԯgĽ/7Tk&_B߉z_4oԯ>4o/7Tyw N+ !~&iıRѿ4XߩSY(3?4Xߩ^}h_,oԩ(3 8T/ІJ>4w/7TpqԨ_ 3K%}h_,oԩ(3?PB;fK(4XߩSTQgR~4w/7Q!&iıRd#Bh_,oԣCG|Lc~MQG GJ4XߩGІJ=# !&iıR 3K%5E&{!G*CG|Lc~B;fK*j8LB8T/ІJ>4w/7TpqԨ_ 3K%}h_,oԩ(3?PB;fK(4XߩSTQgRto/7WB7fK*b8LB^+!~&iıRѿ4hߩSh3GJџhߩ_CGωz[4oԩ|נpY%?;EK߉~} %iѿRo Q+tWĭ-7WFhZ[4oԩp 13pT8z'VM+ߠ'K~&2 }L17^#atcZ::7M4v+mҤ(2!E1/Dp-C#JqJև:+Pcerojǒ<$@@G! LFO( č+?RL7.YpS2fВ:R9ғ`ۼy*InsͷK+JHy!;Cnme1$ N Az\D~PJɌQN»5Ym!KJ2](%MZ \'xZþQ{ӞUC^dNa}ǁٸ-KmEP67HW}; dt0ӷ.۠F[0d;k)SKhmjV =|jCh0iE88TFA H~ -+>M4OhV`RNw${7]cqGkA(Toms)Gi"=tGYmHOđuRvAEntX?+^:@ۆ"Ar DAwldJ|6.M~pfFJ. ,,€8 JuoлsL匫|VIVK(!k@ 63UĹjv~ n%C+$H*^z;riC}9ZA-aukܬ5۴ptH:݇G\(kzl4ZGo>)X%(JOUlQi?iOsmyaLt40{ b9dKӯ)q(amr +w7hV+tJ*y~U#êJPڔRHӮ 7)!(joQ,,at'VMŧ67ǺT/KZ[c C+ ʰRcllU-Qow-sy TYBұ#b?}E}CI^ x ik6gM)$Ot!i&&'zp>B+8#)PV򵜂v6Ǽ5ub{q0˝ܧYzJ((uSJAP,~z{/(+a~SrMV(.I6n&j‹q-M:SI ԥR9k^ks#`%HB{QNrs{/_}Ez{/)1-o[衧{ 0/wD˕ M{P$]k鍭< RAڴ\ t(kVER$M.CaH)BZTA*ʒHW}E# lS[|OҲ-njDoJsB9Po7_\$˲PA;6yhI3#vHRJ|PM;a^v׿wmc[$Q{ j3b'ZW*6d\x DfՎd%M33 Y̋tuemƣ\ ۘmHq+mh%)8RN Ah|{?+»Ka~@ZM@nNӺ&:P.Sp|D =YNFsZ}^}+y 72Gr}MxY+z?+VT¢/iD`5*޾<VSL^oN3G*޾<VRvy:#5{׿CU}vvyz/(|VQ׿CyE{׿CU}vvyz/(|VQ׿C/E^*޾<VQb輢k[!G^*޾;L^;<W {z?(k[!GiGgo_|E {z?(1{H^Q^5ho_|E/i^+U}x5h#yE{׿CU}vvyz/(|VQ׿C/E^*޾<VQb輢k[!G^*޾;L^;<W {z?(k[!GiGgo_|E {z?(1{HFko_|E {z?(1{I{<A<VQC#}_AUU}a{z?);D^^` phCw%/_|E'hQ gjDwc/6A褨Gnre#a+lSC >Q|T}Ct;\FQE=쭛;Ww+fNU*ٿUJ~)^ R哢-쭛;VL^.0| a^ MӻNet%FzSrJ_Q~)^ kN%=V88 BDG dF학<===e:Wo?o}EU4gpNlⒿݗ#߶Q?f쿉{[a~Gky{/);@SF_ ϖOo~RWDsf6{c<#)H(+!N8+JHj؜~( MJ}o𥩶\}Qԅ,!Nr-ՑG$cn*XXZ5QK]Y+K^eoѨ=ShQK'T Bl s_|m~ s_tG4/$d[Gūlq,D7jTwgUήQ HDE/j^ifȗ-)e} :̮P R8[WVݲčG{7Dr $[J H d m \3vT"zqadP))Ghҳ̠RGqnCqmk[Yٙ IʂRT#pEfگoGZiPCnJn{TT~R2Nr/Teri5-!12Tw5R#BYs;9N<㿩=kT@ %@Ђ\|OgTtA$)8ά8q8’r KZ-eȺer0ywp;M@tkߣlIރpnp&cJz*} ) 9# dyH8 yB,:!v Ki9(0Ҏ~r̩ VvJK dvP.lJp=ND!#}qz:Ԍ jd66xmHYq_LV>e9 R/)/~-M~\aqeBR zGFoqlŒ=6JB$2{[/Rh=LLV}|Hl-7 `+ `[#6! clV@Rr 4t㠽t 3Z KХ@H}4+m=M!%>9Va(͐Np$*yQl.ZӞ#[؛Qˍg9ߛ'YE2 ;WIV.l6˫l |o{*K5ZČ؈}m8^Z I 8Rn6-fESB4È v2yRJNH&<~|*Ckn?²֛=趸he! 2$` %J'dꝶH78,϶flGK:TA6Y.Gi}ɚ#a)4ԶVlJT}EY: 2cqHqI,RJRTq@*9881[5ĀܛN903R[q9`&ٓ0mVOyB sY¹#.Iڽ)*:%#*(rC-3@pY.2dJ`˾pyp" )\)s[@WuR2LAt+O%1fXߔ63>6#֬)fSA<;3PiLJH my[Ӌc 'oG?ۺ]v+EŴ=0N LIՠ.8s>MdT8PsI p0e- } 2pFA؏MFbJT f>V(Y4x˯%)¹A8Wǭ6$T|X8?ǤMC&2<Ģ0 JSAY<>j<ЈQS 2{jUHV%]-3\HmXoUЋ86uE-9Qi\VyJ,sQ6P X ;hq& t."+|ħoۿ϶c^1v縭3LĬSٚh(yH￿MU F K׌>UwI6W4iAHs':dT>5?=O;ȂP9\TM\ڕt̺.43zDvE΄c%O}KjBN7V{I{!Z}BwZ!OHlM7jD؟%ẔקdN%&z&r#f) ) Q$%܂ 45ELs mե0TN2IzDZNs4KVzErk';/*LR4e}Y ; ޅ/ ])sL1ie~j[y#ܥjL`dbvGOeW<=A^t`@pPQ⤶S&>qYgtsQ9qC 3H{{|ªk5izqݶtvme7$kΥ*p9Ns>'r;l6{'CY~|p:6`{ߩٻ-(mfa^e/` ˶@xKl%J)< nY5F ;5-HY]Wb- BwTR e1e19!m)J 8j {~gB|GŢ w# JVX*J3J²ECGak)i7 **wTUTyW[^LYJdjD7Uˑ-T,WYؔ,@릠AMUтd*wTb甠s9>㾳k2x) #+*ze'))Hښ1c*UDg}g87qr(*yf$wǮ!?R$N2sr ǛJ7ZOĹ(+ F$ jro%9Oa^3K7U )B|̅ 1Nb" H#o}=ZYY$G]^MX93dɤ\"oTA- IZ22"=j׷v='֜$y+z 7 dhTIN51-a+ AbHoX;겋vVH߻R:r u\GnZ*qN1ֵ-Ш^.NWMkG1|i{u{! #h&o+Qe}Y ]s&/+Qe}Y ]ꌡ1}_gB=|D.Zةr֊r3gwTe [BWԨAQtO:pwXF>dj idY"kҞA\RGd[Jsr9۔GTnZh=q#cѽ1e+}ShQGm4j*i="gf cRM;ه5d7:܊(EP0إ%i.#?,҈)6=) GA۟:IS.zl>o󧘷ZCW=ju2Kj g:mW3R2ɇ%!沗2d( Ϭ{;ͱXIq2`9SELq;ͧmz~g,)1p}nOx%ZmD15ɵ;[\&B!V:77 cjIj3KK)'#@8H/- ($|yMk1n|W3J8* 4N(S ZMq-עx#Ǹ\;iIH[vSfPBHPH)(?[LqKD $ZC.M:@ M-A(IRnMYӞ^d4W RAob0N{ږP[n;qt<1 J+P"B$%(ZBBB=_ ?Mq85`YBz+GJ~/4%;|7 >:#X闣:CS9lrlQ&c.6/)p'J׾퐧v^xFZK'\ϐg /_HYzLI0vWy'Q=cpp;aikV7oted{ zIl} ^gA '?]Qf.cVړ],Z )=V2M%7^4|1w|Z>]Ơt8̄ZyT0\|BgPMyHGK|%'큷Szmۊ쓫 LN0 `RX877!%e천?MDPSvhX角]Wy5(a >>@?`Cgpf{HLcvx p`9pUS7!j밺·sQ #gRD-l6JE.uƜg;gY\f ӳ6N j j^5s|'PLTos.z|޺Jb30-v g֖jc*Jqg/8ZKqSpB0H_?O~|a4{KÈQnipCNt؃V˕kbl#ai ۲Y%G HV⬫*~`B]ay^|'N0+!z]RR7drcՊ.-Z >dcs QE=6 bS<}xR݀/ThҎGrڲqʠ P=?X1"kuWp6#-S1LE)* ۻaTzaը4#;peoZ姕G9!m$=Y^uU+pɢ%7ԍHT}FB*XUrp Tʬ 9o| _Usθd$APs*GHp+I*sq%y8PclkX|ʼrk\EҶ YoN KҖxJӏ$˶v>*N]0ʖ ǫWa/Ǎ K5@:uM:MEZ&S[|8e%@Js~=iNihͮ٤[ Zy@' p]muѷf,`CR%)9I Y7_t ޶ًٱh8aaJ*AY߮7{˶#Do%%zgL]Y *:{2Jw|-'h ]LKoܛ7CԶcpA='G3[Ci6`s!(s(N5p):&ۊcbue[gaʮvcXXQ{*#Vi E@ _z<_(s5w{]d ;UԫEiml}٧9AI!Syo,yk4j+ o22#ޫ)~ О멚IOn,DૻTX5snlZIq7 )}bEk vVU-cqRI9\GzE$[Y;yzjAÙ**9nx"ܴ\JUp;~\ƭ]N8e%JBKYNwۻ>lkZī,TDT6>j;v2 :bBH8?-Zw4dĽƅyR@myh5b4r dRD|WP||qGwu$=2TIO%s(%&F1ꥩǩݱcExd`6#$y211M=5K3\:Yin]-Q] EQBO&;*}7ܾV-S3SGm]M>?H&I/oѨ}ܛGџϟFT,B,cR5>Y0Ƶ:d5>-(L( N v%\{^Cr|zqMg(q-!ZjrTHGK#R4&,JA|nkM=ع%C#}N %(p¥OV{{96&2[$z5DM-8S.eBߐNHeڂy@ 9ifQ"S_993beo0e'Ԙ70&f24@#2DYn2uHds>CUCtԎ Lw bi24 U^>+]6qK1PU!c n*Ɓ- [I/6T6t,a%ܭO@@pkФ#$ڗW"}hJz)$.k܆\T+̍C}* %[IQ֓oZBy5k*-A9NW>ߤśt ۉ^JyuAu NIFm&=2}=O)[a)MQƥ` \k$UƋҥ F<( rE3ۑka.AA0~) lo^qS.]H nR3SǒϒhU"u͏-#) gju`it҅R>CV XM]bΣ A'Jnj_˜mkZdSRUFYcn}5N@)E`ZKРE8:_ 7+Yu,{G0%Xޒ2C)SiRZjŞZ.2X %9u(k]@rt )]@BG+89\v #eOCx{^U<(D |iMe钧| nKQǨb?F Y\icG(ON\O媂W 8[Nxq{S j}X KcSqKWBpzs}c,;44=%㭔Ԣz]?8Ɵjn!BAl. id)Ĥ|;ie:߀VGUdUwj-\d69JgwҼ4j;rVBmY|y4nEkNI;aO ZRSˊH»-Q#6irݶ7'J| ]T_oU-[t6[qi#o)kPPkEM[u$S]ǦX8?fLS6BC q O~`Rm.TTݚ8Zr䝻}?#©[D߀M~/VNW|U)8s #Pj{m]}V m BR:\Iw-dFBMF?muXYrɩ$N%.m%+\<+s RҬl|yW+ SV :CqCzy"3׎QWP|xoYg>f O3/CXR7!Qo y+dqz $I '?]tܯWak38D6WH# TKTCd5뭄[5 0)3q+ʋC8Rc jU6Zxі# FC8W\W{K|ԲI{~ZÍioZܭtBPwC>j.;*ezb(X*ZVˏ]hʽ.|3o<uEHmJV3 f+VL\ !Hu(8LlROLģ koQ`푤O}r\=ovMeG'fGɏM}c=rG0_X@qN{ԣtTq`)⠬ZueG4wP[jLtdn)+YP2pa~UV]MJp2pqsM3,Aҁg#96P3]?6țmX (l6 I\?qĉmVK(XQ'd|X5EW^)Zsi㵺c`;xzvJ!O#WzABÓs#6o殈f6F9 ꔬaKAJBIzsKGаd&XiS]Nl9 g wQѸFl퍽?)[7W߭b#i6RU?(zOR)I ^CsOt!iQdLmC/!!J R0*X^|LzY Y9F7$vN6%ʝ%*h}Oۢ?=pGl6.%BlA7%kɷ-%$n@ZF{kvݩ:&()) Ia;=|ZטQSSnGQHA)%;'SʖnE6 (SI)l$`GN~b=^54_žjWy-+^ z:Ձ'bVɫ7U\[eSů. -QR.yQEE>_tcrr3!XB5?o]MMƚw94 Esϻb>t5cF:ە vVk_@ ?r5EW$|ETJDQE!(#4zRޱf B(ޑ HArZ: mޤLȹC C$vSvlwu50NT\ԭ&L6y^a$q[8MNj  _u(~sD[xaOBq6: 8n4!-̨@ qUgÌHP1'֞nd3)XI'/' V68G\1D%#V:8]gVs] i=^DySl(:1R 5LSڝmĥSyg|z* ,Ksb\$JK $d~@`.otV'6]-j a)|`wq֫N eK-FqջAP>1=~^RaIX%J+R:5FL*eFTJTܣy9teRc.5<©f-mOKhX%)yAAXRR;SCbe N*IU5c=ꒅ$~3Sulٕm[i,:l=R3+ |!ة^[LeifH,+q#x,LW'RڀyFN~\fxk>l\";K-y >Ck+O 4a#v6'ZG3;s)pj)Kj͌ޛ-pF)W2w #P$Uepd:I4Sbo  8.vy |WL v7?;/+&*Gy{Z _sAg'Ҽ_GJ~) R]z]\ًvfoW*̆oȝ=D~?LwVkz$H)R[qŌnaWZ9v)k* h 줡%֠P ;J;6Zߣ;l}S9եIPm9!)jᶢCI*Z2I)=!r|۵6'IJQ$pO)낼@Eޮr_J}*O!볇cӼTzc(j `ad)鎹nG|vhe\rq̼{Uj"HsR5Q%&8IZ|sOu™fr֑hbv '>9RPUϐ´~[[\菖 <խniiV;oIBl=U>bNud ܕuuhG)p6mܞ#3K.b P*(,;C|yl^sfؾ(aK `!ܝu`< 9sɾ BѻuNh7VFRDU&Mqm$Bv͜Xt *aHfwjWIBR9VQ=ۮLi O,8Tq0r:$uEoD?Kkq@x!!9/|Nb\H+}Q+;V߈ km*mrܥ4̜N2Ӹ*z6l\zbL47R%JyIǒ{_eu[b]cGX#J厧>2RCºu/ %ŗAqӭ̐wJ-m2)G kuw9Wۤ Lg P V \ZFFNzbeN19+B ;u߻\~-a`/] hl:qh"D>*VN@:g◯T/W|&"%61J})2{EE\d2ae֝S+>;t؏t/7ԍHT}FB+^pJ%z'Im^\W.F~ZJW=q~*Ѭ;(Pa*=T䬀)-tƹjiLY İ>@wGmSo\d vcplRƥ^nK)-*"Cҝܮ \-/8͎w*+ x5馀|"X[VԖ蚶"fH\K%]䁾U~!\%q3Z@'^T>Y8UH'5.Cj\iE^_+$᝽;CpܷV|ќyGhm$';] Ng1*mpto<bLZR:c,^J$+>uGn)NU1wMOnnQn}0#}`W fptnS zZ@Q‚p;wM}E5Mi<7* CJg>oa~5nxV vU(ܚX^p;NDg';wUL8gܵF~i7`;d.Uٱ NIOzSI=6kYjτWnow0(a?**5=ŇRbq4.iԭ2q^#3J΅78y7xYc4rg Q' 8=<*S_\;g/;F"4߻ܺ*f. ֋Sm,9O;P1IRk`4y!)iO*Yk[F<'R̝9vW##ݧdN*Ns5(3J9#g nشض/?-*h {K㳀 =+,Lڡ Ƹ[b8,!)=dw'uN\=IZB5eSe"w'Uv:~ph'Ĩq^φL7HPY;}ǿeMTXUVN ɷ%k!#tGӥZO3ZچBymO9P X+q\ֵ.1ͳ-;yEkxf]S}=D.&_&.!]C&=qoSI޴da_4vП:zaRL%}ڣeU3*Տ1\/3s)|yHJ~>ZlXW2aL+q #nTӌ3 _dq`iHlz,KѰUΓ䒒@A*\ѽ]\.A߻M23.k~RiQ8Fm{Lgh,ao5/^/Y>K=nܘ\yN$(޵mN}sеIǺw}^u',QzmWk2$ޝ@qs+5{@'"4'ٛˀn8 {h c$TfPc^ǵIYD&m%C#=ٟ菅ټP4pO y[{뿟zs=^IbF[g~Ďm7*\ |`1~J5m qE $8r:wA]! ?k^%1x:|`Yjv_(iO#x{^S) @zժrfǯ7$mje,\ē+sF\EI]MTʋJF:]u x >V7%H.J$t$1M%[Nٵ3 ѵ-!RmW69y]Iww-kd唆d!se![|ǻj]k{-(P"|@˖=7|ڟ[JDTu'|o=<7KizK%-Rf-8`4@pqw^n:KT[f;F~wɅ%1y)QRqlGKY4!#<۫jCX|A&mld6vCZCIgy#XI.[KC L{/KnԨ2[G^2#b>{Ua=8.^퓦eerdRR`} lvz֗Ҏ]&SQa=κV9 PIne>W!iB*@`HR+^D_yթj-JzӚұN  3!ڜ)T0΢98A\BLyEL\wzi|iC$Gw,KKIK] q|?S\K%S6 ]4ۤY0!IW3\HZN1k^sM`:;,q%jA\ZmdLWضW˞Ǫ]wkM;qˆ2S͔׺kH kt还Ic\a_W]Ǫ EYhR(}7ܾ!S F>g(BCLY;,t۠SqtCO`Myn?w4_QZ)JF-I !sިTiiQ.h?Gǵjh0?~`)^U<)w("(4d9䦎dv8|iS;4U  S{]i%˳RXU$DtF K[shFUYgJhcYjMcKlʲBU#8;IHc[QYgM`1o5QJv)f* ]w)axVRy9OQU-y0t%D*HRN6P38ΐ׷Iѻ^g$Cʅ-IsYh(P.ryI*HG[ <vtX=pK,ȍpA!ZC\H[ϧ*QOk% :Ψ(uO.*CmI٭a8) GʈS_ԅ!BT:8w57Ktu-J;n9z} s"k C$ +~ჿZklB`K 4z]*Ĝ yIP^:Fkx7TbTi{pRTvj#AY6 ۪dW^])%S H?jTʫsTmR-H*\7N7k<0n/imKe`L얔y)9yA;ut_`6؂_s\7l;Wh(dy!$$ܾHQ8teBm}L-k@`l00zw{uCp<)(}{bpT$M%]l7Ҧg%`aąsRA8}F*fps+&83PjK3=! Zsu$mE!pmԭíW5gR(6J)Sc"rOF@*Qm*6U:~f/bC G>.QCO)*cY)yF9ȮLiԔ<NžȶE~ $Gxߧ}Wsn! me)=uwHѼvDn4H}%է!),s<8 |Y߀BjIe=yH2wU[kAj)~/0*)iRo-J88 拀!]jR39A˛t!_IyۛeJʆ9FSTdG.G3*ߨ";rY[i-|’ q FjOϠEՁt:6#$SRNFVQr=l%rq'Xccil*2SgG~:|mF<2TU`ƐӈBTP A#jkZm -gs7wsYu E]l_-9NTf,23FBhtӓfʘ9m%(XXAoB'[:+UJ`oR%w)"f+#6TOѺz9{=~[hPמ,r~ ^:UBҔUnO2|W9By2F_c QK:GOKdKJ+뎺)J+%Dg(ϟҳ *pי^>>jy+HQ38)պq)JrJNq|jƍC%KR0:5_4 e@>@Gq;Zln:BSRӺ5 E9ܕ Y]Jp09Fl>p?/N/4f-xp3XTG[.9J-bi+ب-qG[nc3Ӫޯ0~S[rZW6I*t$`d^Lyq*dhq7Z&躔RĤ}_gԖn>NYAH V@SKguÀKj`Ai*KPGח{;c*PQ=g ۻևe2)* c͓߾_[C+m).!!$F:y_qGZNiQ_2ZH$:mtEEOLf䍠u;$'Cj$d8:}u5}Nś]m~Rl*m!jySRZ>SscPϗ(N)eDc&O:Cl=;_~|y^D=_JpzrYWe)!-3P'e@BkY:V5*whv`vmujg(B/k?t3L"պ{{ȩ"Ž_@tiY4j+v:;hUaPCj?(0L֏NA,UۻQESVEPV#r(ZR{A(Bf**ָ/7 j.GKǡIH#5 H}O<*[*RI'zvR5X$'DHBןAPo+>O t9ntrevBd!p$aN8 R7i%eh+lک>`o@Z6RFOK2m48PMP̦хPNjB u-HHqj,g}+ ;%UP[,zrfOJBBѰ$@M:PFiA#d*i0#ҝjx=H?]ޫOQ)GI`A9D7vJwL$x HYwo/MZ|QIRUwJ`_[^i^~c o?y^$ kUy T~LD@H@VGh~oFwuE9@'F:jNw?NUa9. 䤯-͑NqkfxD ]:!HY[䒤#)J^#ʝ<҅\K|wiۧӻU֛</DLҕI=Lg|iLa6nOoa+w_E#9ϠZ8*5$ ^^) ?]W&GoG^&[q%'9.moF RgwT OtPN?Z ts$곹Es\ZneX]5tZw(Ӊ?ZQG({D=4J˩p 9ZI $ 􀑁^(Q9QE EPS F>g) pOc>eٍ%֐R=w!Z՚{ɢmGJKVkjzJ)MmbPPCh H&,NeKŽ_@wݡ5? Y4j*!m_ YkG' i*gZm(H(B(("Ko[h'u+FAJE_m]/LX)R6Pʮdrcl@|.wqhP3 SBb 9}vw?hQG?*}XVkK4hdR%^KMQEU5iQEET^0\y))I g H>q^vmVMoxqPn5*{mRew:jTE*A ΖS=|>qQٮksovcrq9j,~GmٿE'($xbJu< =;vSI($ۇ:nv;DHe`uO= ?תpyRv k|G+nw/ u? dȊWXymcܪ#8*+KZr5:E-4 |٢6A5Nζ][Zcv]ej*}U??*10OtaIQh=">~WAdQ\?ч%eEG1]FEsE}tI?<2zc?+2([5|eхQ崱 ؏׾c -Ik JԔ *PRsN%%KHN_?7f䢹j`iD&D$wxK?XXo9%%#?KZ2QqSɌP0]ÖIX4(ثaiDoEx׎+$M&oɶmBLeJ)MElv;Ds\+wdR!I.v?̟QgO?(Q.v;;SOJ&Y?hQ.v󊢭VN S6y)L1}-*P)Q@=dzhQp>qGh8|{sKv~tk6Jy+ 3ݢ|>q\x< ?ԯ?ȟ¿QtGh8OU8{f&L{dD<'ӽ_ hY_ 1,9펥Iq)%'(#p=zmK{GRv 7K\3;b%B J ֝lq`7nq}QEiF("xnIzK,-S.RZT:N||Pur0$=VV6-`;, ZI0䨏._43R\G=PhQV'A մp? ~`)f_ Yٞ iw("(4mq^ae=)c]jwd*X4ȇ ,{6>C̮pJ3Pj a6LRTA6jDʭV!%^.(8{>jZJ%YZB1/63آ1ܭ)P o|#puԫRJrHVR7Qcm! J_Rp{ճhᴻ\|A=v>TmBbͤl* ; =VomS<>t́Kb3>FR2RQߚˉ 8tHjյ$s>$6`PpR$67WZC KJGU{IQڃ CR^.eIiA\8˧/~٧n[** PHKI$F91M+g #6&,svQB[_ck)!.0~hT2o~ɮl0<^\M.2rH}G>j%Զ?_ETۗIKZP$99Usɵըat(P;u4@QI \Y8P"t Mp{R yJNrZ׋h6TAl8S$*FXtQWP,j0_ڮJܹ A= t͡q 'KO.~J zsMm)IY,+sz01Eʼ*=p: o5}!w'E&zw?T-eNG;pW| 9:go=V&nIEA'{,}Vp3ae#JR\rvH{\J68Rd1vuX|O:xf H\T܋mF\`,\VZ U'>ڦ?s*i{Zw?0:[ %[檅=L,_⦹4~HWʫ[QK}fzk]@o'jJggĢfkRiej?iTwT|ۮ'+qUfJ3LAEe2e痘78_cM%0tR\Ǔ)nso k{^^4=|0ߐ4)nu FZ4qK(6pSfxm'.PyzOtra*PI_v'̓QpaKG+Ѕs̞l,c# 1mlWaoRrd% ZR09'>ZHEpMC ds SwFdAqW&*aI(ijƿVd(}EdglO8$\y@ GAH I8̘mW{tƞ|]t՚<"4AYuU߀}# UQ[nOq-,e,wFԢ"ѶiyE-Mu$:SOL_%<=q`8X[j )ʖ 7'ѕ`c2N>;D{]JXy*R u\XKM|.l S|`dpUH5U7=%z)-r֍I<&BsÉ.2›v7i(*I$o+7tuzf_K1TS 6z|j}?jG_R;i#lc%YyW QgiTü5#koq3!-"ℤׁPq z{G3]@1/&[+J7}J(EQEE>_tcrr3!XBu՚{Ȧ!K?=T+PhQG; Y4j*!aZNA,5kK4{3-6EQTդQE!-k1 HSҕ8b%V4׋ֆMST-9eJp'gMe:n[``q{VL!1+ɜBg'X:tlΑ׻Ҥ#TP_laJ8aO{Kl4T\V\rBwX8Imi^u5$NwKfpg@{]Vث\i(#̮gX:Ũ-|U.0/tvm]Sn$( iUQKc'5Z*VpEk=e7+޶rB`ڶlӯQ:7:p1݊)lVq)KQhk$d &^ r*!.= 7f#*.-s9y7 S$WIP=0Wm#IM[mxy #G(rp:g pmr,|zbk$-Ӻd5 Z)JR`v r,bl(cN㩩=ƞ幸d MJR06=>4pmFBedTS8VCݑsvBb$p&ljS!%KJNFT3W=ʎ۪H etA {ǣ|,[\P[OjJv8Yar8A-N%YNcoJGK2Hc]ڜarC5"*)JF04 {E%mb9ͅutWTʒRpӊ[&^ަ՝}`yX]}%@O[XPOn\WJ=Sw_-V,2k)HerQ-`DS&$R|HӽSpEY+ [*bh{lnE|*|*(XR*V? Zuo}ٿej1ࡡgĢ(D):hEr-oC`)뮼†r+=jKܠG`?uf W)8ư6⁷ym;MR`A\zt1]P)Iy&†l0R*H9 `@*(W]Pq-֒С(ky-Nl3}{7TZ5]ɶRP[e8QHWQ J*JN>f&X_[iK/.6ls69G(W;|\})7@ߍ lXsp8-7ԛ21\EVO,y[(z;(:RԂ]°pnox.>jp0O_\Rl#sglc5tF|i@ʝ.n:Td%(gMsXv#I clt$F)io-w{աž:ɍ! B3/h-< )KVAQQ|6Bɭ,` imq7k{CzwvbfjS2WI錃TNӭGx|D] )rqm7Ñ=|oGOD-쫖\fxL{;RyӐA)$>O*[I r6*Fvzl#:!|j#-FGP69=0:Su\XևnTE PN{kGm%4-WTqR&JTW {&*q0^# JsR}r׺2<ɊCy $~@=uHzb W +ʹs m\BjNi@]8ؑ-ֻ0NzӅQY/RW&%VZGG*˼kQ sTSsm-Oi1`#m)%`ZTj@NzTB̚=nQWs SvR2 PIX ltrnfգ ŤxT,MYLnobTNJoSLH=z)0&ͷZ.K┰áV˄%YNyg". *"V2PoN5s;MZR_tcr+ROOw9)wZYXrs; Y4j(g}A?EY>+V 5fp? ~`)ffx%(*( pvAA%c$d 9JG<6uq\.؂XgZm6Y]x+ yˁvn Km-ղ$P?.@Ǟ#<ʹO !y]c'aዬ,>Ëa%^!^as͌9&n+mؼ  e =A8sH\22Iq FJAK\4KB =%))i92>Ci블Ski\8RT:Ͼ{hZ&[pZgԼTA\F!BCV X)KCsR¤<J^ji{+Q&FOZxK</or J< چBu!"C}6nSp8A)ZG;gUڝ/z 1:OM `wֳfFH9ڊRWF̙#*VoTm;Xe6_)Wûj/_fK I\I#'|onmEojɕڈKN&J ('rIyȮ{v#֍jMaeQ$V`\PHqRvL*IZy%A!I Ba}Ε&#Ku lti6Hw>ZT`c|تƲ*gAK/~UhÌPaEM!8#}FA^g:(zJmMqt5͓!`uOb!@Qº"L^ZӤǒrVvyXV))Z-g2.,vda)r{l?.en) {?% _?Nz4X4˯󜍣?wePPNecamVj8?wJ '<$ fh1&cr\GDUЎ-+) #z[݇{*Ʃ_ UtQYvT纷>lOW2M{s-KW>%+UUrQfi6J[m=u )Uig NY[୕M)Mz )S 7Gu;&Sr)Dzڰ(wy70J3ݸsoH 2=3ԍt ؂}iuKe7QXIyל6p9YR#nn:}d6Ԋ\MQW9oh/$0uHO2Éc>N󆩁s jӒc#wmZEG0 K(}+{xtvC[(}E9 w&%osm\el䴣ޕ?O-\"Hx](D^e|딠X/o}tM+hRTQpI\>7/ #ũmX9 A MAIq/犔{Er'ߣKTS%-:K;}jnjJ9ujX~HҤvh*Hy?\=b{uzJφ+ժaSP;o6+o=WkݴεȓJ$ $sݏSpW#/8_tcrr3!XBu՚{Ȧ!K?=T+PhQG; Y4j*!aZNA,5kK4{3-6EQTդQE!'7N)q%n49 -2 ~:@;-TͧA~ةPְ@y(#b|zvie<[ЖP}tuR9m)22RGR{@璝kGhّP l`g%XByӎimwq!˼9naJ@P()' =AawLêD8 pvYg:qIc>9I 8{^RP8gsӽ,ϑ!6~3㮺SR33WcorAF+l:IԝUp qtZ6ȔvlY# $qdl'_wD0'u`W;]{wIV> 'i4.[ԸC'|wÖwZD{Jd@y|q'׻nʓʖ' s&Ű7鿂EK:45KyRZRSH ʔ4yy<:[[`mRCVK+()ť>1p@>z8O#Wq7[ʵ]\l*KHyBF<||8W( fsk)\rx}/"8XlNNH= ?&i%Qc-//!;qe 'xIӲ7Pm#' H'GTjsaӒٿ[?(6:Rlca pGKĆ@A:_Ch;x@rc9H"rzW ijcҞ̈́?(?O7*qD--ۋ ZTIccVYa!m ^);*te5QQ9?{[RT/RA)Nqփ)%)HqݒSn%OVڂ:oȪLC8iR8p6 ϧ>z5..ZqaF8yQq{ުDܑnU/u$JM*iDm^;S;\n)*[FĩDz7-n;HMSߏuKs[@o'kտfzǂ՟!IZJ||W(ˆW)KÓ);:YR-шZMiNkJL ~CVêlAʗL6{% ҘD%A9#nd}a+3Ui s!ec~=O䩛U|eyڹUӻVkӨV^s L]!^yJxj:sR:LsLg+Fz SBHm~{Z;W*d:"~>(mKh+vhYWʰ^+}w{SqT z+.{*lN{/9Ϣ'MR8f$\MK}))Vos\43Ek| E5=F濚MV<>Q U<աzr l0ѫ'74ҿT#׷XS7}WQ?_UJ+]+(ޫ+j?@,r ^5U̯W%-}*By˂H')S;ӊr\#Wjhʤ&H,?A#N-hi4nl6mqƝW*3멃pwuhXf"=:m@Y઎]T2 ( 3$'+R˿.*˱:CRR\YsX.-A)PRV yY;QNkp°֔on]e4+85ނ[IYm%$gp1߮j]M ʼn6G2D}Ҽ''Ѱq:.)μHumK%\0dS/Ԗ0BmKp10]ֶ7i0nOЊ0i;mtޚ[ݹQb$2a9~Z?hmo=+nSMv'M*=){d=C2lDQBk~Y˵vRC긣(s G_A' ok >MU 6K!a,@6S^^%'QhPδ, sVz}u!7f.ϘFѰIz띭o-<[(C-I#=4vvSyj=U_FpYWt,.{ f5!lL'u6ϛ5u  JP ϪmR`[& #>Vs7qw 9j܋kB zk1.-{t^3=Ý4/1.(%1R{C8oMfY髊ۊrKjl *oVړ̠fVv ܽ)Źp @ͽbS:F6qv*PNZnOYq֞ c /p.!I%¹FVǻ1Ya6iy ^Vs?毋ڢ˱5p[f4ѾS\m=ᴋ/ᤆ'd-ߢ5~J Z̐GR|E1r~JQJ9=n`Ei 4[ǩWLca%on \kb4QvuiK_xO) ˲IiȮïϲ8aոdlVj `ۉz֌ѰPE>\{O4uV͵Vy}#ՊКf~2Kyk8ǒpZOG.P!JN$n4r7@GUW4qn>5ZH]q~^.3u TDGCGo;NOCSZ&jeȞl(oWW2#un.qHq!X-5$|PG;4اm {"!ĭAIܓ8mםupsM[[Ҕ tCF1;ǚ"%KFR$(6 v+4}OIԷcJBVUHF{kv0ꉪ'$[}?%dg5惺bL.5%3y#9;{w v\vCrD#?|. NOZE\91>¡cN- ;gفݎqoP})aQr6AWafRlaW$4 +n{ucZm(GJRFJY1N|:W6wU]Tm2gZYBs2A򐁃q>!'N7p<=EH&_ UlǺeU9[7̵^=\RcCCωElBNrak8\R+ #ު+ Oڎp:zk]?-Tled7|LY3!\BOjъ!6} ͏3&q* @d'>b @gˤa*q 5^Pށpe/B' B\#` P Ϧ)"pZ9m,˞4ơokW P7U`̾]X@mNIxKkY?"A'[K n]ϑ1a/Cf~iRZ\[^c?-;AjYvR5! $]jK+:1>)YZ}VjZ9;Y`t]+(MH+W]35w.:mmФ+t:wuI5zx(LI;4"B g fakէ^;5z$cRʈ❭l3[JUyqk5v=scv4p9rO;E2KlsirTK}C  zkpJCLWmp9Owu -ÝY]R2:S *Yo]ov녁M3a!Xw>+tiȓ*m<< ' l}gqp,]ۋ!%ȼ# /U/i.$ Y;Ҿ^}\u^SNSelQIa|JA#=ՙHWte61I^Rr0zTK/:R_1o8iB0w0Fi6+FK$$J%>$yحN c7g{*&6u{EѻkD(RB =`US>eZfO Dح%Jx9ӺI$7}7ov5* q J$d ޞ^HYI N;vڢp`րKo{S z i_њ-V axKiаf8# I=՟Ki'={f:IRXmZhc J{_]|DzioK+qDDJBZy$ms%Yl\l4o# T4MY{[.:'&j !723mlܽXA%#܊yi.[EWEsN34e&-ś9>n. $Fqӭ%qU͔(*e =TI]+Xˀ5ә꥕sDFHMΟu[u`$)X<soyF1o@9!ϕd ۺZmEg‹EEj-J[=TqӦvۺ wdː[-% 'c$IܚޖK8f.D1u!Aa[x-duy1$n=5'=}@cd%A NXT2<>z"!Zy%C-zHKQAG7rҾa.W8U--ި}U-I Mm r҂} mҼ TV+}O\afR.)zͤC(ŠNppMok{&v֙VRGM\|{YQ$>F1Uؓ5n ˱(,g~Ko!i.recӱ{_s6jTׄ$"AO.ޞo`n.}cExK[w 1FMvz" =]Ý ƨd6C^㧹}qR F|  63V֏s/Q<9 *}d/ۮO+N4U`}fk)hL*Kq¼mԟ>?uS|V[>d_)f@rǹ;[5MCwQZ Ǖ*NTr䌏U>q/W\~1R9XRU՞T|"g #<6l4(w$XV'\ӕ\dəymۻX| Eqw9[}֣c>!h=A6VlOW2WN{?:EH,⊟Wʏ>nW K?κJ@dz+|eԤ:UXw+ge#~uߣm״ɔ! Zy٥JJ'w|*ڭ5$h26,wU8fIrFsU'")%7so%pdu5$jҍN~֒aP^]oX#ؾA S1T򜚄RPbվTn9ةc=osD!R!,s$tP>◸zy0'g) xMw/19B)wZYb~EI 9}ݝ5{uF VhdR3Z?80IW<nQEMZEQBXe@k%!Cd}#STP #Ga eMQB/a e!=5^+!@f(%|"oR 7m5 lbFLa>n3ag <Tz%H)J mA Q!W"{($Q진i8/L6%̍!F5 ,uW>O Yҫĕrw 8B 9*O(E!( #ф{+Ah]X^T')) )9$DP #Ga eMQB/a e!=5EPCIe>ʔZ@J@VJ(B(4QJ B.lVc^bPIirQُEf{c{}yyُ5fohc95x_7%)n>b2Ü>QJPNHIg,9T-0!DT#.zjDUAO.i.Q),)bIq%I(sNH{ֲ2ii8TTl;)ICJ*r8f[SS)CAĐG"Yḃ&6D:4v⺑CII 'ViҧHxÈy-8(svA#LVpAЄEWo;&`CvnZFr[9e%lO9AA'c_YIu|TڛF~yݘU'VI(BCMV(?%e ?❅`lD$JF|saQiЅ #@CAʚ-VՖH  ((B(}7ܾ!S F>g(B._43LBu՚{ȩ!G/W9uFvw?hUaPCµm_ YkG' i*gZm(H(B(("((BבNTk9(BYD+c>ݢ,l2+%PQE!QEEQBEPQE"Ȣ(K"QE-eEy_tbt`ZF)x'/Qb(]H(."(EEPQEEQBkY: !hO6eoBG=}>ޢ,,[p",%O ye9p38MAe @Ϫ QpR}U`UnQB&XC~bEEQBEPQE!QEE>_tcrr3!XBu՚{Ȧ!K?=T+PhQG; Y4j*!aZNA,5kK4{3-6EQTդQE!2M{X:@*bTu Ĥ)}<-ڸepA4yf~ @^t -yTzdf#*B{B_CQV0y(i;*pB/׋fG<YSmm[ (]ҽ<>d"[sx*S M%𶾸{h𶾸{i8L{O`_#jn\vu ƘŮZ^!9%NJm=(2𶾸{h𶾸{i:iL$1pN#hRqG 64U0-<-ƛsq\pm9};[Z49W3'b wbPNHW𶾸{h𶾸{kG\#)zxA[ 'lm fHF%iJAmOXQP8#$lzPBf-&܀HJ#$@x[_\=x[_\=cL:"-8h#:98^´R1HLڊ,M6 wc 6$| 2' Ε V,l&PFd<7WU6oW8cy$)YdTWgu;s\ۑ(s,/uE_L_8 e«3[S.YZ $r9Tq>aTzDUM&KR%o&:J@')H\iM%+%x*rϰUOïMiN(!O1=iM.Bomn~BV9!67!` #cdtۗ<~e+<-<-FoM*jRF'שqHS% O2mw $]m}pm}p=Ƽ<-> 3͎^8C)V%qyFU|RF7x k뇶 k뇶M\ǎ\P`7R{XF..@|~Gq𶾸{h𶾸{kѮ5±xRJz6~juGV3U6+a08!tGJR3~j$˛0 ģ͗)) k뇶 k뇶/(,Z"** Z]rϒ)ϔlaFZӰGP%xz _f#g ;MEuSOGX'iy-(%'W\:mne X 4aR_Di~S/GCvHRU%$ tղ"; 2Ԕ0 Ʊ6cf?@[b/R5-{pq[po]/ k뇶 k뇶O$'7?+/)q4ޖ!1 ʖ쒢wag_R va+~'4p e}Elb,(-O QZ]3YYW220bE):q*QEE>_tcrr3!XBu՚{Ȧ!K?=T+PhQG; Y4j*!aZNA,5kK4{3-6EQTդQE!ۿ==mdcҵ8mU(u9 HU4]ÝT^,EQ*GBO@ziQpjȄւHu Pf.L]u)6kCLI%#R-JQ۔giBqFh=%Ih3 ؎dA' l1Aө{<Kw쬸{Lq*ՐNegIM:ʧyBQӧJu}K-[4R*TDqA9'9NV&Mԗ"Z\}RTpYPZR;Z7=/c|J]R4ڏ'mJ@ Q?[X5'=RF( w7Hw=!lres"3Hli:ZG5D#_-hy3-1/K, 4JNm1se %}4\RTI>ZRNF'UPI6&mcsɲ8w4<”AT u $W@ڡ4,DXfr@$Nzښ?OUxLykx^/tjM{M>XV9!cꯇxÆG(kSuypOj8q UT㯸Zu1Y˟6 :ICu/2..'yg\ CiR$ʞh8T3ΔRz z4 ^ "Bk! IO78ڛkGjj0`},Wp)ƻ$p7Ϙݞh9v׭3&`^% ov1҄J)I%[)#|`[oDHqҨ]Iu*JԒI O6Q),.! Lt)B,e^O0ˁ)Y>\e,ț߻i)ݛT}s3V܋<<]pʐ`g)ݱjؕ|) >ہ8<%X䁃͟0ԧ5j[źY :(Z xo{宊0K{z#m,g(}ml/}қzĻ=OL2RTUyɬjkҦ]q JT9T1liyBleL(%9* n[bx$"5[ý!16BhqJA$$)iފ;J͓ob5ևX(s6(:* ]*%lC+#AB(7,r.enpQ JO^cҗ8~mWRH_, 60pM:q\ j-PK8lfNZOixcb>yeѕdf:MIpr 9Mm>`+55ѱnGf.gBJ`-^`8?&OP_%'3G7)mswRKu.;R\(kn:V |]ȫN-%n[eZxn6zV0TţފQJ qeY7g.Uh >\H#I͚ӏgk"Fi-CB媲SN4ŗnHp,2M]F45n% ǝyҘZ2Z?) uU5Cr|qGb{iP$` Xܗn [JСޤ?ʣLW1 k8lg`neUb44 (RBJNmۥyherRھogsoʭ%Ѣe<шjV]+yPX\$$WZcaгT]ߠTp4J%TұΤˀa*3ƌ5xR@:G?rbV@ E'a3OMfiH+/s(azm._˦%3k=!2#B\ZH'4}6puLV 1mD񔎠gmW&WhU$UU\1流(>tl}9q_8<1Oj=UILͺQ2gj֔oۃn Z"F쏢EW@EPS F>g) xMw/19B)wZYb~EI 9}ݝ5{uF VhdR3Z?80IW<nQEMZEQBzT5ܑ2JO2zT@ԧR.Ő3L6A[EL?gBB: -8 UgE %r,!ЎӔa@ϵLjZk{j &ki@nw{)ɪ;{[Pas(;w^S==Ԯ ᭌ_Gm]Zz VN:?LB@wۚѷi< u KT|'l;+cĻ慲6fɹ#-,jV;EgTFkWq\n9bg}et mKk|D|37'H[~ӭ`.`2\2m;Av]Zލmȹ(T! sHw\}˺˹- ]g!I wg|[gwNU{i3;-08# <͞m47(|{U\ni4MS-r/En)^WRqZ<%٥.l MPxf~In2ޣ[IS^nNyZw֢΢Ie1?Ԣ9 =yNzzC,ce)[X#$JvjYfjhp/$lAkHWL ˱]aK|Er>O7'aʎ4ZuyDqIjzu!ӕeqFEK;Z 5i0/1෇J~+rf0Hx^1TA1܊dssLu+ yO=+d]4R25s#j?ݥ}4u7I jP1+JTQ)=$Uȋ\j"`. cN6DŽ<$-Fp2㬜A;`F>L*mTݓ-d6\aD+ϵ?[kհ&[gaȚԇ;9N!6Ik Sֹ0Cl >. 3(l \o:u5uf l:7dͰ \c8"Nu;wt6 MEsD[%[t:N=NYXP][ZOnX'uoNٻ"$kd\.[;2InG21&BIթnH-XaK8H¹ ͽX 5MӰmtwdt2P(6WƓYKӚ;ଵ.L~fO JvHWxuaJ푹JKmBN7 dʅva.2ot ^L% ؃Wtؚ=R|-,([I֬ muv9xCu;0mH ;dKM5fӗ8sm) Z4{f Z%GzFI QZZ[s15(L2@.7FJN$ qYzVqrBDD:(B%^n5cjn$@y\Lj!̔)>I<ϓU#kǒ9 oL=Y|ԍF8-]ohh&Lu9iWR B780^uӈ1 [($3*ּ:jo:J-E\J'3n wl}=+ TU#ǔ̫JAI(cGQ3P'"ti1i Y#GMPO]PQ''$u$响Z"KnLMIA29'cF3I4l1WJǃEYZ8N1.Ď[tKK;y lwwe Ypw']B+[RdPPzG0ޫTʺC8 ZyTv빦W53OFr!DF?xld A!Þ7oϞðy-mnu~/7iyA|Zp|K"z&k ITw9xz+f~.춘xGRCmudGL'?-nc쩺<$yWQK^v[f벐af69Y[[~dmpBݑ-e*ʝV2II=1SHjkif|,pO*8gڪUnЧpNGq\F\rq֒ .e$+ONV%c cS! O.{.12Ȁ--ŭ8KswomMĘ01؛peiE%#z h4K앶+!Inu$ܟmU6aG MV(duEj93o7719 R72UQj2\\0ITNG^>Ҭ-Gu\ [j)#kb1>z)k.2˫h=Tyӌh_ PMq zgɺ^Eý+Ǣ|Uee+}c"(||R>_tcr+ROOw9)wZYXrs; Y4j(g}A?EY>+V 5fp? ~`)ffx%(*( ^ cIJ3rMGC`7u s%5jOT$ޚf]S%ҜI.(oF߯MJ>Kғ[kG(!8FN=[zwz^Йr/ X-@:*$]ce8ސ@Iܯqlzb}[Akԁм);l*K}YuܜP)>gݵT='.o }{*QBFFRpݟHtmYRPJQgedvQߊ_OioH!3Fik:E-zqlQ"f.yIWjfH$Ns[6!"uwW;Lb6YI j HW90~JiD3̂M,Q%;xL6lxߕHB)+9Q=7dz_ʮe<\m\P>q\slZ=g+thBxth%L -*Yo$gMT-w!Dq_<}Vf67%G>kkGpPLs.GLud'##<⵨+! 8){Phxl[&%٩C@W)#>j["5\NdʧJAH #ζ(Nٶ@JR\#Fnjbmv-!g;iY.I<$ӦUyJl6Qr-erFpA#']ZrdPsF6:aLEDһRSٶғiQ V2&[U%T=m RHK **H2ۚSdsթ&fa"=}*rr8T ˚b! _h9zclbtډH:߆6ԭ>4GSi~XКKu#1ɶ[qD5rOϩ+ Zqj"nόG >ZaR.9KXC/%)#q7 +5.z㐍V9SӮ]xq]MVT=b] 'FxC͕_Z(J{D@NO*$ҴbX[!ȒCer\kvf:) mJ 9$מtd EucNǦ*Xs]k0Dt+,R7!Qo 5W4q7 jkpa98z JTqߤ+ c} {sJGqT0{޸3vsڍn*mM,!']~Jν?Ң{>\NH~*B2D܋#R>V֥Y'?z~jTe*QnSR_43{q϶{F688^™6V[d3bLV\R^TVJ?r4&&bHeYcz|Ãtґ7Cw֙l>5iNw)AʏU WӼ9sUt^e)hEJP>ԫsrNT]/e<`.yTF=U4h w zjYj2P6.u'i]/ ӽ@9V,~̎ew|n wP9,cHϷmފ, MAPW/w>5XZfZ2J;WXR~Z۞u֤AOӃZRzu  K)CEX xg_PpF咷 AܡAXK6EGmR<cNQ zf0 'i7UtxO0+<>e+MsȢ(B)r3O&; ~E1 ]ֿVig"\G=PhQVgA մp? ~`)f_ Yٞ iw("(=*C9jSKBsb2zR|ECrJB.% 89$a@HRRvͶӑ[ -(9I%G|3m)$yP=|Tݑ-y h<18=psjCKd_i.BJwI'7X%y#@z,5bq6%+RqEA!̀FOx ~kZLjW<]pSNY[scCl/v$[a=|AMI,u޸zՕۚڎ)p<3YSTGb ^#rݥvLmqr5r㋹܆*z%c0 E*UncGA+O٬/PVO]9hU.6iH yKqJS 11榋,nzm1 [QJAsw(`9j r)xlӷv$;k%n k$6%E:*Tp)B!J{A>O6ƌƷ[i-οgm8T.ӚWn%ǃ;N;\<)b0A3F Toʂ `I>_q\'(Wݧ JM\+d󒷛K3$t]B&](Nf N=num\WdJ:@[88U*_uVbwx,ˑH! ejVGu#kH֩\jBpQeɚ&+9ad%%|}MHC2MvM:JOMgI%\R[_–ZhSF 6܂Q:B|iuħSDeXfvDicPK\F[\RrMLH/GIMY5-~7H1aIC.^ ɐ[RT#Jq5jp:5ԋt?zFiscÄ2,SM&߭p[šK0h%DTp9J7ܚx/EXdخLٖ] v2aM+QR8Z}>A[Dlp 6\^/`yVv ;R PPdGגqRa݂[HH$XQreOaDNH'Y$lۏ7 Q-C88뚎Z^[B@m:i}=5 ! 򃐜hz_ʮLI$dv5cO+eUBC y,x{^JSzqCɖ9 q/?#{܉ Q]##ك$<z:tF-q^ i rjbFn# )Oh;#ʜԍ:ZH[V"a y2O+a%ĀwVmm 24"uuľ»B SLi; ^g\}JwނE`qo"Zr])g>i6=Bw(nj~6lF3-%,8A>A`gՑu1d^K+IuH#ꜙ+ImU ~J`te6)X*H$޶xԆQ}O+)wg5-? G.{3֚t[ f54"\II‰mDg)'GM;$#e]?{ ٖK+BYɐee-+{֣.ZeCes)cmV}|3mtXyBq|u$bvsޜ&7*_@jX8ZZ8}z q1_' *=H ǚqJpWMEoH^a='*=՚bZ)K 6`X|Un^[aJJN>zJT}[ITT}9x&RBW#gL"-JRX )ۯ.G3ߊC16ڔKuӐN8׬%2xjVIrrC1\t,$zfj]SvmK}L&΅˙]Vlb.Z>Wv{%%2sS55zjϦv-rpW(s ٍMT|S78EiSQ+ts};iݮM3p咖KXABJҀNFU?Vkx-<3aeԒA* 4 $b-ɕ:LHJ@³TM<8_n[7. ¸Jķ̷mY=䣐羥_JC="piŲQ`Ig|\5š:2diM.Gh U?Y}XvM#sS2. K|{'amn.un!Eu?-Q4p$L9_8@:*S\;Ϛoq#ǝXm!!)JS6TW|檞-]"ey:*q!;d VR L-\ }a2T:{dwt4oEm}W͌(ߒy#̓!^Jps I>5pcK.b9AK=4Ǫa-KU=Uaį9Z(5"(||R>_tcr+ROOw9Q ?r5JVkRT6RVT:pApEd ?HPJ渠 A}zXrs; Y4j(g}A?EY>+V 5ֶ[4>ς6-GW%*J$59>}0?MH6f`Ǻ'8r_ h  -QL3w>>}0?M{a-Lۊ2^q-ѰN9Nbυd{LG]O4%'+1t6/vq >:{7;oUŦ4nXF`!ҡ*By•U h 5T΅'~|SRd6WEWیIZ E^J.sTFIQ;ҭ%EN-9*S-!A'1qN3w>>}0?MM>{]ʳk\FBUjqk_(h Iǧ߿'Jn__[";1 0yVxNV vO]O4xυd{LWၱ5.?+7X1^iEUʲ9VEmZlj)"w1J>}0?M3w>kOQI٥Qz>ԄBZ[[q h6yҵeE2JJ A MJ]vf$t5]re䤤m]G>}0?M3w>5E;e<VeQhBxZbGvLu Kl9$.6 l'H篣]υd{LG]O5KWaWPDEV^)HJnQСnԁBko֒U7υd{LG]O5%&qAUQ)hti|.'`~>}0?MTs8`A)^ܭȫqlU¶R/t. m+s`rOu_^3w>>}0?MA6 XXdu5f7fJ]Q$"62ޖEyMH`+t=Ijz-rT@ˏd*oԏ|.'`~}0?M3w>_0gyrxޭ%vkū~r$[Mٍ Ɍdvb3w>>}0?MlSQ#kj7PִpM; jbnhS!8XQJs={BӡL[9&tWRW}yH'Wo]O4xυd{LR>1xJ)Yjkߩw[S\kZSaV/]O4xυd{LQD_y^ZbFlXe +YdkaPq%3|.'`~}0?M3w>֥&)sX0kpwZ|g|=&|.'`~wi>KTS/]O4xυd{LGdGi>KT n@;?3 >}0?M3w>OUH/ɏ%q\V9p-lS@9>}0?MdcPW*%Cq(9r4isyu5 .iJPhQQc&b HwĶ)1qڟuv`!REE42FC+mupen64plus-qt-1.15/resources/demos/list-view.jpg000066400000000000000000002033471413256700400220450ustar00rootroot00000000000000JFIF``C  !"$"$Ce" _ !"12AQR#3aq5BSUt6Ts$7ber%Cu&4VcFfC !1234AQRSq"ar#5Bb$C%s ?깯4"nBBM"cN`egs!/zUQ|ˈ1<=O}wʬ*k;O.#Ǿ(>**k;O.#Ǿ(H}wtYπ"q#sc";E:Uߺ/mgs>`eĎy{9|T:!>M /31<=RtW㬇\鶳D2E=GǾ*V_Y/W|ˉspq~d8|:mgl1sc9 >Y jvw36&\H/Ǿ(N©lVg1w[mV) ){F|ˉs*E2^ 0u(s''9\1z&,6nݻhgl11q=GǾ*itY.f<^r37ہE ڠ=I8>|Yπbxq sc9+>L5f\%]żqdc|ˈ1<=SFKJTRv8#>zZQ2B=GǾ*fVx M '1<=StYπbxq"sc-./Wumgs>aeĉ{9|T羯X.mgs>`eċy{9|T>M /31<=RRt_=W~M /91<=R.t_u6w36&\HǾ(H }*k;O.#Ǿ(>*_{mgl`efsjyR=drsV!,4Yπ"xq<=GǾ)3q`JShAY$f[G@tL.H@Sr>Zmgljmglv51;*emʊH#a[WqeMLCi )Ž84رz ऑ)&PǏ-0{>;\i79wϷg|$t)bmMTG*"܉EO"FhJr =b^[=f^3E5kk4A4k ׌њh5kP5`55h5k^U5*Y ;!TUi «!U*@o AT.oUh Oq\l%E?gjc#%q"Bq5)XTe6˝R"Dw1cOq.XrP<PÞk!5 Q>ZOwHiީՌ,*DYCB98')$AVA &jU7p$BmEJ(Np7nkݦ-EN& t+Z0 sq϶.O~POByXm׍I6LW,һmJS) -% [NF?4dW ֝P&6<$$`c?m]6HʳǹK<Ӊ:&$KPl%*BFԴ8fnGTƼ޴"?r1^apni-72U*uLmR2チ-Izѷc-.ޘ V1Rr ynw=PDbcfFmVO4²Xwz+-'bwQg,%i)P$`E[2OAԶI%Ϗw87f2w0x-I*$hc2niC )ehJR0>'1H64h>D(TUbcg% _DeP`;in(R7G]8])7zR=jfs̞δ2ږn*.x`]&Y۟u`)OlJҞsiiLĚމ=ȫB,lZVAؾG[ۋVŖ@Mz~ m6lOKB| tB#ǝj&,U!J.4vz$aG>:͹.7ӞrKuЗU%T kBTrTHVA'W}.PȶGjD˩ܔHH9'ǫz=KV% ayɌxRRB+vUQ:Fzɖܝ?niDNnij%$' yM8F׉:BٌSE!Z*m4BJxm'4KsZ"VϪ[aBl}Aikco(s[+TVg"R~TVݒ۽`/Rr͖lRlvs}}.#!,Hu TBLҕN7m^P -WK֧aTANHڰ9ۭt;kɹGƹgՂj;FzT.N6[ ww&+HQ)iZT2=F]Jӈɋor ԥ!H(PP2sUXOL%AC q.! ) QW' (8mlewR  I#TO,-$۝llFKTv e8RA87P 7Ѿu}>F7}n7}hX@oXZwV7PQnw@nXZwV7|Ղպ6V ViUcujFU`gdPɬf汑=汚Qk9n=fOX@zɬfhYf3@fhF&?Ҽ7'1H󻔣hIOEXv&6rPuM$@1/i+Ԣ2p8>oJt覦7G](])7mt|f]:>Y/tcv8q[l*-*Ymlu2AY3 QZj1M(}?RoS}?(ʏKKKzKLH㮻oiKqj9*Q($jŢ"4ӱ"#q#e mP0`[<7c%}?9eHH~QyoR:)AR;Ied1GTP[gv[˽i BK! N2IiUFETK-lFC(+ 3~AS )BjG7c}?(ʐJR!xo qmnB%4JUI84Д#Ҕ%)Q`TR}?,<7c5 W^t{[MΘ:ɂێ/)/h8eڭֲ⚈e Y3 QJR5'O9$d ֺ Od+w&X)PT`Dkeg3}~YE(*0o?4gSo :ܨnW-@qָurd[,$twGV3csW䟓g[niSc΋pypŔ瓴L:FJHLJO.uWy6VKwFRNH+h$M(om.*=NZc:yٟ۟^ P^"%He&jNJVvwks 8V[{W6m[Jy/<J೏^0q fxv컃5,B]mQt4vw$xP/[.C-[eD<> pR6v O񃡫v!Kj 5jكCvxqպi[6Q_&-)' !ޒIݵsc¢/xojuJTJdcvN n R+EҜ9aKۯSMC \ xpLZ*dFLl Eeo򥅬eIBVHY]#·n5;ތ4P R| 4}r(U54ZF=]6͗o,҇<<[ٳ9cI-> [mBJv lFad8YXm!![x'#:u%T9M/`f.u%R N|(L3 ""ꃪy:`ߏa2-NLZ QV08a})Mxn~`emm.Gmu8d-PiH2@4m0qf[ԥ+8ARO8ʀU٨lSڟ-Ɖ|!e$~G-$5k69&Yfk -b\\x>BJJI i6vJ-W@hhmO4o%)$rHEwn2ټmooŠ8PN9r -´Ԑ7BH `ddg#WۏQ,_,O9xꦫGiQ·!U>T%[pcC)uMTrAZPR FHZmڼe5 JҢ9YO¼; rU1v5*S$ PzCzsv9QJR#ij'[pbZ,+#M[CJFɃ[ݴ]RpZRT;Sd|'&9V7Gv=7=:}ʭZəRaڋ)!ݣjTr Ӝ "2m:'^-&)N03V7e4Bm}7 ǎ2D$s}L Mm1ndz<9ّ4I +1± $⤔R2ir@ +XSU^?(kl- 2MZKC9'f089cE(*BY*J^fDtF2qqKq_jAV=K(/y2)җd|5IHߘ~QyoRJ)AR7)ebGTPToM#ߎaXsbVAAWKۊaK&4F`%)$|'(A]${j%viwU6 ڹj %-VNs(QVyRyA'QDO;9(h&h?CuM.cE51z:1BIhoGZjfou_uG ݛXwkQ3bq7QZ<?.r?QE&?c:GmǢ:@)~/9~Dy?WE#?kv}mEOH?'Y>[9GE7?m|}rAAŠoſ~;q}ſ~Y>[9GϖOQj=w`Ҳ~WR[=nJn<}u}Cyb2%n,t7?hlj*Rzk9ܓ=9y?m|}rR[rϨmܒWI,7>K6Îv#Bmo:秩W~;[n?LRO>Y~[9G(edHZpmOōlͲejZ֎TӘP%c(Is$jy+i @ ޼RVp1sYͣk>xmEkW8_ϧ|k,y'9[y?UE&_Sh?c:@)t/17Z|/*a+WG_+=v> eQBot+'r=(]z\y%X8]YMv*h;q'ZR7&V>Agԕ۔$ q8<ؾgjԇ;JVi<ЖD(TUbbg% _D:OSHnt覦/G](])7QEk QEEPQ@QEQEF(F(F(F(F(F(F(F(F(Fa}52H1C{}-̓c#9QxjpnElyΞwEZI #&Hj4f\uVJrIKŦ7 % R%#rIe( e( e( e( eQ@QEQEEPQ@QEQEEPS] 9r5ڧ^]鸑_S;oLCEL_c55jCܥ;KO4%>*"|UiؘYCE4CAiv')qJMQZjQEQEEPQ@QEQEEPQ@QEQEEPQ@QEQECk}E1:Ũ3<'g"DuGm]ڵmN997ΏkbO6ŒˍP6ԏGw-/PٞmN]mZ7^sl(aJ;|j m9j2h6k/j'H9!q1<SMz Wq)p Ԥ701SkfOv}֝ݴi-M$'|x?oJA2u@ j弆CpKe [IO|}h;<7c%~U%cL-4m'ySwk=j-T:j:RmJ>kFL ')}jiq2c.YBvbzNJ.Ҝc5whB"PO+%Gӹ#5]iK9u;[Ԧ9o. ww8>ZIqݧܮrmsQb% Yu  >պzVצ]XROqo?{H )?0ekh֖{J"uT̢d9,a-<4~Jш/_vJqK+R@J{۳ d2f:nnд}O!H#Ό8VO ? PE@ j[\˞><=ClکGB~ شxz1OZN_o]U-m [؅ٍ)pxm_`:7fW?"qo%o% +#xH<*g[Kt_P_S+wM~Zw)N lOEZv&rPuM.cE4]I jbubғxEVڅQ@QEQEEPQ@QEQEEPQ@QEQEEPQ@QEUҵvKj$Cn(Xk}DgϮ[UjmNnÉ' KYK*TJۂ8ԥR.'ޮڦcݥW8rqm4I*H!I98MVo?ބ |n,BҀs=DNݧh[w:1wBVDiE8y)#mP6%=FZ\Os.%i v Vc;tdCN%9U!z+=(7F1OR;0ՈgL.xYk3!%j)J\@A98# _Xv9h]ZqLX1ۓ=ql댾q́()!@isݲvlϔu2!D|tgg#z"+bd+zhvENkyRn$u+BJRBs2}eUzbI7coqc 9O\@H=.wM)kw% jNF2p*o fݗF9嶱up)i}p82%8m=>m>^e(D@ T0Ou$P=j}"I.OoASd,'S[q#Pנ$Kw ';4{9ožmG{~P%t@۴3jo7k} d:JܘNAf$)iF79Ghؤ^&zɺ.K]CmM~Iޖj$^qqOKa,Dy Z!.HB O̮\g\pc_#W)0REiԒ<8><"ḛ[dH= n2}ξiV\s#,@MFnq7g*!یB}Иyv\Қ"Nmˍ"O^y_t.jzX]4ۼ1FjXv|Jn@nJ#&[\{9X0Ez^Ri, ZV2Ǎ4jN`[!SHd43z烜)3bkM7*,OqSLWRˏ6 qΓ Cnm S'$&Zw]i}A"*s*~" 曐vŭ*)A$s0iZ(܇ u%i!UYvʆE'Һ"i%+NG}5t򑑟}x|\,Y$'PYj.*ZLp 9ld=@:]n֝-mۜ.2;/!.Z羰~ I;$TIm[Gߔ9)e q@Y9ƙUX}S$ue'de7)*)I>Y߬Z g'JB-K*صrUFw@AzZ҆mX b.޺OcE]lj ڒI$kvޗZSq%%h[X6R@8$릗*F6>.*N5*lg*BArZnNjl7TL;d7 YR"V}$+В٢06IHŀ'|Q@QEQEEPS= 9*gOaQ1^]鸌 VP_Swm~R)\y.TQ⨫NJ.t覐}K??ML^PRo袊Q%{6<ۤarCl$p=Y޴xIQk:"ahBw-Crw~MjU}\(r5"Z׮f5H~I8V>Hȱ#i&ByeKW$收aCs[ŪD[FWc>cߚG 6OW~iIO'²j^lSbߚd}WݚzXlcؔA;sfǘpt>~j#HNG8>5F B7_BŘd+]X-/)vRq0s%bl62q*I Y? [pd[nQ_"<|=#cykCjPM^KMՏN;Qj[Na.%3$zRb?8_?X}!E~.kU|M<4n+~5&L=mjqXNzJdws2kt#HwK L-8v+]FK۷w9;l]Q\=o#ԗKȶtokk 1\*K3aw]z1#!Lp7W #ݟ]9t1nH %Qqջv{R@Vӂiۆ7/j_yaõ 9hHFHOiR.qeԧGmvVL)̐(a)V0 ɼEh=7֕ջ_{zͻ^eԲeǺITdBSG9"v֩1l1Fəʓ&;Z'`[RAG}d^r i-]hېG'ѭ#Zn[Z2!iQc-HRWm9901i_j6[3";݁iE*DA},AݷbV{Kl=xjC.: N hŭ4RLEIO4|АZz~l8]JԲpǯ9⫻Oؽb.(zzQp&C h)Y>$h YM; OHZ5% I%c#9lפmk~ O)?Eɭ#.:UJSۛI+$a hugI6m-!*Z Kq#rhԲ29Gn`-8JRʔ % P8.Π{|Sǖw{wX8'<"\ͼͶX+jыq-%POUe8)O(vnpBe-˄6Y \ZK06 iѪ--(CRX!@26 EEfJ^"LE*EsDhRVv @=rM6 !5ӧJs-}K~'9>g€vq_]L3rK ";I!~Ki :8!Y}j릧ߛoj;䔨<BV'+hPH(4b Qyͮm] vTG:+-%k7e'4D-9. 8aSH %0HF@WEPQ@QEQEEPQ@QEQEEPS= ~SXslW`zn#~/詎n O1c"HsisI愺'QDO; 9(h&h?CuM.cE51z:1BI+XmF. W~zr{>`p|+J-q[AJx?%Y:ѿ|.n)^@ ;7WH8I>ו % \/Q[HxdO[(eCU8e $pUYfTwRF- Sfteo=un9 R'刖S$|IR)k^Gʴ;HM[#tf$TxH֨TUN9?-lIq^ 8>Q^GT7! lI[zU=XV1^V2קkVǨPNsN/K,` >`I,mEYOA{HJRU`)H*..V{H#?wO]6(C"#[,>R9'np=O;Tә꨷{?1 k ]+gj>e=?қŲ?!@J#ڮT^hڢ7?i5K4[$`$+oCIs״15 zjqeDds񕝅ĥ.mq9ۚyPH3]*"ŏlO! Vg\ٓ5S֋یG:(! q)ٍF}qZ3屭2 %Ҥ $Rr l 3m:woU'O"a@-2wlGK+V3sK=UƳqg̓ɹ䮣AD-ؠnmPJ9 dT}iъ,=D9XAl:S=M(cZfv{Sm[0Hn$DmrVBBB$%J) /fjkҶ,]fv5NInia/rT ! qG;Zd#i&nn^]n7HIm+ $]iuz6V.[ڸEdŗӄRFBAsLs{%[=\znצ:-yTU%d2sqBM3>[1Y%ۭK+;g][Ch$8Zv#"=uشڞ |+ftْXx`]"YٲͼX[mjL KR +B$EHt拵ص=Pđ=Wb<$":THTwJ9>oH}JԚ!?*Ei@ H>gf5Ļm'Y%%"i`)MRP8HN*Mlr.7 B\ $\xxL'Pj]C-.AA a qIqd]ER.d5xmRqac=8;)$ګg~qjd(3oG)FBZ'MC[8.TAkfBUUN:ۏ&QJ߬^OEeոcf 8 u- ?@ %&K>2]Z+#:,J]쭑n#G=%'歍q)hKW0|Nj tx ?i# %zTT[͇ FLkx 8G kk[aF) :ڈ0?Dom0ۈ#a"D|%ZfUz-QrSqI=T}UrUͲGehm*VQ10う\S/PB5=q9$<)d#I?Mqd1yWb\B}qw6OQ88Pc>SuPhGjBrw4jpxL̺eؿʛs]^.(A-%o+ؑ3DŎk Crx=cSD$UU"^fPʞ2fi+ ʆ͖ؓY7tBXu%{KJqպۓoNP+rCFTx=i˔բ=Ř#XRI^=npB6M:kQ\\-%8%n>'pɇ]bQ"'©u)ZplY7mi;#jhmJG7Kӳ:BA |+WeoR"?3J|9~~8[[J(Zvzh̉] tN&W4,dПH?X{~T9y/ )&hivKZM_ v/miPJy6VeYjWK[:.k Sj!냎+yM8݃VbVِ]S<ꂖe' 17]6UNi[R̷Y߇p0\ 5+A2"ztd5nD#j]F9$OR;WM-ֻ܉SZq)yX)%K)ٓU;6WYi-?,ȳT&CmJ)%CUm]mQ*wop/G)Wpy uWkatKk3%EobvTS,`iuB\֢&<'zl1Gt'#H> JmV8p|WRt VJ mQʒ.ތ*r;-l {GRܜwW6!#gj3pdy9}ASipH@edJ-]tC]Fk8ZFa$ |NM5.6 ^|Ӎ_&ҭ!Є]4nH+h5$3u&MqdPnYn򐄀Va)e\ mԖ[؝Q%Wm5tWn6ɾM}/ꂈ62GRKm]{-ĔS^>5bMsVͨ/25K Z!ۡHI+ w–+ I):K7!}޵ɇ, .AtKH-I. V1Re:onUrC9(IRI#׻3m.F6ǜ~r^ۉQ'zI>`.7[VŮuǦƥ(HLt:J Zii ° mg[wEwmu?bS2 9݌ڠQʻ՗3Bِ"DF\,H]Y7ꔫ)*p8Qt:N$y1"MƐt| jOQ=>Ft\*M8?UqPGqZRއ3g}7OMf=+jY+BAOyKJ$c[k j0u0p= J&D q&C>$eHkR0]/ $/sw&%m}bHJA^UZ%ZIss>)>nV~OYrP1XP)P2gϴV4L{[f5Kr>soK+&V"-Q~yV IP%41 )Z⤞576޶ttLS. }_ۺ{ nj(A%6N-|[G˷&JLuZ}"*=׳Y 3N0VH°F9%Drb;ʛ^5S沅b6ƌ-$fKQq5E iiP\М%I‘`˪*ݬ`tItTA6:;W]l<as@5t i"ЛJ*+'iz棺.3{z\(wUaJ_])Y[J&2n@Sչ)`}bGÄ|?V h1WŧjVJb*TO]ڤ'KN4j=i+ex!DX!ƢlUHus=j2n|Ի7-P/ R:1S4E[$a' Iie*!5qRw8MWݠh/,[Y@mbmbPWJ55ʒ M@S ?޴hբ[9ACnhhN1$m=6o 0USVx?n\ӍŖZdfp{Vo{;MԴKMݷRA+W|wRA-י(l-[#Tmx΃#UԊņڽbɔ]w2ԹNԎTX#8u`~PjMUE,?.Ջ]T6–DCVf'Shk勴hT\!+=MH\GF;Ax”д[.:I]G@q7>)r(<Ӥ/> *i%.o/V$Q˥iKi)=E g9KXke%EPCʎqvܟR=X^Ѧ9!6k[T Ǹ)!O8Z#}6e}ͪKj77ACii!J($7>.k {lp'D-r6aJv5)A}+^yD2rx9WkӷKS.ifvhVˉSm.C$ @獤pvnK :nb qڌ2T 8shIJ@4fnƝ5lip (+9ՇMxFjirc6^RHøF>_ vͬō{B6f]0%Y 8S wSzÊVvRҩ;n-b) Q鞧!AG0kwtEm<Įm%0ގd.Cu(*QlGrh ! 5F8!w|8RY:cMIzt`!(!6GJ}# b[c:ZT39wh.3Ե$)²`X깍v+UYoHm-"B֦RdgxW M_,QSi@P84Yiu n=x2_mݛrooɟP\ XCjyO\OYm+ؕ8pg'@9QETjձWER Vyv1'ϱ?kEDEK?"HsisI'QDO; 9(h&h?CuM.cE51z:1BI+XmFM\*Pvj WX?%qZRއ3g}7O$' *6l@^@˪)iC/TXLVނΪr+iա`XUжoIݞS :W4ąYNI'Ý$nr]pEfDt7V^M}e #*?T- )(9~s7X\1ImʒVI⧺Z]U%-F\ej>6o[X6: \ʢ[l9$n?Wa~6ѿ[k縡}U6]Gt~c+{%##WFN驲T0@m- XIuUk99> %KQ2v5ٔ%.WKS0 U䨏.n6#FK`!^A"@Xq:LWҔ)RW5=n%/,gQR{0ed$ੑ_ڂ+#V PeҀF3⢖rO Y/Pͨ-1HK JAON?Hcy߃!a<x?V35nScXiTWR9;Z[)$a< qRT($PC}{I#3^"9tVoCFrmD$|C{pu4¸VWU̘Oi"iO%gǷT{ұ(lZ38U{kncztQ=zXnܵ@aɦSReؠ^iԅ($܂x>&uuz3Z)l]ԃ! %ƉRpG E ^i ,-bSis~)g'ۀf>KφNȥ%ڱj?ȭ%ڱkY#vߙJ=Huar R 9CMe.SV i36)nn$ -]lPgH.<'Ks;JuH$OY)X}HK/nJAp}u֔{2:~6g/VSPQaKoJpqDsY^r|uFb~HD@T%bHhj yBm_4Pݢ 2Tޑ߸\b`RpeBIQ}/}b5i65L B.- @NH 8zFѓ嵫'\65xDZdKqΑ۝ۀq>]kf^h:IڱCRȷV'^pf̲/эR$$eDH$╵-f?! BT҂mYTx9 'gxHp7GXDsW,KbX/zbLMzbދ\E:„-tUH ' O#@{ *V۝Sra:ۿC**kS2Š:emn &V)rhzZZ\4Hr⠱p2œ9!l)숃sso"1JV2S(8VJѐl)SOxo)-C}zϧ贻2&{H[ͶVvp0Uہhi5}Sj8S2ҐO59C;K} #,6XZzo^JQRRJ@V65Tԯ^t)-- yM$sRJ;ajQ:$n]Řyr֐TgfҤ]Ѩ[d7= \B%@PH xzuM%owcͥ;ZBp@# ̋\)rwuRGRĥw+j; u+$iCsw쳜đ9%%1Hl)JrWnUB2j^+ Z҄$ʔrfbb~E %nːڗ6I;*>_-jrHd$/{{=F#+Ц MH{DޖmN),y_QjSnDլ:kBAV되4:&9C|4@Roۂurukqk yBOgaoɊ.V{zJEɔhwy2nZ_8S=HGL`Xo wwhOrDd2P()lN@) 9@T]ЯrY]A8⍎ytdv(gH&,=MSbD Go%rB[PN(AۜQ@QEU/ڗlUTj_ձ^]鸌Ik|[Q_R3>R)\y2TQ⨫NJ.t覐}K??ML^PRo袊QUz;@j.O\xkҞzϫh߉>F 8*-nxA*ʇ?L⸶a(B}xrF7gf[-]Ԇ/:oi&L) TeNmI|r?eFp9IM? 5#Rr;"8 ʺ}){&_UZj-a݊u7SV,xG*W;H9)Ǯi:cʐG g< 4: om?))zGc<3PNDwQpT;ZxV~1xϰ| :Z_ji2:!sZYHj u$g>GZwj~CJ&db{8#5EhMt+'̥rjG̓gt‚r;?M|kGgd4Md\ smXNSVC[5GJpdݪZr}jm`O)V9Z=hJBa7BVJV=baoy?,!҆Цv2=QCZeM v;/8JS*$yu\o[we˂Ԥp 5ɱU]"%Y%izzݙYj/e(im{s~O bұ)>=Te "Je\t6ῃ!@ʣT>a_'UIhq!ZmE-VDE*siKH. f9!#1ά4 'Ϭq]j} 25Q56Ϲǒӈ(yk@p|ӊuz.Z<%ۅ[9sMYukxE;?k"jŪk"jŮ[g|#7~e)8o1;I_+\ۆPk y BJe'ou;r4ԍ? "JS7FiN^-kq([۝ ZENd6ڸwLj4Y$Du 0 Sn,wtjXovaDvP$8d#k%iJsܕ֔L}?:Di)KYe2K "-Q%AtxQI#S<(1ufFo)lAPf<2:MƒAVW,Yabj`s*aujc=VIR\`eDWripLJ)FXS*VT0 ]uӒ^-L(8Ӭ&ԆRYBOU< Rqe[3-o-7p*ꨖ[\G1TJ26$Uި.Y|F*ԕTlcxl* •il[7,J{H)d r-{1"'WiѓfȈ|yHA) TdS)ZcP9k$T1lkpy,mp4:久8U:-*ܻk77|JNBp6A`'k+ R!i֙ZV\PV$e *gcډ]5+@3mqi$.$ g989SNn:2lwoYxu-10 +w%nx[z [݃r:$ǘʚ4)j9ߕ`$`Q'Z5Z/˃$-ETBI>Vb?Ȑ5FiWHZvC^N V'f;KaHrTp*?ܫgi*DԳܴb Jk )l}]EDmKlL D6/fZujTJBAlo$  ^Xf1\z!e--%J# jo`>A# T(( Rej]ݚ`C8Jm%JY-MgO-c~ 芇F/LbIס'>FK&oEZw)NS OEZv&rPuM.cE4]I jbubғxEVڌ}4'ZU A?-vwhwg>TUq JHH\~CXᵩUTl8j*Uw YuInϳ_ReYCA(9HtsI\hO%Ma?m#F, jKgHEqEe/%9N=$h&g% /l.kzqmQh8[)H)13$DK:B@CvqEC[.Hmw TW[һ_5";h# qBAW8WϢ1ȈFܹλ>^Z|qeX:LDI$O̐|>z50F|X%pXaI [kvrf>iqM8[a}瓻OjUЭUZۓslEiZois'%ْ[rgbXÄ%.NHONRd%9Yн%+ O>+KMEW̑2\a# ZP.O\owFO pm_Jw^ǭxn=dKwQ9vn\ =(IDu<:D8E}<7~QMɕ/L\$yMt@h雝.sOYԣ%H N2}Gg#R[ȕ`';A],;d& i#'O=m~g8{{%]ӅھӖzKE|w/ J/Dbw#y' ؄m6HSDFR-\vdvX}!@֐Uv(Z)bǷ@\@5$\[k~ɦ^#M?&|pRА=@|=@Uzڎ.[=)ĆWE1¶(U} jL xg9rkު+!^~ԑ4ǖhgnU^N~ueD5r0[?>yxKMYl .HRR2cTr~w99q8ۃ A.oX[,V@oU^u=|ȷtXZ8yꀸ~`fۇ/L.KJҰsgi2q)W?2Ch:]N|'L@qYӽf; 岜,dHSN*g5֕}jAʴjT$Լ-xDOpjQx%p@8Xztir@BG#稈5h$&a@r9:vE,?.ՋUE,?.Ջ]T2$?GnTbk8sTih:W^u=DZC uN%`qH7RlMZ,ܙq3YBb;+ XP_0DsR\ YIje%NA*U]$ fSkyKqmB[* LlsYqм,ݵ)urD:U/6zԔ>6&Z2[qJ\=엙d288:^+4c'#ϴ.6ҬR ?hIwvRYkh]qN6:mjRxr(A:_٤Sgb\)fS/JR"ҚRP*%~5iF6jԑ{ʰ38y{9{;|<3m1ڒ)ԉRv6>ީݱJRZKH}Z:NzIȡ%}/JZ]fxTL3-SSH6>zKJۉ)_#h sK5{OQG3=~`yJ mxЬn8I^vH9.o3r63r v%Kkwwg)J*ګKxm%N%1:,+A  s6q7=K>m*(H 3[aV|٫d\w ~ F8Hn:zob}^QVԼq GNxBtK\y_xJN/%NS (ʼE2l٣R-lqe {gI*94D[#Җع\k[iz{6q())6ykhGN}>ҬɻO4-)HcjzUpЋzd[~Sɸi քe:(a[r@t--)-ͺ㈏ hw$)  v9PHJvbm0ChκOQ(̆\ejʈ´;dK jMyp^vԊf"Pͯ)WJFqK.ޤGbpL$BL6TRA$(  &yCؓO$>}&TwJ[@ z|dbi[vK+ IuH[y]5oݔ`A9+-Яu͵jָrJ%E(CHIQVF0j]D9gH~T䥠ݷ|h Nr}Mf].Ol-$)Zx8ݜqP}K5kِ$ ;擉N;9SnR$4n ;<Z ʄ KN+j qJlF5ZK4%Eya ť,uRs@F' :R,iGw*j~+АV;O# w%$ 4V/ۢujvu: '="s1)Cڭ7"G[LۮJNR %dΧܞwAP @--[Xʆwʼ8d WXtV6Ml%*K'DžIV$z@8fMlub4!B R #3 M5m<UJ =@ӣ;ʋJqej5]:BBK T_H*S~O$χ4> ZVf\jmMr)Iޠ-iP )7Һ;oܕo'.pD~Qd!ZWAV}sKCqrDTIK5س[v1[lN^)n^q+S Tg[nBP@Ye$! (*hHBɻFn^mmGŭ;(LtI) '?ECM ۴ ?JZ;EVDS-R3*.,[}!hJS%$OžJmPR|DfUwrgzhE>*LL䡢]I i 4CA &(aB,}W-0L9hpAnЈP-rTkxTOCdY8ޕETEN0#8ZBX)7n8d3LG :)c B􄚭J|J ?u4lwm̟6H.6pP@όCtE!b!OFd4"JO-.6ҲL+k˴^~촩^W#V Tǃ-7K̥JBe8RjI3t uKBr $|tG!Z)eCj7nʝ $ ӎDi !ޟM`: @#V*Koz>%ui!D1B(G5]Z4Wk0Xq427!n?Sfjg\}Tw!DF|~VH0bFvf$_;jyoNu J ;Eֺ"?,MmϨ{AYkrl<'sj- BpA"leuF-hg=L`4%ǀv~T,}XT R {Q϶0yVb5-I-hV -ȣu Iuڄ G4BIq݂ڷڷpr9kR[}M^rUEr;d7 =#cIQwn ;r;>q[&5Iq*d{>ogLEL0$uD<Ԥ6\IfE)6ٷWr|^\[$-!$c<]S:tKI@nTf%jP* ZR@9"lv{ӖX1SewQxtˬHRJF;6TTu]Lt-^Ap}=#>W#]/cz|&BBís`)SnTT A8Ͷ)]E\e3Bֶ\ܞ)q#rwNc>",G2D$ʼNx,:"HQ !Cvg:^aj1vȷ9YgHOwvd0 eo92FّHZP(JV@K_6k%qWIHy |F291D]Z?zIvƢݽ8G9vfӗ;lcW;j%!%:s|iAd2& #*aNInްK) 9*)7R{˱c) \u%Ԭ@))W A!Ziܶƒ!j*)-d笕@ޓXJZvuݙkvC&<pSP_ki} N> O-]eb^hyimjjRخ$1#ktR,$N V6ʞ }*,)pP 8EvoMrY[WEƜG*;w xҴyuz4(Sܾ͸LE Iq@VrxWq*aX2vU}Lm/I_H*()PPRI<)XA]i.joӷwܫt]% JPBIZFD ֭!a8Jzz#~Q)x4xw#wh붔ϒZ6IBVJ#o,(%-eܥJ"HM%m H Kn3^oRj6ۭRlv6H.;ǣgiPMIɉfsv ٍ-Z?i*i`RxrкۧB/؝ <72C\@Sy!l\1^dyS[mp$I#'h,lpMq:bm1YY[ $٦olO3 hz5M wpq  $!Uhn=nSnD(a(p*pH%dۣz.kb$.']YLHꈐ9(e)Nѻ !;Pj ̳ޕ.n/NmZlvjrj%t"(i-KKrv!X )aN<R0q^0h aVUUK]9ڝq3$Gu+vcZ KlZef[ ɻWH/IR).-0Rg¥wҕ cK]ы-:\Bd!.KjX 2B>0^7i dv\S(TOSk`` Q ]?as7ڥ}.V֢S>$w*Eu|D#τ!̄cTKia' =dfLhv1 }>1r-АьNF%g%CnsHvsm BDe"kːT(ݴ$dP :Ck+O*ŝvhmGK6V܁6tE65sTj1]oN\|ԋUpvM 7;M%~8Q +Cnn| e)1FaV%n=qNT5DDԱԥ-(&C JIJFAh>((Wᛟ+lUTj?G?Wٯ.FD_R->~/^vDUwr.e<КD(TUbag% _D:OSHnt覦/G](])7QEk 9THX*KVyk xY wP͘pH8R=G?5wokc=s͎o 17aG֞ѐ#1m]u9jlUf+ gv=E}IC$JZu 6={][5~u< m #o)eWєL+snm7jfT6i''?-1\Cc2oԈڢ&1:Q:ٖu[e*eHQ"羌NsL6k&ݰ3O6x$'Bx>VBQ}ʸ!Nng^D'Y[['%8BPrϳRm\65! ?+`=ouidU* J$[h V2iXEsa3hG42ﴢ GԫSD-ʛ 3NHQmbr[ZޞqϬU5tpHZBӃ5ZVș#6/RJte+*I Ԣfg^L<ҧJg79tVٓ/ZU\  ?/⦷='cn]u\GM~|}hyP {qPH"$$v-Yo5$NxMtքNվFǤ*lV^{A(jԲ\^7 `-a@&9ҽ"tISM<^RN Br^Ҝp EFvotm3U 8$ "SvלepmmG\[H# %G;AN W/JT-$mj1/R9*Sk%!+pQ%Ŷ#O-p␐mNrq\h*D|{zW2# `+ۂ{N7 (^ k'gV?iYw#E5lpGUy0Q)?zJn"򄰒n IPXwNS&.1M:$dUZ-O)H0ZUU6M/ndɟ!BbmKCJP |*:Զ 7ߩ3'-e%K)ZԒBV*tU,T[Rcpvm$^--O/Ie6W 8?Jt+E_kCeM2]󁳴$_U½(Я.WBڇh}MRX]}MRXY#?GnWIeV{-gQyWWHi3ƒ{CIJMp\lCmF8ݑEk )J$95YdCV)'m' .N!+S;0R yI)8Z;LLviglU>Øac%  @kIs?P[/ڵ$RKx\Q./U$`aC[NY!?zd"LX^AšoNڠ Rv?Li Ŷ-)_P779NTNq{9Y"ڻޙEKm[|u JJx4é]k,\mKUI*h”B3E ِiu+krVj"/YU:%AjIoAϮ1n[fdup]ؒY@u=@I%%m>4k~b%SOO{gO)Nw'IWnlYFnmٸVRz` O-a}r}>8nGIBҀ*#v+p3+Go+ԝ<-WyC0-zZ,:j-F'#a2^|eP庶!-FK %亲TTO0*=jJΛLZ߹*Z&MQ-<˥$B ݒR%}_ jmMMzlh k*KCn9NT;r)GaC Sq6XS4ٷyDmTf${,(`wRN`.܍y@Rm#ʥ6m>pk w e  vo)3[Z[ R[SC*ʂUDp%PޭMLݖ =ٷu:!HmDk nܳC25Qm7dOtBnp{'.͗ubkb6n:.O1^mH dž8z5U<1eV*By20ۊWhʙJ&]%mJi-D[JBύfgh3Y-yR{zvyiڈQp V"GŒ1"Գ-M{{xo+/ s k9s!̆Ua!cp%hu`RFx4hvqԱmWKmce AZB܋(mJlwA)`n1,iBarYT!%J?HlȺș R#[csrVfSjkZ6HX_CRmPS+`F %"r.zfDȷ4N24E\;;9hۙ N!qK nw¦ZFMimqhRI8ӥEPQ@J4we~Gjίճ^]鸌ZS|OQT_R'>/V9SBk⨢'QV4]}K??M!&h?wt?E6cʵp3ch JR Yʱ_L(21+۫jf$LkFh]G˝C pGA#>[6E]-N>넗8)H寧VV7s+_MO Ft\ )[y=XV详Vd؟c[5ʼ2 $cJ[f&;J!K},V ,&z[:)ɭL zϗjˊ)cn3QSV݌Uj"QZyHLkF҉>a6>/ڼmzsQ\I KE_"ҕ_SQI/}P9MZ=ۑNAڂ}I+<}|5,{&y?R6cjҸJooy4m +v||F+h6mX6[!6RrJK̸ NVSn€'2p9=l2qGu˛Vψ{3ƢfXIwi"k29rlwxmqSAIJgzE W)iH-AB|93 veV+# ގu+5s9ɋyVҕʊzee!$Ep=(կm]Es6#"GD@%r>uӭ8GU.URz '}³BZ%7q,tĮ%r!2n߀e[q)Xy̅c~Ï0}VFFZ%wq"CK%lrKJjWҺȥ%ڱh0obPg&6MeUeV=ϭLibs]؋] a&[-x9]n쫍L󛝽շ7 T@{v{Gհ,wI.Zڔx,bKIuO0+8XRV<rTj]GQ5#yDkBe[tę.6U8dm01[p"[BQRNIǯ4Ӧt7sk@*k)iʒ0OsQHIsYۖݺZc(R&JQ% ۅt0| ֫hX-1nbZߝn֤ɜa֒ I S 6_7ooδ Fʛ%+ېV$7$KѶN>7nk]5h + =ʔ-<ؽT%CBr;y kT\hr VrBlⶆuZ;R# vz;gmQDB-UMm[*IaDoTFvOi[^A [6T{>ܮ׭uXqJRiJB2j/w;7nK]Jl(6*=*D!ixY"ZwSh -@+K 2p֒y$ m]Xܞ7g9m-ĺFFnQ#q#lvb*mQm BbB֖P qRZT͕&b&\mThc ~2 Gۡ!Aswj^BcA))@pBB10f jmF^nrÉy#/IM!AC $aEIMa8w?;\a,5n=Kn+=dP$rN@&n:uvƷۧ%:Ԓ˩e}+' Ǯp_g0vDw̛".Jtڣ : oh ]\nszW! /!(ݣ `qZjBΗxƠqL*P+vC2sqj.vmHq]ʷŹ€vpuW%8emAD9`%} ͦ{5IvRmadRr)ܵ+aYH'‹bbܮ2뮭h>wÀ!gֺSh6m\)Sd[P["l- *&¤>+ZIZm(/ ac\R?hzaڞ3VIsb] .I@+(#Igů_jDınZm]'x8oKC˄)=dŵCi~[I]ꗒrڲTԠxQ`ȿkGV1VcDۍJJP iCsͺBa꺡;V#6dH弴JלPw-G9ǭЭ.yMpzD1ĐV-g=Â3R %.j,6܀a-maa)N N< erPBvXҙ4\d>]op mX1I`Gv?XC-(b]6待?9]RKJBҠ!XQ -#eMKRUpn䢹.J@KED:hrMk rby%,-J@!$=U\ߧ'5S}Sr-հDӰ,a#H<=e鏢2nc[k[qZBЮAN#$(%0tS3nv-I_UMy'r+'y9Ji眼nL:BD qc!<:+'ǁG_5mաrRE2LƢJ-6 e qZq[vV7Ifvxɍe9oA(q` P\!& ajO:(uh 0IdTHR0A< eԺ!eqTYmtReJy @ AN0r; ;7k5x-a’ԅl!ƢzͻP4#UXӈiMV䍠p <M#٥;wA7&d&>ZLz`-*JodR8KORt ,Yy"u) ZJG$Uks՚wJ>sq:ɸ04,r%D}S=Kޡq[zPd%p068 PIgI3L~SkvSVZK7~Li=-FLc:E'[kS-1#(*nS^o.ejjhSSc7Ӂ*7># %5-l0;JQQmCFI Q]oĻ[q!Z#f'VBfm%2ڀ,(3[{jlWM:t*: ` = "_U'eVm.ܘ;TtQBC}00) $$>of">*$X[S!}5/)@9ȯliKղc$yZ=w Vy7# KNv-TvKo6ʹ[7 T,к[^.{rSHgkRP[HPX9C`A0ٮ ZQm!鏺pJBRak+^̗&=)!;{RFq9-Q>/_GQ/bRYiQᐤ y:EꋴYE<@L6T=ARBlҠ`~5-deQ/}hIڞ*#{=Ӕ[Spn.ESKnS0/PSǎіڐ[{ژ۞KG(+ޠ0BB@Un麞ڢ˻j#%Q! WBZdnlw|Drp@y}9RN& S>Bܝ}fʆJ9VswA߉=7Clue D\;U']c!\PGķCMmUr=TY$͛s*wU'-mGn+酖u[x}u7B9ek }Hܮ~Q;j ʸ vQ )EL-_ S%T$'51d9 ;7y0 ZBRRiݵZmQl ]x)ո_NHCm+'B m%EUWAu4%qeNTX(>ڔ G+!Nr Hlٴ4$H&"9%qXJ- gwSyҬrdo6\e۫BJ7$i(20*C@QEU)ڏ_fPճ^]鸌G|oQ"C|oQ"Hsis)'QDO; 9(h&h?CuM.cE51z:1BI+XmB( (((( (((( (((( (4u0ټX¤DDS=58䜓맩QcJ)Q~+͖e!h#%'TC.r2VN%ݚ!KK N֑4+9VIz*gZÊլC?%w iʈY '@ nzp1jcK ~"a# A a{sѓ]s̘P BTBRIR@J$pxj{RDgPL 5v&]٨߸ESN>oq IA$]n_n3ڡر$f_T$jmжvNyc3\>lZ !Wa!)B@ܣriT3ܼ;OeNڔK[IF3@#1\ci%_hFlej;_GQpFVp5#QWnzeQaw$[dse̩!.2;+J( 9\"k?qn|#=dncnOktؐӖy1 ".mh m$a<Tԗ+̸LiR"]۩r|aFN;cAnn2g^e:z_Dye)з})J2Tq@]GBL10uo @ZezSH-:oIƁ`3CJhm%۝тR@RU T暽]& C qVe!:#)N=եJYmҠPn(ZP7[{i7]h)N٦c-X B+ BA<</Amt86 @\2|ٚR32-_]\L+kjԧZ@d lH{%RI׽7Qnԏ7 V%JN PH[1;-Q5 m"JNrw94M%םnΩR9'G('iTލbu fj-D3r `acSF‰un#:ڠ̙eCm/]JPn8FҨ̍3fM: R⍻Is]jm [qE6!v@ϮmE+P&ͮo$\얨c$Ǚ܏& qARTVc 5r@k&aըzC2c4 BU*Q%.Io@≥HbӖ#IDƚQ%$C ,O>&\.ܡF Kg8)P E00gde}yQԩTe[o RTrNje@ jfj0ݢ$6 $1o xG/Q֮95Pן ӕ}d4W=JfFCnZmN&Z\BZf2# a$>/@RQ(Fg.ֈKz3@C B(iJlbŢJ$lSKa)RTꂝ V  -hsKivе$R6Kqqʹ8<ҋ=fiMY0-ͨ$)1cBFHK= 6ώ偸דZM-vf1- m)9(rVl9 a*. !dw v;HfgS~BCn<@h‡n+|F"Ee;Ze V( (*CnV]I7{?fqEJ4#EEEJ4#EZw)NS OEZv&rPuM.cE4]I jbubғxEGa~k +?0マ?EktomV?ޠ6Zdgvculϻcd?ޠQIO1~f/KӸX-_ߠHwOp5W+;_)Jv+)Jz{X^2_h ){n{nh%%q7̗:7̗Ɗn[2_h[2_h){n{nh%%󨬑/j[!019XZA=G>S7to|-ߙ/to|-ߙ/ѽ~dѽ~d4SwF{GF{@8M wK wKsѱ7:e11lST^[KjAq ) V;y9wF{GF{@8M wK wKE7to|-ߙ/to|-ߙ/ѽ~dѽ~d4SJ~{Y^3_hRJvEӻyY?_QIw?\+O͕Ue_#0 h&glϻӶ_ݘhzOYhzEj#6Yto6Q^0マ?=O+_Y?QETjwsjO9[5̟*Q>I*.*Q>I*ԇ;Jv2hM|UD*ӱ0iv')7_D:OSS.(((( (((( (((( (((4nG3뵾Չ7ӁRTZNp~CY퓷 A;b?oIar~SN)ƝiP>4/*;R:cPNSޖn:࢖Ң} Ph)rw8a t %Ic#<*>(zz>ho +%e;R6gpݤ}z5ӹJNqciR[EE`v.^* \,k,BpALv[BoX14PwR8#FyEEuh+A5kjZhn8=aXqg=Z/[9oIuH%Ҕͬpx O>¢~;Q7ExS8\}RVp 68{Fg=4ιcD.2,8XN'|= H((( (((( (((( (((>?|lTjwsk˰=7?TH}?DT]?TH}?DUwr.e<Л(TUbag% _D:OSHnt覦/G](])7QEk QEEPQ@QEQEEPQ@QEQEEPQ@QEQEEPgR]fԖښ[S#ti*?GxmՆLf_`/LF;UL퍪8D@32=^>(w}/];m oq.ŽM-> m85( v?VS[mdm jzW݃x2tKڦяUvM@ 6o*ed69V{{S Lx ۝ eQZaf \9`(H.6/fj[e2̞ ppz mqzvkjXms-+$# )I `pza _۫MDبy)>*NM>+F--&#k/: ʔV0IԴ a4\Oiha:%`CE)nz@)$X1]K&Z;NoW*6tl! 80]vwkGMƽ٬s5qҤ)!`{<(*S6Q^ôQ4ղd)s Wu'rd c\{h$YwmS^Q*G9W1okuՄ+rZ$h fՒ͕vFKmM.c 0(aI-i'#Ԋpʗ!hahGTeļM~5h:ߵXb!6N%;a*==a?H1{6sL-2Xz->Ze`#5&Iץ+b; `J=[Iǫ8$4QE (((( (((( (((( (*CnV]I7{?fqEJ4#EEEJ4#EZw)NS OEZv&rPuM.cE4]I jbubғxEVڅӬHh)1imE'5{3&?[K>ɍ>t5Dm~imKv^s[~W\;Clr;Blr?ug;_!~(TN>+kO^E^ EB-_)+蟸j+WmC . #Wv*_ԥx0<~GP16Ur=':vJSȾ,@c ?)הAlW3ө7o %~hYGj=- R/N)ND0 D \~7o]3O~(Uw~VǯB p|>ҽ>Ҍc%7 +0;{.Ҙ n7U{Gru_%~㩨XڧiƔF+ _d6}RPQw*SE~.ȧ??qW*пܣ-v?OI}Wy!~YvϮ zsL`{AJG5rTxq?EpWj9 ʎDTe]GKa.T[* ;PHh:ܔ2R}RuhuP1"C\Jբ UEm~˽{JS~R$r=Tp):ՉlJ)C^Urp=Ty9MØfDrW*U*Tcrez@u64 k9&TԆB_ l6v#2O2xг|]cWj0li Tq㌟^k4Foߚ~ZyʳM-1 kE֤B#6!)p #3y@;BioO[ӗ)亘D9 FB\@ AԒNеuvYڛT52 D9{p!>6pNpRJxNz> njۄU%*涝=`"86;ah(YOm`*AOvu")P[ SaC͸mm- TKZsϨ-]L]%dӟ[7f3k`u ޛX>#|1E )vmIYFyk=x4߭05xf~駯-jK!2y zl{Y$7@V:CmwzMS椓i~ B 0J/HJJJH'W]k+Pi絘e%zcBh.I's{g* 򚼾\y7RP#o 8|+,i=6a-Z`Dm>J0pی맵hvBn?9;!*mLv];1Kv81oFЩ@T{W}Ii?:Uj3$KyK}+ZÉP 'R=!%5\"Ǭ8F-b3%*uؽUZ Tm)P$+JqG, mM,z'xbg5uQ\=)DSMe VNN4>㪵Qww[nԑ-: :aQA-%* 3Kh+ M|bn%M6TF tNn'l'HetlF17 UHp{=b) 90sv◀J*9 7)Vw9|B4mBwmcsߴҘQݟmr^HHJօU.'j@ ݀ɷQb}D\i{D6R 2yA+|jjCT4[j";+uS"XqPPAXA纒\dihdZL qrcsI,CIٖ^nS]1`4[gAڑ8$3Ҙ"Kq:i!kQʔqy'(( ((8.MrGQ@)Rw4ׁ K~z!ŖE>*L,䡢]I i 4CA &(a{E'ڏ W#϶7_}_Uq-#[uv0HاϴI5N_LVi^v$\Hꌁ Odשm;tqm꺖[ԥDdUi+Όm/˒AFρ\DJ"c,9g ϣuZNuHP"qzU"ηozYjJ\l@IIㄏ̑&Dnʖ⃊v$}O-CrT} UQ p~,ƪÚGZ+Z%UQөG`VgȕM\)NH p=~X5Fw:RxZA%п($a'=!(sɫY +*kQ<]r?XQz.-B\mIFR$3Yl'b+KFi.I%PpwqPN5>ioWM AK)NJʱmv* :*RrZۋPmaFWWrdgj>L\\~CV `v*|0FG"{8Ԛhٹ^]'us,!)).9I^ovSmk?Rj8ѧ@A82NI ”pɠҵehi˅P=>zkK)p5"fJ2H|7`bvg.WiCwJ[z(Za,wPJ $f3K_:OĿX]bJ{oN>" ];p=67^vip'ZԫڕKAϩfk]&e,ЍAۚ2aE!) @JO֊ /X;Ŋ/d8F2 RKItRPwϫqzQ:Q^n}C/EҤYq1Kg^*$PKHJڜqX' $s)zJB}|쁧A,9mͻ󻻷)5 XJEiW7R’ya)R\$$S_M1>y2 ^W-;ҳVF 'ēAk"?X҅)* NR ҪRZwj X rUOqY@d7::Z|[ 'ɳNv+=UekxZ ؤqMa@q^u ݧ첯7gx1>mNTh .ۤoqiy3[.OSmʶ a(xw/ەdyg4FBRS)*(']Kے#=VZ3[N+26#xS94QZt-n=#]SD  A%':9ΐwk=nJrK)ZQWEނt0+tlKY/2eդǒ%)2ͶQҮ1b0ZJ@BJHVhFk6OZe3oo-Kh4IX)O4kyˎcN'ZsFU<n&:K{ΤjӦ/`af^r˛ ɗ2YAy6 J@ JRR S )65%V'q8 x- 8)Qyo:betڲB“X3/ (((( (((( 5UEA 8I*ժ[|nFL Pj r1* gK~zzFщka)c3ž`~z!ŖE>*L,䡢 Inzȧ1C츭oB~ P&ؑU[+ND934T tw^>k(vcJ'ƐC\+=5'= x*`lg*8+4dhqڔs.jv+ݿČh#H-[ILhkHZ'pNqπ:X'fqi3&a'6S|j|=G*A5yf Q=r9Vr*-U95GK쩴]=17h*ˬ[:TH;fy{6pR^ʃ.xnW楛SM-{ZB]dE`RgO )\:e~#7ǂB6pQ H/(sS#2du:0Sz3-#ȟDmg櫧̓y{sG}U>E7:ShKV]) Qkզd{-jxi;d\QX<͢mrmB1J$e|̓y{.$C{BR4T•եJ 2U c H+r}kv&t'ؐĖ{vIfʺƥ(l̓tiGTDPda%jWS]SC2[ceoNTORZ7Uɓ NK@6 sy{6pVl ĻuU4ތpl¼[e8;$sՉ;U&EnF`co^=G+ՙnF}Zꢧ˂ӌ=Zn/39$miO+4̓y{^ʃ-a:j""SDb:ш+@og]RN:h}m =)Q˘ԱfrMɐHOQIR NwsW(`e[ˉ|O>rB~ :vr@Q 9]!:nhձ5rf76DSJ@$h1j`l\FρVio jn}pYla3oq%!o\(A`8Aפ#;.u;v#֗[* wz @9!Y <=G)7S4]ml(f-Vw-͢SSPRQ $3Eڵw +kw-z;e V2S6pQ l-m<< 띦ѣ,V$ۭɎ 6*Xh/q'2yQ<=嘵վ~*)ozǽ]Xl̓؛ˈ-M^9C)p12}a]L`Af%܉4 i&5fqcONSe܀s5`l\F>qv\4 E)Nvި^N3UH}}:1Saq,(m8R\lIrpwI|=G)7ρh_/xnFT4TaԖc6##*ɜשvT5xKac-ƔVRܡJRw̓y{{q{< ]]%h%J"dŏlJT*s %%x V5 :bu}XX< KN7a~vqސLL5kJIIzno~ym81+a {Rm.pjk <=M|֘œ/HdS2crGx:{?xvKqajhFif %iSrCj(q{'0xy{6pSbo.#oۼ(qwQykCCzʕR&GEk5(b"] G݋) [޲z:7cAO<=G)7ρvnH-zlGTRKKkIJ.{e3jvZҷ^.[YT632*XI) 3Nl̓؛ˈWM_O6pQ πc>?_?餞l̓؛ˈWM_O6pQ lMm+EXWi'(`6&6y~G+4̓y{{q{< ?_?cI <=M|_M$`l\F>Wj%r΀<~ *E <=Q.$?S $pM:y{ÂG *Z&DmEQJg(nrT֭mupen64plus-qt-1.15/resources/demos/main.jpg000066400000000000000000002267621413256700400210540ustar00rootroot00000000000000JFIF``C  !"$"$C+" [  !1"AQaq#23BRV$4rb%5CUcs&6S7ET'uDetC !1AQaq"23R4#B5br%S$Cs ?\H8&$W.vڮ>eHaYYSiY$.MQwޣָO2dc)~I&_%;!MmwޣָO3O3}=iw$1L%/i?~RRmޣָG3 ]~9+?K!K8]#֗|Z_iR1vL%,,v|Z]=k? =~;3?KG33}=iw$0L%/hRRm#ָ?33?33۝=iw#0vL%/hRRnw#ָ?33 ;3?K!K8]#֗|Z?hR0vL%,,v|Z]=k ;3?K?33۝=iw#0vL%/hRRnw#ָ?33 ;;?K!K8]#֗|Z?hR/L%,,v|Z]=k ;;?K?3۝=iw#/L%/hߤRRnw#ָ733 ~;3?K!K8]#֗|Z?hߤR/L%,,v|Z]=k ~;3?K733۝=iwѿI_ٙJY Y~Q\G_J_ѿI䥐.)w_ѿI^ٙJY Y~Q\E^ٙJ_ѿI䥐.)w_ѿI^ٙJY Y~Q\G_ٙJ_ѿI]䥐.)wѿI]_ٙJY Y~Q\G_ٙJ_ѿI]䥐.)wѿI\_ٙJY Y~Q\G_YJ_ѿI\䥐.)wѿI\`YJY Y~Q\I`YJebq\HʓӅ#>Y Y}q\]t?{TCBԈ 6=p[[b]CY ;JT ǥpV#"CFSSk/73v5Ŀnp-ѐ2aqB$xQT[4-О`an3( xFE@arOΠ*JTQEJ*TJ*TJ*TJ*TJ*TJ*TJ*TJ*TJ*TJ*TJ*TGL;emUx3%Ia;PX k=HꗏX=e]4h̶ҭv}Ґde%JZIY^=[^P&[q(I-ĥ+r˷DMi+:}WygfqiP.RےМϼ[q_)םCTh\pw]Nݤg<j}Iz0UhpH#iN 6!U_=LeJ z+NmFRJph>2eŷj49-4v1ͼ{ [e ;qZ3&ڗ!YBKALd䒮1Y볡䲭99BxyA )*K:P4DVXl9w7aRE֝#&&-;&TVNnR)ImXڜZ0G}.Νnd4*T4(r 'Yt9ҡ-nc=h;ѽ+=@㎃)Niѻ[ƤNNJ"i!SmQG w8*7K۬f4q=ܷN8p1FM"NiE?GSVxfmO.}LV7@/|CZlXt>T/rꟶvv#ϵ[6䘏K PZu{#T:|]^D`ޥ!З-q~M%wj/ir8/|oVAY_?m/P5_M[]$Fm=>BBN ZX>e.<h?~_9OE;5NnӭZJl8mJIJ]b_? >T]XsLoPDHy#s%GQ|$Wf*y`L+CXe>cRp )gb_? >T$p2ISN%2\yU]ZHVj*p`2yh.,$Hu~#R)9OK}G j~)uLX/Q –8x`1ӐNTkz xޛB= )(x(ݷnzĿ)uh~9OK}G iX r\3rS!6 +NʁA X>e.<h?~_9OE-5v$Kro1h)RP' *m=IĿ)uh~>Tu|%1S:iR$KH9*s'L\,))[SJ X ܕuX>e.<>T/rꟶ`~rd{Ro8p 'RvZ^T3i}1oyK-wہ;2rW厼RG̥ա_/rꟶEAS ktgj%avۛ)w;JxRr|o1uPPg IQ\G̥աC9OK}G h>]!ZK!(DRG̥աU9OK}G j 9yϕr$̔,(%*’Jo[WٔK} %)D' +R!$X>e.TQJX>e.<h?~_9OEu~#R!>TQJX>e.<h?~_9OEu~#R!>TQJX>e.<h?~_9OEu~#R!>TQJX>e.<h?~_9OEu~#R!>TQJX>e.<h?~_9OEu~#R!>TQJX>e.<h?~_9OEu~#R!>TQJX>e.<h?~_9OEu~#R!>TQJX>e.<h?~_9OEu~#R!>TQJX>e.<h?~_9OEu~#R!;d얘MCagRG~>Q9FYO6jր{) !5aMN^YKhŚNZHbZm|%$xʇG,]X|^3͸ ٻN73ztԖ3m}"2F1>y=ФN(GPJHȸ['$NCQ27+MʕҰfsl_ mAvy%Q#G3r\%攥aHA`\`:BzW_Y{L0z_m)wo(BݴMf.ǖ wA$eF㭖TnCdBJ1uMl!d+JÛPMYFG #$+##VG[/ܗnisTfOrJՏPHI3MA%\1|Rg+wOle.B̌\ihQ# ʤ,ׯQݘGNǝWZQv"܃/`RF v^ͷշ!) uKe'xǏ>^`}4FpT}M;eaL&9-ijpzn\H+ߍÑ8X >ʑ*_q knaͻVvҬftMn :m qlU4R[*JHc1*pNIv}0[݄!Ax$bJBd(yҧ^L *.:lAPUFhhQ"e,2VWZKhi2՟RWc';&:74؍VkƛkyC}618ssUiW꘷]㡧6i0&]O%ˏHR8WtB~EBꨚl\7x0*"Dy6{ 8T^Bʒn U|"vle[cAB}ЎkͯLh[afSpLM؞`z׵Qkx"[e}n{U.QuB64Iɩh&4q;T -1oKMԕP!HB O5ve b~9gGOקFĆ;!;=!@c֠pH8_ch!.ӮS[P%O'aA'' 1d2qmw6KK b»а4r OkÝؚRC֛[l^lRlKC '?RPJe]9Vt1MdǻCĸR-B^9<`686jzWGC=+shq>:ޠJZqkԐP z}uW ʹjMlnnvnٽ;g8Ȭ\v6h;; )8$pOPOO?fcz 6+ZiLM&BZ6{ݭ-|9ߜҨ,]´8,aDwgm2#{VТ2x<~Ͽ}Q3ԇ1]I[Uox\V\LYi[B$#t%(@BF=a(ݟ)A"Ϫ2N?1Zsҍ6OS*6J #>u2!KLmE4,GnZawgoCL Cܑ3Ov^7' ]\lFe$8t)@vMbmiVkm3'G[^}ǖIi)9;sztoL[mcBn[ RV3L2_mHIx_h48vçQv%:I+>c?QjO4vƛqd TV0G=k[%*8?fczf?JeJ9 e.] J#n t7t]C`$*#k% ]4Bh%;@ntиw%m6`׋gjz)1هt = ot޵ 9 Ȍ(S3:j@)O_ 6G=}[Tz T2U3en3('?ƙgY[e<'<V!"rDzcő%*kD}v' TkND ]j2җP[ސz+Sk֖t 2R=HDVD*oiV8!*qq m UDڴY a}P OAQS[i-9i?/Ö-SzfsW?AOdJX;P^Xw4i@ >j汵,0!?L517w*Oa"JT4u L#8 jeZМ<OMpKRsGhhkA٧GUF2hBH#"}jH#{rXw6i&*ՅXUo,8 M$&9Ś3q8ʷhn)GТxO=P w\Ywn3m]j# +6;IEk!jv{lBQCoGhB@”B s7\ 1#r8#̍~HdŹH\lսek#C;O$t˕[!q-BsNx Za{ӕu2b̞^m?kW-y7۠";{JZFI gs5UMjHs$Û{j!I}##髭kvH8[dw^R=JR* 3pViv V>ݛXnh[*i\R5ghQr]pVڃ9 =xGeI.IzjΣ[7ح˿/o+7) zZlX ( iq{j ynTMmiKDJ j)̷`RJzr9g)kn㨵OoT\qm7jt-`蘶kO??'q?r٦oZSH1zծ2P f.7i)')49ɅxáGJ@Ei}܂5P/ $bԟcuCSC,xARgp1ʰ+8Yڊ>bӨ"0XU (b 'bPOR wg/K2w\"=NJ. t !ѵɍIgoQ[0rFNx2H@JTbDOD%,MZR HO'>fYrbmyAYx4=n!DV? }H2P_P?m@@W&%ז¾C 7!r+uJB\(V?9i?U ^)ܕ֚֡I;\A~S2Vl>|" CE<q1px)j?;rq~qJ! q!_v*$ܯ.T6 @#eH$&ZoKC|P$U=:IۉJQVܤɢDA(댐sXʜ)!ǞPSj-db/~HxQш8=E ƖQJJ93lN >C=vjj> 3ҩo܄mʇ] [LsT,h.UlBi| ccZe7s+<[/e +`BTrC4gB~aU'VyM\fnPpyF?\9ƶcXa47#Q<+T Yi, H_z@ǝ!+02|5v:l,; 4JJUϗR -RlP\6;F:i1#hhn蒋!)Im)m@PNiˁz  dpJ0{ܯAnIÜ$1<2ATA6ʊgTY)$2<Yw KJ>i[2-p Od>^wjTHdybc[ڮ(p 6ʀ'˦kapDm/y7XnCN7玞ޞ_"J\bsYa*OG$2#Ju9 8ȫlvfQl[)V)(S |HuJE kktBy!xxqiVm#̗kspt% #h9i 髣 y'q}U6Leu^7~wmm}US۩@ 3+Ӛ8Zu5H}) OvmGjDͼNw ׂT8ǟJNNٯ< yJRFS,i?略ATG+?)^L _{z0}7c Gԍ}tڝ·UGAki%ZBz`Gv P %JsUN46~>@ ح4kqDڮRR iŤ%'9*Z.fض)8( ǥbJiqԔĊğ1QVpsTvZ<6ڊ6j12''.8k @I:T}tjm] 9'Z@i-oQ[6Ҝi:T$11B`| *I H}/nYL2= 7vbäFY[5[Z[(ct.KFˎ8e)JRI$֭ZFCB76[Kn%YJHH~EԖJt(4R~pQԕb<^17A=Zdf_!ǹ£:* 5=bOk kYn"xL|Dj줕9oPJy \>aQ3eNBIRT ٫+}ґUr9}riPೞ=95ն2}U 94atϐ;Rq#M -mM÷̒BYeDc9#/6)_`ZVMi=)5X%e3`uU͗zDT)[B*Va< ep )ߩ85x.eų5o"3X?Ug-Q tQQ8]}yݯ)]2.3) BF:S:/ 93}5{'}LjM]֥/1avCklHtW])H֪ͣDt+ACf7[1䖓lpCqxV3'9zIڦ{@U*|uSaㅆ%JN NpyNFS!Ӑ*v%ԏ6T9 ?͏J}&ѫTc3A"ݹ?XWަ3H^|?TGԖnZN;C5BӸN?eo\@ܕT}tuR,`Edds~WJqooohy!c9UiakF􂠛GO,n6.R/``d OSwkikt0}#pZvВy% a.䶭q[8yWCiL '9@!#X@6,H7E1Q2[CeNUDxzhbR33F1p09Pw*C{ب*8qCzc e|VUh# pU ~Q*"9>*kNH݃Mletdw!=k%5aUo&hfӴoqo,:厽s&w '{y'R,$fZA?.d5'5H(IzxWC(}QJtPWfWRu-Tm8V0rqO5ޙ}&}R 'ֵ^@ShKW婲wc! 9]>4vOc K,w}32VeqGs5s1pۿ/.-;a`%+Uӌt@v :%vy+m@Kv ȷbJHR\!v|j[7 Nr*~@4GCi݉kmjޥiAy\@Sֲur\g ʐ 8q OkccjNEAhXZWqrp|k/QRchJRA9@4THGrGZ0r.wX$mqc ~$MZV@Σ=!\ ,Խ,vj'd$}IGG™I *tO TҸM.qJ[{l%@4IQ<$)n].l52] $d˟5eDgĈmwFT HԲy(`a I| GzY7+lNq}G~b݆͟=VAx֚vr>EXzKq[ ND7mW 0l!P INϸP&ڽLu}VuJ 6T_)>X1sK24Z\{ 8k\w_ro%X߂Agg39 }ķ(ܜ%8}(v hN FIH;m#&Uufn* RV2|Z*OټO ǴY\vF?%ؐvRSp+>쀒ÏG5Ľߴ9WklѢ&F, ֆM;i1HM4뱤ӪʒH@M^hN5 d5QnSk.E+btLf:cl6 2=w[-ߛQRH¡l>,Z\SY@\iؠtBO#=+91ɯ@MU+5LU89xU#m9=Wn#ta>)jmb$¥sMuJմ鋌NH < wk!'@) }~ [ъ~3"wΓ.x {-2G\vc(pZs;mV;θu)=3Dh)W_#iNPkWO!nV .Iw)Qn_) eÒ%`vJKtw~ըm>V N>,3kd_6TI󪩑PeۈդC#pl_}U{J~~WpNk Ti)˒ս JR YO qⲰ3Z! (` rɥ%\ T$nP,hXiKP$ ~*BR4ޜۧlL!G=zykޫJ8HW!k7BӑuLD $,55̕6-Gg}1:} "棼1=Af5G/?("q/gGʎ@WQJd~U_d4?*Vns!X"ɭ1 #GܨVՍWYmŚ>ѲuNx@WF?\>.g\DS܎D|{Q0Jpβ;*% JIWZ]eʌ {y*ܳ/Ҝ=¾9fEM yTOP?˜+R&:uYSd!12|UՊ٣c2U:IAyQ>ݞ"1O4@o!t$>uDY=e;HVsڴ{*,2-f+mv<] m0ZJǻWyp*nzy{}_c*N-6:qsd츆SG |y.mBG8-cN<7 .8tJR $Ƥ8?ڣ'\QH8ٞi%D+#WWC@Z&y: 1ֹS GS^rmJGS%>&wNrvh'OI *NŽ(\;乲V]MԤ5J~{RvœNc?yCvX# u$s*ܥmfɤ7- rْێVm+jAR+wk™qIoi:nz{$N{*9eU>uR*ܭ:w#u%tYF,ᐳ-yXZСn@ۜ>1Um# x'~L@6ܬsƥ`C%DIGXEǒ|>LnS\ vhpۮ yq>cS uj|E@ahW,u֥>GzxQd78Fal ˷jB\H+~Z_ReNBH+II>yQ`zes5\W_R 9!{cb4.匥 Tq4xl3"/r C)RxKKQuO[BG_vOA:F?@p|;>UoK*6$:z;0RStϯOpAm|?ԉM8X~/rBZ6A]4dfCHT@m$+Ha8ǘWJhLOc}hmaCImݹw\TY nju6Q<`9o<z?<\^{HpJaL'I $w]Rp} BdlrW4Uم$c;Pqӧ Y&x*eWu1Qwx'Ҝq;n%FNU1c J]tH1Oi]Fu=;ʀOw=Qx C?%@<8SӴ꠶:*k%!R#)ݸ _~y.uN~;&n"sUZ\}JA$0݁u$|1S-}. [;Bє㢆ϥ\7i)H*mg}LcN:\"ߦ:ϋijh B\ե;Vɶ'lڜYNyQQL9ǒXPTp/8WGlqmd= ֐[P{ɟiZlj&as[%bbҹ Lt:Pe sjGӚRx*u}XeÒv z4ҠWH7#J^_I9CRd2<838'hzkV[S;.[űAS[-ꚇ:, k8! $7=;20eH\wyTiԅrLu6…͎"i'0Hi|$GDL1U0J *P2ӄwgg#Oa;1sFh8* ݾ@!<=(5NK]e r# uI 8#Ih)JAT%98"i#+ݱ+- 8'_x!l~*$}AڶJJSj,EHuYJmJWO=2mqϼ-[dzJq5шf*VALӑȪ紌ed7%s96Z%{qGew<'zTmPRH1O8nNfg sI`CY=0T(ʇ5XYop9FE]Au{U>xZN-d{q&tqkmi}g>mbbQީK- ' t$ ڹI |rw#;ν$z?Sq9?’\!j;|Ɉ3q }g*:q3RXie RlR U2RU3GD1ӁX|*Ü踳Wqj-/kA9Hobܥ[5m<8vJI}%)>Y#Ψ^ل #V5+z念D(떩,٭<1LmAS{TrזOTy3-PTKX-6'I5pZfrN s\H,l]'j \&72aPxý NpHTMB͡:][2;o{)@ x0B:w#5LJ@!Txu#.2+Lbv+ϖs!GƘhi 'UGSG6=9ǿRdc1V׎ pi5'I! 9>)خzsW8. 9*X\`??*coi֛Lzs}Z~1gq "JfOAϝJ8ZcMU,f MhOx r:yDvRFќuƝf;JwfQ [khaȐ 6T[͔q!$ )G8R0i_bѻ\@[8SR=x JyPY`3%/2Vۨ9K8 GkۉĭN-kY$|ϯ5%Y5u##9V3M cI1M𞃦q nUM IbBs֓dx$sydy'R=y/09=q_A (N3ƼJR{Һ7RZǸ0{h|MK`lGJ/񛓑H\tIY)@x`o U+r9)/hooQl‚/V7,xP~1Q_&d8jld c9d%ф(㩨Im}[56ʸD̈́^]Tfl0kP* *)  Ȫ=O%[[[-}L@%%[H8ae Fu>vS砮>R-лD\ҨBSQ 6Ӌa!eYH=fZvm5GSn2ju5JڬqPkH  ~ ' H’J:|=+&ߚ}KtcEsr~=i"yVR*N'$P4ꛃכv v`/i)I߳Ax\ mg ^+Tte);5цѝcSZ0FУ>n>JOS*8YY*CX51#Hy5Yu_c#+u}tfR0r$ B^wg<f+d9Ρ(3J[75.|8FAbGJ9X==?gFՍWYX4M6si;+Yp'?#V|)B>}BkxzʜB ʕ36xeuA1wO8ЊjwؙFj'l`q&F'=r:^y= )%=2|#謱Kv+'s\}1M/EF+<Ё/o֋O~U5 V?;#ꬩZFA$T5[z9ed%N)ŰHrr5UGl壼7N(w%%)?Ʊk+NAg=~ik굕Dd=~Lrm/?Yݤ`OQ'c<#V)6i/_>@dAkzy1QlqAJV(< 'p93²t/ A W%HϼdfOڦӷEp76aͫI:Gsz|X4+Ved*JA9rY({=VvCvdߜDj_,$pߜ{U3جt$@z)$uZ16dǶw2%˅-/v,\WNA{a*WRG> fV!lڵ<+8Tr8MW#+;pϞx5 ̑qϮ)1N;21r3MD0yõnSR1nr0|䨐J֜ (~h?k]foDZ(\Y!n;Z L\wj6ȕ7HkX@( $֬AA6FH;muL5:)ZpN>K<@9YU&lYQ`gH%Gg"Uߴp*n&ȐêJZH=8"ii ͿeWAy\Nֳ=.9q=1HKΐ]QY zolyfC9! R;4% np:{ӝyO'k3ANQXJtuq̆KcHQN*=muIAiR'RC3ϧ6*j9ofRK#jHp⽩ûzu᫟he2\Phj7\uA$ $U_/Sv̇kLD.⼤99P$}$tt;+fi=9:bܵwg#8i>q~J#6;}u %}19>sٔ]cÅG1S>Ȗq9E]u#+?M.!% v%q=:*H yw`H>\P7@wC&hZf#Qу!7+sFF䓴!Pq;1Xwg8y}5Oh(|wrOItc3ޜZ%QJKu +$x@{B/hw)?~9Vz~ԕk9&9'D37)o%wO#O5׀tծoj{~گB }Z*gr_E+a{I[O$P1־'jH8V?ٮMڅGK Hl nptLi9jy]u BR'oLVv7Pȥi +v:Fg;Ồ~5*;Sv߼%|PqӮ]H-Kpdyj<3%.z#) 1?Ԧi@#vwrxϼ9zuStַ\J}EI\DrH5So~Cb$nZ']KQ\NLYU)<${{Zj#[]Qwm(N}RNzyqҡ.SVHjlAJc SN-dj@wmJyۚNcDtx?G_@u1ˉ\-9rqWv.ocGu[ !,{M:)':.hGfX1&DNVSHQqAIV %Lak^##v.Ů-LQڷ[U!N(r@`׊\Gp-wE MҲSvf&^}R9cryqӊsSFn[w3. Q)>ct{Zs4G q:%guzQO ]ڪc+BE6mjcmb>GXʭŔ(+r K2K )6%N@>f);#u>桭^ɴTg56. c$wIN(f#[gqpRmqOsI J~ơwkoL$%H28p#K#$O;hV]-o4q[H=:zQ: /;$tޡ-<;&-'f9ۘ \R%wkՃ,OeTVd"IBӱe92$$1 2l8ִEYj:VЧzC Iܞ}M;nMEqVT #$d qC %Er7F'MQwR;3%[q.ΒOi[B:} v#sg&sv[Tܴ0sϗUW)WȱdwA^K~XzNs-k%o"֩,%#ʜ)o`c:c>eX8Nc^\1*#P[s-! vԠ:SpT]XHYZLkR\"3| lZe;RQŲmRIzT *,׷zERS ӵugM[]Q+\U)XzGGU.`(qkwe]*6>S,Pޜ0vD=z5rs-;/rCSGxڎRލ2Ѹ $}&k$'2yxMVv#}{ꛞaxHh YP,#u>۷i4V i|*TDF&\YMNfBhc>`xAQ~ieXsہH%KrnÓ\~4we56l2gЯD*OT[>vM6q)$% ޾G@0}jsP-}or k(˞7jѫ-0Ks a 裚v+T!&]n. KҚ۰ #a;A>buنvcWSGkHwI 9 *{.6k֧%i!i lK<\E ,tbуF4Á]sZ{2Ď2y<8g[r@wZ0A?Fk5J[p+;k"jv.j%v2IDV^yM[Ӕ|c]N XX&L9QPYY$)#rI'|wOiIR2~b2;A'n*C%:}aOOv8xIv=uW(P5.DWu +TtI{w5YC:ger`.}Y}tikB,趕 ڙI.+1=31#:4Jm)A#ARPb X'EcЖBVq8Ԭx\ܹ`ⲩRYm,"Aű4> H q-EX'v8׭\Cd)Y綍"K~ԛ5e-0+LGJ@rQaHŞ g6Rv)*]hHwu\KX3xﲭ+4[/Kr\ȭZ) de\M2*ccĶ'`H*w u}Rj'yt*|dt""14 0Mdlwwz"%GHI8}T5rzt l]e_'/FP[m *!5eF܏nՍmr<-?GWDQun#!>1_7#a^}BloROJ62D$HLS98>WC,_Aa`9ryO,cWD%յێju̶ AL6c#:T@[uTˌ8+iM4'Jړ,ͼmMTH|jmWIN2H.2BSr3sqF rm,IҸ6>ES̿+t-Icp)>,*}LYn} BcO2’pA&ƟięJrT>M6n8Xt`&\Dg(i(PRVuΥuqɥ0;|8 UG,W nre!n )$|еn'<M1a5;+uݭ1F<-\Hٽkk (nO }pWyw$%KmRĤN\QƋ[db52^oޣ\"XEjL9P܌TP+H9f̺^nd`m#ùX=(7XeɺXtav8PqRVK%*ʷ qz,ٳm-/˅WRچ֒ RڏwVjDOΒQqC)tSDmJ#չc焕tm_r̷̳hʖ ̓bRwڧJ> r~ֲ&nۓ sb:\+Q8U3Oz$_5莅eL{,6e xʾD+ܗ#ިܒp1pcIGZgdv;nQxdr+P^ 48D,GC1*ΚA\7V x5/E㴃 ^͑ŧ59y K>>8 KvZR<ԩV/2uI[ c#(hk&j|&by'g=t־HD@[YiX+1^jbw&7Ǻl:wJbXՖf;rAJ*'Ԑd snvՠt2yʻ0q=28r9)^|hZѠEZ^-,om z-xHꨍ/?%:r$lw͔:Ӊ)RNAP$".xW"I[mpeO k+zZ9⡮XAIM%9Db ]^'? T¬ps '_6N*Ud[pn4W(88g>ɽ;3{7D*! }~wdS ֱd،'}՚ٯ-o w[OHO H"?Wh7"yv':s=`g;}Vkn.v;\ص4~$j@TeՉز]uE+$ #8!n)2cmKPf|ci:/]`WP(+c%C*<g3epzY+`xi˗F1:a,ef\u]eQύM$䜁*LYM:`3niXu,NKTHO"4-sC)@..7CQ4؈4œ-ԨVݜJӌEyS@ҁ q[}A_Ot H XUk!xՖyqXc;.$PV`Oϥ ^HNz>Q=ϙv]Bw6z{ .l6].!(ڑ%xᵓ{<쳵^zhq-H[ܥK##$ʪ[o\ŽF|6[H7AJ0sB(K\tlmشienoWVQy;)HN8TC-ޚa۩hlm)=qSբ4xv]6Jq#T?~6Pi##nm_txwwt`}C;?OR^4ZEq_Nӡ{aϿ[k\ 2!o]]'7kf^AQ'gy?ƴK?9͸wt*Si ב42^bE2RiqiHi'?@ˋK q mCD(SX=|};ݬ $Drh~ +Ϭ(h[Fki&jR#Ӟ9sW=Ѫ-6+cjvG䥣*8:v4mvMi&Ǐ=NWZ>]5QJL@B>x# >ۭ CK .v$(~6pz\ ^3{,ܢ%.dq"1*DF쨌"1w($ $^e$G5 )\-Da4N+Y-BtFº}t·|ࡵֵ{uwۢXy+ 8A 8C;lfz:KR| ::w[ '8SZ-OGmfq=)F6zv VrվDeЭ/g'ZWϺrU1-( FuNl3֌棋*}r2^÷>@+#* Xz@QD .&"ks )r:#jЕ(1tdιƸ0RK%| ^rr99=kY]W{8u sQTsS ϊd|6v_/Fږ;8?風VnqJ=2_#AӬ<"qkC8'zեvm7iNGycE~>Q$'1:zi e☰vu^Znqi$qsFе%RecN!$FqZ_cng;RpS8P<uvdqMo)JN}A@&Z)KvTk+$ g tzT+& | g΋ 30Pmjk9G$Dec?ZMZց#mS$%#r~f7 :'i$wEZ2=*jOi3J6[[ڒwp}k9 kd%:SN,kD6jԽ#J=~U2E bdeiĤxOٶiEi~(fs[}G7ZH6ۺ\Юw8'+m2֯= BR4 '>dFҵlJ/Iʚe( N +3 fS}Nj8Mdlr\c ־4t5Z:,Kjg!#=@YS֨eRHJ;ims^ѓ{L#j")ex)/֗ <1%X`@qaNa~r (i(nn*܎9'>IF|#g!V:u3@j#~u7'DHmQҦk$< /éCcp6fjyOlD3 K)Ձ$d(VKQZYrBmw뀍C *rR:pV1U?e )c}xϼK} mLr1ko~WGk0H_B(VgʈI9nHf.n.əT'r wuXθ.;ARO~z!mj}?I$zVeau{wS ;*Ho}y-*Q6dvKlGU&ؖ %a* :T_K g)$8&QlD6i8Q!8i걏 zS<Ӎkʊ)^XzO`%}$0xTG>\|AkPD8#κwg²4nO?V7vx&ޕV` ¨kWp9|(;DH9:tZqܝEEoJ]'YDه\8˟Ecl}cC5va@w*=n~!? '*8&~,9ζ+$~,9vBuȞs =xW=P#ńBzOXtcx4P'$8r 66 nYt'kiW<]D18̔o5˝*qJ/>*_h<߄'Fӯ [XGIh/ %#^Q9$~#";*X?tpQ:' 6D3TYV:C+NRփ~ѭ2aDB}/s"mS'぀>M6TXz8Ǵ7`ڵ֠u n9Y!O_*Z/{Z,)Hu>Viv*5{TsKI>>B֖1uH˫W!$V39#Ӡ][cO Z9-6z<>}kR=f 0R_kJmwhx-IqD9ɪ l{4|#98 %dH;v$=s Ւb2B iMYzuOSO'D\V0ְL75niIz,oča9 oJ֗Z"k/RZ 5q]P l4ڊJZԔ}h&<(CIi(, Br@.-̸v+h WB<}hƸ\kq% QY qb4I U!,5ܔ +=8YA9H檙Wu?(ՙXݎssAkvn#"Ȍ1mPSօ[ǎh-N$$]T}}cٻ(fQi J8owS灓Rα =ݱMCjTUqET-w 4Ǒ 8^No>s1뛃ri qi=aB9Rw1)aa;8O@|fiE^RQ\Ngc$fβԖCʵH~f!q|$lŇڍ8j1N{@k~$5%Oi @%!(NL*Ts~oGeQI9wWcX'[<GN!i湵'0Xhf)}Ts/ǢAPtD`Es-x)P [9 r_lc֖9ۄ}dڠ8,=1p#s=8gib{I$؂m btԥ,7.an6aFy[Qf4)c¡47kg*urdH;VpO@T͞үwAPnp:˥ YudGoqFN̕a%ZPcFpƖڐ6o]b%.٭ܞBͥ+eCcG탺 E%6ゟUW_9dkibDFQ ֺNˏt҆{UAeΠ'=k9 cdet[zS@"xwe޳qֻ$GIC,Klޠ:$<m `N:}jGFZEɸC;JvIq^ߣu44G4˛YJRU4zeY#(a"-$Om`X [Cu/ou:{wtjLwC=BZ;4&[J[ ņ9&n ܊؍eRP?sZGIу;rV)X -׷mciGb;-ܔ8)Q9ϙ}JL% BBڞSUԅÄ'QbF27B p3]Y ,`m.G=RNs8(##j2H( a\SO_1VGTVsܥ)[_ Q | ,2Sd Z5ޭ=˃W1qeo!8 8ImVs Xn+Cܒ]ڠT| %#݃BW#YY)B aKHQ=3NmFfB6Zine4屩Lz%N2mVJ|JHO,UZi m`u/VKso5 +i-Wz-9db\HďH:&k{=Źz L&_B?~R{BvMDIihu̩))#dja=A $tmv;'$y?\ڦV<|ϗJGa'odiPzgfu@i׹ӗ+j2p$z;jLF6`ǯb֤e( Ԯ}8|y[7Ap&2@Qܢz!Wg cοtHXȄ6{aksCqnΉ7ZӯSmIJ+e' ZPJipH<ySr'qX_zLLOFWE F9w=3 P2TW9.$8{cSjJS*iH@;9O3{y@Liwr¯SC;Aз;7B$B@8hS}CIDYN51{qR(=SG #tR jw {1SBk --֋ңaJ%l@pz}5f6Jy%G?(˷] ޝ@ #%E v @^pz-պ'r _-%Xܢt3ռ$̒RB}O çv+s 4 !yF}1WLh F`lT7>\ʔB2{1i}(.2҄K?4T0ғmS1֔8@8Y0mf|as> u ;rFO8: ,6Y"׺[(B\p3YRFFG|*9v^'%bm!jA(>3UE&+@ qx)=*5ٻt.DJ@}ijp V04! }FCH [ݵ>@ga֞C>S$3Iv#fmm PF?q֬4WiMT&3[hVTd2Lrp>>S >!s Lc`_e$wUt.-ipᲳPZtMw#,nzJCMEJANz`+hf&9Ό`HYLD!.̩ $PǴ4IShVIyon#Y@I{9έiP־݊T4D9m-ܟ-r=q@TOKӷpH2Cv3Zp$d?Ou%* ѐsM*#sF=j[cA;}%f樭gf*[xwyy,º>}z#[8mg +Iϰm[PW еo<%ˁYV_MބySITSZOvP)I䃔:C7 q!w`jnP+{7Jek:BR)nGCui ɇq%JJ֥mP#H?XzFi1n^i-=AD rB|GP3DDCpwmTT1-ϰZIZnVώ$|VܤyJVӎī3+Me*ۼw^cНЂX^_kP%[č8RH䎞uZ9yZzC}+wem(V} vy'>YOc.n,Jm[ Ҳ[fXgWs*Tx弩 <4:Or\R} dp]xZ;,2lctJx澠pxY$J{0Hu^QP%yYT?s{fTܼ)#‰8<]Gu^`RNW4z9ַ5>ڝiIRq/#V eEܠG17{k71TV`HmӂQs=Jծ3-2㶛绖ҟ;R:|JS }ݚ>ޤ6yu)RJq;v#1%HayBHV0O𣧪B;#Ie{/ ǖѼ~њ73j,Z)ۀ>O9V٬Ǹ0-Cn%Km]Rl,pnIS->=Ƿ@t>޺{Ӽצ=+kFlZL D' J)Q$2=µ~,51jgH14.uFηLm܅ (PG=O4V=]yEaDOW7tNN.laHi GvIQ'' n.\ ]KHB2*CEm>9q3gU rO*ǜ]@ ZZ8==wN}H Hiݹ361'n98Gw'\ /shujqK|܌UʘoQmWV`A+ޡ6м[@/xWLyy=!lsHۻU\#wUTq[l8JAOB\^T@uUZ[KҔBR”3{5R:lI.ʕ%8A\*eJ)4F﹕Y9<pȹ%E#!P u+R3O4U5XfvN-SGks%>R&b03eCh+$%()Dx Fn a-VBz''ߚ=Dx#dXH Iڂ H8O=} 7}yAq,wLe 7$zF 'ÅӎcmΤ 'CxMil'@*aKAJW1?M2|<'Њ]*  i_2' b5vB4qݎ>=x'J?h*JMIZf >?F@7k^a8"w*=ogMYe:j΍ 5ݤxkd~,6ƺ󌸟CqOt;}DIJJz:㯠.лjW:ѲD'H3V9ù0]6T7ڹ<., ?8JP:HUaeN{*eګɨQIuo/y}.Ev~ 4xb=RQ#ٻ$Prq WCg:7u6Rv[vAo +)ݰ zJK䗇tԎp8x>u57G%v}o~*}}n9$@4?ٍQ[m`9m%NLe3S ␤`qOL U];*Qbuj/" !a61Nr>櫒d[U%)|t9ǖ|gukIPgږRh)ܤGQ#Rd4{JR's_p:G8XA)M:sMLvKJqVb%DuPhZqN:7=Icv$  ky8nfիE`R]ŦBR8,q劰&tbU/ʸVT^qe/rv/? ;Nm*Nt鞂I՚m&ިB.k;r\RrDұ,K q=F0zP&QD] .vS=DE@y PZeݤPjf[.7lBOe*qjq'qHP$Мlӝ.jLҘEĖa-9OO1gn#oLS߲JZ)qtUF2 ZLRV6HrPPiJKH8sZ} gf. 3+ W.>X퍩Rc 5A2>}+%Gjr1*x.N}a<tTz`08wm w*`[1Nkƕ\ۯp=s I<*l}9kIomAysYUB*sSZ\I*ҾF8<9o(?-ˮ-޺ =u`kMZrnl64(SkV?Q=kEb},>c8H36Ar )Ld x_# jXl|9$(tPϝKCE|uxc2Y0[q~ ș%rTStnÇaߊԝKx##wCC U=K'.8x MmRK}!:ր;,Tmcth\ZͳuN1Xܴo-S vw,Ѧo .ɷoZW `fb4e~{ꘁu#WP5{!R\)$%^%n}܃U+Ahiq ]{H/:0pH*sL\~o 99s726m+Bs-m)iƿTa&״EE;.r 6hR˧P9VW3& -V{Z jwCє06Zŷ$7*i* VR|9 |XIMymZvTJ7  iHn~#3y}I>7u#j;faZ&QzL$h$_1z5#BJ)km .prh}-w5:J| yr*gK Uvw*1s<<l x*&tKp{{NA'* Wܒ9Q;piѝhuC4!=hpy|+e.׫u802WWC#a(ejcZ)*ȩ)f1}jC-:IinUQڥ4{F@z˦vllXa}m HAۏ$gOa[з;Ȏㅜ *>Z5]oKm: FA ޔښ2f.EԁTS)ᡵm-:n|<Ö v)xqrGUE_R&q;t%L}Qfx?TBTW8HAG*G >:Ĩ4IVFݿ0Q8OYTiCFY|m%^ymc-% R Gօm!*]V#(nm^qB~#TE]#@PH>AJIik'e|-6w+9>?Y3@o]9~7y"4)sl- yETbR[5$)NR3~WIM;K'RrI|(篆 i/\q|m6@>I SHq!|C/ڴ) IAxN||'˰^E3oyYIR8w` czBȜg 4œ$Sgg{QmkNܥCI\uϑP s4PMP;˴kdPN'+rR=z-uZSj1*2L˻3⓽  wU{"N5EA)AL Ul}g[>Zf# 9~\+#tdOMkGP Q ?BǽnO:gMYѴ7#fwuIi\Y'\]xQ47CQL'Cm6ӯMOB##`~OpFFsx8(HϞ?ƁBT8VvpkːSDxy(R*Q>SdK1K(PS qci`0Bs qN#w|਀y}5)A+DӟYhN[t޴%]V2BH+ c!9E'|'!Gsܬֆn ZT[#J|Dt4Gbhr"/MRYKr|)H\*=+>>hTv Wd:?([NsxO7R}=T’198zԛ߇peA8r#:SݢܩD' NRJqQCg_ 7G݆`޸LHRxa`ki8_![4rǮ2H֬b냭@yvnuq\@A.OW;i2"dH NO8 Z%j `[-ǎi͔H<73+3{6Y$Y}s9P''G”b-<ך Ɩ'j#qZKͩJ6LUϲyʔ5On MHtm2%̒>$ 88.KQS:8sb%-%hܕc㡯HRR`+\i=' `EPP=#?Twg9", CqqKњ1Aʒ2R03ּ髨Kq&:зn8dH(R~n 5);9wj'.!6VJqeN#IQΡ--N'[ IJҴ()$:nDԈ)wL[*PTRv(dAqEoVhGۯwJL!c)a8Pۀj^9!書D}ۙuҰK9O4YەױOiv⎔yB<U:( pI#'#'I+ c>֪TT3ktx<9W+!<`RHPa#'R|#}>ZBQHh >c}+npSCA'8:+R&E-qI،–_YM'MIZJ865pc/x)j P35{5X.rxmV$By t1ʹ0ZrJe;!'$i`.PR]n. Bj'Won/,sQnvz$M.w:FZQ)Cn^\'5j19z9E|b |v[:DG^S wp8碒ss֌jT1dW̽ gt.Iiė2@*HVP8Q:6vitǢCK8qp@ph$ (#}<[L?I݌NA*qL+{Iڄ)JJ$ kFFyZtƑ C.Ce)䨭}H AۣvMU $NpOFKufkyvŋle.GܤZpI gg eؾS}=v*B߹I"8+R`JBy#NJ 8KFZ^rqS$jmT.yxcDYȣR*l-=pf~ͷ|3Í$pkE'L%qfCfIxiRGn4V˳q"$im!qam8۸IVᑐ:+wkM$*vVI Y\-ŜW9\g zK9H(|4ĐT#8q,2[i)JpF 2HsAA:*1iR\w M*cjFsQIw)JN8ϟZ1VrL u9>E82B Ty't>Uӂc .q=\2<^?SjW-2񄯯$~U>Uv>s 8Tֽن;#?>k^˖Vs?Q ?Bǽn6?UVXVtmMQj8$kp QbH*X)IGO!00GB:S.KksS^%A+`Wu] JGg $`P%wxYP8~qMYP< XzRP> z{(OE{!2 yKTBc=?u&Kk<)]y?ME!zV2<ϐAJ8+Lxw+'W̤90-9Ǚh<.}|ܒN}GxJm;~R gVCy#5% F@E}œ5^O!I'jjT9(yUtEm9=>mXy8]7A#~zxW0:S y !J+ĩMӚMpq}hʓJ a -'rzfƔhGʏSs]i 99OO3`|FT%ogrAZ?*ÿlliIjNɎ'Q瞼L#<E}~C=)'8}"BVA I>H˭NrmJ6v@q瞕AH*sz>uRPs9搑;j%͹GPd>)rڊPPeFQ?;16R#Cҕ@}W ;RS5Α˦6%,IJ0'Z°W*9&VaԈ[ $@J"\JkZ_Ex9g7pXO#'Zۨp21ϯL!JY"!>$kRAZQ2@AWR䀐8'l#yi`\'kaJP> 8e4 )_x0R [g'o^%@m'g~DϞ'vW5 DS~Rqr9u`iNTBHJT&JxB=1VlF6sJ'#)8zץ0[#ʘw%fU(╊𦒢t^Ǡ{($K.+URRǟH)NJrG= z6(<ƕpXh/v9"!6jHLS"Ka9+zu]Z1iBI? l<Ox jmWS쯈#`LK P6j1dy<2[TR8v&CTTH;tX'/;zu/F#¹N<6M,k uFJ{qԡR=F:O7JFQj[hAzujِSl$zyUnI%! {kx+h'$9(U1U |VJ}!-Ĥ~a/(֨>)o'ScSJh(VϬH~:^JQy:U;mhWM=.)?ꁦ\PG T?q[E,4eKT^+@[V{†BÑTuS,:?uo:Zc] e-ǖGcO4)h-#28 ) WN}A|gNNai .Z]f*+N픕(tȢbdF[Jq\Xm&0I_RوZǑŷI$l+Zl;Ը<~Z8r3S a3I^+(pmvP9jtSmД:*f-%+I8+ޢAr ĂRP`fcomIXکk\wS>>OꜴ>o:Fs=jIɷ[c+e Oxs;eRpgyv<%YQI'{춝nԎrr0aa GU:?JDz.x`4| +i' R-fCPMOiǑ=-OҥgTa YK\ua%*=)ӑ(5RVFp=<8 Ɯ Q9ZsjY u* TL([ك?&eqjVJy(;}9RF5\A~GV+/਑S!1>d%G,rۙjJ\8lU=ss/m0i۪ 2,WZ,~ͮ}A-V[B$ZJX(~JV2|JmMFAtֈpGƷ(lm 3MO 9.1H.{@B]+i>yrS H}%C#hKRo=3Zl8G H@I}݆T~=`d~6RKQ%Y\:uVo\vh⿭)POۏ֓1ڳCqZXy79@?IMvV8VV.6Y˩[-2b _%kz$Uh*/]?0+(6>7%2{%B'dֹٷsߜc+!I {ZfU7ˆR~w!տ=pgMZU]:~iFܟ F;ĎG15k;L8W1˩&w )v/?4yzpЅӃVIHNNu)g?R<[|][%nJp*~GlMͶ]e'{ 'JA'rTI<{@ś#TO~z ͐U BB@N}xKv[ij&iZQ Q;HYV~,m24yf{7h+ ;Iu_Zw`[RYQI;x8 Fe.3,\QJ$6yMD:@/Ṯ}\Q dLK-%R!IFO4'tX[ۑ9Ro pT+YUJxެ-I1Aӛq!Nd}\S d=jWŹ%8T}3(nWO#"e߲>[)3 2q'n<Ǘ1C_6S-p*dTӁ4cd#N0 q'yR0qX֧*ّ(ie s(<"`ZAY1¸GuJ/Sw>\- Hb4D@ՊOX5-,m m ֐꓂yF9=+V̇%8J SGaVI;Y٦NAIӽ>zTǐվIZg܌˘1hwnԠvk8s8()D n뷶T҆A8i5+ճ@vWeO=2T3*6RE 7 c$PbT. DZ=cIoءd(VWi=?FpR>Xy>-DUїHFגq n}҄\y&=Ľ9>E9[V)5$PFT8h,M1'j}vlu NJ֗) V>0ؖ XX`vxhA %v Kޘ'R#5$$)T8R$uǨu:.H \xv#O4/K;Y(B e.LHHړ@W%wn-qsx%Qj:@?iJjO|v6Z7~B:cißy]> Rp dJU⎜ddc*yƏt5hR|>sd~Foxz;EW!Cw9&[dƐIiEL 4Tr$xy4j *dgh:mXU*36x.) ψ9K1սu㊊(GtkiN֬|Z#TYZcɚ>j'rVjghΌYLGqZu%*B SR& iap& XJp3Jp)ypN<9VM(ɦ-=d|e{ F:汇xXRRI8&>|)1cJt8o+LduL gr7(t#)?+spTLۼm/j ,TdW Zz{EӖl\|愣J*Q Ղɫgnk*í!(>=Y_kttkAZfyה{Cl{bΟZU]:~iFܟ~ӹWADk$(3vkb:q4;CQL'Cpۃ;>?+9I`rLr85XmEX 5#3Gp(03ki-ԣ8#^E΢&0*YU4҆84\>jNLNbus ̧gO@w鐆 |;:˅JrMVi]De-NĘ+HV?ugHX % 9q8gI\k{)pww+z{ʖ^(`v>=>9\hj\ѐ{*ۯ(dHuQOB ʻ w{p Y3{ @zҡHmF,Dw +[<SGdP؉da<ʜme!!Om }7V/:-IY׊҉iCc~DNY$u*I B P"KJLp7l;qJ"u[pe)btRI$g#?hs;{5E٠P ԠR06~TH6#IE)^V9߇f1$d4_EtNM;כoMW<Γ}MVs}B6Ov !)/$+՞v53[ͩ^ҡ T8r|qRfY\*Dm. d FrZf|ntT :a qzzGc)#q+PTUs8Y`RHp8׏S'kt,Mݧf<2Cg׭Ats1w90ÅՂq/4ޘG vK*y =I۴*;Ja**kl {E,DJgRBIH8x?E3Û%5-P GYc]t {d}RPЂGMxBX#&&s򻝬 ,-J_8P$, [F=ء=;%mpgqA%@[yհ-wִunA!<9O* E캃F&*XiB怠6 VT8RϕtS0ZH~"ӽPR\Z/V8jY $i6gJlqB7|sfMvmQkyIuNg錚;+O?:p5a>ISϴJp: ޻2;RY'g\u3>NN;h9'ϠcRxӪ; q[*u1$,4 CdeHQ2U<4~mZgjnp(Ydt VxWJ6w qpqǕeIHz:wNsDh6h\ǑjuըkM7li'a[Si$Au{EX]Jbv?D6\/ӻT7`$۹?4 m âe>eF#*氘HJyB MLTgN)8JMKcP-رFx--śMmH˫*Wv6$W(Rf6c>vj%6/E{Ҥ':=lTO#| BASKVֶXu4}ҧX󀝬sZsJ.ZHZ˲pBCllDS#x 8hvæu3.[`?!tMJМD/2z$?>]ϜzK*ZϙӆR57iwЯFéb'><[Z_|`Nzt8?EQ&:鶹p%q( OQWnW7 q,:gŅ>hnxZRT!*>HH+(ck۾cQEt˴h}C!€ H$rrWXHY)R@4ygHO%kHGLt *":G6ca`8wO+'_WS]IխπtHT]w>#[NHhr8c [d2ڛRA?}U׭43`(d 1[@lLJ▵!SǼkre X ITzلfBQ$ +!XW2\ilg.ayfTkܽhtHJTG'IX! @!}U^odi;-dm 9:f?6?xԘ2ޕs3rl1N־5Cl>; m1HVVmOӱi[f\ySy`MWX8%•V7$S 3WG>dIB G[М`rxQ?E\2բZC--$[%{ڟ@JtiKGxRvxw{Nj# [ Xw<(Ңc<@ 8XQhϟd s^ StİpPp|އ>T,9QPwʏ|£LߧܼԆא0 MB 9=3XgE>(eN)!IRBJQ)Hܜ$t]Z.z}L,: PrPRq uT=# y,-Rqw}#wQo{?oV[Lm̈PuYĜ|gכNfX^[^ߎRH*Vsܞ{hZvQFSvj>Mڅ%$TFs^RBCCx&ȱW5lݜTe;$q$doY9mM ڽ*l{%P#yBk6qp?)T[8\-oiOL܅Ėq8ҷ$.k satĻ;Ōv!C'5Dc\ ڣZ|⋭ZnǶͲO0yث./$(wxfn~3{PRTRy))*_T `Нt"'MtQ{9E\T2:ͬ[eȟ B7%;R,r'n*qp\빧=s^yKNM/Qa=ER[aDWa:VV@=SHHR4G~4- S)ؔy|TS@HО ? vDp[}Q$F@O$rj{I GGZ99 iJ[$8amN(⃻$Œnx$c mW#eu& RpGimx}!9'^ZH*^kMEr +FkvƠO<%{)NOs4݌ưisWRD$zd Yaq 3s%0VmҖN|AG?=wz~@R9 zPHPQc%m&@|Q`uYw05ʮᵫ/Ą%]ғ'{x-rL[Ebq@IJG[Ns 1Awki R#©5#4C?3@&07=]=gpmeZ{jdɍ6a1=K* ̃ק%m MkɆ!I[T)Mfquq Yn]òkQ09R}UG=`e- p0d%w ' H G#\:Xh3ۿiUgZ㜆\䳸:VZr)X*ܧ} @OU7ݿIvzKN8⺬/@B'%6qxP衞AUًoa/Xew]dݏuz ;nCzy]Ֆbp1d)! OR@EmM@>68W\נ/wb-ݶBy$3r}<|\vsd[$n2RӁszFz_uF?pn͝GD p(z1W}hQlq-s8AP*ԩ'yW[[R$$$|acãk6?Tܰɨnu?*%'~#U=ݶ{RY!YP?@(tGÇisF3uk-dJm I[ a!=Hyta{%{džz3ҋP91(KHsyhVQ(Vhڂ'ȕRj|>JE]2O_*uGk @$Je霫k]9QY_88'v=Zeb Omozݬ_JfO­(\ں 馺Orj ^=hv'ТG=P"$`IdT`'7o;'γ#2:3T&/k5!%IVĀ؈ОO8dd _@Ox{͘inS4/k6~w i2#b[\Qt)+=x<vkdiZB݈˲$xC|qh״kn!IEٽj?$=2G>u]0;/GZ4ϲ\{UT*tj G=z䞴1nHcŎ/[IQ]);3j"m S^*&HL1n2$ $Uc>shd =)ك( 𥄯duQ>fط[)z3lB[;z{ 0v#o}syO$vz}igU.*B\ @KBJo$d4WO˫UD)ͻ:K6] 8QN>!FwkhpJ]C0agn2b-iwt͉.z I2|9OEN#!ǟe%K I؜䎜SWRT<=<'{Yan]l9-wu)ܼ˃3YPW܎oxnX'}Fs$q4ޏS[.{ְego@ T1&ry2f3jt6Gp7#9dJ ]ӝ@y:1c8=z֏bғL} :c)>D8~,S+EKӃY&Nӷ.p\Ski`uvQm ;BHu) |v1hcr635'.1MtGYY+N{;j 7$a*ǻ8k57) J 86DŽ# #%.cl;%ˮ8$eJ9'{h$O':t{PҴm!K,p3#"6x4ݪLIrSrVQǿ}Yڕh9?H1THRyzxnlp9 HǗR⇝e9:Fz׆U˒_ZH NsP.nqM#ɯadlxloq X T9NiwmP n=!҆`t*뫼>ؐ͟sї{u#$++ J=rwtXƢ]bҊYi;J`d9ıݝ\dLv3HtN9Q*:QXۆ{W=6ꬡk;LI1Ԙť6UtIF0=sGQtKm~䠀VAZt4[;MɅF9 q+ _5=!kZx6gn??Qf7+cO 4TJ -dY|9~TZcCqKHH$:ۅVR12R0 7_Fr!RƐZ3 #|v[.ڧ{@fJRS BHB9QWԓwK܅/:bTHNKY%JH!{ҭq8&Wq:(_Y#i}e%lԕn * Ux(fY}˃!S {!;SU&[O\@rbR;HC$`z FP]-RaI, xJM\`vk8/y-=jmm='SiFqª f7**띣9kS ԭhqJ PG"g438_^x` 6+ 6Vv)|zC4Lcn?U%!= A YNP!P|iQ IdahmGҟkQr{9m Do9P$ƙ+j  >@NpqCSӠ[I#Z'үT(G 6y^xe xJ*mi&^(Q_A)!78VPαeČԾR@V׊ loX 67N6\!;JNќu;NqXZښ#key<_p[ 2w\x;E_/dP(W(!*I2cZ$@)Rܒ=׻3;L1an_ ASnsxls^ w<!CˑB@(mkBH$*HVO/Z%3uE 䝪=zy& *9uATT\gJl {%1gO89R+_츃X|de^ciAZe ee'#?Q O6ǽnUU&(\ڡu?]C'FwRhw@|֌qf(w!dłBQk} NE4ЖN 8N2(XkgO6lN -(Vw"\Yq\!+T>"mw^ "ClWXA# >MNȷ- [+P+r|Y p9'5_.})G Sq'骳a]4mW$6>ٛTf%]P,s*HIMFejP; POCoh[]iAo;o>][OzIMj8)钴Jw! JFO8ݻ+pz=Ď[qTwX7]T]؇ S\z0ya%x.E%!]"CEz9[$1}b944(AZސHH#q€'Wd͊Mp֓SVB jϣ3ݽf Gz%k+%✑B-AOvB8f|iUlJ}mHt-xN9*QG?LkaLAu='9P#Nwr#'$ 'Y=”p|#Q'gҮ 77KθC'ַanhG!;wKR|)@ gF#nU3QV6طKiseQIc__j?ՐO\c쯷D)r /89 3j EF0pFzиGQD>BvyK`?#-x j#AC%A/@`[J;H xښk;5]/'3RS:qCD1ֿrUjnjv3f:RYShCJT^U Ȧ)Jp<<֠ۻIĵĴ%?5_mG#q1h!h3QXdo՜/E4 [VXMjLN(o4ڰ?֭%Z*ܣHeMS6:a[]%GM{NZf-#0rU|+CX e]<]s뚣Ez-P0f:8 r?(V؏f(]ﭧԤca!F:$g]㾒V 82x'5]EIYtr ,Hy*xc[{mد M $+j|l_P-n'##pq2O-Ib <%xUǸQJ!uP^PAPm>%TI>;: }lJۍ8%} i$."eJi6T R (.t-ɔ ְVyE*8ebY$woq̍9/\vW JH8=5$…tW?QkV =8=`eILALA;Q8VQˡ69<fADWR{pT81U#`Q <^SܞLņ )I ?]wI̐@!lSϗ r+d^ ( y$6((t9jyЗ)SXpɎaq(x( )+PW'r}*dZuQT.R`5 |x q o3`J,Ԅ@CH#<+UZZRX+ F@$EX*t #P#gFR:-< |@wi-7 0g 6B6RZsJVo>h h~tRH%W6[D`C*m_ү)38?H9eL%paOx2ܠ0Nw Ϧ}mQAnDDIBAmg*<ÚJ;Ԟe ;1z.)P-vwiU:2yV<0AB,_g(8Y Yv`[\78#|G*? G 4!xRHMRe!ZC)(Xcyiqx~l恙{¹1-(R9P9ƩuuFlTJRx'Wwݦ6dt;+uisABt)IWɎ= YlR_JYiM$d\q*HclmX#U$JL`NN FPT;$$@ֆ4o^sDwd>T܁zEQrk>ǻJ S _*i_CA*h,(|}|hǬI͌(YH8A:O~rV7bɸx5v^Aexsewz?ٓVWa̚hnOrk~$]I'F[?$PO+{ 0RBy#TBApCj)>,`yPp8pYD9 L] ecNך,(CϸKJTGy (8 됟*KHW0U& 7_f\Й(̢"+\z fDvZTAZ S:}57oY gSVs{yѤĆ#%e% Cc*'jTzz AUENXdVzҴ!zӔv`G_Ji-cm1>é>qBC T(.":$#{*y6N:7G#֭::tlJP[^x~x<>J讐t^|ΚַWRZr YJ}īֽBЗɎMb0d/ >kmrIBnHmhJa덓qQض܊R,a//5gIjD8lF&:[eV R+Ħ}X>jX"P rMMĒCn#rEYip2-h&`RWĵùrP{ $ r*,)JN1D~HXA/WJB[iBI)Rr:D~+f~^KJWv䤑pSzcp󯈈zO椖Z=TnHXܭ&21mW=!^B7N4%Cph W$qWMI $`c0i|[ J36*MFenR+$LvcQKenQ[ ͥG` դPOu_$0 ;1uQqㅱ˄E#n9*k3V[TDEF2vM[eƐVoMJw[%@(zס#4Hͥ@wX=Xwa~2]S51Ĺ*di?c$m e:b!ĸ'zKC>?@ݐ1CwvnRB3D7-.{QܑzS3%-!It%Hg MrWH~~ d]_H٫v{jv!JDe)nIRR;x UmIPjWq!ȝRTWkrpIP¬7\f;v[[a-&*ʊ%9u柵+Ep-^x{IS [}RWn$IF)17Ǒ> )`k[䧔HI?h8{U*S2d,GJ VV [-C@#&m/CEđ)H+#oY3w=A[d7m >ORZ#E+))چ TƋJ368N D9 Z{C.N#-8?2(=% vr<mE R09gʟZ]I.E)RIX)1U/~OQ3Ͽ%҈cGzt.$VExwQy} '裻v*=ۗSǓE $|늀hP>[ )g8VQj\R Vi5ٷ{0[v ដWN>(QӼuR O9>X:A{ƪF(Zbo%Z ACyS} y%=Ƥi;ZI tHe$% I)*wP~/W aOgma|$NOZxJ~9_AQ1O9ǙDg:t ~m2ʀB8~Co42r'EE*gy^Z sD]HJx K{^ᖜ돛E0(d!M;*qҧ?ZB!הʼ.4=׷֏`Fl@Q%' tc6u8k/Lu\,Pexz5ݻci*^̈1 Tzz hyA[WԶNHIpp1O"G}kOe+M/pQZrq*4|}6$!$t<Ԏ$? M7ke[ orTRfRE[#i{dC9I G;aO'G%S>u~`T+81^%CpGpFָl;S'Nʌe'TH'nG - n!|yMŵBm˴t% ?-a)K?7+JHH|iP#Qm[NddUP,Iz*MkX0y7i.}K7`8I*dT6Br𰦕(01RSXQZtvH ;׿|(UNƕD%.+IS;T9Li ܯW_zZCqxNf3 ?dnRuItn-b+/lA=HRԝ@$Rm{WNn=LЄwT/dHlt~=m?^U4ZMy5X炭io{gR*d0NGTFC+(JpCYq^9n0rH-8mЮ~b2|#M'HhԤ[_{t'&iZkNTCltkk§‡t{\KL)&1l RRg=SF=C2j.[H}L-ll+ mԾ4P$VO_jwAS|z'#v.IKB61zv,kAyDFRF|i=m{Ki =X[9z46sj_у]sǾڎJ)܏4X<@_kJ{7cJWct]pMYxts>O} Oա~_u)],u*<WFs׏Ǿ4ڡ?6^c]dV}@=x ~׸xBԬsEkz7Wi#K|y-??a~_uR>j+Y"@>EZZ4Diǻ][\$IhEYG3{2IbAӻns3xַhkKP˪C Gat_eiS TH8 kEKD1ﶼJ+$`Rt炈TҎ@ ݋5Ȣzgfd%*f9d:qA[Ymm7 CGzn/ppIJy9x??RF~OƿނKr?/2*{%] n/Ԏ񢆜QO aCp5Qe*wI. &ͮLt6d2T\6hV%MXGKhzAUJEt ׄ",e&2ҥJ2\`#3Aq<[!/={J}X=GxQE< HAXA䤜pRi xo_En<}ZcEqhQ~/1)V}G,!J'ֶ%J=k޾=X_~x%<`" 1aO,dxV;?_bV**=|o}M\< 㠧ݕČ#ϑVtMY^@@Vi~/1qhydo X%_4c8=:S-<ĭ3T@$74 -m/e?<ZGY\lpD)>3e<\eH'zu9;;׳cdW=|O}e?<N+ ;gFw.Ӫa/FQp C%cz՝SYm9'n#{ VPR]Oi㒬\#< D#=̥GYLI^̟Զm1+qv\iJB: 71Pr-wKHi DVFBP⭄.8*FԒ0xzQ%VIm|FhVH?OP8᪥<&")݊\W~WǟAQu>=839 mg\1\Zʷ!J$r'84?sxKyW}V0, `JqJӒ%)Iis$?F07~x.V,A}%f.>]y)F:{*FV'M\9H$}塋d+sjX*ARI4ԒG.w,9 lٓZ1Ǖ[Q4%yq;EPϳ, I!(_pQOKҒHc ?6~mzRͥE=)$#i}GF,JI!oK?4QN,JI!oV,JI!_~hUKҒHS})}#EzRG_{Ex=)$>Ky(Җ$?4WϽ~hJXB{Ku(Җ$?2E=)$QE=)$Kq[KҒHGqͥQv,JI!_>EJI!0RoJI!0WϽ0QKho/0QKho|oF;G-ҒH;iU/2vJ[G$w)}7vJ[G$o)}J[G$o)}J[G$o)}J[G$g_KaUJI Ͻ0RoJI Ͻ0WϽ0QKh o/v26J[G$_޻K]izR=)$_>FG-ҒH+].m=GJI(# &*I/mupen64plus-qt-1.15/resources/demos/table-view.jpg000066400000000000000000001367021413256700400221610ustar00rootroot00000000000000JFIF``C  !"$"$C\"f  !1A"QUV2a#47RWqru$B%356ETb'FcdtCSes&8=!1QAaRq"2SBr#$ₒ ?t / }; K2\m(  Oay뮅5AJ ݟlT4 `;ёI݄\.qmryq)i' J#gehjUOG--**X υ(}+2W4B1.'o sqkQsvoTft-)Sԝ˕KineԹmJ% )X~ڤVuJ :3 Mi> MSj!KC٭ v_2u^tC{lGF$FeIgi[k-l𝔣Ӯ;jwA?=rwK|k*SaBNR/qg-h JHlg7G-N3!JRO!WMR.`ĸ2V]t0NF 8ikyXRtˊn$%=I VSNvWjyp̂(tOO }Tї`?[Rs0ӿGdW6tjkxVa[R0c~9<"+?X{F_߰ emIn:Tc͍T"4p)%}m~i?_[R30y߃Ml}f5Pdؗ|XJ'5Ix?%}eHتkc25PuQO 8t-OW߱g.VcMl}f5PuQW xv~OW%᫿M+*A߃ ?j:d 4y2+1P3XJڑ?N𩭙|F_U)I 8!+h9ۦq+jFfzzwlkfuAF<&F0ЕQ|~`#NsMlγ5R}qW=]?/!`Ml}f5PdثxWÓקOeo fiEv?r\[R1?oJ~d5FehN]+Edqӿ_YOO=]ueѧڞLEɷ/v^V9{sYdf܋SR..KKm2JTUVFI͏dL*7bn}e oWTe|C)Cd]^.+1 m8Rd8>O2I6?o+*2q4,D^YZEI$' 桞C5ZOe$07MxjO}++'ߋFvFVqKPWއy6 .#EM-kK-vTr+>b3^ Q3J(,|-ڒAVdpGɂ$ _ Zpկ _YX6]W24X%CAE*[%II*#+ɴ>ץ9!2!BInDe) [J+Q>PEɋ!Küpկ _[Ca+kOn] kp bߌGK*ʔV@VЌ#Uk/-Q m"o+4SO@%]d?]\m%RTI%ˡ*'UH}x_wxڒ߃TǛ2Dg92.cWԘcQ65Pdox}Z޿7~ |*wjkc͍\"3o2}[8ۓ_ cWH}xY\-ԛd{kt.4bK)3ºLn_NMdy"/Yz2a~ux=/jP& r44Ǜ2D[/¯UemUH9 Vkc͍\"-O2}xSQ<禶<"a^z=/kSiKڕ߃TǛ2DS'ƒA[C.=/jVf u禲<"a.=/i9zc21߃񩬏65pdD\'S_#lc2gnOӿy|*k#͍\/ Sҗ<(L{F_ԴQT-2uӍ!!*YقIdcWDU%I7) ]8Y;@%J;k|m6\\Q-) R{pPf+&B&K"DJmpNIC< 3CDȴٌp܋D c:2p[ITmxZ= Ut 6vs"IR5aq4Qp?%#W_xZ= S-9VyU%w,JEU%1P܏9Y5|5u9qf!K_9*ĵop!)'O"edwpuYIJ”u W;G|%ґ﫯-G}zCxZ= RX)O-#/H-{ G>wpu)|"#_xZ= RX)O-#H-=^{ G>wpu)|"kx?{:֑﫫-G}Ԥ9V,S;G_HKGR=upu;O>xőJ Ih{5%#W_xZ= S-Hs爬YvKR=KGe#W_xZ= S-Hs爬Y˼{*Gz>xZ= RX)O-#/H-{ G>wpu)|"#Xmҭ).يuJJ! )y_ TG=ַ-W{^^v\5!Ǝ lu\m % $u rnAHqNtfP1o!hġ#=|EzYl/*ڤ4뎶qR T%ù.aEG)9r&uRZ[fgԭ.''\޿kخZm񤰦#01'-P_RJ >H_t2,ϙ]#y.$d*%p4)wpu'9Z qQomNc-f-My r,V]~ [S y.@n[R` UG} G>ꟷ>x(ˍN\;6*+NIFr FxőJi?{ʑﯞ2ZqVW_xZ= S-Hs爬Yv{ R=uwpu;O>xőJxiR=KI?x_HG} G>R+E){%)Oh|?'JCxőJi?{ʑﯞ2ZqVW_xZ= Uax [O1dsLILKи-#J HR<Rdhd#_|dx^ʑ梅wd؜Ȱ j}:zra/zq! ( +3nmQLg%CCs,2NlxQJK,ysV2Zz-oT}}O^W=)L]AVQRYp`+ ծг\$=4k`aFp-8Ki,6)ڒ>PIQHs!RZ9| IiH՞!pApChkfv!ΥJJvTR=I v,YyGy ڶBVIP HRHA\X;G|ZSw/) %[ў0k{Oe Dg)*CNulTþuþuk?|x'Gþuþuk?|x'Gþuþuk?|x'Gþuþuk?|x'Gþuþuk?|x'Gþuþuk?|x'Gþuþuk?|x'Gþuþuk?|x'Gþuþuk?|x'Gþuþuk?|x'Gk-1t^-zxMlC̶La݅Ɯl- #-@i u pRljA)'%vTŒ7ّ) Iu'G*T&v*5Kɟq\[q P %)IY8YcIۻ* W.&6m6È*?sI O k?|x'G=ڞv;bi `BR#'byrNNMG-Z%:D|&Nu< gT@a:z"jgB$Lzn@E󎆜³I(c | gTOYT4$Rihʲi ibLK1bBڅ!/!/PqD#JPT[xO&8MOjrafؤ.Rˊ)’@ XYNMk?|x'GqcHW`S(5Yni&*<NE-/~࠳‡cj;}Xw7ޭ6usm'G*;FvޣZYdf\Ji+h.G%Q/Hۨծ.&ARR (uR)K* <5P>Nu< gTGz5) /Y_Oz77d.^lOù 9MV1*ΧH閻]P)ҧ\*^<Ð| gTOYTm$F篭nO_쬿YWĐzDb>wDNc5> s_wWᵮCt> sOwaP[U ;4 {C82CdoDj4!p)-mo% yum\!ke6Q(UȎTHgbƲ4rP ZR*iIR^(`qCW[M,EE1EW.io.lJ>潋݌|2I݌O` Ķq~O*d+'_#AsBnTż8gRNؔ!<$?qv\ڒ$HDt9 +;NUI".ZKf| f{m7RJi>NZuuJ[WnZeKLFq|f:oSj}H\ZI6%.L@q8J2H<1XK/ jِR_htR@WP_IϥW'ZHj` Mܻ:/:Wtی4xYV[9\Z)kPJR2I8y;8rRfq;Vy])r }Lf%BҚ m0E$56 Z"zMw=?bbe?0VLsɈ }BqȂ^P[|%aۮw)H}T+~#ioTI1@Clixaju$64S%`8|M14azU/0^& i+JJ/ Py+Rwu 5 _4X)jdx/)L%5*%Gn ocLXi4^<&qB-AW0p0'E0UQm.Xn3p'yGʍ) \Y >8Ԧ)'DACt-/-GoN%1ėZJBҗ@RRJ1RHWY)v{..onҊYerT`$Yʎ!{1*:s^ kVlj;V7mz~fj5Ea+ [ZK)yމ)AOrELxZ~p~j ŖP$Gl#j%\ʆ!ʾ^;Ξ~̱JP R)@)JP R)@)JP R)@)JP R)@)JP Rl}Aa~~:D*CH8”;9+e\էޏL+T=w)rPg RRؑE%@:=qYg7[RSFUugrC >{= |Bi Qt-}X1g#qg( ghf2/O%ͫ*Rm聯_ӛe j==>.ktT&e6ܞ+" ίB^)JJǡ *-tws?TJVR\ʞJM|~{_7ki)#4oE-A1#_: =xWޯ| 1lb0!D2XPN|$uuu׈jW[ō)aL6NAф窻Η pś9bTdW kjօF' IT{XpS]Xr_y$K`g f 2+kLNop)G*QyZh]X.șhD4`*Q*sqs52(6>&(9/+PʲSiq%Z+$Oa\pG|S> CS~rϬ$qYmK,-%K^2?U-LVh.goEۣocZ)W! SNK-:IgY“FqU3E 33*,<[n!C JyA PՋXLe[G*w6G"=m!ZrZHo3%J*RFIjT09>Ud.L [8;P))(;()JJRR()JJRR()JJRR()JJRR()JJRV.1u$KOZXN-m2\O!8<( ȕTuCz(COǽ)źn [YLzPە''=u 0W0t呻DK)kQ7ZG U+!%‘b9H')sTXIܐIO<㶼xzB P쯢K| 6B-[z577I|NךNWR&ks*IQ O5Ux[ Z i#<[=4uG*]kћ9ܩ>IhM(E6q.aׄOfc%n3-lp1*sU7sχur ny9D|歒@dcӐj6y!ehLxx*-M}ex&$iCqJNF2q @*EpDϷ,fpАJO`]4zW9,LȮm.4z >" zѪ\ʦ(Hit64 C3YŠy$vv‡IXFtm&SLu6%hQ RG.Gp$d+2t/C EGjCi۸h<Ҭvi{-G횂w#rڮą'#f )LJX kiev--xo%95`j;vM=rˏ5#xܰ  d6&tF}+tnnyj;),9 ('\˶3qD&<ɒ haIjyC8 "iMD[LVMD{TS;zYR)@)JP R)@)JP R)@)JP R)@)JP R\Z夣»:L㲠\OH[JX Sˑ %ſ[ZZ)wf*R~ ڎ$n+r>!ymKb+vJJH,ܤ?KUgt !%#?8>:LaýGErd6{IuHKjp'םl>)ˍ-td% zt:[^W;{ϠYU7wGMU%ηBV B^oBN`Vi6 Pk+4ΐ?FohvIk?RV؝!1nٔ-xQն˄ .tc 㫗Td=g"BHQRވ8̕~~<ᦠyB4U۾ZCB z6C<ްjzBtٲ1¯j=!pEo~EܹM[z5Y\k7{6ϦHMB xrB~ [AZ˘ֵzڣڔW1 %r_Uk͜3#uT/?􇏩4zj.ߪtdZ6$pA[:F62IUk9Ar*d+p%+yA$#)J뎱O?ׯ̔!*Bp+  mGmuZ.3Dt-)/h@K^v_~aN9p7qfϤ=UB>}K ^ kptkmJS;I8vˆk{ kޖ+R OB+$y*PJ㪮Vz?m9…}y̯8em[7$iӱmIy.tDmRO0ujL:A.z豥@̲v(Hwh*WFnXI5~RxWW DZMd붙jzm3XE.Ȇ^[(*RN:TIQ +e7-,Vs/5}m"AT"y(JjˣiS eZ=3GMG54Qp[h2,!J]mɒVSVݣ,GnnqNiKhO)mrG! 'vTJTZTAӜ;-qsۊFwmSKe79ũj4Q A+*Aq80Ojڙղ-:[A4p-&H*z0F\$aKMO|͎܈SO2B0zVJh)^C%)CζiOIQ;5E*<eTy{TcB5EVK-͆h)-d(: c-YˠA6:#r3Y3F|)%$Z7]IeO *>BM4ī;Nħ%D+BʇAnR1EHJKR)JP R)@)JP R)@)JP R)@)JP R)@*zKbͨnd}ݼm=@ZS;pOVO]H^.Z,R>=RWh2+qɼgU_UGŎ/.6XI5;(cEK)h ܵGdd\ʾ.;O֖.o25d,czkz*-.!M7g+;mUVDHpB9`ù5[\n*b;̅6ѻ)(EX!4kk;PpW&vr4BG1't> fD^kba. Ҧ%Cь(+jm ( 9q1έqcBXqٍ@B<Bj)uT%::κCL\&ӐiM5g pZѼXm8ٸ%()Uk5mﳴeZyQHq2$:TRBynU_VNjVSpx{{\a8Yq)M/xv*jךgUXsgU痉;[*|n{_7kQk{ ~cݭb g`³M 2"Yq6F3g9Uīt(0|H{8i`{TAJli1$O.?h =Dd:-%J–(?,G`vuMQI^t5 с7u.ox~SϰyUeG1ގ]y)X= ꉁ𔄬%=q瞾V gSRhպrK w`<ȭA{LMTȼJRޅe۰8Ӝ1ݛ~Yl֘:Oz粺*m/c 2%LqHmo%'= GZƱJ\ZKa߆+ c6D(.'=8&LfذV(-Vs>:徠9 9Ҧ;cnQyrab@K.uߏ}ɵ̙6f{KbX*+!JRaJRR()JJRR()JJRR()JJRR()JJRR()JJRR()JJRR()JJRR(aWeaWJ> s/wH5̽?Sn1}3EĐgC]3[BH WB|_4p5ol#!@w'qkq:ثrۍ=MXNH2p cLخ[z3)P\an8Ru d^I¢45 r"j:kE.MeO#lnx[܎NFH8O\xpÈS8d\$`eGq9zAn=o!Tzs+H ZO1VPޠن&>2q8qIIVrM71@_\*򾎗K&p5iw][5<3u\5gw2I&BIFs̐'$gtN"\zy=7 6[qj5 Um=O;:e%&('y)Rӹ$#VYhCm)OCI !)*9'dI6n%Gj›Yn&kZSñr}RKKMX3Ґl,a])WAoc=.9i-"IPh7gϹc.Ցc`:<~޺.ɺw]=nLʖ؉)a{BVBTv> R Rj7+E`ˆh$[ZeNipL$MjQq%, `Gtm?o?jGt8 d7J TqP#u^r%Z?LTXqm;!\ +Bg~!ZH8'yD!m;VUmnV2%t=W#WT~WGvOD]9nyTE;5EuHͶu%%Q#!`<h >jC^ oi]]ģ3smVnemqCB[{ݼ)[U[__x CZ[Wr5,sq(p:paRǦ5MkW]cM d뺭$K-E9ެ\nެӷglLJS8sQE-lKAkܤ' ^U_)B"XӗK=$ O1 3Hd<0טmCiQkAMY6l)frqc][^N2jfK!1ҕ+%Ig/Mz*mׄ%n)heJU9_?N'en2se[[JIRgv\@[m8޻D@Ry)$՞y9_l[Z $$n$8Jx`ihA^V[e`t8Lvx0juW4O.)nڎe0a&Av(i2o1!IݵM$W/@w]%Eöx%JP6)*AWޭ=/ܛ6iDdhdyTNq"mmK(܃ZZnTc:V}Ԯ71:X% ܭwdYcW{F*QUumkfei,%+wn Å)2d9 /CLJ`קъ5eNdUu7I0J. <9Q<=7 E]Iv 0$iƐTd)mmz WmoqUNٖ}"nw+ecܐXm&:r!$'I,I oH\n'Oa*>LĔ RJ$/t-T:78g;ZRa,ABUNq쨎ӓmIiZdi˪*pVI1Ϟ Unԛ;_t9Vy~lq/ERC-+IIRT#UwrvnR0O29쫮$-IIgkB;c#i;q;ѧ ۲T.%@oGq' m4 69%zIW8jò[Z”\ZMN]iӞdyy欪5>ZcԲ&^wT@sH糉{,{xf-Wh%y3, 6 8,!N`Ңd"wVߧBmDӏ]՘쾅ҖzMF2 [zPK]/D̵Yy ê ^ԭjJwr>xQe-xȴ%e͘pPP܌6PNJ֤w!bߏ<ՐxUgn%ZWE!ܢdH:Sc628㚏!λ:6U*Vy%bcMTSޕg롖nBNQ{= |JV,K\"O"DmO%KkvqDž,3R쐯v7H@p8[`Nr:6T%i2PZ. p~/XwRiFT W⨠ZVӨ,7yaZovۄ 3R\u\JI(<#Zu6l+Mp}cJC~KI%q@l m 6nf\ &EbRz9=AJ3^gUR\埳_7kk; ~cݭ̧`{Q]:J\4n"$h*iG?ؓqTVSeFCΔ^!kެm ՏIkܫ×P *5w8С,NĤ$;#s%Ȃ9= 5nr-l~}5d9K!o!9JS%Nm8rp 7Cd:ʜ.)%<2yל-2+\ t{qNOWWmxLQCRkPe;!aHz1؟8|G.%F֗—}̥Ѷ9Z$[[v䓅*ָqrCjEZc-q (5еAI;@{*c{z̑&dw]iV `<~Oq|H"n%eo*^ťM>W .5Yzd Y/'hg-h͉}*$qt鶤)8ڢxh趿^iޥ3)!!DOHMXi*)mTU[FJwu$uVCV[3MEiL7I@ ۜ@ǒ/*yJ֞kN<+Im SLp)KExJTRd$5U|];Ecd(* y)#nJ ' eip*ԚsOjhmv]3Nt>#r+F~3XG$i{"lK9U0Iݻ=NΊ^v?L{7TpQE0,THO{a{<]'.G/AOmJYE\I&Zu>d{R̚<[-+g:.ٜWȺgNEjQljmMD&1B*Il I<қ)>;w׺_np n'ƶv!,?ڢ;pO2jͬ ?d[v dFYm$Ik>b$R()JJRR()JJRR()JJRR()JJRR()JUcDjo:vڤ sw~-~nN#1z~ZOݭ$ #SktaK2a]c<ִG<󃊮]\VE["P.k..-V$'8]Iݍ%Kk)$#O?m0+KtBVO#!GjCg7 Ɏ8!۰U%.DirRLF:=+Smx qK\0Ԥ`Ci|G5j 2Ba ,\>{+#XܫCJ[wC:Zi{zhq>h0-+(FI!\ζ F<ɑ`=mC)P*9|^nԱu**)pmVDgʬnj]N.n!<|\g⮾W -DQ~Ցtu;,iWoM4i6 ZNf,opiTH! ?Aʺj-Ù.6Nb|gabqT=Y 5`C)kuuu؈+dvKFaT ]oR()JJRR()JJRR()JJRR()JJRR()JJRR()JJRR()JJRR()JJRV-fV-T1z~ZOݮGb8?[IřM-jZF+kRr:%ڡKFz,Ÿi]RJw\` '97WU t=(x:yG)Oqu>1isyt|ؐYYoטJÎ9oqը@(gP$S8>p!o|$?g>cV=-Z{N;n~;ma8➑+Ԭ| \ƷȹYd.TJ2y PA Q%/|pDZOmydH[[qz7L,y i竮VwJmGePK)Ig()Q%@'S BI QڑNWkmslæ)6Hŋ[62ųK.!g))mJJPVRNG1+l6E"}-Zmlsٺޭp%I ѵ]`+UyE퀳j3ZΉsvۭwmjKK-`$Km}!lZHȶ'}c4uÄT{Cl̊]iqԤdkd!]쳖zqu&_%.#!VZ-%#Op^&޸n49ݟ(r=`sTߓEIDO70[ݦi][{wx آ2qhJ `XMkSGk-QiCvŦ-DBC萄5)%>VG*٬7$^m(FĆRU 忾VYTr%h)$]+\xo$R7?\婴[GxY f^#>kRQ(RBTHWYzEV)ףLun xt6Mv\_HqOJjl-`y|FzWWu|+vp4{WIΉFcp[S浄#*%@D)JT)JP R)@)JP R)@)JP R)@)JP R)@)JP R)@)JP #PnǕ5t+.S!E[^JHJj Rs݆yzѢ8t,7x=;;%P\D.eלA.(Q~Is|G#W =i10.3"%eRBBR)XNRO,ȧߠCv*\~C)B2lޥ>2Um#XuSX5]v(ILWB9;:2.ӸN);eZif]YRT{4v,@NOIISu~vMB7K|aKRiEa."@hhLe HZj))ނPBPz -TƲjl0 ]\}+hPYX#wHJUѫi=n^$MsVjU[Xŋm/-p%R9)Jpĝ?r9pzf[]E.IHl/%*YXug ܺOxk·nU#MT[!#'mxdhufՖL\i҅⸕VA;I?0k[¢n^^T Xr5{=}SE|.KJdF00q,q@:+v=缐i*Epj/V]&-3p7< $mXB'n+xdWx-wjU*[n$$( XB'n*b5vJi swůN,nFc<&)1mS:R:UΩS&zzgyAHv?>E[z$7;*/^TVbMO'n>sήuw,ʞW/2Xl (O$@r:y*9e)*.[aܜP.($wVI`$vb|Qvgv9BPL$s]䵤8ȟC$'i׹mXE Ĥ('fQmwb=&BP㒵qzՌ+<u;E%**dr XQ}xE՟aRyr;^jOod.ѧH)u,txmik;) |窹VY% m¸C&*(.tw:Wo?]͍:WNT Ij#֑^%3rz`%y< ca gmVꚇ'JR4R( ;ͮy9nfl7 T^N䨥AI$|JH?Ve)@)JP R)@)JP R)@)JP R)@)JP R)@)JP R)@)JP R)@+,XRbeuw-G*QY'=JJR4(Ň!+֔(aWeaWJ> swwS7ko߽Яݷ5rwşN,nF0+I*ǚǺ4Ç3 {Y@)l!;2$]p@QJJaUnީmmK)|㯲`*ې0~*4haB urlNjuhtxۅVz;cZ-4[$kx&\^JN2w[P޼Xfs )ۜe_S3tmz\rZD+u [yn/5yZMR_k[T7ՒzUc꣮͢ӷ]ĵ86˾ZR[Nn8 \IniZ%1$W䀖Lu =+rvfn]>tҫm USޠȞv0r8&- VKA'3S5xvնXtC*HQi2s@R)\} 5fXS$L"ڠG JB9T4SFӷ #ϒL6Uf@GD%`\wTR?BzgURbIJc:0CImńlB3@}׺J&l[-Ʉyq B\qA ! Vq 6W`BMJN`_ ע\[PunB Hڭ)vx4&61]&LRy '+yTWNs+4&*)@)JP R)@)JP R)@)JP R)@)JP R)@)JP R)@)JX&{M-yZ#[ !^q6 NgmK*ɾoҪ8KԮ͍4\DHHp d+͊};z=jsp[KÌӨz"|TNYJHjԵ6+|_+ùӜiq#VMUmF]f6n3]/!P l)t="t6ַH#Sϝ7hkJX q~-Pu ()F¤/~!TQL ݴ0p# WZfʙV5I%dg]3\Y/Ͷ25FO q(x< ,9&:^W{^8q8ۛ%*u.s(hPRyϺpYܥKbרa^"KʂZSVT-J+R*wqʝZKVDֽW9W8*,m$ǔL6`-`)D iM/I[mC=(S nZ0;*MJrD<RԎtaZ\2Y8%=$>@ u7&NcOͅlneciiJc62 Ӑy[vsMqWpL܎3e6rIh{pR0j7~w7Z5~7ۛʑބ@dR;>6qo-ѧi}ka %(RUE` aҥ\ y?,Nq)@)JP R)@)JP R)@)JP R)@)JP R)@)JP R)@)JP 3~Mvm M+Kq.t}NǪSjyAޚ)\8Ռ\:tMHoCLFSE8.o9B~QȖh}k,ثamS,v ǙO^Ofxr엎[e R(JJ1Ҝ;Րv)R`a.vj:\h%t(-{$~)ם1x3m_.]qNqR4f:HTw<YJA emԮ+\Zeiƭ1$3"*P[@e[NܭJW)w;<(QXuvNbnKsVofKPm)A-&i|3*Z̬9|3*ZBhwhi+i͇RhkjНz&wbk= 4d*HR15y]VG#2nŊuݱ;O>Tvzo+c!\[n˨՟?^yvN`INu\ȜTUZDCT;oƻs՛R8q㆞Bbd;e6Gc8Uftz1&ARd`'ጞ@_o5M֛.#e㠥-H*_3(@$׊_`ᖟܪ =hz58tTkIIcKtVvE~SVKfU!e%q[ds;Tp1bleTC:Ky (ُVi=u)B)Dj[Ym4mﺭ_ss8|dVUH[0M͛T JAal;0_!DetIN+%c9QxvIt4Tw6n\z-Q)JWI)@)JP R)@)JP R)@)JP R)@)JP R)@)JP R)@)JP R)@)JP R)@)JP Rk2hf p'wɟ pwɟV9~{tuKA{"b̯k!Xdy՞uKk[ȋ,NF=9޷qܱYV(HKG!|G]Rmj!D(aAX׀9sZJ8&tP}ox/SʓGWwcEӚylpЦu9;;N]U(w]~F-r{ RVY:gkvU\}zqN^@>N㑻n9vsȩY\҃]Hx2'gwn> mrNJm]m2GZOy;3ɵnR)$vn|đ[nh!oG(+5vcf]o+kĻJJJv)Mz u{?P2m4V4i )ܲH9R'54Nos;y&H( B-R04|(*؍\RP)JP R)@)JP R)@)JP R)@)JP R)@)JP R)@*u{r*șĔ:2V% O U^sV5CXjkXj7$)WzA5shRrG*י/k?"'u.[M3uejR'Qmm€ŰB4wT{MTH\,} yח:U$Rk8fޚVi]c.;θ0A=K $$o[+_=2d4hĩDunq]}uz4N?*S *|pJs^Ꭵn7u+YrkMFR*X|i֖zum٘D/=0)<㚍d41-Z@kb؇kjB<FNrmz P87YaH{ӽT (/,q帪nrqZ:>N\'] &;Cm`#Ԓml*6wQ[q54MmܸxFu- J7J@ɩ4=`y?q:LIIqT@$%%(HZZwXK;:5=ãa i ;qO:cYw-[bzj榽^SMq}Ţ=і+F^Sm);ysxKֺ0#fFpy{M%RFXQqJN@cOh4[7mM|wVb$90idMrQ3MhF획/zeDȰ!F+ 2 WWSWe9{߉v |jfhZ C̸P||o-|3*Zh+nґn,vS\\=%aN8rH BGbR?HNpO' ݮoy|pi(VV9 |cw,h1^  Lt !UoFt6[nB'%A)Iʲ#E&7Own,Vcru#sYyr/xw$)I'6qSόg ^88^9|u/"%='8{w?ee6JBt ԛg"{yz-՞[ [+;HRO,|56;e 0ʹHZI+OH`CtV\quZ|V"4 V%Y1ä%#$Fԗuw=6uW}J3!ґ94V~NVd i]TIV:TWxkAߥOwh;Zw\|_UJOwh;㖯~>>w/˥qZֻTW}JKN;geҸ-_]*xkAߥO~곲\i㖯~>w/˥qZֻTW}JKN;geҸ-_]*xkAߥO~곲\i㖯~>w/˥qZֻTW}JKN;geҸ-_]*xkAߥO~곲\i㖯~>w/˥qZֻTW}JKN;geҸ-_]*xkAߥO~곲\i㖯~D:>vg7`[o@LHtQ<] )Z. pwɟ pwɟ%iLFjRTYSzc29qĸ% X8JRA=Hr C _ʼYb W􁰒r%G8˟ B)S'K]31ĔDRӹi Jg9WѦ'b;L!#JT/d#, vV:7LmСrG*\ʔ[)#qʼ[hbzy&r(mQI6|}6)]e-C(]ltUp$֛)q _ @R k7[[tݶ")h2_+ ojr绞qMXJr\8yKҖQ8VH9PN3Z> 'i^K^HNW,vֱHfqUW%Ǣdʶt3iMз:'Tj;KķYj*Y_1•)$)D 9MDҕ)A)IRd^ ]!Rs`H]@vT m\EJZ[iFKEڶ~Lrz")'{>K**&Zr㧯ؽq+2zH41jϨ&^ ]*x򂼔l~MNBu$ǻ%HtC%JIq]蔃SHNqb}+M*>ۯΘww{eJWLNTWk- CHWQ?Z("fFKif F i)T Ց:v{e =0ԧe*$eK@%e9@Q1c[`ܪ vE+mZϕeEҭ3؈ _r:ځˆn8hELC4cqB3VS4G Ř) "[JRT~rІ\P+ϻܴqCfǭ'kHK΁yʶʻjb=mbpb+E!ݩx:e\2@?. ilƹǎ#XҰGE-:+鵫u+{۱;yY-kp-sfcSs՝4"Ir4aն T bScqC*O#μzmAqn,G!-N.8- ;NҢH<+GnЙ}>5fҗ |^sYU\lżt; snzumjͥbtW)Ĉ (_nk@=pc@k'mnjX0v*2Uꩊۥ)ڕ[2m1=ݺ*lIP*4Fy? (P%ۮ:Ӣ`m71oseG4)$uM~.#NJժ \.12PBTHV~gkzVE/W6sx`h1;w]7x*G86V]ԐSGY=;t^ 09y5JT6s pDxJRPR()JJRR()JJRR()JJRR()JJRRձGmQÄJS (KQ*/ O|_oKFO{lSٝ۱uk9 $yg֯wBcƶ"pf T.FZZPWQ #= aaղ % dz':4íIWܑjBLLD0\+n6=1.H%X+'oZڤ6ȫ-^KRUggZ*$|?"'.u cőXR[s##jϕx&,D\xBAؕN*uJ2L{#ʃmHk`άko3;͕\-6i]RJW}&چ 47M(OP[梾e+KdFBc{i-t=|[[׍f72ۧtҦ*C*;Ψ%yU"mQ:FԻ9K)Z"՜[bN$hQ]=ޗ;+9ɟlYq1$IɟlYq1$Wk.~kT)BoQB &Q=BsdYx)nw{vԯP\vbqDl~Wo6 ix}]|F. eZRR1{TW[t=)*S(l$z:Uܿc+dȢƴ `PZ^5}?&SG7k5}?&SG7k%i5[ed*D6 lI`C^6+2 ;|&-JW9=uηS!tYM=fL8 &-ƝHQ*ąOaJ[-M%I$lX_>rCd͚VmQ̊dQU_Lr{)6h!`S܊vU[#pOYbM JNy* r^nQe4!MZVמʺup(P'|*.-U:&-6QGzdrZz)ũu{SzAZ$޴aN$IWMQ}Ke(6)9G#_ړPr3o) rRZPRI<9GK47W}{F`>a].HJR2;:]ummH^skkM_}ѧK5me5Fܟ3R).s}R,C G#n21oĒv:˅ NFHK47W}{FLD;sui˝}Zܦș!@2k&Vʁ7˛֦:q7W}{F&U/w,*hέhO9:SI5F-e%>b3^w=G.q|K`Vud7W}{F&U/wTVUzpIMKs% M}]PSSH쬫ek[m|&PZbBIW.xVgK47W}{FTmxLȟ:C*fD/6TA?^\6t4vdmgT=ߣOughmShMh!zxZw(Qw9[k.BCO&ꢦ\-iP=8ouginR~R7eQp N'yyo:-ŨjQQ<5K47W}{FV+UʘZf7W}{F&U/wDLEJT=ߣOugizˊVh[uginR~>"OYqCU3+{nR~"OYqCU3+{nR~[nDe-$wIuginR~UGfN9{YbGBï)HF890DJ&3 [!d!J)$df&U/w_6ΕK{Lǽߧ/&,}{e!(̑_]cKs)VܙKq!X#8Q#nUnR~<}=Gn;Ҫu2# 6Qq*؞cm *Y͗s D4&2$-HڵA(p!`(y=uuPj曉(lTKaĥMผ, RRm`9it s<6քd1-̓Zڲw m2P\W*ؼ('c>P 6WT HqWO+Q|z.V7(iDQ +ji;q_ &Ʒ1k[U>Rw( Y!DYji;qSGDjiݦviͺqq-[AZz8-Fʍ=5=_6SG7k5=_6SG7kI{JG>A>^ D}'* PEݯqml&Hg^w.W B.kq EaO: )[PT'ʸA٢WcxɂnnPhV%}^sV'۩s{=˃Fz_SwM/_GG-:j צ*Unr ZRMJ)[B@'ZlcC+Pm h k啤ct`Km ¼sCԹOM_W_>[u]޽|3^ɽK X.w9k疶HWх.(FnZm3m׻e6e!aRU~O+'8Z_V?5}]aT'Lᕵ67WqQS R ϑ)QV7s{|_e <6nO^UPi YsWM=#-N) -G̓P;]!8{sq[e,Z(:"cRv))HJqU۫snj9Mxs!chIEb >=VLK}2$ =Rw_Wo= m¹\w13To܁cYxnp$0(*;.jan m8DŽu;*TR ὾DY r!(aI H^fkw_eX8mBhƔ@R1茗CVoQR xdaK qJk=Eȫ[Jt-GJS~9Lw(#x8XېyyJwP4!ޒ[iOIQl9VչOM_WS*U_ ݗ=oٗ{P8W6v4b76&͔nLkjH7 a }:v,r*X%ĤJQl#׿n}jK8@omv[Wi-e}[×t}CfYchz6`IP u34tl #wk~ n0%Ajm !^Iun~W֯SwZG~e|vKےAi!@sjpW%G|Yw57^/:zm]ZMƷP*/  nM\3-l<+[`J{Jw(ϙ''ۭsCnK{ٔ3u~WֹOM_WQybW{tu~WTőٴ2u~W׹OQ_WMSkGfҸ׹OQ_WO^>E}]5O1YJ/^>E}]>{u,ͥqۯsC}??j+ybW??j+\'x,ΥqعOaQ_W^ӹ;nxM\æx,ɥq۳sn{ڊjbW}7??j+'xn;>n{ڊ\'xn;Bn{ڊ}7??j+5ϥqڹOaQ_W_>ݫthRڹOaQ_WOj=E}]5{v+ݫtv~WWi;Bnyڊ}w??j+4Jnݻt4Jnݻt4JnݻuƞjzKbͨnd}ݼm=@PMwkfڷ6>fNzzaj_iGt<R*3vWF!tnԨ1̊饛ueISزE9=&&AO%5_nyڊݥ*(q1xim`8p7q)ܵG s$' Bp:ũþ\%MKQVո@QRs cPBX|4Ǝm5q(BGz??j+꺵J7^ĩ:4Rj9wVy˷ >]%Á+}훊BR[Y|6^\F>;N |Z~姜C-2C+}ݡEN%)l'UY'ۻsC}k~8t]Jsv^7|N6{i- fK] ڄ5橴-q۹sCչ:( )ۏ?B:WC5=_6SG7jaHODu|XWn$I: d6d95hVHmupen64plus-qt-1.15/resources/images/000077500000000000000000000000001413256700400175455ustar00rootroot00000000000000mupen64plus-qt-1.15/resources/images/keyboard.png000066400000000000000000001260771413256700400220700ustar00rootroot00000000000000PNG  IHDRR2 sBIT|d pHYs.#.#x?vtEXtSoftwarewww.inkscape.org<tEXtTitleKeyboard5EtEXtAuthorFEVRIER Dorian$tEXtCreation Time20100222{ IDATxw\Uǿ{oKB&' vA}@"A ("bHfH  [f?֞d2;s̹~>3s>9s^{6lr0 lnf)/2~s%9}}oз*s諥/F\nNO`_[ep5|=Ve}|}H䋍:-A~B_c4}Ͳ~eqFV75H,yC_S6=87k~B_eV}Y6_Թ]̪ }YUeAW<<  Ǜv?* kN>IuXC_eV}-&o*i~Pn8.CV=lfNҪAwTgmGm}߀݁H.5n3/eov{`?;fER* >Ual3eC3vw;kǙNlSaR3N˄%9G[ H7{[|ޏs=yW]C[5%yWǁ_,eO?lqV F"U GJ_e:9 IK@[ Gj[=Ӓcf@_kEifrI a87᠔ q'/{Z^\<7De^pd}wßwJ}?ϗ[%]i}0C߁[ÁwIzrOvxΩxifcwIzCRh3gvK^K03k`8O`X[]/e8\ifGW[Jla#4Xͨu L_>IK.ñlfqFxeaZ;%?W7H3|OٝmTJ5}^/k,_ ,V6ZMsfvDZsI[^ʣ)T{G[ldfOe/?C? }$}7NϮ4f}8hHȲ+pC 6ʙx%df dI٧v6yyv90Ki 8pXnWni/ e8VE8I5_o~`-I72мJ`fH:23gHgјx: RʅlF8R Pi! i\ΝR†61 7l;+;Fc%gf?u?~_4|I7Y֗Bfߜρ7cZNtIoQ`]<->DDF< *D3{0:^x;߁o7HO9ID;fvwxZf1Iyf6i\0vI坉o3𒇀lPv_=@kT(N혚K_z6eNM}ɣC+Sv6ʒc$W2pHz xRZF5}ƍRS~6~^M+fj~Kp۬T֓SV4$6}s3˖&gY%our0nyY0&o1m r} 3; >|DҶs)k(2}\WRvP~kq/:Ҵ5'{4C˖C-WiE0RK%xq07evI{{$^!v~>xVA]+!i7"0ȡ n<¤%]u:1Rt@ˈFқqo!6oKIE/fKIsl ʞ;]fv\jܽ+F: ($#*ըW~&=y|>8̾)G2XpFqJ=wCCjg^r\“zL(C3>^A"mLgVD_$iMѨ~E}xXi!IgJj5RM_)gUk}  iss#}mJ*޿f6K OŎ髓X9ԚxC[V[=H vyn62AtI ,Ix CUN},hKz%Ad8x8 64uX\xxͦlKt㡫Gҁx~CmfX1$daf#- rl<˂:6|zJ1S͛OKV^bf_KdFnqO-W~)>͊9ZYk °Id]\K[sRv!i=oF$Z2߅{1Nfℷd{G*vN}=uS [nKu2p鴞2HVѹ #o)80m#5{- <h $Cyٲ?>gIlW,3 <&z`xt$Կ H{=pUO8$pwYCͬ7&i:Y1yWGU^*>`><ıٵCvϣ%qy(`pZTxZ|Զ6ć]YU˩yx>٭y'Zl8[|(3{fe vn7Z/ 2g>LE^ߏ?t fxG mW,;mlY99A)-k[TYwOkDU}fvpwC<,6-Q|.h5Ui}Wuxqx?w>3 !.+ɐp~[PcK7wlq$K^VgSfvs^}Uد|Ҭr'#T5Z;$,l[XMTq޲PK_ }j*j='C+v>&\?6wwER_xݶReT:-V9x #dH Z߬t}ƲJoVߝ Xv   `   &a8AAA5 1   IAAAAMp   jcAAAP0   AAA$   &a8AAA5 1   IAAAAMp   jcAAAP0   D;;8f+ }YU5u#ZLWUE_ }ASn81M }͑w}Yph Akk%yC_s]_ }Aie!F,],K ҅nFV75ĈӗyC_ݴ@fi AAAAAAAAMp   jcAAAP0   AAA$   &a8AAA5 1   IAAAAMp   jcAAAP0   88 بZ ̞*.'5Gk9B_s]_Y5$z*wg B_C/ $ [gϙ!F> 38B_B_Luf'`iЗ7NCߪ ؀|9B_s}#Nk)[|}Л4Ć,͇Ƚ!^]v29B_s}#N/ >ҦsVۅԫYnvj>Wp.nՑ%m|=Pmjȃס*{3qMk9/4pnqʑT*S w'~ }i ǖbfKz8x_eJJ}̖ cYhJn-wf6XvN \mf2ҷ>pr}yf620KX  | f1;ܧ3 ` 7[j+~]\ \g)={/p=T6[W3:>\jfSRPc]uEw37̲ ̾]fkݥ,G/3Hn  LnCXV4pyŬtAA6:3tZG^pkHL!FKz%1 ?cdr%g"`,ÁXk%oG♾fnHz4noe6QFLFQpBT:U>(=;k%d0< m xXݏK* jI-7^mHzi`OܸjK%g^C~'iRx8CYܑ@fƙ-ko ؘY~M!inH#i6RA08` $[i&35W07,78a&KMSQf68#0}&TDҗqz06 o\XK{?5_J|8=;װ׼^_Gw cߏqY*R~uTc_#{~^5Km3XW$afW5PΧv` z,UIå3JČYգ'izKwp@~o\ Eoh~ ll ̓<fY&t07 ӀӒDwl9}"#ig<kp0uc%Uҵfvs8"nVv ܋PW0YҨT1[Jmx[ןڡAX\`fm$16doJQn 37X o#0β6=s~=#xև3 %AI/ރWʾw5YxHpv G`+3O +fvZzoCoTe⡭M!4 Qt-p(W^3/ܨ%B˽gYzncfV֋et/E3[Ty@%]-黒J}-yOP\} H(O",N!iIH:~ /dTfbjJ!4?Hf,Sx;%8l?ާ'5d,*q{nBJHW[g3.B'»Z tVaG1艋CiѸAXL%p8o ^9n iS(^_> _76^<0Yz{yxd9xkʍ[`4t3צe -7\>^4mexI]yQI .Kto4hU2$P"\cg!2G1Y֘G['y;:YblfK,?Z7h"iJH<L J:LJz \|(n\#໒cSƸavIz.ߛ*]nf?O/#hMH}C_'n,o fo0I2$]LÅ=-7xo-ŻUib%Uҍx+.fpa+~V^p2 3UVw=0t Ij$8od|_"!&Yr%K$`f[Hx-Q(#o<ٗ=K^įe px䃣Iqc8[ҷqOx7£AٛG,#tO*uO\ r㢙n*#{O %xn>Gy4?'j0%į@K);H^7JUSbnЍG8MңxH";^򎓌߿⛦u~f6kC!rm|fʒYG4 ' ْd} 9ٻOPΘ-̺i){2IWស$cm09P;vx;~mlKSV22_{&*=IcmF^@FUu/_IS>% ~n7:<ͩx_'oނ.ybjC֫@<Ĵ?UjfWWKox?),?=6Qf$Y1ܢ"~xu0ހ3O+I" 3BRǫ̀?.43q<}Oiý~;^F6#378"He˪vb",$͗S_mƠ5]s}ⵙE%nEj&i5}_N cfU%R}L[an[^.xII^Vу˳ Ff oPX=x8k7 .`qw1~<74}ƽJm_un{'84& y?s$7NvlY 8p#{T<|9Fog{}p580[Aha5B>q7ƇȮ7h##pLqf ؗwD'➨[TC6Ȱuf[|؏ֵX}-+J.dDdcqxeFާH3kd{)~̊CML&ƽ2yɣT7C67Ok32oX17֭ a3 %7xl4L+'>tlNb^ـ?$twvAdf5acPN촆 "d{`hFᘼgP΢,ʩPIĽc\=J_opyxb\[hWae a'v;g??aHڮ~O ʗfk&oHH HgePVRL0iq ex)$7Ucσ | B{G&#p\IycP'A,L7rAʘ{!pQnvCǍ # DjA i\U H^Z^{uZG)f6(IOA؊C#V AAA"AAA$   &a8AAA5 1   IAAAAMp   jcAAAP0   AAA$   &a8AAA5 1   IAAAAMzktTv29B_s}#Ni$Ѯc qvoܺB_eFrq49B_s}#V!}ek7B:Ak\鴈2]6#5Gk9B_s]_)?r7`,|}Л4,C_c^7_I~B_s}#5G坕_Hn`t%f҅n*kIo;fY0 bfdƘ    ODV   &a8AAA5'`kiwG3{C_s}#5G+,%7rApP0ME}"i*p [!F>Yx֩Uu:(N9/B_ x}~SsF_FȷsJGBߪ ªVey9B_s}#,d\^#'qf\^lY6#z2C}k;{ҷ;[VZL>s;r}}}QŎ }ͯWrE7 ئsrۅԫYv#~ ËWn8^afWOHz=;{շV[gPn(Vz \?B_EB_sԫowtq~]%A?Pmeq\?B_EB_h6Ҏ#.'U^}@'~ }i F_ l:>ذ"io<.~p/3)=fs=ǣ>.̸;d-`„ ˖?C;6hXFcG$@េl(ӷx :~k%>2&]'la3ppۀ }*,33 M%ffeYnHضª>3;z*!i#h<|lٸ:^ ƶ iMI.6^ܛnSj]{<˖=x+}7p .D=(VNvUj ORdqAҔ I]O ;m4&>Mcf-N2 hzT`"k87Ͽ2NnfGU-gMV`3> 8(+1j|+j*r%&I[Ic;FV\ۋ \ER/pa#IYI>]ꨰ*H'iT;Uhf* Nv-]VnqsxW*/UǤmrB\;;ŅNΓt=NAECn„ Z̜9cf<L8'xbŋ3eʔaMWљ칦O}xzQ)x (>)5sPd |˯*[ݭ3o7vSl7I#C~U4ApI:JEo"@7p4E͈I>|Ҕ^9e7a;(rn)?8;#fƬYxk^ÕW^I?̚5' qD*ByoH#xk;׀ClageU6rf4W 3{Rҝ֒1f'E\cIm~G5`f^Y葖tE[P+9x(IA'aYKG\8 OmT'iw]Qϙٷ%ZG'FI9YI{r_loIk JfA\3tOT U+l=3[ ܂}J(o>0/ࡌ[#Hd3Ҵ^ w|o}Iƍÿ7u qGۀ9x8o ϛ/z%fvo$.7HCgqiYxRfv@K4!O\0Ix3+o/ QR5@.:新a|gKiExXp!&M)S0k,LٳmX`=ѣG.qPdsg!St9^I~%3ÚF'ᙵI \R͘i,0԰ctJyՌƩWE\#T$|8^谤Z-qfV? Zafxo`MЛE6M9kZjf(H  xKlŻU=<ŽaӁ8^竁{\3{15lIrNя{ON*,BIƼ#i<{%G>+gN3~܋g^̻{2#}N-^Tv)hȍ;'㏯Џqԩ_bܹ,Y^3c6㡇bܸq3fZq̦e[xY"Id,c)gtETn8OAXd Z@ߓ=Eh} wmK͞ x3xKVqO[IčxBzf\宊ȹ%7xb^<$33I7=gӁْNY?5nX_qWIIgBU bx2_% l|o$-OyT3i!% ˢ~7vN0_$}xR{qe8VӀٹiVx6Z쌻s=+i6qu&a u@ oO<%K ͱFqW3sL&Nŋr-eĉߊc׃e,6%ef_eI{nM5stBgwXAV!f<3!lPI~ۀ\=kfQG끟ۊyfZp8vU%Pae~~I a(_y .mJY3glfFoo/Ǐ箻bcܸqԩSYt)K.P(D< \VUd5!o툢JcD8\,z6+0_ hbz͡50 3 *ir㸧֚1pAV̬>ۇ{/^"L^$}qXy<(d!0VBҗ%u$|:!O p4ܝSI7Jz8¦H-">ǔOxK$!O歁}C#K)SǺ뮻q 7d…:l,^n IlF,\vHzl:/s%C>OUҙ!|O^pI aY!hf0HL;PΘ?1X0W9o\Gy!,I]>\at86G/IWIqv-' U= Y3_1_*|<x[:˩;+ lc{}Ҵ4j(<@ƌwܑ[ɓ'} saԩ<,]ފfd8OU9oE\=[r̯cx\KYs[`1pGwHOրit]6FA ,6 5n 2zRE/~h[OtGNƕU')Q䁞@ )^yrx X^r6BS=yW%xZ7!m.@0fvkH̦Xn.r}Ǔ MY25̾f-+M'O^aP(0j(ƎoΒ%K?>뮻.3gdҤIl=^t1j{_4eX2^p$*i$SwVR/w8p~|[$}AbfKLws i{I]ֆhfvA1ifՁnrΖ3W(Xx1k6  A$9F]p\hǏ;c00RySO馛ѣm[OMRmmT6.|DX/4>X(c6,~ݣ 1 35}ӚF=9sI3)p1\lL<ɓ'/3+S(=z4]]]l&#-x̒6袑$i]̞k*<Ү?9k$ ldӺf߮ߓ5a8j l`ԩ Յ$lgA15רeFIsR1A%}"}s[$m\ 7 #2`ߙ-헿"f63pqo`aI?.N(DA8C8:p@Bk1T3%1fx,YBPX}~}njSh 12Zgb>gQ+Fcw0  \+ifJ-I;㡚7;k%Ed`m`K|.VIק%mĊoB~ iMlf~MsuGq 9&c%RjA1U Q[XTZ_dAeA= l!S;1+'Ց'$. {J] ;w"0T)i-KpÄc2KCN51. hjaih ,w.}3}XK?+-kS+yu7Ei$MkױC8;t7{*1Mx`Vr{gΜ̙). IDAT%, ~} *jGkз7(af/tZH7ϗ/1InVxݟl[sܹ̜9.9}>gÌ#U.}/FO]l?#O,~pGί?roq=a/`ȪlTuy% u3봺_Qg|%AWzrWfvqGtn.qwo۴xufo{lZ=fI"a1S^I, 5\V.z.'5_\vk+1Jr#*ͪ|]}V0J Dz*~Yiʬ|eVBA^Wr1rd``n5Xv!s_ {`ܸqL:ɓ'w\ߜ9sxG03FŚkĉ0aBG_IUX5oXIpx}l|@ͬ’2E8ge>.&MhY656>}x \K;3yo7cFuNWh{XeSA׀]l&˹?`.m3!hZ+ɊVA0ˉsH(V=mfYNU$X |gUu\ !i\iDQ#p7sUW1̍F/"{.v L>yn&}YfΜm%\ŋ;W'MNa@^ǀ5yk$Im;Mc+xzOgSR޽ Xn.l:j4bp}p.;j ,3(R mw "i<Nk)a^x'+֯^IQH8ގ=zfvp d3b0}8j wܑ{|w޹궭6|J[8C^`>34,XW\_a2έDTRfvJutDXIFp=~8uL =1LwJvG9+=GWhlxԍWPZp̚O|>M|ovޛqߧH,66e ̒ŀsCn&@XÒf]$H q 7lƶ,Y>#ɣ3V={3ݒtW:tK]ƈ$oݺhްɊ5{ň> |lJor藁3RU~@DzEg0En/~s6w,"„|/y90kƇl6fdqqcǎ׼h4ʁػwoKLY>,1P[諴?v'9t\sM+McNo(W뮨)6-d-MO_r}[ߚ}׀570p%p^x߯5)pmXo ]`# ,䔘+cGMr*G=ͱ+ċ"|"7ĸ"Rt_~[h֔Zy]6Z._ <.*~ Gjtq܍d"o/;D1pozLjPD6`1yx]Dz_n#(D!9x DwùsUT='kosW^y%6l```)l6-+^^l޼ӧO*<&DeXU'ܭ;E|L`KfJ4=QѤի8>Y~]~.]iTX05U__ڮ\/۷jL0&5ڑ_nB?X'/Y~q1߶#gs=EN&=dE4'/}3Y]D/{ c`>U{F-A탯SZ ` g<sB`"ɖ*k1Ռ:PH[L>LoFUS"2\cxU~7O&J)`kE9pW_}5JOO6my{ފ&<<#NNQUbpvj퇇yȽ˷m4H]vq8N:_D"DSo},dYbr 560Ei"2 U/]U)f f1[[!@q*~ƼF@B6V7e9;%foQlba@)dd}fK)dA\pRpfụ~ƌݽ"N@+Cl/s+$=-q<ላ#S]9A(,/6pբFtY V!HSUz~-*"W[̰s88fe}Z'T3SKgΜԩSw}ڵݻkN9t8}ů}gaaX,Foo/ϟ9bV|`Ut<\^: lZmZ6>@DVXV2%5F,xY[P;Xp R:eouy9}Ϛrc!‰+, 7HI(Bn!$"8,zL%yߧ)8sSow=O d9GIڬ#-@őQH9EdْRS͔` ԦZ. d| -VP,'$"q̵A;G3dC"ohc/Z*GU^+"@JU=RY>oH.U}_DE&8}~8Kwwwa> NbdddEøyfFFF 5+FWՋ޽1>`!ԇlݺurM1e}d2 tiJh4ZUD~CUgU"m̐xA]iGL`s>nO|pW}E=5WYMhD ~.~N+xϽ_6u*xݨD)/ԗ6F|;~N2&@H,kEh@|Ep nPx8MQx< +~wgٜ/SoHa#8SibLK`2Fj=-"dX7r3}1<@Sl-TbmŠ8V0o_+[^E֋bC㏳o>:t-[T4 #">}t:͎;W Qa)5UyUD Q"a0*JN!1erD)MWPRdԘE_!yQxuPlK^Rk~di4YO!CZd=!Yl#q6F-5ys>1-="^:~KSiM錪+_JH/zы8<͛K S}x<"~^ӓo->qD"ttt͚֭[9uXP(Tئd2I$8pD"Q,Z[rm/,,p58xپ};_=333;vy8hqJY# S5mQՇEybVWjG1S,[_x8#7)z܂ $-܄9}kzu~pp߅.]|Q#ISq Yp:< ,KmLKnd R:"u::NW` w-2Ybl6/CU1MꯍW5+r i_Yq }2 tЇ,?{@W!!:\r7C?0I}5Q0& ?v^`J}L1OöPL^n|^Q²dDW[b(Vȣ#"`t|Sh젪|3ীT5-m5$oB:;;XfSD"6mڴ0jIuҼ+ޱzqmVxfdtt[2::J:.|uynB8[l)RBWuFDQbw} qegfff8rϟ/Fc15abSY-AaCRVȥ_f!\T{-膅M6ŕȐ>nZpcKLWb|m5fej' QUrI!  dXP'?GJy u]NnMZW}a@TFdtE! 2EjL㼧x!@|' y yj!c }̩i!O+s#〣&E<3bU=j ##ֱPu8A%S~ 7oFD8{,D ʯ:Fyꩧadd'Nq]H&xbTt-7PX, bǎݻšL&I$Ua4tx$F cv!ܰI tƒ7e̔oɝsp‚8z EHBIb=f ~XBꐃD7į$ K!بi.;iNT)M@3/w&KW#Ƞ'ppmV/DS! x>&4I}EW#$Y 0SUp|H?*ƘV%BHxʐ $bƱ8ƁT|;288HWWp✄^M=D"BBɓR)bX!ڸo>RhJ}7S_8HN8y _Hoo/===t!K*GB/G|_f4qAB#r%SS͔F>8a8dƕFS>~JҘBq&6 k/-X"b + *datv(Jℜۨ#yIMѵ_z QޜES8J݂"h^H w&CHu,Jq?9aoTQzxZ#,SO,ᙑ73IF q5Y?bav…&|M=(16hXXX 뺜>}\.ƍ馛ellP41/d":& Μ9Ö-[ضmGu]$q{BZZEMyd2Y\\dxx;wFYXXȑ#M5eT1e()yky܄Cn1aYrCˢacT3nTHO !lɝqp\R x(,XULyhvPN-q=z' ~ uh4Zh#Z::ŒD<ɲ LШTOgyF8D#|'!.)|fr>9_ G\ ?!-B;DE|‘TÜ8rnKsּ,5d%Sbr)n֭[allSNRE bm8~8$Bd]~jL&C2dqqvM$aaa ,OsV0eNDIaq'<T">s!1Zlr=oP_?P!3٨dr)]U4 ڸil>]և&YKA.HWeaLX;8Y^u,MW q3Gxy,´3ʜ* |"Is=y t q7QgSY0)!L8tC,.&PUe939⅄8ĂZAtjZ,5uR$/G*ɓ922–-[ɓH*4C_x<Ι3ga۶m! d)qL1JRjzV\48.q!}!2zBphhc Q9Ӡؙ#m0O8.v DE/HW1F73(a+]忽,#QzޢuҪ<<1)lp 8Q:E98AvsS'RYL!DJ`X:qQ,seiJb<ƱV2>5Qt:ͱc8q۷o/Q?f+::S:W\Qmf#144ԐaѲj,qA&grɜqn_3=+Ƣ y'IYģcEl/%Tqzahhk`:Tbkje2 .d]yi;;٠D=Oh.躒A[Q qF}fRYYORnG2øPlM(jSYZ8(]1QDq{><ͱuyV47_\HI0'Ȩ+D0q"3yy`0QĎ = T[u-KqK!"[w[[RDD>ZzbcƜU2.;v-.RGSF."O<BBEpiȅLY% 9,VMK-Z烄79٬&=t6۝^3+SA1.D0ƥOST(NK_M|9z:JƇ'91q 3B?BNabYNe ?X2nQI߰(aYAUע¬xA23vbK#"olKwTVY/q\%1+ d~ZT*šC8r;wY|ٳ qђymVZ7]t]cǎ1==M"Zkk-4V@:%w!;v˪ BFW(&d\S3|SVY#ҫt x\"1.6P`MY cJ$'LJV_! )f-5cQUp|Q3N;D>YIC4Cc } I?5EvB"x~r1E=Ȉy}5Wˌccm" +0THU H/jlܠf Kk""[(<@1N \YH$#8Xو>X,8cccB!K06quR)K2$hVN9P0JFDͼauDpVNZ[@(Lܤ! +K) %s-YCW|YjcևC'1/ǤBNM.HWSG5L\6.㾏%n;yF/ntDXf&!@HZH+$}:Jih_X,:-"AUl U~BD@x.6:״.6yRkODF)BQ}P|_־nMeuhOQAjTY-FjY)kPB((ҲT6[e)6U t"v,*]aR}NG+f 1 7;.G#x>*LQ/֐U0BL>ݎC}$\cU4 Dʜ:<+FW⮰2hU~qm J5_5Z쭥X+}y QF4YhYFqa%Vԇ':ҔUC B{k/=c6KqBO9"Х1@ Veϱ(JOHf|cF!<˱urՇOq1mzܒ7$2DEsXF5(tB,h"JUE9p.b%c,޸b5u곦Ѳ0= Z}鳬+aGD!=CbR {˜/OMí\~zQT(+p0xEѥHh Dqi?L*k>5M~ T{nX,Oq\%0=1?V5hwcYZG4vB!>xAS|:j L@HEt|}1h bb#FI!rfTՌwxbQߕw@>b\XXnz>k-+ަ'Ge(7Z`etI[2f:&*KO˗|)%#K,oKXfj`UBAX,KqUD6DI빡F333G;'_pߟ+Kī6E:.Чh6MTVq3mh V_5.*}RI&''[-*V_@?V`5O~/~xU/?hA9.,k͞WDEuưk 1>Khʇ3Z- ݎ~cOU$ұhV^bE6ʛG+ +꾗%*Mbq{+o`Ν]o+- ccX}ևg- ̕5*c5ϲĭ]KΩ~}{%Jc?BfiVzT)%rVvh=ƍДGd|a^Kf""?Im"V_cX},K&q(9Csε˭q,`W\_be+E*/7-7e^ +6aJijMÇ9w\;mCCCl߾}yfK:y$SSS޽esss>|8\qD"} kY" /:Λ\hfǁRUUU-~V,-UV{? \o8 |\UϵTXD gϪ-TCER;P?7:jh[ mk568}tqַʼn'[>l6Edy4Քգ/xG? äR)>>} ϯOoί&> Z)= LZ%)[E$|*xώ)4Y"@05U@Db""UދKi4ZX\D^ | sNo'^&7pKU-A"rcKU#O|?q?`?!NW1uvV}{ 7Tj"uEVcm^ zUjo>[獵g?-1foFx s뭷yl߾+&'';ٳgO{=вiU_ܮު:]U'/D"3:B]yN0%okJW VZ+U-nJM]m!"U`tFiWHތ[,y70D[->% "^ |.SՅys\ezϫGb# [w?0~L>΋^r}w%1_O,k">RDݵڑT<H05཭LDD0'v@oȏG|׀7߄f1G9\eURw17W~^D>$"1M݌(" K+L5Q׫7 U=71marUu4nd :U%Pշ3";">yWps"'"r*"/"?""a]&YvrU/g_efKfUoCc$'}5~x ]}ڌHJmYi.@xn^100f}.wus/}K$M}ΝcddM`2^^'Uz:\"bvL*+[-5OcR?ۑ6ȏ`QLFD:E1cQލlGTo1w? >pXD~%G4o>@w#ХgDs"@"xs_o[>gҠxyjh%Ë0c j!ň>D>("1f(eNW/&- <x>QφKc Eؼl'K Dp'^vBW塽4puD4Tm4U}sζ0eŏݻwL&b%FI$jQS&Z/ژSV2xBQs/~V{Q#"IYUݭK+)U^)by&[lY}LY د1S [a5*u,z4f2癟',3kZ V[؞{(9/X:C10f픖F{WEI#oN|3 Pt{w3u$m*5S(j5*[կ~7N#|KDSsa ʆkiT,wkP/PXn6_gaZ6^.L&,^hT@\L8oEqk*P]z"Zt7A:~jcrpRdf}G'=|_e۶m_PcanذÇfq]J)2"LW' MUme^jx/& ifA TD#}'"6ՍĀc t` ,N4>WD1sYr<7RAUnQ_k\[,yTl6ٳR)8f#cwfR_6TWm)~zf7}$xĻB#ۯ"NȘr*ɛSVfI>$>Kzx ֘ں>+6t{Gx'x+^A(4fY昜dbb8|crrْT[\\;om۶k..<ϫzrH&β@.i\ɠl2 _KZ&I.m3ͲTdff>y^]*Mm4Dd&.yDLOVibDU(꼪5@0鐧U\E YǺ1doL)KUY1u׵z*3a-] =1\9lRXuV^OXx 61U &9 "CA44z3E_Q*Fzɑ>stƕ;aA<7_!4Օ4Se/@EqJP'/زe /~d2xx8:tdj}k_cxx>Ml߾q4hg5Xx<ΏrwO|A$SSS-3y}"_^#͒dbzzh!37E tA._RU`Bohn/h6~3XNLF}`7rIQG8z(_~9'O$N#"%[kW|S4luy窺9h׊_nYTk*߳> |MDÜ uMc\GGGG 7iǩ_Ke{m bPGG[X94Օ6Kϼ,^f-LM$f`ӳǣdsH(lxF]f">Mu5Fu]:;; y3嘝e׮]ܹqN:EOOOak~e u]4SR)y6m ?8̱X."Z-"c$K;Q61??_6Յ2::ʖ-[ؾ};ǎ9hX֏ DIS]i-7iKK鿒9r`vڜ{eL(PVҰU5="8@:&sײ?Ngg'PMW~P/^֬A뺄B!"|:BK;kkTUjй6mTU+kQ6n܈NkoXևRxrل|/7CG 3=qH|;͠dgSe+/`vƧҁ9Jq 7y> xWW}~<<,鴙5Nru?bX.u.tmL$xQTMA+sŲT/^YGQW[jf2mE60=qΎfi)U m|&|й9:0?0.Zq} 'i&''kLq#G4bX,+c7ndll u Å00::ʦMj:Z,,oqN[fԵ IDATDyz.c~$/$ę:Ɇg;E7 *zukYI1_ +CHWWW]Y*u~{ H$p]H$B?pG2>>NWWWa{bXX1uVT96nH,+OvDgPO-oWiQ-<"W]u>n=쿌Nֹ L%]]zTWQ=U|o#ޱtrYDBTT5K߆'E#xN$nmT1?fYK&ᡇ"H^u~[/ C,#LgnF""D"#lX,e8 rqg8xU%J f0_|``~شi<Lx< d `?4tBUogߗ+"75*~:[rX!UQ#}/WeS{\E,H=ۉ[@x!YQ ߳R D9/ShY:f~~}.qBl|kҶUKWq x'+ػw/wD"{X,k7n,Tv&JL&KBI\XX[277$===[iUω#FM[%kT"!`ǁ,T"&` U}?|FD7i`+3:Ο)qh+T]e7K4^vYѮq.DT}Ts8n'SNw0SS^-X uu,YMAR:g9s RLp ꮵ|.NL_/}OW=vlڴݻw#ى_Rb4D|ȧ |eqqqH$7\l6"db ټy3tttnZ2 fTEm37ꏉc"R~ 0zFDv/ꟉȧD$xl)0,T7EMA ^0ڸʵGKOt w2=q'2qbFGEXX8 ^!UHm(j tY:xy244޽{ٻwo/DZ_u8^{<ϨYW߈PNy$IN8arZ,R Bs&l޼BL&SH$ 1== Ν+;?Qmփê!6Y WEý6آbbvj'S??+$yY.^8?oň܅.!ba,ss"$W@qHhO7#UvvUV]}me̠8x^#T`Ν;W1J7iQt?\.W0y]px<<5\COO===NbX,E猎D"l޼#Lٲe hHR%CWB-`bbiqzzzfL|0xi`-[ ;?tPT5'"s,wo?n?=F3v9#Eu@]H !&1!&6` cg1e؁^0e7Kvǻ3=f$nyi5nΧN}s}{;w>L(ȯp ?u\GՐ~8IeGdW `i~X!R4y` Rw AR2\rjڵw[l0vm۔e\ʹWozJ_Rʕ+ֲޗ/j0 k2;w˲y&bd2Ze MFΆrρr~T*s<}oɤ44F"wp6_eK-EۊnWeiy: ϒ]cOQ]}c #IluA,D,PYH#QL,vdUgc= ;Sm ]ݭAoj۹ϏBeYB!ْNe=E'a۰`0N4\Ɖ lL&C<grrl6zD"ӧ}mhƒTL&Í7'LR( 88qeiii]`8AZàlem3 %4r]d^5tupnb~MdZl~/}_`s/&`0 Q¶m%jkkkضr,ٳD"?\՛F'$IJt)(l9n޼ aX^ PkIq.ž*=d?s;:}`0*^q||5jSSS+++i, ,..F&^Sd2lllp "055066G$8``8Il8ן^t)8䴛CPYl7znqh^ t:˻M6套^jݔ{=t;}+";ƺc93F_o}1 w$RL&XB2Iq$H۶q]ZFXdccϤ8Zt:M>'Q,IӔe$$kkkaܯ:3*m1ir2Y__T#F_ϼ)}aî0xaqqt:M*̙3~d1 bYb6R)VWW) &}z-VWWCD9DŸd'&&L}#, Z `m@Tt 70z3A 8bb1l`. XE8R5֗DULT r\O#"9:^6so,Vo۶w΍킁3la_7}CG1iz'eTc<<|rv`F_o}aŗimN չOYd(-ˢV8mSV, qPUjpG3T*"hT xqDQqR "n(J"ei :emsܗ/G0~u r`0 ~1U}-oyKq||3ϟ?_y_WbKKKwkkk/}Kc>h~zqUwg}vT*z׻=^xamo{\SkΞ=O,~]XXE_7}A/Di;MUfU5 `0v!Z8kYyq7[[[6Dukk˪j233S׾fonnZTzgΜ)j5+JSSS%T* Bp8\!Qq|ފV-0`0 oTP4h,J%VFk[[[v<[E* s=5۶UD{ReYL&[[[x<^V@,+e  ժ`09v Qi`Ψ|M;mG?'~{*Z>?|]w̌W^y%F̙3t:mmnn333@ KKKx<^nll H$jPh\d٠8D"Qd2RH&u%D"H9Wr\VxVrh~~_U߰_Hx~;p!NnjZH| Ci9f18F;97.F=!"1\k]DJU_SN8T*6`rLP`}}H$BTX,R(ˬlODkYmN.CD*JZF.3VU:벸!~:7[:,٣cM;˨`0tgT>ŨWs}b} JTBUqzNZ%h,^,QUN:fƈD""pUVu#]EUu-w$wQ~@k8SL?-dv5VGen_SS \K6ʙv A3*sӎ⤵g~b@{6}ycyuW:T affqVWW9udSNdH$qRB@ @ukUU,`p8L,P(P*Äa"Jh4 "Tʏd3_Ŏ<鿽 s$?6 xcƣnw_ǯkǹ;sgTj{ioà9fv 2*sӎgxm尶d|9}4{/hE2B!T* IRR)T۶ B*Jj8DD"ARaee@ JuOT*>UU}Q]Мc6ۇ`{V#x'=lۺ9~`0 'AZ'q]MX[[CU7oR(H&;wX,P(( DQΜ9C"X,r-$NH$*\udyHDSD("@Fݝ{{CG[M@jk5w }ݥ?{CDU0hp'pTHUޱ1M^ORayyRD.#L&dssqu7o$HL&DU9=%I&,jx^#\ˡ<d0 JD~PD>$">rw`?F /47w{׏0={5> }L&B!4|h4,㰵"+++B@:fmmerP)ǡT*f DQ\%. Ǿx/zU#_0Z_V8uoӏPҚ˵߫R2 /ԨSMc^|o@KUf?\UfEdFzjN5c5U}ID~XU}ߪ;׫izx`ޯ?Bo%E^[G(Gc^+ @"rm4$0={5>ju9333LOO X Μ9C&!S*!uD@ @$!RI DNj ս/ꕦu(gg/$}LZ/ڜ0 7\?&扻E[[b;=i"/G#U.fKHFg_TՂ< _o/"8tD$ԁw\Ud^c PՓD䬈(pC`aD?F/{;*OE256#q]_^xy666Ff,,,fFLNNr9I&3g0>>8r9VWWa&&&!r_3Wשj~eh{y`80;"(8S:CWl3zӲ}^c˹ی5v5]U1` __m+VU"m{".`|LUo4c>?_Ձx/0~u)=҈SQUa/:$Wժ#ZRTA ed|T-yӧO399eYN B8C8 YU=CL&( j5rܾuڪ*RreYB!l{/bhOmr4~3/i; |Du96δ3eN?];͏5lgXj'ANѹ~CD~84(Oߣ+=);.U6=DD~LU?rDZ a7x1wh _cpQ>3),pED~@Fo(iX$Ǚƍ*PuZb6[[[eFu˫v7z8HH$TUro.m,q@9Vq0(Xa5^R$"0v?h!z.*$Q9jcmfUUu = "-uX9r_TZ{S o x?_ǏU|{E`0 a/+U!"fK7GvwFcTP,͈m r_ä1xlllP(HQ13t)0B* Ph4eYyŢI58Z\~[n ~0vluÁ8T7!6D] 薕JVA뷣` V@5^{i'}F ai_jqZa8njm|j(/_&QTx嗹tzݟ:WTD TqI$mkJ*bkkt:M:& R(T*}r .\ :hp8L0c@|O"QբX4(A y xQUnZYUwz3rLP*c jݑtzlF=8ea;ap> W+H`0 þ0rPDxay&rҥmuwN/!Ju.e*"y^/BY(X[[#c[yqee[n1==<\y6667v/Yু)"1,A :N4qlaqh 6ǚ "Tl0@"rD:E04 I`0 a0:O<\./J$I⎺;]W},366mU*>΅ x衇f\zrXcSK 8hcڋQAu(i g,wZL.M`0 Ii2z%N:E\楗^"NDߑSݝӫ>p8L6%077s=8JBTfkk|>O2So-rxX,s1>>=#<H/mCe `PU *gV@syLo^c: 5 `8it6j50s׮]9}4D_/NS8ڶM4e~~~I٬oO  ox/^DUY\\dkkkc Fס#-CLwᕲ,/q%5*PLWnQO`0 D7cb6H[n,̰J$! !"۱NU_{1q5yHR*~q/s8z%J2\… XuȨ0J\ǑD4n3l/٬YU)fʵEj;4LW6 `8t3="B(bllJ*; 9,uwNڵar[[[\p{1r{xXj'fbr"o0;";MmOY\^+FVUQpJmAB'oB3nb0 p¸27odiikvBc}xyy}6C@XT*9o|Fdq4"ۓx;klc *XXr<& vsj0 pҸ9"zm8o YXX\^^W_QwS<N&ҥKlmmfmw1pΝ;}'"D" 㠌 %9 DDf2.DyLUh qF=;bkV%Vcú Xw$nԿ`0  NC,ۏ{2HFYXXgeqqsַ?]xW A^}=gR>/*&Ξ=_WY\\$H$pgq< >ͽT""9  W]ùwȏAyjy-F".5jurW"-\WP',_%Җ^`0 ᄱ[o7g~~/2;;]hQT*E*², DbOꧾ@ qh@FTuUDsy3 ! @x;0|YցokGސRD~|p{Eiy|䁿NUkG܆CPNMz7ՌK;0Zp]Q,[XYH#yNpW3`0  fq7YTvضM(z9u J<^`B /@^'l)0I#E7hv'T5+"U'{(PUWD"2<-"["F9ເTc"|xm7v_wC;:4 sh+S-}; 6M6R SIPj8ۯn$w5 `8A }3{Ȫ6sZ\zeD@-/ҨnskDQӶs `0N 0=bN>8mG2qd4wD;ѝ洣4U `0Laz}q`[17_lڠi~pkl=.Z=o]u9raЌܴc8i虫W0*>S9"Q_ VXlO3h=lݷQv 6C$!hS}}z<=m:V۰߰hrYo7,НQv ڎ^Ys}`ѷ\d_s}F߱n7}l_osf3oϹipm#ێ>E~Zrn_ `}ׇEkCо=w,dˢIENDB`mupen64plus-qt-1.15/resources/images/mupen64plus-qt.png000066400000000000000000000265521413256700400231110ustar00rootroot00000000000000PNG  IHDRƽbKGDtIME $X IDATxwx?ْI Z" (HФrQtIB ED.AA@)R !ewm~lvln\}yv9{Ι3ϭ.-B2( y,wpPׁ27,X~JSv۷>0-,05uIܱch{ݻwAb8 r߁Q̤]vw-(yq̙bvv(xAo߾'(O[W;  A{-fdd(999… ^zݺu.={?CҥK(!_d ppwhdtL& ׯ'NYfn:vލht~6mOP^=N>YjxaߌhjҤIrrrزe L&'͛S^=Ο?ϴiXr%:M7J73ur>>n^NիG۶m_Øo+nV .iܸ1ǎ`0`2EQ+VA6~~~CQ^^Nyy9Z~p6lȊ+|0"^~ӭQMN7>j޼j={+W% ȶh4:$ٙh4F#?۷o.\PT&v" xbYBBwFK&_Jͼ^G8U3#>>d>sV.[j3g&++cEQ߿Ae3hVECgܹs!n­2+`n(>LMMEVohh4ju} uŵ?D&j5TjԱ^q.I l#A||ݓ  449rds=ZtAֱcG6oLqqk4F~OKٳ1$K:26nH-ӧ@l\.c (KKKի^'> cڂ̝J"$$+C&Yo۵YN+e2Yz*СC[(̙Ð!C;v,>,4nܘM6o ݻw93to[ uU_Ǐ'((5kְ`Μ9\.___kpEVVM6eС 42cǂFc' sG1=waʕ8qBvcǎ-O=֭ޝ?W37of׮]  w}7ݺu#11/2k,.]Jyyy)P}X^x\ӳGΛ޷+SjgϞiӦyfW( >@ק,7#UɗAA#+ RINIvmڴa֬Yyfٻwוpx|AXf ɄЧO|||<&Yf x>Ǎ/~=>;cpYlذ={xUV˶mxtwٳ5b+AhԨ<QQQߟ %SϺ2#VvmKׯ[ןyz-JKKQ* J%Fuݺa(+0.T,24=N;O?T}saٲe$&&Z~:zWyy9 >}<=p>HRR;vB*O&MHJJBm+//FԮ]:; sjhj*W+leݲϨVsl4ZΝK۶mٴi` GJJ )))dffrqJKKWZHNN&.. hт'=&L>+$$d2ڷ'&&|Ν;WIGbb"р)R2((ɓ'#dF#O?4bNa%m/8X~ )cң{w[2gDQzJBT@BBW\ܹsIWPPDDD "ea?}EEc ԙ h}aBCIcB%::'Q* v(/`bF\.gNٳ'jؘ1KJ6;RqgRK?G|7:u*_~XXsg.^\.w頖+0;00YE'k0tr˔oo]E` q 3[4 N8A޽9[oT*;vW^AO…mvwп?sAV)0aWW[o$;3gXCɥ PmM-h2Qv-^|]AFv[WxnߒXKKs]lĊ8;V).:sꫯ2{JS 4Z3- Z]Xul^ 4 _x*0a&L`R$>}Z<m0`,*TXA_PPd{W|WSj.<,`4Vٷo~?26ѣə1ÇpBt:5ݛoAyN?%|cr֬) N5m4ƏOKk&p@222(--ְ^G˹K\jB/۷Sc>`UpIj,A.ٓ'3l0.]^筷"(8sS"jU]gb4rv$T~~j9r$W>W_EEꥧ79|Wīj2#G7NIv&2oUoYv_ĉ I+ݷez=$gryTLz=NNhH:t@Eβe.zw>oQ^:PO\3@~lGCR",(Lhc-ޢ;&IԻwSiUi8r 쳿30j{ 0 :S"-:PD (}}߿?Ӧqq1jȨT^j Bs׻#{jp2y22&4 ~-79Jԕ>/s]e?׿?OS@^xA:#/5&XQFG֍kRVVRIK|GR@}urs qKV"'O9٫  ۗDzo }>(f@%K蚓CZRk!~r>yc?RDE޳M*˛Ir&MG.aÆ dl殛wA _?:df?חE1l0~G ;w_dܹ"-VSv61!\vx:|rU'k4+L dgg|2VSFFM6ݛf+W9w%*,ի/L);:@ä$vtvI$;-w؏Eʔ)>${/?W3̝;֭)ݿOT+/d v['NpI7oNnh|9OҤI\P1T5hσ>eu m"W*vzg-:|'z56 U88z()Kruk2FG^^}LT,/^LƍСIKR/5y`}Fvv6۶msI3iB͔-t|Gگr+Sҿ?;w/*bfڴi^'PVDܹ c5eW_}Efҥ f"6|8ƣkQ(OnRĄͯ7|&_[9+h:x0 ׯ{ǎCVJ%ڵ#&&#GxX 4oޜ oC#@_Zʁh4T*iղ%%ף7Ƀ:Gט=Unj*ѯ_?ΝlJ%KU0 ~jLM _֭!fOcҨQ#իg.ѣK믈ݺ|f OOiidF#G.N%!WKu\3~E|$PTi޴)AoڄL&xи1%K~Abbb((( ++KRܜ?qqq p_߸gO׮>O>Iqq$h{| CjD!Z3| h>,otaÆIWTYMb8Ӻuz|r+S?0ڤ$[J˖-iѢ111TYiժuօ_EhZL>[`ޝF |v-AAA \N)V-E;t.]"㏹eKBCC]:?_Y]{i 7M&L\^33?X6@RR5"22"""hР 4Vp0܉а!m Ǐat?NbTׯ'44JmJ=DhNF|G^8g7krFT1oh'1lقyOT"d<<"^kW4QQ֮E)Sn]իGxx8~Oۃ+ݰk#>lݼ} 9hywwMCJkgGrCB_v 99t0(HKs^bSR\)Q<'0һ"T. g,,$oZn͉' wo4tJmGDy"?ystB/]r@HLO@^YzoPc}>l~@С.'鈢JRǎ[%ߌ{h}gΐӮWOwj0O[5Y $ ŸU\r'ժȄ?۶-g&bVUZwjhNDMcpiC^Q#Gw6)š`*&QcZ ʡ|BB߶ i94ARCKwh))D3HDߟ)Sܶ4_Sj|iCJeۦhlC(T*թCn~!2g23ܩΙ<1 23 `j* s'>NG =ի);yRmT^q IDATѾZ_/=f{1^#磏Y " m%'t&JW_Mqq1{r"r~^DEd2/Dl˘.H]Ё򬬿R>t(QcƠJHξ&>wE>I3k(R~?v?5'!0yd$ɻw㗜 2HV~%M0`R)]Z1tP4l'K qq$ k?IX͕+֍}[t_ 8wb֬Ytuwz{m)D;Rff՝;۹Zmr$>QK`ŷϟϜ9sJ@OGLT}Q9IDqNw#iii#GdԨQcbH>QGP(ЗGu+>[?C[**4#;ǩgPsS}Mκ#pLsyS&[{ фd} #ߖD{oV'_g3&LFф^4CUCFPw1.`,++C9~WmI7ELFGK,4";;|6uqjؐgθ<^mW*|߾}.]X"X 3+ժU>oĈ ?\ذC3gr]uyG1cH6 A.U1cN2TxZg n\;īx@3v 鮨u1t˶SqC ZڨTZ!6_ވK|oL2E7o#xupz͛9d9]ԇv[t@ 04--Q&{jEy|"mHȈrr;}Ad4_֝?^R@URA#F Έ^gD3יEgfn3/دNoβ<" H+))>{&))?]w}&mDtǎD"д)-֬?$өȈ#/^TcG O9@5U/ 7tK>NIY s3h`RYYٵ&))5 a=o hw0wٳ_#GjrrrԘ|NaX˺ڷVn&eɣ| h4qiJӦk6߳IMK#.!qiˀt xEj.e$ﮕj:4$O9%`Nlٲ+WNvvvf}E`7UmQtTΖ(J{0OchN X9(u^F hyKRzd`:a~@ KQ'wB@yn݌Rn0n8ƌc4мc2zĈ^Zj6i8G[LҸrZJ,s] s H/y;!Uȶk}7}~60у>gי$ zmsQ6d&`ҥT8ru&c;ʯbQMRRXh={r ˗{].GTjܘiv4%Khа!/.??_8pt973`~I //O=~xM|ɾ}$|%-$u"njR'lN+^ /h[ҭ(1 JLnܤ iӦ!kG][|ﻯJg--ht=,pp t˶;;{\g8tƌe4aʔ)w۷So|_t{}Į_O NlNyyye_HpG%Xpٗ ˛2T8CMr2*bLm[&ND\B'OV_vs ި '?E ~ Ed^(oT@8n8d2gE&1A7<Ƒ#GޠO>"p 93%H> kֻwoh4k}yyW7xo(_4#L4vakya~'cXSO|IENDB`mupen64plus-qt-1.15/resources/images/mupen64plus.png000066400000000000000000000265521413256700400224670ustar00rootroot00000000000000PNG  IHDRƽbKGDtIME $X IDATxwx?ْI Z" (HФrQtIB ED.AA@)R !ewm~lvln\}yv9{Ι3ϭ.-B2( y,wpPׁ27,X~JSv۷>0-,05uIܱch{ݻwAb8 r߁Q̤]vw-(yq̙bvv(xAo߾'(O[W;  A{-fdd(999… ^zݺu.={?CҥK(!_d ppwhdtL& ׯ'NYfn:vލht~6mOP^=N>YjxaߌhjҤIrrrزe L&'͛S^=Ο?ϴiXr%:M7J73ur>>n^NիG۶m_Øo+nV .iܸ1ǎ`0`2EQ+VA6~~~CQ^^Nyy9Z~p6lȊ+|0"^~ӭQMN7>j޼j={+W% ȶh4:$ٙh4F#?۷o.\PT&v" xbYBBwFK&_Jͼ^G8U3#>>d>sV.[j3g&++cEQ߿Ae3hVECgܹs!n­2+`n(>LMMEVohh4ju} uŵ?D&j5TjԱ^q.I l#A||ݓ  449rds=ZtAֱcG6oLqqk4F~OKٳ1$K:26nH-ӧ@l\.c (KKKի^'> cڂ̝J"$$+C&Yo۵YN+e2Yz*СC[(̙Ð!C;v,>,4nܘM6o ݻw93to[ uU_Ǐ'((5kְ`Μ9\.___kpEVVM6eС 42cǂFc' sG1=waʕ8qBvcǎ-O=֭ޝ?W37of׮]  w}7ݺu#11/2k,.]Jyyy)P}X^x\ӳGΛ޷+SjgϞiӦyfW( >@ק,7#UɗAA#+ RINIvmڴa֬Yyfٻwוpx|AXf ɄЧO|||<&Yf x>Ǎ/~=>;cpYlذ={xUV˶mxtwٳ5b+AhԨ<QQQߟ %SϺ2#VvmKׯ[ןyz-JKKQ* J%Fuݺa(+0.T,24=N;O?T}saٲe$&&Z~:zWyy9 >}<=p>HRR;vB*O&MHJJBm+//FԮ]:; sjhj*W+leݲϨVsl4ZΝK۶mٴi` GJJ )))dffrqJKKWZHNN&.. hт'=&L>+$$d2ڷ'&&|Ν;WIGbb"р)R2((ɓ'#dF#O?4bNa%m/8X~ )cң{w[2gDQzJBT@BBW\ܹsIWPPDDD "ea?}EEc ԙ h}aBCIcB%::'Q* v(/`bF\.gNٳ'jؘ1KJ6;RqgRK?G|7:u*_~XXsg.^\.w頖+0;00YE'k0tr˔oo]E` q 3[4 N8A޽9[oT*;vW^AO…mvwп?sAV)0aWW[o$;3gXCɥ PmM-h2Qv-^|]AFv[WxnߒXKKs]lĊ8;V).:sꫯ2{JS 4Z3- Z]Xul^ 4 _x*0a&L`R$>}Z<m0`,*TXA_PPd{W|WSj.<,`4Vٷo~?26ѣə1ÇpBt:5ݛoAyN?%|cr֬) N5m4ƏOKk&p@222(--ְ^G˹K\jB/۷Sc>`UpIj,A.ٓ'3l0.]^筷"(8sS"jU]gb4rv$T~~j9r$W>W_EEꥧ79|Wīj2#G7NIv&2oUoYv_ĉ I+ݷez=$gryTLz=NNhH:t@Eβe.zw>oQ^:PO\3@~lGCR",(Lhc-ޢ;&IԻwSiUi8r 쳿30j{ 0 :S"-:PD (}}߿?Ӧqq1jȨT^j Bs׻#{jp2y22&4 ~-79Jԕ>/s]e?׿?OS@^xA:#/5&XQFG֍kRVVRIK|GR@}urs qKV"'O9٫  ۗDzo }>(f@%K蚓CZRk!~r>yc?RDE޳M*˛Ir&MG.aÆ dl殛wA _?:df?חE1l0~G ;w_dܹ"-VSv61!\vx:|rU'k4+L dgg|2VSFFM6ݛf+W9w%*,ի/L);:@ä$vtvI$;-w؏Eʔ)>${/?W3̝;֭)ݿOT+/d v['NpI7oNnh|9OҤI\P1T5hσ>eu m"W*vzg-:|'z56 U88z()Kruk2FG^^}LT,/^LƍСIKR/5y`}Fvv6۶msI3iB͔-t|Gگr+Sҿ?;w/*bfڴi^'PVDܹ c5eW_}Efҥ f"6|8ƣkQ(OnRĄͯ7|&_[9+h:x0 ׯ{ǎCVJ%ڵ#&&#GxX 4oޜ oC#@_Zʁh4T*iղ%%ף7Ƀ:Gט=Unj*ѯ_?ΝlJ%KU0 ~jLM _֭!fOcҨQ#իg.ѣK믈ݺ|f OOiidF#G.N%!WKu\3~E|$PTi޴)AoڄL&xи1%K~Abbb((( ++KRܜ?qqq p_߸gO׮>O>Iqq$h{| CjD!Z3| h>,otaÆIWTYMb8Ӻuz|r+S?0ڤ$[J˖-iѢ111TYiժuօ_EhZL>[`ޝF |v-AAA \N)V-E;t.]"㏹eKBCC]:?_Y]{i 7M&L\^33?X6@RR5"22"""hР 4Vp0܉а!m Ǐat?NbTׯ'44JmJ=DhNF|G^8g7krFT1oh'1lقyOT"d<<"^kW4QQ֮E)Sn]իGxx8~Oۃ+ݰk#>lݼ} 9hywwMCJkgGrCB_v 99t0(HKs^bSR\)Q<'0һ"T. g,,$oZn͉' wo4tJmGDy"?ystB/]r@HLO@^YzoPc}>l~@С.'鈢JRǎ[%ߌ{h}gΐӮWOwj0O[5Y $ ŸU\r'ժȄ?۶-g&bVUZwjhNDMcpiC^Q#Gw6)š`*&QcZ ʡ|BB߶ i94ARCKwh))D3HDߟ)Sܶ4_Sj|iCJeۦhlC(T*թCn~!2g23ܩΙ<1 23 `j* s'>NG =ի);yRmT^q IDATѾZ_/=f{1^#磏Y " m%'t&JW_Mqq1{r"r~^DEd2/Dl˘.H]Ё򬬿R>t(QcƠJHξ&>wE>I3k(R~?v?5'!0yd$ɻw㗜 2HV~%M0`R)]Z1tP4l'K qq$ k?IX͕+֍}[t_ 8wb֬Ytuwz{m)D;Rff՝;۹Zmr$>QK`ŷϟϜ9sJ@OGLT}Q9IDqNw#iii#GdԨQcbH>QGP(ЗGu+>[?C[**4#;ǩgPsS}Mκ#pLsyS&[{ фd} #ߖD{oV'_g3&LFф^4CUCFPw1.`,++C9~WmI7ELFGK,4";;|6uqjؐgθ<^mW*|߾}.]X"X 3+ժU>oĈ ?\ذC3gr]uyG1cH6 A.U1cN2TxZg n\;īx@3v 鮨u1t˶SqC ZڨTZ!6_ވK|oL2E7o#xupz͛9d9]ԇv[t@ 04--Q&{jEy|"mHȈrr;}Ad4_֝?^R@URA#F Έ^gD3יEgfn3/دNoβ<" H+))>{&))?]w}&mDtǎD"д)-֬?$өȈ#/^TcG O9@5U/ 7tK>NIY s3h`RYYٵ&))5 a=o hw0wٳ_#GjrrrԘ|NaX˺ڷVn&eɣ| h4qiJӦk6߳IMK#.!qiˀt xEj.e$ﮕj:4$O9%`Nlٲ+WNvvvf}E`7UmQtTΖ(J{0OchN X9(u^F hyKRzd`:a~@ KQ'wB@yn݌Rn0n8ƌc4мc2zĈ^Zj6i8G[LҸrZJ,s] s H/y;!Uȶk}7}~60у>gי$ zmsQ6d&`ҥT8ru&c;ʯbQMRRXh={r ˗{].GTjܘiv4%Khа!/.??_8pt973`~I //O=~xM|ɾ}$|%-$u"njR'lN+^ /h[ҭ(1 JLnܤ iӦ!kG][|ﻯJg--ht=,pp t˶;;{\g8tƌe4aʔ)w۷So|_t{}Į_O NlNyyye_HpG%Xpٗ ˛2T8CMr2*bLm[&ND\B'OV_vs ި '?E ~ Ed^(oT@8n8d2gE&1A7<Ƒ#GޠO>"p 93%H> kֻwoh4k}yyW7xo(_4#L4vakya~'cXSO|IENDB`mupen64plus-qt-1.15/resources/images/not-found.png000066400000000000000000000042561413256700400221730ustar00rootroot00000000000000PNG  IHDR,MbKGD pHYs  tIME )XsEiTXtCommentCREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 75 tIDATxnџ:[GQl6(},WN#iIIc)d- PQ4K h&S` X` X X`` X` X X`` X`` XZN$8z<x"˲Ȳ,uz<_ft:htjŧ@HHIpzߟE׋n**MvcUw]vbjlV4  ^-Mx}dYdplg#X:1p88iJOEi>|p" >4u" aX5,,b8հ\. q@ *A *cݚL*XP IeUxQݎVfikm & |[eq{{[oá!X%]z^eW@fyc,/Y!V^ |]eqssSXu+1K3o ^43̻,},,?n…,xv:bXf)MjZ\]]Y]xHӴT5N ݕd,lVm߯n@fې۷Vc,xlb>YVX,Jd͂>~X{_3Lb4@ϱZ,&6PJ$@븻+UzLS/M#-93$5#(5*%*0'++,DI'/yI/J6J6vJ6@MŅN7R:3R:gV %V>V=Z8,DBZm`Zy%E&[5I]F,n@;93 lD]Oe4ý9B-1DphW[%o&I-{Lͬ6sU6zEoj PzAbJ )4@[KG[ G[1;Z~RnMu w1zq,W;FIjI I ^I_I#I0/ 9}82Ft|(Ę).u.`J;;V|=(h=l:q6rw:2|%?~}jCG!FAe;9%"]PN5ÊEg{3gF^C?7INZ Dvm#Y)>|X~0J J0  GZ8c&Ѫ.(!o-_^; ;>N_^l +l8l^J l^JA4tn&G5.% A dC[h؊ypG C7Ԣ 3}4% M  !-g 0r! QZw J` 2a 4p y*7u .>a I e T TF p (Ѯ )U QW GJ? 0N   E μ"S قn ^D (6< *s fC< u" '| sh sB ,@ ɠ ʧ!?  ޴d %" H |( 0, XVB [IA {u=_ * k P P& 29g9 _$0ZB-3?nL5usB7j/1 z2P`@R/@V+XaY:eG  -@HXXAr5ԇ%'֕iP0Site web de <ParentName> website AboutDialogXUne interface lmentaire pour <ParentName>.2A customizable launcher for using Qt. AboutDialog* propos de <AppName>About  AboutDialogDpt GitHubGitHub repository AboutDialogVersionVersion AboutDialogJditeur de configuration <ParentName> Config Editor ConfigEditorAnnulerCancel ConfigEditorSauvegarderSave ConfigEditor FermerClose ControlInfoAnnulerCancelDownloadDialogFichierFileDownloadDialog:URL du jeu sur thegamesdb.netFrom thegamesdb.net URL of gameDownloadDialogNom du jeu : Name of Game:DownloadDialogChercherSearchDownloadDialogHChercher des informations sur le jeuSearch Game InformationDownloadDialog.ou identifiant du jeu : or Game ID:DownloadDialogFExecutable <ParentName> non trouv." executable not found.EmulatorHandler<ParentName> a quitt de manire inattendue. Consultez les journaux pour plus d'informations.C quit unexpectedly. Check the log for more information.EmulatorHandler*Fichier ROM invalide.Not a valid ROM File.EmulatorHandler*Fichier ROM manquant.ROM file not found.EmulatorHandler,Consulter les journauxView LogEmulatorHandlerAttentionWarningEmulatorHandler4Paramtres supplmentairesAdditional SettingsGameSettingsDialogGreffon audio : Audio Plugin:GameSettingsDialogNaviguer... Browse...GameSettingsDialogAnnulerCancelGameSettingsDialog0Dossier de configurationConfig DirectoryGameSettingsDialog4Dossier de configuration :Config Directory:GameSettingsDialog"Paramtres du jeu Game SettingsGameSettingsDialog0Paramtres du jeu pour :Game settings for:GameSettingsDialog$Greffon d'entre : Input Plugin:GameSettingsDialog&Autres paramtres :Other Parameters:GameSettingsDialogGreffonsPluginsGameSettingsDialogGreffon RSP : RSP Plugin:GameSettingsDialog`Ces paramtres prvaudront sur les paramtres gnraux. Si vous souhaitez modifier un paramtre pour ce jeu, mais qu'il n'apparat pas ici, ajoutez-le dans "Autres paramtres."These settings will override any global settings. If there's a setting you want to modify for this game, but it doesn't appear here, add the parameter for it to "Other Parameters."GameSettingsDialogGreffon vido : Video Plugin:GameSettingsDialog DfautdefaultGameSettingsDialog Pas de cartoucheNo CartGridView FermerCloseKeyCodes Pas de cartoucheNo CartListView*Journaux <ParentName> Log LogDialog FermerClose LogDialog &propos&About MainWindow&Configurer... &Configure... MainWindowN&Tlcharger/Mettre jour les infos...&Download/Update Info... MainWindow&mulation &Emulation MainWindow&Fichier&File MainWindowPl&ein-cran &Full-screen MainWindow &Aide&Help MainWindow&Disposition&Layout MainWindow$&Ouvrir une ROM... &Open ROM... MainWindow&Quitter&Quit MainWindow&&Recharger la liste &Refresh List MainWindow&Paramtres &Settings MainWindow&Dmarrer&Start MainWindow&Affichage&View MainWindow"Tous les fichiers All Files MainWindowAnnulerCancel MainWindow*Configurer le &jeu...Configure &Game... MainWindow>&Effacer les infos actuelles...D&elete Current Info... MainWindow2diter mupen64plus.cfg...Edit mupen64plus.cfg... MainWindowrL'diteur ncessite que le dossier de configuration soit 'Editor requires config directory to be  MainWindow Vue en vignettes Grid View MainWindow LancerLaunch MainWindowVue en liste List View MainWindowPas de sortie No Output MainWindowAucune ROMNo ROMs MainWindowNAucune ROM trouve dans le fichier ZIP.No ROMs found in ZIP file. MainWindow AucuneNone MainWindowNon trouv Not Found MainWindow*Ouvrir un fichier ROM Open ROM File MainWindowTVoir ici pour la configuration par dfaut:)See here for the default config location: MainWindowChoisir une ROM Select ROM MainWindow&ArrterSt&op MainWindowVue en tableau Table View MainWindowIl n'y a pas de journaux. Soit <ParentName> n a pas encore t lanc, soit il n y a pas de donnes concernant le dernier dmarrage.^There is no log. Either has not yet run or there was no output from the last run. MainWindow,Consulter les journauxView Log MainWindowJun dossier contenant mupen64plus.cfg.(set to a directory with mupen64plus.cfg. MainWindow%1 - MB%1 MBQObjectCRC1CRC1QObjectCRC2CRC2QObjectDveloppeur DeveloperQObject0Tlchargement termin !Download Complete!QObjectESRBESRBQObjectNom du fichierFilenameQObject4Nom du fichier (extension)Filename (extension)QObjectJaquette Game CoverQObjectNTlcharger des informations sur le jeuGame Information DownloadQObjectTitre du jeu Game TitleQObject GenreGenreQObjectNom GoodGoodNameQObjectNom interne Internal NameQObjectMD5MD5QObject*Pas d'autre rsultat.No more results found.QObjectAucun rsultat.No results found.QObjectNon trouv Not foundQObject AperuOverviewQObjectJoueursPlayersQObjectditeur PublisherQObjectCertificationRatingQObjectDate de sortie Release DateQObject2Fichier catalogue demandRequires catalog fileQObject RumbleRumbleQObject$Type de sauvegarde Save TypeQObject TailleSizeQObjectROM inconnue Unknown ROMQObjectFichier ZipZip FileQObjectAnnulerCancel RomCollectionImpossible de se connecter la base de donnes Sqlite. L'application peut dysfonctionner.@Could not connect to Sqlite database. Application may misbehave. RomCollection6Base de donnes non chargeDatabase Not Loaded RomCollection,Chargement des ROMS...Loading ROMs... RomCollection.Aucune ROM trouve dansNo ROMs found in  RomCollection&Aucune ROM trouve.No ROMs found. RomCollectionAttentionWarning RomCollection......SettingsDialog>Fichier excutable <ParentName> ExecutableSettingsDialog<b>Remarque :</b> Le changement de langue ne prendra effet qu'aprs le redmarrage du programme.LNote: Language changes will not take place until application restart.SettingsDialog<html><head/><body><p> utiliser pour ajouter des paramtres supplmentaires (cheats, etc.). Voir <a href="http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage"><span style=" text-decoration: underline; color:#0057ae;">http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage</span></a> pour plus d'informations.</p></body></html>G

Use to pass additional parameters to Mupen64Plus (cheats, etc.). See http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage for more information.

SettingsDialogAjouter...Add...SettingsDialog8Paramtres supplmentaires :Additional Parameters:SettingsDialog(Ajustage automatiqueAdjust AutomaticallySettingsDialogGreffon Audio : Audio Plugin:SettingsDialog,Colonnes disponibles :Available Columns:SettingsDialog.Intituls disponibles :Available Items:SettingsDialog(Image d'arrire-planBackground ImageSettingsDialogArrire-plan: Background:SettingsDialogNoirBlackSettingsDialogBleuBlueSettingsDialog MarronBrownSettingsDialogNaviguer... Browse...SettingsDialog.Interprteur avec cacheCached InterpreterSettingsDialogAnnulerCancelSettingsDialog(Nombre de colonnes :Columns:SettingsDialog0Dossier de configurationConfig DirectorySettingsDialog4Dossier de configuration :Config directory:SettingsDialog(Colonnes utilises :Current Columns:SettingsDialog"Intitul actuel :Current Items:SettingsDialog2Choisir un arrire-plan :Custom Background:SettingsDialogCyanCyanSettingsDialog SombreDarkSettingsDialogGris fonc Dark GraySettingsDialog&Dossier des donnesData DirectorySettingsDialog*Dossier des donnes :Data directory:SettingsDialogDescendant DescendingSettingsDialog,Afficher les vignettesDisplay Cover ImageSettingsDialogxTlcharger les informations sur les jeux (thegamesdb.net) :+Download Game Information (thegamesdb.net):SettingsDialog.Recompilation dynamiqueDynamic RecompilerSettingsDialogmulation EmulationSettingsDialogTrs grande Extra LargeSettingsDialogTrs petite Extra SmallSettingsDialog<Premier intitul comme en-tteFirst Item as HeaderSettingsDialogPlein cran : Fullscreen:SettingsDialogGraphiqueGraphicsSettingsDialogVertGreenSettingsDialogVignettesGridSettingsDialog&Taille des images : Image Size:SettingsDialogImage :Image:SettingsDialog$Greffon d'entre : Input Plugin:SettingsDialog.Couleur de l'intitul : Label Color:SettingsDialog*Texte de l'intitul : Label Text:SettingsDialogLangue : Language:SettingsDialog GrandeLargeSettingsDialogLumireLightSettingsDialogGris clair Light GraySettingsDialog ListeListSettingsDialogMoyenneMediumSettingsDialog&Fichier Mupen64PlusMupen64Plus FilesSettingsDialog0Excutable Mupen64Plus :Mupen64Plus executable: SettingsDialogMupen64Plus-Qt cherchera tous les fichiers z64, v64, n64 et zip dans ces dossiersXMupen64Plus-Qt will search for all .z64, .v64, .n64, and .zip files in these directoriesSettingsDialogNormaleNormalSettingsDialogSur-affichage:On Screen Display:SettingsDialog OrangeOrangeSettingsDialog AutreOtherSettingsDialogCheminsPathsSettingsDialogRosePinkSettingsDialog&Dossier des pluginsPlugin DirectorySettingsDialogGreffonsPluginsSettingsDialog*Dossier des plugins :Plugins directory:SettingsDialog Interprteur purPure InterpreterSettingsDialog VioletPurpleSettingsDialog Dossier des ROMSROM DirectoriesSettingsDialog"Dossiers des ROMS ROM DirectorySettingsDialogImages des ROMS ROM ImageSettingsDialog$Intitul de la ROM ROM LabelSettingsDialogGreffon RSP : RSP Plugin:SettingsDialog RougeRedSettingsDialogSupprimerRemoveSettingsDialogRsolution : Resolution:SettingsDialog2Sauvegarder les options : Save Options:SettingsDialogParamtresSettingsSettingsDialogVCouleur de l ombre de la vignette active : Shadow Active Color:SettingsDialogXCouleur de l ombre des vignettes inactives :Shadow Inactive Color:SettingsDialog(Montrer l'intitul : Show Label:SettingsDialog PetiteSmallSettingsDialogClassement :Sorting:SettingsDialog6tendre la premire colonneStretch First ColumnSettingsDialogTableauTableSettingsDialog Taille du texte: Text Size:SettingsDialog Thme:Theme:SettingsDialogGreffon vido : Video Plugin:SettingsDialog BlancWhiteSettingsDialog JauneYellowSettingsDialog DfautdefaultSettingsDialog Pas de cartoucheNo Cart TableView<b>Remarque :</b> Si vous effacez les informations de ce jeu parce que le jeu RNOTE: If you are deleting this game's information because the game doesn't TheGamesDBScraperNContinuer rcolter des informations ?Continue scraping information?TheGamesDBScraper>Effacer les informations du jeuDelete Game InformationTheGamesDBScraperNEffacer les informations actuelles pour"Delete the current information forTheGamesDBScraper`Ces informations vous semblent-elles correctes ?Does this look correct?TheGamesDBScraperErreur rseau Network ErrorTheGamesDBScraperPubli le : Released on: TheGamesDBScraperhTemps dpass. Vrifier vos paramtres de connexion./Request timed out. Check your network settings.TheGamesDBScraper<AppName> ne mettra pas jour les informations jusqu' ce que vous PThis will cause to not update the information for this game until you TheGamesDBScraperet d ajouter le jeu afin d en faire profiter galement les autres utilisateurs.4and add the game so other users can benefit as well.TheGamesDBScraperHil est prfrable de crer un comptebetter to create an account onTheGamesDBScrapern est pas dans TheGamesDB et que <AppName> vous propose des informations pour un autre jeu, Rexist on TheGamesDB and pulled the information for different game, it's TheGamesDBScraperjrelanciez le "Tlcharger/Mettre jour les infos..."'force it with "Download/Update Info..."TheGamesDBScrapermupen64plus-qt-1.15/resources/locale/mupen64plus-qt_fr.ts000066400000000000000000001616311413256700400234320ustar00rootroot00000000000000 AboutDialog About <AppName> À propos de <AppName> Version Version A customizable launcher for <ParentName> using Qt. Une interface élémentaire pour <ParentName>. <ParentName> website Site web de <ParentName> GitHub repository Dépôt GitHub ConfigEditor <ParentName> Config Editor Éditeur de configuration <ParentName> Key Codes Reference Mupen64Plus Documentation Save Sauvegarder Cancel Annuler ControlInfo Mupen64Plus Default Controls These are the default controls for Mupen64Plus. They can be modified using Settings->Edit mupen64plus.cfg... For more information see the <link>Mupen64Plus Documentation<linkend>. For setting up a gamepad controller see <link>this page<linkend>. Note that Mupen64Plus output can be viewed with Emulation->View Log. N64 Input Key Quit the emulator Select virtual 'slot' for save/load state commands Save emulator state Load emulator state Reset emulator Slow down emulator by 5% Speed up emulator by 5% Take screenshot Toggle between windowed and fullscreen Pause on/off Mute/unmute sound Press "Game Shark" button (only if cheats are enabled) Single frame advance while paused Fast forward (playback at 250% normal speed while pressed) Decrease volume Increase volume Action Close Fermer Controller Input Hotkeys DownloadDialog Search Game Information Chercher des informations sur le jeu File Fichier Name of Game: Nom du jeu : or Game ID: ou identifiant du jeu : From thegamesdb.net URL of game URL du jeu sur thegamesdb.net Search Chercher Cancel Annuler EmulatorHandler Warning Attention <ParentName> quit unexpectedly. Check the log for more information. <ParentName> a quitté de manière inattendue. Consultez les journaux pour plus d'informations. View Log Consulter les journaux There was no output. If you were expecting Mupen64Plus to run, try copying the above command into a command prompt to see if you get an error. <ParentName> executable not found. Executable <ParentName> non trouvé. Not a valid ROM File. Fichier ROM invalide. ROM file not found. Fichier ROM manquant. GameSettingsDialog Game Settings Paramètres du jeu Game settings for: Paramètres du jeu pour : These settings will override any global settings. If there's a setting you want to modify for this game, but it doesn't appear here, add the parameter for it to "Other Parameters." Ces paramètres prévaudront sur les paramètres généraux. Si vous souhaitez modifier un paramètre pour ce jeu, mais qu'il n'apparaît pas ici, ajoutez-le dans "Autres paramètres." Plugins Greffons Audio Plugin: Greffon audio : Input Plugin: Greffon d'entrée : RSP Plugin: Greffon RSP : Video Plugin: Greffon vidéo : default Défaut Additional Settings Paramètres supplémentaires Config Directory: Dossier de configuration : Other Parameters: Autres paramètres : Browse... Naviguer... OK Cancel Annuler Config Directory Dossier de configuration GridView No Cart Pas de cartouche KeyCodes SDL Key Codes Key SDL Code Close Fermer ListView No Cart Pas de cartouche LogDialog <ParentName> Log Journaux <ParentName> Close Fermer MainWindow &File &Fichier &Open ROM... &Ouvrir une ROM... &Refresh List &Recharger la liste &Download/Update Info... &Télécharger/Mettre à jour les infos... D&elete Current Info... &Effacer les infos actuelles... &Quit &Quitter &Emulation &Émulation &Start &Démarrer St&op &Arrêter &Settings &Paramètres Edit mupen64plus.cfg... Éditer mupen64plus.cfg... Configure &Game... Configurer le &jeu... &Configure... &Configurer... There is no log. Either <ParentName> has not yet run or there was no output from the last run. Il n'y a pas de journaux. Soit <ParentName> n’a pas encore été lancé, soit il n’y a pas de données concernant le dernier démarrage. &Layout &Disposition View Log Consulter les journaux &View &Affichage None Aucune Table View Vue en tableau Grid View Vue en vignettes List View Vue en liste &Full-screen Pl&ein-Écran &Help &Aide Documentation Mupen64Plus Docs Default Controls &About À &propos Add a directory containing ROMs under Settings->Configure->Paths to use this view. Not Found Non trouvé Editor requires config directory to be L'éditeur nécessite que le dossier de configuration soit set to a directory with mupen64plus.cfg. un dossier contenant mupen64plus.cfg. See here for the default config location: Voir ici pour la configuration par défaut: No Output Pas de sortie All Files Tous les fichiers Open ROM File Ouvrir un fichier ROM No ROMs Aucune ROM No ROMs found in ZIP file. Aucune ROM trouvée dans le fichier ZIP. Select ROM Choisir une ROM Launch Lancer Cancel Annuler QObject GoodName Nom Good Filename Nom du fichier Filename (extension) Nom du fichier (extension) Zip File Fichier Zip Internal Name Nom interne Size Taille MD5 MD5 CRC1 CRC1 CRC2 CRC2 Players Joueurs Rumble Rumble Save Type Type de sauvegarde Game Title Titre du jeu Release Date Date de sortie Overview Aperçu ESRB ESRB Genre Genre Publisher Éditeur Developer Développeur Rating Certification Game Cover Jaquette Unknown ROM ROM inconnue Requires catalog file Fichier catalogue demandé Not found Non trouvé %1 MB %1 - MB Game Information Download Télécharger des informations sur le jeu No results found. Aucun résultat. No more results found. Pas d'autre résultat. Download Complete! Téléchargement terminé ! RomCollection Warning Attention No ROMs found in Aucune ROM trouvée dans No ROMs found. Aucune ROM trouvée. Database Not Loaded Base de données non chargée Could not connect to Sqlite database. Application may misbehave. Impossible de se connecter à la base de données Sqlite. L'application peut dysfonctionner. Loading ROMs... Chargement des ROMS... Cancel Annuler SettingsDialog Settings Paramètres Paths Chemins Browse... Naviguer... Mupen64Plus Files Fichier Mupen64Plus Mupen64Plus executable: Exécutable Mupen64Plus : ROM Directories Dossier des ROMS Add... Ajouter... Remove Supprimer Emulation Émulation Config directory: Dossier de configuration : Plugins directory: Dossier des plugins : Data directory: Dossier des données : Mupen64Plus-Qt will search for all .z64, .v64, .n64, and .zip files in these directories Mupen64Plus-Qt cherchera tous les fichiers z64, v64, n64 et zip dans ces dossiers Cached Interpreter Interpréteur avec cache Pure Interpreter Interpréteur pur Dynamic Recompiler Recompilation dynamique Graphics Graphique On Screen Display: Sur-affichage: Fullscreen: Plein écran : Resolution: Résolution : Plugins Garder l'anglicisme Plugins, Extensions, ou encore Plugiciel pour le Canada Greffons Video Plugin: Greffon vidéo : Audio Plugin: Greffon Audio : Input Plugin: Greffon d'entrée : RSP Plugin: Greffon RSP : Table Tableau Image: Image : ... ... Current Columns: Colonnes utilisées : Stretch First Column Étendre la première colonne Available Columns: Colonnes disponibles : Grid Vignettes ROM Label Intitulé de la ROM Show Label: Montrer l'intitulé : Label Text: Texte de l'intitulé : Label Color: Couleur de l'intitulé : Other Autre Custom Background: Choisir un arrière-plan : Sorting: Classement : Descending Descendant Background: Arrière-plan: ROM Image Images des ROMS Shadow Inactive Color: Couleur de l’ombre des vignettes inactives : Shadow Active Color: Couleur de l’ombre de la vignette active : Columns: Nombre de colonnes : Image Size: Taille des images : Adjust Automatically Ajustage automatique List Liste First Item as Header Premier intitulé comme en-tête Current Items: Intitulé actuel : Available Items: Intitulés disponibles : Display Cover Image Afficher les vignettes Text Size: Taille du texte: Theme: Thème: Additional Parameters: Paramètres supplémentaires : Save Options: Sauvegarder les options : Download Game Information (thegamesdb.net): Télécharger les informations sur les jeux (thegamesdb.net) : Language: Langue : <html><head/><body><p>Use to pass additional parameters to Mupen64Plus (cheats, etc.). See <a href="http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage"><span style=" text-decoration: underline; color:#0057ae;">http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage</span></a> for more information.</p></body></html> <html><head/><body><p>À utiliser pour ajouter des paramètres supplémentaires (cheats, etc.). Voir <a href="http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage"><span style=" text-decoration: underline; color:#0057ae;">http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage</span></a> pour plus d'informations.</p></body></html> <b>Note:</b> Language changes will not take place until application restart. <b>Remarque :</b> Le changement de langue ne prendra effet qu'après le redémarrage du programme. default Défaut Extra Small Très petite Small Petite Medium Moyenne Large Grande Extra Large Très grande Super Black Noir White Blanc Light Gray Gris clair Dark Gray Gris foncé Green Vert Cyan Cyan Blue Bleu Purple Violet Red Rouge Pink Rose Orange Orange Yellow Jaune Brown Marron Light Lumière Normal Normale Dark Sombre OK Cancel Annuler ROM Directory Dossiers des ROMS <ParentName> Executable Fichier exécutable <ParentName> Plugin Directory Dossier des plugins Data Directory Dossier des données Config Directory Dossier de configuration Background Image Image d'arrière-plan TableView No Cart Pas de cartouche TheGamesDBScraper <b>NOTE:</b> If you are deleting this game's information because the game doesn't <b>Remarque :</b> Si vous effacez les informations de ce jeu parce que le jeu better to create an account on il est préférable de créer un compte and add the game so other users can benefit as well. et d’ajouter le jeu afin d’en faire profiter également les autres utilisateurs. exist on TheGamesDB and <AppName> pulled the information for different game, it's n’est pas dans TheGamesDB et que <AppName> vous propose des informations pour un autre jeu, This will cause <AppName> to not update the information for this game until you <AppName> ne mettra pas à jour les informations jusqu'à ce que vous force it with "Download/Update Info..." relanciez le "Télécharger/Mettre à jour les infos..." Delete the current information for Effacer les informations actuelles pour Delete Game Information Effacer les informations du jeu The following error from TheGamesDB occured while downloading: Released on: Publié le : Does this look correct? Ces informations vous semblent-elles correctes ? Request timed out. Check your network settings. Temps dépassé. Vérifier vos paramètres de connexion. Continue scraping information? Continuer à récolter des informations ? Network Error Erreur réseau Erreur réseau mupen64plus-qt-1.15/resources/locale/mupen64plus-qt_ru.qm000066400000000000000000000647251413256700400234460ustar00rootroot00000000000000(5/*%"*0#+#+$,D0H) I'AIB@J6J6J6!2MŅ2PNJRLRLSV 8VP>VP Z8,UZm`,Zy%VZV[5[f]Wn@MUz93D]`d yF2ýK\-w1D$phW-#[8%!cs&Z${^".)ͬHsUHEojT= E5PzS\c  )F@[]G[G[CI Z~nM!urwCzq?=z^WM8IvIII%I6rIC/ K}J Xt)(Ę()@.`\K;MV|OhOl)eq61rw:D|%Q~}jUG4FSe*9%5^]P_ÊW|g0Y3gX+CR$]7IN"-Z Vm5)P{X~.JJB| GZJ}&Ѫ@(4+-_^&;P;PN1g_2l{lKl^Jl^JSOn85AL%2ƶ%ۀ$8 #>" PA dU6h؊+'ypXCIԢ E %47 _v ! !? 0r4Z 4U5 QZ& s(` [ D F y*I .>'i I3w e( TC TX` _dG p' (Ѯ&V )#G .Bl QW GJQ. 3P B ,  W μ4 قn ^V` (HH *"e N 9 O*. fCN u5 : s$L sT ,0 S X ɠ ʧ3  ޴V %4 Y |; 0?| Gi P# XVT [I1 _ __ {uOt * P P9= 29wK3$0ZTB3QL%w,BG7[S&usBIjAe l^'% zD6P`RR/SV+aYLeG(okC T @PRsXpX.ArGԇ%9֕Tia"!09B <ParentName> website AboutDialogv0AB@08205<K9 8=B5@D59A 4;O <ParentName>, =0?8A0==K9 =0 Qt.2A customizable launcher for using Qt. AboutDialog* ?@>3@0<<5 <AppName>About  AboutDialog$ 5?>78B>@89 GitHubGitHub repository AboutDialog 5@A8OVersion AboutDialog< 540:B>@ =0AB@>5: <ParentName> Config Editor ConfigEditor B<5=0Cancel ConfigEditor("01;8F0 :>4>2 :;028HKey Codes Reference ConfigEditor0>:C<5=B0F8O Mupen64PlusMupen64Plus Documentation ConfigEditor!>E@0=8BLSave ConfigEditor59AB285Action ControlInfo0:@KBLClose ControlInfo 2>4 :>=B@>;;5@0Controller Input ControlInfo&#<5=LH8BL 3@><:>ABLDecrease volume ControlInfoKAB@0O ?5@5<>B:0 2?5@54 (2>A?@>872545=85 =0 250% >B =>@<0;L=>9 A:>@>AB8 ?@8 C45@6820=88):Fast forward (playback at 250% normal speed while pressed) ControlInfo;O ?>;CG5=8O 1>;55 ?>4@>1=>9 8=D>@<0F88 87CG8B5 <link>>:C<5=B0F8N Mupen64Plus<linkend>. GFor more information see the Mupen64Plus Documentation.  ControlInfo;O =0AB@>9:8 :>=B@>;;5@0 ?5@5948B5 =0 <link>MBC AB@0=8FC<linkend>. BFor setting up a gamepad controller see this page.  ControlInfo>@OG85 :;028H8Hotkeys ControlInfo&#25;8G8BL 3@><:>ABLIncrease volume ControlInfo;028H0Key ControlInfo:03@C78BL A>AB>O=85 M<C;OB>@0Load emulator state ControlInfo@;028H8 Mupen64Plus ?> C<>;G0=8NMupen64Plus Default Controls ControlInfo.:;NG8BL/2K:;NG8BL 72C:Mute/unmute sound ControlInfo2>4 N64 N64 Input ControlInfo@8<5G0=85: 2K2>4 <>6=> ?>A<>B@5BL 2 <5=N -<C;OF8O->@>A<>B@5BL 6C@=0;.DNote that Mupen64Plus output can be viewed with Emulation->View Log. ControlInfo 0C70 Pause on/off ControlInfol060BL :=>?:C "Game Shark" (B>;L:> 5A;8 G8BK 2:;NG5=K)6Press "Game Shark" button (only if cheats are enabled) ControlInfo$K9B8 87 M<C;OB>@0Quit the emulator ControlInfo"!1@>A8BL M<C;OB>@Reset emulator ControlInfo:!>E@0=8BL A>AB>O=85 M<C;OB>@0Save emulator state ControlInfoK1@0BL 28@BC0;L=K9 'A;>B' 4;O :><0=4 A>E@0=5=8O/703@C7:8 A>AB>O=8O2Select virtual 'slot' for save/load state commands ControlInfoT5@59B8 =0 >48= :04@ 2?5@54 2> 2@5<O ?0C7K!Single frame advance while paused ControlInfo00<54;8BL M<C;OB>@ =0 5%Slow down emulator by 5% ControlInfo.#A:>@8BL M<C;OB>@ =0 5%Speed up emulator by 5% ControlInfo!=OBL A:@8=H>BTake screenshot ControlInfoL-B> :;028H8 Mupen64Plus ?> C<>;G0=8N. 0These are the default controls for Mupen64Plus.  ControlInfoE <>6=> <>48D8F8@>20BL 2 <5=N 0AB@>9:8->7<5=8BL mupen64plus.cfg...Edit mupen64plus.cfg... ControlInfoh5@5:;NG8BLAO <564C >:>==K< 8 ?>;=>M:@0==K< @568<0<8&Toggle between windowed and fullscreen ControlInfo B<5=0CancelDownloadDialog$09;FileDownloadDialog4URL 83@K =0 thegamesdb.netFrom thegamesdb.net URL of gameDownloadDialog0720=85 83@K: Name of Game:DownloadDialog 09B8SearchDownloadDialog009B8 8=D>@<0F8N >1 83@5Search Game InformationDownloadDialog.8;8 845=B8D8:0B>@ 83@K: or Game ID:DownloadDialogPA?>;=O5<K9 D09; <ParentName> =5 =0945=." executable not found.EmulatorHandler<ParentName> =5>6840==> ?@5:@0B8; @01>BC. @>A<>B@8B5 6C@=0; 4;O ?>;CG5=8O 1>;55 ?>4@>1=>9 8=D>@<0F88.C quit unexpectedly. Check the log for more information.EmulatorHandler,5:>@@5:B=K9 D09; ROM.Not a valid ROM File.EmulatorHandler&$09; ROM =5 =0945=.ROM file not found.EmulatorHandler5B 2K2>40. A;8 2K 4C<0;8, GB> Mupen64Plus 70?CI5=, ?>?@>1C9B5 A:>?8@>20BLJThere was no output. If you were expecting Mupen64Plus to run, try copyingEmulatorHandler$@>A<>B@5BL 6C@=0;View LogEmulatorHandler=8<0=85WarningEmulatorHandler:><0=4C 2KH5 2 :><0=4=CN AB@>:C 8 ?@>25@8BL 2K2>4 =0 =0;8G85 >H81>:.Cthe above command into a command prompt to see if you get an error.EmulatorHandler0>?>;=8B5;L=K5 =0AB@>9:8Additional SettingsGameSettingsDialog;038= 0C48>: Audio Plugin:GameSettingsDialog17>@... Browse...GameSettingsDialog B<5=0CancelGameSettingsDialog 0B0;>3 =0AB@>5:Config DirectoryGameSettingsDialog"0B0;>3 =0AB@>5::Config Directory:GameSettingsDialog0AB@>9:8 83@K Game SettingsGameSettingsDialog&0AB@>9:8 83@K 4;O:Game settings for:GameSettingsDialog;038= 22>40: Input Plugin:GameSettingsDialogOKOKGameSettingsDialog"@C385 =0AB@>9:8:Other Parameters:GameSettingsDialog;038=KPluginsGameSettingsDialog;038= RSP: RSP Plugin:GameSettingsDialogF0==K5 =0AB@>9:8 1C4CB 8<5BL ?@8>@8B5B =04 >1I8<8. A;8 2K E>B8B5 87<5=8BL :0:CN-;81> =0AB@>9:C 4;O 40==>9 83@K, => 55 745AL =5B, 4>102LB5 55 2 "@C385 =0AB@>9:8."These settings will override any global settings. If there's a setting you want to modify for this game, but it doesn't appear here, add the parameter for it to "Other Parameters."GameSettingsDialog;038= 2845>: Video Plugin:GameSettingsDialog> C<>;G0=8NdefaultGameSettingsDialog5B :0@B@8460No CartGridView0:@KBLCloseKeyCodes;028H0KeyKeyCodes>4 SDLSDL CodeKeyCodes>4K :;028H SDL SDL Key CodesKeyCodes5B :0@B@8460No CartListView&C@=0; <ParentName> Log LogDialog0:@KBLClose LogDialog& ?@>3@0<<5...&About MainWindow&0@0<5B@K... &Configure... MainWindowB&03@C78BL/>1=>28BL 8=D>@<0F8N...&Download/Update Info... MainWindow&-<C;OF8O &Emulation MainWindow &$09;&File MainWindow&> 25AL M:@0= &Full-screen MainWindow&><>IL&Help MainWindow& 0A?>;>65=85&Layout MainWindow&B:@KBL ROM... &Open ROM... MainWindow &K9B8&Quit MainWindow &1=>28BL A?8A>: &Refresh List MainWindow&0AB@>9:8 &Settings MainWindow&0?CAB8BL&Start MainWindow&84&View MainWindowd>102LB5 :0B0;>3, A>45@60I89 D09;K ROM, A ?><>ILN &Add a directory containing ROMs under  MainWindowA5 D09;K All Files MainWindow B<5=0Cancel MainWindow$0AB@>8BL &83@C...Configure &Game... MainWindow<&#40;8BL B5:CICN 8=D>@<0F8N...D&elete Current Info... MainWindow.#?@02;5=85 ?> C<>;G0=8NDefault Controls MainWindow>:C<5=B0F8O Documentation MainWindow67<5=8BL mupen64plus.cfg...Edit mupen64plus.cfg... MainWindowT;O @01>BK @540:B>@0 =C6=> 7040BL :0B0;>3 'Editor requires config directory to be  MainWindow !5B:0 Grid View MainWindow0?CAB8BLLaunch MainWindow !?8A>: List View MainWindow*>:C<5=BK Mupen64PlusMupen64Plus Docs MainWindow,5B 2K2>40 @57C;LB0B>2 No Output MainWindow5B D09;>2 ROMNo ROMs MainWindowB5 =0945=K D09;K ROM 2 D09;5 ZIP.No ROMs found in ZIP file. MainWindow5BNone MainWindow5 =0945=> Not Found MainWindow B:@KBL D09; ROM Open ROM File MainWindow>A<>B@8B5 745AL, GB>1K C7=0BL <5AB>?>;>65=85 D09;0 =0AB@>5: ?> C<>;G0=8N:)See here for the default config location: MainWindowK1@0BL ROM Select ROM MainWindow\0AB@>9:8->0@0<5B@K->CB8, GB>1K 8E ?>:070BL.,Settings->Configure->Paths to use this view. MainWindow&AB0=>28BLSt&op MainWindow"01;8F0 Table View MainWindowC@=0; >BACBAB2C5B. <ParentName> =5 1K; 70?CI5= 8;8 =5 2K2>48; 8=D>@<0F8N A <><5=B0 ?>A;54=53> 70?CA:0.^There is no log. Either has not yet run or there was no output from the last run. MainWindow$@>A<>B@5BL 6C@=0;View Log MainWindowlA D09;>< mupen64plus.cfg 2 :0G5AB25 :0B0;>30 =0AB@>5:.(set to a directory with mupen64plus.cfg. MainWindow %1 %1 MBQObjectCRC1CRC1QObjectCRC2CRC2QObject 07@01>BG8: DeveloperQObject&03@C7:0 7025@H5=0!Download Complete!QObjectESRBESRBQObject<O D09;0FilenameQObject,<O D09;0 (@0AH8@5=85)Filename (extension)QObject1;>6:0 83@K Game CoverQObject603@C7:0 8=D>@<0F88 >1 83@5Game Information DownloadQObject0720=85 83@K Game TitleQObject0=@GenreQObjectGoodNameGoodNameQObject=CB@5==55 8<O Internal NameQObjectMD5MD5QObject.>;LH5 @57C;LB0B>2 =5B.No more results found.QObject  57C;LB0B>2 =5B.No results found.QObject5 =0945=> Not foundQObject 17>@OverviewQObject 3@>:8PlayersQObject740B5;L PublisherQObject 59B8=3RatingQObject0B0 2K?CA:0 Release DateQObject."@51C5BAO D09; :0B0;>30Requires catalog fileQObject RumbleRumbleQObject"8? A>E@0=5=89 Save TypeQObject  07<5@SizeQObject58725AB=K9 ROM Unknown ROMQObject$09; ZipZip FileQObject B<5=0Cancel RomCollection5 C40;>AL ?>4:;NG8BLAO : 1075 40==KE SQLite. 5:>B>@K5 DC=:F88 ?@>3@0<<K <>3CB =5 @01>B0BL.@Could not connect to Sqlite database. Application may misbehave. RomCollection0070 40==KE =5 703@C65=0Database Not Loaded RomCollection,03@C7:0 D09;>2 ROM...Loading ROMs... RomCollection.$09;K ROM =5 =0945=K 2 No ROMs found in  RomCollection*$09;K ROM =5 =0945=K.No ROMs found. RomCollection=8<0=85Warning RomCollection......SettingsDialog:A?>;=O5<K9 D09; <ParentName> ExecutableSettingsDialog<b>@8<5G0=85:</b> /7K: 87<5=8BAO B>;L:> ?>A;5 ?5@570?CA:0 ?@>3@0<<K.LNote: Language changes will not take place until application restart.SettingsDialog<html><head/><body><p>8A?>;L7C9B5 4;O ?5@540G8 4>?>;=8B5;L=KE ?0@0<5B@>2 (G8B>2 8 B.4.). !<>B@8B5 <a href="http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage"><span style=" text-decoration: underline; color:#0057ae;">http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage</span></a> 4;O ?>;CG5=8O 1>;55 ?>4@>1=>9 8=D>@<0F88.</p></body></html>G

Use to pass additional parameters to Mupen64Plus (cheats, etc.). See http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage for more information.

SettingsDialog>1028BL...Add...SettingsDialog2>?>;=8B5;L=K5 =0AB@>9:8:Additional Parameters:SettingsDialog2K@02=820BL 02B><0B8G5A:8Adjust AutomaticallySettingsDialog;038= 0C48>: Audio Plugin:SettingsDialog$>ABC?=K5 AB>;1FK:Available Columns:SettingsDialog&>ABC?=K5 M;5<5=BK:Available Items:SettingsDialog&$>=>2>5 87>1@065=85Background ImageSettingsDialog$>=: Background:SettingsDialog '5@=K9BlackSettingsDialog !8=89BlueSettingsDialog>@8G=52K9BrownSettingsDialog17>@... Browse...SettingsDialog0MH8@C5<K9 8=B5@?@5B0B>@Cached InterpreterSettingsDialog B<5=0CancelSettingsDialog!B>;1FK:Columns:SettingsDialog 0B0;>3 =0AB@>5:Config DirectorySettingsDialog"0B0;>3 =0AB@>5::Config directory:SettingsDialog "5:CI85 AB>;1FK:Current Columns:SettingsDialog""5:CI85 M;5<5=BK:Current Items:SettingsDialog*>;L7>20B5;LA:89 D>=:Custom Background:SettingsDialog>;C1>9CyanSettingsDialog "5<=K9DarkSettingsDialog"5<=>-A5@K9 Dark GraySettingsDialog0B0;>3 40==KEData DirectorySettingsDialog0B0;>3 40==KE:Data directory:SettingsDialog> C1K20=8N DescendingSettingsDialog<B>1@060BL 87>1@065=85 >1;>6:8Display Cover ImageSettingsDialog\03@C60BL 8=D>@<0F8N >1 83@5 (thegamesdb.net):+Download Game Information (thegamesdb.net):SettingsDialog28=0<8G5A:89 @5:><?8;OB>@Dynamic RecompilerSettingsDialog-<C;OF8O EmulationSettingsDialogG5=L 1>;LH>9 Extra LargeSettingsDialogG5=L <0;5=L:89 Extra SmallSettingsDialogF5@2K9 M;5<5=B 2 :0G5AB25 703>;>2:0First Item as HeaderSettingsDialog>;=K9 M:@0=: Fullscreen:SettingsDialog@0D8:0GraphicsSettingsDialog5;5=K9GreenSettingsDialog !5B:0GridSettingsDialog& 07<5@ 87>1@065=8O: Image Size:SettingsDialog7>1@065=85:Image:SettingsDialog;038= 22>40: Input Plugin:SettingsDialog&25B <5B:8: Label Color:SettingsDialog"5:AB <5B:8: Label Text:SettingsDialog /7K:: Language:SettingsDialog>;LH>9LargeSettingsDialog!25B;K9LightSettingsDialog!25B;>-A5@K9 Light GraySettingsDialog !?8A>:ListSettingsDialog!@54=89MediumSettingsDialog"$09;K Mupen64PlusMupen64Plus FilesSettingsDialog<A?>;=O5<K9 D09; Mupen64Plus :Mupen64Plus executable: SettingsDialogMupen64Plus-Qt 1C45B 8A:0BL 2A5 D09;K Z64, V64, N64 8 ZIP 2 MB8E :0B0;>30EXMupen64Plus-Qt will search for all .z64, .v64, .n64, and .zip files in these directoriesSettingsDialog1KG=K9NormalSettingsDialogOKOKSettingsDialog&0M:@0==K9 48A?;59:On Screen Display:SettingsDialog@0=652K9OrangeSettingsDialog @C3>5OtherSettingsDialogCB8PathsSettingsDialog >7>2K9PinkSettingsDialog 0B0;>3 ?;038=>2Plugin DirectorySettingsDialog;038=KPluginsSettingsDialog"0B0;>3 ?;038=>2:Plugins directory:SettingsDialog('8ABK9 8=B5@?@5B0B>@Pure InterpreterSettingsDialogC@?C@=K9PurpleSettingsDialog&0B0;>38 D09;>2 ROMROM DirectoriesSettingsDialog$0B0;>3 D09;>2 ROM ROM DirectorySettingsDialog7>1@065=85 ROM ROM ImageSettingsDialog5B:0 ROM ROM LabelSettingsDialog;038= RSP: RSP Plugin:SettingsDialog@0A=K9RedSettingsDialog#40;8BLRemoveSettingsDialog 07@5H5=85: Resolution:SettingsDialog(!>E@0=OBL =0AB@>9:8: Save Options:SettingsDialog0AB@>9:8SettingsSettingsDialog(&25B 0:B82=>9 B5=8: Shadow Active Color:SettingsDialog*&25B =50:B82=>9 B5=8:Shadow Inactive Color:SettingsDialog>:070BL <5B:C: Show Label:SettingsDialog0;5=L:89SmallSettingsDialog!>@B8@>2:0:Sorting:SettingsDialog4 0ABO3820BL ?5@2K9 AB>;15FStretch First ColumnSettingsDialog !C?5@SuperSettingsDialog"01;8F0TableSettingsDialog 07<5@ B5:AB0: Text Size:SettingsDialog "5<0:Theme:SettingsDialog;038= 2845>: Video Plugin:SettingsDialog 5;K9WhiteSettingsDialog 5;BK9YellowSettingsDialog> C<>;G0=8NdefaultSettingsDialog5B :0@B@8460No Cart TableView<b>@8<5G0=85:</b> A;8 2K C40;O5B5 8=D>@<0F8N >1 MB>9 83@5, ?>A:>;L:C 55 RNOTE: If you are deleting this game's information because the game doesn't TheGamesDBScraper@@>4>;68BL ?>;CG5=85 8=D>@<0F88?Continue scraping information?TheGamesDBScraper4#40;8BL 8=D>@<0F8N >1 83@5Delete Game InformationTheGamesDBScraper<#40;8BL B5:CICN 8=D>@<0F8N 4;O"Delete the current information forTheGamesDBScraperA5 25@=>?Does this look correct?TheGamesDBScraperH81:0 A5B8 Network ErrorTheGamesDBScraperK?CI5=> =0: Released on: TheGamesDBScraper`@5<O 70?@>A0 8AB5:;>. @>25@LB5 =0AB@>9:8 A5B8./Request timed out. Check your network settings.TheGamesDBScraper`> 2@5<O 703@C7:8 A TheGamesDB ?@>87>H;0 >H81:0:>The following error from TheGamesDB occured while downloading:TheGamesDBScraper~<AppName> =5 1C45B >1=>2;OBL 8=D>@<0F8N 4;O MB>9 83@K, ?>:0 2K PThis will cause to not update the information for this game until you TheGamesDBScraperd8 4>1028BL 83@C. -B> ?><>65B 4@C38< ?>;L7>20B5;O<.4and add the game so other users can benefit as well.TheGamesDBScraper0;CGH5 A>740BL 0::0C=B =0better to create an account onTheGamesDBScraper~=5B =0 TheGamesDB 8 <AppName> 2K405B 8=D>@<0F8N > 4@C3>9 83@5, Rexist on TheGamesDB and pulled the information for different game, it's TheGamesDBScraper\=5 2K?>;=8B5 "03@C7:0/1=>28BL 8=D>@<0F8N..."'force it with "Download/Update Info..."TheGamesDBScraper ) , mupen64plus-qt-1.15/resources/locale/mupen64plus-qt_ru.ts000066400000000000000000001715301413256700400234500ustar00rootroot00000000000000 AboutDialog About <AppName> О программе <AppName> Version Версия A customizable launcher for <ParentName> using Qt. Настраиваемый интерфейс для <ParentName>, написанный на Qt. <ParentName> website Сайт <ParentName> GitHub repository Репозиторий GitHub ConfigEditor <ParentName> Config Editor Редактор настроек <ParentName> Key Codes Reference Таблица кодов клавиш Mupen64Plus Documentation Документация Mupen64Plus Save Сохранить Cancel Отмена ControlInfo Mupen64Plus Default Controls Клавиши Mupen64Plus по умолчанию These are the default controls for Mupen64Plus. Это клавиши Mupen64Plus по умолчанию. They can be modified using Settings->Edit mupen64plus.cfg... Их можно модифицировать в меню Настройки->Изменить mupen64plus.cfg... For more information see the <link>Mupen64Plus Documentation<linkend>. Для получения более подробной информации изучите <link>Документацию Mupen64Plus<linkend>. For setting up a gamepad controller see <link>this page<linkend>. Для настройки контроллера перейдите на <link>эту страницу<linkend>. Note that Mupen64Plus output can be viewed with Emulation->View Log. Примечание: вывод можно посмотреть в меню Эмуляция->Просмотреть журнал. N64 Input Ввод N64 Key Клавиша Quit the emulator Выйти из эмулятора Select virtual 'slot' for save/load state commands Выбрать виртуальный 'слот' для команд сохранения/загрузки состояния Save emulator state Сохранить состояние эмулятора Load emulator state Загрузить состояние эмулятора Reset emulator Сбросить эмулятор Slow down emulator by 5% Замедлить эмулятор на 5% Speed up emulator by 5% Ускорить эмулятор на 5% Take screenshot Снять скриншот Toggle between windowed and fullscreen Переключиться между оконным и полноэкранным режимами Pause on/off Пауза Mute/unmute sound Включить/выключить звук Press "Game Shark" button (only if cheats are enabled) Нажать кнопку "Game Shark" (только если читы включены) Single frame advance while paused Перейти на один кадр вперед во время паузы Fast forward (playback at 250% normal speed while pressed) Быстрая перемотка вперед (воспроизведение на 250% от нормальной скорости при удерживании) Decrease volume Уменьшить громкость Increase volume Увеличить громкость Action Действие Close Закрыть Controller Input Ввод контроллера Hotkeys Горячие клавиши DownloadDialog Search Game Information Найти информацию об игре File Файл Name of Game: Название игры: or Game ID: или идентификатор игры: From thegamesdb.net URL of game URL игры на thegamesdb.net Search Найти Cancel Отмена EmulatorHandler Warning Внимание <ParentName> quit unexpectedly. Check the log for more information. <ParentName> неожиданно прекратил работу. Просмотрите журнал для получения более подробной информации. View Log Просмотреть журнал There was no output. If you were expecting Mupen64Plus to run, try copying Нет вывода. Если вы думали, что Mupen64Plus запущен, попробуйте скопировать the above command into a command prompt to see if you get an error. команду выше в командную строку и проверить вывод на наличие ошибок. <ParentName> executable not found. Исполняемый файл <ParentName> не найден. Not a valid ROM File. Некорректный файл ROM. ROM file not found. Файл ROM не найден. GameSettingsDialog Game Settings Настройки игры Game settings for: Настройки игры для: These settings will override any global settings. If there's a setting you want to modify for this game, but it doesn't appear here, add the parameter for it to "Other Parameters." Данные настройки будут иметь приоритет над общими. Если вы хотите изменить какую-либо настройку для данной игры, но ее здесь нет, добавьте ее в "Другие настройки." Plugins Плагины Audio Plugin: Плагин аудио: Input Plugin: Плагин ввода: RSP Plugin: Плагин RSP: Video Plugin: Плагин видео: default По умолчанию Additional Settings Дополнительные настройки Config Directory: Каталог настроек: Other Parameters: Другие настройки: Browse... Обзор... OK OK Cancel Отмена Config Directory Каталог настроек GridView No Cart Нет картриджа KeyCodes SDL Key Codes Коды клавиш SDL Key Клавиша SDL Code Код SDL Close Закрыть ListView No Cart Нет картриджа LogDialog <ParentName> Log Журнал <ParentName> Close Закрыть MainWindow &File &Файл &Open ROM... &Открыть ROM... &Refresh List О&бновить список &Download/Update Info... &Загрузить/обновить информацию... D&elete Current Info... &Удалить текущую информацию... &Quit &Выйти &Emulation &Эмуляция &Start &Запустить St&op &Остановить &Settings &Настройки Edit mupen64plus.cfg... Изменить mupen64plus.cfg... Configure &Game... Настроить &игру... &Configure... &Параметры... There is no log. Either <ParentName> has not yet run or there was no output from the last run. Журнал отсутствует. <ParentName> не был запущен или не выводил информацию с момента последнего запуска. &Layout &Расположение View Log Просмотреть журнал &View &Вид None Нет Table View Таблица Grid View Сетка List View Список &Full-screen В&о весь экран &Help &Помощь Documentation Документация Mupen64Plus Docs Документы Mupen64Plus Default Controls Управление по умолчанию &About &О программе... Add a directory containing ROMs under Добавьте каталог, содержащий файлы ROM, с помощью Settings->Configure->Paths to use this view. Настройки->Параметры->Пути, чтобы их показать. Not Found Не найдено Editor requires config directory to be Для работы редактора нужно задать каталог set to a directory with mupen64plus.cfg. с файлом mupen64plus.cfg в качестве каталога настроек. See here for the default config location: Посмотрите здесь, чтобы узнать местоположение файла настроек по умолчанию: No Output Нет вывода результатов All Files Все файлы Open ROM File Открыть файл ROM No ROMs Нет файлов ROM No ROMs found in ZIP file. Не найдены файлы ROM в файле ZIP. Select ROM Выбрать ROM Launch Запустить Cancel Отмена QObject GoodName GoodName Filename Имя файла Filename (extension) Имя файла (расширение) Zip File Файл Zip Internal Name Внутреннее имя Size Размер MD5 MD5 CRC1 CRC1 CRC2 CRC2 Players Игроки Rumble Rumble Save Type Тип сохранений Game Title Название игры Release Date Дата выпуска Overview Обзор ESRB ESRB Genre Жанр Publisher Издатель Developer Разработчик Rating Рейтинг Game Cover Обложка игры Unknown ROM Неизвестный ROM Requires catalog file Требуется файл каталога Not found Не найдено %1 MB %1 МБ Game Information Download Загрузка информации об игре No results found. Результатов нет. No more results found. Больше результатов нет. Download Complete! Загрузка завершена! RomCollection Warning Внимание No ROMs found in Файлы ROM не найдены в No ROMs found. Файлы ROM не найдены. Database Not Loaded База данных не загружена Could not connect to Sqlite database. Application may misbehave. Не удалось подключиться к базе данных SQLite. Некоторые функции программы могут не работать. Loading ROMs... Загрузка файлов ROM... Cancel Отмена SettingsDialog Settings Настройки Paths Пути Browse... Обзор... Mupen64Plus Files Файлы Mupen64Plus Mupen64Plus executable: Исполняемый файл Mupen64Plus : ROM Directories Каталоги файлов ROM Add... Добавить... Remove Удалить Emulation Эмуляция Config directory: Каталог настроек: Plugins directory: Каталог плагинов: Data directory: Каталог данных: Mupen64Plus-Qt will search for all .z64, .v64, .n64, and .zip files in these directories Mupen64Plus-Qt будет искать все файлы Z64, V64, N64 и ZIP в этих каталогах Cached Interpreter Кэшируемый интерпретатор Pure Interpreter Чистый интерпретатор Dynamic Recompiler Динамический рекомпилятор Graphics Графика On Screen Display: Наэкранный дисплей: Fullscreen: Полный экран: Resolution: Разрешение: Plugins Плагины Video Plugin: Плагин видео: Audio Plugin: Плагин аудио: Input Plugin: Плагин ввода: RSP Plugin: Плагин RSP: Table Таблица Image: Изображение: ... ... Current Columns: Текущие столбцы: Stretch First Column Растягивать первый столбец Available Columns: Доступные столбцы: Grid Сетка ROM Label Метка ROM Show Label: Показать метку: Label Text: Текст метки: Label Color: Цвет метки: Other Другое Custom Background: Пользовательский фон: Sorting: Сортировка: Descending По убыванию Background: Фон: ROM Image Изображение ROM Shadow Inactive Color: Цвет неактивной тени: Shadow Active Color: Цвет активной тени: Columns: Столбцы: Image Size: Размер изображения: Adjust Automatically Выравнивать автоматически List Список First Item as Header Первый элемент в качестве заголовка Current Items: Текущие элементы: Available Items: Доступные элементы: Display Cover Image Отображать изображение обложки Text Size: Размер текста: Theme: Тема: Additional Parameters: Дополнительные настройки: Save Options: Сохранять настройки: Download Game Information (thegamesdb.net): Загружать информацию об игре (thegamesdb.net): Language: Язык: <html><head/><body><p>Use to pass additional parameters to Mupen64Plus (cheats, etc.). See <a href="http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage"><span style=" text-decoration: underline; color:#0057ae;">http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage</span></a> for more information.</p></body></html> <html><head/><body><p>используйте для передачи дополнительных параметров (читов и т.д.). Смотрите <a href="http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage"><span style=" text-decoration: underline; color:#0057ae;">http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage</span></a> для получения более подробной информации.</p></body></html> <b>Note:</b> Language changes will not take place until application restart. <b>Примечание:</b> Язык изменится только после перезапуска программы. default По умолчанию Extra Small Очень маленький Small Маленький Medium Средний Large Большой Extra Large Очень большой Super Супер Black Черный White Белый Light Gray Светло-серый Dark Gray Темно-серый Green Зеленый Cyan Голубой Blue Синий Purple Пурпурный Red Красный Pink Розовый Orange Оранжевый Yellow Желтый Brown Коричневый Light Светлый Normal Обычный Dark Темный OK OK Cancel Отмена ROM Directory Каталог файлов ROM <ParentName> Executable Исполняемый файл <ParentName> Plugin Directory Каталог плагинов Data Directory Каталог данных Config Directory Каталог настроек Background Image Фоновое изображение TableView No Cart Нет картриджа TheGamesDBScraper <b>NOTE:</b> If you are deleting this game's information because the game doesn't <b>Примечание:</b> Если вы удаляете информацию об этой игре, поскольку ее better to create an account on лучше создать аккаунт на and add the game so other users can benefit as well. и добавить игру. Это поможет другим пользователям. exist on TheGamesDB and <AppName> pulled the information for different game, it's нет на TheGamesDB и <AppName> выдает информацию о другой игре, This will cause <AppName> to not update the information for this game until you <AppName> не будет обновлять информацию для этой игры, пока вы force it with "Download/Update Info..." не выполните "Загрузка/Обновить информацию..." Delete the current information for Удалить текущую информацию для Delete Game Information Удалить информацию об игре The following error from TheGamesDB occured while downloading: Во время загрузки с TheGamesDB произошла ошибка: Released on: Выпущено на: Does this look correct? Все верно? Request timed out. Check your network settings. Время запроса истекло. Проверьте настройки сети. Continue scraping information? Продолжить получение информации? Network Error Ошибка сети mupen64plus-qt-1.15/resources/mupen64plus-qt.6000066400000000000000000000041001413256700400212060ustar00rootroot00000000000000.\" Copyright (c) 2013, Dan Hasting .\" All rights reserved. .\" .\" %%%LICENSE_START(BSD_3_CLAUSE) .\" 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 the organization 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 THE COPYRIGHT HOLDER 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. .\" %%%LICENSE_END .TH "Mupen64Plus-Qt" "6" "September 2015" "Mupen64Plus-Qt" "User Manual" .SH "NAME" Mupen64Plus-Qt \- a customizable launcher for Mupen64Plus .SH "SYNOPSIS" \fBmupen64plus-qt\fR .SH "DESCRIPTION" \fBMupen64Plus-Qt\fR is a customizable launcher for the mupen64plus-ui-console frontend. It was adapted from CEN64-Qt to work with Mupen64Plus. .TP See the README at https://www.github.com/dh4/mupen64plus-qt for a detailed description of its features and usage. mupen64plus-qt-1.15/resources/mupen64plus-qt.desktop000077500000000000000000000006271413256700400225270ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=Mupen64Plus-Qt GenericName=Mupen64Plus Launcher GenericName[ru]=Интерфейс для Mupen64Plus Comment=A customizable launcher for Mupen64Plus Comment[ru]=Настраиваемый интерфейс для Mupen64Plus Exec=mupen64plus-qt Icon=mupen64plus-qt Terminal=false Categories=Game;Emulator; Keywords=emulator;mupen;mupen64plus;Nintendo;64; mupen64plus-qt-1.15/resources/mupen64plusqt.qrc000066400000000000000000000005401413256700400215550ustar00rootroot00000000000000 images/mupen64plus.png other/LICENSE images/not-found.png other/VERSION locale/mupen64plus-qt_fr.qm locale/mupen64plus-qt_ru.qm images/keyboard.png mupen64plus-qt-1.15/resources/other/000077500000000000000000000000001413256700400174215ustar00rootroot00000000000000mupen64plus-qt-1.15/resources/other/LICENSE000066400000000000000000000027171413256700400204350ustar00rootroot00000000000000Copyright (c) 2013, Dan Hasting All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of the organization 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 HOLDER 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.mupen64plus-qt-1.15/resources/other/VERSION000077700000000000000000000000001413256700400221622../../VERSIONustar00rootroot00000000000000mupen64plus-qt-1.15/src/000077500000000000000000000000001413256700400150555ustar00rootroot00000000000000mupen64plus-qt-1.15/src/common.cpp000066400000000000000000000342541413256700400170610ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "common.h" #include "global.h" #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include #else #include #endif QByteArray byteswap(QByteArray romData) { QByteArray flipped; if (romData.left(4).toHex() == "37804012") { for (int i = 0; i < romData.length(); i += 2) { flipped.append(romData[i + 1]); flipped.append(romData[i]); } return flipped; } else { return romData; } } QString getCacheLocation() { return getDataLocation() + "/cache_v2/"; } QString getDataLocation() { QString dataDir; #ifdef Q_OS_WIN dataDir = QCoreApplication::applicationDirPath(); #else dataDir = QStandardPaths::writableLocation(QStandardPaths::DataLocation) .replace(ParentName+"/"+AppName,AppNameLower); #endif QDir data(dataDir); if (!data.exists()) data.mkpath(dataDir); return dataDir; } QColor getColor(QString color, int transparency) { if (transparency <= 255) { if (color == "Black") return QColor(0, 0, 0, transparency); else if (color == "White") return QColor(255, 255, 255, transparency); else if (color == "Light Gray") return QColor(200, 200, 200, transparency); else if (color == "Dark Gray") return QColor(50, 50, 59, transparency); else if (color == "Green") return QColor(0, 255, 0, transparency); else if (color == "Cyan") return QColor(30, 175, 255, transparency); else if (color == "Blue") return QColor(0, 0, 255, transparency); else if (color == "Purple") return QColor(128, 0, 128, transparency); else if (color == "Red") return QColor(255, 0, 0, transparency); else if (color == "Pink") return QColor(246, 96, 171, transparency); else if (color == "Orange") return QColor(255, 165, 0, transparency); else if (color == "Yellow") return QColor(255, 255, 0, transparency); else if (color == "Brown") return QColor(127, 70, 44, transparency); } return QColor(0, 0, 0, 255); } QString getDefaultLanguage() { QString systemLanguage = QLocale::system().name().left(2); //Add other languages here as translations are done if (systemLanguage == "fr") return "FR"; else if (systemLanguage == "ru") return "RU"; else return "EN"; } int getDefaultWidth(QString id, int imageWidth) { if (id == "Overview") return 400; else if (id == "GoodName" || id.left(8) == "Filename" || id == "Game Title") return 300; else if (id == "MD5") return 250; else if (id == "Internal Name" || id == "Publisher" || id == "Developer") return 200; else if (id == "ESRB" || id == "Genre") return 150; else if (id == "Save Type" || id == "Release Date") return 100; else if (id == "CRC1" || id == "CRC2") return 90; else if (id == "Size" || id == "Rumble" || id == "Players" || id == "Rating") return 75; else if (id == "Game Cover") return imageWidth; else return 100; } int getGridSize(QString which) { QString size = SETTINGS.value("Grid/imagesize","Medium").toString(); if (which == "height") { if (SETTINGS.value("Grid/label", "true").toString() == "true") { if (size == "Extra Small") return 65; if (size == "Small") return 90; if (size == "Medium") return 145; if (size == "Large") return 190; if (size == "Extra Large") return 250; if (size == "Super") return 360; } else { if (size == "Extra Small") return 47; if (size == "Small") return 71; if (size == "Medium") return 122; if (size == "Large") return 172; if (size == "Extra Large") return 224; if (size == "Super") return 330; } } else if (which == "width") { if (size == "Extra Small") return 60; if (size == "Small") return 90; if (size == "Medium") return 160; if (size == "Large") return 225; if (size == "Extra Large") return 300; if (size == "Super") return 440; } else if (which == "font") { if (size == "Extra Small") return 5; if (size == "Small") return 7; if (size == "Medium") return 10; if (size == "Large") return 12; if (size == "Extra Large") return 13; if (size == "Super") return 15; } return 0; } QSize getImageSize(QString view) { QString size = SETTINGS.value(view+"/imagesize","Medium").toString(); if (view == "Table") { if (size == "Extra Small") return QSize(33, 24); if (size == "Small") return QSize(48, 35); if (size == "Medium") return QSize(69, 50); if (size == "Large") return QSize(103, 75); if (size == "Extra Large") return QSize(138, 100); if (size == "Super") return QSize(210, 150); } else if (view == "Grid" || view == "List") { if (size == "Extra Small") return QSize(48, 35); if (size == "Small") return QSize(69, 50); if (size == "Medium") return QSize(138, 100); if (size == "Large") return QSize(203, 150); if (size == "Extra Large") return QSize(276, 200); if (size == "Super") return QSize(425, 300); } return QSize(); } QString getRomInfo(QString identifier, const Rom *rom, bool removeWarn, bool sort) { QString text = ""; if (identifier == "GoodName") text = rom->goodName; else if (identifier == "Filename") text = rom->baseName; else if (identifier == "Filename (extension)") text = rom->fileName; else if (identifier == "Zip File") text = rom->zipFile; else if (identifier == "Internal Name") text = rom->internalName; else if (identifier == "Size") text = rom->size; else if (identifier == "MD5") text = rom->romMD5.toLower(); else if (identifier == "CRC1") text = rom->CRC1.toLower(); else if (identifier == "CRC2") text = rom->CRC2.toLower(); else if (identifier == "Players") text = rom->players; else if (identifier == "Rumble") text = rom->rumble; else if (identifier == "Save Type") text = rom->saveType; else if (identifier == "Game Title") text = rom->gameTitle; else if (identifier == "Release Date") text = rom->releaseDate; else if (identifier == "Overview") text = rom->overview; else if (identifier == "ESRB") text = rom->esrb; else if (identifier == "Genre") text = rom->genre; else if (identifier == "Publisher") text = rom->publisher; else if (identifier == "Developer") text = rom->developer; else if (identifier == "Rating") text = rom->rating; if (!removeWarn) return text; else if (text == getTranslation("Unknown ROM") || text == getTranslation("Requires catalog file") || text == getTranslation("Not found")) { if (sort) return "ZZZ"; //Sort warnings at the end else return ""; } else return text; } QGraphicsDropShadowEffect *getShadow(bool active) { QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect; if (active) { shadow->setBlurRadius(25.0); shadow->setColor(getColor(SETTINGS.value("Grid/activecolor","Cyan").toString(), 255)); shadow->setOffset(0); } else { shadow->setBlurRadius(10.0); shadow->setColor(getColor(SETTINGS.value("Grid/inactivecolor","Black").toString(), 200)); shadow->setOffset(0); } return shadow; } int getTableDataIndexFromName(QString infoName) { if (infoName == "fileName") return 0; else if (infoName == "dirName") return 1; else if (infoName == "search") return 2; else if (infoName == "romMD5") return 3; else if (infoName == "zipFile") return 4; return 0; } int getTextSize() { QString size = SETTINGS.value("List/textsize","Medium").toString(); if (size == "Extra Small") return 7; if (size == "Small") return 9; if (size == "Medium") return 10; if (size == "Large") return 12; if (size == "Extra Large") return 14; return 10; } QString getTranslation(QString text) { if (text == "GoodName") return QObject::tr("GoodName"); else if (text == "Filename") return QObject::tr("Filename"); else if (text == "Filename (extension)") return QObject::tr("Filename (extension)"); else if (text == "Zip File") return QObject::tr("Zip File"); else if (text == "Internal Name") return QObject::tr("Internal Name"); else if (text == "Size") return QObject::tr("Size"); else if (text == "MD5") return QObject::tr("MD5"); else if (text == "CRC1") return QObject::tr("CRC1"); else if (text == "CRC2") return QObject::tr("CRC2"); else if (text == "Players") return QObject::tr("Players"); else if (text == "Rumble") return QObject::tr("Rumble"); else if (text == "Save Type") return QObject::tr("Save Type"); else if (text == "Game Title") return QObject::tr("Game Title"); else if (text == "Release Date") return QObject::tr("Release Date"); else if (text == "Overview") return QObject::tr("Overview"); else if (text == "ESRB") return QObject::tr("ESRB"); else if (text == "Genre") return QObject::tr("Genre"); else if (text == "Publisher") return QObject::tr("Publisher"); else if (text == "Developer") return QObject::tr("Developer"); else if (text == "Rating") return QObject::tr("Rating"); else if (text == "Game Cover") return QObject::tr("Game Cover"); else if (text == "Unknown ROM") return QObject::tr("Unknown ROM"); else if (text == "Requires catalog file") return QObject::tr("Requires catalog file"); else if (text == "Not found") return QObject::tr("Not found"); return text; } QString getVersion() { QFile versionFile(":/other/VERSION"); versionFile.open(QIODevice::ReadOnly); QString version = versionFile.readAll(); versionFile.close(); return version; } QStringList getZippedFiles(QString completeFileName) { QuaZip zipFile(completeFileName); zipFile.open(QuaZip::mdUnzip); QStringList files = zipFile.getFileNameList(); zipFile.close(); return files; } QByteArray *getZippedRom(QString romFileName, QString zipFile) { QuaZipFile zippedFile(zipFile, romFileName); zippedFile.open(QIODevice::ReadOnly); QByteArray *romData = new QByteArray(); romData->append(zippedFile.readAll()); zippedFile.close(); return romData; } bool romSorter(const Rom &firstRom, const Rom &lastRom) { QString sort, direction; QString layout = SETTINGS.value("View/layout", "None").toString(); if (layout == "grid") { sort = SETTINGS.value("Grid/sort", "Filename").toString(); direction = SETTINGS.value("Grid/sortdirection", "ascending").toString(); } else if (layout == "list") { sort = SETTINGS.value("List/sort", "Filename").toString(); direction = SETTINGS.value("List/sortdirection", "ascending").toString(); } else //just return sort by filename return firstRom.fileName < lastRom.fileName; QString sortFirst = "", sortLast = ""; if (sort == "Size") { int firstSize = firstRom.sortSize; int lastSize = lastRom.sortSize; if (direction == "descending") return firstSize > lastSize; else return firstSize < lastSize; } else if (sort == "Release Date") { sortFirst = firstRom.sortDate; sortLast = lastRom.sortDate; } else { sortFirst = getRomInfo(sort, &firstRom, true, true); sortLast = getRomInfo(sort, &lastRom, true, true); } if (sortFirst == sortLast) { //Equal so sort on filename sortFirst = firstRom.fileName; sortLast = lastRom.fileName; } if (direction == "descending") return sortFirst > sortLast; else return sortFirst < sortLast; } mupen64plus-qt-1.15/src/common.h000066400000000000000000000057641413256700400165320ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef COMMON_H #define COMMON_H #include #include #include class QColor; class QSize; struct Rom { QString fileName; QString directory; QString romMD5; QString internalName; QString zipFile; QString baseName; QString size; int sortSize; QString goodName; QString CRC1; QString CRC2; QString players; QString saveType; QString rumble; QString gameTitle; QString releaseDate; QString sortDate; QString overview; QString esrb; QString genre; QString publisher; QString developer; QString rating; QPixmap image; int count; bool imageExists; }; bool romSorter(const Rom &firstRom, const Rom &lastRom); int getDefaultWidth(QString id, int imageWidth); int getGridSize(QString which); int getTableDataIndexFromName(QString infoName); int getTextSize(); QByteArray byteswap(QByteArray romData); QStringList getZippedFiles(QString completeFileName); QByteArray *getZippedRom(QString romFileName, QString zipFile); QColor getColor(QString color, int transparency = 255); QString getDefaultLanguage(); QString getTranslation(QString text); QGraphicsDropShadowEffect *getShadow(bool active); QSize getImageSize(QString view); QString getCacheLocation(); QString getDataLocation(); QString getRomInfo(QString identifier, const Rom *rom, bool removeWarn = false, bool sort = false); QString getVersion(); #endif // COMMON_H mupen64plus-qt-1.15/src/dialogs/000077500000000000000000000000001413256700400164775ustar00rootroot00000000000000mupen64plus-qt-1.15/src/dialogs/aboutdialog.cpp000066400000000000000000000071301413256700400214760ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "aboutdialog.h" #include "../global.h" #include "../common.h" #include #include #include #include AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent) { setWindowTitle(tr("About ").replace("",AppName)); setMinimumSize(600, 300); aboutLayout = new QGridLayout(this); icon = new QLabel(this); icon->setPixmap(QPixmap(":/images/"+ParentNameLower+".png")); QFile licenseFile(":/other/LICENSE"); licenseFile.open(QIODevice::ReadOnly); license = new QPlainTextEdit(licenseFile.readAll(), this); license->setReadOnly(true); licenseFile.close(); QString description = ""+AppName+"
" + tr("Version") + " " + getVersion() + "

" + tr("A customizable launcher for using Qt.") .replace("",ParentName); QString website = "" + tr(" website").replace("",ParentName) + ""; QString github = "" + tr("GitHub repository") + ""; descriptionLabel = new QLabel(description, this); copyrightLabel = new QLabel(Copyright, this); websiteLink = new QLabel(website, this); githubLink = new QLabel(github, this); websiteLink->setOpenExternalLinks(true); githubLink->setOpenExternalLinks(true); buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, this); aboutLayout->addWidget(icon, 0, 0, 4, 1); aboutLayout->addWidget(descriptionLabel, 0, 1); aboutLayout->addWidget(license, 1, 1); aboutLayout->addWidget(copyrightLabel, 2, 1); aboutLayout->addWidget(websiteLink, 3, 1); aboutLayout->addWidget(githubLink, 4, 1); aboutLayout->addWidget(buttonBox, 5, 1); aboutLayout->setColumnStretch(1, 1); aboutLayout->setRowStretch(1, 1); aboutLayout->setColumnMinimumWidth(0, 150); connect(buttonBox, SIGNAL(accepted()), this, SLOT(close())); setLayout(aboutLayout); } mupen64plus-qt-1.15/src/dialogs/aboutdialog.h000066400000000000000000000040531413256700400211440ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef ABOUTDIALOG_H #define ABOUTDIALOG_H #include class QDialogButtonBox; class QGridLayout; class QLabel; class QPlainTextEdit; class AboutDialog : public QDialog { Q_OBJECT public: explicit AboutDialog(QWidget *parent = 0); private: QDialogButtonBox *buttonBox; QGridLayout *aboutLayout; QLabel *copyrightLabel; QLabel *descriptionLabel; QLabel *githubLink; QLabel *websiteLink; QLabel *icon; QPlainTextEdit *license; }; #endif // ABOUTDIALOG_H mupen64plus-qt-1.15/src/dialogs/configeditor.cpp000066400000000000000000000132141413256700400216600ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "configeditor.h" #include "../global.h" #include "keycodesdialog.h" #include #include #include #include #include ConfigEditor::ConfigEditor(QString configFile, QWidget *parent) : QDialog(parent) { config.setFileName(configFile); setWindowTitle(tr(" Config Editor").replace("",ParentName)); setMinimumSize(600, 400); editorLayout = new QGridLayout(this); editorLayout->setContentsMargins(5, 10, 5, 10); editorArea = new QTextEdit(this); editorArea->setWordWrapMode(QTextOption::NoWrap); QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); editorArea->setFont(font); highlighter = new ConfigHighlighter(editorArea->document()); config.open(QIODevice::ReadOnly); editorArea->setPlainText(config.readAll()); config.close(); controls = new QWidget(this); controlsLayout = new QGridLayout(controls); controlsLayout->setContentsMargins(0, 0, 0, 0); codesButtonBox = new QDialogButtonBox(Qt::Horizontal, controls); codesButtonBox->addButton(tr("Key Codes Reference"), QDialogButtonBox::HelpRole); QString docsLinkString = "" + tr("Mupen64Plus Documentation") + ""; docsLink = new QLabel(docsLinkString, controls); docsLink->setOpenExternalLinks(true); QSpacerItem *spacer = new QSpacerItem(1,1, QSizePolicy::Expanding, QSizePolicy::Fixed); editorButtonBox = new QDialogButtonBox(Qt::Horizontal, controls); editorButtonBox->addButton(tr("Save"), QDialogButtonBox::AcceptRole); editorButtonBox->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); controlsLayout->addWidget(codesButtonBox, 0, 0); controlsLayout->addWidget(docsLink, 0, 1); controlsLayout->addItem(spacer, 0, 2); controlsLayout->addWidget(editorButtonBox, 0, 3); controls->setLayout(controlsLayout); editorLayout->addWidget(editorArea, 0, 0); editorLayout->addWidget(controls, 1, 0); connect(codesButtonBox, SIGNAL(helpRequested()), this, SLOT(openKeyCodes())); connect(editorButtonBox, SIGNAL(accepted()), this, SLOT(saveConfig())); connect(editorButtonBox, SIGNAL(rejected()), this, SLOT(close())); setLayout(editorLayout); } void ConfigEditor::openKeyCodes() { if (!keyCodes) { keyCodes = new KeyCodes(this); } keyCodes->show(); keyCodes->raise(); keyCodes->activateWindow(); } void ConfigEditor::saveConfig() { config.open(QIODevice::WriteOnly); config.write(editorArea->toPlainText().toUtf8().data()); config.close(); close(); } ConfigHighlighter::ConfigHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent) { Rule rule; headerFormat.setFontWeight(QFont::Bold); headerFormat.setForeground(Qt::darkMagenta); rule.pattern = QRegExp("\\[[A-Za-z0-9\\(\\)-\\W]*\\]"); rule.format = headerFormat; rules.append(rule); variableFormat.setForeground(Qt::black); rule.pattern = QRegExp("[A-Za-z0-9_- ]*="); rule.format = variableFormat; rules.append(rule); valueFormat.setForeground(Qt::blue); rule.pattern = QRegExp("=[^\\n]+"); rule.format = valueFormat; rules.append(rule); separatorFormat.setFontWeight(QFont::Bold); separatorFormat.setForeground(Qt::black); rule.pattern = QRegExp("="); rule.format = separatorFormat; rules.append(rule); quotationFormat.setForeground(Qt::darkGreen); rule.pattern = QRegExp("\".*\""); rule.format = quotationFormat; rules.append(rule); commentFormat.setForeground(Qt::gray); rule.pattern = QRegExp("#[^\\n]*"); rule.format = commentFormat; rules.append(rule); } void ConfigHighlighter::highlightBlock(const QString &text) { foreach (const Rule &rule, rules) { QRegExp expression(rule.pattern); int index = expression.indexIn(text); while (index >= 0) { int length = expression.matchedLength(); setFormat(index, length, rule.format); index = expression.indexIn(text, index + length); } } } mupen64plus-qt-1.15/src/dialogs/configeditor.h000066400000000000000000000054631413256700400213340ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef CONFIGEDITOR_H #define CONFIGEDITOR_H #include #include #include class QDialogButtonBox; class QGridLayout; class QLabel; class QTextCharFormat; class QTextEdit; class KeyCodes; class ConfigHighlighter : public QSyntaxHighlighter { Q_OBJECT public: ConfigHighlighter(QTextDocument *parent = 0); protected: void highlightBlock(const QString &text); private: struct Rule { QRegExp pattern; QTextCharFormat format; }; QVector rules; QTextCharFormat headerFormat; QTextCharFormat variableFormat; QTextCharFormat valueFormat; QTextCharFormat separatorFormat; QTextCharFormat quotationFormat; QTextCharFormat commentFormat; }; class ConfigEditor : public QDialog { Q_OBJECT public: explicit ConfigEditor(QString configFile, QWidget *parent = 0); private: QDialogButtonBox *codesButtonBox; QDialogButtonBox *editorButtonBox; QFile config; QGridLayout *controlsLayout; QGridLayout *editorLayout; QLabel *docsLink; QTextEdit *editorArea; QWidget *controls; ConfigHighlighter *highlighter; KeyCodes *keyCodes = nullptr; private slots: void openKeyCodes(); void saveConfig(); }; #endif // CONFIGEDITOR_H mupen64plus-qt-1.15/src/dialogs/controlinfodialog.cpp000066400000000000000000000204241413256700400227210ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "controlinfodialog.h" #include #include #include #include #include #include #include ControlInfo::ControlInfo(QWidget *parent) : QDialog(parent) { setWindowTitle(tr("Mupen64Plus Default Controls")); setMinimumSize(600, 600); controlInfoLayout = new QGridLayout(this); controlInfoLayout->setContentsMargins(5, 10, 5, 10); QString description = tr("These are the default controls for Mupen64Plus. ") + tr("They can be modified using Settings->Edit mupen64plus.cfg...") + "

" + tr("For more information see the Mupen64Plus Documentation. ") .replace("", "") .replace("", "") + tr("For setting up a gamepad controller see this page. ") .replace("", "") .replace("", "") + tr("Note that Mupen64Plus output can be viewed with Emulation->View Log.") + "
"; descriptionLabel = new QLabel(description, this); descriptionLabel->setOpenExternalLinks(true); descriptionLabel->setWordWrap(true); controlInfoTabs = new QTabWidget(this); //Controller Input Tab inputTab = new QWidget(controlInfoTabs); inputLayout = new QGridLayout(inputTab); inputLayout->setContentsMargins(5, 10, 5, 10); QList inputs; inputs << (QStringList() << "Control Stick" << "Arrow Keys") << (QStringList() << "C Up/Left/Down/Right " << "I, J, K, L") << (QStringList() << "DPad Up/Left/Down/Right " << "W, A, S, D") << (QStringList() << "Z Trigger" << "Z") << (QStringList() << "L Trigger" << "X") << (QStringList() << "R Trigger" << "C") << (QStringList() << "Start" << "Enter") << (QStringList() << "A Button" << "Left Shift") << (QStringList() << "B Button" << "Left Ctrl") << (QStringList() << "Select Mem Pak" << ",") << (QStringList() << "Select Rumblep Pak" << "."); inputTable = new QTableWidget(this); inputTable->setColumnCount(2); inputTable->setRowCount(inputs.length()); inputTable->setHorizontalHeaderLabels(QStringList() << tr("N64 Input") << tr("Key")); inputTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); inputTable->verticalHeader()->setVisible(false); inputTable->setEditTriggers(QAbstractItemView::NoEditTriggers); inputTable->setSelectionMode(QAbstractItemView::NoSelection); inputTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); for (int i = 0; i < inputs.length(); i++) { inputTable->setItem(i, 0, new QTableWidgetItem(inputs.at(i).at(0))); inputTable->setItem(i, 1, new QTableWidgetItem(inputs.at(i).at(1))); } keyboard = new QPixmap(":/images/keyboard.png"); keyboardImage = new QLabel(this); keyboardImage->setAlignment(Qt::AlignCenter); keyboardImage->setPixmap(*keyboard); inputLayout->addWidget(inputTable, 0, 0); inputLayout->addWidget(keyboardImage, 1, 0); inputTab->setLayout(inputLayout); //Hotkeys Tab hotkeysTab = new QWidget(controlInfoTabs); hotkeysLayout = new QGridLayout(hotkeysTab); hotkeysLayout->setContentsMargins(5, 10, 5, 10); QList hotkeys; hotkeys << (QStringList() << "Esc" << tr("Quit the emulator")) << (QStringList() << "0 through 9" << tr("Select virtual 'slot' for save/load state commands")) << (QStringList() << "F5" << tr("Save emulator state")) << (QStringList() << "F7" << tr("Load emulator state")) << (QStringList() << "F9" << tr("Reset emulator")) << (QStringList() << "F10" << tr("Slow down emulator by 5%")) << (QStringList() << "F11" << tr("Speed up emulator by 5%")) << (QStringList() << "F12" << tr("Take screenshot")) << (QStringList() << "Alt+Enter" << tr("Toggle between windowed and fullscreen")) << (QStringList() << "P" << tr("Pause on/off")) << (QStringList() << "M" << tr("Mute/unmute sound")) << (QStringList() << "G" << tr("Press \"Game Shark\" button (only if cheats are enabled)")) << (QStringList() << "/" << tr("Single frame advance while paused")) << (QStringList() << "F" << tr("Fast forward (playback at 250% normal speed while pressed)")) << (QStringList() << "[" << tr("Decrease volume")) << (QStringList() << "]" << tr("Increase volume")); hotkeysTable = new QTableWidget(this); hotkeysTable->setColumnCount(2); hotkeysTable->setRowCount(hotkeys.length()); hotkeysTable->setHorizontalHeaderLabels(QStringList() << tr("Key") << tr("Action")); hotkeysTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); hotkeysTable->horizontalHeader()->setStretchLastSection(true); hotkeysTable->verticalHeader()->setVisible(false); hotkeysTable->setEditTriggers(QAbstractItemView::NoEditTriggers); hotkeysTable->setSelectionMode(QAbstractItemView::NoSelection); for (int i = 0; i < hotkeys.length(); i++) { hotkeysTable->setItem(i, 0, new QTableWidgetItem(hotkeys.at(i).at(0))); hotkeysTable->setItem(i, 1, new QTableWidgetItem(hotkeys.at(i).at(1))); } hotkeysLayout->addWidget(hotkeysTable, 0, 0); hotkeysTab->setLayout(hotkeysLayout); controlInfoButtonBox = new QDialogButtonBox(Qt::Horizontal, this); controlInfoButtonBox->addButton(tr("Close"), QDialogButtonBox::RejectRole); connect(controlInfoButtonBox, SIGNAL(rejected()), this, SLOT(close())); connect(controlInfoTabs, SIGNAL(currentChanged(int)), this, SLOT(resizeImageSlot(int))); controlInfoTabs->addTab(inputTab, tr("Controller Input")); controlInfoTabs->addTab(hotkeysTab, tr("Hotkeys")); controlInfoLayout->addWidget(descriptionLabel, 0, 0); controlInfoLayout->addWidget(controlInfoTabs, 1, 0); controlInfoLayout->addWidget(controlInfoButtonBox, 2, 0); setLayout(controlInfoLayout); } void ControlInfo::resizeEvent(QResizeEvent*) { resizeImage(); } void ControlInfo::resizeImage() { int width = keyboardImage->width(); if (width > 300 && width < 910) keyboardImage->setPixmap(keyboard->scaledToWidth(width,Qt::SmoothTransformation)); else if (width >= 910) keyboardImage->setPixmap(keyboard->scaledToWidth(910,Qt::SmoothTransformation)); } void ControlInfo::resizeImageSlot(int) { resizeImage(); } mupen64plus-qt-1.15/src/dialogs/controlinfodialog.h000066400000000000000000000045431413256700400223720ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef CONTROLINFODIALOG_H #define CONTROLINFODIALOG_H #include class QDialogButtonBox; class QGridLayout; class QLabel; class QTableWidget; class QTabWidget; class ControlInfo : public QDialog { Q_OBJECT public: explicit ControlInfo(QWidget *parent = 0); protected: void resizeEvent(QResizeEvent *) override; private: QDialogButtonBox *controlInfoButtonBox; QGridLayout *controlInfoLayout; QGridLayout *inputLayout; QGridLayout *hotkeysLayout; QLabel *descriptionLabel; QLabel *keyboardImage; QPixmap *keyboard; QTableWidget *hotkeysTable; QTableWidget *inputTable; QTabWidget *controlInfoTabs; QWidget *inputTab; QWidget *hotkeysTab; void resizeImage(); private slots: void resizeImageSlot(int); }; #endif // CONTROLINFODIALOG_H mupen64plus-qt-1.15/src/dialogs/downloaddialog.cpp000066400000000000000000000070501413256700400221740ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "downloaddialog.h" #include "../common.h" #include "../roms/thegamesdbscraper.h" #include #include #include #include DownloadDialog::DownloadDialog(QString fileText, QString defaultText, QString romMD5, QWidget *parent) : QDialog(parent) { this->romMD5 = romMD5; this->parent = parent; setWindowTitle(tr("Search Game Information")); downloadLayout = new QGridLayout(this); fileLabel = new QLabel("" + tr("File") + ": " + fileText, this); gameNameLabel = new QLabel(tr("Name of Game:"), this); gameIDLabel = new QLabel(tr("or Game ID:"), this); defaultText.remove(QRegExp("\\W*(\\(|\\[).+(\\)|\\])\\W*")); gameNameField = new QLineEdit(defaultText, this); gameIDField = new QLineEdit(this); gameIDField->setToolTip(tr("From thegamesdb.net URL of game")); downloadButtonBox = new QDialogButtonBox(Qt::Horizontal, this); downloadButtonBox->addButton(tr("Search"), QDialogButtonBox::AcceptRole); downloadButtonBox->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); downloadLayout->addWidget(fileLabel, 0, 0, 1, 2); downloadLayout->addWidget(gameNameLabel, 1, 0); downloadLayout->addWidget(gameIDLabel, 2, 0); downloadLayout->addWidget(gameNameField, 1, 1); downloadLayout->addWidget(gameIDField, 2, 1); downloadLayout->addWidget(downloadButtonBox, 4, 0, 1, 3); downloadLayout->setRowStretch(3,1); downloadLayout->setColumnStretch(1,1); downloadLayout->setColumnMinimumWidth(1, 300); downloadLayout->setRowMinimumHeight(0, 20); downloadLayout->setRowMinimumHeight(3, 20); connect(downloadButtonBox, SIGNAL(accepted()), this, SLOT(runDownloader())); connect(downloadButtonBox, SIGNAL(rejected()), this, SLOT(close())); setLayout(downloadLayout); } void DownloadDialog::runDownloader() { close(); scraper = new TheGamesDBScraper(parent, true); scraper->downloadGameInfo(romMD5, gameNameField->text(), gameIDField->text()); } mupen64plus-qt-1.15/src/dialogs/downloaddialog.h000066400000000000000000000043561413256700400216470ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef DOWNLOADDIALOG_H #define DOWNLOADDIALOG_H #include class QDialogButtonBox; class QGridLayout; class QLabel; class QLineEdit; class TheGamesDBScraper; class DownloadDialog : public QDialog { Q_OBJECT public: explicit DownloadDialog(QString fileText, QString defaultText, QString romMD5, QWidget *parent = 0); private: QDialogButtonBox *downloadButtonBox; QGridLayout *downloadLayout; QLabel *fileLabel; QLabel *gameNameLabel; QLabel *gameIDLabel; QLineEdit *gameNameField; QLineEdit *gameIDField; QString romMD5; QWidget *parent; TheGamesDBScraper *scraper; private slots: void runDownloader(); }; #endif // DOWNLOADDIALOG_H mupen64plus-qt-1.15/src/dialogs/gamesettingsdialog.cpp000066400000000000000000000130501413256700400230540ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "gamesettingsdialog.h" #include "ui_gamesettingsdialog.h" #include "../global.h" #include GameSettingsDialog::GameSettingsDialog(QString fileName, QWidget *parent) : QDialog(parent), ui(new Ui::GameSettingsDialog) { this->fileName = fileName; ui->setupUi(this); QString labelText = ui->gameLabel->text(); labelText.append(" "+fileName+""); ui->gameLabel->setText(labelText); QStringList audioPlugins, inputPlugins, rspPlugins, videoPlugins; pluginsDir = QDir(SETTINGS.value("Paths/plugins", "").toString()); if (pluginsDir.exists()) { QStringList files = pluginsDir.entryList(QStringList() << "*", QDir::Files); if (files.size() > 0) { foreach (QString fileName, files) { QString baseName = QFileInfo(fileName).completeBaseName(); if (fileName.contains("-audio-")) audioPlugins << baseName; else if (fileName.contains("-input-")) inputPlugins << baseName; else if (fileName.contains("-rsp-")) rspPlugins << baseName; else if (fileName.contains("-video-")) videoPlugins << baseName; } } } ui->videoBox->insertItems(1, videoPlugins); ui->audioBox->insertItems(1, audioPlugins); ui->inputBox->insertItems(1, inputPlugins); ui->rspBox->insertItems(1, rspPlugins); int videoIndex = videoPlugins.indexOf(SETTINGS.value(fileName+"/video","").toString()); int audioIndex = audioPlugins.indexOf(SETTINGS.value(fileName+"/audio","").toString()); int inputIndex = inputPlugins.indexOf(SETTINGS.value(fileName+"/input","").toString()); int rspIndex = rspPlugins.indexOf(SETTINGS.value(fileName+"/rsp","").toString()); if (videoIndex >= 0) ui->videoBox->setCurrentIndex(videoIndex + 1); if (audioIndex >= 0) ui->audioBox->setCurrentIndex(audioIndex + 1); if (inputIndex >= 0) ui->inputBox->setCurrentIndex(inputIndex + 1); if (rspIndex >= 0) ui->rspBox->setCurrentIndex(rspIndex + 1); ui->configPath->setText(SETTINGS.value(fileName+"/config", "").toString()); ui->parameters->setText(SETTINGS.value(fileName+"/parameters", "").toString()); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK")); ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); connect(ui->configButton, SIGNAL(clicked()), this, SLOT(browseConfig())); connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(editGameSettings())); connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } GameSettingsDialog::~GameSettingsDialog() { delete ui; } void GameSettingsDialog::browseConfig() { QString path = QFileDialog::getExistingDirectory(this, tr("Config Directory")); if (path != "") ui->configPath->setText(path); } void GameSettingsDialog::editGameSettings() { if (ui->videoBox->currentIndex() > 0) SETTINGS.setValue(fileName+"/video", ui->videoBox->currentText()); else SETTINGS.setValue(fileName+"/video", ""); if (ui->audioBox->currentIndex() > 0) SETTINGS.setValue(fileName+"/audio", ui->audioBox->currentText()); else SETTINGS.setValue(fileName+"/audio", ""); if (ui->inputBox->currentIndex() > 0) SETTINGS.setValue(fileName+"/input", ui->inputBox->currentText()); else SETTINGS.setValue(fileName+"/input", ""); if (ui->rspBox->currentIndex() > 0) SETTINGS.setValue(fileName+"/rsp", ui->rspBox->currentText()); else SETTINGS.setValue(fileName+"/rsp", ""); SETTINGS.setValue(fileName+"/config", ui->configPath->text()); SETTINGS.setValue(fileName+"/parameters", ui->parameters->text()); // Clean up game settings if they are empty bool emptyCheck = true; foreach (QString key, SETTINGS.allKeys()) if (key.startsWith(fileName) && SETTINGS.value(key, "") != "") emptyCheck = false; if (emptyCheck) SETTINGS.remove(fileName); } mupen64plus-qt-1.15/src/dialogs/gamesettingsdialog.h000066400000000000000000000040711413256700400225240ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef GAMESETTINGSDIALOG_H #define GAMESETTINGSDIALOG_H #include #include namespace Ui { class GameSettingsDialog; } class GameSettingsDialog : public QDialog { Q_OBJECT public: explicit GameSettingsDialog(QString fileName, QWidget *parent = 0); ~GameSettingsDialog(); private: Ui::GameSettingsDialog *ui; QDir pluginsDir; QString fileName; QWidget *parent; private slots: void browseConfig(); void editGameSettings(); }; #endif // GAMESETTINGSDIALOG_H mupen64plus-qt-1.15/src/dialogs/gamesettingsdialog.ui000066400000000000000000000137121413256700400227140ustar00rootroot00000000000000 GameSettingsDialog 0 0 450 450 450 450 Game Settings Game settings for: These settings will override any global settings. If there's a setting you want to modify for this game, but it doesn't appear here, add the parameter for it to "Other Parameters." true Plugins false false Audio Plugin: Input Plugin: RSP Plugin: Video Plugin: default default default default Additional Settings Config Directory: Other Parameters: Browse... Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() GameSettingsDialog accept() 248 254 157 274 buttonBox rejected() GameSettingsDialog reject() 316 260 286 274 mupen64plus-qt-1.15/src/dialogs/keycodesdialog.cpp000066400000000000000000000224041413256700400221730ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "keycodesdialog.h" #include #include #include #include #include KeyCodes::KeyCodes(QWidget *parent) : QDialog(parent) { setWindowTitle(tr("SDL Key Codes")); setMinimumSize(300, 400); keyCodesLayout = new QGridLayout(this); keyCodesLayout->setContentsMargins(5, 10, 5, 10); QList codes; codes << (QStringList() << "Backspace" << "8") << (QStringList() << "Tab" << "9") << (QStringList() << "Clear" << "12") << (QStringList() << "Enter" << "13") << (QStringList() << "Pause" << "19") << (QStringList() << "Escape" << "27") << (QStringList() << "Space" << "32") << (QStringList() << "!" << "33") << (QStringList() << "\"" << "34") << (QStringList() << "#" << "35") << (QStringList() << "$" << "36") << (QStringList() << "&" << "38") << (QStringList() << "'" << "39") << (QStringList() << "(" << "40") << (QStringList() << ")" << "41") << (QStringList() << "*" << "42") << (QStringList() << "+" << "43") << (QStringList() << "," << "44") << (QStringList() << "-" << "45") << (QStringList() << "." << "46") << (QStringList() << "/" << "47") << (QStringList() << "0" << "48") << (QStringList() << "1" << "49") << (QStringList() << "2" << "50") << (QStringList() << "3" << "51") << (QStringList() << "4" << "52") << (QStringList() << "5" << "53") << (QStringList() << "6" << "54") << (QStringList() << "7" << "55") << (QStringList() << "8" << "56") << (QStringList() << "9" << "57") << (QStringList() << ":" << "58") << (QStringList() << ";" << "59") << (QStringList() << "<" << "60") << (QStringList() << "=" << "61") << (QStringList() << ">" << "62") << (QStringList() << "?" << "63") << (QStringList() << "@" << "64") << (QStringList() << "[" << "91") << (QStringList() << "\\" << "92") << (QStringList() << "]" << "93") << (QStringList() << "^" << "94") << (QStringList() << "_" << "95") << (QStringList() << "`" << "96") << (QStringList() << "a" << "97") << (QStringList() << "b" << "98") << (QStringList() << "c" << "99") << (QStringList() << "d" << "100") << (QStringList() << "e" << "101") << (QStringList() << "f" << "102") << (QStringList() << "g" << "103") << (QStringList() << "h" << "104") << (QStringList() << "i" << "105") << (QStringList() << "j" << "106") << (QStringList() << "k" << "107") << (QStringList() << "l" << "108") << (QStringList() << "m" << "109") << (QStringList() << "n" << "110") << (QStringList() << "o" << "111") << (QStringList() << "p" << "112") << (QStringList() << "q" << "113") << (QStringList() << "r" << "114") << (QStringList() << "s" << "115") << (QStringList() << "t" << "116") << (QStringList() << "u" << "117") << (QStringList() << "v" << "118") << (QStringList() << "w" << "119") << (QStringList() << "x" << "120") << (QStringList() << "y" << "121") << (QStringList() << "z" << "122") << (QStringList() << "Delete" << "127") << (QStringList() << "Keypad 0" << "256") << (QStringList() << "Keypad 1" << "257") << (QStringList() << "Keypad 2" << "258") << (QStringList() << "Keypad 3" << "259") << (QStringList() << "Keypad 4" << "260") << (QStringList() << "Keypad 5" << "261") << (QStringList() << "Keypad 6" << "262") << (QStringList() << "Keypad 7" << "263") << (QStringList() << "Keypad 8" << "264") << (QStringList() << "Keypad 9" << "265") << (QStringList() << "Keypad ." << "266") << (QStringList() << "Keypad /" << "267") << (QStringList() << "Keypad *" << "268") << (QStringList() << "Keypad -" << "269") << (QStringList() << "Keypad +" << "270") << (QStringList() << "Keypad Enter" << "271") << (QStringList() << "Keypad =" << "272") << (QStringList() << "Up" << "273") << (QStringList() << "Down" << "274") << (QStringList() << "Right" << "275") << (QStringList() << "Left" << "276") << (QStringList() << "Insert" << "277") << (QStringList() << "Home" << "278") << (QStringList() << "End" << "279") << (QStringList() << "Page Up" << "280") << (QStringList() << "Page Down" << "281") << (QStringList() << "F1" << "282") << (QStringList() << "F2" << "283") << (QStringList() << "F3" << "284") << (QStringList() << "F4" << "285") << (QStringList() << "F5" << "286") << (QStringList() << "F6" << "287") << (QStringList() << "F7" << "288") << (QStringList() << "F8" << "289") << (QStringList() << "F9" << "290") << (QStringList() << "F10" << "291") << (QStringList() << "F11" << "292") << (QStringList() << "F12" << "293") << (QStringList() << "F13" << "294") << (QStringList() << "F14" << "295") << (QStringList() << "F15" << "296") << (QStringList() << "Num Lock" << "300") << (QStringList() << "Caps Lock" << "301") << (QStringList() << "Scroll Lock" << "302") << (QStringList() << "Right Shift" << "303") << (QStringList() << "Left Shift" << "304") << (QStringList() << "Right Ctrl" << "305") << (QStringList() << "Left Ctrl" << "306") << (QStringList() << "Right Alt" << "307") << (QStringList() << "Left Alt" << "308") << (QStringList() << "Right Meta" << "309") << (QStringList() << "Left Meta" << "310") << (QStringList() << "Left Super" << "311") << (QStringList() << "Right Super" << "312") << (QStringList() << "Mode" << "313") << (QStringList() << "Compose" << "314") << (QStringList() << "Help" << "315") << (QStringList() << "Print" << "316") << (QStringList() << "SysRq" << "317") << (QStringList() << "Break" << "318") << (QStringList() << "Menu" << "319") << (QStringList() << "Power" << "320") << (QStringList() << "Euro" << "321") << (QStringList() << "Undo" << "322"); keyCodesTable = new QTableWidget(this); keyCodesTable->setColumnCount(2); keyCodesTable->setRowCount(codes.length()); keyCodesTable->setHorizontalHeaderLabels(QStringList() << tr("Key") << tr("SDL Code")); keyCodesTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); keyCodesTable->verticalHeader()->setVisible(false); keyCodesTable->setEditTriggers(QAbstractItemView::NoEditTriggers); keyCodesTable->setSelectionMode(QAbstractItemView::SingleSelection); for (int i = 0; i < codes.length(); i++) { keyCodesTable->setItem(i, 0, new QTableWidgetItem(codes.at(i).at(0))); keyCodesTable->setItem(i, 1, new QTableWidgetItem(codes.at(i).at(1))); } keyCodesButtonBox = new QDialogButtonBox(Qt::Horizontal, this); keyCodesButtonBox->addButton(tr("Close"), QDialogButtonBox::RejectRole); keyCodesLayout->addWidget(keyCodesTable, 0, 0); keyCodesLayout->addWidget(keyCodesButtonBox, 1, 0); connect(keyCodesButtonBox, SIGNAL(rejected()), this, SLOT(close())); setLayout(keyCodesLayout); } mupen64plus-qt-1.15/src/dialogs/keycodesdialog.h000066400000000000000000000036571413256700400216510ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef KEYCODESDIALOG_H #define KEYCODESDIALOG_H #include class QDialogButtonBox; class QGridLayout; class QTableWidget; class KeyCodes : public QDialog { Q_OBJECT public: explicit KeyCodes(QWidget *parent = 0); private: QDialogButtonBox *keyCodesButtonBox; QGridLayout *keyCodesLayout; QTableWidget *keyCodesTable; }; #endif // KEYCODESDIALOG_H mupen64plus-qt-1.15/src/dialogs/logdialog.cpp000066400000000000000000000047731413256700400211570ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "logdialog.h" #include "../global.h" #include #include #include #include LogDialog::LogDialog(QString lastOutput, QWidget *parent) : QDialog(parent) { setWindowTitle(tr(" Log").replace("",ParentName)); setMinimumSize(600, 400); logLayout = new QGridLayout(this); logLayout->setContentsMargins(5, 10, 5, 10); logArea = new QTextEdit(this); logArea->setWordWrapMode(QTextOption::NoWrap); QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); logArea->setFont(font); logArea->setPlainText(lastOutput); logButtonBox = new QDialogButtonBox(Qt::Horizontal, this); logButtonBox->addButton(tr("Close"), QDialogButtonBox::AcceptRole); logLayout->addWidget(logArea, 0, 0); logLayout->addWidget(logButtonBox, 1, 0); connect(logButtonBox, SIGNAL(accepted()), this, SLOT(close())); setLayout(logLayout); } mupen64plus-qt-1.15/src/dialogs/logdialog.h000066400000000000000000000036401413256700400206140ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef LOGDIALOG_H #define LOGDIALOG_H #include class QDialogButtonBox; class QGridLayout; class QTextEdit; class LogDialog : public QDialog { Q_OBJECT public: explicit LogDialog(QString lastOutput, QWidget *parent = 0); private: QDialogButtonBox *logButtonBox; QGridLayout *logLayout; QTextEdit *logArea; }; #endif // LOGDIALOG_H mupen64plus-qt-1.15/src/dialogs/settingsdialog.cpp000066400000000000000000000776571413256700400222510ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "settingsdialog.h" #include "ui_settingsdialog.h" #include "../global.h" #include "../common.h" #include #include #include #include SettingsDialog::SettingsDialog(QWidget *parent, int activeTab) : QDialog(parent), ui(new Ui::SettingsDialog) { ui->setupUi(this); ui->tabWidget->setCurrentIndex(activeTab); //Populate Paths tab ui->emulatorPath->setText(SETTINGS.value("Paths/mupen64plus", "").toString()); ui->pluginPath->setText(SETTINGS.value("Paths/plugins", "").toString()); ui->dataPath->setText(SETTINGS.value("Paths/data", "").toString()); ui->configPath->setText(SETTINGS.value("Paths/config", "").toString()); QStringList romDirectories = SETTINGS.value("Paths/roms", "").toString().split("|"); romDirectories.removeAll(""); foreach (QString directory, romDirectories) ui->romList->addItem(directory); connect(ui->emulatorButton, SIGNAL(clicked()), this, SLOT(browseEmulator())); connect(ui->pluginButton, SIGNAL(clicked()), this, SLOT(browsePlugin())); connect(ui->dataButton, SIGNAL(clicked()), this, SLOT(browseData())); connect(ui->configButton, SIGNAL(clicked()), this, SLOT(browseConfig())); connect(ui->romAddButton, SIGNAL(clicked()), this, SLOT(addRomDirectory())); connect(ui->romRemoveButton, SIGNAL(clicked()), this, SLOT(removeRomDirectory())); //Populate Emulation tab QString emuMode = SETTINGS.value("Emulation/mode", "").toString(); if (emuMode == "0") ui->pureButton->setChecked(true); else if (emuMode == "1") ui->cachedButton->setChecked(true); else ui->dynamicButton->setChecked(true); //Populate Graphics tab if (SETTINGS.value("Graphics/osd", "true").toString() == "true") ui->osdOption->setChecked(true); if (SETTINGS.value("Graphics/fullscreen", "").toString() == "true") ui->fullscreenOption->setChecked(true); QStringList useableModes, modes; useableModes << tr("default"); //Allow users to use the screen resolution set in the config file modes << "2560x1600" << "2560x1440" << "2048x1152" << "1920x1200" << "1920x1080" << "1680x1050" << "1600x1200" << "1600x900" << "1440x900" << "1400x1050" << "1366x768" << "1360x768" << "1280x1024" << "1280x960" << "1280x800" << "1280x768" << "1280x720" << "1152x864" << "1024x768" << "1024x600" << "800x600" << "640x480"; desktop = new QDesktopWidget; int screenWidth = desktop->width(); int screenHeight = desktop->height(); foreach (QString mode, modes) { QStringList values = mode.split("x"); if (values.value(0).toInt() <= screenWidth && values.value(1).toInt() <= screenHeight) useableModes << mode; } ui->resolutionBox->insertItems(0, useableModes); int resIndex = useableModes.indexOf(SETTINGS.value("Graphics/resolution","").toString()); if (resIndex >= 0) ui->resolutionBox->setCurrentIndex(resIndex); //Populate Plugins tab QStringList audioPlugins, inputPlugins, rspPlugins, videoPlugins; pluginsDir = QDir(SETTINGS.value("Paths/plugins", "").toString()); if (pluginsDir.exists()) { QStringList files = pluginsDir.entryList(QStringList() << "*", QDir::Files); if (files.size() > 0) { foreach (QString fileName, files) { QString baseName = QFileInfo(fileName).completeBaseName(); if (fileName.contains("-audio-")) audioPlugins << baseName; else if (fileName.contains("-input-")) inputPlugins << baseName; else if (fileName.contains("-rsp-")) rspPlugins << baseName; else if (fileName.contains("-video-")) videoPlugins << baseName; } } } ui->videoBox->insertItems(0, videoPlugins); ui->audioBox->insertItems(0, audioPlugins); ui->inputBox->insertItems(0, inputPlugins); ui->rspBox->insertItems(0, rspPlugins); //Set Rice as default QString videoDefault = ""; if (videoPlugins.contains("mupen64plus-video-rice")) videoDefault = "mupen64plus-video-rice"; int videoIndex = videoPlugins.indexOf(SETTINGS.value("Plugins/video",videoDefault).toString()); int audioIndex = audioPlugins.indexOf(SETTINGS.value("Plugins/audio","").toString()); int inputIndex = inputPlugins.indexOf(SETTINGS.value("Plugins/input","").toString()); int rspIndex = rspPlugins.indexOf(SETTINGS.value("Plugins/rsp","").toString()); if (videoIndex >= 0) ui->videoBox->setCurrentIndex(videoIndex); if (audioIndex >= 0) ui->audioBox->setCurrentIndex(audioIndex); if (inputIndex >= 0) ui->inputBox->setCurrentIndex(inputIndex); if (rspIndex >= 0) ui->rspBox->setCurrentIndex(rspIndex); //Populate Table tab int tableSizeIndex = 0; QString currentTableSize = SETTINGS.value("Table/imagesize","Medium").toString(); QList sizes; sizes << (QStringList() << tr("Extra Small") << "Extra Small") << (QStringList() << tr("Small") << "Small") << (QStringList() << tr("Medium") << "Medium") << (QStringList() << tr("Large") << "Large") << (QStringList() << tr("Extra Large") << "Extra Large") << (QStringList() << tr("Super") << "Super"); if (SETTINGS.value("Other/downloadinfo", "").toString() == "true") populateTableAndListTab(true); else populateTableAndListTab(false); if (SETTINGS.value("Table/stretchfirstcolumn", "true").toString() == "true") ui->tableStretchOption->setChecked(true); for (int i = 0; i < sizes.length(); i++) { ui->tableSizeBox->insertItem(i, sizes.at(i).at(0), sizes.at(i).at(1)); if (currentTableSize == sizes.at(i).at(1)) tableSizeIndex = i; } if (tableSizeIndex >= 0) ui->tableSizeBox->setCurrentIndex(tableSizeIndex); connect(ui->tableAddButton, SIGNAL(clicked()), this, SLOT(tableAddColumn())); connect(ui->tableRemoveButton, SIGNAL(clicked()), this, SLOT(tableRemoveColumn())); connect(ui->tableSortUpButton, SIGNAL(clicked()), this, SLOT(tableSortUp())); connect(ui->tableSortDownButton, SIGNAL(clicked()), this, SLOT(tableSortDown())); //Populate Grid tab int gridSizeIndex = 0, activeIndex = 0, inactiveIndex = 0, labelColorIndex = 0, bgThemeIndex = 0; QString currentGridSize = SETTINGS.value("Grid/imagesize","Medium").toString(); QString currentActiveColor = SETTINGS.value("Grid/activecolor","Cyan").toString(); QString currentInactiveColor = SETTINGS.value("Grid/inactivecolor","Black").toString(); QString currentLabelColor = SETTINGS.value("Grid/labelcolor","White").toString(); QString currentBGTheme = SETTINGS.value("Grid/theme","Normal").toString(); QList colors; colors << (QStringList() << tr("Black") << "Black") << (QStringList() << tr("White") << "White") << (QStringList() << tr("Light Gray") << "Light Gray") << (QStringList() << tr("Dark Gray") << "Dark Gray") << (QStringList() << tr("Green") << "Green") << (QStringList() << tr("Cyan") << "Cyan") << (QStringList() << tr("Blue") << "Blue") << (QStringList() << tr("Purple") << "Purple") << (QStringList() << tr("Red") << "Red") << (QStringList() << tr("Pink") << "Pink") << (QStringList() << tr("Orange") << "Orange") << (QStringList() << tr("Yellow") << "Yellow") << (QStringList() << tr("Brown") << "Brown"); QList bgThemes; bgThemes << (QStringList() << tr("Light") << "Light") << (QStringList() << tr("Normal") << "Normal") << (QStringList() << tr("Dark") << "Dark"); for (int i = 0; i < sizes.length(); i++) { ui->gridSizeBox->insertItem(i, sizes.at(i).at(0), sizes.at(i).at(1)); if (currentGridSize == sizes.at(i).at(1)) gridSizeIndex = i; } if (gridSizeIndex >= 0) ui->gridSizeBox->setCurrentIndex(gridSizeIndex); int gridColumnCount = SETTINGS.value("Grid/columncount","4").toInt(); ui->columnCountBox->setValue(gridColumnCount); if (SETTINGS.value("Grid/autocolumns", "true").toString() == "true") { toggleGridColumn(true); ui->autoColumnOption->setChecked(true); } else toggleGridColumn(false); for (int i = 0; i < colors.length(); i++) { ui->shadowActiveBox->insertItem(i, colors.at(i).at(0), colors.at(i).at(1)); if (currentActiveColor == colors.at(i).at(1)) activeIndex = i; ui->shadowInactiveBox->insertItem(i, colors.at(i).at(0), colors.at(i).at(1)); if (currentInactiveColor == colors.at(i).at(1)) inactiveIndex = i; ui->labelColorBox->insertItem(i, colors.at(i).at(0), colors.at(i).at(1)); if (currentLabelColor == colors.at(i).at(1)) labelColorIndex = i; } if (activeIndex >= 0) ui->shadowActiveBox->setCurrentIndex(activeIndex); if (inactiveIndex >= 0) ui->shadowInactiveBox->setCurrentIndex(inactiveIndex); if (labelColorIndex >= 0) ui->labelColorBox->setCurrentIndex(labelColorIndex); //Widgets to enable when label active labelEnable << ui->labelTextLabel << ui->labelTextBox << ui->labelColorLabel << ui->labelColorBox; if (SETTINGS.value("Grid/label", "true").toString() == "true") { toggleLabel(true); ui->labelOption->setChecked(true); } else toggleLabel(false); for (int i = 0; i < bgThemes.length(); i++) { ui->bgThemeBox->insertItem(i, bgThemes.at(i).at(0), bgThemes.at(i).at(1)); if (currentBGTheme == bgThemes.at(i).at(1)) bgThemeIndex = i; } if (bgThemeIndex >= 0) ui->bgThemeBox->setCurrentIndex(bgThemeIndex); QString imagePath = SETTINGS.value("Grid/background", "").toString(); ui->backgroundPath->setText(imagePath); hideBGTheme(imagePath); if (SETTINGS.value("Grid/sortdirection", "ascending").toString() == "descending") ui->gridDescendingOption->setChecked(true); connect(ui->autoColumnOption, SIGNAL(toggled(bool)), this, SLOT(toggleGridColumn(bool))); connect(ui->backgroundPath, SIGNAL(textChanged(QString)), this, SLOT(hideBGTheme(QString))); connect(ui->backgroundButton, SIGNAL(clicked()), this, SLOT(browseBackground())); connect(ui->labelOption, SIGNAL(toggled(bool)), this, SLOT(toggleLabel(bool))); //Populate List tab int listSizeIndex = 0, listTextIndex = 0, listThemeIndex = 0; QString currentListSize = SETTINGS.value("List/imagesize","Medium").toString(); QString currentListText = SETTINGS.value("List/textsize","Medium").toString(); QString currentListTheme = SETTINGS.value("List/theme","Light").toString(); QList themes; themes << (QStringList() << tr("Light") << "Light") << (QStringList() << tr("Dark") << "Dark"); listCoverEnable << ui->listSizeLabel << ui->listSizeBox; if (SETTINGS.value("List/displaycover", "").toString() == "true") { toggleListCover(true); ui->listCoverOption->setChecked(true); } else toggleListCover(false); if (SETTINGS.value("List/firstitemheader", "true").toString() == "true") ui->listHeaderOption->setChecked(true); for (int i = 0; i < sizes.length(); i++) { ui->listSizeBox->insertItem(i, sizes.at(i).at(0), sizes.at(i).at(1)); if (currentListSize == sizes.at(i).at(1)) listSizeIndex = i; ui->listTextBox->insertItem(i, sizes.at(i).at(0), sizes.at(i).at(1)); if (currentListText == sizes.at(i).at(1)) listTextIndex = i; } if (listSizeIndex >= 0) ui->listSizeBox->setCurrentIndex(listSizeIndex); if (listTextIndex >= 0) ui->listTextBox->setCurrentIndex(listTextIndex); for (int i = 0; i < themes.length(); i++) { ui->listThemeBox->insertItem(i, themes.at(i).at(0), themes.at(i).at(1)); if (currentListTheme == themes.at(i).at(1)) listThemeIndex = i; } if (listThemeIndex >= 0) ui->listThemeBox->setCurrentIndex(listThemeIndex); if (SETTINGS.value("List/sortdirection", "ascending").toString() == "descending") ui->listDescendingOption->setChecked(true); connect(ui->listCoverOption, SIGNAL(toggled(bool)), this, SLOT(toggleListCover(bool))); connect(ui->listAddButton, SIGNAL(clicked()), this, SLOT(listAddColumn())); connect(ui->listRemoveButton, SIGNAL(clicked()), this, SLOT(listRemoveColumn())); connect(ui->listSortUpButton, SIGNAL(clicked()), this, SLOT(listSortUp())); connect(ui->listSortDownButton, SIGNAL(clicked()), this, SLOT(listSortDown())); //Populate Other tab int languageIndex = 0; QString currentLanguage = SETTINGS.value("language", getDefaultLanguage()).toString(); QList languages; languages << (QStringList() << QString::fromUtf8("English") << "EN") << (QStringList() << QString::fromUtf8("Français") << "FR") << (QStringList() << QString::fromUtf8("Русский") << "RU"); downloadEnable << ui->tableSizeLabel << ui->tableSizeBox << ui->listCoverOption << ui->listSizeLabel << ui->listSizeBox; if (SETTINGS.value("Other/downloadinfo", "").toString() == "true") { toggleDownload(true); ui->downloadOption->setChecked(true); } else toggleDownload(false); if (SETTINGS.value("saveoptions", "").toString() == "true") ui->saveOption->setChecked(true); ui->parametersLine->setText(SETTINGS.value("Other/parameters", "").toString()); for (int i = 0; i < languages.length(); i++) { ui->languageBox->insertItem(i, languages.at(i).at(0), languages.at(i).at(1)); if (currentLanguage == languages.at(i).at(1)) languageIndex = i; } ui->languageBox->setCurrentIndex(languageIndex); ui->languageInfoLabel->setHidden(true); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK")); ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); connect(ui->downloadOption, SIGNAL(toggled(bool)), this, SLOT(toggleDownload(bool))); connect(ui->downloadOption, SIGNAL(toggled(bool)), this, SLOT(populateTableAndListTab(bool))); connect(ui->languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateLanguageInfo())); connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(editSettings())); connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } SettingsDialog::~SettingsDialog() { delete ui; } void SettingsDialog::addColumn(QListWidget *currentList, QListWidget *availableList) { int row = availableList->currentRow(); if (row >= 0) currentList->addItem(availableList->takeItem(row)); } void SettingsDialog::addRomDirectory() { QString path = QFileDialog::getExistingDirectory(this, tr("ROM Directory")); if (path != "") { //check for duplicates bool found = false; foreach (QListWidgetItem *item, ui->romList->findItems("*", Qt::MatchWildcard)) if (path == item->text()) found = true; if (!found) ui->romList->addItem(path); } } void SettingsDialog::browseBackground() { QString path = QFileDialog::getOpenFileName(this, tr("Background Image")); if (path != "") ui->backgroundPath->setText(path); } void SettingsDialog::browseEmulator() { QString path = QFileDialog::getOpenFileName(this, tr(" Executable") .replace("",ParentName)); if (path != "") ui->emulatorPath->setText(path); #ifdef Q_OS_OSX //Allow OSX users to just select the .app directory and auto-populate for them if (path.right(15) == "mupen64plus.app") { ui->emulatorPath->setText(path+"/Contents/MacOS/mupen64plus"); ui->pluginPath->setText(path+"/Contents/MacOS"); ui->dataPath->setText(path+"/Contents/Resources"); } #endif } void SettingsDialog::browsePlugin() { QString path = QFileDialog::getExistingDirectory(this, tr("Plugin Directory")); if (path != "") ui->pluginPath->setText(path); } void SettingsDialog::browseData() { QString path = QFileDialog::getExistingDirectory(this, tr("Data Directory")); if (path != "") ui->dataPath->setText(path); } void SettingsDialog::browseConfig() { QString path = QFileDialog::getExistingDirectory(this, tr("Config Directory")); if (path != "") ui->configPath->setText(path); } void SettingsDialog::editSettings() { //Set download option first if (ui->downloadOption->isChecked()) { SETTINGS.setValue("Other/downloadinfo", true); populateAvailable(true); //This removes thegamesdb.net options if user unselects download } else { SETTINGS.setValue("Other/downloadinfo", ""); populateAvailable(false); } //Paths tab SETTINGS.setValue("Paths/mupen64plus", ui->emulatorPath->text()); SETTINGS.setValue("Paths/plugins", ui->pluginPath->text()); SETTINGS.setValue("Paths/data", ui->dataPath->text()); SETTINGS.setValue("Paths/config", ui->configPath->text()); QStringList romDirectories; for (int i = 0; i < ui->romList->count(); i++) romDirectories << ui->romList->item(i)->text(); SETTINGS.setValue("Paths/roms", romDirectories.join("|")); //Emulation tab if (ui->pureButton->isChecked()) SETTINGS.setValue("Emulation/mode", "0"); else if (ui->cachedButton->isChecked()) SETTINGS.setValue("Emulation/mode", "1"); else SETTINGS.setValue("Emulation/mode", "2"); //Graphics tab if (ui->osdOption->isChecked()) SETTINGS.setValue("Graphics/osd", true); else SETTINGS.setValue("Graphics/osd", ""); if (ui->fullscreenOption->isChecked()) SETTINGS.setValue("Graphics/fullscreen", true); else SETTINGS.setValue("Graphics/fullscreen", ""); if (ui->resolutionBox->currentText() != "default") SETTINGS.setValue("Graphics/resolution", ui->resolutionBox->currentText()); else SETTINGS.setValue("Graphics/resolution", ""); //Plugins tab SETTINGS.setValue("Plugins/video", ui->videoBox->currentText()); SETTINGS.setValue("Plugins/audio", ui->audioBox->currentText()); SETTINGS.setValue("Plugins/input", ui->inputBox->currentText()); SETTINGS.setValue("Plugins/rsp", ui->rspBox->currentText()); //Table tab QStringList tableVisibleItems; foreach (QListWidgetItem *item, ui->tableCurrentList->findItems("*", Qt::MatchWildcard)) if (available.contains(item->data(Qt::UserRole).toString())) tableVisibleItems << item->data(Qt::UserRole).toString(); SETTINGS.setValue("Table/columns", tableVisibleItems.join("|")); if (ui->tableStretchOption->isChecked()) SETTINGS.setValue("Table/stretchfirstcolumn", true); else SETTINGS.setValue("Table/stretchfirstcolumn", ""); SETTINGS.setValue("Table/imagesize", ui->tableSizeBox->itemData(ui->tableSizeBox->currentIndex())); //Grid tab SETTINGS.setValue("Grid/imagesize", ui->gridSizeBox->itemData(ui->gridSizeBox->currentIndex())); SETTINGS.setValue("Grid/columncount", ui->columnCountBox->value()); if (ui->autoColumnOption->isChecked()) SETTINGS.setValue("Grid/autocolumns", true); else SETTINGS.setValue("Grid/autocolumns", ""); SETTINGS.setValue("Grid/inactivecolor", ui->shadowInactiveBox->itemData(ui->shadowInactiveBox->currentIndex())); SETTINGS.setValue("Grid/activecolor", ui->shadowActiveBox->itemData(ui->shadowActiveBox->currentIndex())); SETTINGS.setValue("Grid/theme", ui->bgThemeBox->itemData(ui->bgThemeBox->currentIndex())); SETTINGS.setValue("Grid/background", ui->backgroundPath->text()); if (ui->labelOption->isChecked()) SETTINGS.setValue("Grid/label", true); else SETTINGS.setValue("Grid/label", ""); SETTINGS.setValue("Grid/labeltext", ui->labelTextBox->itemData(ui->labelTextBox->currentIndex())); SETTINGS.setValue("Grid/labelcolor", ui->labelColorBox->itemData(ui->labelColorBox->currentIndex())); SETTINGS.setValue("Grid/sort", ui->gridSortBox->itemData(ui->gridSortBox->currentIndex())); if (ui->gridDescendingOption->isChecked()) SETTINGS.setValue("Grid/sortdirection", "descending"); else SETTINGS.setValue("Grid/sortdirection", "ascending"); //List tab QStringList listVisibleItems; foreach (QListWidgetItem *item, ui->listCurrentList->findItems("*", Qt::MatchWildcard)) if (available.contains(item->data(Qt::UserRole).toString())) listVisibleItems << item->data(Qt::UserRole).toString(); SETTINGS.setValue("List/columns", listVisibleItems.join("|")); if (ui->listHeaderOption->isChecked()) SETTINGS.setValue("List/firstitemheader", true); else SETTINGS.setValue("List/firstitemheader", ""); if (ui->listCoverOption->isChecked() && ui->downloadOption->isChecked()) SETTINGS.setValue("List/displaycover", true); else SETTINGS.setValue("List/displaycover", ""); SETTINGS.setValue("List/imagesize", ui->listSizeBox->itemData(ui->listSizeBox->currentIndex())); SETTINGS.setValue("List/textsize", ui->listTextBox->itemData(ui->listTextBox->currentIndex())); SETTINGS.setValue("List/theme", ui->listThemeBox->itemData(ui->listThemeBox->currentIndex())); SETTINGS.setValue("List/sort", ui->listSortBox->itemData(ui->listSortBox->currentIndex())); if (ui->listDescendingOption->isChecked()) SETTINGS.setValue("List/sortdirection", "descending"); else SETTINGS.setValue("List/sortdirection", "ascending"); //Other tab if (ui->saveOption->isChecked()) SETTINGS.setValue("saveoptions", true); else SETTINGS.setValue("saveoptions", ""); SETTINGS.setValue("Other/parameters", ui->parametersLine->text()); SETTINGS.setValue("language", ui->languageBox->itemData(ui->languageBox->currentIndex())); close(); } void SettingsDialog::hideBGTheme(QString imagePath) { if (imagePath == "") { ui->bgThemeLabel->setEnabled(true); ui->bgThemeBox->setEnabled(true); } else { ui->bgThemeLabel->setEnabled(false); ui->bgThemeBox->setEnabled(false); } } void SettingsDialog::listAddColumn() { addColumn(ui->listCurrentList, ui->listAvailableList); } void SettingsDialog::listRemoveColumn() { removeColumn(ui->listCurrentList, ui->listAvailableList); } void SettingsDialog::listSortDown() { sortDown(ui->listCurrentList); } void SettingsDialog::listSortUp() { sortUp(ui->listCurrentList); } void SettingsDialog::populateAvailable(bool downloadItems) { available.clear(); labelOptions.clear(); sortOptions.clear(); available << "Filename" << "Filename (extension)" << "Zip File" << "GoodName" << "Internal Name" << "Size" << "MD5" << "CRC1" << "CRC2" << "Players" << "Rumble" << "Save Type"; labelOptions << "Filename" << "Filename (extension)" << "GoodName" << "Internal Name"; sortOptions << "Filename" << "GoodName" << "Internal Name" << "Size"; if (downloadItems) { available << "Game Title" << "Release Date" << "Overview" << "ESRB" << "Genre" << "Publisher" << "Developer" << "Game Cover"; labelOptions << "Game Title" << "Release Date" << "Genre"; sortOptions << "Game Title" << "Release Date" << "ESRB" << "Genre" << "Publisher" << "Developer"; } available.sort(); labelOptions.sort(); sortOptions.sort(); } void SettingsDialog::populateTableAndListTab(bool downloadItems) { populateAvailable(downloadItems); QStringList tableCurrent, tableAvailable; tableCurrent = SETTINGS.value("Table/columns", "Filename|Size").toString().split("|"); tableAvailable = available; foreach (QString cur, tableCurrent) { if (tableAvailable.contains(cur)) tableAvailable.removeOne(cur); else //Someone added an invalid item tableCurrent.removeOne(cur); } ui->tableAvailableList->clear(); foreach (QString listItem, tableAvailable) { QListWidgetItem *item = new QListWidgetItem; item->setText(getTranslation(listItem)); item->setData(Qt::UserRole, listItem); ui->tableAvailableList->addItem(item); } ui->tableAvailableList->sortItems(); ui->tableCurrentList->clear(); foreach (QString listItem, tableCurrent) { QListWidgetItem *item = new QListWidgetItem; item->setText(getTranslation(listItem)); item->setData(Qt::UserRole, listItem); ui->tableCurrentList->addItem(item); } //Grid sort field and label text int labelTextIndex = 0, gridSortIndex = 0; QString currentLabelText = SETTINGS.value("Grid/labeltext","Filename").toString(); QString currentGridSort = SETTINGS.value("Grid/sort","Filename").toString(); ui->labelTextBox->clear(); for (int i = 0; i < labelOptions.length(); i++) { ui->labelTextBox->insertItem(i, getTranslation(labelOptions.at(i)), labelOptions.at(i)); if (currentLabelText == labelOptions.at(i)) labelTextIndex = i; } if (labelTextIndex >= 0) ui->labelTextBox->setCurrentIndex(labelTextIndex); ui->gridSortBox->clear(); for (int i = 0; i < sortOptions.length(); i++) { ui->gridSortBox->insertItem(i, getTranslation(sortOptions.at(i)), sortOptions.at(i)); if (currentGridSort == sortOptions.at(i)) gridSortIndex = i; } if (gridSortIndex >= 0) ui->gridSortBox->setCurrentIndex(gridSortIndex); //List items and sort field QStringList listCurrent, listAvailable; listCurrent = SETTINGS.value("List/columns", "Filename|Internal Name|Size").toString().split("|"); listAvailable = available; listAvailable.removeOne("Game Cover"); //Game Cover handled separately foreach (QString cur, listCurrent) { if (listAvailable.contains(cur)) listAvailable.removeOne(cur); else //Someone added an invalid item listCurrent.removeOne(cur); } ui->listAvailableList->clear(); foreach (QString listItem, listAvailable) { QListWidgetItem *item = new QListWidgetItem; item->setText(getTranslation(listItem)); item->setData(Qt::UserRole, listItem); ui->listAvailableList->addItem(item); } ui->listAvailableList->sortItems(); ui->listCurrentList->clear(); foreach (QString listItem, listCurrent) { QListWidgetItem *item = new QListWidgetItem; item->setText(getTranslation(listItem)); item->setData(Qt::UserRole, listItem); ui->listCurrentList->addItem(item); } int listSortIndex = 0; QString currentListSort = SETTINGS.value("List/sort","Filename").toString(); ui->listSortBox->clear(); for (int i = 0; i < sortOptions.length(); i++) { ui->listSortBox->insertItem(i, getTranslation(sortOptions.at(i)), sortOptions.at(i)); if (currentListSort == sortOptions.at(i)) listSortIndex = i; } if (listSortIndex >= 0) ui->listSortBox->setCurrentIndex(listSortIndex); } void SettingsDialog::removeColumn(QListWidget *currentList, QListWidget *availableList) { int row = currentList->currentRow(); if (row >= 0) { availableList->addItem(currentList->takeItem(row)); availableList->sortItems(); } } void SettingsDialog::removeRomDirectory() { int row = ui->romList->currentRow(); if (row >= 0) delete ui->romList->takeItem(row); } void SettingsDialog::sortDown(QListWidget *currentList) { int row = currentList->currentRow(); if (row > 0) { QListWidgetItem *item = currentList->takeItem(row); currentList->insertItem(row - 1, item); currentList->setCurrentRow(row - 1); } } void SettingsDialog::sortUp(QListWidget *currentList) { int row = currentList->currentRow(); if (row >= 0 && row < currentList->count() - 1) { QListWidgetItem *item = currentList->takeItem(row); currentList->insertItem(row + 1, item); currentList->setCurrentRow(row + 1); } } void SettingsDialog::tableAddColumn() { addColumn(ui->tableCurrentList, ui->tableAvailableList); } void SettingsDialog::tableRemoveColumn() { removeColumn(ui->tableCurrentList, ui->tableAvailableList); } void SettingsDialog::tableSortDown() { sortDown(ui->tableCurrentList); } void SettingsDialog::tableSortUp() { sortUp(ui->tableCurrentList); } void SettingsDialog::toggleDownload(bool active) { foreach (QWidget *next, downloadEnable) next->setEnabled(active); if (active) toggleListCover(ui->listCoverOption->isChecked()); } void SettingsDialog::toggleGridColumn(bool active) { if (active) ui->columnCountBox->setEnabled(false); else ui->columnCountBox->setEnabled(true); } void SettingsDialog::toggleLabel(bool active) { foreach (QWidget *next, labelEnable) next->setEnabled(active); } void SettingsDialog::toggleListCover(bool active) { foreach (QWidget *next, listCoverEnable) next->setEnabled(active); } void SettingsDialog::updateLanguageInfo() { ui->languageInfoLabel->setHidden(false); const char *sourceText = "Note: Language changes will not take place until application restart."; QTranslator translator; QString language = ui->languageBox->itemData(ui->languageBox->currentIndex()).toString().toLower(); QString resource = ":/locale/"+AppNameLower+"_"+language+".qm"; if (QFileInfo(resource).exists()) { translator.load(resource); ui->languageInfoLabel->setText(translator.translate("SettingsDialog", sourceText)); } else ui->languageInfoLabel->setText(sourceText); } mupen64plus-qt-1.15/src/dialogs/settingsdialog.h000066400000000000000000000062351413256700400216760ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef SETTINGSDIALOG_H #define SETTINGSDIALOG_H #include #include class QDesktopWidget; class QListWidget; namespace Ui { class SettingsDialog; } class SettingsDialog : public QDialog { Q_OBJECT public: explicit SettingsDialog(QWidget *parent = 0, int activeTab = 0); ~SettingsDialog(); private: void populateAvailable(bool downloadItems); Ui::SettingsDialog *ui; QDesktopWidget *desktop; QDir pluginsDir; QList downloadEnable; QList labelEnable; QList listCoverEnable; QStringList available; QStringList labelOptions; QStringList sortOptions; private slots: void addColumn(QListWidget *currentList, QListWidget *availableList); void addRomDirectory(); void browseBackground(); void browseEmulator(); void browsePlugin(); void browseData(); void browseConfig(); void editSettings(); void hideBGTheme(QString imagePath); void listAddColumn(); void listRemoveColumn(); void listSortDown(); void listSortUp(); void removeColumn(QListWidget *currentList, QListWidget *availableList); void removeRomDirectory(); void populateTableAndListTab(bool downloadItems); void sortDown(QListWidget *currentList); void sortUp(QListWidget *currentList); void tableAddColumn(); void tableRemoveColumn(); void tableSortDown(); void tableSortUp(); void toggleDownload(bool active); void toggleGridColumn(bool active); void toggleLabel(bool active); void toggleListCover(bool active); void updateLanguageInfo(); }; #endif // SETTINGSDIALOG_H mupen64plus-qt-1.15/src/dialogs/settingsdialog.ui000066400000000000000000001250261413256700400220640ustar00rootroot00000000000000 SettingsDialog 0 0 600 600 550 575 Settings Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok QTabWidget::Rounded 0 Paths Mupen64Plus Files Browse... Browse... Config directory: Browse... Plugins directory: Browse... Mupen64Plus executable: Data directory: Qt::Vertical 20 40 16777215 120 ROM Directories 0 0 16777215 80 0 0 Mupen64Plus-Qt will search for all .z64, .v64, .n64, and .zip files in these directories 16777215 80 Add... Remove Emulation 10 Cached Interpreter Pure Interpreter Dynamic Recompiler Qt::Vertical 20 40 Qt::Horizontal 40 20 Graphics 0 On Screen Display: Fullscreen: Resolution: 100 16777215 Qt::Vertical 20 40 Qt::Horizontal 40 20 Plugins Video Plugin: Audio Plugin: Input Plugin: RSP Plugin: Qt::Vertical 20 40 Table 20 0 Image: 160 250 160 300 16777215 80 ... Qt::UpArrow ... Qt::DownArrow 160 250 160 300 16777215 80 ... Qt::RightArrow ... Qt::LeftArrow Current Columns: Available Columns: Stretch First Column Qt::Horizontal 40 20 Qt::Horizontal 40 20 Qt::Vertical 20 40 Grid ROM Label QLayout::SetMinAndMaxSize Show Label: 150 0 150 16777215 Label Text: Label Color: 100 16777215 Qt::Horizontal 40 20 Other Browse... Custom Background: Sorting: Descending 150 16777215 Background: ROM Image Qt::Horizontal 40 20 0 0 0 0 Shadow Inactive Color: Shadow Active Color: Columns: Image Size: 100 16777215 100 16777215 1 20 100 16777215 false 100 16777215 Adjust Automatically Qt::Vertical 20 40 List Qt::Horizontal 40 20 20 0 Current Items: 16777215 80 ... Qt::RightArrow ... Qt::LeftArrow 160 250 16777215 80 ... Qt::UpArrow ... Qt::DownArrow 160 250 Available Items: Image Size: Descending Sorting: 130 16777215 First Item as Header Display Cover Image Text Size: Theme: Qt::Horizontal 40 20 Qt::Vertical 20 40 Other 10 10 150 0 Additional Parameters: 330 0 Download Game Information (thegamesdb.net): Save Options: Language: <html><head/><body><p>Use to pass additional parameters to Mupen64Plus (cheats, etc.). See <a href="http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage"><span style=" text-decoration: underline; color:#0057ae;">http://mupen64plus.org/wiki/index.php?title=UIConsoleUsage</span></a> for more information.</p></body></html> Qt::Vertical 20 40 Qt::Horizontal 40 20 <b>Note:</b> Language changes will not take place until application restart. true 10 tabWidget emulatorPath pluginPath dataPath configPath emulatorButton pluginButton dataButton configButton romList romAddButton romRemoveButton pureButton cachedButton dynamicButton osdOption fullscreenOption resolutionBox videoBox audioBox inputBox rspBox tableAvailableList tableAddButton tableRemoveButton tableCurrentList tableSortDownButton tableSortUpButton tableSizeBox tableStretchOption gridSizeBox columnCountBox autoColumnOption shadowInactiveBox shadowActiveBox labelOption labelTextBox labelColorBox bgThemeBox backgroundPath backgroundButton gridSortBox gridDescendingOption listAvailableList listAddButton listRemoveButton listCurrentList listSortDownButton listSortUpButton listCoverOption listHeaderOption listSizeBox listTextBox listThemeBox listSortBox listDescendingOption downloadOption saveOption parametersLine languageBox mupen64plus-qt-1.15/src/emulation/000077500000000000000000000000001413256700400170525ustar00rootroot00000000000000mupen64plus-qt-1.15/src/emulation/emulatorhandler.cpp000066400000000000000000000246511413256700400227540ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "emulatorhandler.h" #include "../global.h" #include "../common.h" #include #include #include #include #include EmulatorHandler::EmulatorHandler(QWidget *parent) : QObject(parent) { this->parent = parent; lastOutput = ""; } void EmulatorHandler::checkStatus(int status, QProcess::ExitStatus exitStatus) { if (status != 0 || exitStatus != QProcess::NormalExit) { QMessageBox exitDialog(parent); exitDialog.setWindowTitle(tr("Warning")); exitDialog.setText(tr(" quit unexpectedly. Check the log for more information.") .replace("",ParentName)); exitDialog.setIcon(QMessageBox::Warning); exitDialog.addButton(QMessageBox::Ok); exitDialog.addButton(tr("View Log"), QMessageBox::HelpRole); int ret = exitDialog.exec(); if (ret == 0) emit showLog(); } } void EmulatorHandler::cleanTemp() { QFile::remove(QDir::tempPath() + "/" + AppNameLower + "-" + qgetenv("USER") + "/temp.z64"); } void EmulatorHandler::emitFinished() { emit finished(); } QStringList EmulatorHandler::parseArgString(QString argString) { QStringList result; QString arg; bool inQuote = false; bool inApos = false; for (int i = 0; i < argString.size(); i++) { // Check if inside of a quote if (argString.at(i) == QLatin1Char('"')) { inQuote = !inQuote; // Only continue if outside of both quotes and apostrophes if (arg.isEmpty() || (!inQuote && !inApos)) continue; } // Same check for apostrophes if (argString.at(i) == QLatin1Char('\'')) { inApos = !inApos; if (arg.isEmpty() || (!inQuote && !inApos)) continue; } if (!inQuote && !inApos && argString.at(i).isSpace()) { if (!arg.isEmpty()) { result += arg; arg.clear(); } } else arg += argString.at(i); } if (!arg.isEmpty()) result += arg; return result; } void EmulatorHandler::readOutput() { QString output = emulatorProc->readAllStandardOutput(); if (output != "") lastOutput.append(output); else lastOutput.append("\n" + tr("There was no output. If you were expecting Mupen64Plus to run, try copying") + "\n" + tr("the above command into a command prompt to see if you get an error.")); } void EmulatorHandler::startEmulator(QDir romDir, QString romFileName, QString zipFileName) { QString completeRomPath; bool zip = false; if (zipFileName != "") { //If zipped file, extract and write to temp location for loading zip = true; QString zipFile = romDir.absoluteFilePath(zipFileName); QuaZipFile zippedFile(zipFile, romFileName); zippedFile.open(QIODevice::ReadOnly); QByteArray romData; romData.append(zippedFile.readAll()); zippedFile.close(); QString tempDir = QDir::tempPath() + "/" + AppNameLower + "-" + qgetenv("USER"); QDir().mkpath(tempDir); completeRomPath = tempDir + "/temp.n64"; QFile tempRom(completeRomPath); tempRom.open(QIODevice::WriteOnly); tempRom.write(romData); tempRom.close(); } else completeRomPath = romDir.absoluteFilePath(romFileName); QString emulatorPath = SETTINGS.value("Paths/mupen64plus", "").toString(); QString dataPath = SETTINGS.value("Paths/data", "").toString(); QString configPath = SETTINGS.value("Paths/config", "").toString(); QString pluginPath = SETTINGS.value("Paths/plugins", "").toString(); QDir dataDir(dataPath); QDir configDir(configPath); QDir pluginDir(pluginPath); QString emuMode = SETTINGS.value("Emulation/mode", "").toString(); QString resolution = SETTINGS.value("Graphics/resolution", "").toString(); QString videoPlugin = SETTINGS.value("Plugins/video", "").toString(); QString audioPlugin = SETTINGS.value("Plugins/audio", "").toString(); QString inputPlugin = SETTINGS.value("Plugins/input", "").toString(); QString rspPlugin = SETTINGS.value("Plugins/rsp", "").toString(); QFile emulatorFile(emulatorPath); QFile romFile(completeRomPath); QString gameVideoPlugin = SETTINGS.value(romFileName+"/video", "").toString(); QString gameAudioPlugin = SETTINGS.value(romFileName+"/audio", "").toString(); QString gameInputPlugin = SETTINGS.value(romFileName+"/input", "").toString(); QString gameRSPPlugin = SETTINGS.value(romFileName+"/rsp", "").toString(); QString gameConfigPath = SETTINGS.value(romFileName+"/config", "").toString(); QDir gameConfigDir(gameConfigPath); //Sanity checks if(!emulatorFile.exists() || QFileInfo(emulatorFile).isDir() || !QFileInfo(emulatorFile).isExecutable()) { QMessageBox::warning(parent, tr("Warning"), tr(" executable not found.").replace("",ParentName)); if (zip) cleanTemp(); return; } if(!romFile.exists() || QFileInfo(romFile).isDir()) { QMessageBox::warning(parent, tr("Warning"), tr("ROM file not found.")); if (zip) cleanTemp(); return; } romFile.open(QIODevice::ReadOnly); QByteArray romCheck = romFile.read(4); romFile.close(); if (romCheck.toHex() != "80371240" && romCheck.toHex() != "37804012") { QMessageBox::warning(parent, tr("Warning"), tr("Not a valid ROM File.")); if (zip) cleanTemp(); return; } QStringList args; if (SETTINGS.value("saveoptions", "").toString() != "true") args << "--nosaveoptions"; if (dataPath != "" && dataDir.exists()) args << "--datadir" << dataPath; if (gameConfigPath != "" && gameConfigDir.exists()) args << "--configdir" << gameConfigPath; else if (configPath != "" && configDir.exists()) args << "--configdir" << configPath; if (pluginPath != "" && pluginDir.exists()) args << "--plugindir" << pluginPath; if (emuMode != "") args << "--emumode" << emuMode; if (SETTINGS.value("Graphics/osd", "").toString() == "true") args << "--osd"; else args << "--noosd"; if (SETTINGS.value("Graphics/fullscreen", "").toString() == "true") args << "--fullscreen"; else args << "--windowed"; if (resolution != "") args << "--resolution" << resolution; if (gameVideoPlugin != "") args << "--gfx" << gameVideoPlugin; else if (videoPlugin != "") args << "--gfx" << videoPlugin; if (gameAudioPlugin != "") args << "--audio" << gameAudioPlugin; else if (audioPlugin != "") args << "--audio" << audioPlugin; if (gameInputPlugin != "") args << "--input" << gameInputPlugin; else if (inputPlugin != "") args << "--input" << inputPlugin; if (gameRSPPlugin != "") args << "--rsp" << gameRSPPlugin; else if (rspPlugin != "") args << "--rsp" << rspPlugin; QString otherParameters = SETTINGS.value("Other/parameters", "").toString(); if (otherParameters != "") args.append(parseArgString(otherParameters)); QString gameParameters = SETTINGS.value(romFileName+"/parameters").toString(); if (gameParameters != "") args.append(parseArgString(gameParameters)); args << completeRomPath; emulatorProc = new QProcess(this); connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(readOutput())); connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(emitFinished())); connect(emulatorProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(checkStatus(int, QProcess::ExitStatus))); if (zip) connect(emulatorProc, SIGNAL(finished(int)), this, SLOT(cleanTemp())); // GLideN64 workaround. Can be removed if workaround is no longer needed // See: https://github.com/gonetz/GLideN64/issues/454#issuecomment-126853972 if (SETTINGS.value("Other/forcegl33", "").toString() == "true") { QProcessEnvironment emulatorEnv = QProcessEnvironment::systemEnvironment(); emulatorEnv.insert("MESA_GL_VERSION_OVERRIDE", "3.3COMPAT"); emulatorProc->setProcessEnvironment(emulatorEnv); } emulatorProc->setWorkingDirectory(QFileInfo(emulatorFile).dir().canonicalPath()); emulatorProc->setProcessChannelMode(QProcess::MergedChannels); emulatorProc->start(emulatorPath, args); //Add command to log QString executable = emulatorPath; if (executable.contains(" ")) executable = '"' + executable + '"'; QString argString; foreach(QString arg, args) { if (arg.contains(" ")) argString += " \"" + arg + "\""; else argString += " " + arg; } lastOutput = executable + argString + "\n\n"; emit started(); } void EmulatorHandler::stopEmulator() { emulatorProc->terminate(); } mupen64plus-qt-1.15/src/emulation/emulatorhandler.h000066400000000000000000000044011413256700400224100ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef EMULATORHANDLER_H #define EMULATORHANDLER_H #include #include class QDir; class EmulatorHandler : public QObject { Q_OBJECT public: explicit EmulatorHandler(QWidget *parent = 0); void startEmulator(QDir romDir, QString romFileName, QString zipFileName = ""); void stopEmulator(); QString lastOutput; signals: void finished(); void showLog(); void started(); private: QStringList parseArgString(QString argString); QProcess *emulatorProc; QWidget *parent; private slots: void checkStatus(int status, QProcess::ExitStatus exitStatus); void cleanTemp(); void emitFinished(); void readOutput(); }; #endif // EMULATORHANDLER_H mupen64plus-qt-1.15/src/global.h000066400000000000000000000046121413256700400164710ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef GLOBAL_H #define GLOBAL_H #include #include const QString AppName = "Mupen64Plus-Qt"; const QString AppNameLower = "mupen64plus-qt"; const QString ParentName = "Mupen64Plus"; const QString ParentNameLower = "mupen64plus"; const QString Copyright = ""; const QString Website = "https://www.mupen64plus.org/"; const QString GitHub = "https://github.com/dh4/mupen64plus-qt"; const QString TheGamesDBAPIKey = "5dd59a89da5c0018b0e9ded34e299cf675e94d51e229a19822074d1a67019f9f"; #ifdef Q_OS_WIN #define SETTINGS QSettings(AppNameLower+".ini", QSettings::IniFormat) #else #define SETTINGS QSettings(ParentNameLower, AppNameLower) #endif #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD) #define OS_LINUX_OR_BSD #endif #endif // GLOBAL_H mupen64plus-qt-1.15/src/main.cpp000066400000000000000000000054621413256700400165140ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "global.h" #include "common.h" #include "mainwindow.h" #include #include #include #include #include int main(int argc, char *argv[]) { QApplication application(argc, argv); QTranslator translator; QString language = SETTINGS.value("language", getDefaultLanguage()).toString(); if (language != "EN") { QString resource = ":/locale/"+AppNameLower+"_"+language.toLower()+".qm"; if (QFileInfo(resource).exists()) { translator.load(resource); application.installTranslator(&translator); } } QCoreApplication::setOrganizationName(ParentName); QCoreApplication::setApplicationName(AppName); MainWindow window; QString maximized = SETTINGS.value("Geometry/maximized", "").toString(); QString windowx = SETTINGS.value("Geometry/windowx", "").toString(); QString windowy = SETTINGS.value("Geometry/windowy", "").toString(); if (maximized == "true") { window.showMaximized(); } else { window.show(); } if (windowx == "" && windowy == "") { window.move(QGuiApplication::primaryScreen()->geometry().center() - window.rect().center()); } return application.exec(); } mupen64plus-qt-1.15/src/mainwindow.cpp000066400000000000000000001030011413256700400177300ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "mainwindow.h" #include "global.h" #include "common.h" #include "dialogs/aboutdialog.h" #include "dialogs/configeditor.h" #include "dialogs/controlinfodialog.h" #include "dialogs/downloaddialog.h" #include "dialogs/gamesettingsdialog.h" #include "dialogs/logdialog.h" #include "dialogs/settingsdialog.h" #include "emulation/emulatorhandler.h" #include "roms/romcollection.h" #include "roms/thegamesdbscraper.h" #include "views/gridview.h" #include "views/listview.h" #include "views/tableview.h" #include #include #include #include #include #include #include #include #include #include #include #include #if defined(Q_OS_WIN) || defined(Q_OS_OSX) #include #endif MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setWindowTitle(AppName); setWindowIcon(QIcon(":/images/"+ParentNameLower+".png")); installEventFilter(this); autoloadSettings(); emulation = new EmulatorHandler(this); romCollection = new RomCollection(QStringList() << "*.z64" << "*.v64" << "*.n64" << "*.zip", QStringList() << SETTINGS.value("Paths/roms","").toString().split("|"), this); createMenu(); createRomView(); connect(emulation, SIGNAL(started()), this, SLOT(disableButtons())); connect(emulation, SIGNAL(finished()), this, SLOT(enableButtons())); connect(emulation, SIGNAL(showLog()), this, SLOT(openLog())); connect(romCollection, SIGNAL(updateStarted(bool)), this, SLOT(disableViews(bool))); connect(romCollection, SIGNAL(romAdded(Rom*, int)), this, SLOT(addToView(Rom*, int))); connect(romCollection, SIGNAL(updateEnded(int, bool)), this, SLOT(enableViews(int, bool))); romCollection->cachedRoms(false, true); mainWidget = new QWidget(this); setCentralWidget(mainWidget); setGeometry(QRect(SETTINGS.value("Geometry/windowx", 0).toInt(), SETTINGS.value("Geometry/windowy", 0).toInt(), SETTINGS.value("Geometry/width", 900).toInt(), SETTINGS.value("Geometry/height", 600).toInt())); if (SETTINGS.value("View/fullscreen", "").toString() == "true") updateFullScreenMode(); mainLayout = new QVBoxLayout(mainWidget); mainLayout->setMenuBar(menuBar); mainLayout->addWidget(emptyView); mainLayout->addWidget(tableView); mainLayout->addWidget(gridView); mainLayout->addWidget(listView); mainLayout->addWidget(disabledView); mainLayout->setMargin(0); mainWidget->setLayout(mainLayout); mainWidget->setMinimumSize(300, 200); } void MainWindow::addToView(Rom *currentRom, int count) { QString visibleLayout = SETTINGS.value("View/layout", "none").toString(); if (visibleLayout == "table") tableView->addToTableView(currentRom); else if (visibleLayout == "grid") gridView->addToGridView(currentRom, count, false); else if (visibleLayout == "list") listView->addToListView(currentRom, count, false); } void MainWindow::autoloadSettings() { QString emulatorPath = SETTINGS.value("Paths/mupen64plus", "").toString(); QString dataPath = SETTINGS.value("Paths/data", "").toString(); QString pluginPath = SETTINGS.value("Paths/plugins", "").toString(); if (emulatorPath == "" && dataPath == "" && pluginPath == "") { #ifdef OS_LINUX_OR_BSD //If user has not entered any settings, check common locations for them QStringList emulatorCheck, dataCheck, pluginCheck; emulatorCheck << "/usr/bin/mupen64plus" << "/usr/games/mupen64plus" << "/usr/local/bin/mupen64plus"; pluginCheck << "/usr/lib/mupen64plus" << "/usr/lib64/mupen64plus" << "/usr/lib/x86_64-linux-gnu/mupen64plus" << "/usr/lib/i386-linux-gnu/mupen64plus" << "/usr/lib/mupen64plus/mupen64plus" << "/usr/lib64/mupen64plus/mupen64plus/" << "/usr/local/lib/mupen64plus"; dataCheck << "/usr/share/mupen64plus" << "/usr/share/games/mupen64plus" << "/usr/local/share/mupen64plus"; foreach (QString check, emulatorCheck) if (QFileInfo(check).exists()) SETTINGS.setValue("Paths/mupen64plus", check); foreach (QString check, pluginCheck) if (QFileInfo(check+"/mupen64plus-video-rice.so").exists()) SETTINGS.setValue("Paths/plugins", check); foreach (QString check, dataCheck) if (QFileInfo(check+"/mupen64plus.ini").exists()) SETTINGS.setValue("Paths/data", check); #endif #ifdef Q_OS_WIN //Check for Mupen64Plus within the same directory QString currentDir = QCoreApplication::applicationDirPath(); if (QFileInfo(currentDir+"/mupen64plus-ui-console.exe").exists()) SETTINGS.setValue("Paths/mupen64plus", currentDir+"/mupen64plus-ui-console.exe"); else if (QFileInfo(currentDir+"/mupen64plus.exe").exists()) SETTINGS.setValue("Paths/mupen64plus", currentDir+"/mupen64plus.exe"); if (QFileInfo(currentDir+"/mupen64plus-video-rice.dll").exists()) SETTINGS.setValue("Paths/plugins", currentDir); if (QFileInfo(currentDir+"/mupen64plus.ini").exists()) SETTINGS.setValue("Paths/data", currentDir); #endif #ifdef Q_OS_OSX //Check for Mupen64Plus App within the same directory QString currentDir = QCoreApplication::applicationDirPath(); QString mupen64App = currentDir+"/mupen64plus.app/Contents"; if (QFileInfo(mupen64App+"/MacOS/mupen64plus").exists()) { SETTINGS.setValue("Paths/mupen64plus", mupen64App+"/MacOS/mupen64plus"); SETTINGS.setValue("Paths/plugins", mupen64App+"/MacOS"); SETTINGS.setValue("Paths/data", mupen64App+"/Resources"); } #endif } checkConfigLocation(); } void MainWindow::checkConfigLocation() { //Check default location for mupen64plus.cfg in case user wants to use editor QString configPath = SETTINGS.value("Paths/config", "").toString(); if (configPath == "") { QString homeDir = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first(); #ifdef Q_OS_WIN QString configCheck = homeDir + "/AppData/Roaming/Mupen64Plus/"; #else QString configCheck = homeDir + "/.config/mupen64plus"; #endif if (QFileInfo(configCheck+"/mupen64plus.cfg").exists()) SETTINGS.setValue("Paths/config", configCheck); } } void MainWindow::closeEvent(QCloseEvent *event) { SETTINGS.setValue("Geometry/windowx", geometry().x()); SETTINGS.setValue("Geometry/windowy", geometry().y()); SETTINGS.setValue("Geometry/width", geometry().width()); SETTINGS.setValue("Geometry/height", geometry().height()); if (isMaximized()) SETTINGS.setValue("Geometry/maximized", true); else SETTINGS.setValue("Geometry/maximized", ""); tableView->saveColumnWidths(); event->accept(); } void MainWindow::createMenu() { menuBar = new QMenuBar(this); //File fileMenu = new QMenu(tr("&File"), this); openAction = fileMenu->addAction(tr("&Open ROM...")); fileMenu->addSeparator(); refreshAction = fileMenu->addAction(tr("&Refresh List")); downloadAction = fileMenu->addAction(tr("&Download/Update Info...")); deleteAction = fileMenu->addAction(tr("D&elete Current Info...")); #ifndef Q_OS_OSX //OSX does not show the quit action so the separator is unneeded fileMenu->addSeparator(); #endif quitAction = fileMenu->addAction(tr("&Quit")); openAction->setIcon(QIcon::fromTheme("document-open")); refreshAction->setIcon(QIcon::fromTheme("view-refresh")); quitAction->setIcon(QIcon::fromTheme("application-exit")); downloadAction->setEnabled(false); deleteAction->setEnabled(false); menuBar->addMenu(fileMenu); connect(openAction, SIGNAL(triggered()), this, SLOT(openRom())); connect(refreshAction, SIGNAL(triggered()), romCollection, SLOT(addRoms())); connect(downloadAction, SIGNAL(triggered()), this, SLOT(openDownloader())); connect(deleteAction, SIGNAL(triggered()), this, SLOT(openDeleteDialog())); connect(quitAction, SIGNAL(triggered()), this, SLOT(close())); //Emulation emulationMenu = new QMenu(tr("&Emulation"), this); startAction = emulationMenu->addAction(tr("&Start")); stopAction = emulationMenu->addAction(tr("St&op")); emulationMenu->addSeparator(); logAction = emulationMenu->addAction(tr("View Log")); startAction->setIcon(QIcon::fromTheme("media-playback-start")); stopAction->setIcon(QIcon::fromTheme("media-playback-stop")); startAction->setEnabled(false); stopAction->setEnabled(false); menuBar->addMenu(emulationMenu); connect(startAction, SIGNAL(triggered()), this, SLOT(launchRomFromMenu())); connect(stopAction, SIGNAL(triggered()), this, SLOT(stopEmulator())); connect(logAction, SIGNAL(triggered()), this, SLOT(openLog())); //Settings settingsMenu = new QMenu(tr("&Settings"), this); editorAction = settingsMenu->addAction(tr("Edit mupen64plus.cfg...")); settingsMenu->addSeparator(); configureGameAction = settingsMenu->addAction(tr("Configure &Game...")); #ifndef Q_OS_OSX //OSX does not show the quit action so the separator is unneeded settingsMenu->addSeparator(); #endif configureAction = settingsMenu->addAction(tr("&Configure...")); configureAction->setIcon(QIcon::fromTheme("preferences-other")); configureGameAction->setEnabled(false); menuBar->addMenu(settingsMenu); connect(editorAction, SIGNAL(triggered()), this, SLOT(openEditor())); connect(configureGameAction, SIGNAL(triggered()), this, SLOT(openGameSettings())); connect(configureAction, SIGNAL(triggered()), this, SLOT(openSettings())); //View viewMenu = new QMenu(tr("&View"), this); layoutMenu = viewMenu->addMenu(tr("&Layout")); layoutGroup = new QActionGroup(this); QList layouts; layouts << (QStringList() << tr("None") << "none") << (QStringList() << tr("Table View") << "table") << (QStringList() << tr("Grid View") << "grid") << (QStringList() << tr("List View") << "list"); QString layoutValue = SETTINGS.value("View/layout", "none").toString(); foreach (QStringList layoutName, layouts) { QAction *layoutItem = layoutMenu->addAction(layoutName.at(0)); layoutItem->setData(layoutName.at(1)); layoutItem->setCheckable(true); layoutGroup->addAction(layoutItem); //Only enable layout changes when emulator is not running menuEnable << layoutItem; if(layoutValue == layoutName.at(1)) layoutItem->setChecked(true); } viewMenu->addSeparator(); //OSX El Capitan adds it's own full-screen option #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) if (!(QOperatingSystemVersion::current() >= QOperatingSystemVersion::OSXElCapitan && QOperatingSystemVersion::currentType() == QOperatingSystemVersion::MacOS)) #else if (QSysInfo::macVersion() < QSysInfo::MV_ELCAPITAN || QSysInfo::macVersion() == QSysInfo::MV_None) #endif fullScreenAction = viewMenu->addAction(tr("&Full-screen")); else fullScreenAction = new QAction(this); fullScreenAction->setCheckable(true); if (SETTINGS.value("View/fullscreen", "") == "true") fullScreenAction->setChecked(true); menuBar->addMenu(viewMenu); connect(layoutGroup, SIGNAL(triggered(QAction*)), this, SLOT(updateLayoutSetting())); connect(fullScreenAction, SIGNAL(triggered()), this, SLOT(updateFullScreenMode())); //Help helpMenu = new QMenu(tr("&Help"), this); documentationAction = helpMenu->addAction(tr("Documentation")); mupenDocsAction = helpMenu->addAction(tr("Mupen64Plus Docs")); helpMenu->addSeparator(); controlInfoAction = helpMenu->addAction(tr("Default Controls")); helpMenu->addSeparator(); aboutAction = helpMenu->addAction(tr("&About")); aboutAction->setIcon(QIcon::fromTheme("help-about")); menuBar->addMenu(helpMenu); connect(documentationAction, SIGNAL(triggered()), this, SLOT(openDocumentation())); connect(mupenDocsAction, SIGNAL(triggered()), this, SLOT(openMupenDocs())); connect(controlInfoAction, SIGNAL(triggered()), this, SLOT(openDefaultControls())); connect(aboutAction, SIGNAL(triggered()), this, SLOT(openAbout())); //Create list of actions that are enabled only when emulator is not running menuEnable << startAction << logAction << openAction << refreshAction << downloadAction << deleteAction << configureAction << configureGameAction << editorAction << quitAction; //Create list of actions that are disabled when emulator is not running menuDisable << stopAction; //Create list of actions that are only active when a ROM is selected menuRomSelected << startAction << deleteAction << downloadAction << configureGameAction; } void MainWindow::createRomView() { //Create empty view emptyView = new QScrollArea(this); emptyView->setStyleSheet("QScrollArea { border: none; }"); emptyView->setBackgroundRole(QPalette::Base); emptyView->setAutoFillBackground(true); emptyView->setHidden(true); emptyLayout = new QGridLayout(emptyView); emptyIcon = new QLabel(emptyView); emptyIcon->setPixmap(QPixmap(":/images/"+ParentNameLower+".png")); emptyLayout->addWidget(emptyIcon, 1, 1); emptyLayout->setColumnStretch(0, 1); emptyLayout->setColumnStretch(2, 1); emptyLayout->setRowStretch(0, 1); emptyLayout->setRowStretch(2, 1); emptyView->setLayout(emptyLayout); //Create table view tableView = new TableView(this); connect(tableView, SIGNAL(clicked(QModelIndex)), this, SLOT(enableButtons())); connect(tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(launchRomFromTable())); connect(tableView, SIGNAL(tableActive()), this, SLOT(enableButtons())); connect(tableView, SIGNAL(enterPressed()), this, SLOT(launchRomFromTable())); //Create grid view gridView = new GridView(this); connect(gridView, SIGNAL(gridItemSelected(bool)), this, SLOT(toggleMenus(bool))); //Create list view listView = new ListView(this); connect(listView, SIGNAL(listItemSelected(bool)), this, SLOT(toggleMenus(bool))); //Create disabled view disabledView = new QWidget(this); disabledView->setHidden(true); disabledLayout = new QVBoxLayout(disabledView); disabledLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); disabledView->setLayout(disabledLayout); QString disabledText = QString(tr("Add a directory containing ROMs under ")) + tr("Settings->Configure->Paths to use this view."); disabledLabel = new QLabel(disabledText, disabledView); disabledLabel->setWordWrap(true); disabledLabel->setAlignment(Qt::AlignCenter); disabledLayout->addWidget(disabledLabel); showActiveView(); } void MainWindow::disableButtons() { toggleMenus(false); } void MainWindow::disableViews(bool imageUpdated) { QString visibleLayout = SETTINGS.value("View/layout", "none").toString(); //Save position in current layout if (visibleLayout == "table") tableView->saveTablePosition(); else if (visibleLayout == "grid") gridView->saveGridPosition(); else if (visibleLayout == "list") listView->saveListPosition(); resetLayouts(imageUpdated); tableView->clear(); tableView->setEnabled(false); gridView->setEnabled(false); listView->setEnabled(false); foreach (QAction *next, menuRomSelected) next->setEnabled(false); } void MainWindow::enableButtons() { toggleMenus(true); } void MainWindow::enableViews(int romCount, bool cached) { QString visibleLayout = SETTINGS.value("View/layout", "none").toString(); if (romCount != 0) { //else no ROMs, so leave views disabled QStringList tableVisible = SETTINGS.value("Table/columns", "Filename|Size").toString().split("|"); if (tableVisible.join("") != "") tableView->setEnabled(true); gridView->setEnabled(true); listView->setEnabled(true); if (visibleLayout == "table") tableView->setFocus(); else if (visibleLayout == "grid") gridView->setFocus(); else if (visibleLayout == "list") listView->setFocus(); //Check if disabled view is showing. If it is, re-enabled the selected view if (!disabledView->isHidden()) { disabledView->setHidden(true); showActiveView(); } if (cached) { QTimer *timer = new QTimer(this); timer->setSingleShot(true); timer->setInterval(0); timer->start(); if (visibleLayout == "table") connect(timer, SIGNAL(timeout()), tableView, SLOT(setTablePosition())); else if (visibleLayout == "grid") connect(timer, SIGNAL(timeout()), gridView, SLOT(setGridPosition())); else if (visibleLayout == "list") connect(timer, SIGNAL(timeout()), listView, SLOT(setListPosition())); } } else { if (visibleLayout != "none") { tableView->setHidden(true); gridView->setHidden(true); listView->setHidden(true); disabledView->setHidden(false); } } } bool MainWindow::eventFilter(QObject*, QEvent *event) { //Show menu bar if mouse is at top of screen in full-screen mode if (event->type() == QEvent::HoverMove && isFullScreen()) { QMouseEvent *mouseEvent = static_cast(event); int mousePos = mouseEvent->pos().y(); if (mousePos < 5) showMenuBar(true); if (mousePos > 30) showMenuBar(false); } if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); //Exit fullscreen mode if Esc key is pressed if (keyEvent->key() == Qt::Key_Escape && isFullScreen()) updateFullScreenMode(); } //OSX El Capitan adds it's own full-screen option, so handle the event change here #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::OSXElCapitan && QOperatingSystemVersion::currentType() == QOperatingSystemVersion::MacOS) { #else if (QSysInfo::macVersion() >= QSysInfo::MV_ELCAPITAN && QSysInfo::macVersion() != QSysInfo::MV_None) { #endif if (event->type() == QEvent::WindowStateChange) { QWindowStateChangeEvent *windowEvent = static_cast(event); if (windowEvent->oldState() == Qt::WindowNoState) { SETTINGS.setValue("View/fullscreen", true); tableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); gridView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); listView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { SETTINGS.setValue("View/fullscreen", ""); tableView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); gridView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); listView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); } } } return false; } QString MainWindow::getCurrentRomInfoFromView(QString infoName) { QString visibleLayout = SETTINGS.value("View/layout", "none").toString(); if (visibleLayout == "table") return tableView->getCurrentRomInfo(infoName); else if (visibleLayout == "grid" && gridView->hasSelectedRom()) return gridView->getCurrentRomInfo(infoName); else if (visibleLayout == "list" && listView->hasSelectedRom()) return listView->getCurrentRomInfo(infoName); return ""; } void MainWindow::launchRomFromMenu() { QString visibleLayout = layoutGroup->checkedAction()->data().toString(); if (visibleLayout == "table") launchRomFromTable(); else if (visibleLayout == "grid") launchRomFromWidget(gridView->getCurrentRomWidget()); else if (visibleLayout == "list") launchRomFromWidget(listView->getCurrentRomWidget()); } void MainWindow::launchRomFromTable() { QString romFileName = tableView->getCurrentRomInfo("fileName"); QString romDirName = tableView->getCurrentRomInfo("dirName"); QString zipFileName = tableView->getCurrentRomInfo("zipFile"); emulation->startEmulator(QDir(romDirName), romFileName, zipFileName); } void MainWindow::launchRomFromWidget(QWidget *current) { QString romFileName = current->property("fileName").toString(); QString romDirName = current->property("directory").toString(); QString zipFileName = current->property("zipFile").toString(); emulation->startEmulator(QDir(romDirName), romFileName, zipFileName); } void MainWindow::launchRomFromZip() { QString fileName = zipList->currentItem()->text(); zipDialog->close(); emulation->startEmulator(QDir(), fileName, openPath); } void MainWindow::openAbout() { AboutDialog aboutDialog(this); aboutDialog.exec(); } void MainWindow::openDefaultControls() { ControlInfo controlInfo(this); controlInfo.exec(); } void MainWindow::openDeleteDialog() { scraper = new TheGamesDBScraper(this); scraper->deleteGameInfo(getCurrentRomInfoFromView("fileName"), getCurrentRomInfoFromView("romMD5")); delete scraper; romCollection->cachedRoms(); } void MainWindow::openDocumentation() { QDesktopServices::openUrl(QUrl("https://github.com/dh4/mupen64plus-qt#table-of-contents")); } void MainWindow::openDownloader() { DownloadDialog downloadDialog(getCurrentRomInfoFromView("fileName"), getCurrentRomInfoFromView("search"), getCurrentRomInfoFromView("romMD5"), this); downloadDialog.exec(); romCollection->cachedRoms(); } void MainWindow::openEditor() { checkConfigLocation(); QString configPath = SETTINGS.value("Paths/config", "").toString(); QDir configDir = QDir(configPath); QString configFile = configDir.absoluteFilePath("mupen64plus.cfg"); QFile config(configFile); if (configPath == "" || !config.exists()) { QMessageBox::information(this, tr("Not Found"), QString(tr("Editor requires config directory to be ")) + tr("set to a directory with mupen64plus.cfg.") + "

" + tr("See here for the default config location:") + "
" + "" + "https://mupen64plus.org/wiki/index.php?title=FileLocations"); } else { ConfigEditor configEditor(configFile, this); configEditor.exec(); } } void MainWindow::openGameSettings() { GameSettingsDialog gameSettingsDialog(getCurrentRomInfoFromView("fileName"), this); gameSettingsDialog.exec(); } void MainWindow::openLog() { if (emulation->lastOutput == "") { QMessageBox::information(this, tr("No Output"), tr("There is no log. Either has not yet run or there was no output from the last run.") .replace("",ParentName)); } else { LogDialog logDialog(emulation->lastOutput, this); logDialog.exec(); } } void MainWindow::openMupenDocs() { QDesktopServices::openUrl(QUrl("https://mupen64plus.org/docs/")); } void MainWindow::openSettings() { QString tableImageBefore = SETTINGS.value("Table/imagesize", "Medium").toString(); QString columnsBefore = SETTINGS.value("Table/columns", "Filename|Size").toString(); QString downloadBefore = SETTINGS.value("Other/downloadinfo", "").toString(); SettingsDialog settingsDialog(this, 0); settingsDialog.exec(); QString tableImageAfter = SETTINGS.value("Table/imagesize", "Medium").toString(); QString columnsAfter = SETTINGS.value("Table/columns", "Filename|Size").toString(); QString downloadAfter = SETTINGS.value("Other/downloadinfo", "").toString(); //Reset columns widths if user has selected different columns to display if (columnsBefore != columnsAfter) { SETTINGS.setValue("Table/width", ""); tableView->setColumnCount(3); tableView->setHeaderLabels(QStringList("")); } QStringList romSave = SETTINGS.value("Paths/roms","").toString().split("|"); if (romCollection->romPaths != romSave) { romCollection->updatePaths(romSave); romCollection->addRoms(); } else if (downloadBefore == "" && downloadAfter == "true") { romCollection->addRoms(); } else { if (tableImageBefore != tableImageAfter) romCollection->cachedRoms(true); else romCollection->cachedRoms(false); } gridView->setGridBackground(); listView->setListBackground(); toggleMenus(true); } void MainWindow::openRom() { QString filter = "N64 ROMs ("; foreach (QString type, romCollection->getFileTypes(true)) filter += type + " "; filter += ");;" + tr("All Files") + " (*)"; QString searchPath = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first(); if (romCollection->romPaths.count() > 0) searchPath = romCollection->romPaths.at(0); openPath = QFileDialog::getOpenFileName(this, tr("Open ROM File"), searchPath, filter); if (openPath != "") { if (QFileInfo(openPath).suffix() == "zip") { QStringList zippedFiles = getZippedFiles(openPath); QString last; int count = 0; foreach (QString file, zippedFiles) { QString ext = file.right(4).toLower(); if (romCollection->getFileTypes().contains("*" + ext)) { last = file; count++; } } if (count == 0) QMessageBox::information(this, tr("No ROMs"), tr("No ROMs found in ZIP file.")); else if (count == 1) emulation->startEmulator(QDir(QFileInfo(openPath).dir()), last, openPath); else { //More than one ROM in zip file, so let user select openZipDialog(zippedFiles); } } else emulation->startEmulator(QDir(QFileInfo(openPath).dir()), openPath); } } void MainWindow::openZipDialog(QStringList zippedFiles) { zipDialog = new QDialog(this); zipDialog->setWindowTitle(tr("Select ROM")); zipDialog->setMinimumSize(200, 150); zipDialog->resize(300, 150); zipLayout = new QGridLayout(zipDialog); zipLayout->setContentsMargins(5, 10, 5, 10); zipList = new QListWidget(zipDialog); foreach (QString file, zippedFiles) { QString ext = file.right(4); if (romCollection->getFileTypes().contains("*" + ext)) zipList->addItem(file); } zipList->setCurrentRow(0); zipButtonBox = new QDialogButtonBox(Qt::Horizontal, zipDialog); zipButtonBox->addButton(tr("Launch"), QDialogButtonBox::AcceptRole); zipButtonBox->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); zipLayout->addWidget(zipList, 0, 0); zipLayout->addWidget(zipButtonBox, 1, 0); connect(zipList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(launchRomFromZip())); connect(zipButtonBox, SIGNAL(accepted()), this, SLOT(launchRomFromZip())); connect(zipButtonBox, SIGNAL(rejected()), zipDialog, SLOT(close())); zipDialog->setLayout(zipLayout); zipDialog->exec(); } void MainWindow::resetLayouts(bool imageUpdated) { tableView->resetView(imageUpdated); gridView->resetView(); listView->resetView(); } void MainWindow::showActiveView() { QString visibleLayout = SETTINGS.value("View/layout", "none").toString(); if (visibleLayout == "table") tableView->setHidden(false); else if (visibleLayout == "grid") gridView->setHidden(false); else if (visibleLayout == "list") listView->setHidden(false); else emptyView->setHidden(false); } void MainWindow::showMenuBar(bool mouseAtTop) { menuBar->setHidden(!mouseAtTop); } void MainWindow::showRomMenu(const QPoint &pos) { QMenu *contextMenu = new QMenu(this); QAction *contextStartAction = contextMenu->addAction(tr("&Start")); contextStartAction->setIcon(QIcon::fromTheme("media-playback-start")); contextMenu->addSeparator(); QAction *contextConfigureGameAction = contextMenu->addAction(tr("Configure &Game...")); connect(contextStartAction, SIGNAL(triggered()), this, SLOT(launchRomFromMenu())); connect(contextConfigureGameAction, SIGNAL(triggered()), this, SLOT(openGameSettings())); if (SETTINGS.value("Other/downloadinfo", "").toString() == "true") { contextMenu->addSeparator(); QAction *contextDownloadAction = contextMenu->addAction(tr("&Download/Update Info...")); QAction *contextDeleteAction = contextMenu->addAction(tr("D&elete Current Info...")); connect(contextDownloadAction, SIGNAL(triggered()), this, SLOT(openDownloader())); connect(contextDeleteAction, SIGNAL(triggered()), this, SLOT(openDeleteDialog())); } QWidget *activeWidget = new QWidget(this); QString visibleLayout = SETTINGS.value("View/layout", "none").toString(); if (visibleLayout == "table") activeWidget = tableView->viewport(); else if (visibleLayout == "grid") activeWidget = gridView->getCurrentRomWidget(); else if (visibleLayout == "list") activeWidget = listView->getCurrentRomWidget(); contextMenu->exec(activeWidget->mapToGlobal(pos)); } void MainWindow::stopEmulator() { emulation->stopEmulator(); } void MainWindow::toggleMenus(bool active) { foreach (QAction *next, menuEnable) next->setEnabled(active); foreach (QAction *next, menuDisable) next->setEnabled(!active); tableView->setEnabled(active); gridView->setEnabled(active); listView->setEnabled(active); if (!tableView->hasSelectedRom() && !gridView->hasSelectedRom() && !listView->hasSelectedRom() ) { foreach (QAction *next, menuRomSelected) next->setEnabled(false); } if (SETTINGS.value("Other/downloadinfo", "").toString() == "") { downloadAction->setEnabled(false); deleteAction->setEnabled(false); } } void MainWindow::updateFullScreenMode() { if (isFullScreen()) { fullScreenAction->setChecked(false); SETTINGS.setValue("View/fullscreen", ""); menuBar->setHidden(false); tableView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); gridView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); listView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); showNormal(); } else { fullScreenAction->setChecked(true); SETTINGS.setValue("View/fullscreen", true); menuBar->setHidden(true); tableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); gridView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); listView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); showFullScreen(); } } void MainWindow::updateLayoutSetting() { QString visibleLayout = layoutGroup->checkedAction()->data().toString(); SETTINGS.setValue("View/layout", visibleLayout); emptyView->setHidden(true); tableView->setHidden(true); gridView->setHidden(true); listView->setHidden(true); disabledView->setHidden(true); int romCount = romCollection->cachedRoms(); if (romCount > 0 || visibleLayout == "none") showActiveView(); //View was updated so no ROM will be selected. Update menu items accordingly foreach (QAction *next, menuRomSelected) next->setEnabled(false); } mupen64plus-qt-1.15/src/mainwindow.h000066400000000000000000000110671413256700400174070ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include class QActionGroup; class QDialogButtonBox; class QHeaderView; class QGridLayout; class QLabel; class QListWidget; class QMenuBar; class QScrollArea; class QTreeWidget; class QVBoxLayout; class EmulatorHandler; class GridView; class ListView; class RomCollection; class TableView; class TheGamesDBScraper; class TreeWidgetItem; struct Rom; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); protected: void closeEvent(QCloseEvent *event); bool eventFilter(QObject*, QEvent *event); private: void autoloadSettings(); void checkConfigLocation(); void createMenu(); void createRomView(); void openZipDialog(QStringList zippedFiles); void resetLayouts(bool imageUpdated = false); void showActiveView(); QString getCurrentRomInfoFromView(QString infoName); QString openPath; QAction *aboutAction; QAction *configureAction; QAction *configureGameAction; QAction *controlInfoAction; QAction *deleteAction; QAction *documentationAction; QAction *downloadAction; QAction *editorAction; QAction *fullScreenAction; QAction *logAction; QAction *mupenDocsAction; QAction *openAction; QAction *quitAction; QAction *refreshAction; QAction *startAction; QAction *stopAction; QActionGroup *layoutGroup; QDialog *zipDialog; QDialogButtonBox *zipButtonBox; QGridLayout *emptyLayout; QGridLayout *zipLayout; QLabel *emptyIcon; QLabel *disabledLabel; QList menuEnable; QList menuDisable; QList menuRomSelected; QListWidget *zipList; QMenu *emulationMenu; QMenu *fileMenu; QMenu *helpMenu; QMenu *layoutMenu; QMenu *settingsMenu; QMenu *viewMenu; QMenuBar *menuBar; QScrollArea *emptyView; QVBoxLayout *disabledLayout; QVBoxLayout *mainLayout; QWidget *disabledView; QWidget *mainWidget; EmulatorHandler *emulation; GridView *gridView; ListView *listView; RomCollection *romCollection; TableView *tableView; TheGamesDBScraper *scraper; TreeWidgetItem *fileItem; private slots: void addToView(Rom *currentRom, int count); void disableButtons(); void disableViews(bool imageUpdated); void enableButtons(); void enableViews(int romCount, bool cached); void launchRomFromMenu(); void launchRomFromTable(); void launchRomFromWidget(QWidget *current); void launchRomFromZip(); void openAbout(); void openDefaultControls(); void openDeleteDialog(); void openDownloader(); void openEditor(); void openGameSettings(); void openLog(); void openSettings(); void openRom(); void showMenuBar(bool mouseAtTop); void showRomMenu(const QPoint &); void stopEmulator(); void toggleMenus(bool active); void updateFullScreenMode(); void updateLayoutSetting(); void openDocumentation(); void openMupenDocs(); }; #endif // MAINWINDOW_H mupen64plus-qt-1.15/src/roms/000077500000000000000000000000001413256700400160355ustar00rootroot00000000000000mupen64plus-qt-1.15/src/roms/romcollection.cpp000066400000000000000000000414241413256700400214170ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "romcollection.h" #include "../global.h" #include "../common.h" #include "thegamesdbscraper.h" #include #include #include #include #include #include #include #include #include RomCollection::RomCollection(QStringList fileTypes, QStringList romPaths, QWidget *parent) : QObject(parent) { this->fileTypes = fileTypes; this->romPaths = romPaths; this->romPaths.removeAll(""); this->parent = parent; setupDatabase(); } Rom RomCollection::addRom(QByteArray *romData, QString fileName, QString directory, QString zipFile, QSqlQuery query, bool ddRom) { Rom currentRom; currentRom.fileName = fileName; currentRom.directory = directory; if (ddRom) currentRom.internalName = ""; else currentRom.internalName = QString(romData->mid(32, 20)).trimmed(); currentRom.romMD5 = QString(QCryptographicHash::hash(*romData, QCryptographicHash::Md5).toHex()); currentRom.zipFile = zipFile; currentRom.sortSize = romData->size(); query.bindValue(":filename", currentRom.fileName); query.bindValue(":directory", currentRom.directory); query.bindValue(":internal_name", currentRom.internalName); query.bindValue(":md5", currentRom.romMD5); query.bindValue(":zip_file", currentRom.zipFile); query.bindValue(":size", currentRom.sortSize); if (ddRom) query.bindValue(":dd_rom", 1); else query.bindValue(":dd_rom", 0); query.exec(); if (!ddRom) initializeRom(¤tRom, false); return currentRom; } int RomCollection::addRoms() { emit updateStarted(); //Count files so we know how to setup the progress dialog int totalCount = 0; foreach (QString romPath, romPaths) { QDir romDir(romPath); if (romDir.exists()) { QStringList files = scanDirectory(romDir); totalCount += files.size(); } } QList roms; QList ddRoms; database.open(); QSqlQuery query("DELETE FROM rom_collection", database); if (totalCount != 0) { int count = 0; setupProgressDialog(totalCount); query.prepare(QString("INSERT INTO rom_collection ") + "(filename, directory, internal_name, md5, zip_file, size, dd_rom) " + "VALUES (:filename, :directory, :internal_name, :md5, :zip_file, :size, :dd_rom)"); scraper = new TheGamesDBScraper(parent); foreach (QString romPath, romPaths) { QDir romDir(romPath); QStringList files = scanDirectory(romDir); int romCount = 0; foreach (QString fileName, files) { QString completeFileName = romDir.absoluteFilePath(fileName); QFile file(completeFileName); //If file is a zip file, extract info from any zipped ROMs if (QFileInfo(file).suffix().toLower() == "zip") { foreach (QString zippedFile, getZippedFiles(completeFileName)) { //check for ROM files QByteArray *romData = getZippedRom(zippedFile, completeFileName); if (fileTypes.contains("*.v64")) *romData = byteswap(*romData); if (romData->left(4).toHex() == "80371240") { //Z64 ROM roms.append(addRom(romData, zippedFile, romPath, fileName, query)); romCount++; } else if (romData->left(4).toHex() == "e848d316") { //64DD ROM ddRoms.append(addRom(romData, zippedFile, romPath, fileName, query, true)); romCount++; } delete romData; } } else { //Just a normal file file.open(QIODevice::ReadOnly); QByteArray *romData = new QByteArray(file.readAll()); file.close(); if (fileTypes.contains("*.v64")) *romData = byteswap(*romData); if (romData->left(4).toHex() == "80371240") { //Z64 ROM roms.append(addRom(romData, fileName, romPath, "", query)); romCount++; } else if (romData->left(4).toHex() == "e848d316") { //64DD ROM ddRoms.append(addRom(romData, fileName, romPath, "", query, true)); romCount++; } delete romData; } count++; progress->setValue(count); QCoreApplication::processEvents(QEventLoop::AllEvents); } if (romCount == 0) QMessageBox::warning(parent, tr("Warning"), tr("No ROMs found in ") + romPath + "."); } delete scraper; progress->close(); } else if (romPaths.size() != 0) { QMessageBox::warning(parent, tr("Warning"), tr("No ROMs found.")); } database.close(); //Emit signals for regular roms std::sort(roms.begin(), roms.end(), romSorter); for (int i = 0; i < roms.size(); i++) emit romAdded(&roms[i], i); //Emit signals for 64DD roms std::sort(ddRoms.begin(), ddRoms.end(), romSorter); for (int i = 0; i < ddRoms.size(); i++) emit ddRomAdded(&ddRoms[i]); emit updateEnded(roms.size()); return roms.size(); } int RomCollection::cachedRoms(bool imageUpdated, bool onStartup) { emit updateStarted(imageUpdated); database.open(); QSqlQuery query(QString("SELECT filename, directory, md5, internal_name, zip_file, size, dd_rom ") + "FROM rom_collection", database); query.last(); int romCount = query.at() + 1; query.seek(-1); if (romCount == -1) //Nothing cached so try adding ROMs instead return addRoms(); //Check if user has data from TheGamesDB API v1 and update them to v2 data if (onStartup) { bool onV1 = false; QDir cacheDir(getCacheLocation()); if (!cacheDir.exists() && SETTINGS.value("Other/downloadinfo", "").toString() == "true") onV1 = true; if (onV1) return addRoms(); } QList roms; QList ddRoms; int count = 0; bool showProgress = false; QTime checkPerformance; while (query.next()) { Rom currentRom; currentRom.fileName = query.value(0).toString(); currentRom.directory = query.value(1).toString(); currentRom.romMD5 = query.value(2).toString(); currentRom.internalName = query.value(3).toString(); currentRom.zipFile = query.value(4).toString(); currentRom.sortSize = query.value(5).toInt(); int ddRom = query.value(6).toInt(); //Check performance of adding first item to see if progress dialog needs to be shown if (count == 0) checkPerformance.start(); if (ddRom == 1) ddRoms.append(currentRom); else { initializeRom(¤tRom, true); roms.append(currentRom); } if (count == 0) { int runtime = checkPerformance.elapsed(); //check if operation expected to take longer than two seconds if (runtime * romCount > 2000) { setupProgressDialog(romCount); showProgress = true; } } count++; if (showProgress) { progress->setValue(count); QCoreApplication::processEvents(QEventLoop::AllEvents); } } database.close(); if (showProgress) progress->close(); //Emit signals for regular roms std::sort(roms.begin(), roms.end(), romSorter); for (int i = 0; i < roms.size(); i++) emit romAdded(&roms[i], i); //Emit signals for 64DD roms std::sort(ddRoms.begin(), ddRoms.end(), romSorter); for (int i = 0; i < ddRoms.size(); i++) emit ddRomAdded(&ddRoms[i]); emit updateEnded(roms.size(), true); return roms.size(); } QStringList RomCollection::getFileTypes(bool archives) { QStringList returnList = fileTypes; if (!archives) returnList.removeOne("*.zip"); return returnList; } void RomCollection::initializeRom(Rom *currentRom, bool cached) { QSettings *romCatalog = new QSettings(parent); QString catalogFile = SETTINGS.value("Paths/catalog", "").toString(); if (catalogFile == "") { QString dataPath = SETTINGS.value("Paths/data", "").toString(); QDir dataDir(dataPath); if (QFileInfo(dataDir.absoluteFilePath("mupen64plus.ini")).exists()) catalogFile = dataDir.absoluteFilePath("mupen64plus.ini"); } QDir romDir(currentRom->directory); //Default text for GoodName to notify user currentRom->goodName = getTranslation("Requires catalog file"); currentRom->imageExists = false; bool getGoodName = false; if (QFileInfo(catalogFile).exists()) { romCatalog = new QSettings(catalogFile, QSettings::IniFormat, parent); getGoodName = true; } QFile file(romDir.absoluteFilePath(currentRom->fileName)); currentRom->romMD5 = currentRom->romMD5.toUpper(); currentRom->baseName = QFileInfo(file).completeBaseName(); currentRom->size = QObject::tr("%1 MB").arg((currentRom->sortSize + 1023) / 1024 / 1024); if (getGoodName) { //Join GoodName on ", ", otherwise entries with a comma won't show QVariant gNameRaw = romCatalog->value(currentRom->romMD5+"/GoodName",getTranslation("Unknown ROM")); currentRom->goodName = gNameRaw.toStringList().join(", "); QStringList CRC = romCatalog->value(currentRom->romMD5+"/CRC","").toString().split(" "); if (CRC.size() == 2) { currentRom->CRC1 = CRC[0]; currentRom->CRC2 = CRC[1]; } QString newMD5 = romCatalog->value(currentRom->romMD5+"/RefMD5","").toString(); if (newMD5 == "") newMD5 = currentRom->romMD5; currentRom->players = romCatalog->value(newMD5+"/Players","").toString(); currentRom->saveType = romCatalog->value(newMD5+"/SaveType","").toString(); currentRom->rumble = romCatalog->value(newMD5+"/Rumble","").toString(); } if (!cached && SETTINGS.value("Other/downloadinfo", "").toString() == "true") { if (currentRom->goodName != getTranslation("Unknown ROM") && currentRom->goodName != getTranslation("Requires catalog file")) { scraper->downloadGameInfo(currentRom->romMD5, currentRom->goodName); } else { //tweak internal name by adding spaces to get better results QString search = currentRom->internalName; search.replace(QRegExp("([a-z])([A-Z])"),"\\1 \\2"); search.replace(QRegExp("([^ \\d])(\\d)"),"\\1 \\2"); scraper->downloadGameInfo(currentRom->romMD5, search); } } if (SETTINGS.value("Other/downloadinfo", "").toString() == "true") { QString dataFile = getCacheLocation() + currentRom->romMD5.toLower() + "/data.json"; QFile file(dataFile); file.open(QIODevice::ReadOnly); QString data = file.readAll(); file.close(); QJsonDocument document = QJsonDocument::fromJson(data.toUtf8()); QJsonObject json = document.object(); //Remove any non-standard characters QString regex = "[^A-Za-z 0-9 \\.,\\?'""!@#\\$%\\^&\\*\\(\\)-_=\\+;:<>\\/\\\\|\\}\\{\\[\\]`~é]*"; currentRom->gameTitle = json.value("game_title").toString().remove(QRegExp(regex)); if (currentRom->gameTitle == "") currentRom->gameTitle = getTranslation("Not found"); currentRom->releaseDate = json.value("release_date").toString(); currentRom->sortDate = json.value("release_date").toString(); currentRom->releaseDate.replace(QRegExp("(\\d{4})-(\\d{2})-(\\d{2})"), "\\2/\\3/\\1"); currentRom->overview = json.value("overview").toString().remove(QRegExp(regex)); currentRom->esrb = json.value("rating").toString(); currentRom->genre = json.value("genres").toString(); currentRom->publisher = json.value("publisher").toString(); currentRom->developer = json.value("developer").toString(); foreach (QString ext, QStringList() << "jpg" << "png") { QString imageFile = getCacheLocation() + currentRom->romMD5.toLower() + "/boxart-front." + ext; QFile cover(imageFile); if (cover.exists() && currentRom->image.load(imageFile)) { currentRom->imageExists = true; break; } } } } QStringList RomCollection::scanDirectory(QDir romDir) { QStringList files = romDir.entryList(fileTypes, QDir::Files | QDir::NoSymLinks); QStringList dirs = romDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); foreach (QString dir, dirs) { QString subDir = romDir.absolutePath() + "/" + dir; QStringList subFiles = QDir(subDir).entryList(fileTypes, QDir::Files | QDir::NoSymLinks); foreach (QString subFile, subFiles) files << dir + "/" + subFile; } return files; } void RomCollection::setupDatabase() { // Bump this when updating rom_collection structure // Will cause clients to delete and recreate the table int dbVersion = 2; database = QSqlDatabase::addDatabase("QSQLITE"); database.setDatabaseName(getDataLocation() + "/"+AppNameLower+".sqlite"); if (!database.open()) QMessageBox::warning(parent, tr("Database Not Loaded"), tr("Could not connect to Sqlite database. Application may misbehave.")); QSqlQuery version = database.exec("PRAGMA user_version"); version.next(); if (version.value(0).toInt() != dbVersion) { //old database version, reset rom_collection version.finish(); database.exec("DROP TABLE rom_collection"); database.exec("PRAGMA user_version = " + QString::number(dbVersion)); } database.exec(QString() + "CREATE TABLE IF NOT EXISTS rom_collection (" + "rom_id INTEGER PRIMARY KEY ASC, " + "filename TEXT NOT NULL, " + "directory TEXT NOT NULL, " + "md5 TEXT NOT NULL, " + "internal_name TEXT, " + "zip_file TEXT, " + "size INTEGER, " + "dd_rom INTEGER)"); database.close(); } void RomCollection::setupProgressDialog(int size) { progress = new QProgressDialog(tr("Loading ROMs..."), tr("Cancel"), 0, size, parent); progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowCloseButtonHint); progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowMinimizeButtonHint); progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowContextHelpButtonHint); progress->setCancelButton(nullptr); progress->setWindowModality(Qt::WindowModal); progress->show(); } void RomCollection::updatePaths(QStringList romPaths) { this->romPaths = romPaths; this->romPaths.removeAll(""); } mupen64plus-qt-1.15/src/roms/romcollection.h000066400000000000000000000054471413256700400210710ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef ROMCOLLECTION_H #define ROMCOLLECTION_H #include #include #include class QDir; class QProgressDialog; class TheGamesDBScraper; struct Rom; class RomCollection : public QObject { Q_OBJECT public: explicit RomCollection(QStringList fileTypes, QStringList romPaths, QWidget *parent = 0); int cachedRoms(bool imageUpdated = false, bool onStartup = false); void updatePaths(QStringList romPaths); QStringList getFileTypes(bool archives = false); QStringList romPaths; public slots: int addRoms(); signals: void ddRomAdded(Rom *currentRom); void romAdded(Rom *currentRom, int count); void updateEnded(int romCount, bool cached = false); void updateStarted(bool imageUpdated = false); private: void initializeRom(Rom *currentRom, bool cached); void setupDatabase(); void setupProgressDialog(int size); Rom addRom(QByteArray *romData, QString fileName, QString directory, QString zipFile, QSqlQuery query, bool ddRom = false); QStringList fileTypes; QStringList scanDirectory(QDir romDir); QWidget *parent; QProgressDialog *progress; QSqlDatabase database; TheGamesDBScraper *scraper; }; #endif // ROMCOLLECTION_H mupen64plus-qt-1.15/src/roms/thegamesdbscraper.cpp000066400000000000000000000416261413256700400222350ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "thegamesdbscraper.h" #include "../global.h" #include "../common.h" #include #include #include #include #include #include #include #include #include #include #include #include TheGamesDBScraper::TheGamesDBScraper(QWidget *parent, bool force) : QObject(parent) { this->parent = parent; this->force = force; this->keepGoing = true; } QString TheGamesDBScraper::convertIDs(QJsonObject foundGame, QString typeName, QString listName) { QJsonArray idArray = foundGame.value(typeName).toArray(); QString cacheFileString = getCacheLocation() + typeName + ".json"; QFile cacheFile(cacheFileString); cacheFile.open(QIODevice::ReadOnly); QString data = cacheFile.readAll(); cacheFile.close(); QJsonDocument document = QJsonDocument::fromJson(data.toUtf8()); QJsonObject cache = document.object(); QString result = ""; foreach (QJsonValue id, idArray) { QString entryID = QString::number(id.toInt()); QString entryName = cache.value(entryID).toObject().value("name").toString(); if (entryName == "") { updateListCache(&cacheFile, listName); cacheFile.open(QIODevice::ReadOnly); data = cacheFile.readAll(); cacheFile.close(); document = QJsonDocument::fromJson(data.toUtf8()); entryName = cache.value(entryID).toObject().value("name").toString(); } if (entryName != "") result += entryName + ", "; } int pos = result.lastIndexOf(QChar(',')); return result.left(pos); } void TheGamesDBScraper::deleteGameInfo(QString fileName, QString identifier) { QString text; text = QString(tr("NOTE: If you are deleting this game's information because the game doesn't ")) + tr("exist on TheGamesDB and pulled the information for different game, it's ") + tr("better to create an account on")+" TheGamesDB " + tr("and add the game so other users can benefit as well.") + "

" + tr("This will cause to not update the information for this game until you ") + tr("force it with \"Download/Update Info...\"") + "

" + tr("Delete the current information for") + " " + fileName + "?"; text.replace("",AppName); int answer = QMessageBox::question(parent, tr("Delete Game Information"), text, QMessageBox::Yes | QMessageBox::No); if (answer == QMessageBox::Yes) { QString gameCache = getCacheLocation() + identifier.toLower(); QString dataFile = gameCache + "/data.xml"; QFile file(dataFile); // Remove game information file.open(QIODevice::WriteOnly); QTextStream stream(&file); stream << "NULL"; file.close(); // Remove cover image QString coverFile = gameCache + "/boxart-front."; QFile coverJPG(coverFile + "jpg"); QFile coverPNG(coverFile + "png"); if (coverJPG.exists()) coverJPG.remove(); if (coverPNG.exists()) coverPNG.remove(); coverJPG.open(QIODevice::WriteOnly); QTextStream streamImage(&coverJPG); streamImage << ""; coverJPG.close(); } } void TheGamesDBScraper::downloadGameInfo(QString identifier, QString searchName, QString gameID) { if (keepGoing && identifier != "") { if (force) parent->setEnabled(false); bool updated = false; QString gameCache = getCacheLocation() + identifier.toLower(); QDir cache(gameCache); if (!cache.exists()) { cache.mkpath(gameCache); } QFile genres(getCacheLocation() + "genres.json"); if (!genres.exists()) updateListCache(&genres, "Genres"); QFile developers(getCacheLocation() + "developers.json"); if (!developers.exists()) updateListCache(&developers, "Developers"); QFile publishers(getCacheLocation() + "publishers.json"); if (!publishers.exists()) updateListCache(&publishers, "Publishers"); //Get game JSON info from thegamesdb.net QString dataFile = gameCache + "/data.json"; QFile file(dataFile); if (!file.exists() || file.size() == 0 || force) { QUrl url; //Remove [!], (U), etc. from GoodName for searching searchName.remove(QRegExp("\\W*(\\(|\\[).+(\\)|\\])\\W*")); //Few game specific hacks if (searchName == "Legend of Zelda, The - Majora's Mask" || searchName == "ZELDA MAJORA'S MASK") searchName = "Majora's Mask"; else if (searchName == "Legend of Zelda, The - Ocarina of Time" || searchName == "THE LEGEND OF ZELDA") searchName = "The Legend of Zelda: Ocarina of Time"; else if (searchName.toLower().startsWith("tsumi to batsu")) searchName = "Sin and Punishment"; else if (searchName.toLower() == "1080 snowboarding") searchName = "1080: TenEighty Snowboarding"; else if (searchName == "Extreme-G XG2" || searchName == "Extreme G 2") searchName = "Extreme-G 2"; else if (searchName.contains("Pokemon", Qt::CaseInsensitive)) searchName.replace("Pokemon", "Pokémon", Qt::CaseInsensitive); else if (searchName.toLower() == "smash brothers") searchName = "Super Smash Bros."; else if (searchName.toLower() == "conker bfd") searchName = "Conker's Bad Fur Day"; QString apiFilter = "&filter[platform]=3&include=boxart&fields=game_title,release_date,"; apiFilter += "developers,publishers,genres,overview,rating,players"; QString apiURL = SETTINGS.value("TheGamesDB/url", "https://api.thegamesdb.net/").toString(); QString gameIDPrefix = SETTINGS.value("TheGamesDB/ByGameID", "/v1/Games/ByGameID").toString(); QString gameNamePrefix = SETTINGS.value("TheGamesDB/ByGameName", "/v1.1/Games/ByGameName").toString(); //If user submits gameID, use that if (gameID != "") url.setUrl(apiURL + gameIDPrefix + "?apikey=" + TheGamesDBAPIKey + "&id=" + gameID + apiFilter); else url.setUrl(apiURL + gameNamePrefix + "?apikey=" + TheGamesDBAPIKey + "&name=" + searchName + apiFilter); QString data = getUrlContents(url); QJsonDocument document = QJsonDocument::fromJson(data.toUtf8()); QJsonObject json = document.object(); if (json.value("code").toInt() != 200 && json.value("code").toInt() != 0) { QString status = json.value("status").toString(); QString message; message = QString(tr("The following error from TheGamesDB occured while downloading:")) + "

" + status + "

"; showError(message); if (force) parent->setEnabled(true); return; } QJsonValue games = json.value("data").toObject().value("games"); QJsonArray gamesArray = games.toArray(); int count = 0, found = 0; foreach (QJsonValue game, gamesArray) { QJsonValue title = game.toObject().value("game_title"); if (force) { //from user dialog QJsonValue date = game.toObject().value("release_date"); QString check = "Game: " + title.toString(); check.remove(QRegExp(QString("[^A-Za-z 0-9 \\.,\\?'""!@#\\$%\\^&\\*\\") + "(\\)-_=\\+;:<>\\/\\\\|\\}\\{\\[\\]`~é]*")); if (date.toString() != "") check += "\n" + tr("Released on: ") + date.toString(); check += "\n\n" + tr("Does this look correct?"); int answer = QMessageBox::question(parent, QObject::tr("Game Information Download"), check, QMessageBox::Yes | QMessageBox::No); if (answer == QMessageBox::Yes) { found = count; updated = true; break; } } else { //We only want one game, so search for a perfect match in the GameTitle element. //Otherwise this will default to 0 (the first game found) if(title.toString().toLower() == searchName.toLower()) found = count; } count++; } if (!force || updated) { QJsonObject foundGame = gamesArray.at(found).toObject(); QJsonObject saveData; QString gameID = QString::number(foundGame.value("id").toInt()); QJsonObject boxart = json.value("include").toObject().value("boxart").toObject(); QString thumbURL = boxart.value("base_url").toObject().value("thumb").toString(); QJsonArray imgArray = boxart.value("data").toObject().value(gameID).toArray(); QString frontImg = ""; foreach (QJsonValue img, imgArray) { QString type = img.toObject().value("type").toString(); QString side = img.toObject().value("side").toString(); QString filename = img.toObject().value("filename").toString(); if (type == "boxart" && side == "front") frontImg = thumbURL + filename; } //Convert IDs from API to text names QString genresString = convertIDs(foundGame, "genres", "Genres"); QString developerString = convertIDs(foundGame, "developers", "Developers"); QString publisherString = convertIDs(foundGame, "publishers", "Publishers"); QString players = QString::number(foundGame.value("players").toInt()); if (players == "0") players = ""; saveData.insert("game_title", foundGame.value("game_title").toString()); saveData.insert("release_date", foundGame.value("release_date").toString()); saveData.insert("rating", foundGame.value("rating").toString()); saveData.insert("overview", foundGame.value("overview").toString()); saveData.insert("players", players); saveData.insert("boxart", frontImg); saveData.insert("genres", genresString); saveData.insert("developer", developerString); saveData.insert("publisher", publisherString); QJsonDocument document(saveData); file.open(QIODevice::WriteOnly); file.write(document.toJson()); file.close(); } if (force && !updated) { QString message; if (count == 0) message = QObject::tr("No results found."); else message = QObject::tr("No more results found."); QMessageBox::information(parent, QObject::tr("Game Information Download"), message); } } //Get front cover QString boxartURL = ""; QString boxartExt = ""; QString coverFile = gameCache + "/boxart-front."; QFile coverJPG(coverFile + "jpg"); QFile coverPNG(coverFile + "png"); if ((!coverJPG.exists() && !coverPNG.exists()) || (force && updated)) { file.open(QIODevice::ReadOnly); QString data = file.readAll(); file.close(); QJsonDocument document = QJsonDocument::fromJson(data.toUtf8()); QJsonObject json = document.object(); QString boxartURL = json.value("boxart").toString(); if (boxartURL != "") { QUrl url(boxartURL); //Delete current box art QFile::remove(coverFile + "jpg"); QFile::remove(coverFile + "png"); //Check to save as JPG or PNG boxartExt = QFileInfo(boxartURL).completeSuffix().toLower(); QFile cover(coverFile + boxartExt); cover.open(QIODevice::WriteOnly); cover.write(getUrlContents(url)); cover.close(); } } if (updated) QMessageBox::information(parent, QObject::tr("Game Information Download"), QObject::tr("Download Complete!")); if (force) parent->setEnabled(true); } } QByteArray TheGamesDBScraper::getUrlContents(QUrl url) { QNetworkAccessManager *manager = new QNetworkAccessManager; QNetworkRequest request; request.setUrl(url); request.setRawHeader("User-Agent", AppName.toUtf8().constData()); QNetworkReply *reply = manager->get(request); QTimer timer; timer.setSingleShot(true); QEventLoop loop; connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); int time = SETTINGS.value("Other/networktimeout", 10).toInt(); if (time == 0) time = 10; time *= 1000; timer.start(time); loop.exec(); if(timer.isActive()) { //Got reply timer.stop(); if(reply->error() > 0) showError(reply->errorString()); else return reply->readAll(); } else //Request timed out showError(tr("Request timed out. Check your network settings.")); return QByteArray(); } void TheGamesDBScraper::showError(QString error) { QString question = "\n\n" + tr("Continue scraping information?"); if (force) QMessageBox::information(parent, tr("Network Error"), error); else { int answer = QMessageBox::question(parent, tr("Network Error"), error + question, QMessageBox::Yes | QMessageBox::No); if (answer == QMessageBox::No) keepGoing = false; } } void TheGamesDBScraper::updateListCache(QFile *file, QString list) { if (keepGoing) { QUrl url; QString apiURL = SETTINGS.value("TheGamesDB/url", "https://api.thegamesdb.net/").toString(); QString prefix; if (list == "Genres") prefix = SETTINGS.value("TheGamesDB/Genres", "/v1/Genres").toString(); else if (list == "Developers") prefix = SETTINGS.value("TheGamesDB/Developers", "/v1/Developers").toString(); else if (list == "Publishers") prefix = SETTINGS.value("TheGamesDB/Publishers", "/v1/Publishers").toString(); url.setUrl(apiURL + prefix + "?apikey=" + TheGamesDBAPIKey); QString data = getUrlContents(url); QJsonDocument document = QJsonDocument::fromJson(data.toUtf8()); QJsonDocument result(document.object().value("data").toObject().value(list.toLower()).toObject()); file->open(QIODevice::WriteOnly); file->write(result.toJson()); file->close(); } } mupen64plus-qt-1.15/src/roms/thegamesdbscraper.h000066400000000000000000000043661413256700400217020ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef THEGAMESDBSCRAPER_H #define THEGAMESDBSCRAPER_H #include #include class QUrl; class TheGamesDBScraper : public QObject { Q_OBJECT public: explicit TheGamesDBScraper(QWidget *parent = 0, bool force = false); void deleteGameInfo(QString fileName, QString identifier); void downloadGameInfo(QString identifier, QString searchName, QString gameID = ""); private: QString convertIDs(QJsonObject foundGame, QString typeName, QString listName); QByteArray getUrlContents(QUrl url); void showError(QString error); void updateListCache(QFile *file, QString list); bool force; bool keepGoing; QWidget *parent; }; #endif // THEGAMESDBSCRAPER_H mupen64plus-qt-1.15/src/views/000077500000000000000000000000001413256700400162125ustar00rootroot00000000000000mupen64plus-qt-1.15/src/views/gridview.cpp000066400000000000000000000307511413256700400205440ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "gridview.h" #include "../global.h" #include "../common.h" #include "widgets/clickablewidget.h" #include #include #include #include #include #include #include GridView::GridView(QWidget *parent) : QScrollArea(parent) { this->parent = parent; setObjectName("gridView"); setStyleSheet("#gridView { border: none; }"); setBackgroundRole(QPalette::Dark); setAlignment(Qt::AlignHCenter); setHidden(true); setGridBackground(); gridWidget = new QWidget(this); gridWidget->setObjectName("gridWidget"); gridWidget->setStyleSheet("#gridWidget { background: transparent; }"); setWidget(gridWidget); gridLayout = new QGridLayout(gridWidget); gridLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); gridLayout->setRowMinimumHeight(0, 10); gridWidget->setLayout(gridLayout); gridCurrent = false; currentGridRom = 0; } void GridView::addToGridView(Rom *currentRom, int count, bool ddEnabled) { if (ddEnabled) // Add place for "No Cart" entry count++; ClickableWidget *gameGridItem = new ClickableWidget(gridWidget); gameGridItem->setMinimumWidth(getGridSize("width")); gameGridItem->setMaximumWidth(getGridSize("width")); gameGridItem->setGraphicsEffect(getShadow(false)); gameGridItem->setContextMenuPolicy(Qt::CustomContextMenu); //Assign ROM data to widget for use in click events gameGridItem->setProperty("fileName", currentRom->fileName); gameGridItem->setProperty("directory", currentRom->directory); if (currentRom->goodName == getTranslation("Unknown ROM") || currentRom->goodName == getTranslation("Requires catalog file")) gameGridItem->setProperty("search", currentRom->internalName); else gameGridItem->setProperty("search", currentRom->goodName); gameGridItem->setProperty("romMD5", currentRom->romMD5); gameGridItem->setProperty("zipFile", currentRom->zipFile); QGridLayout *gameGridLayout = new QGridLayout(gameGridItem); gameGridLayout->setColumnStretch(0, 1); gameGridLayout->setColumnStretch(3, 1); gameGridLayout->setRowMinimumHeight(1, getImageSize("Grid").height()); QLabel *gridImageLabel = new QLabel(gameGridItem); gridImageLabel->setMinimumHeight(getImageSize("Grid").height()); gridImageLabel->setMinimumWidth(getImageSize("Grid").width()); QPixmap image; if (currentRom->imageExists) { //Use uniform aspect ratio to account for fluctuations in TheGamesDB box art Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio; //Don't warp aspect ratio though if image is too far away from standard size (JP box art) double aspectRatio = double(currentRom->image.width()) / currentRom->image.height(); if (aspectRatio < 1.1 || aspectRatio > 1.8) aspectRatioMode = Qt::KeepAspectRatio; image = currentRom->image.scaled(getImageSize("Grid"), aspectRatioMode, Qt::SmoothTransformation); } else { if (ddEnabled && count == 0) image = QPixmap(":/images/no-cart.png").scaled(getImageSize("Grid"), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); else image = QPixmap(":/images/not-found.png").scaled(getImageSize("Grid"), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } gridImageLabel->setPixmap(image); gridImageLabel->setAlignment(Qt::AlignCenter); gameGridLayout->addWidget(gridImageLabel, 1, 1); if (SETTINGS.value("Grid/label","true") == "true") { QLabel *gridTextLabel = new QLabel(gameGridItem); //Don't allow label to be wider than image gridTextLabel->setMaximumWidth(getImageSize("Grid").width()); QString text = ""; QString labelText = SETTINGS.value("Grid/labeltext","Filename").toString(); text = getRomInfo(labelText, currentRom); if (ddEnabled && count == 0) text = tr("No Cart"); gridTextLabel->setText(text); QString textHex = getColor(SETTINGS.value("Grid/labelcolor","White").toString()).name(); int fontSize = getGridSize("font"); gridTextLabel->setStyleSheet("QLabel { font-weight: bold; color: " + textHex + "; font-size: " + QString::number(fontSize) + "px; }"); gridTextLabel->setWordWrap(true); gridTextLabel->setAlignment(Qt::AlignHCenter | Qt::AlignTop); gameGridLayout->addWidget(gridTextLabel, 2, 1); } gameGridItem->setLayout(gameGridLayout); gameGridItem->setMinimumHeight(gameGridItem->sizeHint().height()); int columnCount; if (SETTINGS.value("Grid/autocolumns","true").toString() == "true") columnCount = viewport()->width() / (getGridSize("width") + 10); else columnCount = SETTINGS.value("Grid/columncount", "4").toInt(); if (columnCount == 0) columnCount = 1; gridLayout->addWidget(gameGridItem, count / columnCount + 1, count % columnCount + 1); gridWidget->adjustSize(); connect(gameGridItem, SIGNAL(singleClicked(QWidget*)), this, SLOT(highlightGridWidget(QWidget*))); connect(gameGridItem, SIGNAL(doubleClicked(QWidget*)), parent, SLOT(launchRomFromWidget(QWidget*))); connect(gameGridItem, SIGNAL(arrowPressed(QWidget*, QString)), this, SLOT(selectNextRom(QWidget*, QString))); connect(gameGridItem, SIGNAL(enterPressed(QWidget*)), parent, SLOT(launchRomFromWidget(QWidget*))); connect(gameGridItem, SIGNAL(customContextMenuRequested(const QPoint &)), parent, SLOT(showRomMenu(const QPoint &))); } int GridView::getCurrentRom() { return currentGridRom; } QString GridView::getCurrentRomInfo(QString infoName) { const char *property = infoName.toUtf8().constData(); if (gridLayout->count() > currentGridRom) return gridLayout->itemAt(currentGridRom)->widget()->property(property).toString(); return ""; } QWidget *GridView::getCurrentRomWidget() { return gridLayout->itemAt(currentGridRom)->widget(); } bool GridView::hasSelectedRom() { return gridCurrent; } void GridView::highlightGridWidget(QWidget *current) { current->setFocus(); //Set all to inactive shadow QLayoutItem *gridItem; for (int item = 0; (gridItem = gridLayout->itemAt(item)) != nullptr; item++) { gridItem->widget()->setGraphicsEffect(getShadow(false)); if (gridItem->widget() == current) currentGridRom = item; } //Set current to active shadow current->setGraphicsEffect(getShadow(true)); gridCurrent = true; emit gridItemSelected(true); } void GridView::keyPressEvent(QKeyEvent *event) { if ((event->key() == Qt::Key_Down || event->key() == Qt::Key_Right) && gridLayout->count() > 0) { highlightGridWidget(gridLayout->itemAt(0)->widget()); ensureWidgetVisible(gridLayout->itemAt(0)->widget()); } else QScrollArea::keyPressEvent(event); } void GridView::resetView() { QLayoutItem *gridItem; while ((gridItem = gridLayout->takeAt(0)) != nullptr) { delete gridItem->widget(); delete gridItem; } gridCurrent = false; } void GridView::resizeEvent(QResizeEvent *event) { int check = event->size().width() / (getGridSize("width") + 10); bool autoAdjustColumns = SETTINGS.value("Grid/autocolumns","true").toString() == "true"; if (autoAdjustColumns && check != autoColumnCount && check != 0) { autoColumnCount = check; updateGridColumns(event->size().width()); } else QScrollArea::resizeEvent(event); } void GridView::saveGridPosition() { positionx = horizontalScrollBar()->value(); positiony = verticalScrollBar()->value(); if (gridCurrent) savedGridRom = currentGridRom; else savedGridRom = -1; savedGridRomFilename = getCurrentRomInfo("fileName"); } void GridView::selectNextRom(QWidget* current, QString keypress) { int columnCount; if (SETTINGS.value("Grid/autocolumns","true").toString() == "true") columnCount = autoColumnCount; else columnCount = SETTINGS.value("Grid/columncount", "4").toInt(); int offset = 0; if (keypress == "UP") offset = columnCount * -1; else if (keypress == "DOWN") offset = columnCount; else if (keypress == "RIGHT") offset = 1; else if (keypress == "LEFT") offset = -1; QLayoutItem *gridItem; for (int item = 0; (gridItem = gridLayout->itemAt(item)) != nullptr; item++) { if (gridItem->widget() == current && item + offset >= 0 && gridLayout->itemAt(item + offset) != nullptr) { ensureWidgetVisible(gridLayout->itemAt(item + offset)->widget()); highlightGridWidget(gridLayout->itemAt(item + offset)->widget()); } } } void GridView::setGridBackground() { QString theme = SETTINGS.value("Grid/theme", "Normal").toString(); if (theme == "Light") setStyleSheet("#gridView { border: none; background: #FFF; } #gridWidget { background: transparent; }"); else if (theme == "Dark") setStyleSheet("#gridView { border: none; background: #222; } #gridWidget { background: transparent; }"); else setStyleSheet("#gridView { border: none; }"); QString background = SETTINGS.value("Grid/background", "").toString(); if (background != "") { QFile backgroundFile(background); if (backgroundFile.exists() && !QFileInfo(backgroundFile).isDir()) setStyleSheet(QString() + "#gridView { " + "border: none; " + "background: url(" + background + "); " + "background-attachment: fixed; " + "background-position: top center; " + "} #gridWidget { background: transparent; } " ); } } void GridView::setGridPosition() { horizontalScrollBar()->setValue(positionx); verticalScrollBar()->setValue(positiony); //Restore selected ROM if it is in the same position if (savedGridRom != -1 && gridLayout->count() > savedGridRom) { QWidget *checkWidget = gridLayout->itemAt(savedGridRom)->widget(); if (checkWidget->property("fileName").toString() == savedGridRomFilename) highlightGridWidget(checkWidget); } if (SETTINGS.value("Grid/autocolumns","true").toString() == "true") setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); else setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); } void GridView::updateGridColumns(int width) { int columnCount = width / (getGridSize("width") + 10); int gridCount = gridLayout->count(); QList gridItems; for (int count = 0; count < gridCount; count++) gridItems << gridLayout->takeAt(0)->widget(); int count = 0; foreach(QWidget *gridItem, gridItems) { gridLayout->addWidget(gridItem, count / columnCount + 1, count % columnCount + 1); count++; } gridWidget->adjustSize(); } mupen64plus-qt-1.15/src/views/gridview.h000066400000000000000000000051751413256700400202130ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef GRIDVIEW_H #define GRIDVIEW_H #include class QGridLayout; struct Rom; class GridView : public QScrollArea { Q_OBJECT public: explicit GridView(QWidget *parent = 0); void addToGridView(Rom *currentRom, int count, bool ddEnabled); int getCurrentRom(); QString getCurrentRomInfo(QString infoName); QWidget *getCurrentRomWidget(); bool hasSelectedRom(); void resetView(); void saveGridPosition(); void setGridBackground(); protected: void keyPressEvent(QKeyEvent *event); void resizeEvent(QResizeEvent *event); signals: void gridItemSelected(bool active); private: void updateGridColumns(int width); int autoColumnCount; int currentGridRom; bool gridCurrent; int savedGridRom; QString savedGridRomFilename; int positionx; int positiony; QGridLayout *gridLayout; QWidget *gridWidget; QWidget *parent; private slots: void highlightGridWidget(QWidget *current); void selectNextRom(QWidget *current, QString keypress); void setGridPosition(); }; #endif // GRIDVIEW_H mupen64plus-qt-1.15/src/views/listview.cpp000066400000000000000000000253631413256700400205750ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "listview.h" #include "../global.h" #include "../common.h" #include "widgets/clickablewidget.h" #include #include #include #include #include #include #include #include ListView::ListView(QWidget *parent) : QScrollArea(parent) { this->parent = parent; setObjectName("listView"); setWidgetResizable(true); setHidden(true); setListBackground(); listWidget = new QWidget(this); listWidget->setObjectName("listWidget"); setWidget(listWidget); listLayout = new QVBoxLayout(listWidget); listLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); listWidget->setLayout(listLayout); listCurrent = false; currentListRom = 0; } void ListView::addToListView(Rom *currentRom, int count, bool ddEnabled) { if (ddEnabled) // Add place for "No Cart" entry count++; QStringList visible = SETTINGS.value("List/columns", "Filename|Internal Name|Size").toString().split("|"); if (visible.join("") == "" && SETTINGS.value("List/displaycover", "") != "true") //Otherwise no columns, so don't bother populating return; ClickableWidget *gameListItem = new ClickableWidget(listWidget); gameListItem->setContentsMargins(0, 0, 20, 0); gameListItem->setContextMenuPolicy(Qt::CustomContextMenu); if (SETTINGS.value("List/theme","Light").toString() == "Dark") gameListItem->setStyleSheet("color:#EEE;"); //Assign ROM data to widget for use in click events gameListItem->setProperty("fileName", currentRom->fileName); gameListItem->setProperty("directory", currentRom->directory); if (currentRom->goodName == getTranslation("Unknown ROM") || currentRom->goodName == getTranslation("Requires catalog file")) gameListItem->setProperty("search", currentRom->internalName); else gameListItem->setProperty("search", currentRom->goodName); gameListItem->setProperty("romMD5", currentRom->romMD5); gameListItem->setProperty("zipFile", currentRom->zipFile); QGridLayout *gameListLayout = new QGridLayout(gameListItem); gameListLayout->setColumnStretch(3, 1); //Add image if (SETTINGS.value("List/displaycover", "") == "true") { QLabel *listImageLabel = new QLabel(gameListItem); listImageLabel->setMinimumHeight(getImageSize("List").height()); listImageLabel->setMinimumWidth(getImageSize("List").width()); QPixmap image; if (currentRom->imageExists) image = currentRom->image.scaled(getImageSize("List"), Qt::KeepAspectRatio, Qt::SmoothTransformation); else { if (ddEnabled && count == 0) image = QPixmap(":/images/no-cart.png").scaled(getImageSize("List"), Qt::KeepAspectRatio, Qt::SmoothTransformation); else image = QPixmap(":/images/not-found.png").scaled(getImageSize("List"), Qt::KeepAspectRatio, Qt::SmoothTransformation); } listImageLabel->setPixmap(image); listImageLabel->setAlignment(Qt::AlignCenter); gameListLayout->addWidget(listImageLabel, 0, 1); } //Create text label QLabel *listTextLabel = new QLabel("", gameListItem); QString listText = ""; int i = 0; foreach (QString current, visible) { QString addition = ""; if (i == 0 && SETTINGS.value("List/firstitemheader","true") == "true") addition += "

"; else addition += "
" + getTranslation(current) + ": "; addition += getRomInfo(current, currentRom, true); if (i == 0 && SETTINGS.value("List/firstitemheader","true") == "true") addition += "

"; else addition += ""; if (addition.right(12) != ":
") listText += addition; i++; } if (ddEnabled && count == 0) listText = "

" + tr("No Cart") + "

"; listTextLabel->setText(listText); listTextLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); listTextLabel->setWordWrap(true); QFont font = listTextLabel->font(); font.setPointSize(getTextSize()); listTextLabel->setFont(font); gameListLayout->addWidget(listTextLabel, 0, 3); gameListLayout->setColumnMinimumWidth(0, 20); gameListLayout->setColumnMinimumWidth(2, 10); gameListItem->setLayout(gameListLayout); if (count != 0) { QFrame *separator = new QFrame(); separator->setFrameShape(QFrame::HLine); separator->setStyleSheet("margin:0;padding:0;"); QPalette palette = separator->palette(); if (SETTINGS.value("List/theme","Light").toString() == "Dark") palette.setColor(QPalette::Window, Qt::black); else palette.setColor(QPalette::Window, Qt::gray); separator->setPalette(palette); listLayout->addWidget(separator); } listLayout->addWidget(gameListItem); connect(gameListItem, SIGNAL(singleClicked(QWidget*)), this, SLOT(highlightListWidget(QWidget*))); connect(gameListItem, SIGNAL(doubleClicked(QWidget*)), parent, SLOT(launchRomFromWidget(QWidget*))); connect(gameListItem, SIGNAL(arrowPressed(QWidget*, QString)), this, SLOT(selectNextRom(QWidget*, QString))); connect(gameListItem, SIGNAL(enterPressed(QWidget*)), parent, SLOT(launchRomFromWidget(QWidget*))); connect(gameListItem, SIGNAL(customContextMenuRequested(const QPoint &)), parent, SLOT(showRomMenu(const QPoint &))); } int ListView::getCurrentRom() { return currentListRom; } QString ListView::getCurrentRomInfo(QString infoName) { const char *property = infoName.toUtf8().constData(); if (listLayout->count() > currentListRom) return listLayout->itemAt(currentListRom)->widget()->property(property).toString(); return ""; } QWidget *ListView::getCurrentRomWidget() { return listLayout->itemAt(currentListRom)->widget(); } bool ListView::hasSelectedRom() { return listCurrent; } void ListView::highlightListWidget(QWidget *current) { current->setFocus(); QLayoutItem *listItem; for (int item = 0; (listItem = listLayout->itemAt(item)) != nullptr; item++) { if (listItem->widget() == current) currentListRom = item; } //Give current left margin to stand out //Delay with QTimer so right-click menu appears correctly QTimer::singleShot(5, this, SLOT(highlightListWidgetSetMargin())); listCurrent = true; emit listItemSelected(true); } void ListView::highlightListWidgetSetMargin() { //Reset all margins QLayoutItem *listItem; for (int item = 0; (listItem = listLayout->itemAt(item)) != nullptr; item++) listItem->widget()->setContentsMargins(0, 0, 20, 0); getCurrentRomWidget()->setContentsMargins(20, 0, 0, 0); } void ListView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Down && listLayout->count() > 0) { highlightListWidget(listLayout->itemAt(0)->widget()); ensureWidgetVisible(listLayout->itemAt(0)->widget()); } else QScrollArea::keyPressEvent(event); } void ListView::resetView() { QLayoutItem *listItem; while ((listItem = listLayout->takeAt(0)) != nullptr) { delete listItem->widget(); delete listItem; } listCurrent = false; } void ListView::saveListPosition() { positionx = horizontalScrollBar()->value(); positiony = verticalScrollBar()->value(); if (listCurrent) savedListRom = currentListRom; else savedListRom = -1; savedListRomFilename = getCurrentRomInfo("fileName"); } void ListView::selectNextRom(QWidget* current, QString keypress) { int offset = 0; if (keypress == "UP" || keypress == "LEFT") offset = -2; else if (keypress == "DOWN" || keypress == "RIGHT") offset = 2; QLayoutItem *listItem; for (int item = 0; (listItem = listLayout->itemAt(item)) != nullptr; item++) { if (listItem->widget() == current && item + offset >= 0 && listLayout->itemAt(item + offset) != nullptr) { ensureWidgetVisible(listLayout->itemAt(item + offset)->widget()); highlightListWidget(listLayout->itemAt(item + offset)->widget()); } } } void ListView::setListBackground() { if (SETTINGS.value("List/theme","Light").toString() == "Dark") setStyleSheet("#listView { border: none; background: #222; } #listWidget { background: transparent; }"); else setStyleSheet("#listView { border: none; background: #FFF; } #listWidget { background: transparent; }"); } void ListView::setListPosition() { horizontalScrollBar()->setValue(positionx); verticalScrollBar()->setValue(positiony); //Restore selected ROM if it is in the same position if (savedListRom != -1 && listLayout->count() > savedListRom) { QWidget *checkWidget = listLayout->itemAt(savedListRom)->widget(); if (checkWidget->property("fileName").toString() == savedListRomFilename) highlightListWidget(checkWidget); } } mupen64plus-qt-1.15/src/views/listview.h000066400000000000000000000050731413256700400202360ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef LISTVIEW_H #define LISTVIEW_H #include class QVBoxLayout; struct Rom; class ListView : public QScrollArea { Q_OBJECT public: explicit ListView(QWidget *parent = 0); void addToListView(Rom *currentRom, int count, bool ddEnabled); int getCurrentRom(); QString getCurrentRomInfo(QString infoName); QWidget *getCurrentRomWidget(); bool hasSelectedRom(); void resetView(); void saveListPosition(); void setListBackground(); protected: void keyPressEvent(QKeyEvent *event); signals: void listItemSelected(bool active); private: int currentListRom; bool listCurrent; int savedListRom; QString savedListRomFilename; int positionx; int positiony; QVBoxLayout *listLayout; QWidget *listWidget; QWidget *parent; private slots: void highlightListWidget(QWidget *current); void highlightListWidgetSetMargin(); void selectNextRom(QWidget *current, QString keypress); void setListPosition(); }; #endif // LISTVIEW_H mupen64plus-qt-1.15/src/views/tableview.cpp000066400000000000000000000270561413256700400207120ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "tableview.h" #include "../global.h" #include "../common.h" #include "widgets/treewidgetitem.h" #include #include #include #include #include #include #include #include TableView::TableView(QWidget *parent) : QTreeWidget(parent) { this->parent = parent; setWordWrap(false); setAllColumnsShowFocus(true); setRootIsDecorated(false); setSortingEnabled(true); setContextMenuPolicy(Qt::CustomContextMenu); setStyleSheet("QTreeView { border: none; } QTreeView::item { height: 25px; }"); headerView = new QHeaderView(Qt::Horizontal, this); setHeader(headerView); setHidden(true); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), parent, SLOT(showRomMenu(const QPoint &))); connect(headerView, SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(saveSortOrder(int,Qt::SortOrder))); } void TableView::addNoCartRow() { QStringList visible = SETTINGS.value("Table/columns", "Filename|Size").toString().split("|"); fileItem = new TreeWidgetItem(this); if (visible.at(0) == "Game Cover") { fileItem->setText(6, " " + tr("No Cart")); fileItem->setForeground(6, QBrush(Qt::gray)); } else { fileItem->setText(5, " " + tr("No Cart")); fileItem->setForeground(5, QBrush(Qt::gray)); } addTopLevelItem(fileItem); } void TableView::addToTableView(Rom *currentRom) { QStringList visible = SETTINGS.value("Table/columns", "Filename|Size").toString().split("|"); if (visible.join("") == "") //Otherwise no columns, so don't bother populating return; fileItem = new TreeWidgetItem(this); //Filename for launching ROM fileItem->setText(0, currentRom->fileName); //Directory ROM is located in fileItem->setText(1, currentRom->directory); //GoodName or Internal Name for searching if (currentRom->goodName == getTranslation("Unknown ROM") || currentRom->goodName == getTranslation("Requires catalog file")) fileItem->setText(2, currentRom->internalName); else fileItem->setText(2, currentRom->goodName); //MD5 for cache info fileItem->setText(3, currentRom->romMD5.toLower()); //Zip file fileItem->setText(4, currentRom->zipFile); int i = 5, c = 0; bool addImage = false; foreach (QString current, visible) { QString text = getRomInfo(current, currentRom); fileItem->setText(i, text); if (current == "GoodName" || current == "Game Title") { if (text == getTranslation("Unknown ROM") || text == getTranslation("Requires catalog file") || text == getTranslation("Not found")) { fileItem->setForeground(i, QBrush(Qt::gray)); fileItem->setData(i, Qt::UserRole, "ZZZ"); //end of sorting } else fileItem->setData(i, Qt::UserRole, text); } if (current == "Size") fileItem->setData(i, Qt::UserRole, currentRom->sortSize); if (current == "Release Date") fileItem->setData(i, Qt::UserRole, currentRom->sortDate); if (current == "Game Cover") { c = i; addImage = true; } QStringList center, right; center << "MD5" << "CRC1" << "CRC2" << "Rumble" << "ESRB" << "Genre" << "Publisher" << "Developer"; right << "Size" << "Players" << "Save Type" << "Release Date" << "Rating"; if (center.contains(current)) fileItem->setTextAlignment(i, Qt::AlignHCenter | Qt::AlignVCenter); else if (right.contains(current)) fileItem->setTextAlignment(i, Qt::AlignRight | Qt::AlignVCenter); i++; } addTopLevelItem(fileItem); if (currentRom->imageExists && addImage) { QPixmap image(currentRom->image.scaled(getImageSize("Table"), Qt::KeepAspectRatio, Qt::SmoothTransformation)); QWidget *imageContainer = new QWidget(this); QGridLayout *imageGrid = new QGridLayout(imageContainer); QLabel *imageLabel = new QLabel(imageContainer); imageLabel->setPixmap(image); imageGrid->addWidget(imageLabel, 1, 1); imageGrid->setColumnStretch(0, 1); imageGrid->setColumnStretch(2, 1); imageGrid->setRowStretch(0, 1); imageGrid->setRowStretch(2, 1); imageGrid->setContentsMargins(0,0,0,0); imageContainer->setLayout(imageGrid); setItemWidget(fileItem, c, imageContainer); } } QString TableView::getCurrentRomInfo(QString infoName) { int index = getTableDataIndexFromName(infoName); return QVariant(currentItem()->data(index, 0)).toString(); } bool TableView::hasSelectedRom() { return currentItem() != nullptr; } void TableView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) emit enterPressed(); else if (event->key() == Qt::Key_Down && selectedItems().count() == 0) { setCurrentItem(topLevelItem(0)); emit tableActive(); } else if (event->key() == Qt::Key_Down && selectedItems().count() == 1) { int current = indexOfTopLevelItem(selectedItems().at(0)); if (current < topLevelItemCount() - 1) setCurrentItem(topLevelItem(current + 1)); } else if (event->key() == Qt::Key_Up && selectedItems().count() == 1) { int current = indexOfTopLevelItem(selectedItems().at(0)); if (current > 0) setCurrentItem(topLevelItem(current - 1)); } else QTreeWidget::keyPressEvent(event); } void TableView::resetView(bool imageUpdated) { QStringList tableVisible = SETTINGS.value("Table/columns", "Filename|Size").toString().split("|"); QStringList translations; foreach (QString header, tableVisible) translations << getTranslation(header); int hidden = 5; saveColumnWidths(); QStringList widths = SETTINGS.value("Table/width", "").toString().split("|"); headerLabels.clear(); headerLabels << "" << "" << "" << "" << "" << translations; //First 5 blank for hidden columns //Remove Game Cover title for aesthetics for (int i = 0; i < headerLabels.size(); i++) if (headerLabels.at(i) == getTranslation("Game Cover")) headerLabels.replace(i, ""); setColumnCount(headerLabels.size()); setHeaderLabels(headerLabels); headerView->setSortIndicatorShown(false); double height = 0, width = 0; if (tableVisible.contains("Game Cover")) { //Get optimal height/width for cover column height = getImageSize("Table").height() * 1.1; width = getImageSize("Table").width() * 1.2; setStyleSheet("QTreeView { border: none; } QTreeView::item { height: " + QString::number(static_cast(std::round(height))) + "px; }"); } else setStyleSheet("QTreeView { border: none; } QTreeView::item { height: 25px; }"); QStringList sort = SETTINGS.value("Table/sort", "").toString().split("|"); if (sort.size() == 2) { if (sort[1] == "descending") headerView->setSortIndicator(tableVisible.indexOf(sort[0]) + hidden, Qt::DescendingOrder); else headerView->setSortIndicator(tableVisible.indexOf(sort[0]) + hidden, Qt::AscendingOrder); } setColumnHidden(0, true); //Hidden filename for launching emulator setColumnHidden(1, true); //Hidden directory of ROM location setColumnHidden(2, true); //Hidden goodname for searching setColumnHidden(3, true); //Hidden md5 for cache info setColumnHidden(4, true); //Hidden column for zip file int i = hidden; foreach (QString current, tableVisible) { if (i == hidden) { int c = i; if (current == "Game Cover") c++; //If first column is game cover, use next column if (SETTINGS.value("Table/stretchfirstcolumn", "true") == "true") header()->setSectionResizeMode(c, QHeaderView::Stretch); else header()->setSectionResizeMode(c, QHeaderView::Interactive); } if (widths.size() == tableVisible.size()) setColumnWidth(i, widths[i - hidden].toInt()); else setColumnWidth(i, getDefaultWidth(current, static_cast(std::round(width)))); //Overwrite saved value if switching image sizes if (imageUpdated && current == "Game Cover") setColumnWidth(i, static_cast(std::round(width))); i++; } } void TableView::saveColumnWidths() { QStringList widths; for (int i = 5; i < columnCount(); i++) { widths << QString::number(columnWidth(i)); } if (widths.size() > 0) SETTINGS.setValue("Table/width", widths.join("|")); } void TableView::saveSortOrder(int column, Qt::SortOrder order) { QString columnName = headerLabels.value(column); if (order == Qt::DescendingOrder) SETTINGS.setValue("Table/sort", columnName + "|descending"); else SETTINGS.setValue("Table/sort", columnName + "|ascending"); } void TableView::saveTablePosition() { positionx = horizontalScrollBar()->value(); positiony = verticalScrollBar()->value(); if (selectedItems().count() > 0) { int index = getTableDataIndexFromName("fileName"); savedTableRom = indexOfTopLevelItem(selectedItems().at(0)); savedTableRomFilename = QVariant(topLevelItem(savedTableRom)->data(index, 0)).toString(); } else { savedTableRom = -1; savedTableRomFilename = ""; } } void TableView::setTablePosition() { horizontalScrollBar()->setValue(positionx); verticalScrollBar()->setValue(positiony); //Restore selected ROM if it is in the same position if (savedTableRom >= 0) { int index = getTableDataIndexFromName("fileName"); QString checkFilename = QVariant(topLevelItem(savedTableRom)->data(index, 0)).toString(); if (savedTableRomFilename == checkFilename) { setCurrentItem(topLevelItem(savedTableRom)); emit tableActive(); } } } mupen64plus-qt-1.15/src/views/tableview.h000066400000000000000000000046721413256700400203560ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef TABLEVIEW_H #define TABLEVIEW_H #include class TreeWidgetItem; struct Rom; class TableView : public QTreeWidget { Q_OBJECT public: explicit TableView(QWidget *parent = 0); void addNoCartRow(); void addToTableView(Rom *currentRom); QString getCurrentRomInfo(QString infoName); bool hasSelectedRom(); void resetView(bool imageUpdated); void saveColumnWidths(); void saveTablePosition(); protected: void keyPressEvent(QKeyEvent *event); signals: void enterPressed(); void tableActive(); private: int positionx; int positiony; int savedTableRom; QString savedTableRomFilename; QStringList headerLabels; QHeaderView *headerView; QWidget *parent; TreeWidgetItem *fileItem; private slots: void saveSortOrder(int column, Qt::SortOrder order); void setTablePosition(); }; #endif // TABLEVIEW_H mupen64plus-qt-1.15/src/views/widgets/000077500000000000000000000000001413256700400176605ustar00rootroot00000000000000mupen64plus-qt-1.15/src/views/widgets/clickablewidget.cpp000066400000000000000000000050761413256700400235110ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "clickablewidget.h" #include ClickableWidget::ClickableWidget(QWidget *parent) : QWidget(parent) { setFocusPolicy(Qt::StrongFocus); } void ClickableWidget::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Up) emit arrowPressed(this, "UP"); else if (event->key() == Qt::Key_Down) emit arrowPressed(this, "DOWN"); else if (event->key() == Qt::Key_Left) emit arrowPressed(this, "LEFT"); else if (event->key() == Qt::Key_Right) emit arrowPressed(this, "RIGHT"); else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) emit enterPressed(this); else QWidget::keyPressEvent(event); } void ClickableWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) emit singleClicked(this); } void ClickableWidget::mouseDoubleClickEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) emit doubleClicked(this); } mupen64plus-qt-1.15/src/views/widgets/clickablewidget.h000066400000000000000000000041711413256700400231510ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef CLICKABLEWIDGET_H #define CLICKABLEWIDGET_H #include class QMouseEvent; class ClickableWidget : public QWidget { Q_OBJECT public: explicit ClickableWidget(QWidget *parent = 0); protected: void keyPressEvent(QKeyEvent* event); void mousePressEvent(QMouseEvent *event); void mouseDoubleClickEvent(QMouseEvent *event); signals: void arrowPressed(QWidget *current, QString keypress); void enterPressed(QWidget *current); void singleClicked(QWidget *current); void doubleClicked(QWidget *current); }; #endif // CLICKABLEWIDGET_H mupen64plus-qt-1.15/src/views/widgets/treewidgetitem.cpp000066400000000000000000000047021413256700400234110ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #include "treewidgetitem.h" //Reimplemented QTreeWidgetItem operator to sort based on both integers and text bool TreeWidgetItem::operator< (const QTreeWidgetItem &other) const { int column = treeWidget()->sortColumn(); if (data(column, Qt::UserRole).toString() != "") { if (data(column, Qt::UserRole).toString().contains(QRegExp("[^\\d]+"))) { QString firstText = data(column, Qt::UserRole).toString(); QString otherText = other.data(column, Qt::UserRole).toString(); return firstText < otherText; } else { int firstNumber = data(column, Qt::UserRole).toInt(); int otherNumber = other.data(column, Qt::UserRole).toInt(); return firstNumber < otherNumber; } } else { QString firstText = text(column); QString otherText = other.text(column); return firstText < otherText; } } mupen64plus-qt-1.15/src/views/widgets/treewidgetitem.h000066400000000000000000000035261413256700400230610ustar00rootroot00000000000000/*** * Copyright (c) 2013, Dan Hasting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the organization 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 HOLDER 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. * ***/ #ifndef TREEWIDGETITEM_H #define TREEWIDGETITEM_H #include class TreeWidgetItem : public QTreeWidgetItem { public: TreeWidgetItem(QTreeWidget *parent) : QTreeWidgetItem(parent) {} bool operator< (const QTreeWidgetItem &other) const; }; #endif // TREEWIDGETITEM_H