pax_global_header00006660000000000000000000000064132323764560014525gustar00rootroot0000000000000052 comment=6ab86664d7a8981c889e3a8597ae041b13023e95 mupen64plus-qt-1.11/000077500000000000000000000000001323237645600142735ustar00rootroot00000000000000mupen64plus-qt-1.11/.gitignore000066400000000000000000000000101323237645600162520ustar00rootroot00000000000000*.user* mupen64plus-qt-1.11/.travis.yml000066400000000000000000000053531323237645600164120ustar00rootroot00000000000000language: cpp sudo: required dist: xenial 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: BxDOO5XecOYTtpivcT/9aIW+oxOLO0U4J96Tc1QTpQOu50qlnXUanPsXvF3tSOari9tFvpxDgRt+OJeYCwL6fw/oG2n8TlGjt7XSEZ6bKZ1n3TW/lESUpE2c9gKFHCvBbepW1addkyBW+T6FmXz2rwDzy/fKSShgeT8NfZaczsPMQe1TWS6alRNtHbMtwNT1gqoH1JdgXFo2zvcZFSVVpILnOtIJ8iPuyjGNgtLd5Tx/6gjtI5MPXM9PsmIjealyYUQ8arnw2jiHx3sRtP03AAZdAhwM3AaWXkjGLGFA2kj/Lxf2Ly+EMbPwLY3Q0lMEZxVU5SdQzs2c/YmbN3DNTIkaOgnJc3Pj/hZB7I+kJuZA2OchmsvIrRoldMNX6sIoABjLiVeTEvDQoFV+F4Ynyg2FEk0R8spb2S6qr3lr7QIH4Y83oeVOzb09SvcFT0ub/Q7OqwGwLOg5+jA3bBTbWLCpDW1eo7SlemSKN4RG8uj91aHDh1eUW8IPd7yg+EhNJnKU4+6vfyaEM39BfME/q2Y7kcw/DCmOoqn6nvifIjxdM3YGJS3vybtQf5+chrpn50Rdo805uddlTjk7Zi8xsBSpUcQOeCywDo19kjeC6WngDRbUFOSvVmhw7elKvU9ghNccdiX3qm+0ylfI7rRjFvSBaQxEf1QtWtzZFcjZkdM= skip_cleanup: true file_glob: true file: build/*/mupen64plus-qt_* on: tags: true mupen64plus-qt-1.11/LICENSE000066400000000000000000000027231323237645600153040ustar00rootroot00000000000000Copyright (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.11/README.md000066400000000000000000000363671323237645600155710ustar00rootroot00000000000000# Mupen64Plus-Qt [![Build Status](https://travis-ci.org/dh4/mupen64plus-qt.svg?branch=master)](https://travis-ci.org/dh4/mupen64plus-qt) A basic cross-platform launcher. This was adapted from CEN64-Qt to work with [Mupen64Plus](http://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. ##### Building with Qt4 Install the Qt4 dependencies instead. On Debian/Ubuntu: ``` # apt-get install qt4-qmake g++ libqt4-dev libquazip-dev libqt4-sql-sqlite ``` Then create the Makefile with qmake and build with make. Run the following commands from the directory that contains mupen64plus-qt.pro: ``` $ qmake-qt4 $ make ``` ##### 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 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. #### 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/\/ Windows: cache folder in same directory as executable -> MD5 of game OSX: /Users/\/Library/Application Support/mupen64plus-qt/cache/\/ You can find the MD5 of a game by using the table or list view and adding "MD5" to the current information. Edit data.xml 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](http://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.11/VERSION000066400000000000000000000000051323237645600153360ustar00rootroot000000000000001.11 mupen64plus-qt-1.11/build-scripts/000077500000000000000000000000001323237645600170575ustar00rootroot00000000000000mupen64plus-qt-1.11/build-scripts/platform/000077500000000000000000000000001323237645600207035ustar00rootroot00000000000000mupen64plus-qt-1.11/build-scripts/platform/linux.sh000077500000000000000000000014421323237645600224020ustar00rootroot00000000000000#!/bin/bash [[ -z $WORKING_DIR ]] && WORKING_DIR=$(pwd) [[ -z $VERSION ]] && VERSION=$(git log --oneline -n 1 | awk '{print $1}') 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.tar.gz" mupen64plus-qt README.txt ;; esac mupen64plus-qt-1.11/build-scripts/platform/osx.sh000077500000000000000000000033741323237645600220620ustar00rootroot00000000000000#!/bin/bash [[ -z $WORKING_DIR ]] && WORKING_DIR=$(pwd) [[ -z $VERSION ]] && VERSION=$(git log --oneline -n 1 | awk '{print $1}') 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" hdiutil attach "mupen64plus-qt_osx_$VERSION.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.dmg" \ -ov "mupen64plus-qt_osx_$VERSION.dmg" mv "mupen64plus-qt_osx_$VERSION.dmg" "build/$TRAVIS_BRANCH" ;; esac mupen64plus-qt-1.11/build-scripts/platform/windows.sh000077500000000000000000000030621323237645600227350ustar00rootroot00000000000000#!/bin/bash [[ -z $WORKING_DIR ]] && WORKING_DIR=$(pwd) [[ -z $VERSION ]] && VERSION=$(git log --oneline -n 1 | awk '{print $1}') 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 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.zip" mupen64plus-qt.exe README.txt ;; esac mupen64plus-qt-1.11/build-scripts/revision.sh000077500000000000000000000004041323237645600212520ustar00rootroot00000000000000#!/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.11/build-scripts/travis-ci.sh000077500000000000000000000006631323237645600213240ustar00rootroot00000000000000#!/bin/bash export WORKING_DIR=$HOME/build/dh4 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.11/dist/000077500000000000000000000000001323237645600152365ustar00rootroot00000000000000mupen64plus-qt-1.11/dist/debian/000077500000000000000000000000001323237645600164605ustar00rootroot00000000000000mupen64plus-qt-1.11/dist/debian/compat000066400000000000000000000000031323237645600176570ustar00rootroot0000000000000010 mupen64plus-qt-1.11/dist/debian/control000066400000000000000000000014411323237645600200630ustar00rootroot00000000000000Source: mupen64plus-qt Section: games Priority: optional Maintainer: Dan Hasting Homepage: https://github.com/dh4/mupen64plus-qt Standards-Version: 3.9.6 Build-Depends: debhelper (>= 10), libqt5sql5-sqlite, libquazip5-dev, qt5-qmake, qtbase5-dev Package: mupen64plus-qt Architecture: any-i386 any-amd64 Depends: mupen64plus-ui-console, libqt5sql5-sqlite, ${misc:Depends}, ${shlibs:Depends} Description: A basic launcher for Mupen64Plus Mupen64Plus-Qt is a basic 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 itsfeatures and usage. mupen64plus-qt-1.11/dist/debian/copyright000066400000000000000000000027231323237645600204170ustar00rootroot00000000000000Copyright (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.11/dist/debian/deb-build.sh000077500000000000000000000006751323237645600206560ustar00rootroot00000000000000#!/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.11/dist/debian/docs000066400000000000000000000000121323237645600173240ustar00rootroot00000000000000README.md mupen64plus-qt-1.11/dist/debian/install000066400000000000000000000003741323237645600200550ustar00rootroot00000000000000mupen64plus-qt usr/games resources/mupen64plus-qt.desktop usr/share/applications resources/mupen64plus-qt.6 usr/share/man/man6 resources/images/mupen64plus.png usr/share/icons/hicolor/128x128/apps/mupen64plus-qt.png mupen64plus-qt-1.11/dist/debian/rules000077500000000000000000000002571323237645600175440ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ override_dh_auto_configure: QT_SELECT=qt5 dh_auto_configure mupen64plus-qt-1.11/dist/debian/source/000077500000000000000000000000001323237645600177605ustar00rootroot00000000000000mupen64plus-qt-1.11/dist/debian/source/format000066400000000000000000000000141323237645600211660ustar00rootroot000000000000003.0 (quilt) mupen64plus-qt-1.11/dist/macosx/000077500000000000000000000000001323237645600165305ustar00rootroot00000000000000mupen64plus-qt-1.11/dist/macosx/mupen64plus.icns000066400000000000000000001474461323237645600216300ustar00rootroot00000000000000icns&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.11/dist/redhat/000077500000000000000000000000001323237645600165055ustar00rootroot00000000000000mupen64plus-qt-1.11/dist/redhat/mupen64plus-qt.spec.in000066400000000000000000000027271323237645600226220ustar00rootroot00000000000000%define _name mupen64plus-qt Name: mupen64plus-qt-rpm Version: @@VERSION@@ Release: 1 Summary: A basic 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 basic 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. %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.11/dist/redhat/rpm-build.sh000077500000000000000000000003121323237645600207330ustar00rootroot00000000000000#!/bin/bash cd `dirname $0` VERSION=`cat ../../VERSION` sed "s/@@VERSION@@/$VERSION/g" mupen64plus-qt.spec.in > mupen64plus-qt.spec spectool -g -R mupen64plus-qt.spec rpmbuild -ba mupen64plus-qt.spec mupen64plus-qt-1.11/dist/windows/000077500000000000000000000000001323237645600167305ustar00rootroot00000000000000mupen64plus-qt-1.11/dist/windows/icon.rc000066400000000000000000000000551323237645600202060ustar00rootroot00000000000000IDI_ICON1 ICON DISCARDABLE "mupen64plus.ico" mupen64plus-qt-1.11/dist/windows/mupen64plus.ico000066400000000000000000002020621323237645600216300ustar00rootroot00000000000000 (   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.11/mupen64plus-qt.pro000066400000000000000000000043701323237645600176450ustar00rootroot00000000000000QT += 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/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/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 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.11/resources/000077500000000000000000000000001323237645600163055ustar00rootroot00000000000000mupen64plus-qt-1.11/resources/README.txt000066400000000000000000000005341323237645600200050ustar00rootroot00000000000000Github 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.11/resources/demos/000077500000000000000000000000001323237645600174145ustar00rootroot00000000000000mupen64plus-qt-1.11/resources/demos/config-editor.jpg000066400000000000000000001641711323237645600226610ustar00rootroot00000000000000JFIF``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.11/resources/demos/list-view.jpg000066400000000000000000002033471323237645600220520ustar00rootroot00000000000000JFIF``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.11/resources/demos/main.jpg000066400000000000000000002267621323237645600210610ustar00rootroot00000000000000JFIF``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.11/resources/demos/table-view.jpg000066400000000000000000001367021323237645600221660ustar00rootroot00000000000000JFIF``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.11/resources/images/000077500000000000000000000000001323237645600175525ustar00rootroot00000000000000mupen64plus-qt-1.11/resources/images/mupen64plus.png000066400000000000000000000265521323237645600224740ustar00rootroot00000000000000PNG  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.11/resources/images/not-found.png000066400000000000000000000042561323237645600222000ustar00rootroot00000000000000PNG  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/Q~}jB|G }}WF@<e"G9%!}]PLÊD9g3gDC>7IN Cm" )= X~J-J. GZ6&Ѫ,( 6-_^ ; ;=eN'_l l7~l^J l^J?n%5-% ^@A dAh؊xypEfC6MԢ 2 4# L  !,. 0r g QZ H 0 3 y*6 .> I et T pC (Ѯg ) QW GJ= /   Dn μ! قn ^CI (4 * fC: u! &C s sA , ɠY ʧ   % Fz |'r 0+ XVA^ [I {u; * F P0 P%d 29I8/&$0Z@3=L3ussB5j-> z0P`?*R/?tV+maY9-eG  ,>Ar4iԇ%%֕iNv* propos de <AppName>About  AboutDialogVersionVersion AboutDialogJditeur de configuration <ParentName> Config Editor ConfigEditorSauvegarderSave ConfigEditorFichierFileDownloadDialog: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.EmulatorHandler2Consulter les journaux... View Log...EmulatorHandlerAttentionWarningEmulatorHandler4Paramtres supplmentairesAdditional SettingsGameSettingsDialogGreffon audio : Audio Plugin:GameSettingsDialogNaviguer... Browse...GameSettingsDialog0Dossier 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 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 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. MainWindow2Consulter les journaux... View Log... MainWindowJun dossier contenant mupen64plus.cfg.(set to a directory with mupen64plus.cfg. MainWindow%1 - MB%1 MBQObject0Site web de <ParentName> websiteQObjectXUne interface lmentaire pour <ParentName>.+A basic launcher for using Qt.QObjectCRC1CRC1QObjectCRC2CRC2QObjectDveloppeur 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 GenreGenreQObjectDpt GitHubGitHub repositoryQObjectNom 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 InterpreterSettingsDialog(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 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.11/resources/locale/mupen64plus-qt_fr.ts000066400000000000000000001342711323237645600234370ustar00rootroot00000000000000 AboutDialog About <AppName> À propos de <AppName> Version Version ConfigEditor <ParentName> Config Editor Éditeur de configuration <ParentName> Save Sauvegarder 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 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... <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... Config Directory Dossier de configuration GridView No Cart Pas de cartouche 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 View Log... Consulter les journaux... &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 &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 &About À &propos 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 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é ! A basic launcher for <ParentName> using Qt. Une interface élémentaire pour <ParentName>. <ParentName> website Site web de <ParentName> GitHub repository Dépôt GitHub 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. Extra Small Très petite Small Petite Medium Moyenne Large Grande Extra Large Très grande 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 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 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.11/resources/mupen64plus-qt.6000066400000000000000000000040621323237645600212220ustar00rootroot00000000000000.\" 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 basic launcher for Mupen64Plus .SH "SYNOPSIS" \fBmupen64plus-qt\fR .SH "DESCRIPTION" \fBMupen64Plus-Qt\fR is a basic 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.11/resources/mupen64plus-qt.desktop000077500000000000000000000004151323237645600225270ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=Mupen64Plus-Qt GenericName=Mupen64Plus Launcher Comment=A basic launcher for Mupen64plus Exec=mupen64plus-qt Icon=mupen64plus-qt Terminal=false Categories=Game;Emulator; Keywords=emulator;mupen;mupen64plus;Nintendo;64; mupen64plus-qt-1.11/resources/mupen64plusqt.qrc000066400000000000000000000004061323237645600215630ustar00rootroot00000000000000 images/mupen64plus.png other/LICENSE images/not-found.png other/VERSION locale/mupen64plus-qt_fr.qm mupen64plus-qt-1.11/resources/other/000077500000000000000000000000001323237645600174265ustar00rootroot00000000000000mupen64plus-qt-1.11/resources/other/LICENSE000066400000000000000000000027171323237645600204420ustar00rootroot00000000000000Copyright (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.11/resources/other/VERSION000077700000000000000000000000001323237645600221672../../VERSIONustar00rootroot00000000000000mupen64plus-qt-1.11/src/000077500000000000000000000000001323237645600150625ustar00rootroot00000000000000mupen64plus-qt-1.11/src/common.cpp000066400000000000000000000340321323237645600170600ustar00rootroot00000000000000/*** * 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 #if QT_VERSION >= 0x050000 #include #include #else #include #include #endif #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 getDataLocation() { QString dataDir; #ifdef Q_OS_WIN dataDir = QCoreApplication::applicationDirPath(); #else #if QT_VERSION >= 0x050000 dataDir = QStandardPaths::writableLocation(QStandardPaths::DataLocation) .replace(ParentName+"/"+AppName,AppNameLower); #else dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation) .remove("data/").replace(ParentName+"/"+AppName,AppNameLower); #endif #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 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; } 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; } } 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; } 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; } 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); } 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); } 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.11/src/common.h000066400000000000000000000057301323237645600165300ustar00rootroot00000000000000/*** * 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 getDataLocation(); QString getRomInfo(QString identifier, const Rom *rom, bool removeWarn = false, bool sort = false); QString getVersion(); #endif // COMMON_H mupen64plus-qt-1.11/src/dialogs/000077500000000000000000000000001323237645600165045ustar00rootroot00000000000000mupen64plus-qt-1.11/src/dialogs/aboutdialog.cpp000066400000000000000000000063731323237645600215130ustar00rootroot00000000000000/*** * 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() + "

"; description += Description; 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.11/src/dialogs/aboutdialog.h000066400000000000000000000040531323237645600211510ustar00rootroot00000000000000/*** * 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.11/src/dialogs/configeditor.cpp000066400000000000000000000111651323237645600216700ustar00rootroot00000000000000/*** * 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 #include #include #if QT_VERSION >= 0x050200 #include #endif 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); #if QT_VERSION >= 0x050200 QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); #else QFont font("Monospace"); font.setStyleHint(QFont::TypeWriter); #endif editorArea->setFont(font); highlighter = new ConfigHighlighter(editorArea->document()); config.open(QIODevice::ReadOnly); editorArea->setPlainText(config.readAll()); config.close(); editorButtonBox = new QDialogButtonBox(Qt::Horizontal, this); editorButtonBox->addButton(tr("Save"), QDialogButtonBox::AcceptRole); editorButtonBox->addButton(QDialogButtonBox::Cancel); editorLayout->addWidget(editorArea, 0, 0); editorLayout->addWidget(editorButtonBox, 1, 0); connect(editorButtonBox, SIGNAL(accepted()), this, SLOT(saveConfig())); connect(editorButtonBox, SIGNAL(rejected()), this, SLOT(close())); setLayout(editorLayout); } 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.11/src/dialogs/configeditor.h000066400000000000000000000051451323237645600213360ustar00rootroot00000000000000/*** * 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 QTextCharFormat; class QTextEdit; 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 *editorButtonBox; QFile config; QGridLayout *editorLayout; QTextEdit *editorArea; ConfigHighlighter *highlighter; private slots: void saveConfig(); }; #endif // CONFIGEDITOR_H mupen64plus-qt-1.11/src/dialogs/downloaddialog.cpp000066400000000000000000000070261323237645600222040ustar00rootroot00000000000000/*** * 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(QDialogButtonBox::Cancel); 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.11/src/dialogs/downloaddialog.h000066400000000000000000000043561323237645600216540ustar00rootroot00000000000000/*** * 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.11/src/dialogs/gamesettingsdialog.cpp000066400000000000000000000126261323237645600230710ustar00rootroot00000000000000/*** * 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()); 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.11/src/dialogs/gamesettingsdialog.h000066400000000000000000000040711323237645600225310ustar00rootroot00000000000000/*** * 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.11/src/dialogs/gamesettingsdialog.ui000066400000000000000000000137131323237645600227220ustar00rootroot00000000000000 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.11/src/dialogs/logdialog.cpp000066400000000000000000000052151323237645600211540ustar00rootroot00000000000000/*** * 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 #if QT_VERSION >= 0x050200 #include #endif 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); #if QT_VERSION >= 0x050200 QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); #else QFont font("Monospace"); font.setStyleHint(QFont::TypeWriter); #endif 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.11/src/dialogs/logdialog.h000066400000000000000000000036401323237645600206210ustar00rootroot00000000000000/*** * 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.11/src/dialogs/settingsdialog.cpp000066400000000000000000000773331323237645600222450ustar00rootroot00000000000000/*** * 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 << "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"); 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"); 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); 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; foreach (QListWidgetItem *item, ui->romList->findItems("*", Qt::MatchWildcard)) romDirectories << item->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" << "Rating" << "Game Cover"; labelOptions << "Game Title" << "Release Date" << "Genre"; sortOptions << "Game Title" << "Release Date" << "ESRB" << "Genre" << "Publisher" << "Developer" << "Rating"; } 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.11/src/dialogs/settingsdialog.h000066400000000000000000000062351323237645600217030ustar00rootroot00000000000000/*** * 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.11/src/dialogs/settingsdialog.ui000066400000000000000000001250261323237645600220710ustar00rootroot00000000000000 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.11/src/emulation/000077500000000000000000000000001323237645600170575ustar00rootroot00000000000000mupen64plus-qt-1.11/src/emulation/emulatorhandler.cpp000066400000000000000000000242531323237645600227570ustar00rootroot00000000000000/*** * 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 #if QT_VERSION >= 0x050000 #include #include #else #include #include #endif EmulatorHandler::EmulatorHandler(QWidget *parent) : QObject(parent) { this->parent = parent; lastOutput = ""; } void EmulatorHandler::checkStatus(int status) { if (status > 0) { 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() { lastOutput.append(emulatorProc->readAllStandardOutput()); } 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 << "--saveoptions"; else 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)), this, SLOT(checkStatus(int))); 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.11/src/emulation/emulatorhandler.h000066400000000000000000000043341323237645600224220ustar00rootroot00000000000000/*** * 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 class QDir; class QProcess; 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); void cleanTemp(); void emitFinished(); void readOutput(); }; #endif // EMULATORHANDLER_H mupen64plus-qt-1.11/src/global.h000066400000000000000000000051261323237645600164770ustar00rootroot00000000000000/*** * 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 Description = QObject::tr("A basic launcher for using Qt.").replace("",ParentName); const QString Copyright = ""; const QString Website = "" + QObject::tr(" website").replace("",ParentName) + ""; const QString GitHub = "" + QObject::tr("GitHub repository") + ""; #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.11/src/main.cpp000066400000000000000000000054341323237645600165200ustar00rootroot00000000000000/*** * 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 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(QApplication::desktop()->screen()->rect().center() - window.rect().center()); } return application.exec(); } mupen64plus-qt-1.11/src/mainwindow.cpp000066400000000000000000001010551323237645600177440ustar00rootroot00000000000000/*** * 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/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 #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(); 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 } //Check default location for mupen64plus.cfg in case user wants to use editor QString configPath = SETTINGS.value("Paths/config", "").toString(); if (configPath == "") { #if QT_VERSION >= 0x050000 QString homeDir = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first(); #else QString homeDir = QDesktopServices::storageLocation(QDesktopServices::HomeLocation); #endif #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(); #if QT_VERSION >= 0x050000 //OSX El Capitan adds it's own full-screen option if (QSysInfo::macVersion() < QSysInfo::MV_ELCAPITAN || QSysInfo::macVersion() == QSysInfo::MV_None) fullScreenAction = viewMenu->addAction(tr("&Full-screen")); else fullScreenAction = new QAction(this); #else fullScreenAction = viewMenu->addAction(tr("&Full-screen")); #endif 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); aboutAction = helpMenu->addAction(tr("&About")); aboutAction->setIcon(QIcon::fromTheme("help-about")); menuBar->addMenu(helpMenu); 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); disabledView->setDisabled(true); disabledLayout = new QVBoxLayout(disabledView); disabledLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); disabledView->setLayout(disabledLayout); QString disabledText = QString("Add a directory containing ROMs under ") + "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); //x and y axis are reversed in Qt4 #if QT_VERSION >= 0x050000 int mousePos = mouseEvent->pos().y(); #else int mousePos = mouseEvent->pos().x(); #endif 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(); } #if QT_VERSION >= 0x050000 //OSX El Capitan adds it's own full-screen option, so handle the event change here if (QSysInfo::macVersion() >= QSysInfo::MV_ELCAPITAN && QSysInfo::macVersion() != QSysInfo::MV_None) { 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); } } } #endif 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::openDeleteDialog() { scraper = new TheGamesDBScraper(this); scraper->deleteGameInfo(getCurrentRomInfoFromView("fileName"), getCurrentRomInfoFromView("romMD5")); delete scraper; romCollection->cachedRoms(); } void MainWindow::openDownloader() { DownloadDialog downloadDialog(getCurrentRomInfoFromView("fileName"), getCurrentRomInfoFromView("search"), getCurrentRomInfoFromView("romMD5"), this); downloadDialog.exec(); romCollection->cachedRoms(); } void MainWindow::openEditor() { 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:") + "
" + "" + "http://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::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") + " (*)"; #if QT_VERSION >= 0x050000 QString searchPath = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first(); #else QString searchPath = QDesktopServices::storageLocation(QDesktopServices::HomeLocation); #endif 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(QDialogButtonBox::Cancel); 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.11/src/mainwindow.h000066400000000000000000000105401323237645600174070ustar00rootroot00000000000000/*** * 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 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 *deleteAction; QAction *downloadAction; QAction *editorAction; QAction *fullScreenAction; QAction *logAction; 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 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(); }; #endif // MAINWINDOW_H mupen64plus-qt-1.11/src/roms/000077500000000000000000000000001323237645600160425ustar00rootroot00000000000000mupen64plus-qt-1.11/src/roms/romcollection.cpp000066400000000000000000000425221323237645600214240ustar00rootroot00000000000000/*** * 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 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 qSort(roms.begin(), roms.end(), romSorter); for (int i = 0; i < roms.size(); i++) emit romAdded(&roms[i], i); //Emit signals for 64DD roms qSort(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) { 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(); 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 qSort(roms.begin(), roms.end(), romSorter); for (int i = 0; i < roms.size(); i++) emit romAdded(&roms[i], i); //Emit signals for 64DD roms qSort(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 cacheDir = getDataLocation() + "/cache"; QString dataFile = cacheDir + "/" + currentRom->romMD5.toLower() + "/data.xml"; QFile file(dataFile); file.open(QIODevice::ReadOnly); QString dom = file.readAll(); file.close(); QDomDocument xml; xml.setContent(dom); QDomNode game = xml.elementsByTagName("Game").at(0); //Remove any non-standard characters QString regex = "[^A-Za-z 0-9 \\.,\\?'""!@#\\$%\\^&\\*\\(\\)-_=\\+;:<>\\/\\\\|\\}\\{\\[\\]`~]*"; currentRom->gameTitle = game.firstChildElement("GameTitle").text().remove(QRegExp(regex)); if (currentRom->gameTitle == "") currentRom->gameTitle = getTranslation("Not found"); currentRom->releaseDate = game.firstChildElement("ReleaseDate").text(); //Fix missing 0's in date currentRom->releaseDate.replace(QRegExp("^(\\d)/(\\d{2})/(\\d{4})"), "0\\1/\\2/\\3"); currentRom->releaseDate.replace(QRegExp("^(\\d{2})/(\\d)/(\\d{4})"), "\\1/0\\2/\\3"); currentRom->releaseDate.replace(QRegExp("^(\\d)/(\\d)/(\\d{4})"), "0\\1/0\\2/\\3"); currentRom->sortDate = currentRom->releaseDate; currentRom->sortDate.replace(QRegExp("(\\d{2})/(\\d{2})/(\\d{4})"), "\\3-\\1-\\2"); currentRom->overview = game.firstChildElement("Overview").text().remove(QRegExp(regex)); currentRom->esrb = game.firstChildElement("ESRB").text(); int count = 0; QDomNode genreNode = game.firstChildElement("Genres").firstChild(); while(!genreNode.isNull()) { if (count != 0) currentRom->genre += "/" + genreNode.toElement().text(); else currentRom->genre = genreNode.toElement().text(); genreNode = genreNode.nextSibling(); count++; } currentRom->publisher = game.firstChildElement("Publisher").text(); currentRom->developer = game.firstChildElement("Developer").text(); currentRom->rating = game.firstChildElement("Rating").text(); foreach (QString ext, QStringList() << "jpg" << "png") { QString imageFile = getDataLocation() + "/cache/" + 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); #if QT_VERSION >= 0x050000 progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowCloseButtonHint); progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowMinimizeButtonHint); progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowContextHelpButtonHint); #else progress->setWindowFlags(Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); #endif progress->setCancelButton(0); progress->setWindowModality(Qt::WindowModal); progress->show(); } void RomCollection::updatePaths(QStringList romPaths) { this->romPaths = romPaths; this->romPaths.removeAll(""); } mupen64plus-qt-1.11/src/roms/romcollection.h000066400000000000000000000054171323237645600210730ustar00rootroot00000000000000/*** * 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); 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.11/src/roms/thegamesdbscraper.cpp000066400000000000000000000261041323237645600222340ustar00rootroot00000000000000/*** * 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 TheGamesDBScraper::TheGamesDBScraper(QWidget *parent, bool force) : QObject(parent) { this->parent = parent; this->force = force; this->keepGoing = true; } 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 = getDataLocation() + "/cache/" + 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 = getDataLocation() + "/cache/" + identifier.toLower(); QDir cache(gameCache); if (!cache.exists()) { cache.mkpath(gameCache); } //Get game XML info from thegamesdb.net QString dataFile = gameCache + "/data.xml"; 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 //TODO: Contact thegamesdb.net and see if these can be fixed on their end if (searchName == "Legend of Zelda, The - Majora's Mask") searchName = "Majora's Mask"; else if (searchName == "Legend of Zelda, The - Ocarina of Time - Master Quest") searchName = "Master Quest"; else if (searchName == "Legend of Zelda, The - Ocarina of Time" || searchName == "THE LEGEND OF ZELDA") searchName = "Ocarina of Time"; else if (searchName.toLower() == "f-zero x") gameID = "10836"; //If user submits gameID, use that if (gameID != "") url.setUrl("http://thegamesdb.net/api/GetGame.php?id=" + gameID + "&platform=Nintendo 64"); else url.setUrl("http://thegamesdb.net/api/GetGame.php?name=" + searchName + "&platform=Nintendo 64"); QString dom = getUrlContents(url); QDomDocument xml; xml.setContent(dom); QDomNode node = xml.elementsByTagName("Data").at(0).firstChildElement("Game"); int count = 0, found = 0; while(!node.isNull()) { QDomElement element = node.firstChildElement("GameTitle").toElement(); if (force) { //from user dialog QDomElement date = node.firstChildElement("ReleaseDate").toElement(); QString check = "Game: " + element.text(); check.remove(QRegExp(QString("[^A-Za-z 0-9 \\.,\\?'""!@#\\$%\\^&\\*\\") + "(\\)-_=\\+;:<>\\/\\\\|\\}\\{\\[\\]`~]*")); if (date.text() != "") check += "\n" + tr("Released on: ") + date.text(); 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(element.text() == searchName) found = count; } node = node.nextSibling(); count++; } if (!force || updated) { file.open(QIODevice::WriteOnly); QTextStream stream(&file); QDomNodeList gameList = xml.elementsByTagName("Game"); gameList.at(found).save(stream, QDomNode::EncodingFromDocument); 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 dom = file.readAll(); file.close(); QDomDocument xml; xml.setContent(dom); QDomNode node = xml.elementsByTagName("Game").at(0).firstChildElement("Images").firstChild(); while(!node.isNull()) { QDomElement element = node.toElement(); if(element.tagName() == "boxart" && element.attribute("side") == "front") boxartURL = element.attribute("thumb"); node = node.nextSibling(); } if (boxartURL != "") { QUrl url("http://thegamesdb.net/banners/" + 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; } } mupen64plus-qt-1.11/src/roms/thegamesdbscraper.h000066400000000000000000000041351323237645600217010ustar00rootroot00000000000000/*** * 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 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: QByteArray getUrlContents(QUrl url); void showError(QString error); bool force; bool keepGoing; QWidget *parent; }; #endif // THEGAMESDBSCRAPER_H mupen64plus-qt-1.11/src/views/000077500000000000000000000000001323237645600162175ustar00rootroot00000000000000mupen64plus-qt-1.11/src/views/gridview.cpp000066400000000000000000000306201323237645600205440ustar00rootroot00000000000000/*** * 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"); 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) float aspectRatio = float(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)) != NULL; 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)) != NULL) { 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)) != NULL; item++) { if (gridItem->widget() == current && item + offset >= 0 && gridLayout->itemAt(item + offset) != NULL) { 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.11/src/views/gridview.h000066400000000000000000000051751323237645600202200ustar00rootroot00000000000000/*** * 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.11/src/views/listview.cpp000066400000000000000000000252771323237645600206060ustar00rootroot00000000000000/*** * 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 += "
" + 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)) != NULL; 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)) != NULL; 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)) != NULL) { 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)) != NULL; item++) { if (listItem->widget() == current && item + offset >= 0 && listLayout->itemAt(item + offset) != NULL) { 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.11/src/views/listview.h000066400000000000000000000050731323237645600202430ustar00rootroot00000000000000/*** * 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.11/src/views/tableview.cpp000066400000000000000000000272241323237645600207140ustar00rootroot00000000000000/*** * 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 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() != NULL; } 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); int 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(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") #if QT_VERSION >= 0x050000 header()->setSectionResizeMode(c, QHeaderView::Stretch); #else header()->setResizeMode(c, QHeaderView::Stretch); #endif else #if QT_VERSION >= 0x050000 header()->setSectionResizeMode(c, QHeaderView::Interactive); #else header()->setResizeMode(c, QHeaderView::Interactive); #endif } if (widths.size() == tableVisible.size()) setColumnWidth(i, widths[i - hidden].toInt()); else setColumnWidth(i, getDefaultWidth(current, width)); //Overwrite saved value if switching image sizes if (imageUpdated && current == "Game Cover") setColumnWidth(i, 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.11/src/views/tableview.h000066400000000000000000000046721323237645600203630ustar00rootroot00000000000000/*** * 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.11/src/views/widgets/000077500000000000000000000000001323237645600176655ustar00rootroot00000000000000mupen64plus-qt-1.11/src/views/widgets/clickablewidget.cpp000066400000000000000000000050761323237645600235160ustar00rootroot00000000000000/*** * 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.11/src/views/widgets/clickablewidget.h000066400000000000000000000041711323237645600231560ustar00rootroot00000000000000/*** * 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.11/src/views/widgets/treewidgetitem.cpp000066400000000000000000000047021323237645600234160ustar00rootroot00000000000000/*** * 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.11/src/views/widgets/treewidgetitem.h000066400000000000000000000035261323237645600230660ustar00rootroot00000000000000/*** * 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