summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcoadde [Márcio Alexandre Silva Delgado] <coadde@parabola.nu>2015-10-07 03:45:47 -0300
committercoadde [Márcio Alexandre Silva Delgado] <coadde@parabola.nu>2015-10-07 03:45:47 -0300
commitbfe5973df4540facd6ff7aff52fec4fb5ae1f0a1 (patch)
tree4f6efde3995d3f524392ff72737fbf6db598f25a
parent8bdfdd5e5f4918e7563319ebd10b4f19324b6bff (diff)
downloadabslibre-bfe5973df4540facd6ff7aff52fec4fb5ae1f0a1.tar.gz
abslibre-bfe5973df4540facd6ff7aff52fec4fb5ae1f0a1.tar.bz2
abslibre-bfe5973df4540facd6ff7aff52fec4fb5ae1f0a1.zip
pcr/reicast-multilib-git: update pkg
-rw-r--r--pcr/reicast-multilib-git/PKGBUILD114
-rw-r--r--pcr/reicast-multilib-git/fix-dyna-constprop.patch63
-rw-r--r--pcr/reicast-multilib-git/futuristic-memops.patch31
-rw-r--r--pcr/reicast-multilib-git/generalize-mappings.patch1979
-rw-r--r--pcr/reicast-multilib-git/loop-tracing.patch109
-rw-r--r--pcr/reicast-multilib-git/loungekatt_rm-nonfree-fp.patch1451
-rw-r--r--pcr/reicast-multilib-git/multiplayer-unstable.patch450
-rw-r--r--pcr/reicast-multilib-git/multiplayer.patch1211
-rw-r--r--pcr/reicast-multilib-git/refactor-rend-stuff.patch1244
-rw-r--r--pcr/reicast-multilib-git/sdl-opengl.patch251
-rw-r--r--pcr/reicast-multilib-git/sh-block-graphs.patch296
-rw-r--r--pcr/reicast-multilib-git/ta-hash-logs.patch120
12 files changed, 7313 insertions, 6 deletions
diff --git a/pcr/reicast-multilib-git/PKGBUILD b/pcr/reicast-multilib-git/PKGBUILD
index 7693804e4..6d511e847 100644
--- a/pcr/reicast-multilib-git/PKGBUILD
+++ b/pcr/reicast-multilib-git/PKGBUILD
@@ -2,9 +2,12 @@
# Maintainer: André Silva <emulatorman@parabola.nu>
# Maintainer: Márcio Silva <coadde@parabola.nu>
+# error: in fault_handler -> ../../core/linux/common.cpp : 99
+# read: error-mem.txt
+
pkgname=reicast-multilib-git
-pkgver=r1651.56f8ffa
-pkgrel=2
+pkgver=r1665.915d6b2
+pkgrel=1
pkgdesc="A multiplatform Sega Dreamcast emulator"
arch=('x86_64')
url="http://reicast.com/"
@@ -14,9 +17,31 @@ conflicts=('reicast')
makedepends=('git' 'gcc-multilib')
depends=('lib32-libgl' 'lib32-alsa-plugins' 'lib32-libpulse' 'python-evdev' )
source=(reicast::"git+https://github.com/reicast/reicast-emulator.git"
- 'enable_multiplayer_support.patch')
+ 'enable_multiplayer_support.patch'
+ 'fix-dyna-constprop.patch'
+ 'futuristic-memops.patch'
+ 'generalize-mappings.patch'
+ 'loop-tracing.patch'
+ 'loungekatt_rm-nonfree-fp.patch'
+ 'multiplayer.patch'
+ 'multiplayer-unstable.patch'
+ 'refactor-rend-stuff.patch'
+ 'sdl-opengl.patch'
+ 'sh-block-graphs.patch'
+ 'ta-hash-logs.patch')
sha256sums=('SKIP'
- '12bfc58e12b3ee79b0c82159cdc70c76a4b6804f5c6986853156602bb0e6beb0')
+ '12bfc58e12b3ee79b0c82159cdc70c76a4b6804f5c6986853156602bb0e6beb0'
+ 'c14287cf2b2289b9de28cedeee06fcb89ca40da50e34607780dce55d7d8e5fd6'
+ 'ce3fe9f10555c473cafbf4e85724ebe7a8535a1fa3bfae3c9bc0fe518024f71e'
+ '4c0227db07dc9fa4713694bc438345261e401e0b10c89b25a3c1d20ac9acd9b9'
+ 'af47982ca67babb18a96014643c2394b45316f474c3b07b4e38079f780606fce'
+ '4a6025daded179e88174057affadbcfd539d919c35c36945aa3273fab0b0cb49'
+ '09097f59200daaa919fee6f50473f4295a7de6702f40ad5e2648d12c9478a080'
+ '7c0e2a158d7d37ddbf99a40d11a0a889e55c1e85f9c17a2602e5a2bc809ff4ac'
+ 'aead8326ac6815b555be03030ffbdc8f6ced625c980e77eca89e570591c5eb34'
+ 'cf386ebaeafce046a1fc971a5b140bb6a1245840ad2c2a341541327ed6f5606c'
+ '94694d1b615facb39b5ee92ed90c6cefc77fab23fb97f2fcc82e0aa6e1cb14c5'
+ 'ead1e44b82c5a58beca6550a7620fc426b7729f0b7e2ebe27583397fac2a574d')
pkgver() {
cd reicast
@@ -27,16 +52,93 @@ prepare () {
cd reicast
# Remove nonfree fp.h header
- rm -v core/deps/libpng/fp.h
+ # rm -v core/deps/libpng/fp.h
# Add Multiplayer support
patch -Np1 -i "$srcdir"/enable_multiplayer_support.patch
+ # Add extra patches
+ patch -Np1 -i "$srcdir"/fix-dyna-constprop.patch
+ patch -Np1 -i "$srcdir"/futuristic-memops.patch
+ patch -Np1 -i "$srcdir"/generalize-mappings.patch
+# patch -Np1 -i "$srcdir"/loop-tracing.patch # failed build on i686
+ patch -Np1 -i "$srcdir"/loungekatt_rm-nonfree-fp.patch
+ patch -Np1 -i "$srcdir"/refactor-rend-stuff.patch
+ patch -Np1 -i "$srcdir"/sdl-opengl.patch
+ patch -Np1 -i "$srcdir"/sh-block-graphs.patch
+ patch -Np1 -i "$srcdir"/ta-hash-logs.patch
+
+ # Add Multiplayer support (unstable)
+ # core/linux-dist/main.cpp
+ # evdev_controllers[port] = { -1, NULL };
+ # evdev_device_id[port] = -1;
+ # maple_controller[port].enabled = true;
+# patch -Np1 -i "$srcdir"/multiplayer-unstable.patch
+# sed -i 's|kcode[[]port[]]|maple_controller[port].buttons|g
+# s|lt[[]port[]]|maple_controller[port].trigger_left|g
+# s|rt[[]port[]]|maple_controller[port].trigger_right|g
+# s|joyx[[]port[]]|maple_controller[port].stick_x|g
+# s|joyy[[]port[]]|maple_controller[port].stick_y|g
+# s|DC_DPAD_LEFT|DC_BTN_DPAD1_LEFT|g
+# s|DC_DPAD_RIGHT|DC_BTN_DPAD1_RIGHT|g
+# s|DC_DPAD_UP|DC_BTN_DPAD1_UP|g
+# s|DC_DPAD_DOWN|DC_BTN_DPAD1_DOWN|g
+# s|DC_DPAD2_LEFT|DC_BTN_DPAD2_LEFT|g
+# s|DC_DPAD2_RIGHT|DC_BTN_DPAD2_RIGHT|g
+# s|DC_DPAD2_UP|DC_BTN_DPAD2_UP|g
+# s|DC_DPAD2_DOWN|DC_BTN_DPAD2_DOWN|g
+# s|const u32 sdl_map_|const DreamcastControllerCodes sdl_map_|
+# s|const u32[*] sdl_map_|const DreamcastControllerCodes* sdl_map_|
+# \|extern u16|d
+# \|extern u32|d
+# \|extern u8 |d
+# \|extern s8 |d
+# \|#define DC_|d
+# \|u16 kcode|d
+# \|u8 rt|d
+# \|u8 lt|d
+# \|u32 vks|d
+# \|s8 joy|d
+# \|int port = 0; port| s|4|MAPLE_NUM_PORTS|
+# \|enum DreamcastController|,+32 d
+# s|#include ["]types[.]h["]|#include "types.h"\n#include "hw/maple/maple_controller.h"|
+# ' core/sdl/sdl.cpp \
+# core/linux-dist/main.cpp \
+# core/linux-dist/main.h \
+# shell/apple/emulator-ios/emulator/EmulatorView.mm
+# sed -i 's|kcode[[]port[]]|maple_controller[port].buttons|g
+# s|lt[[]port[]]|maple_controller[port].trigger_left|g
+# s|rt[[]port[]]|maple_controller[port].trigger_right|g
+# s|joyx[[]port[]]|maple_controller[port].stick_x|g
+# s|joyy[[]port[]]|maple_controller[port].stick_y|g
+# s|DC_DPAD_LEFT|DC_BTN_DPAD1_LEFT|g
+# s|DC_DPAD_RIGHT|DC_BTN_DPAD1_RIGHT|g
+# s|DC_DPAD_UP|DC_BTN_DPAD1_UP|g
+# s|DC_DPAD_DOWN|DC_BTN_DPAD1_DOWN|g
+# s|DC_DPAD2_LEFT|DC_BTN_DPAD2_LEFT|g
+# s|DC_DPAD2_RIGHT|DC_BTN_DPAD2_RIGHT|g
+# s|DC_DPAD2_UP|DC_BTN_DPAD2_UP|g
+# s|DC_DPAD2_DOWN|DC_BTN_DPAD2_DOWN|g
+# \|extern u16|d
+# \|extern u8 |d
+# \|extern s8 |d
+# \|#define key_CONT_|d
+# s|#include ["]glshaders[.]h["]|#include "glshaders.h"\n#include "hw/maple/maple_controller.h"|
+# ' core/rend/gles/gles.cpp
+# sed -i 's|x11_keymap[[]53[]] = DC_BTN_X;|x11_keymap[53] = DC_BTN_X;\nx11_keymap[52] = DC_BTN_Y;|
+# ' core/linux-dist/x11.cpp
+# sed -i 's|DC_BTN_DPAD_|DC_BTN_DPAD1_|
+# ' core/hw/maple/maple_controller.h \
+# core/windows/winmain.cpp \
+# core/rend/gles/gles.cpp \
+# shell/apple/emulator-osx/emulator-osx/osx-main.mm
+
# Add experimental shadow support: http://github.com/reicast-emulator/issues/94
sed -i 's|//DrawModVols|DrawModVols|' core/rend/gles/gldraw.cpp
# Fix Xbox Input Axis
- sed -i 's|DC_AXIS_LT, 0, 0, DC_AXIS_RT|0, 0, DC_AXIS_RT, DC_AXIS_LT|' core/linux-dist/joystick.cpp
+ # sed -i 's|DC_AXIS_LT, 0, 0, DC_AXIS_RT|0, 0, DC_AXIS_RT, DC_AXIS_LT|' core/linux-dist/joystick.cpp
+ sed -i 's|DC_AXIS_LT, 0, 0, DC_AXIS_RT|0, 0, DC_AXIS_RT, DC_AXIS_LT|' core/sdl/sdl.cpp # with generalize-mappings.patch
# Enable Pulseaudio
sed -i 's|#USE_PULSEAUDIO|USE_PULSEAUDIO|' shell/linux/Makefile
diff --git a/pcr/reicast-multilib-git/fix-dyna-constprop.patch b/pcr/reicast-multilib-git/fix-dyna-constprop.patch
new file mode 100644
index 000000000..c7c89b5e2
--- /dev/null
+++ b/pcr/reicast-multilib-git/fix-dyna-constprop.patch
@@ -0,0 +1,63 @@
+diff -Nur a/core/hw/sh4/dyna/shil.cpp b/core/hw/sh4/dyna/shil.cpp
+--- a/core/hw/sh4/dyna/shil.cpp 2015-10-06 21:43:53.031336322 -0300
++++ b/core/hw/sh4/dyna/shil.cpp 2015-10-06 21:45:06.558863627 -0300
+@@ -521,20 +521,39 @@
+ rv[op->rd._reg]=op->rs1._imm;
+ }
+
+- //NOT WORKING
+- //WE NEED PROPER PAGELOCKS
+- if (op->op==shop_readm && op->rs1.is_imm() && op->rd.is_r32i() && op->rd._reg<16 && op->flags==0x4 && op->rs3.is_null())
+- {
+- u32 baddr=blk->addr&0x0FFFFFFF;
++ if (0) {
++ //a more minimalistic but still broken approach for this.
++ //this is clearly a hack
++ if (op->op == shop_readm && op->rs1.is_imm() && IsOnRam(op->rs1.imm_value()))
++ {
++ u32 round = op->rs1.imm_value() & ~PAGE_MASK;
++ bool optmize_away = round == (blk->addr & ~PAGE_MASK) && (blk->addr & 0x007FFFFF) > 0x200000;
++
++ if (optmize_away && op->flags == 4) {
++ u32 data = _vmem_ReadMem32(op->rs1.imm_value());
++ op->op = shop_mov32;
++ op->rs1.type = FMT_IMM;
++ op->rs1._imm = data;
++ }
++ }
++ }
+
+- if (/*baddr==0xC158400 &&*/ blk->addr/PAGE_SIZE == op->rs1._imm/PAGE_SIZE)
++ if (0) {
++ //NOT WORKING
++ //WE NEED PROPER PAGELOCKS
++ if (op->op == shop_readm && op->rs1.is_imm() && op->rd.is_r32i() && op->rd._reg<16 && op->flags == 0x4 && op->rs3.is_null())
+ {
+- isi[op->rd._reg]=true;
+- rv[op->rd._reg]= ReadMem32(op->rs1._imm);
+- printf("IMM MOVE: %08X -> %08X\n",op->rs1._imm,rv[op->rd._reg]);
++ u32 baddr = blk->addr & 0x0FFFFFFF;
+
+- op->op=shop_mov32;
+- op->rs1._imm=rv[op->rd._reg];
++ if (/*baddr==0xC158400 &&*/ blk->addr / PAGE_SIZE == op->rs1._imm / PAGE_SIZE)
++ {
++ isi[op->rd._reg] = true;
++ rv[op->rd._reg] = ReadMem32(op->rs1._imm);
++ printf("IMM MOVE: %08X -> %08X\n", op->rs1._imm, rv[op->rd._reg]);
++
++ op->op = shop_mov32;
++ op->rs1._imm = rv[op->rd._reg];
++ }
+ }
+ }
+ }
+@@ -925,7 +944,7 @@
+ */
+ if (settings.dynarec.unstable_opt)
+ sq_pref(blk);
+- //constprop(blk); // crashes on ip
++ constprop(blk); // maybe it works w/o the readm parts?
+ #if HOST_CPU==CPU_X86
+ // rdgrp(blk);
+ // wtgrp(blk);
diff --git a/pcr/reicast-multilib-git/futuristic-memops.patch b/pcr/reicast-multilib-git/futuristic-memops.patch
new file mode 100644
index 000000000..517670402
--- /dev/null
+++ b/pcr/reicast-multilib-git/futuristic-memops.patch
@@ -0,0 +1,31 @@
+diff -Nur a/core/hw/sh4/dyna/shil_canonical.h b/core/hw/sh4/dyna/shil_canonical.h
+--- a/core/hw/sh4/dyna/shil_canonical.h 2015-10-06 21:43:53.031336322 -0300
++++ b/core/hw/sh4/dyna/shil_canonical.h 2015-10-06 21:48:30.009328396 -0300
+@@ -206,6 +206,27 @@
+ shil_recimp()
+ shil_opc_end()
+
++//mem io v2
++
++//vref = vrans<mode|size>(ptr, nil)
++//vref = vrans<mode|size>(base, offset)
++shil_opc(vtrans)
++shil_recimp()
++shil_opc_end()
++
++//dst = readpvr<size>(vref)
++//dst = readpvr<size>(vref, nil, offs) w/ offs < 1024
++shil_opc(readvptr)
++shil_recimp()
++shil_opc_end()
++
++//writevptr<size>(vref, src)
++//writevptr<size>(vref, src, offs) w/ offs < 1024
++shil_opc(writevptr)
++shil_recimp()
++shil_opc_end()
++
++
+ //Canonical impl. opcodes !
+ shil_opc(sync_sr)
+ shil_canonical
diff --git a/pcr/reicast-multilib-git/generalize-mappings.patch b/pcr/reicast-multilib-git/generalize-mappings.patch
new file mode 100644
index 000000000..a47681f39
--- /dev/null
+++ b/pcr/reicast-multilib-git/generalize-mappings.patch
@@ -0,0 +1,1979 @@
+diff -Nur a/core/linux-dist/bimap.h b/core/linux-dist/bimap.h
+--- a/core/linux-dist/bimap.h 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/bimap.h 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,67 @@
++/* SimpleBimap
++ *
++ * A basic implementation of a bidirectional map that not only allows
++ * you to get a mapped value from a key, but also the other way around.
++ * Deleting elements and other fancy (and not-so-fancy) stuff is not
++ * supported.
++ *
++ * Usage example:
++ * SimpleBimap<std::string, std::string> bimap;
++ * bimap.insert("foo", "bar");
++ * printf("foo -> %s\n", bimap.get_by_key("foo")->c_str());
++ * printf("bar <- %s\n", bimap.get_by_value("bar")->c_str());
++ * if(bimap.get_by_key("somekey") == NULL)
++ * puts("somekey not found");
++ *
++ * The above example's output:
++ * foo -> bar
++ * bar <- foo
++ * somekey not found
++ */
++#include <cstddef>
++#include <utility>
++#include <map>
++
++template<typename T1, typename T2>
++class SimpleBimap
++{
++ private:
++ typedef typename std::map<T2, T1*> MapA;
++ typedef typename std::map<T1, T2*> MapB;
++ MapA map_a;
++ MapB map_b;
++ public:
++ void insert(const T1& a, const T2& b)
++ {
++ // create first pair
++ typename MapA::iterator iter_a = map_a.insert(std::pair<T2, T1*>(b, NULL)).first;
++ T2* ptr_b = const_cast<T2*>(&(iter_a->first));
++
++ // insert second pair (a, pointer_to_b)
++ typename MapB::iterator iter_b = map_b.insert(std::pair<T1, T2*>(a, ptr_b)).first;
++
++ // update pointer in map_a to point to a
++ T1* ptr_a = const_cast<T1*>(&(iter_b->first));
++ iter_a->second = ptr_a;
++ }
++
++ const T2* get_by_key(const T1 &a)
++ {
++ typename MapB::iterator it = this->map_b.find(a);
++ if(it != this->map_b.end())
++ {
++ return (it->second);
++ }
++ return NULL;
++ }
++
++ const T1* get_by_value(const T2 &b)
++ {
++ typename MapA::iterator it = this->map_a.find(b);
++ if(it != this->map_a.end())
++ {
++ return (it->second);
++ }
++ return NULL;
++ }
++};
+\ No newline at end of file
+diff -Nur a/core/linux-dist/evdev.cpp b/core/linux-dist/evdev.cpp
+--- a/core/linux-dist/evdev.cpp 2015-10-06 21:43:53.042336401 -0300
++++ b/core/linux-dist/evdev.cpp 1969-12-31 21:00:00.000000000 -0300
+@@ -1,450 +0,0 @@
+-#include <unistd.h>
+-#include <fcntl.h>
+-#include <linux/input.h>
+-#include "linux-dist/evdev.h"
+-#include "linux-dist/main.h"
+-#include "cfg/ini.h"
+-#include <vector>
+-#include <map>
+-#include <dlfcn.h>
+-
+-#if defined(USE_EVDEV)
+- bool libevdev_tried = false;
+- bool libevdev_available = false;
+- typedef int (*libevdev_func1_t)(int, const char*);
+- typedef const char* (*libevdev_func2_t)(int, int);
+- libevdev_func1_t libevdev_event_code_from_name;
+- libevdev_func2_t libevdev_event_code_get_name;
+-
+- void load_libevdev()
+- {
+- if (libevdev_tried)
+- {
+- return;
+- }
+-
+- libevdev_tried = true;
+- void* lib_handle = dlopen("libevdev.so", RTLD_NOW);
+-
+- bool failed = false;
+-
+- if (!lib_handle)
+- {
+- fprintf(stderr, "%s\n", dlerror());
+- failed = true;
+- }
+- else
+- {
+- libevdev_event_code_from_name = reinterpret_cast<libevdev_func1_t>(dlsym(lib_handle, "libevdev_event_code_from_name"));
+-
+- const char* error1 = dlerror();
+- if (error1 != NULL)
+- {
+- fprintf(stderr, "%s\n", error1);
+- failed = true;
+- }
+-
+- libevdev_event_code_get_name = reinterpret_cast<libevdev_func2_t>(dlsym(lib_handle, "libevdev_event_code_get_name"));
+-
+- const char* error2 = dlerror();
+- if (error2 != NULL)
+- {
+- fprintf(stderr, "%s\n", error2);
+- failed = true;
+- }
+- }
+-
+- if(failed)
+- {
+- puts("WARNING: libevdev is not available. You'll not be able to use button names instead of numeric codes in your controller mappings!\n");
+- return;
+- }
+-
+- libevdev_available = true;
+- }
+-
+- s8 EvdevAxisData::convert(s32 value)
+- {
+- return (((value - min) * 255) / range);
+- }
+-
+- void EvdevAxisData::init(int fd, int code, bool inverted)
+- {
+- struct input_absinfo abs;
+- if(code < 0 || ioctl(fd, EVIOCGABS(code), &abs))
+- {
+- if(code >= 0)
+- {
+- perror("evdev ioctl");
+- }
+- this->range = 255;
+- this->min = 0;
+- return;
+- }
+- s32 min = abs.minimum;
+- s32 max = abs.maximum;
+- printf("evdev: range of axis %d is from %d to %d\n", code, min, max);
+- if(inverted)
+- {
+- this->range = (min - max);
+- this->min = max;
+- }
+- else
+- {
+- this->range = (max - min);
+- this->min = min;
+- }
+- }
+-
+- void EvdevController::init()
+- {
+- this->data_x.init(this->fd, this->mapping->Axis_Analog_X, this->mapping->Axis_Analog_X_Inverted);
+- this->data_y.init(this->fd, this->mapping->Axis_Analog_Y, this->mapping->Axis_Analog_Y_Inverted);
+- this->data_trigger_left.init(this->fd, this->mapping->Axis_Trigger_Left, this->mapping->Axis_Trigger_Left_Inverted);
+- this->data_trigger_right.init(this->fd, this->mapping->Axis_Trigger_Right, this->mapping->Axis_Trigger_Right_Inverted);
+- }
+-
+- std::map<std::string, EvdevControllerMapping> loaded_mappings;
+-
+- int load_keycode(ConfigFile* cfg, string section, string dc_key)
+- {
+- int code = -1;
+-
+- string keycode = cfg->get(section, dc_key, "-1");
+- if (strstr(keycode.c_str(), "KEY_") != NULL ||
+- strstr(keycode.c_str(), "BTN_") != NULL ||
+- strstr(keycode.c_str(), "ABS_") != NULL)
+- {
+- if(libevdev_available)
+- {
+- int type = ((strstr(keycode.c_str(), "ABS_") != NULL) ? EV_ABS : EV_KEY);
+- code = libevdev_event_code_from_name(type, keycode.c_str());
+- }
+- if(code < 0)
+- {
+- printf("evdev: failed to find keycode for '%s'\n", keycode.c_str());
+- }
+- else
+- {
+- printf("%s = %s (%d)\n", dc_key.c_str(), keycode.c_str(), code);
+- }
+- return code;
+- }
+-
+- code = cfg->get_int(section, dc_key, -1);
+- if(code >= 0)
+- {
+- char* name = NULL;
+- if(libevdev_available)
+- {
+- int type = ((strstr(dc_key.c_str(), "axis_") != NULL) ? EV_ABS : EV_KEY);
+- name = (char*)libevdev_event_code_get_name(type, code);
+- }
+- if (name != NULL)
+- {
+- printf("%s = %s (%d)\n", dc_key.c_str(), name, code);
+- }
+- else
+- {
+- printf("%s = %d\n", dc_key.c_str(), code);
+- }
+- }
+- return code;
+- }
+-
+- EvdevControllerMapping load_mapping(FILE* fd)
+- {
+- ConfigFile mf;
+- mf.parse(fd);
+-
+- EvdevControllerMapping mapping = {
+- mf.get("emulator", "mapping_name", "<Unknown>").c_str(),
+- load_keycode(&mf, "dreamcast", "btn_a"),
+- load_keycode(&mf, "dreamcast", "btn_b"),
+- load_keycode(&mf, "dreamcast", "btn_c"),
+- load_keycode(&mf, "dreamcast", "btn_d"),
+- load_keycode(&mf, "dreamcast", "btn_x"),
+- load_keycode(&mf, "dreamcast", "btn_y"),
+- load_keycode(&mf, "dreamcast", "btn_z"),
+- load_keycode(&mf, "dreamcast", "btn_start"),
+- load_keycode(&mf, "emulator", "btn_escape"),
+- load_keycode(&mf, "dreamcast", "btn_dpad1_left"),
+- load_keycode(&mf, "dreamcast", "btn_dpad1_right"),
+- load_keycode(&mf, "dreamcast", "btn_dpad1_up"),
+- load_keycode(&mf, "dreamcast", "btn_dpad1_down"),
+- load_keycode(&mf, "dreamcast", "btn_dpad2_left"),
+- load_keycode(&mf, "dreamcast", "btn_dpad2_right"),
+- load_keycode(&mf, "dreamcast", "btn_dpad2_up"),
+- load_keycode(&mf, "dreamcast", "btn_dpad2_down"),
+- load_keycode(&mf, "compat", "btn_trigger_left"),
+- load_keycode(&mf, "compat", "btn_trigger_right"),
+- load_keycode(&mf, "compat", "axis_dpad1_x"),
+- load_keycode(&mf, "compat", "axis_dpad1_y"),
+- load_keycode(&mf, "compat", "axis_dpad2_x"),
+- load_keycode(&mf, "compat", "axis_dpad2_y"),
+- load_keycode(&mf, "dreamcast", "axis_x"),
+- load_keycode(&mf, "dreamcast", "axis_y"),
+- load_keycode(&mf, "dreamcast", "axis_trigger_left"),
+- load_keycode(&mf, "dreamcast", "axis_trigger_right"),
+- mf.get_bool("compat", "axis_x_inverted", false),
+- mf.get_bool("compat", "axis_y_inverted", false),
+- mf.get_bool("compat", "axis_trigger_left_inverted", false),
+- mf.get_bool("compat", "axis_trigger_right_inverted", false)
+- };
+- return mapping;
+- }
+-
+- int input_evdev_init(EvdevController* controller, const char* device, const char* custom_mapping_fname = NULL)
+- {
+- load_libevdev();
+-
+- char name[256] = "Unknown";
+-
+- printf("evdev: Trying to open device at '%s'\n", device);
+-
+- int fd = open(device, O_RDONLY);
+-
+- if (fd >= 0)
+- {
+- fcntl(fd, F_SETFL, O_NONBLOCK);
+- if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)
+- {
+- perror("evdev: ioctl");
+- return -2;
+- }
+- else
+- {
+- printf("evdev: Found '%s' at '%s'\n", name, device);
+-
+- controller->fd = fd;
+-
+- const char* mapping_fname;
+-
+- if(custom_mapping_fname != NULL)
+- {
+- mapping_fname = custom_mapping_fname;
+- }
+- else
+- {
+- #if defined(TARGET_PANDORA)
+- mapping_fname = "controller_pandora.cfg";
+- #elif defined(TARGET_GCW0)
+- mapping_fname = "controller_gcwz.cfg";
+- #else
+- if (strcmp(name, "Microsoft X-Box 360 pad") == 0 ||
+- strcmp(name, "Xbox 360 Wireless Receiver") == 0 ||
+- strcmp(name, "Xbox 360 Wireless Receiver (XBOX)") == 0)
+- {
+- mapping_fname = "controller_xpad.cfg";
+- }
+- else if (strstr(name, "Xbox Gamepad (userspace driver)") != NULL)
+- {
+- mapping_fname = "controller_xboxdrv.cfg";
+- }
+- else if (strstr(name, "keyboard") != NULL ||
+- strstr(name, "Keyboard") != NULL)
+- {
+- mapping_fname = "keyboard.cfg";
+- }
+- else
+- {
+- mapping_fname = "controller_generic.cfg";
+- }
+- #endif
+- }
+- if(loaded_mappings.count(string(mapping_fname)) == 0)
+- {
+- FILE* mapping_fd = NULL;
+- if(mapping_fname[0] == '/')
+- {
+- // Absolute mapping
+- mapping_fd = fopen(mapping_fname, "r");
+- }
+- else
+- {
+- // Mapping from ~/.reicast/mappings/
+- size_t size_needed = snprintf(NULL, 0, EVDEV_MAPPING_PATH, mapping_fname) + 1;
+- char* mapping_path = (char*)malloc(size_needed);
+- sprintf(mapping_path, EVDEV_MAPPING_PATH, mapping_fname);
+- mapping_fd = fopen(get_readonly_data_path(mapping_path).c_str(), "r");
+- free(mapping_path);
+- }
+-
+- if(mapping_fd != NULL)
+- {
+- printf("evdev: reading mapping file: '%s'\n", mapping_fname);
+- loaded_mappings.insert(std::make_pair(string(mapping_fname), load_mapping(mapping_fd)));
+- fclose(mapping_fd);
+-
+- }
+- else
+- {
+- printf("evdev: unable to open mapping file '%s'\n", mapping_fname);
+- perror("evdev");
+- return -3;
+- }
+- }
+- controller->mapping = &loaded_mappings[string(mapping_fname)];
+- printf("evdev: Using '%s' mapping\n", controller->mapping->name);
+- controller->init();
+-
+- return 0;
+- }
+- }
+- else
+- {
+- perror("evdev: open");
+- return -1;
+- }
+- }
+-
+- bool input_evdev_handle(EvdevController* controller, u32 port)
+- {
+- #define SET_FLAG(field, mask, expr) field =((expr) ? (field & ~mask) : (field | mask))
+- if (controller->fd < 0 || controller->mapping == NULL)
+- {
+- return false;
+- }
+-
+- input_event ie;
+-
+- while(read(controller->fd, &ie, sizeof(ie)) == sizeof(ie))
+- {
+- switch(ie.type)
+- {
+- case EV_KEY:
+- if (ie.code == controller->mapping->Btn_A) {
+- SET_FLAG(kcode[port], DC_BTN_A, ie.value);
+- } else if (ie.code == controller->mapping->Btn_B) {
+- SET_FLAG(kcode[port], DC_BTN_B, ie.value);
+- } else if (ie.code == controller->mapping->Btn_C) {
+- SET_FLAG(kcode[port], DC_BTN_C, ie.value);
+- } else if (ie.code == controller->mapping->Btn_D) {
+- SET_FLAG(kcode[port], DC_BTN_D, ie.value);
+- } else if (ie.code == controller->mapping->Btn_X) {
+- SET_FLAG(kcode[port], DC_BTN_X, ie.value);
+- } else if (ie.code == controller->mapping->Btn_Y) {
+- SET_FLAG(kcode[port], DC_BTN_Y, ie.value);
+- } else if (ie.code == controller->mapping->Btn_Z) {
+- SET_FLAG(kcode[port], DC_BTN_Z, ie.value);
+- } else if (ie.code == controller->mapping->Btn_Start) {
+- SET_FLAG(kcode[port], DC_BTN_START, ie.value);
+- } else if (ie.code == controller->mapping->Btn_Escape) {
+- die("death by escape key");
+- } else if (ie.code == controller->mapping->Btn_DPad_Left) {
+- SET_FLAG(kcode[port], DC_DPAD_LEFT, ie.value);
+- } else if (ie.code == controller->mapping->Btn_DPad_Right) {
+- SET_FLAG(kcode[port], DC_DPAD_RIGHT, ie.value);
+- } else if (ie.code == controller->mapping->Btn_DPad_Up) {
+- SET_FLAG(kcode[port], DC_DPAD_UP, ie.value);
+- } else if (ie.code == controller->mapping->Btn_DPad_Down) {
+- SET_FLAG(kcode[port], DC_DPAD_DOWN, ie.value);
+- } else if (ie.code == controller->mapping->Btn_DPad2_Left) {
+- SET_FLAG(kcode[port], DC_DPAD2_LEFT, ie.value);
+- } else if (ie.code == controller->mapping->Btn_DPad2_Right) {
+- SET_FLAG(kcode[port], DC_DPAD2_RIGHT, ie.value);
+- } else if (ie.code == controller->mapping->Btn_DPad2_Up) {
+- SET_FLAG(kcode[port], DC_DPAD2_UP, ie.value);
+- } else if (ie.code == controller->mapping->Btn_DPad2_Down) {
+- SET_FLAG(kcode[port], DC_DPAD2_DOWN, ie.value);
+- } else if (ie.code == controller->mapping->Btn_Trigger_Left) {
+- lt[port] = (ie.value ? 255 : 0);
+- } else if (ie.code == controller->mapping->Btn_Trigger_Right) {
+- rt[port] = (ie.value ? 255 : 0);
+- }
+- break;
+- case EV_ABS:
+- if (ie.code == controller->mapping->Axis_DPad_X)
+- {
+- switch(ie.value)
+- {
+- case -1:
+- SET_FLAG(kcode[port], DC_DPAD_LEFT, 1);
+- SET_FLAG(kcode[port], DC_DPAD_RIGHT, 0);
+- break;
+- case 0:
+- SET_FLAG(kcode[port], DC_DPAD_LEFT, 0);
+- SET_FLAG(kcode[port], DC_DPAD_RIGHT, 0);
+- break;
+- case 1:
+- SET_FLAG(kcode[port], DC_DPAD_LEFT, 0);
+- SET_FLAG(kcode[port], DC_DPAD_RIGHT, 1);
+- break;
+- }
+- }
+- else if (ie.code == controller->mapping->Axis_DPad_Y)
+- {
+- switch(ie.value)
+- {
+- case -1:
+- SET_FLAG(kcode[port], DC_DPAD_UP, 1);
+- SET_FLAG(kcode[port], DC_DPAD_DOWN, 0);
+- break;
+- case 0:
+- SET_FLAG(kcode[port], DC_DPAD_UP, 0);
+- SET_FLAG(kcode[port], DC_DPAD_DOWN, 0);
+- break;
+- case 1:
+- SET_FLAG(kcode[port], DC_DPAD_UP, 0);
+- SET_FLAG(kcode[port], DC_DPAD_DOWN, 1);
+- break;
+- }
+- }
+- else if (ie.code == controller->mapping->Axis_DPad2_X)
+- {
+- switch(ie.value)
+- {
+- case -1:
+- SET_FLAG(kcode[port], DC_DPAD2_LEFT, 1);
+- SET_FLAG(kcode[port], DC_DPAD2_RIGHT, 0);
+- break;
+- case 0:
+- SET_FLAG(kcode[port], DC_DPAD2_LEFT, 0);
+- SET_FLAG(kcode[port], DC_DPAD2_RIGHT, 0);
+- break;
+- case 1:
+- SET_FLAG(kcode[port], DC_DPAD2_LEFT, 0);
+- SET_FLAG(kcode[port], DC_DPAD2_RIGHT, 1);
+- break;
+- }
+- }
+- else if (ie.code == controller->mapping->Axis_DPad2_X)
+- {
+- switch(ie.value)
+- {
+- case -1:
+- SET_FLAG(kcode[port], DC_DPAD2_UP, 1);
+- SET_FLAG(kcode[port], DC_DPAD2_DOWN, 0);
+- break;
+- case 0:
+- SET_FLAG(kcode[port], DC_DPAD2_UP, 0);
+- SET_FLAG(kcode[port], DC_DPAD2_DOWN, 0);
+- break;
+- case 1:
+- SET_FLAG(kcode[port], DC_DPAD2_UP, 0);
+- SET_FLAG(kcode[port], DC_DPAD2_DOWN, 1);
+- break;
+- }
+- }
+- else if (ie.code == controller->mapping->Axis_Analog_X)
+- {
+- joyx[port] = (controller->data_x.convert(ie.value) + 128);
+- }
+- else if (ie.code == controller->mapping->Axis_Analog_Y)
+- {
+- joyy[port] = (controller->data_y.convert(ie.value) + 128);
+- }
+- else if (ie.code == controller->mapping->Axis_Trigger_Left)
+- {
+- lt[port] = controller->data_trigger_left.convert(ie.value);
+- }
+- else if (ie.code == controller->mapping->Axis_Trigger_Right)
+- {
+- rt[port] = controller->data_trigger_right.convert(ie.value);
+- }
+- break;
+- }
+- }
+- }
+-#endif
+-
+diff -Nur a/core/linux-dist/evdev.h b/core/linux-dist/evdev.h
+--- a/core/linux-dist/evdev.h 2015-10-06 21:43:53.042336401 -0300
++++ b/core/linux-dist/evdev.h 1969-12-31 21:00:00.000000000 -0300
+@@ -1,74 +0,0 @@
+-#pragma once
+-#include <linux/input.h>
+-#include "types.h"
+-
+-struct EvdevControllerMapping
+-{
+- const char* name;
+- const int Btn_A;
+- const int Btn_B;
+- const int Btn_C;
+- const int Btn_D;
+- const int Btn_X;
+- const int Btn_Y;
+- const int Btn_Z;
+- const int Btn_Start;
+- const int Btn_Escape;
+- const int Btn_DPad_Left;
+- const int Btn_DPad_Right;
+- const int Btn_DPad_Up;
+- const int Btn_DPad_Down;
+- const int Btn_DPad2_Left;
+- const int Btn_DPad2_Right;
+- const int Btn_DPad2_Up;
+- const int Btn_DPad2_Down;
+- const int Btn_Trigger_Left;
+- const int Btn_Trigger_Right;
+- const int Axis_DPad_X;
+- const int Axis_DPad_Y;
+- const int Axis_DPad2_X;
+- const int Axis_DPad2_Y;
+- const int Axis_Analog_X;
+- const int Axis_Analog_Y;
+- const int Axis_Trigger_Left;
+- const int Axis_Trigger_Right;
+- const bool Axis_Analog_X_Inverted;
+- const bool Axis_Analog_Y_Inverted;
+- const bool Axis_Trigger_Left_Inverted;
+- const bool Axis_Trigger_Right_Inverted;
+-};
+-
+-struct EvdevAxisData
+-{
+- s32 range; // smaller size than 32 bit might cause integer overflows
+- s32 min;
+- void init(int fd, int code, bool inverted);
+- s8 convert(int value);
+-};
+-
+-struct EvdevController
+-{
+- int fd;
+- EvdevControllerMapping* mapping;
+- EvdevAxisData data_x;
+- EvdevAxisData data_y;
+- EvdevAxisData data_trigger_left;
+- EvdevAxisData data_trigger_right;
+- void init();
+-};
+-
+-#define EVDEV_DEVICE_CONFIG_KEY "evdev_device_id_%d"
+-#define EVDEV_MAPPING_CONFIG_KEY "evdev_mapping_%d"
+-#define EVDEV_DEVICE_STRING "/dev/input/event%d"
+-#define EVDEV_MAPPING_PATH "/mappings/%s"
+-
+-#ifdef TARGET_PANDORA
+- #define EVDEV_DEFAULT_DEVICE_ID_1 4
+-#else
+- #define EVDEV_DEFAULT_DEVICE_ID_1 0
+-#endif
+-
+-#define EVDEV_DEFAULT_DEVICE_ID(port) (port == 1 ? EVDEV_DEFAULT_DEVICE_ID_1 : -1)
+-
+-extern int input_evdev_init(EvdevController* controller, const char* device, const char* mapping_fname);
+-extern bool input_evdev_handle(EvdevController* controller, u32 port);
+diff -Nur a/core/linux-dist/handler.cpp b/core/linux-dist/handler.cpp
+--- a/core/linux-dist/handler.cpp 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/handler.cpp 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,263 @@
++#include "handler.h"
++
++#define SET_FLAG(field, mask, expr) field =((expr) ? (field & ~mask) : (field | mask))
++static InputAxisID axis_ids[] = { DC_AXIS_X, DC_AXIS_Y, DC_AXIS_TRIGGER_LEFT, DC_AXIS_TRIGGER_RIGHT, EMU_AXIS_DPAD1_X, EMU_AXIS_DPAD1_Y, EMU_AXIS_DPAD2_X, EMU_AXIS_DPAD2_Y };
++
++InputAxisConverter::InputAxisConverter(bool inverted, InputAxisLimits limits)
++{
++ this->m_deadzone = limits.deadzone;
++ if(inverted)
++ {
++ this->m_range = (limits.minimum - limits.maximum);
++ this->m_minimum = limits.maximum;
++ }
++ else
++ {
++ this->m_range = (limits.maximum - limits.minimum);
++ this->m_minimum = limits.minimum;
++ }
++}
++
++s8 InputAxisConverter::convert(s32 value)
++{
++ // If value is in deadzone, return 0
++ if (this->m_deadzone && ((value >= 0 && value <= this->m_deadzone) || (value < 0 && value >= -this->m_deadzone)))
++ {
++ return 0;
++ }
++ if (this->m_range)
++ {
++ return (((value - this->m_minimum) * 255) / this->m_range);
++ }
++ return value;
++}
++
++InputMappingStore InputHandler::s_mappingstore;
++
++
++InputHandler::InputHandler()
++{
++ this->m_initialized = false;
++}
++
++InputHandler::~InputHandler()
++{
++ //TODO;
++}
++
++bool InputHandler::initialize(u32 port, std::string device, std::string custom_mapping_filename)
++{
++ if(this->m_initialized)
++ {
++ printf("%s: Handler is already initialized!\n", this->get_api_name().c_str());
++ return true;
++ }
++
++ this->m_port = port;
++
++ bool success = this->setup_device(device);
++
++ if(!success)
++ {
++ printf("%s: Initialization of device '%s' failed!\n", this->get_api_name().c_str(), device.c_str());
++ return false;
++ }
++
++ if(custom_mapping_filename.empty())
++ {
++ this->m_mapping = NULL;
++ }
++ else
++ {
++ this->m_mapping = InputHandler::s_mappingstore.get(custom_mapping_filename, this->get_api_name());
++ }
++
++ if(this->m_mapping == NULL)
++ {
++ if(!custom_mapping_filename.empty())
++ {
++ printf("%s: Loading custom mapping '%s' failed!\n", this->get_api_name().c_str(), custom_mapping_filename.c_str());
++ }
++ std::string default_mapping_filename = this->get_default_mapping_filename();
++ if(default_mapping_filename.empty())
++ {
++ printf("%s: No default mapping available!\n", this->get_api_name().c_str());
++ }
++ else
++ {
++ printf("%s: Using default mapping '%s'.\n", this->get_api_name().c_str(), default_mapping_filename.c_str());
++ this->m_mapping = InputHandler::s_mappingstore.get(default_mapping_filename, this->get_api_name());
++ }
++ }
++
++
++ if(this->m_mapping == NULL)
++ {
++ printf("%s: Couldn't load a mapping!\n", this->get_api_name().c_str());
++ return false;
++ }
++
++ for(int i = 0; i < 8; i++)
++ {
++ InputAxisID id = axis_ids[i];
++ const InputAxisCode* code = this->m_mapping->get_axis_code(id);
++ if(code != NULL)
++ {
++ InputAxisLimits limits;
++ this->get_axis_limits(*code, limits);
++ if(limits.minimum != limits.maximum)
++ {
++ bool inverted = this->m_mapping->get_axis_inverted(*code);
++ this->enable_axis_converter(id, inverted, limits);
++ }
++ }
++ }
++ this->m_initialized = true;
++ return true;
++}
++
++bool InputHandler::is_initialized()
++{
++ return this->m_initialized;
++}
++
++std::string InputHandler::get_default_mapping_filename()
++{
++ return "default.cfg";
++}
++
++void InputHandler::get_axis_limits(const InputAxisCode code, InputAxisLimits& limits)
++{
++ // Default stub, can be overridden in subclasses
++ limits.minimum = 0;
++ limits.maximum = 0;
++ limits.deadzone = 0;
++}
++
++void InputHandler::handle_button(InputButtonCode code, int value)
++{
++ if(this->m_mapping == NULL)
++ {
++ return;
++ }
++ const InputButtonID* button_id = this->m_mapping->get_button_id(code);
++ if(button_id == NULL)
++ {
++ printf("Ignoring %d (%d)\n", code, button_id);
++ return;
++ }
++ switch(*button_id)
++ {
++ case EMU_BTN_ESCAPE:
++ if(value)
++ {
++ die("death by escape key");
++ }
++ break;
++ case EMU_BTN_TRIGGER_LEFT:
++ lt[this->m_port] = (value ? 255 : 0);
++ break;
++ case EMU_BTN_TRIGGER_RIGHT:
++ rt[this->m_port] = (value ? 255 : 0);
++ break;
++ default:
++ SET_FLAG(kcode[this->m_port], *button_id, value);
++ };
++}
++
++void InputHandler::handle_axis(InputAxisCode code, int value)
++{
++ if(this->m_mapping == NULL)
++ {
++ return;
++ }
++ const InputAxisID* axis_id = this->m_mapping->get_axis_id(code);
++ if(axis_id == NULL)
++ {
++ printf("Ignoring %d\n", code);
++ return;
++ }
++ switch(*axis_id)
++ {
++ case EMU_AXIS_DPAD1_X:
++ case EMU_AXIS_DPAD1_Y:
++ case EMU_AXIS_DPAD2_X:
++ case EMU_AXIS_DPAD2_Y:
++ {
++ InputButtonID axis_button_id[2];
++ switch(*axis_id)
++ {
++ case EMU_AXIS_DPAD1_X:
++ axis_button_id[0] = DC_BTN_DPAD1_LEFT;
++ axis_button_id[1] = DC_BTN_DPAD1_RIGHT;
++ break;
++ case EMU_AXIS_DPAD1_Y:
++ axis_button_id[0] = DC_BTN_DPAD1_UP;
++ axis_button_id[1] = DC_BTN_DPAD1_DOWN;
++ break;
++ case EMU_AXIS_DPAD2_X:
++ axis_button_id[0] = DC_BTN_DPAD2_LEFT;
++ axis_button_id[1] = DC_BTN_DPAD2_RIGHT;
++ break;
++ case EMU_AXIS_DPAD2_Y:
++ axis_button_id[0] = DC_BTN_DPAD2_UP;
++ axis_button_id[1] = DC_BTN_DPAD2_DOWN;
++ }
++ bool axis_button_value[2];
++ axis_button_value[0] = (value < 0);
++ axis_button_value[1] = (value > 0);
++ SET_FLAG(kcode[this->m_port], axis_button_id[0], axis_button_value[0]);
++ SET_FLAG(kcode[this->m_port], axis_button_id[1], axis_button_value[1]);
++ break;
++ }
++ case DC_AXIS_X:
++ case DC_AXIS_Y:
++ case DC_AXIS_TRIGGER_LEFT:
++ case DC_AXIS_TRIGGER_RIGHT:
++ {
++ InputAxisConverter* converter = this->get_axis_converter(*axis_id);
++ s8 converted_value = ((converter == NULL) ? value : converter->convert(value));
++ switch(*axis_id)
++ {
++ case DC_AXIS_X:
++ joyx[this->m_port] = (converted_value + 128);
++ break;
++ case DC_AXIS_Y:
++ joyy[this->m_port] = (converted_value + 128);
++ break;
++ case DC_AXIS_TRIGGER_LEFT:
++ lt[this->m_port] = converted_value;
++ break;
++ case DC_AXIS_TRIGGER_RIGHT:
++ rt[this->m_port] = converted_value;
++ break;
++ }
++ }
++ }
++}
++
++void InputHandler::enable_axis_converter(InputAxisID axis, bool inverted, InputAxisLimits limits)
++{
++ this->disable_axis_converter(axis); // Delete old axis converter
++ this->m_axis_converters[axis] = new InputAxisConverter(inverted, limits);
++}
++
++void InputHandler::disable_axis_converter(InputAxisID axis)
++{
++ InputAxisConverterStore::iterator iter = this->m_axis_converters.find(axis);
++ if(iter != this->m_axis_converters.end())
++ {
++ delete iter->second;
++ this->m_axis_converters.erase(iter);
++ }
++}
++
++InputAxisConverter* InputHandler::get_axis_converter(InputAxisID axis)
++{
++ InputAxisConverterStore::iterator iter = this->m_axis_converters.find(axis);
++ if(iter == this->m_axis_converters.end())
++ {
++ return NULL;
++ }
++ return iter->second;
++}
+\ No newline at end of file
+diff -Nur a/core/linux-dist/handler_evdev.cpp b/core/linux-dist/handler_evdev.cpp
+--- a/core/linux-dist/handler_evdev.cpp 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/handler_evdev.cpp 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,132 @@
++#if defined(USE_EVDEV)
++#include "handler_evdev.h"
++#include <cstring>
++#include <cerrno>
++#include <fcntl.h>
++#include <unistd.h>
++#include <linux/input.h>
++
++#define EVDEV_DEVICE_STRING "/dev/input/event%s"
++
++void EvdevInputHandler::get_axis_limits(const InputAxisCode code, InputAxisLimits& limits)
++{
++ struct input_absinfo abs;
++ if(!this->m_evdev_fd < 0 || code < 0 || ioctl(this->m_evdev_fd, EVIOCGABS(code), &abs))
++ {
++ if(this->m_evdev_fd >= 0 && code >= 0)
++ {
++ perror("evdev ioctl");
++ }
++ limits.minimum = 0;
++ limits.maximum = 0;
++ limits.deadzone = 0;
++ return;
++ }
++ limits.minimum = abs.minimum;
++ limits.maximum = abs.maximum;
++ limits.deadzone = abs.flat;
++}
++
++std::string EvdevInputHandler::get_api_name()
++{
++ return "evdev";
++}
++
++bool EvdevInputHandler::setup_device(std::string device)
++{
++ size_t size_needed = snprintf(NULL, 0, EVDEV_DEVICE_STRING, device.c_str()) + 1;
++ char* evdev_fname = (char*)malloc(size_needed);
++ sprintf(evdev_fname, EVDEV_DEVICE_STRING, device.c_str());
++
++ printf("evdev: Trying to open device '%s'\n", evdev_fname);
++
++ this->m_evdev_fd = open(evdev_fname, O_RDONLY);
++
++ char device_name[256] = "Unknown";
++
++ if (this->m_evdev_fd < 0)
++ {
++ printf("evdev: Opening device '%s' failed - %s", evdev_fname, strerror(errno));
++ free(evdev_fname);
++ return false;
++ }
++
++ fcntl(this->m_evdev_fd, F_SETFL, O_NONBLOCK);
++
++ // Get device name
++ if(ioctl(this->m_evdev_fd, EVIOCGNAME(sizeof(device_name)), device_name) < 0)
++ {
++ printf("evdev: Getting name of '%s' (ioctl) failed - %s", evdev_fname, strerror(errno));
++ free(evdev_fname);
++ return false;
++ }
++
++ printf("evdev: Found '%s' at '%s'\n", device_name, evdev_fname);
++ this->m_evdev_devname = std::string(device_name);
++
++ free(evdev_fname);
++
++ return true;
++}
++
++std::string EvdevInputHandler::get_default_mapping_filename()
++{
++ if (this->m_evdev_devname.empty())
++ {
++ return "";
++ }
++
++ std::string mapping_filename;
++ #if defined(TARGET_PANDORA)
++ mapping_filename = "controller_pandora.cfg";
++ #elif defined(TARGET_GCW0)
++ mapping_filename = "controller_gcwz.cfg";
++ #else
++ if (strstr(this->m_evdev_devname.c_str(), "Microsoft X-Box 360 pad") == NULL || strstr(this->m_evdev_devname.c_str(), "Xbox 360 Wireless Receiver") == NULL)
++ {
++ mapping_filename = "controller_xpad.cfg";
++ }
++ else if (strstr(this->m_evdev_devname.c_str(), "Xbox Gamepad (userspace driver)") != NULL)
++ {
++ mapping_filename = "controller_xboxdrv.cfg";
++ }
++ else if (strstr(this->m_evdev_devname.c_str(), "keyboard") != NULL || strstr(this->m_evdev_devname.c_str(), "Keyboard") != NULL)
++ {
++ mapping_filename = "keyboard.cfg";
++ }
++ else
++ {
++ mapping_filename = "controller_generic.cfg";
++ }
++ #endif
++
++ return mapping_filename;
++}
++
++void EvdevInputHandler::handle()
++{
++ if (!this->is_initialized())
++ {
++ return;
++ }
++
++ input_event ie;
++ while(read(this->m_evdev_fd, &ie, sizeof(ie)) == sizeof(ie))
++ {
++ //printf("evdev: type = %d - code = %d - value = %d\n", ie.type, ie.code, ie.value);
++ switch(ie.type)
++ {
++ case EV_KEY:
++ {
++ this->handle_button(ie.code, ie.value);
++ break;
++ }
++ case EV_ABS:
++ {
++ this->handle_axis(ie.code, ie.value);
++ break;
++ }
++ }
++ }
++}
++#endif
+diff -Nur a/core/linux-dist/handler_evdev.h b/core/linux-dist/handler_evdev.h
+--- a/core/linux-dist/handler_evdev.h 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/handler_evdev.h 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,15 @@
++#pragma once
++#include "handler.h"
++
++class EvdevInputHandler : public InputHandler
++{
++ private:
++ int m_evdev_fd;
++ std::string m_evdev_devname;
++ void get_axis_limits(const InputAxisCode code, InputAxisLimits& limits);
++ public:
++ void handle();
++ std::string get_api_name();
++ std::string get_default_mapping_filename();
++ bool setup_device(std::string device);
++};
+\ No newline at end of file
+diff -Nur a/core/linux-dist/handler.h b/core/linux-dist/handler.h
+--- a/core/linux-dist/handler.h 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/handler.h 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,49 @@
++#pragma once
++#include "types.h"
++#include "mapping.h"
++#include "mappingstore.h"
++
++struct InputAxisLimits
++{
++ s32 minimum;
++ s32 maximum;
++ s32 deadzone;
++};
++
++class InputAxisConverter
++{
++ private:
++ s32 m_minimum;
++ s32 m_range;
++ s32 m_deadzone;
++ public:
++ InputAxisConverter(bool inverted, InputAxisLimits limits);
++ s8 convert(s32 value);
++};
++
++class InputHandler
++{
++ typedef std::map<InputAxisID, InputAxisConverter*> InputAxisConverterStore;
++ private:
++ InputAxisConverterStore m_axis_converters;
++ void enable_axis_converter(InputAxisID axis, bool inverted, InputAxisLimits limits);
++ void disable_axis_converter(InputAxisID axis);
++ InputAxisConverter* get_axis_converter(InputAxisID axis);
++ bool m_initialized;
++ protected:
++ static InputMappingStore s_mappingstore;
++ u32 m_port;
++ InputMapping* m_mapping;
++ bool is_initialized();
++ void handle_button(InputButtonCode code, int value);
++ void handle_axis(InputAxisCode code, int value);
++ virtual void get_axis_limits(const InputAxisCode code, InputAxisLimits& limits);
++ public:
++ InputHandler();
++ ~InputHandler();
++ bool initialize(u32 port, std::string device, std::string custom_mapping);
++ virtual std::string get_default_mapping_filename();
++ virtual void handle() = 0;
++ virtual std::string get_api_name() = 0;
++ virtual bool setup_device(std::string device) = 0;
++};
+\ No newline at end of file
+diff -Nur a/core/linux-dist/handler_linuxjs.cpp b/core/linux-dist/handler_linuxjs.cpp
+--- a/core/linux-dist/handler_linuxjs.cpp 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/handler_linuxjs.cpp 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,127 @@
++#if defined(USE_JOYSTICK)
++#include "handler_linuxjs.h"
++#include <cstring>
++#include <cerrno>
++#include <fcntl.h>
++#include <unistd.h>
++#include <linux/joystick.h>
++
++#define LINUXJS_DEVICE_STRING "/dev/input/js%s"
++
++void LinuxJoystickInputHandler::get_axis_limits(const InputAxisCode code, InputAxisLimits& limits)
++{
++ // The Linux Joystick API's axes always normalized to this minimum/maximum
++ limits.minimum = -32767;
++ limits.maximum = 32767;
++ limits.deadzone = 0;
++}
++
++std::string LinuxJoystickInputHandler::get_api_name()
++{
++ return "linuxjs";
++}
++
++bool LinuxJoystickInputHandler::setup_device(std::string device)
++{
++ size_t size_needed = snprintf(NULL, 0, LINUXJS_DEVICE_STRING, device.c_str()) + 1;
++ char* linuxjs_fname = (char*)malloc(size_needed);
++ sprintf(linuxjs_fname, LINUXJS_DEVICE_STRING, device.c_str());
++
++ printf("linuxjs: Trying to open device '%s'\n", linuxjs_fname);
++
++ this->m_linuxjs_fd = open(linuxjs_fname, O_RDONLY);
++
++ if (this->m_linuxjs_fd < 0)
++ {
++ printf("linuxjs: Opening device '%s' failed - %s", linuxjs_fname, strerror(errno));
++ free(linuxjs_fname);
++ return false;
++ }
++
++ fcntl(this->m_linuxjs_fd, F_SETFL, O_NONBLOCK);
++
++ char device_name[256] = "Unknown";
++ int button_count = 0;
++ int axis_count = 0;
++
++ // Get device name
++ if(ioctl(this->m_linuxjs_fd, JSIOCGNAME(sizeof(device_name)), device_name) < 0)
++ {
++ printf("linuxjs: Getting name of '%s' (ioctl) failed - %s", linuxjs_fname, strerror(errno));
++ free(linuxjs_fname);
++ return false;
++ }
++
++ // Get number of buttons
++ if(ioctl(this->m_linuxjs_fd, JSIOCGBUTTONS, &button_count) < 0)
++ {
++ printf("linuxjs: Getting button count of '%s' (ioctl) failed - %s", linuxjs_fname, strerror(errno));
++ free(linuxjs_fname);
++ return false;
++ }
++
++ // Get number of axes
++ if(ioctl(this->m_linuxjs_fd, JSIOCGAXES, &axis_count) < 0)
++ {
++ printf("linuxjs: Getting axis count of '%s' (ioctl) failed - %s", linuxjs_fname, strerror(errno));
++ free(linuxjs_fname);
++ return false;
++ }
++
++ printf("linuxjs: Found '%s' with %d axes and %d buttons at '%s'\n", device_name, axis_count, button_count, linuxjs_fname);
++ this->m_linuxjs_devname = std::string(device_name);
++
++ free(linuxjs_fname);
++
++ return true;
++}
++
++std::string LinuxJoystickInputHandler::get_default_mapping_filename()
++{
++ if (this->m_linuxjs_devname.empty())
++ {
++ return "";
++ }
++
++ std::string mapping_filename;
++ if (strstr(this->m_linuxjs_devname.c_str(), "Microsoft X-Box 360 pad") != NULL ||
++ strstr(this->m_linuxjs_devname.c_str(), "Xbox Gamepad (userspace driver)") != NULL ||
++ strstr(this->m_linuxjs_devname.c_str(), "Xbox 360 Wireless Receiver") != NULL)
++ {
++ mapping_filename = "controller_xbox360.cfg";
++ }
++ else
++ {
++ mapping_filename = "controller_generic.cfg";
++ }
++
++ return mapping_filename;
++}
++
++void LinuxJoystickInputHandler::handle()
++{
++ if (!this->is_initialized())
++ {
++ return;
++ }
++
++ struct js_event je;
++ while(read(this->m_linuxjs_fd, &je, sizeof(je)) == sizeof(je))
++ {
++ printf("linuxjs: type = %d - code = %d - value = %d\n", je.type, je.number, je.value);
++ switch(je.type)
++ {
++ case JS_EVENT_BUTTON:
++ {
++ this->handle_button(je.number, je.value);
++ break;
++ }
++ case JS_EVENT_AXIS:
++ {
++ this->handle_axis(je.number, je.value);
++ break;
++ }
++ }
++ }
++}
++#endif
+\ No newline at end of file
+diff -Nur a/core/linux-dist/handler_linuxjs.h b/core/linux-dist/handler_linuxjs.h
+--- a/core/linux-dist/handler_linuxjs.h 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/handler_linuxjs.h 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,15 @@
++#pragma once
++#include "handler.h"
++
++class LinuxJoystickInputHandler : public InputHandler
++{
++ private:
++ int m_linuxjs_fd;
++ std::string m_linuxjs_devname;
++ void get_axis_limits(const InputAxisCode code, InputAxisLimits& limits);
++ public:
++ void handle();
++ std::string get_api_name();
++ std::string get_default_mapping_filename();
++ bool setup_device(std::string device);
++};
+\ No newline at end of file
+diff -Nur a/core/linux-dist/joystick.cpp b/core/linux-dist/joystick.cpp
+--- a/core/linux-dist/joystick.cpp 2015-10-06 21:43:53.042336401 -0300
++++ b/core/linux-dist/joystick.cpp 1969-12-31 21:00:00.000000000 -0300
+@@ -1,158 +0,0 @@
+-#include <unistd.h>
+-#include <fcntl.h>
+-#include <sys/types.h>
+-#include <linux/joystick.h>
+-#include "linux-dist/joystick.h"
+-
+-#if defined(USE_JOYSTICK)
+- const u32 joystick_map_btn_usb[JOYSTICK_MAP_SIZE] = { DC_BTN_Y, DC_BTN_B, DC_BTN_A, DC_BTN_X, 0, 0, 0, 0, 0, DC_BTN_START };
+- const u32 joystick_map_axis_usb[JOYSTICK_MAP_SIZE] = { DC_AXIS_X, DC_AXIS_Y, 0, 0, 0, 0, 0, 0, 0, 0 };
+-
+- const u32 joystick_map_btn_xbox360[JOYSTICK_MAP_SIZE] = { DC_BTN_A, DC_BTN_B, DC_BTN_X, DC_BTN_Y, 0, 0, 0, DC_BTN_START, 0, 0 };
+- const u32 joystick_map_axis_xbox360[JOYSTICK_MAP_SIZE] = { DC_AXIS_X, DC_AXIS_Y, DC_AXIS_LT, 0, 0, DC_AXIS_RT, DC_DPAD_LEFT, DC_DPAD_UP, 0, 0 };
+-
+- const u32* joystick_map_btn = joystick_map_btn_usb;
+- const u32* joystick_map_axis = joystick_map_axis_usb;
+-
+- int input_joystick_init(const char* device)
+- {
+- int axis_count = 0;
+- int button_count = 0;
+- char name[128] = "Unknown";
+-
+- printf("joystick: Trying to open device at '%s'\n", device);
+-
+- int fd = open(device, O_RDONLY);
+-
+- if(fd >= 0)
+- {
+- fcntl(fd, F_SETFL, O_NONBLOCK);
+- ioctl(fd, JSIOCGAXES, &axis_count);
+- ioctl(fd, JSIOCGBUTTONS, &button_count);
+- ioctl(fd, JSIOCGNAME(sizeof(name)), &name);
+-
+- printf("joystick: Found '%s' with %d axis and %d buttons at '%s'.\n", name, axis_count, button_count, device);
+-
+- if (strcmp(name, "Microsoft X-Box 360 pad") == 0 ||
+- strcmp(name, "Xbox Gamepad (userspace driver)") == 0 ||
+- strcmp(name, "Xbox 360 Wireless Receiver (XBOX)") == 0)
+- {
+- joystick_map_btn = joystick_map_btn_xbox360;
+- joystick_map_axis = joystick_map_axis_xbox360;
+- printf("joystick: Using Xbox 360 map\n");
+- }
+- }
+- else
+- {
+- perror("joystick open");
+- }
+-
+- return fd;
+- }
+-
+- bool input_joystick_handle(int fd, u32 port)
+- {
+- // Joystick must be connected
+- if(fd < 0) {
+- return false;
+- }
+-
+- struct js_event JE;
+- while(read(fd, &JE, sizeof(JE)) == sizeof(JE))
+- if (JE.number < JOYSTICK_MAP_SIZE)
+- {
+- switch(JE.type & ~JS_EVENT_INIT)
+- {
+- case JS_EVENT_AXIS:
+- {
+- u32 mt = joystick_map_axis[JE.number] >> 16;
+- u32 mo = joystick_map_axis[JE.number] & 0xFFFF;
+-
+- //printf("AXIS %d,%d\n",JE.number,JE.value);
+- s8 v=(s8)(JE.value/256); //-127 ... + 127 range
+-
+- if (mt == 0)
+- {
+- kcode[port] |= mo;
+- kcode[port] |= mo*2;
+- if (v<-64)
+- {
+- kcode[port] &= ~mo;
+- }
+- else if (v>64)
+- {
+- kcode[port] &= ~(mo*2);
+- }
+-
+- //printf("Mapped to %d %d %d\n",mo,kcode[port]&mo,kcode[port]&(mo*2));
+- }
+- else if (mt == 1)
+- {
+- if (v >= 0)
+- {
+- v++; //up to 255
+- }
+- //printf("AXIS %d,%d Mapped to %d %d %d\n",JE.number,JE.value,mo,v,v+127);
+- if (mo == 0)
+- {
+- lt[port] = (v + 127);
+- }
+- else if (mo == 1)
+- {
+- rt[port] = (v + 127);
+- }
+- }
+- else if (mt == 2)
+- {
+- // printf("AXIS %d,%d Mapped to %d %d [%d]",JE.number,JE.value,mo,v);
+- if (mo == 0)
+- {
+- joyx[port] = v;
+- }
+- else if (mo == 1)
+- {
+- joyy[port] = v;
+- }
+- }
+- }
+- break;
+-
+- case JS_EVENT_BUTTON:
+- {
+- u32 mt = joystick_map_btn[JE.number] >> 16;
+- u32 mo = joystick_map_btn[JE.number] & 0xFFFF;
+-
+- // printf("BUTTON %d,%d\n",JE.number,JE.value);
+-
+- if (mt == 0)
+- {
+- // printf("Mapped to %d\n",mo);
+- if (JE.value)
+- {
+- kcode[port] &= ~mo;
+- }
+- else
+- {
+- kcode[port] |= mo;
+- }
+- }
+- else if (mt == 1)
+- {
+- // printf("Mapped to %d %d\n",mo,JE.value?255:0);
+- if (mo==0)
+- {
+- lt[port] = JE.value ? 255 : 0;
+- }
+- else if (mo==1)
+- {
+- rt[port] = JE.value ? 255 : 0;
+- }
+- }
+- }
+- break;
+- }
+- }
+-
+- return true;
+- }
+-#endif
+diff -Nur a/core/linux-dist/joystick.h b/core/linux-dist/joystick.h
+--- a/core/linux-dist/joystick.h 2015-10-06 21:43:53.042336401 -0300
++++ b/core/linux-dist/joystick.h 1969-12-31 21:00:00.000000000 -0300
+@@ -1,10 +0,0 @@
+-#include "types.h"
+-#include "linux-dist/main.h"
+-
+-#pragma once
+-#define JOYSTICK_DEVICE_STRING "/dev/input/js%d"
+-#define JOYSTICK_DEFAULT_DEVICE_ID -1
+-#define JOYSTICK_MAP_SIZE 32
+-
+-extern int input_joystick_init(const char* device);
+-extern bool input_joystick_handle(int fd, u32 port);
+diff -Nur a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp
+--- a/core/linux-dist/main.cpp 2015-10-06 21:43:53.042336401 -0300
++++ b/core/linux-dist/main.cpp 2015-10-06 21:55:43.966475140 -0300
+@@ -14,6 +14,7 @@
+ #include <sys/time.h>
+ #include "hw/sh4/dyna/blockmanager.h"
+ #include <unistd.h>
++#include "proxy.h"
+
+ #if defined(TARGET_EMSCRIPTEN)
+ #include <emscripten.h>
+@@ -32,11 +33,11 @@
+ #endif
+
+ #if defined(USE_EVDEV)
+- #include "linux-dist/evdev.h"
++ #include "linux-dist/handler_evdev.h"
+ #endif
+
+ #if defined(USE_JOYSTICK)
+- #include "linux-dist/joystick.h"
++ #include "linux-dist/handler_linuxjs.h"
+ #endif
+
+ #ifdef TARGET_PANDORA
+@@ -84,82 +85,68 @@
+
+ void emit_WriteCodeCache();
+
+-#if defined(USE_EVDEV)
+- /* evdev input */
+- static EvdevController evdev_controllers[4] = {
+- { -1, NULL },
+- { -1, NULL },
+- { -1, NULL },
+- { -1, NULL }
+- };
+-#endif
+-
+-#if defined(USE_JOYSTICK)
+- /* legacy joystick input */
+- static int joystick_fd = -1; // Joystick file descriptor
+-#endif
++static InputHandlerProxy input_handlers;
+
+ void SetupInput()
+ {
+ #if defined(USE_EVDEV)
+- int evdev_device_id[4] = { -1, -1, -1, -1 };
+- size_t size_needed;
+- int port, i;
++ #define EVDEV_DEVICE_CONFIG_KEY "evdev_device_id_%d"
++ #define EVDEV_MAPPING_CONFIG_KEY "evdev_mapping_%d"
+
+- char* evdev_device;
++ #ifdef TARGET_PANDORA
++ #define EVDEV_DEFAULT_DEVICE_ID_1 "4"
++ #else
++ #define EVDEV_DEFAULT_DEVICE_ID_1 "0"
++ #endif
++ #define EVDEV_DEFAULT_DEVICE_ID(port) (port == 1 ? EVDEV_DEFAULT_DEVICE_ID_1 : "-1")
+
+- for (port = 0; port < 4; port++)
++ for (int port = 0; port < 4; port++)
+ {
++ size_t size_needed;
++ char* evdev_config_key;
++ std::string evdev_device;
++ std::string custom_mapping_filename;
++
+ size_needed = snprintf(NULL, 0, EVDEV_DEVICE_CONFIG_KEY, port+1) + 1;
+- char* evdev_config_key = (char*)malloc(size_needed);
++ evdev_config_key = (char*)malloc(size_needed);
+ sprintf(evdev_config_key, EVDEV_DEVICE_CONFIG_KEY, port+1);
+- evdev_device_id[port] = cfgLoadInt("input", evdev_config_key, EVDEV_DEFAULT_DEVICE_ID(port+1));
++ evdev_device = cfgLoadStr("input", evdev_config_key, EVDEV_DEFAULT_DEVICE_ID(port+1));
+ free(evdev_config_key);
+
+- // Check if the same device is already in use on another port
+- if (evdev_device_id[port] < 0)
++ if(evdev_device.c_str()[0] == '-')
+ {
+ printf("evdev: Controller %d disabled by config.\n", port + 1);
++ continue;
+ }
+- else
+- {
+- for (i = 0; i < port; i++)
+- {
+- if (evdev_device_id[port] == evdev_device_id[i])
+- {
+- die("You can't assign the same device to multiple ports!\n");
+- }
+- }
+-
+- size_needed = snprintf(NULL, 0, EVDEV_DEVICE_STRING, evdev_device_id[port]) + 1;
+- evdev_device = (char*)malloc(size_needed);
+- sprintf(evdev_device, EVDEV_DEVICE_STRING, evdev_device_id[port]);
+-
+- size_needed = snprintf(NULL, 0, EVDEV_MAPPING_CONFIG_KEY, port+1) + 1;
+- evdev_config_key = (char*)malloc(size_needed);
+- sprintf(evdev_config_key, EVDEV_MAPPING_CONFIG_KEY, port+1);
+- const char* mapping = (cfgExists("input", evdev_config_key) == 2 ? cfgLoadStr("input", evdev_config_key, "").c_str() : NULL);
+- free(evdev_config_key);
+
+- input_evdev_init(&evdev_controllers[port], evdev_device, mapping);
++ size_needed = snprintf(NULL, 0, EVDEV_MAPPING_CONFIG_KEY, port+1) + 1;
++ evdev_config_key = (char*)malloc(size_needed);
++ sprintf(evdev_config_key, EVDEV_MAPPING_CONFIG_KEY, port+1);
++ custom_mapping_filename = (cfgExists("input", evdev_config_key) == 2 ? cfgLoadStr("input", evdev_config_key, "") : "");
++ free(evdev_config_key);
+
+- free(evdev_device);
+- }
++ EvdevInputHandler* handler = new EvdevInputHandler();
++ handler->initialize(port, evdev_device, custom_mapping_filename);
++ input_handlers.add(port, handler);
+ }
+ #endif
+
+ #if defined(USE_JOYSTICK)
+- int joystick_device_id = cfgLoadInt("input", "joystick_device_id", JOYSTICK_DEFAULT_DEVICE_ID);
+- if (joystick_device_id < 0) {
+- puts("Legacy Joystick input disabled by config.\n");
++ #define JOYSTICK_DEFAULT_DEVICE_ID "-1"
++
++ std::string linuxjs_device;
++ std::string custom_mapping_filename = "";
++
++ linuxjs_device = cfgLoadStr("input", "joystick_device_id", JOYSTICK_DEFAULT_DEVICE_ID);
++ if (linuxjs_device.c_str()[0] == '-')
++ {
++ puts("LinuxJoystick input disabled by config.\n");
+ }
+ else
+ {
+- int joystick_device_length = snprintf(NULL, 0, JOYSTICK_DEVICE_STRING, joystick_device_id);
+- char* joystick_device = (char*)malloc(joystick_device_length + 1);
+- sprintf(joystick_device, JOYSTICK_DEVICE_STRING, joystick_device_id);
+- joystick_fd = input_joystick_init(joystick_device);
+- free(joystick_device);
++ LinuxJoystickInputHandler* handler = new LinuxJoystickInputHandler();
++ handler->initialize(0, linuxjs_device, custom_mapping_filename);
++ input_handlers.add(0, handler);
+ }
+ #endif
+
+@@ -178,13 +165,7 @@
+ return;
+ #endif
+
+- #if defined(USE_JOYSTICK)
+- input_joystick_handle(joystick_fd, port);
+- #endif
+-
+- #if defined(USE_EVDEV)
+- input_evdev_handle(&evdev_controllers[port], port);
+- #endif
++ input_handlers.handle(port);
+
+ #if defined(USE_SDL)
+ input_sdl_handle(port);
+diff -Nur a/core/linux-dist/main.h b/core/linux-dist/main.h
+--- a/core/linux-dist/main.h 2015-10-06 21:43:53.042336401 -0300
++++ b/core/linux-dist/main.h 2015-10-06 21:55:43.966475140 -0300
+@@ -11,25 +11,34 @@
+
+ enum DreamcastController
+ {
+- DC_BTN_C = 1,
+- DC_BTN_B = 1<<1,
+- DC_BTN_A = 1<<2,
+- DC_BTN_START = 1<<3,
+- DC_DPAD_UP = 1<<4,
+- DC_DPAD_DOWN = 1<<5,
+- DC_DPAD_LEFT = 1<<6,
+- DC_DPAD_RIGHT = 1<<7,
+- DC_BTN_Z = 1<<8,
+- DC_BTN_Y = 1<<9,
+- DC_BTN_X = 1<<10,
+- DC_BTN_D = 1<<11,
+- DC_DPAD2_UP = 1<<12,
+- DC_DPAD2_DOWN = 1<<13,
+- DC_DPAD2_LEFT = 1<<14,
+- DC_DPAD2_RIGHT = 1<<15,
++ EMU_BTN_NONE = 0,
++ EMU_BTN_ESCAPE = 1<<16,
++ EMU_BTN_TRIGGER_LEFT = 1<<17,
++ EMU_BTN_TRIGGER_RIGHT = 1<<18,
++ DC_BTN_C = 1,
++ DC_BTN_B = 1<<1,
++ DC_BTN_A = 1<<2,
++ DC_BTN_START = 1<<3,
++ DC_BTN_DPAD1_UP = 1<<4,
++ DC_BTN_DPAD1_DOWN = 1<<5,
++ DC_BTN_DPAD1_LEFT = 1<<6,
++ DC_BTN_DPAD1_RIGHT = 1<<7,
++ DC_BTN_Z = 1<<8,
++ DC_BTN_Y = 1<<9,
++ DC_BTN_X = 1<<10,
++ DC_BTN_D = 1<<11,
++ DC_BTN_DPAD2_UP = 1<<12,
++ DC_BTN_DPAD2_DOWN = 1<<13,
++ DC_BTN_DPAD2_LEFT = 1<<14,
++ DC_BTN_DPAD2_RIGHT = 1<<15,
+
+- DC_AXIS_LT = 0X10000,
+- DC_AXIS_RT = 0X10001,
+- DC_AXIS_X = 0X20000,
+- DC_AXIS_Y = 0X20001,
++ EMU_AXIS_NONE = 0X00000,
++ EMU_AXIS_DPAD1_X = 0X00001,
++ EMU_AXIS_DPAD1_Y = 0X00002,
++ EMU_AXIS_DPAD2_X = 0X00003,
++ EMU_AXIS_DPAD2_Y = 0X00004,
++ DC_AXIS_TRIGGER_LEFT = 0X10000,
++ DC_AXIS_TRIGGER_RIGHT = 0X10001,
++ DC_AXIS_X = 0X20000,
++ DC_AXIS_Y = 0X20001,
+ };
+diff -Nur a/core/linux-dist/mapping.cpp b/core/linux-dist/mapping.cpp
+--- a/core/linux-dist/mapping.cpp 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/mapping.cpp 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,159 @@
++#include "linux-dist/mapping.h"
++#include "cfg/ini.h"
++
++struct ButtonMap
++{
++ InputButtonID id;
++ string section;
++ string option;
++};
++
++struct AxisMap
++{
++ InputAxisID id;
++ string section;
++ string option;
++ string section_inverted;
++ string option_inverted;
++};
++
++static ButtonMap button_list[19] = {
++ { DC_BTN_A, "dreamcast", "btn_a" },
++ { DC_BTN_B, "dreamcast", "btn_b" },
++ { DC_BTN_C, "dreamcast", "btn_c" },
++ { DC_BTN_D, "dreamcast", "btn_d" },
++ { DC_BTN_X, "dreamcast", "btn_x" },
++ { DC_BTN_Y, "dreamcast", "btn_y" },
++ { DC_BTN_Z, "dreamcast", "btn_z" },
++ { DC_BTN_START, "dreamcast", "btn_start" },
++ { DC_BTN_DPAD1_LEFT, "dreamcast", "btn_dpad1_left" },
++ { DC_BTN_DPAD1_RIGHT, "dreamcast", "btn_dpad1_right" },
++ { DC_BTN_DPAD1_UP, "dreamcast", "btn_dpad1_up" },
++ { DC_BTN_DPAD1_DOWN, "dreamcast", "btn_dpad1_down" },
++ { DC_BTN_DPAD2_LEFT, "dreamcast", "btn_dpad2_left" },
++ { DC_BTN_DPAD2_RIGHT, "dreamcast", "btn_dpad2_right" },
++ { DC_BTN_DPAD2_UP, "dreamcast", "btn_dpad2_up" },
++ { DC_BTN_DPAD2_DOWN, "dreamcast", "btn_dpad2_down" },
++ { EMU_BTN_ESCAPE, "emulator", "btn_escape" },
++ { EMU_BTN_TRIGGER_LEFT, "compat", "btn_trigger_left" },
++ { EMU_BTN_TRIGGER_RIGHT, "compat", "btn_trigger_right" }
++};
++
++static AxisMap axis_list[8] = {
++ { DC_AXIS_X, "dreamcast", "axis_x", "compat", "axis_x_inverted" },
++ { DC_AXIS_Y, "dreamcast", "axis_y", "compat", "axis_y_inverted" },
++ { DC_AXIS_TRIGGER_LEFT, "dreamcast", "axis_trigger_left", "compat", "axis_trigger_left_inverted" },
++ { DC_AXIS_TRIGGER_RIGHT, "dreamcast", "axis_trigger_right", "compat", "axis_trigger_right_inverted" },
++ { EMU_AXIS_DPAD1_X, "compat", "axis_dpad1_x", "compat", "axis_dpad1_x_inverted" },
++ { EMU_AXIS_DPAD1_Y, "compat", "axis_dpad1_y", "compat", "axis_dpad1_y_inverted" },
++ { EMU_AXIS_DPAD2_X, "compat", "axis_dpad2_x", "compat", "axis_dpad2_x_inverted" },
++ { EMU_AXIS_DPAD2_Y, "compat", "axis_dpad2_y", "compat", "axis_dpad2_y_inverted" }
++};
++
++const InputButtonID* InputMapping::get_button_id(InputButtonCode code)
++{
++ return this->buttons.get_by_value(code);
++}
++
++const InputButtonCode* InputMapping::get_button_code(InputButtonID id)
++{
++ return this->buttons.get_by_key(id);
++}
++
++void InputMapping::set_button(InputButtonID id, InputButtonCode code)
++{
++ if(id != EMU_BTN_NONE)
++ {
++ this->buttons.insert(id, code);
++ }
++}
++
++const InputAxisID* InputMapping::get_axis_id(InputAxisCode code)
++{
++ return this->axes.get_by_value(code);
++}
++
++const InputAxisCode* InputMapping::get_axis_code(InputAxisID id)
++{
++ return this->axes.get_by_key(id);
++}
++
++bool InputMapping::get_axis_inverted(InputAxisCode code)
++{
++ std::map<InputAxisCode, bool>::iterator it = this->axes_inverted.find('b');
++ if (it != this->axes_inverted.end())
++ {
++ return it->second;
++ }
++ return false;
++}
++
++void InputMapping::set_axis(InputAxisID id, InputAxisCode code, bool is_inverted)
++{
++ if(id != EMU_AXIS_NONE)
++ {
++ this->axes.insert(id, code);
++ this->axes_inverted.insert(std::pair<InputAxisCode,bool>(code, is_inverted));
++ }
++}
++
++void InputMapping::load(FILE* fd)
++{
++ ConfigFile mf;
++ mf.parse(fd);
++
++ this->name = mf.get("emulator", "mapping_name", "<Unknown>").c_str();
++
++ int i;
++ for(i = 0; i < 19; i++)
++ {
++ InputButtonCode button_code = mf.get_int(button_list[i].section, button_list[i].option, -1);
++ if (button_code >= 0)
++ {
++ this->set_button(button_list[i].id, button_code);
++ }
++ }
++
++ for(i = 0; i < 8; i++)
++ {
++ InputAxisCode axis_code = mf.get_int(axis_list[i].section, axis_list[i].option, -1);
++ if(axis_code >= 0)
++ {
++ this->set_axis(axis_list[i].id, axis_code, mf.get_bool(axis_list[i].section_inverted, axis_list[i].option_inverted, false));
++ }
++ }
++}
++
++void InputMapping::print()
++{
++ int i;
++
++ printf("\nMAPPING NAME: %s\n", this->name);
++
++ puts("MAPPED BUTTONS:");
++ for(i = 0; i < 19; i++)
++ {
++ const InputButtonCode* button_code = this->get_button_code(button_list[i].id);
++ if(button_code)
++ {
++ printf("%-18s = %d\n", button_list[i].option.c_str(), *button_code);
++ }
++ }
++
++ puts("MAPPED AXES:");
++ for(i = 0; i < 8; i++)
++ {
++ const InputAxisCode* axis_code = this->get_axis_code(axis_list[i].id);
++ if(axis_code)
++ {
++ printf("%-18s = %d", axis_list[i].option.c_str(), *axis_code);
++ if(this->get_axis_inverted(*axis_code))
++ {
++ printf("%s", "(inverted)");
++ }
++ printf("%s", "\n");
++ }
++ }
++ puts("");
++}
++
+diff -Nur a/core/linux-dist/mapping.h b/core/linux-dist/mapping.h
+--- a/core/linux-dist/mapping.h 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/mapping.h 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,32 @@
++#pragma once
++#include <map>
++#include "linux-dist/main.h"
++#include "linux-dist/bimap.h"
++
++typedef DreamcastController InputButtonID;
++typedef DreamcastController InputAxisID;
++typedef int InputButtonCode;
++typedef int InputAxisCode;
++
++class InputMapping
++{
++ private:
++ SimpleBimap<InputButtonID, InputButtonCode> buttons;
++ SimpleBimap<InputAxisID, InputAxisCode> axes;
++ std::map<InputAxisCode, bool> axes_inverted;
++
++ public:
++ const char* name;
++
++ void set_button(InputButtonID id, InputButtonCode code);
++ const InputButtonID* get_button_id(InputButtonCode code);
++ const InputButtonCode* get_button_code(InputButtonID id);
++
++ void set_axis(InputAxisID id, InputAxisCode code, bool inverted);
++ const InputAxisID* get_axis_id(InputAxisCode code);
++ const InputAxisCode* get_axis_code(InputAxisID id);
++ bool get_axis_inverted(InputAxisCode code);
++
++ void load(FILE* file);
++ void print();
++};
+diff -Nur a/core/linux-dist/mappingstore.cpp b/core/linux-dist/mappingstore.cpp
+--- a/core/linux-dist/mappingstore.cpp 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/mappingstore.cpp 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,61 @@
++#include "mappingstore.h"
++#include <errno.h>
++
++static std::string get_mapping_path(std::string filepath, std::string subdir)
++{
++ if (filepath.empty())
++ {
++ return "";
++ }
++
++ std::string new_filepath;
++ if (filepath.at(0) != '/')
++ {
++ // It's not an absolute path
++ std::string mapping_dir = "/mappings/";
++ if(!subdir.empty())
++ {
++ mapping_dir.append(subdir);
++ mapping_dir.append("/");
++ }
++ filepath.insert(0, mapping_dir);
++ new_filepath = get_readonly_data_path(filepath);
++ }
++ else
++ {
++ new_filepath = filepath;
++ }
++
++ // TODO: Some realpath magic? How portable is it?
++
++ return new_filepath;
++}
++
++InputMapping* InputMappingStore::get(std::string filepath, std::string subdir)
++{
++ std::string full_filepath = get_mapping_path(filepath, subdir);
++ if(!full_filepath.empty())
++ {
++ if(this->loaded_mappings.count(full_filepath) == 0)
++ {
++ // Mapping has not been loaded yet
++ FILE* fp = fopen(full_filepath.c_str(), "r");
++ if(fp == NULL)
++ {
++ printf("Unable to open mapping file '%s': %s\n", full_filepath.c_str(), strerror(errno));
++ }
++ else
++ {
++ InputMapping* mapping = new InputMapping();
++ mapping->load(fp);
++ this->loaded_mappings.insert(std::pair<std::string, InputMapping*>(full_filepath, mapping));
++ }
++ }
++ std::map<std::string, InputMapping*>::iterator it = loaded_mappings.find(full_filepath);
++ if(it != loaded_mappings.end())
++ {
++ return it->second;
++ }
++ }
++ return NULL;
++}
+\ No newline at end of file
+diff -Nur a/core/linux-dist/mappingstore.h b/core/linux-dist/mappingstore.h
+--- a/core/linux-dist/mappingstore.h 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/mappingstore.h 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,11 @@
++#pragma once
++#include "mapping.h"
++#include <string>
++
++class InputMappingStore
++{
++ private:
++ std::map<std::string, InputMapping*> loaded_mappings;
++ public:
++ InputMapping* get(std::string filepath, std::string subdir = "");
++};
+diff -Nur a/core/linux-dist/proxy.cpp b/core/linux-dist/proxy.cpp
+--- a/core/linux-dist/proxy.cpp 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/proxy.cpp 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,25 @@
++#include "proxy.h"
++
++InputHandlerProxy::~InputHandlerProxy()
++{
++ //TODO
++}
++
++void InputHandlerProxy::add(u32 port, InputHandler* handler)
++{
++ if(handler != NULL)
++ {
++ this->handlers.insert(std::pair<u32, InputHandler*>(port, handler));
++ }
++}
++
++void InputHandlerProxy::handle(u32 port)
++{
++ std::pair <InputHandlerStore::iterator, InputHandlerStore::iterator> range;
++ range = this->handlers.equal_range(port);
++
++ for (InputHandlerStore::iterator it = range.first; it != range.second; ++it)
++ {
++ it->second->handle();
++ }
++}
+\ No newline at end of file
+diff -Nur a/core/linux-dist/proxy.h b/core/linux-dist/proxy.h
+--- a/core/linux-dist/proxy.h 1969-12-31 21:00:00.000000000 -0300
++++ b/core/linux-dist/proxy.h 2015-10-06 21:55:43.966475140 -0300
+@@ -0,0 +1,15 @@
++#pragma once
++#include <map>
++#include "types.h"
++#include "handler.h"
++
++class InputHandlerProxy
++{
++ typedef std::multimap<u32, InputHandler*> InputHandlerStore;
++ private:
++ InputHandlerStore handlers;
++ public:
++ ~InputHandlerProxy();
++ void add(u32 port, InputHandler* handler);
++ void handle(u32 port);
++};
+\ No newline at end of file
+diff -Nur a/core/linux-dist/x11.cpp b/core/linux-dist/x11.cpp
+--- a/core/linux-dist/x11.cpp 2015-10-06 21:43:53.042336401 -0300
++++ b/core/linux-dist/x11.cpp 2015-10-06 21:55:43.966475140 -0300
+@@ -112,11 +112,11 @@
+
+ void input_x11_init()
+ {
+- x11_keymap[113] = DC_DPAD_LEFT;
+- x11_keymap[114] = DC_DPAD_RIGHT;
++ x11_keymap[113] = DC_BTN_DPAD1_LEFT;
++ x11_keymap[114] = DC_BTN_DPAD1_RIGHT;
+
+- x11_keymap[111] = DC_DPAD_UP;
+- x11_keymap[116] = DC_DPAD_DOWN;
++ x11_keymap[111] = DC_BTN_DPAD1_UP;
++ x11_keymap[116] = DC_BTN_DPAD1_DOWN;
+
+ x11_keymap[53] = DC_BTN_X;
+ x11_keymap[54] = DC_BTN_B;
diff --git a/pcr/reicast-multilib-git/loop-tracing.patch b/pcr/reicast-multilib-git/loop-tracing.patch
new file mode 100644
index 000000000..2e2b5ea97
--- /dev/null
+++ b/pcr/reicast-multilib-git/loop-tracing.patch
@@ -0,0 +1,109 @@
+diff -Nur a/core/rec-x86/rec_x86_driver.cpp b/core/rec-x86/rec_x86_driver.cpp
+--- a/core/rec-x86/rec_x86_driver.cpp 2015-10-06 21:43:53.045336422 -0300
++++ b/core/rec-x86/rec_x86_driver.cpp 2015-10-06 21:56:33.125833142 -0300
+@@ -289,6 +289,94 @@
+ }
+
+
++RuntimeBlockInfo* old_block;
++
++u32 old_loop;
++u32 old_old_loop;
++#include <map>
++
++#include <algorithm>
++
++u32 loops[RAM_SIZE];
++u32 loops_cyc[RAM_SIZE];
++u32 loops_hot[RAM_SIZE];
++u32 loops_end[RAM_SIZE];
++
++
++void print_loop_stats() {
++
++ vector<pair<u32, u32>> vc;
++ int loopc = 0;
++ int loopo = 0;
++
++ int ooc = 0;
++ int ooo = 0;
++
++ for (int i = 0; i < RAM_SIZE; i += 2) {
++ if (loops_hot[i]) {
++ vc.push_back(pair<u32, u32>(-loops_cyc[i], i));
++
++ loopc += loops[i];
++
++ loopo += loops_cyc[i];
++ }
++
++ ooc += loops[i];
++ ooo += loops_cyc[i];
++ }
++
++ sort(vc.begin(), vc.end());
++
++ printf("%d loops, %d, %d, %.2f, %.2f\n", vc.size(), loopc, loopo, loopc *100.0 / 1000000, loopo * 100.0 / ooo);
++
++ memset(loops, 0, sizeof(loops));
++ memset(loops_cyc, 0, sizeof(loops_cyc));
++ memset(loops_hot, 0, sizeof(loops_hot));
++ memset(loops_end, 0, sizeof(loops_end));
++}
++int counts = 10000;
++void DYNACALL ngen_enter(RuntimeBlockInfo* block) {
++ if (BET_GET_CLS(block->BlockType) == BET_CLS_Dynamic)
++ old_block = 0;
++
++ if (old_block) {
++ if ((old_block->addr & RAM_MASK) >= (block->addr & RAM_MASK)) {
++ loops[RAM_MASK & block->addr]++;
++ loops_cyc[RAM_MASK & block->addr] += block->guest_cycles;
++
++ loops_end[RAM_MASK & block->addr] = max(loops_end[RAM_MASK & block->addr], RAM_MASK & old_block->addr);
++
++ if (!loops_hot[RAM_MASK & block->addr] && loops[RAM_MASK & block->addr] > 1000) {
++ //printf("HOT LOOP %08X\n", block->addr);
++
++ loops_hot[RAM_MASK & block->addr] = 1;
++ }
++
++ old_old_loop = old_loop;
++ old_loop = old_block->addr & RAM_MASK;
++ }
++
++ else {
++ if ((block->addr & RAM_MASK) > loops_end[old_loop] && old_old_loop != -1) {
++ old_loop = old_old_loop;
++ old_old_loop = -1;
++ }
++
++ if ((block->addr & RAM_MASK) <= loops_end[old_loop]) {
++ loops[old_loop] ++;
++ loops_cyc[old_loop] += block->guest_cycles;
++ }
++ }
++ }
++
++ old_block = block;
++
++ if (--counts < 0) {
++ counts = 10000000;
++ print_loop_stats();
++ }
++}
++
+ void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool staging,bool optimise)
+ {
+ //initialise stuff
+@@ -306,6 +394,10 @@
+
+ block->code=(DynarecCodeEntryPtr)emit_GetCCPtr();
+
++ x86e->Emit(op_mov32, ECX, unat(block));
++
++ x86e->Emit(op_call, x86_ptr_imm(&ngen_enter));
++
+ x86e->Emit(op_add32,&memops_t,block->memops);
+ x86e->Emit(op_add32,&memops_l,block->linkedmemops);
+
diff --git a/pcr/reicast-multilib-git/loungekatt_rm-nonfree-fp.patch b/pcr/reicast-multilib-git/loungekatt_rm-nonfree-fp.patch
new file mode 100644
index 000000000..9d0b588d9
--- /dev/null
+++ b/pcr/reicast-multilib-git/loungekatt_rm-nonfree-fp.patch
@@ -0,0 +1,1451 @@
+diff -Nur a/core/deps/libpng/fp.h b/core/deps/libpng/fp.h
+--- a/core/deps/libpng/fp.h 2015-10-06 21:43:53.002336114 -0300
++++ b/core/deps/libpng/fp.h 1969-12-31 21:00:00.000000000 -0300
+@@ -1,62 +0,0 @@
+-/*
+-* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+-*
+-* @APPLE_LICENSE_HEADER_START@
+-*
+-* The contents of this file constitute Original Code as defined in and
+-* are subject to the Apple Public Source License Version 1.1 (the
+-* "License"). You may not use this file except in compliance with the
+-* License. Please obtain a copy of the License at
+-* http://www.apple.com/publicsource and read it before using this file.
+-*
+-* This Original Code and all software distributed under the License are
+-* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+-* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+-* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+-* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+-* License for the specific language governing rights and limitations
+-* under the License.
+-*
+-* @APPLE_LICENSE_HEADER_END@
+-*/
+-/* Copyright (c) 1992, NeXT Computer, Inc. All rights reserved.
+-*
+-* File: libc/m98k/gen/fp.h
+-* Author: Derek B Clegg, NeXT Computer, Inc.
+-*
+-* HISTORY
+-* 11-Nov-92 Derek B Clegg (dclegg@next.com)
+-* Created.
+-*
+-* Common definitions for floating-point numbers.
+-*/
+-
+-/* The following definitions for for double precision IEEE format numbers. */
+-
+-#define EXPONENT_BIAS 1023
+-
+-#define SIGN_BITS 1
+-#define EXPONENT_BITS 11
+-#define FRACTION_BITS 52
+-#define HI_FRACTION_BITS 20
+-#define LO_FRACTION_BITS 32
+-
+-struct double_format {
+-unsigned sign: SIGN_BITS;
+-unsigned exponent: EXPONENT_BITS;
+-unsigned hi_fraction: HI_FRACTION_BITS;
+-unsigned lo_fraction: LO_FRACTION_BITS;
+-};
+-
+-union dbl {
+-struct double_format s;
+-unsigned int u[2];
+-double value;
+-};
+-
+-#define PlusInfinity (1.0/0.0)
+-#define MinusInfinity (-1.0/0.0)
+-
+-#define not_a_number(x) ((x) != (x))
+-#define positive_infinity(x) ((x) == PlusInfinity)
+-#define negative_infinity(x) ((x) == MinusInfinity)
+Binary files a/shell/apple/emulator-ios/emulator/assets/Icon@2x.png and b/shell/apple/emulator-ios/emulator/assets/Icon@2x.png differ
+Binary files a/shell/apple/emulator-ios/emulator/assets/Icon-72@2x.png and b/shell/apple/emulator-ios/emulator/assets/Icon-72@2x.png differ
+Binary files a/shell/apple/emulator-ios/emulator/assets/Icon-72.png and b/shell/apple/emulator-ios/emulator/assets/Icon-72.png differ
+Binary files a/shell/apple/emulator-ios/emulator/assets/Icon.png and b/shell/apple/emulator-ios/emulator/assets/Icon.png differ
+diff -Nur a/shell/apple/emulator-ios/emulator/DiskViewCell.h b/shell/apple/emulator-ios/emulator/DiskViewCell.h
+--- a/shell/apple/emulator-ios/emulator/DiskViewCell.h 1969-12-31 21:00:00.000000000 -0300
++++ b/shell/apple/emulator-ios/emulator/DiskViewCell.h 2015-10-06 22:10:31.206968127 -0300
+@@ -0,0 +1,8 @@
++#import <UIKit/UIKit.h>
++
++@interface DiskViewCell : UITableViewCell
++
++@property (nonatomic, retain) IBOutlet UIImageView *diskImage;
++@property (nonatomic, retain) IBOutlet UILabel *nameLabel;
++
++@end
+diff -Nur a/shell/apple/emulator-ios/emulator/DiskViewCell.m b/shell/apple/emulator-ios/emulator/DiskViewCell.m
+--- a/shell/apple/emulator-ios/emulator/DiskViewCell.m 1969-12-31 21:00:00.000000000 -0300
++++ b/shell/apple/emulator-ios/emulator/DiskViewCell.m 2015-10-06 22:10:31.206968127 -0300
+@@ -0,0 +1,26 @@
++#import "DiskViewCell.h"
++
++@implementation DiskViewCell
++
++- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
++{
++ self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
++ if (self) {
++ // Initialization code
++ }
++ return self;
++}
++
++- (void)awakeFromNib
++{
++ // Initialization code
++}
++
++- (void)setSelected:(BOOL)selected animated:(BOOL)animated
++{
++ [super setSelected:selected animated:animated];
++
++ // Configure the view for the selected state
++}
++
++@end
+diff -Nur a/shell/apple/emulator-ios/emulator/EmulatorViewController.h b/shell/apple/emulator-ios/emulator/EmulatorViewController.h
+--- a/shell/apple/emulator-ios/emulator/EmulatorViewController.h 2015-10-06 21:43:53.121336967 -0300
++++ b/shell/apple/emulator-ios/emulator/EmulatorViewController.h 2015-10-06 22:10:31.206968127 -0300
+@@ -10,10 +10,18 @@
+ #import <GLKit/GLKit.h>
+ #import <GameController/GameController.h>
+ #import "iCadeReaderView.h"
++#import "PadViewController.h"
++#import "EmulatorView.h"
+
+-@interface ViewController : GLKViewController <iCadeEventDelegate>
++@interface EmulatorViewController : GLKViewController <iCadeEventDelegate>
+
++@property NSString* diskImage;
+ @property (nonatomic) iCadeReaderView* iCadeReader;
+ @property (nonatomic) GCController *gController __attribute__((weak_import));
++@property (nonatomic, strong) id connectObserver;
++@property (nonatomic, strong) id disconnectObserver;
++@property (nonatomic, strong) EmulatorView *emuView;
++
++@property (nonatomic, strong) PadViewController *controllerView;
+
+ @end
+diff -Nur a/shell/apple/emulator-ios/emulator/EmulatorViewController.mm b/shell/apple/emulator-ios/emulator/EmulatorViewController.mm
+--- a/shell/apple/emulator-ios/emulator/EmulatorViewController.mm 2015-10-06 21:43:53.121336967 -0300
++++ b/shell/apple/emulator-ios/emulator/EmulatorViewController.mm 2015-10-06 22:10:31.206968127 -0300
+@@ -16,7 +16,7 @@
+ #include "hw/maple/maple_devs.h"
+ #include "hw/maple/maple_if.h"
+
+-@interface ViewController () {
++@interface EmulatorViewController () {
+ }
+
+ @property (strong, nonatomic) EAGLContext *context;
+@@ -35,25 +35,34 @@
+ extern "C" int reicast_main(int argc, char* argv[]);
+
+
+-@implementation ViewController
++@implementation EmulatorViewController
+
+ -(void)emuThread
+ {
+ install_prof_handler(1);
+-
+
+- //This looks like the right place, rite?
+- char text[2]="";
+-
+- char* prms[2];
+- prms[0]=text;
+-
+- reicast_main(1, prms);
++ char *Args[3];
++ const char *P;
++
++ P = (const char *)[self.diskImage UTF8String];
++ Args[0] = "dc";
++ Args[1] = "-config";
++ Args[2] = P&&P[0]? (char *)malloc(strlen(P)+32):0;
++
++ if(Args[2])
++ {
++ strcpy(Args[2],"config:image=");
++ strcat(Args[2],P);
++ }
++
++ reicast_main(Args[2]? 3:1,Args);
+ }
+
+ - (void)viewDidLoad
+ {
+ [super viewDidLoad];
++
++ self.controllerView = [[PadViewController alloc] initWithNibName:@"PadViewController" bundle:nil];
+
+ self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+
+@@ -61,15 +70,33 @@
+ NSLog(@"Failed to create ES context");
+ }
+
+- GLKView *view = (GLKView *)self.view;
+- view.context = self.context;
+- view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
++ self.emuView = (EmulatorView *)self.view;
++ self.emuView.context = self.context;
++ self.emuView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
+
+- self.iCadeReader = [[iCadeReaderView alloc] init];
+- [self.view addSubview:self.iCadeReader];
+- self.iCadeReader.delegate = self;
+- self.iCadeReader.active = YES;
++ [self.controllerView setControlOutput:self.emuView];
+
++ self.connectObserver = [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidConnectNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
++ if ([[GCController controllers] count] == 1) {
++ [self toggleHardwareController:YES];
++ }
++ }];
++ self.disconnectObserver = [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidDisconnectNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
++ if (![[GCController controllers] count]) {
++ [self toggleHardwareController:NO];
++ }
++ }];
++
++ if ([[GCController controllers] count]) {
++ [self toggleHardwareController:YES];
++ }
++ [self.controllerView showController:self.view];
++
++ self.iCadeReader = [[iCadeReaderView alloc] init];
++ [self.view addSubview:self.iCadeReader];
++ self.iCadeReader.delegate = self;
++ self.iCadeReader.active = YES;
++
+ [self setupGL];
+
+ if (!gles_init())
+@@ -82,7 +109,7 @@
+ }
+
+ - (void)dealloc
+-{
++{
+ [self tearDownGL];
+
+ if ([EAGLContext currentContext] == self.context) {
+@@ -127,6 +154,126 @@
+
+ }
+
++- (void)toggleHardwareController:(BOOL)useHardware {
++ if (useHardware) {
++// [self.controllerView hideController];
++ self.gController = [GCController controllers][0];
++ if (self.gController.gamepad) {
++ [self.gController.gamepad.buttonA setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
++ if (pressed && value >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_abxy_a];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_abxy_a];
++ }
++ }];
++ [self.gController.gamepad.buttonB setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
++ if (pressed && value >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_abxy_b];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_abxy_b];
++ }
++ }];
++ [self.gController.gamepad.buttonX setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
++ if (pressed && value >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_abxy_x];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_abxy_x];
++ }
++ }];
++ [self.gController.gamepad.buttonY setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
++ if (pressed && value >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_abxy_y];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_abxy_y];
++ }
++ }];
++ [self.gController.gamepad.dpad setValueChangedHandler:^(GCControllerDirectionPad *dpad, float xValue, float yValue){
++ if (xValue >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_dpad_r];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_dpad_r];
++ }
++ if (xValue <= -0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_dpad_l];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_dpad_l];
++ }
++ if (yValue >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_dpad_u];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_dpad_u];
++ }
++ if (yValue <= -0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_dpad_d];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_dpad_d];
++ }
++ }];
++ //Add controller pause handler here
++ }
++ if (self.gController.extendedGamepad) {
++ [self.gController.extendedGamepad.buttonA setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
++ if (pressed && value >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_abxy_a];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_abxy_a];
++ }
++ }];
++ [self.gController.extendedGamepad.buttonB setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
++ if (pressed && value >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_abxy_b];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_abxy_b];
++ }
++ }];
++ [self.gController.extendedGamepad.buttonX setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
++ if (pressed && value >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_abxy_x];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_abxy_x];
++ }
++ }];
++ [self.gController.extendedGamepad.buttonY setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
++ if (pressed && value >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_abxy_y];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_abxy_y];
++ }
++ }];
++ [self.gController.extendedGamepad.dpad setValueChangedHandler:^(GCControllerDirectionPad *dpad, float xValue, float yValue){
++ if (xValue >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_dpad_r];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_dpad_r];
++ }
++ if (xValue <= -0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_dpad_l];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_dpad_l];
++ }
++ if (yValue >= 0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_dpad_u];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_dpad_u];
++ }
++ if (yValue <= -0.1) {
++ [self.emuView handleKeyDown:self.controllerView.img_dpad_d];
++ } else {
++ [self.emuView handleKeyUp:self.controllerView.img_dpad_d];
++ }
++ }];
++ [self.gController.extendedGamepad.leftThumbstick.xAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value){
++
++ }];
++ [self.gController.extendedGamepad.leftThumbstick.yAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value){
++
++ }];
++ }
++ } else {
++ self.gController = nil;
++// [self.controllerView showController:self.view];
++ }
++}
+
+ - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
+ {
+diff -Nur a/shell/apple/emulator-ios/emulator/EmulatorView.h b/shell/apple/emulator-ios/emulator/EmulatorView.h
+--- a/shell/apple/emulator-ios/emulator/EmulatorView.h 2015-10-06 21:43:53.121336967 -0300
++++ b/shell/apple/emulator-ios/emulator/EmulatorView.h 2015-10-06 22:10:31.206968127 -0300
+@@ -10,4 +10,9 @@
+
+ @interface EmulatorView : GLKView
+
++- (void)handleKeyDown:(UIButton*)button;
++- (void)handleKeyUp:(UIButton*)button;
++
++@property (nonatomic, strong) UIViewController *controllerView;
++
+ @end
+diff -Nur a/shell/apple/emulator-ios/emulator/EmulatorView.mm b/shell/apple/emulator-ios/emulator/EmulatorView.mm
+--- a/shell/apple/emulator-ios/emulator/EmulatorView.mm 2015-10-06 21:43:53.121336967 -0300
++++ b/shell/apple/emulator-ios/emulator/EmulatorView.mm 2015-10-06 22:10:31.206968127 -0300
+@@ -7,6 +7,7 @@
+ //
+
+ #import "EmulatorView.h"
++#import "PadViewController.h"
+
+ #include "types.h"
+
+@@ -15,11 +16,27 @@
+ extern s8 joyx[4],joyy[4];
+ extern u8 rt[4],lt[4];
+
+-#define key_CONT_A (1 << 2)
+-#define key_CONT_START (1 << 3)
+-#define key_CONT_DPAD_LEFT (1 << 6)
+-
+-int dpad_or_btn = 0;
++#define DC_BTN_C (1)
++#define DC_BTN_B (1<<1)
++#define DC_BTN_A (1<<2)
++#define DC_BTN_START (1<<3)
++#define DC_DPAD_UP (1<<4)
++#define DC_DPAD_DOWN (1<<5)
++#define DC_DPAD_LEFT (1<<6)
++#define DC_DPAD_RIGHT (1<<7)
++#define DC_BTN_Z (1<<8)
++#define DC_BTN_Y (1<<9)
++#define DC_BTN_X (1<<10)
++#define DC_BTN_D (1<<11)
++#define DC_DPAD2_UP (1<<12)
++#define DC_DPAD2_DOWN (1<<13)
++#define DC_DPAD2_LEFT (1<<14)
++#define DC_DPAD2_RIGHT (1<<15)
++
++#define DC_AXIS_LT (0X10000)
++#define DC_AXIS_RT (0X10001)
++#define DC_AXIS_X (0X20000)
++#define DC_AXIS_Y (0X20001)
+
+ @implementation EmulatorView
+
+@@ -31,23 +48,85 @@
+ }
+ */
+
+--(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
+-
+- if (dpad_or_btn &1)
+- kcode[0] &= ~(key_CONT_START|key_CONT_A);
+- else
+- kcode[0] &= ~(key_CONT_DPAD_LEFT);
++- (void)setControlInput:(PadViewController *)input
++{
++ self.controllerView = input;
++}
++
++- (void)handleKeyDown:(UIButton*)button
++{
++ PadViewController * controller = (PadViewController *)self.controllerView;
++ if (button == controller.img_dpad_l) {
++ kcode[0] &= ~(DC_DPAD_LEFT);
++ }
++ if (button == controller.img_dpad_r) {
++ kcode[0] &= ~(DC_DPAD_RIGHT);
++ }
++ if (button == controller.img_dpad_u) {
++ kcode[0] &= ~(DC_DPAD_UP);
++ }
++ if (button == controller.img_dpad_d) {
++ kcode[0] &= ~(DC_DPAD_DOWN);
++ }
++ if (button == controller.img_abxy_a) {
++ kcode[0] &= ~(DC_BTN_A);
++ }
++ if (button == controller.img_abxy_b) {
++ kcode[0] &= ~(DC_BTN_B);
++ }
++ if (button == controller.img_abxy_x) {
++ kcode[0] &= ~(DC_BTN_X);
++ }
++ if (button == controller.img_abxy_y) {
++ kcode[0] &= ~(DC_BTN_Y);
++ }
++ if (button == controller.img_lt) {
++ kcode[0] &= ~(DC_AXIS_LT);
++ }
++ if (button == controller.img_rt) {
++ kcode[0] &= ~(DC_AXIS_RT);
++ }
++ if (button == controller.img_start) {
++ kcode[0] &= ~(DC_BTN_START);
++ }
+ }
+
+--(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+-
+- // [event allTouches];
+-
+- if (dpad_or_btn &1)
+- kcode[0] |= (key_CONT_START|key_CONT_A);
+- else
+- kcode[0] |= (key_CONT_DPAD_LEFT);
+-
+- dpad_or_btn++;
++- (void)handleKeyUp:(UIButton*)button
++{
++ PadViewController * controller = (PadViewController *)self.controllerView;
++ if (button == controller.img_dpad_l) {
++ kcode[0] |= ~(DC_DPAD_LEFT);
++ }
++ if (button == controller.img_dpad_r) {
++ kcode[0] |= ~(DC_DPAD_RIGHT);
++ }
++ if (button == controller.img_dpad_u) {
++ kcode[0] |= ~(DC_DPAD_UP);
++ }
++ if (button == controller.img_dpad_d) {
++ kcode[0] |= ~(DC_DPAD_DOWN);
++ }
++ if (button == controller.img_abxy_a) {
++ kcode[0] |= (DC_BTN_A);
++ }
++ if (button == controller.img_abxy_b) {
++ kcode[0] |= (DC_BTN_B);
++ }
++ if (button == controller.img_abxy_x) {
++ kcode[0] |= (DC_BTN_X);
++ }
++ if (button == controller.img_abxy_y) {
++ kcode[0] |= (DC_BTN_Y);
++ }
++ if (button == controller.img_lt) {
++ kcode[0] |= (DC_AXIS_LT);
++ }
++ if (button == controller.img_rt) {
++ kcode[0] |= (DC_AXIS_RT);
++ }
++ if (button == controller.img_start) {
++ kcode[0] |= (DC_BTN_START);
++ }
+ }
++
+ @end
+Binary files a/shell/apple/emulator-ios/emulator/Images/disk_unknown.png and b/shell/apple/emulator-ios/emulator/Images/disk_unknown.png differ
+diff -Nur a/shell/apple/emulator-ios/emulator/MainStoryboard.storyboard b/shell/apple/emulator-ios/emulator/MainStoryboard.storyboard
+--- a/shell/apple/emulator-ios/emulator/MainStoryboard.storyboard 2015-10-06 21:43:53.123336981 -0300
++++ b/shell/apple/emulator-ios/emulator/MainStoryboard.storyboard 2015-10-06 22:10:31.207968135 -0300
+@@ -1,32 +1,100 @@
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6250" systemVersion="13F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="WRM-pR-XCP">
++<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="mSg-lr-0DL">
+ <dependencies>
+- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6244"/>
++ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
+ </dependencies>
+ <scenes>
+- <!--View Controller-->
++ <!--Root View Controller-->
++ <scene sceneID="q8K-fk-uPf">
++ <objects>
++ <tableViewController id="ZcT-ex-GUK" customClass="PathsViewController" sceneMemberID="viewController">
++ <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="LNG-KY-d6N">
++ <rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
++ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
++ <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
++ <prototypes>
++ <tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" rowHeight="80" id="mMa-C3-VYu" customClass="DiskViewCell">
++ <rect key="frame" x="0.0" y="22" width="320" height="80"/>
++ <autoresizingMask key="autoresizingMask"/>
++ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="mMa-C3-VYu" id="snt-ku-Aaq">
++ <rect key="frame" x="0.0" y="0.0" width="287" height="79.5"/>
++ <autoresizingMask key="autoresizingMask"/>
++ <subviews>
++ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="disk_unknown.png" id="6Qh-mW-Z9j">
++ <rect key="frame" x="8" y="4" width="72" height="72"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ </imageView>
++ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Epl-iu-f5s">
++ <rect key="frame" x="88" y="24" width="199" height="32"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <fontDescription key="fontDescription" type="system" pointSize="17"/>
++ <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
++ <nil key="highlightedColor"/>
++ </label>
++ </subviews>
++ </tableViewCellContentView>
++ <connections>
++ <outlet property="diskImage" destination="6Qh-mW-Z9j" id="b3r-L8-LS3"/>
++ <outlet property="nameLabel" destination="Epl-iu-f5s" id="i0S-JF-deT"/>
++ </connections>
++ </tableViewCell>
++ </prototypes>
++ <connections>
++ <outlet property="dataSource" destination="ZcT-ex-GUK" id="TGz-Qe-tBz"/>
++ <outlet property="delegate" destination="ZcT-ex-GUK" id="LAp-Yu-Mwb"/>
++ </connections>
++ </tableView>
++ <navigationItem key="navigationItem" title="Root View Controller" id="5jN-Uj-bBb"/>
++ <simulatedOrientationMetrics key="simulatedOrientationMetrics"/>
++ <connections>
++ <segue destination="WRM-pR-XCP" kind="modal" identifier="emulatorView" id="b10-sb-xpr"/>
++ </connections>
++ </tableViewController>
++ <placeholder placeholderIdentifier="IBFirstResponder" id="mdD-bX-Gqc" userLabel="First Responder" sceneMemberID="firstResponder"/>
++ </objects>
++ <point key="canvasLocation" x="-88" y="-692"/>
++ </scene>
++ <!--Navigation Controller-->
++ <scene sceneID="VLz-J1-2XD">
++ <objects>
++ <navigationController navigationBarHidden="YES" id="mSg-lr-0DL" sceneMemberID="viewController">
++ <navigationBar key="navigationBar" contentMode="scaleToFill" id="vT9-en-Ct5">
++ <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
++ <autoresizingMask key="autoresizingMask"/>
++ </navigationBar>
++ <connections>
++ <segue destination="ZcT-ex-GUK" kind="relationship" relationship="rootViewController" id="fhp-zt-Xhh"/>
++ </connections>
++ </navigationController>
++ <placeholder placeholderIdentifier="IBFirstResponder" id="i7I-Ys-F6b" userLabel="First Responder" sceneMemberID="firstResponder"/>
++ </objects>
++ <point key="canvasLocation" x="-692" y="-692"/>
++ </scene>
++ <!--Emulator View Controller-->
+ <scene sceneID="h6I-2s-MCy">
+ <objects>
+- <viewController storyboardIdentifier="emulatorView" wantsFullScreenLayout="YES" id="WRM-pR-XCP" customClass="ViewController" sceneMemberID="viewController">
+- <view key="view" contentMode="scaleToFill" id="JQE-db-ZiC" customClass="EmulatorView">
++ <viewController storyboardIdentifier="emulatorView" wantsFullScreenLayout="YES" id="WRM-pR-XCP" customClass="EmulatorViewController" sceneMemberID="viewController">
++ <view key="view" multipleTouchEnabled="YES" contentMode="scaleToFill" id="JQE-db-ZiC" customClass="EmulatorView">
+ <rect key="frame" x="0.0" y="0.0" width="568" height="320"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+- <color key="backgroundColor" cocoaTouchSystemColor="darkTextColor"/>
++ <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
++ <accessibility key="accessibilityConfiguration">
++ <accessibilityTraits key="traits" none="YES" notEnabled="YES"/>
++ </accessibility>
+ <gestureRecognizers/>
+ </view>
++ <navigationItem key="navigationItem" id="FmO-Yo-nVN"/>
+ <nil key="simulatedStatusBarMetrics"/>
+ <simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="ioY-OH-Jev" userLabel="First Responder" sceneMemberID="firstResponder"/>
+- <pongPressGestureRecognizer delaysTouchesEnded="NO" allowableMovement="10" minimumPressDuration="0.5" id="J8w-PT-eHl">
+- <connections>
+- <action selector="doSaveState:" destination="WRM-pR-XCP" id="6ND-tU-CZE"/>
+- </connections>
+- </pongPressGestureRecognizer>
+ </objects>
+ <point key="canvasLocation" x="486" y="-692"/>
+ </scene>
+ </scenes>
++ <resources>
++ <image name="disk_unknown.png" width="95" height="95"/>
++ </resources>
+ <simulatedMetricsContainer key="defaultSimulatedMetrics">
+ <simulatedStatusBarMetrics key="statusBar"/>
+ <simulatedOrientationMetrics key="orientation"/>
+diff -Nur a/shell/apple/emulator-ios/emulator/PadViewController.h b/shell/apple/emulator-ios/emulator/PadViewController.h
+--- a/shell/apple/emulator-ios/emulator/PadViewController.h 1969-12-31 21:00:00.000000000 -0300
++++ b/shell/apple/emulator-ios/emulator/PadViewController.h 2015-10-06 22:10:31.207968135 -0300
+@@ -0,0 +1,34 @@
++//
++// PadViewController.h
++// reicast-ios
++//
++// Created by Lounge Katt on 8/25/15.
++// Copyright (c) 2015 reicast. All rights reserved.
++//
++
++#import <UIKit/UIKit.h>
++#import "EmulatorView.h"
++
++@interface PadViewController : UIViewController
++
++@property (nonatomic, strong) IBOutlet UIButton* img_dpad_l;
++@property (nonatomic, strong) IBOutlet UIButton* img_dpad_r;
++@property (nonatomic, strong) IBOutlet UIButton* img_dpad_u;
++@property (nonatomic, strong) IBOutlet UIButton* img_dpad_d;
++@property (nonatomic, strong) IBOutlet UIButton* img_abxy_a;
++@property (nonatomic, strong) IBOutlet UIButton* img_abxy_b;
++@property (nonatomic, strong) IBOutlet UIButton* img_abxy_x;
++@property (nonatomic, strong) IBOutlet UIButton* img_abxy_y;
++@property (nonatomic, strong) IBOutlet UIButton* img_vjoy;
++@property (nonatomic, strong) IBOutlet UIButton* img_lt;
++@property (nonatomic, strong) IBOutlet UIButton* img_rt;
++@property (nonatomic, strong) IBOutlet UIButton* img_start;
++
++@property (nonatomic, strong) EmulatorView *handler;
++
++- (void) showController:(UIView *)parentView;
++- (void) hideController;
++- (BOOL) isControllerVisible;
++- (void) setControlOutput:(EmulatorView *)output;
++
++@end
+diff -Nur a/shell/apple/emulator-ios/emulator/PadViewController.m b/shell/apple/emulator-ios/emulator/PadViewController.m
+--- a/shell/apple/emulator-ios/emulator/PadViewController.m 1969-12-31 21:00:00.000000000 -0300
++++ b/shell/apple/emulator-ios/emulator/PadViewController.m 2015-10-06 22:10:31.207968135 -0300
+@@ -0,0 +1,78 @@
++//
++// PadViewController.m
++// reicast-ios
++//
++// Created by Lounge Katt on 8/25/15.
++// Copyright (c) 2015 reicast. All rights reserved.
++//
++
++#import "PadViewController.h"
++#import "EmulatorView.h"
++
++@interface PadViewController ()
++
++@end
++
++@implementation PadViewController
++
++- (void)viewDidLoad {
++ [super viewDidLoad];
++}
++
++- (void)didReceiveMemoryWarning {
++ [super didReceiveMemoryWarning];
++ // Dispose of any resources that can be recreated.
++}
++
++- (void)showController:(UIView *)parentView
++{
++ [parentView addSubview:self.view];
++}
++
++- (void)hideController
++{
++ [self.view removeFromSuperview];
++}
++
++- (BOOL)isControllerVisible {
++ if (self.view.window != nil) {
++ return YES;
++ }
++ return NO;
++}
++
++- (void)setControlOutput:(EmulatorView *)output
++{
++ self.handler = output;
++}
++
++- (IBAction)keycodeDown:(id)sender
++{
++ [self.handler handleKeyDown:(UIButton*)sender];
++}
++
++- (IBAction)keycodeUp:(id)sender
++{
++ [self.handler handleKeyUp:(UIButton*)sender];
++}
++
++- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
++{
++ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
++ if (self) {
++ // Custom initialization
++ }
++ return self;
++}
++
++/*
++#pragma mark - Navigation
++
++// In a storyboard-based application, you will often want to do a little preparation before navigation
++- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
++ // Get the new view controller using [segue destinationViewController].
++ // Pass the selected object to the new view controller.
++}
++*/
++
++@end
+diff -Nur a/shell/apple/emulator-ios/emulator/PadViewController.xib b/shell/apple/emulator-ios/emulator/PadViewController.xib
+--- a/shell/apple/emulator-ios/emulator/PadViewController.xib 1969-12-31 21:00:00.000000000 -0300
++++ b/shell/apple/emulator-ios/emulator/PadViewController.xib 2015-10-06 22:10:31.207968135 -0300
+@@ -0,0 +1,197 @@
++<?xml version="1.0" encoding="UTF-8" standalone="no"?>
++<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
++ <dependencies>
++ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
++ </dependencies>
++ <objects>
++ <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PadViewController">
++ <connections>
++ <outlet property="img_abxy_a" destination="iKO-3z-Ias" id="ENi-No-2tP"/>
++ <outlet property="img_abxy_b" destination="7LB-OY-vh3" id="pGH-6d-IgP"/>
++ <outlet property="img_abxy_x" destination="iwO-7q-c8H" id="ZhP-Zp-Qnj"/>
++ <outlet property="img_abxy_y" destination="hGZ-v7-VA5" id="5qv-nJ-V1w"/>
++ <outlet property="img_dpad_d" destination="s7g-nq-lRU" id="9MP-1k-eUW"/>
++ <outlet property="img_dpad_l" destination="rp6-Nd-1qa" id="LNo-9e-3og"/>
++ <outlet property="img_dpad_r" destination="CVH-hw-R8F" id="vbf-4S-SBb"/>
++ <outlet property="img_dpad_u" destination="WMD-Fv-ibu" id="1kE-zb-8gR"/>
++ <outlet property="img_lt" destination="8Gl-Iv-u8L" id="4R8-pf-PYz"/>
++ <outlet property="img_rt" destination="V8J-vG-dlF" id="Bn6-Zm-Ojo"/>
++ <outlet property="img_start" destination="VtI-tC-PSX" id="o3u-Cb-G2g"/>
++ <outlet property="view" destination="3M7-1s-N5r" id="Tac-YU-UYE"/>
++ </connections>
++ </placeholder>
++ <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
++ <view multipleTouchEnabled="YES" contentMode="scaleToFill" id="3M7-1s-N5r">
++ <rect key="frame" x="0.0" y="0.0" width="568" height="320"/>
++ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
++ <subviews>
++ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="LTrigger.png" id="H57-MD-elm">
++ <rect key="frame" x="0.0" y="0.0" width="80" height="40"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ </imageView>
++ <button opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="8Gl-Iv-u8L" userLabel="LT-Button">
++ <rect key="frame" x="0.0" y="0.0" width="80" height="40"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <state key="normal">
++ <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
++ </state>
++ <connections>
++ <action selector="keycodeDown:" destination="-1" eventType="touchDown" id="34L-sO-g81"/>
++ <action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="iDv-U3-6OX"/>
++ </connections>
++ </button>
++ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="RTrigger.png" id="Cjn-zx-eSs">
++ <rect key="frame" x="488" y="0.0" width="80" height="40"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ </imageView>
++ <button opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="V8J-vG-dlF" userLabel="RT-Button">
++ <rect key="frame" x="488" y="0.0" width="80" height="40"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <state key="normal">
++ <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
++ </state>
++ <connections>
++ <action selector="keycodeDown:" destination="-1" eventType="touchDown" id="vPf-qF-m13"/>
++ <action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="hQh-8f-5jG"/>
++ </connections>
++ </button>
++ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="JoystickButton.png" id="ivh-8r-bw3" userLabel="JoystickThumbpad.png">
++ <rect key="frame" x="20" y="206" width="100" height="100"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ </imageView>
++ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="JoystickBackground.png" id="OMP-L6-n0A">
++ <rect key="frame" x="6" y="192" width="128" height="128"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ </imageView>
++ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="DPad.png" id="FLe-Gr-hny">
++ <rect key="frame" x="0.0" y="44" width="140" height="140"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ </imageView>
++ <button opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="rp6-Nd-1qa" userLabel="L-Button">
++ <rect key="frame" x="0.0" y="94" width="46" height="40"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <state key="normal">
++ <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
++ </state>
++ <connections>
++ <action selector="keycodeDown:" destination="-1" eventType="touchDown" id="3Yw-AP-xVf"/>
++ <action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="5gI-j0-ANf"/>
++ </connections>
++ </button>
++ <button opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="CVH-hw-R8F" userLabel="R-Button">
++ <rect key="frame" x="94" y="94" width="46" height="40"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <state key="normal">
++ <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
++ </state>
++ <connections>
++ <action selector="keycodeDown:" destination="-1" eventType="touchDown" id="2Dv-zb-f8V"/>
++ <action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="woi-3Y-IfD"/>
++ </connections>
++ </button>
++ <button opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="WMD-Fv-ibu" userLabel="U-Button">
++ <rect key="frame" x="50" y="44" width="40" height="40"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <state key="normal">
++ <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
++ </state>
++ <connections>
++ <action selector="keycodeDown:" destination="-1" eventType="touchDown" id="kT6-yy-ZtY"/>
++ <action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="R0R-dl-GAG"/>
++ </connections>
++ </button>
++ <button opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="s7g-nq-lRU" userLabel="D-Button">
++ <rect key="frame" x="50" y="144" width="40" height="40"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <state key="normal">
++ <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
++ </state>
++ <connections>
++ <action selector="keycodeDown:" destination="-1" eventType="touchDown" id="Wck-mk-4Py"/>
++ <action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="Qox-hz-p3A"/>
++ </connections>
++ </button>
++ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ABXYPad.png" id="xbP-E4-fCE">
++ <rect key="frame" x="408" y="159" width="160" height="161"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ </imageView>
++ <button opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="iwO-7q-c8H" userLabel="X-Button">
++ <rect key="frame" x="408" y="210" width="60" height="60"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <state key="normal">
++ <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
++ </state>
++ <connections>
++ <action selector="keycodeDown:" destination="-1" eventType="touchDown" id="IBH-TK-vfV"/>
++ <action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="dhr-NT-lcF"/>
++ </connections>
++ </button>
++ <button opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="7LB-OY-vh3" userLabel="B-Button">
++ <rect key="frame" x="508" y="210" width="60" height="60"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <state key="normal">
++ <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
++ </state>
++ <connections>
++ <action selector="keycodeDown:" destination="-1" eventType="touchDown" id="dhg-58-L8C"/>
++ <action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="zqg-KK-Wxb"/>
++ </connections>
++ </button>
++ <button opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="hGZ-v7-VA5" userLabel="Y-Button">
++ <rect key="frame" x="458" y="159" width="60" height="60"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <state key="normal">
++ <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
++ </state>
++ <connections>
++ <action selector="keycodeDown:" destination="-1" eventType="touchDown" id="tyb-H4-TqJ"/>
++ <action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="oai-Xb-scl"/>
++ </connections>
++ </button>
++ <button opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="iKO-3z-Ias" userLabel="A-Button">
++ <rect key="frame" x="458" y="260" width="60" height="60"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <state key="normal">
++ <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
++ </state>
++ <connections>
++ <action selector="keycodeDown:" destination="-1" eventType="touchDown" id="Ysa-m4-KnN"/>
++ <action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="MTf-ND-WNy"/>
++ </connections>
++ </button>
++ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Start.png" id="9K0-cV-7zu">
++ <rect key="frame" x="244" y="272" width="80" height="40"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ </imageView>
++ <button opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="VtI-tC-PSX" userLabel="S-Button">
++ <rect key="frame" x="257" y="272" width="54" height="40"/>
++ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
++ <state key="normal">
++ <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
++ </state>
++ <connections>
++ <action selector="keycodeDown:" destination="-1" eventType="touchDown" id="kwd-jB-5Wn"/>
++ <action selector="keycodeUp:" destination="-1" eventType="touchUpInside" id="gHx-tA-QlF"/>
++ </connections>
++ </button>
++ </subviews>
++ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
++ <simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
++ <point key="canvasLocation" x="325" y="329"/>
++ </view>
++ </objects>
++ <resources>
++ <image name="ABXYPad.png" width="120" height="120"/>
++ <image name="DPad.png" width="120" height="120"/>
++ <image name="JoystickBackground.png" width="120" height="120"/>
++ <image name="JoystickButton.png" width="56" height="56"/>
++ <image name="LTrigger.png" width="67" height="44"/>
++ <image name="RTrigger.png" width="67" height="44"/>
++ <image name="Start.png" width="48" height="26"/>
++ </resources>
++ <simulatedMetricsContainer key="defaultSimulatedMetrics">
++ <simulatedStatusBarMetrics key="statusBar"/>
++ <simulatedOrientationMetrics key="orientation"/>
++ <simulatedScreenMetrics key="destination" type="retina4"/>
++ </simulatedMetricsContainer>
++</document>
+diff -Nur a/shell/apple/emulator-ios/emulator/PathsViewController.h b/shell/apple/emulator-ios/emulator/PathsViewController.h
+--- a/shell/apple/emulator-ios/emulator/PathsViewController.h 2015-10-06 21:43:53.123336981 -0300
++++ b/shell/apple/emulator-ios/emulator/PathsViewController.h 2015-10-06 22:10:31.207968135 -0300
+@@ -8,7 +8,9 @@
+
+ #import <UIKit/UIKit.h>
+
+-@interface PathsViewController : UITableViewController
++@interface PathsViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
+ @property (weak, nonatomic) IBOutlet UIBarButtonItem *sidebarButton;
+
++@property (nonatomic, strong) NSMutableArray* diskImages;
++
+ @end
+diff -Nur a/shell/apple/emulator-ios/emulator/PathsViewController.m b/shell/apple/emulator-ios/emulator/PathsViewController.m
+--- a/shell/apple/emulator-ios/emulator/PathsViewController.m 2015-10-06 21:43:53.123336981 -0300
++++ b/shell/apple/emulator-ios/emulator/PathsViewController.m 2015-10-06 22:10:31.207968135 -0300
+@@ -7,7 +7,9 @@
+ //
+
+ #import "PathsViewController.h"
+-#import "SWRevealViewController.h"
++//#import "SWRevealViewController.h"
++#import "EmulatorViewController.h"
++#import "DiskViewCell.h"
+
+ @interface PathsViewController ()
+
+@@ -24,22 +26,32 @@
+ return self;
+ }
+
++- (NSURL *)documents
++{
++ return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
++}
++
+ - (void)viewDidLoad
+ {
+ [super viewDidLoad];
+ self.title = @"Paths";
+
+ // Set the side bar button action. When it's tapped, it'll show up the sidebar.
+- _sidebarButton.target = self.revealViewController;
+- _sidebarButton.action = @selector(revealToggle:);
+-
++// _sidebarButton.target = self.revealViewController;
++// _sidebarButton.action = @selector(revealToggle:);
++
+ // Set the gesture
+- [self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
++// [self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
+ // Uncomment the following line to preserve selection between presentations.
+ // self.clearsSelectionOnViewWillAppear = NO;
+
+ // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
+ // self.navigationItem.rightBarButtonItem = self.editButtonItem;
++
++ self.diskImages = [[NSMutableArray alloc] init];
++ NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self documents].path error:NULL];
++ NSPredicate *diskPredicate = [NSPredicate predicateWithFormat:@"self ENDSWITH '.chd' || self ENDSWITH '.gdi' || self ENDSWITH '.cdi' || self ENDSWITH '.CHD' || self ENDSWITH '.GDI' || self ENDSWITH '.CDI'"];
++ self.diskImages = [NSMutableArray arrayWithArray:[files filteredArrayUsingPredicate:diskPredicate]];
+ }
+
+ - (void)didReceiveMemoryWarning
+@@ -50,6 +62,52 @@
+
+ #pragma mark - Table view data source
+
+-// TODO: paths view controller logic
++-(NSInteger)numberOfSectionsInTableView: (UITableView*)tableView
++{
++ return 1;
++}
++
++-(NSInteger)tableView: (UITableView *)tableView numberOfRowsInSection: (NSInteger)section
++{
++ return [self.diskImages count];
++}
++
++-(NSString*)tableView: (UITableView*)tableView titleForHeaderInSection: (NSInteger)section
++{
++ return @"";
++}
++
++- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
++ return 80;
++ // Assign the specific cell height to prevent issues with custom size
++}
++
++-(UITableViewCell*)tableView: (UITableView*)tableView cellForRowAtIndexPath: (NSIndexPath*)indexPath
++{
++ static NSString *CellIdentifier = @"Cell";
++
++ DiskViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
++ NSString* imagePath = [self.diskImages objectAtIndex: indexPath.row];
++
++ cell.nameLabel.text = [[imagePath lastPathComponent] stringByDeletingPathExtension];
++
++ return cell;
++}
++
++-(void)prepareForSegue: (UIStoryboardSegue*)segue sender: (id)sender
++{
++ if ([segue.identifier isEqualToString:@"emulatorView"]) {
++ NSIndexPath* indexPath = self.tableView.indexPathForSelectedRow;
++ NSString* filePath = [self.diskImages objectAtIndex: indexPath.row];
++ NSString* diskPath = [[self documents].path stringByAppendingPathComponent: filePath];
++ EmulatorViewController* emulatorView = segue.destinationViewController;
++ emulatorView.diskImage = diskPath;
++ }
++}
++
++-(void)tableView: (UITableView*)tableView didSelectRowAtIndexPath: (NSIndexPath*)indexPath
++{
++ [self performSegueWithIdentifier: @"emulatorView" sender: self];
++}
+
+ @end
+diff -Nur a/shell/apple/emulator-ios/emulator/reicast-ios-Info.plist b/shell/apple/emulator-ios/emulator/reicast-ios-Info.plist
+--- a/shell/apple/emulator-ios/emulator/reicast-ios-Info.plist 2015-10-06 21:43:53.124336988 -0300
++++ b/shell/apple/emulator-ios/emulator/reicast-ios-Info.plist 2015-10-06 22:10:31.207968135 -0300
+@@ -8,13 +8,6 @@
+ <string>Reicast</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+- <key>CFBundleIconFiles</key>
+- <array>
+- <string>emulator/assets/Icon-72.png</string>
+- <string>emulator/assets/Icon-72@2x.png</string>
+- <string>emulator/assets/Icon.png</string>
+- <string>emulator/assets/Icon@2x.png</string>
+- </array>
+ <key>CFBundleIdentifier</key>
+ <string>com.reicast.$(PRODUCT_NAME:rfc1034identifier)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+diff -Nur a/shell/apple/emulator-ios/reicast-ios/Images.xcassets/AppIcon.appiconset/Contents.json b/shell/apple/emulator-ios/reicast-ios/Images.xcassets/AppIcon.appiconset/Contents.json
+--- a/shell/apple/emulator-ios/reicast-ios/Images.xcassets/AppIcon.appiconset/Contents.json 1969-12-31 21:00:00.000000000 -0300
++++ b/shell/apple/emulator-ios/reicast-ios/Images.xcassets/AppIcon.appiconset/Contents.json 2015-10-06 22:10:31.209968149 -0300
+@@ -0,0 +1,60 @@
++{
++ "images" : [
++ {
++ "idiom" : "iphone",
++ "size" : "29x29",
++ "scale" : "2x"
++ },
++ {
++ "idiom" : "iphone",
++ "size" : "40x40",
++ "scale" : "2x"
++ },
++ {
++ "size" : "60x60",
++ "idiom" : "iphone",
++ "filename" : "Icon-60@2x.png",
++ "scale" : "2x"
++ },
++ {
++ "idiom" : "iphone",
++ "size" : "60x60",
++ "scale" : "3x"
++ },
++ {
++ "idiom" : "ipad",
++ "size" : "29x29",
++ "scale" : "1x"
++ },
++ {
++ "idiom" : "ipad",
++ "size" : "29x29",
++ "scale" : "2x"
++ },
++ {
++ "idiom" : "ipad",
++ "size" : "40x40",
++ "scale" : "1x"
++ },
++ {
++ "idiom" : "ipad",
++ "size" : "40x40",
++ "scale" : "2x"
++ },
++ {
++ "idiom" : "ipad",
++ "size" : "76x76",
++ "scale" : "1x"
++ },
++ {
++ "size" : "76x76",
++ "idiom" : "ipad",
++ "filename" : "Icon-76@2x.png",
++ "scale" : "2x"
++ }
++ ],
++ "info" : {
++ "version" : 1,
++ "author" : "xcode"
++ }
++}
+\ No newline at end of file
+Binary files a/shell/apple/emulator-ios/reicast-ios/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png and b/shell/apple/emulator-ios/reicast-ios/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png differ
+Binary files a/shell/apple/emulator-ios/reicast-ios/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png and b/shell/apple/emulator-ios/reicast-ios/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png differ
+diff -Nur a/shell/apple/emulator-ios/reicast-ios/Images.xcassets/LaunchImage.launchimage/Contents.json b/shell/apple/emulator-ios/reicast-ios/Images.xcassets/LaunchImage.launchimage/Contents.json
+--- a/shell/apple/emulator-ios/reicast-ios/Images.xcassets/LaunchImage.launchimage/Contents.json 1969-12-31 21:00:00.000000000 -0300
++++ b/shell/apple/emulator-ios/reicast-ios/Images.xcassets/LaunchImage.launchimage/Contents.json 2015-10-06 22:10:31.209968149 -0300
+@@ -0,0 +1,49 @@
++{
++ "images" : [
++ {
++ "orientation" : "portrait",
++ "idiom" : "ipad",
++ "minimum-system-version" : "7.0",
++ "extent" : "full-screen",
++ "scale" : "2x"
++ },
++ {
++ "orientation" : "landscape",
++ "idiom" : "ipad",
++ "minimum-system-version" : "7.0",
++ "extent" : "full-screen",
++ "scale" : "1x"
++ },
++ {
++ "orientation" : "landscape",
++ "idiom" : "ipad",
++ "minimum-system-version" : "7.0",
++ "extent" : "full-screen",
++ "scale" : "2x"
++ },
++ {
++ "orientation" : "portrait",
++ "idiom" : "iphone",
++ "minimum-system-version" : "7.0",
++ "scale" : "2x"
++ },
++ {
++ "orientation" : "portrait",
++ "idiom" : "iphone",
++ "minimum-system-version" : "7.0",
++ "subtype" : "retina4",
++ "scale" : "2x"
++ },
++ {
++ "orientation" : "portrait",
++ "idiom" : "ipad",
++ "minimum-system-version" : "7.0",
++ "extent" : "full-screen",
++ "scale" : "1x"
++ }
++ ],
++ "info" : {
++ "version" : 1,
++ "author" : "xcode"
++ }
++}
+\ No newline at end of file
+diff -Nur a/shell/apple/emulator-ios/reicast-ios.xcodeproj/project.pbxproj b/shell/apple/emulator-ios/reicast-ios.xcodeproj/project.pbxproj
+--- a/shell/apple/emulator-ios/reicast-ios.xcodeproj/project.pbxproj 2015-10-06 21:43:53.125336995 -0300
++++ b/shell/apple/emulator-ios/reicast-ios.xcodeproj/project.pbxproj 2015-10-06 22:10:31.209968149 -0300
+@@ -50,6 +50,9 @@
+ 877652C61B6157BD00437F10 /* audiostream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 877652BF1B6157BD00437F10 /* audiostream.cpp */; };
+ 877652C91B6157FC00437F10 /* ngen_arm.S in Sources */ = {isa = PBXBuildFile; fileRef = 877652C71B6157FC00437F10 /* ngen_arm.S */; };
+ 877652CA1B6157FC00437F10 /* rec_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 877652C81B6157FC00437F10 /* rec_arm.cpp */; };
++ 878B0CFC1B8BB5B400A8D1C5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 878B0CFB1B8BB5B400A8D1C5 /* Images.xcassets */; };
++ 878B0D001B8BFE6200A8D1C5 /* disk_unknown.png in Resources */ = {isa = PBXBuildFile; fileRef = 878B0CFF1B8BFE6200A8D1C5 /* disk_unknown.png */; };
++ 8794D9C31B88F3D600B1B3A3 /* DiskViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 8794D9C21B88F3D600B1B3A3 /* DiskViewCell.m */; };
+ 87C208D71B7A4BFA00638BDD /* AboutViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 87C208C61B7A4BFA00638BDD /* AboutViewController.m */; };
+ 87C208D81B7A4BFA00638BDD /* BrowserTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 87C208C81B7A4BFA00638BDD /* BrowserTableViewController.m */; };
+ 87C208D91B7A4BFA00638BDD /* CloudVMUViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 87C208CA1B7A4BFA00638BDD /* CloudVMUViewController.m */; };
+@@ -87,10 +90,8 @@
+ 87D92F4E1B7A1B5700D8FD9E /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87D92F4D1B7A1B5700D8FD9E /* GameController.framework */; };
+ 87D92F541B7A1BB100D8FD9E /* iCadeReaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 87D92F511B7A1BB100D8FD9E /* iCadeReaderView.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+ 87D92F551B7A1BB100D8FD9E /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 87D92F531B7A1BB100D8FD9E /* LICENSE */; };
+- 87DCDB251B7EE57D0054D67C /* Icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 8703BC371A44B8DA00E7E939 /* Icon-72.png */; };
+- 87DCDB261B7EE5850054D67C /* Icon-72@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8703BC381A44B8DA00E7E939 /* Icon-72@2x.png */; };
+- 87DCDB271B7EE5850054D67C /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 8703BC391A44B8DA00E7E939 /* Icon.png */; };
+- 87DCDB281B7EE5850054D67C /* Icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8703BC3A1A44B8DA00E7E939 /* Icon@2x.png */; };
++ 87FA52E91B8CE18600CEFC32 /* PadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 87FA52E71B8CE18600CEFC32 /* PadViewController.m */; };
++ 87FA52EA1B8CE18600CEFC32 /* PadViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 87FA52E81B8CE18600CEFC32 /* PadViewController.xib */; };
+ 9C7A393318C804A80070BB5F /* reicast.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = 9C7A393218C804A80070BB5F /* reicast.entitlements */; };
+ 9C7A3AA218C806E00070BB5F /* cfg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9C7A395118C806DE0070BB5F /* cfg.cpp */; };
+ 9C7A3AA318C806E00070BB5F /* cl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9C7A395318C806DE0070BB5F /* cl.cpp */; };
+@@ -244,7 +245,6 @@
+ 84967C751B8F492C005F1140 /* filter_neon.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = filter_neon.S; sourceTree = "<group>"; };
+ 84967C761B8F492C005F1140 /* filter_neon_intrinsics.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = filter_neon_intrinsics.c; sourceTree = "<group>"; };
+ 84967C771B8F492C005F1140 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
+- 84967C781B8F492C005F1140 /* fp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fp.h; sourceTree = "<group>"; };
+ 84967C791B8F492C005F1140 /* png.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = png.c; sourceTree = "<group>"; };
+ 84967C7A1B8F492C005F1140 /* png.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = png.h; sourceTree = "<group>"; };
+ 84967C7B1B8F492C005F1140 /* pngconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pngconf.h; sourceTree = "<group>"; };
+@@ -282,10 +282,6 @@
+ 849C0D6A1B072D14008BAAA4 /* gdrom_hle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gdrom_hle.h; path = reios/gdrom_hle.h; sourceTree = "<group>"; };
+ 849C0D6B1B072D14008BAAA4 /* reios_elf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = reios_elf.cpp; path = reios/reios_elf.cpp; sourceTree = "<group>"; };
+ 849C0D6C1B072D14008BAAA4 /* reios_elf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reios_elf.h; path = reios/reios_elf.h; sourceTree = "<group>"; };
+- 8703BC371A44B8DA00E7E939 /* Icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon-72.png"; path = "emulator/assets/Icon-72.png"; sourceTree = "<group>"; };
+- 8703BC381A44B8DA00E7E939 /* Icon-72@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon-72@2x.png"; path = "emulator/assets/Icon-72@2x.png"; sourceTree = "<group>"; };
+- 8703BC391A44B8DA00E7E939 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon.png; path = emulator/assets/Icon.png; sourceTree = "<group>"; };
+- 8703BC3A1A44B8DA00E7E939 /* Icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon@2x.png"; path = "emulator/assets/Icon@2x.png"; sourceTree = "<group>"; };
+ 87078A8318A47FE90034C7A0 /* reicast-ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "reicast-ios.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 87078A8618A47FE90034C7A0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 87078A8818A47FE90034C7A0 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+@@ -316,6 +312,10 @@
+ 877652C11B6157BD00437F10 /* oslib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = oslib.h; sourceTree = "<group>"; };
+ 877652C71B6157FC00437F10 /* ngen_arm.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = ngen_arm.S; sourceTree = "<group>"; };
+ 877652C81B6157FC00437F10 /* rec_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rec_arm.cpp; sourceTree = "<group>"; };
++ 878B0CFB1B8BB5B400A8D1C5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = "../reicast-ios/Images.xcassets"; sourceTree = "<group>"; };
++ 878B0CFF1B8BFE6200A8D1C5 /* disk_unknown.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = disk_unknown.png; path = emulator/Images/disk_unknown.png; sourceTree = "<group>"; };
++ 8794D9C11B88F3D600B1B3A3 /* DiskViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DiskViewCell.h; path = emulator/DiskViewCell.h; sourceTree = "<group>"; };
++ 8794D9C21B88F3D600B1B3A3 /* DiskViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DiskViewCell.m; path = emulator/DiskViewCell.m; sourceTree = "<group>"; };
+ 87C208C51B7A4BFA00638BDD /* AboutViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AboutViewController.h; path = emulator/AboutViewController.h; sourceTree = "<group>"; };
+ 87C208C61B7A4BFA00638BDD /* AboutViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AboutViewController.m; path = emulator/AboutViewController.m; sourceTree = "<group>"; };
+ 87C208C71B7A4BFA00638BDD /* BrowserTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BrowserTableViewController.h; path = emulator/BrowserTableViewController.h; sourceTree = "<group>"; };
+@@ -364,6 +364,9 @@
+ 87D92F511B7A1BB100D8FD9E /* iCadeReaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iCadeReaderView.m; sourceTree = "<group>"; };
+ 87D92F521B7A1BB100D8FD9E /* iCadeState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iCadeState.h; sourceTree = "<group>"; };
+ 87D92F531B7A1BB100D8FD9E /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
++ 87FA52E61B8CE18600CEFC32 /* PadViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PadViewController.h; path = emulator/PadViewController.h; sourceTree = "<group>"; };
++ 87FA52E71B8CE18600CEFC32 /* PadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PadViewController.m; path = emulator/PadViewController.m; sourceTree = "<group>"; };
++ 87FA52E81B8CE18600CEFC32 /* PadViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = PadViewController.xib; path = emulator/PadViewController.xib; sourceTree = "<group>"; };
+ 9C7A393218C804A80070BB5F /* reicast.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = reicast.entitlements; sourceTree = "<group>"; };
+ 9C7A393A18C806DE0070BB5F /* arm_coding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arm_coding.h; sourceTree = "<group>"; };
+ 9C7A393B18C806DE0070BB5F /* arm_disasm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arm_disasm.h; sourceTree = "<group>"; };
+@@ -382,7 +385,6 @@
+ 9C7A394818C806DE0070BB5F /* E_VLoadStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = E_VLoadStore.h; sourceTree = "<group>"; };
+ 9C7A394918C806DE0070BB5F /* E_VRegXfer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = E_VRegXfer.h; sourceTree = "<group>"; };
+ 9C7A394A18C806DE0070BB5F /* H_Branches.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = H_Branches.h; sourceTree = "<group>"; };
+- 9C7A394B18C806DE0070BB5F /* H_fp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = H_fp.h; sourceTree = "<group>"; };
+ 9C7A394C18C806DE0070BB5F /* H_LoadStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = H_LoadStore.h; sourceTree = "<group>"; };
+ 9C7A394D18C806DE0070BB5F /* H_psuedo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = H_psuedo.h; sourceTree = "<group>"; };
+ 9C7A394E18C806DE0070BB5F /* H_state.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = H_state.h; sourceTree = "<group>"; };
+@@ -722,17 +724,6 @@
+ name = reios;
+ sourceTree = "<group>";
+ };
+- 8703BC361A44B8DA00E7E939 /* assets */ = {
+- isa = PBXGroup;
+- children = (
+- 8703BC371A44B8DA00E7E939 /* Icon-72.png */,
+- 8703BC381A44B8DA00E7E939 /* Icon-72@2x.png */,
+- 8703BC391A44B8DA00E7E939 /* Icon.png */,
+- 8703BC3A1A44B8DA00E7E939 /* Icon@2x.png */,
+- );
+- name = assets;
+- sourceTree = "<group>";
+- };
+ 87078A7A18A47FE90034C7A0 = {
+ isa = PBXGroup;
+ children = (
+@@ -769,6 +760,7 @@
+ 87078A9018A47FE90034C7A0 /* reicast */ = {
+ isa = PBXGroup;
+ children = (
++ 878B0CFB1B8BB5B400A8D1C5 /* Images.xcassets */,
+ 9C7A3BC318C84EA10070BB5F /* MainStoryboard.storyboard */,
+ 87078A9918A47FE90034C7A0 /* AppDelegate.h */,
+ 87078A9A18A47FE90034C7A0 /* AppDelegate.m */,
+@@ -790,7 +782,6 @@
+ 87078A9118A47FE90034C7A0 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+- 8703BC361A44B8DA00E7E939 /* assets */,
+ 87078A9218A47FE90034C7A0 /* reicast-ios-Info.plist */,
+ 87078A9618A47FE90034C7A0 /* main.m */,
+ 87078A9818A47FE90034C7A0 /* reicast-ios-Prefix.pch */,
+@@ -823,6 +814,11 @@
+ 9C7A393618C805F70070BB5F /* View Controller Subclasses */ = {
+ isa = PBXGroup;
+ children = (
++ 87FA52E61B8CE18600CEFC32 /* PadViewController.h */,
++ 87FA52E71B8CE18600CEFC32 /* PadViewController.m */,
++ 87FA52E81B8CE18600CEFC32 /* PadViewController.xib */,
++ 8794D9C11B88F3D600B1B3A3 /* DiskViewCell.h */,
++ 8794D9C21B88F3D600B1B3A3 /* DiskViewCell.m */,
+ 87C208C51B7A4BFA00638BDD /* AboutViewController.h */,
+ 87C208C61B7A4BFA00638BDD /* AboutViewController.m */,
+ 87C208C71B7A4BFA00638BDD /* BrowserTableViewController.h */,
+@@ -860,6 +856,7 @@
+ 87D92F291B7A1B4800D8FD9E /* JoystickBackground.png */,
+ 87D92F2A1B7A1B4800D8FD9E /* JoystickBackground@2x.png */,
+ 87D92F2B1B7A1B4800D8FD9E /* JoystickButton.png */,
++ 878B0CFF1B8BFE6200A8D1C5 /* disk_unknown.png */,
+ 87D92F2C1B7A1B4800D8FD9E /* JoystickButton@2x.png */,
+ 87D92F2D1B7A1B4800D8FD9E /* LTrigger.png */,
+ 87D92F2E1B7A1B4800D8FD9E /* LTrigger@2x.png */,
+@@ -924,7 +921,6 @@
+ 9C7A394818C806DE0070BB5F /* E_VLoadStore.h */,
+ 9C7A394918C806DE0070BB5F /* E_VRegXfer.h */,
+ 9C7A394A18C806DE0070BB5F /* H_Branches.h */,
+- 9C7A394B18C806DE0070BB5F /* H_fp.h */,
+ 9C7A394C18C806DE0070BB5F /* H_LoadStore.h */,
+ 9C7A394D18C806DE0070BB5F /* H_psuedo.h */,
+ 9C7A394E18C806DE0070BB5F /* H_state.h */,
+@@ -1007,7 +1003,6 @@
+ children = (
+ 84967C731B8F492C005F1140 /* arm */,
+ 84967C771B8F492C005F1140 /* config.h */,
+- 84967C781B8F492C005F1140 /* fp.h */,
+ 84967C791B8F492C005F1140 /* png.c */,
+ 84967C7A1B8F492C005F1140 /* png.h */,
+ 84967C7B1B8F492C005F1140 /* pngconf.h */,
+@@ -1506,10 +1501,6 @@
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+- 87DCDB251B7EE57D0054D67C /* Icon-72.png in Resources */,
+- 87DCDB261B7EE5850054D67C /* Icon-72@2x.png in Resources */,
+- 87DCDB271B7EE5850054D67C /* Icon.png in Resources */,
+- 87DCDB281B7EE5850054D67C /* Icon@2x.png in Resources */,
+ 87D92F461B7A1B4800D8FD9E /* menuback@2x.png in Resources */,
+ 87D92F491B7A1B4800D8FD9E /* RTrigger.png in Resources */,
+ 87D92F4B1B7A1B4800D8FD9E /* Start.png in Resources */,
+@@ -1517,6 +1508,7 @@
+ 87D92F391B7A1B4800D8FD9E /* 210-twitterbird.png in Resources */,
+ 87D92F3A1B7A1B4800D8FD9E /* 210-twitterbird@2x.png in Resources */,
+ 87D92F3D1B7A1B4800D8FD9E /* DPad.png in Resources */,
++ 878B0CFC1B8BB5B400A8D1C5 /* Images.xcassets in Resources */,
+ 87D92F3C1B7A1B4800D8FD9E /* ABXYPad@2x.png in Resources */,
+ 87D92F401B7A1B4800D8FD9E /* JoystickBackground@2x.png in Resources */,
+ 9C7A3AA418C806E00070BB5F /* core.mk in Resources */,
+@@ -1535,7 +1527,9 @@
+ 87D92F441B7A1B4800D8FD9E /* LTrigger@2x.png in Resources */,
+ 87D92F551B7A1BB100D8FD9E /* LICENSE in Resources */,
+ 87D92F451B7A1B4800D8FD9E /* menuback.png in Resources */,
++ 87FA52EA1B8CE18600CEFC32 /* PadViewController.xib in Resources */,
+ 87078AA518A47FE90034C7A0 /* Shader.vsh in Resources */,
++ 878B0D001B8BFE6200A8D1C5 /* disk_unknown.png in Resources */,
+ 87D92F3B1B7A1B4800D8FD9E /* ABXYPad.png in Resources */,
+ 87D92F481B7A1B4800D8FD9E /* menuicon@2x.png in Resources */,
+ 87D92F371B7A1B4800D8FD9E /* 210-octocat.png in Resources */,
+@@ -1601,6 +1595,7 @@
+ 9C7A3B2318C806E00070BB5F /* sh4_opcodes.cpp in Sources */,
+ 84967C951B8F492C005F1140 /* pngmem.c in Sources */,
+ 9C7A3ADA18C806E00070BB5F /* zip_name_locate.c in Sources */,
++ 8794D9C31B88F3D600B1B3A3 /* DiskViewCell.m in Sources */,
+ 9C7A3B1B18C806E00070BB5F /* ta_ctx.cpp in Sources */,
+ 9C7A3AE018C806E00070BB5F /* zip_set_archive_flag.c in Sources */,
+ 9C7A3B3518C806E00070BB5F /* cdi.cpp in Sources */,
+@@ -1635,6 +1630,7 @@
+ 9C7A3B5918C81A4F0070BB5F /* SWRevealViewController.m in Sources */,
+ 9C7A3B0F18C806E00070BB5F /* maple_cfg.cpp in Sources */,
+ 9C7A3AF318C806E00070BB5F /* crc32.c in Sources */,
++ 87FA52E91B8CE18600CEFC32 /* PadViewController.m in Sources */,
+ 8497BCC01A41A0E900EFB9ED /* nixprof.cpp in Sources */,
+ 9C7A3AE118C806E00070BB5F /* zip_set_file_comment.c in Sources */,
+ 84967C9A1B8F492C005F1140 /* pngrutil.c in Sources */,
+@@ -1840,6 +1836,8 @@
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = armv7;
++ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
++ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+@@ -1875,6 +1873,8 @@
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = armv7;
++ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
++ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+diff -Nur a/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-ios.xcscheme b/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-ios.xcscheme
+--- a/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-ios.xcscheme 2015-10-06 21:43:53.127337010 -0300
++++ b/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-ios.xcscheme 2015-10-06 22:10:31.210968157 -0300
+@@ -62,7 +62,8 @@
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ allowLocationSimulation = "YES">
+- <BuildableProductRunnable>
++ <BuildableProductRunnable
++ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "87078A8218A47FE90034C7A0"
+@@ -80,7 +81,8 @@
+ useCustomWorkingDirectory = "NO"
+ buildConfiguration = "Release"
+ debugDocumentVersioning = "YES">
+- <BuildableProductRunnable>
++ <BuildableProductRunnable
++ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "87078A8218A47FE90034C7A0"
diff --git a/pcr/reicast-multilib-git/multiplayer-unstable.patch b/pcr/reicast-multilib-git/multiplayer-unstable.patch
new file mode 100644
index 000000000..cc4e11906
--- /dev/null
+++ b/pcr/reicast-multilib-git/multiplayer-unstable.patch
@@ -0,0 +1,450 @@
+diff -Nur a/core/hw/maple/maple_cfg.cpp b/core/hw/maple/maple_cfg.cpp
+--- a/core/hw/maple/maple_cfg.cpp 2015-10-06 21:43:53.027336293 -0300
++++ b/core/hw/maple/maple_cfg.cpp 2015-10-06 22:08:18.378992677 -0300
+@@ -3,6 +3,7 @@
+ #include "maple_helper.h"
+ #include "maple_devs.h"
+ #include "maple_cfg.h"
++#include "maple_controller.h"
+
+ #define HAS_VMU
+ /*
+@@ -22,11 +23,6 @@
+ */
+ void UpdateInputState(u32 port);
+
+-extern u16 kcode[4];
+-extern u32 vks[4];
+-extern s8 joyx[4],joyy[4];
+-extern u8 rt[4],lt[4];
+-
+ u8 GetBtFromSgn(s8 val)
+ {
+ return val+128;
+@@ -45,11 +41,11 @@
+ {
+ UpdateInputState(dev->bus_id);
+
+- pjs->kcode=kcode[dev->bus_id] | 0xF901;
+- pjs->joy[PJAI_X1]=GetBtFromSgn(joyx[dev->bus_id]);
+- pjs->joy[PJAI_Y1]=GetBtFromSgn(joyy[dev->bus_id]);
+- pjs->trigger[PJTI_R]=rt[dev->bus_id];
+- pjs->trigger[PJTI_L]=lt[dev->bus_id];
++ pjs->kcode = maple_controller[dev->bus_id].buttons | 0xF901;
++ pjs->joy[PJAI_X1] = GetBtFromSgn(maple_controller[dev->bus_id].stick_x);
++ pjs->joy[PJAI_Y1] = GetBtFromSgn(maple_controller[dev->bus_id].stick_y);
++ pjs->trigger[PJTI_R] = maple_controller[dev->bus_id].trigger_right;
++ pjs->trigger[PJTI_L] = maple_controller[dev->bus_id].trigger_left;
+ }
+ void SetImage(void* img)
+ {
+@@ -68,14 +64,19 @@
+
+ void mcfg_CreateDevices()
+ {
+-#if DC_PLATFORM == DC_PLATFORM_DREAMCAST
+- mcfg_Create(MDT_SegaController,0,5);
+-
+- mcfg_Create(MDT_SegaVMU,0,0);
+- mcfg_Create(MDT_SegaVMU,0,1);
+-#else
+- mcfg_Create(MDT_NaomiJamma, 0, 5);
+-#endif
++ for(int port = 0; port < MAPLE_NUM_PORTS; port++)
++ {
++ if(maple_controller[port].enabled)
++ {
++ #if DC_PLATFORM == DC_PLATFORM_DREAMCAST
++ mcfg_Create(MDT_SegaController, port, 5);
++ mcfg_Create(MDT_SegaVMU, port, 0);
++ mcfg_Create(MDT_SegaVMU, port, 1);
++ #else
++ mcfg_Create(MDT_NaomiJamma, port, 5);
++ #endif
++ }
++ }
+ }
+
+ void mcfg_DestroyDevices()
+diff -Nur a/core/hw/maple/maple_controller.cpp b/core/hw/maple/maple_controller.cpp
+--- a/core/hw/maple/maple_controller.cpp 1969-12-31 21:00:00.000000000 -0300
++++ b/core/hw/maple/maple_controller.cpp 2015-10-06 22:08:18.378992677 -0300
+@@ -0,0 +1,8 @@
++#include "hw/maple/maple_controller.h"
++
++MapleController maple_controller[MAPLE_NUM_PORTS] = {
++ { 1 , 0xFFFF, 0, 0, 0, 0 },
++ { 0 , 0xFFFF, 0, 0, 0, 0 },
++ { 0 , 0xFFFF, 0, 0, 0, 0 },
++ { 0 , 0xFFFF, 0, 0, 0, 0 }
++};
+diff -Nur a/core/hw/maple/maple_controller.h b/core/hw/maple/maple_controller.h
+--- a/core/hw/maple/maple_controller.h 1969-12-31 21:00:00.000000000 -0300
++++ b/core/hw/maple/maple_controller.h 2015-10-06 22:08:18.378992677 -0300
+@@ -0,0 +1,42 @@
++#pragma once
++#include "types.h"
++
++// If you change the value of MAPLE_NUM_PORTS, please note that you need to change the initializers in maple_controller.cpp as well
++#define MAPLE_NUM_PORTS 4
++
++struct MapleController
++{
++ bool enabled;
++ u16 buttons;
++ u8 trigger_left;
++ u8 trigger_right;
++ s8 stick_x;
++ s8 stick_y;
++};
++
++extern MapleController maple_controller[MAPLE_NUM_PORTS];
++
++enum DreamcastControllerCodes
++{
++ DC_BTN_C = 1,
++ DC_BTN_B = 1<<1,
++ DC_BTN_A = 1<<2,
++ DC_BTN_START = 1<<3,
++ DC_BTN_DPAD_UP = 1<<4,
++ DC_BTN_DPAD_DOWN = 1<<5,
++ DC_BTN_DPAD_LEFT = 1<<6,
++ DC_BTN_DPAD_RIGHT = 1<<7,
++ DC_BTN_Z = 1<<8,
++ DC_BTN_Y = 1<<9,
++ DC_BTN_X = 1<<10,
++ DC_BTN_D = 1<<11,
++ DC_BTN_DPAD2_UP = 1<<12,
++ DC_BTN_DPAD2_DOWN = 1<<13,
++ DC_BTN_DPAD2_LEFT = 1<<14,
++ DC_BTN_DPAD2_RIGHT = 1<<15,
++
++ DC_AXIS_LT = 0X10000,
++ DC_AXIS_RT = 0X10001,
++ DC_AXIS_X = 0X20000,
++ DC_AXIS_Y = 0X20001
++};
+diff -Nur a/core/linux-dist/x11.cpp b/core/linux-dist/x11.cpp
+--- a/core/linux-dist/x11.cpp 2015-10-06 22:04:14.520204440 -0300
++++ b/core/linux-dist/x11.cpp 2015-10-06 22:08:18.381992699 -0300
+@@ -82,11 +82,11 @@
+ int dc_key = x11_keymap[e.xkey.keycode];
+ if (e.type == KeyPress)
+ {
+- kcode[0] &= ~dc_key;
++ maple_controller[0].buttons &= ~dc_key;
+ }
+ else
+ {
+- kcode[0] |= dc_key;
++ maple_controller[0].buttons |= dc_key;
+ }
+ }
+ //printf("KEY: %d -> %d: %d\n",e.xkey.keycode, dc_key, x11_dc_buttons );
+diff -Nur a/core/nacl/nacl.cpp b/core/nacl/nacl.cpp
+--- a/core/nacl/nacl.cpp 2015-10-06 22:07:39.028703879 -0300
++++ b/core/nacl/nacl.cpp 2015-10-06 22:08:18.381992699 -0300
+@@ -15,6 +15,7 @@
+ #include "ppapi/utility/completion_callback_factory.h"
+
+ #include "types.h"
++#include "hw/maple/maple_controller.h"
+
+ #include <GLES2/gl2.h>
+
+@@ -234,12 +235,6 @@
+ }
+ } // namespace pp
+
+-
+-u16 kcode[4];
+-u32 vks[4];
+-s8 joyx[4],joyy[4];
+-u8 rt[4],lt[4];
+-
+ int get_mic_data(u8* buffer) { return 0; }
+ int push_vmu_screen(u8* buffer) { return 0; }
+
+diff -Nur a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp
+--- a/core/rend/gles/gles.cpp 2015-10-06 22:07:39.029703886 -0300
++++ b/core/rend/gles/gles.cpp 2015-10-06 22:08:18.382992707 -0300
+@@ -1833,7 +1810,7 @@
+ }
+
+ bool do_swp=false;
+- //if (kcode[0]&(1<<9))
++ //if (maple_controller[0].buttons&(1<<9))
+ {
+
+
+diff -Nur a/core/windows/winmain.cpp b/core/windows/winmain.cpp
+--- a/core/windows/winmain.cpp 2015-10-06 22:07:39.030703893 -0300
++++ b/core/windows/winmain.cpp 2015-10-06 22:08:18.383992714 -0300
+@@ -1,5 +1,6 @@
+ #include "oslib\oslib.h"
+ #include "oslib\audiostream.h"
++#include "hw\maple\maple_controller.h"
+ #include "imgread\common.h"
+
+ #define _WIN32_WINNT 0x0500
+@@ -173,66 +174,46 @@
+ return MessageBox(NULL,temp,VER_SHORTNAME,type | MB_TASKMODAL);
+ }
+
+-u16 kcode[4];
+-u32 vks[4];
+-s8 joyx[4],joyy[4];
+-u8 rt[4],lt[4];
+-#define key_CONT_C (1 << 0)
+-#define key_CONT_B (1 << 1)
+-#define key_CONT_A (1 << 2)
+-#define key_CONT_START (1 << 3)
+-#define key_CONT_DPAD_UP (1 << 4)
+-#define key_CONT_DPAD_DOWN (1 << 5)
+-#define key_CONT_DPAD_LEFT (1 << 6)
+-#define key_CONT_DPAD_RIGHT (1 << 7)
+-#define key_CONT_Z (1 << 8)
+-#define key_CONT_Y (1 << 9)
+-#define key_CONT_X (1 << 10)
+-#define key_CONT_D (1 << 11)
+-#define key_CONT_DPAD2_UP (1 << 12)
+-#define key_CONT_DPAD2_DOWN (1 << 13)
+-#define key_CONT_DPAD2_LEFT (1 << 14)
+-#define key_CONT_DPAD2_RIGHT (1 << 15)
+ void UpdateInputState(u32 port)
+ {
+- //joyx[port]=pad.Lx;
+- //joyy[port]=pad.Ly;
+- lt[port]=GetAsyncKeyState('A')?255:0;
+- rt[port]=GetAsyncKeyState('S')?255:0;
++ //maple_controller[port].stick_x = pad.Lx;
++ //maple_controller[port].stick_y = pad.Ly;
++ maple_controller[port].trigger_left = GetAsyncKeyState('A') ? 255 : 0;
++ maple_controller[port].trigger_right = GetAsyncKeyState('S') ? 255 : 0;
+
+- joyx[port]=joyy[port]=0;
++ maple_controller[port].stick_x = maple_controller[port].stick_y = 0;
+
+ if (GetAsyncKeyState('J'))
+- joyx[port]-=126;
++ maple_controller[port].stick_x -= 126;
+ if (GetAsyncKeyState('L'))
+- joyx[port]+=126;
++ maple_controller[port].stick_x += 126;
+
+ if (GetAsyncKeyState('I'))
+- joyy[port]-=126;
++ maple_controller[port].stick_y -= 126;
+ if (GetAsyncKeyState('K'))
+- joyy[port]+=126;
++ maple_controller[port].stick_y += 126;
+
+- kcode[port]=0xFFFF;
++ maple_controller[port].buttons = 0xFFFF;
+ if (GetAsyncKeyState('V'))
+- kcode[port]&=~key_CONT_A;
++ maple_controller[port].buttons &= ~DC_BTN_A;
+ if (GetAsyncKeyState('C'))
+- kcode[port]&=~key_CONT_B;
++ maple_controller[port].buttons &= ~DC_BTN_B;
+ if (GetAsyncKeyState('X'))
+- kcode[port]&=~key_CONT_Y;
++ maple_controller[port].buttons &= ~DC_BTN_Y;
+ if (GetAsyncKeyState('Z'))
+- kcode[port]&=~key_CONT_X;
++ maple_controller[port].buttons &= ~DC_BTN_X;
+
+ if (GetAsyncKeyState(VK_SHIFT))
+- kcode[port]&=~key_CONT_START;
++ maple_controller[port].buttons &= ~DC_BTN_START;
+
+ if (GetAsyncKeyState(VK_UP))
+- kcode[port]&=~key_CONT_DPAD_UP;
++ maple_controller[port].buttons &= ~DC_BTN_DPAD_UP;
+ if (GetAsyncKeyState(VK_DOWN))
+- kcode[port]&=~key_CONT_DPAD_DOWN;
++ maple_controller[port].buttons &= ~DC_BTN_DPAD_DOWN;
+ if (GetAsyncKeyState(VK_LEFT))
+- kcode[port]&=~key_CONT_DPAD_LEFT;
++ maple_controller[port].buttons &= ~DC_BTN_DPAD_LEFT;
+ if (GetAsyncKeyState(VK_RIGHT))
+- kcode[port]&=~key_CONT_DPAD_RIGHT;
++ maple_controller[port].buttons &= ~DC_BTN_DPAD_RIGHT;
+
+ if (GetAsyncKeyState(VK_F1))
+ settings.pvr.ta_skip = 100;
+diff -Nur a/shell/android/jni/src/Android.cpp b/shell/android/jni/src/Android.cpp
+--- a/shell/android/jni/src/Android.cpp 2015-10-06 22:07:39.030703893 -0300
++++ b/shell/android/jni/src/Android.cpp 2015-10-06 22:08:18.383992714 -0300
+@@ -16,6 +16,7 @@
+ #include "rend/TexCache.h"
+ #include "hw/maple/maple_devs.h"
+ #include "hw/maple/maple_if.h"
++#include "hw/maple/maple_controller.h"
+ #include "oslib/audiobackend_android.h"
+
+ #include "util.h"
+@@ -175,10 +176,6 @@
+ // Additonal controllers 2, 3 and 4 connected ?
+ static bool add_controllers[3] = { false, false, false };
+
+-u16 kcode[4];
+-u32 vks[4];
+-s8 joyx[4],joyy[4];
+-u8 rt[4],lt[4];
+ float vjoy_pos[14][8];
+
+ extern bool print_stats;
+@@ -211,7 +208,7 @@
+ }
+
+ // Add additonal controllers
+- for (int i = 0; i < 3; i++)
++ for (int i = 0; i < (MAPLE_NUM_PORTS - 1); i++)
+ {
+ if (add_controllers[i])
+ mcfg_Create(MDT_SegaController,i+1,5);
+@@ -443,13 +440,13 @@
+ jint *jx_body = env->GetIntArrayElements(jx, 0);
+ jint *jy_body = env->GetIntArrayElements(jy, 0);
+
+- for(int i = 0; i < 4; i++)
++ for(int i = 0; i < MAPLE_NUM_PORTS; i++)
+ {
+- kcode[i] = k_code_body[i];
+- lt[i] = l_t_body[i];
+- rt[i] = r_t_body[i];
+- joyx[i] = jx_body[i];
+- joyy[i] = jy_body[i];
++ maple_controller[i].buttons = k_code_body[i];
++ maple_controller[i].trigger_left = l_t_body[i];
++ maple_controller[i].trigger_right = r_t_body[i];
++ maple_controller[i].stick_x = jx_body[i];
++ maple_controller[i].stick_y = jy_body[i];
+ }
+
+ env->ReleaseIntArrayElements(k_code, k_code_body, 0);
+diff -Nur a/shell/apple/emulator-ios/emulator/ios_main.mm b/shell/apple/emulator-ios/emulator/ios_main.mm
+--- a/shell/apple/emulator-ios/emulator/ios_main.mm 2015-10-06 22:07:39.030703893 -0300
++++ b/shell/apple/emulator-ios/emulator/ios_main.mm 2015-10-06 22:08:18.383992714 -0300
+@@ -20,6 +20,7 @@
+ #include <sys/mman.h>
+ #include <sys/time.h>
+ #include "hw/sh4/dyna/blockmanager.h"
++#include "hw/maple/maple_controller.h"
+ #include <unistd.h>
+
+
+@@ -55,11 +56,6 @@
+ int dc_init(int argc,wchar* argv[]);
+ void dc_run();
+
+-u16 kcode[4];
+-u32 vks[4];
+-s8 joyx[4],joyy[4];
+-u8 rt[4],lt[4];
+-
+ extern "C" int reicast_main(int argc, wchar* argv[])
+ {
+ //if (argc==2)
+diff -Nur a/shell/apple/emulator-osx/emulator-osx/osx-main.mm b/shell/apple/emulator-osx/emulator-osx/osx-main.mm
+--- a/shell/apple/emulator-osx/emulator-osx/osx-main.mm 2015-10-06 22:07:39.030703893 -0300
++++ b/shell/apple/emulator-osx/emulator-osx/osx-main.mm 2015-10-06 22:08:18.383992714 -0300
+@@ -8,6 +8,7 @@
+ #import <Carbon/Carbon.h>
+
+ #include "types.h"
++#include "hw/maple/maple_controller.h"
+ #include <sys/stat.h>
+
+ #include <OpenGL/gl3.h>
+@@ -38,11 +39,6 @@
+ return 0;
+ }
+
+-u16 kcode[4] = { 0xFFFF };
+-u32 vks[4];
+-s8 joyx[4],joyy[4];
+-u8 rt[4],lt[4];
+-
+ int get_mic_data(u8* buffer) { return 0; }
+ int push_vmu_screen(u8* buffer) { return 0; }
+
+@@ -131,35 +127,11 @@
+ gles_init();
+ }
+
+-enum DCPad {
+- Btn_C = 1,
+- Btn_B = 1<<1,
+- Btn_A = 1<<2,
+- Btn_Start = 1<<3,
+- DPad_Up = 1<<4,
+- DPad_Down = 1<<5,
+- DPad_Left = 1<<6,
+- DPad_Right = 1<<7,
+- Btn_Z = 1<<8,
+- Btn_Y = 1<<9,
+- Btn_X = 1<<10,
+- Btn_D = 1<<11,
+- DPad2_Up = 1<<12,
+- DPad2_Down = 1<<13,
+- DPad2_Left = 1<<14,
+- DPad2_Right = 1<<15,
+-
+- Axis_LT= 0x10000,
+- Axis_RT= 0x10001,
+- Axis_X= 0x20000,
+- Axis_Y= 0x20001,
+-};
+-
+ void handle_key(int dckey, int state) {
+ if (state)
+- kcode[0] &= ~dckey;
++ maple_controller[0].buttons &= ~dckey;
+ else
+- kcode[0] |= dckey;
++ maple_controller[0].buttons |= dckey;
+ }
+
+ void handle_trig(u8* dckey, int state) {
+@@ -172,18 +144,18 @@
+ extern "C" void emu_key_input(char* keyt, int state) {
+ int key = keyt[0];
+ switch(key) {
+- case 'z': handle_key(Btn_X, state); break;
+- case 'x': handle_key(Btn_Y, state); break;
+- case 'c': handle_key(Btn_B, state); break;
+- case 'v': handle_key(Btn_A, state); break;
++ case 'z': handle_key(DC_BTN_X, state); break;
++ case 'x': handle_key(DC_BTN_Y, state); break;
++ case 'c': handle_key(DC_BTN_B, state); break;
++ case 'v': handle_key(DC_BTN_A, state); break;
+
+ case 'a': handle_trig(lt, state); break;
+ case 's': handle_trig(rt, state); break;
+
+- case 'j': handle_key(DPad_Left, state); break;
+- case 'k': handle_key(DPad_Down, state); break;
+- case 'l': handle_key(DPad_Right, state); break;
+- case 'i': handle_key(DPad_Up, state); break;
+- case 0xa: handle_key(Btn_Start, state); break;
++ case 'j': handle_key(DC_BTN_DPAD_LEFT, state); break;
++ case 'k': handle_key(DC_BTN_DPAD_DOWN, state); break;
++ case 'l': handle_key(DC_BTN_DPAD_RIGHT, state); break;
++ case 'i': handle_key(DC_BTN_DPAD_UP, state); break;
++ case 0xa: handle_key(DC_BTN_START, state); break;
+ }
+ }
+\ No newline at end of file
+diff -Nur a/shell/reicast.vcxproj b/shell/reicast.vcxproj
+--- a/shell/reicast.vcxproj 2015-10-06 22:07:39.030703893 -0300
++++ b/shell/reicast.vcxproj 2015-10-06 22:08:18.383992714 -0300
+@@ -92,6 +92,7 @@
+ <ClCompile Include="..\core\hw\holly\sb_dma.cpp" />
+ <ClCompile Include="..\core\hw\holly\sb_mem.cpp" />
+ <ClCompile Include="..\core\hw\maple\maple_cfg.cpp" />
++ <ClCompile Include="..\core\hw\maple\maple_controller.cpp" />
+ <ClCompile Include="..\core\hw\maple\maple_devs.cpp" />
+ <ClCompile Include="..\core\hw\maple\maple_helper.cpp" />
+ <ClCompile Include="..\core\hw\maple\maple_if.cpp" />
diff --git a/pcr/reicast-multilib-git/multiplayer.patch b/pcr/reicast-multilib-git/multiplayer.patch
new file mode 100644
index 000000000..19ee3c869
--- /dev/null
+++ b/pcr/reicast-multilib-git/multiplayer.patch
@@ -0,0 +1,1211 @@
+diff -Nur a/core/hw/maple/maple_cfg.cpp b/core/hw/maple/maple_cfg.cpp
+--- a/core/hw/maple/maple_cfg.cpp 2015-10-06 21:43:53.027336293 -0300
++++ b/core/hw/maple/maple_cfg.cpp 2015-10-06 22:08:18.378992677 -0300
+@@ -3,6 +3,7 @@
+ #include "maple_helper.h"
+ #include "maple_devs.h"
+ #include "maple_cfg.h"
++#include "maple_controller.h"
+
+ #define HAS_VMU
+ /*
+@@ -22,11 +23,6 @@
+ */
+ void UpdateInputState(u32 port);
+
+-extern u16 kcode[4];
+-extern u32 vks[4];
+-extern s8 joyx[4],joyy[4];
+-extern u8 rt[4],lt[4];
+-
+ u8 GetBtFromSgn(s8 val)
+ {
+ return val+128;
+@@ -45,11 +41,11 @@
+ {
+ UpdateInputState(dev->bus_id);
+
+- pjs->kcode=kcode[dev->bus_id] | 0xF901;
+- pjs->joy[PJAI_X1]=GetBtFromSgn(joyx[dev->bus_id]);
+- pjs->joy[PJAI_Y1]=GetBtFromSgn(joyy[dev->bus_id]);
+- pjs->trigger[PJTI_R]=rt[dev->bus_id];
+- pjs->trigger[PJTI_L]=lt[dev->bus_id];
++ pjs->kcode = maple_controller[dev->bus_id].buttons | 0xF901;
++ pjs->joy[PJAI_X1] = GetBtFromSgn(maple_controller[dev->bus_id].stick_x);
++ pjs->joy[PJAI_Y1] = GetBtFromSgn(maple_controller[dev->bus_id].stick_y);
++ pjs->trigger[PJTI_R] = maple_controller[dev->bus_id].trigger_right;
++ pjs->trigger[PJTI_L] = maple_controller[dev->bus_id].trigger_left;
+ }
+ void SetImage(void* img)
+ {
+@@ -68,14 +64,19 @@
+
+ void mcfg_CreateDevices()
+ {
+-#if DC_PLATFORM == DC_PLATFORM_DREAMCAST
+- mcfg_Create(MDT_SegaController,0,5);
+-
+- mcfg_Create(MDT_SegaVMU,0,0);
+- mcfg_Create(MDT_SegaVMU,0,1);
+-#else
+- mcfg_Create(MDT_NaomiJamma, 0, 5);
+-#endif
++ for(int port = 0; port < MAPLE_NUM_PORTS; port++)
++ {
++ if(maple_controller[port].enabled)
++ {
++ #if DC_PLATFORM == DC_PLATFORM_DREAMCAST
++ mcfg_Create(MDT_SegaController, port, 5);
++ mcfg_Create(MDT_SegaVMU, port, 0);
++ mcfg_Create(MDT_SegaVMU, port, 1);
++ #else
++ mcfg_Create(MDT_NaomiJamma, port, 5);
++ #endif
++ }
++ }
+ }
+
+ void mcfg_DestroyDevices()
+diff -Nur a/core/hw/maple/maple_controller.cpp b/core/hw/maple/maple_controller.cpp
+--- a/core/hw/maple/maple_controller.cpp 1969-12-31 21:00:00.000000000 -0300
++++ b/core/hw/maple/maple_controller.cpp 2015-10-06 22:08:18.378992677 -0300
+@@ -0,0 +1,8 @@
++#include "hw/maple/maple_controller.h"
++
++MapleController maple_controller[MAPLE_NUM_PORTS] = {
++ { 1 , 0xFFFF, 0, 0, 0, 0 },
++ { 0 , 0xFFFF, 0, 0, 0, 0 },
++ { 0 , 0xFFFF, 0, 0, 0, 0 },
++ { 0 , 0xFFFF, 0, 0, 0, 0 }
++};
+diff -Nur a/core/hw/maple/maple_controller.h b/core/hw/maple/maple_controller.h
+--- a/core/hw/maple/maple_controller.h 1969-12-31 21:00:00.000000000 -0300
++++ b/core/hw/maple/maple_controller.h 2015-10-06 22:08:18.378992677 -0300
+@@ -0,0 +1,42 @@
++#pragma once
++#include "types.h"
++
++// If you change the value of MAPLE_NUM_PORTS, please note that you need to change the initializers in maple_controller.cpp as well
++#define MAPLE_NUM_PORTS 4
++
++struct MapleController
++{
++ bool enabled;
++ u16 buttons;
++ u8 trigger_left;
++ u8 trigger_right;
++ s8 stick_x;
++ s8 stick_y;
++};
++
++extern MapleController maple_controller[MAPLE_NUM_PORTS];
++
++enum DreamcastControllerCodes
++{
++ DC_BTN_C = 1,
++ DC_BTN_B = 1<<1,
++ DC_BTN_A = 1<<2,
++ DC_BTN_START = 1<<3,
++ DC_BTN_DPAD_UP = 1<<4,
++ DC_BTN_DPAD_DOWN = 1<<5,
++ DC_BTN_DPAD_LEFT = 1<<6,
++ DC_BTN_DPAD_RIGHT = 1<<7,
++ DC_BTN_Z = 1<<8,
++ DC_BTN_Y = 1<<9,
++ DC_BTN_X = 1<<10,
++ DC_BTN_D = 1<<11,
++ DC_BTN_DPAD2_UP = 1<<12,
++ DC_BTN_DPAD2_DOWN = 1<<13,
++ DC_BTN_DPAD2_LEFT = 1<<14,
++ DC_BTN_DPAD2_RIGHT = 1<<15,
++
++ DC_AXIS_LT = 0X10000,
++ DC_AXIS_RT = 0X10001,
++ DC_AXIS_X = 0X20000,
++ DC_AXIS_Y = 0X20001
++};
+diff -Nur a/core/linux-dist/evdev.cpp b/core/linux-dist/evdev.cpp
+--- a/core/linux-dist/evdev.cpp 2015-10-06 22:07:39.028703879 -0300
++++ b/core/linux-dist/evdev.cpp 2015-10-06 22:08:18.381992699 -0300
+@@ -314,43 +314,43 @@
+ {
+ case EV_KEY:
+ if (ie.code == controller->mapping->Btn_A) {
+- SET_FLAG(kcode[port], DC_BTN_A, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_A, ie.value);
+ } else if (ie.code == controller->mapping->Btn_B) {
+- SET_FLAG(kcode[port], DC_BTN_B, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_B, ie.value);
+ } else if (ie.code == controller->mapping->Btn_C) {
+- SET_FLAG(kcode[port], DC_BTN_C, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_C, ie.value);
+ } else if (ie.code == controller->mapping->Btn_D) {
+- SET_FLAG(kcode[port], DC_BTN_D, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_D, ie.value);
+ } else if (ie.code == controller->mapping->Btn_X) {
+- SET_FLAG(kcode[port], DC_BTN_X, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_X, ie.value);
+ } else if (ie.code == controller->mapping->Btn_Y) {
+- SET_FLAG(kcode[port], DC_BTN_Y, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_Y, ie.value);
+ } else if (ie.code == controller->mapping->Btn_Z) {
+- SET_FLAG(kcode[port], DC_BTN_Z, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_Z, ie.value);
+ } else if (ie.code == controller->mapping->Btn_Start) {
+- SET_FLAG(kcode[port], DC_BTN_START, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_START, ie.value);
+ } else if (ie.code == controller->mapping->Btn_Escape) {
+ die("death by escape key");
+ } else if (ie.code == controller->mapping->Btn_DPad_Left) {
+- SET_FLAG(kcode[port], DC_DPAD_LEFT, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_LEFT, ie.value);
+ } else if (ie.code == controller->mapping->Btn_DPad_Right) {
+- SET_FLAG(kcode[port], DC_DPAD_RIGHT, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_RIGHT, ie.value);
+ } else if (ie.code == controller->mapping->Btn_DPad_Up) {
+- SET_FLAG(kcode[port], DC_DPAD_UP, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_UP, ie.value);
+ } else if (ie.code == controller->mapping->Btn_DPad_Down) {
+- SET_FLAG(kcode[port], DC_DPAD_DOWN, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_DOWN, ie.value);
+ } else if (ie.code == controller->mapping->Btn_DPad2_Left) {
+- SET_FLAG(kcode[port], DC_DPAD2_LEFT, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_LEFT, ie.value);
+ } else if (ie.code == controller->mapping->Btn_DPad2_Right) {
+- SET_FLAG(kcode[port], DC_DPAD2_RIGHT, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_RIGHT, ie.value);
+ } else if (ie.code == controller->mapping->Btn_DPad2_Up) {
+- SET_FLAG(kcode[port], DC_DPAD2_UP, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_UP, ie.value);
+ } else if (ie.code == controller->mapping->Btn_DPad2_Down) {
+- SET_FLAG(kcode[port], DC_DPAD2_DOWN, ie.value);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_DOWN, ie.value);
+ } else if (ie.code == controller->mapping->Btn_Trigger_Left) {
+- lt[port] = (ie.value ? 255 : 0);
++ maple_controller[port].trigger_left = (ie.value ? 255 : 0);
+ } else if (ie.code == controller->mapping->Btn_Trigger_Right) {
+- rt[port] = (ie.value ? 255 : 0);
++ maple_controller[port].trigger_right = (ie.value ? 255 : 0);
+ }
+ break;
+ case EV_ABS:
+@@ -359,16 +359,16 @@
+ switch(ie.value)
+ {
+ case -1:
+- SET_FLAG(kcode[port], DC_DPAD_LEFT, 1);
+- SET_FLAG(kcode[port], DC_DPAD_RIGHT, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_LEFT, 1);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_RIGHT, 0);
+ break;
+ case 0:
+- SET_FLAG(kcode[port], DC_DPAD_LEFT, 0);
+- SET_FLAG(kcode[port], DC_DPAD_RIGHT, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_LEFT, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_RIGHT, 0);
+ break;
+ case 1:
+- SET_FLAG(kcode[port], DC_DPAD_LEFT, 0);
+- SET_FLAG(kcode[port], DC_DPAD_RIGHT, 1);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_LEFT, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_RIGHT, 1);
+ break;
+ }
+ }
+@@ -377,16 +377,16 @@
+ switch(ie.value)
+ {
+ case -1:
+- SET_FLAG(kcode[port], DC_DPAD_UP, 1);
+- SET_FLAG(kcode[port], DC_DPAD_DOWN, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_UP, 1);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_DOWN, 0);
+ break;
+ case 0:
+- SET_FLAG(kcode[port], DC_DPAD_UP, 0);
+- SET_FLAG(kcode[port], DC_DPAD_DOWN, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_UP, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_DOWN, 0);
+ break;
+ case 1:
+- SET_FLAG(kcode[port], DC_DPAD_UP, 0);
+- SET_FLAG(kcode[port], DC_DPAD_DOWN, 1);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_UP, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD_DOWN, 1);
+ break;
+ }
+ }
+@@ -395,16 +395,16 @@
+ switch(ie.value)
+ {
+ case -1:
+- SET_FLAG(kcode[port], DC_DPAD2_LEFT, 1);
+- SET_FLAG(kcode[port], DC_DPAD2_RIGHT, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_LEFT, 1);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_RIGHT, 0);
+ break;
+ case 0:
+- SET_FLAG(kcode[port], DC_DPAD2_LEFT, 0);
+- SET_FLAG(kcode[port], DC_DPAD2_RIGHT, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_LEFT, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_RIGHT, 0);
+ break;
+ case 1:
+- SET_FLAG(kcode[port], DC_DPAD2_LEFT, 0);
+- SET_FLAG(kcode[port], DC_DPAD2_RIGHT, 1);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_LEFT, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_RIGHT, 1);
+ break;
+ }
+ }
+@@ -413,34 +413,34 @@
+ switch(ie.value)
+ {
+ case -1:
+- SET_FLAG(kcode[port], DC_DPAD2_UP, 1);
+- SET_FLAG(kcode[port], DC_DPAD2_DOWN, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_UP, 1);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_DOWN, 0);
+ break;
+ case 0:
+- SET_FLAG(kcode[port], DC_DPAD2_UP, 0);
+- SET_FLAG(kcode[port], DC_DPAD2_DOWN, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_UP, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_DOWN, 0);
+ break;
+ case 1:
+- SET_FLAG(kcode[port], DC_DPAD2_UP, 0);
+- SET_FLAG(kcode[port], DC_DPAD2_DOWN, 1);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_UP, 0);
++ SET_FLAG(maple_controller[port].buttons, DC_BTN_DPAD2_DOWN, 1);
+ break;
+ }
+ }
+ else if (ie.code == controller->mapping->Axis_Analog_X)
+ {
+- joyx[port] = (controller->data_x.convert(ie.value) + 128);
++ maple_controller[port].stick_x = (controller->data_x.convert(ie.value) + 128);
+ }
+ else if (ie.code == controller->mapping->Axis_Analog_Y)
+ {
+- joyy[port] = (controller->data_y.convert(ie.value) + 128);
++ maple_controller[port].stick_y = (controller->data_y.convert(ie.value) + 128);
+ }
+ else if (ie.code == controller->mapping->Axis_Trigger_Left)
+ {
+- lt[port] = controller->data_trigger_left.convert(ie.value);
++ maple_controller[port].trigger_left = controller->data_trigger_left.convert(ie.value);
+ }
+ else if (ie.code == controller->mapping->Axis_Trigger_Right)
+ {
+- rt[port] = controller->data_trigger_right.convert(ie.value);
++ maple_controller[port].trigger_right = controller->data_trigger_right.convert(ie.value);
+ }
+ break;
+ }
+diff -Nur a/core/linux-dist/joystick.cpp b/core/linux-dist/joystick.cpp
+--- a/core/linux-dist/joystick.cpp 2015-10-06 21:43:53.042336401 -0300
++++ b/core/linux-dist/joystick.cpp 2015-10-06 22:08:18.381992699 -0300
+@@ -5,14 +5,14 @@
+ #include "linux-dist/joystick.h"
+
+ #if defined(USE_JOYSTICK)
+- const u32 joystick_map_btn_usb[JOYSTICK_MAP_SIZE] = { DC_BTN_Y, DC_BTN_B, DC_BTN_A, DC_BTN_X, 0, 0, 0, 0, 0, DC_BTN_START };
+- const u32 joystick_map_axis_usb[JOYSTICK_MAP_SIZE] = { DC_AXIS_X, DC_AXIS_Y, 0, 0, 0, 0, 0, 0, 0, 0 };
++ const DreamcastControllerCodes joystick_map_btn_usb[JOYSTICK_MAP_SIZE] = { DC_BTN_Y, DC_BTN_B, DC_BTN_A, DC_BTN_X, 0, 0, 0, 0, 0, DC_BTN_START };
++ const DreamcastControllerCodes joystick_map_axis_usb[JOYSTICK_MAP_SIZE] = { DC_AXIS_X, DC_AXIS_Y, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+- const u32 joystick_map_btn_xbox360[JOYSTICK_MAP_SIZE] = { DC_BTN_A, DC_BTN_B, DC_BTN_X, DC_BTN_Y, 0, 0, 0, DC_BTN_START, 0, 0 };
+- const u32 joystick_map_axis_xbox360[JOYSTICK_MAP_SIZE] = { DC_AXIS_X, DC_AXIS_Y, DC_AXIS_LT, 0, 0, DC_AXIS_RT, DC_DPAD_LEFT, DC_DPAD_UP, 0, 0 };
++ const DreamcastControllerCodes joystick_map_btn_xbox360[JOYSTICK_MAP_SIZE] = { DC_BTN_A, DC_BTN_B, DC_BTN_X, DC_BTN_Y, 0, 0, 0, DC_BTN_START, 0, 0 };
++ const DreamcastControllerCodes joystick_map_axis_xbox360[JOYSTICK_MAP_SIZE] = { DC_AXIS_X, DC_AXIS_Y, DC_AXIS_LT, 0, 0, DC_AXIS_RT, DC_BTN_DPAD_LEFT, DC_BTN_DPAD_UP, 0, 0 };
+
+- const u32* joystick_map_btn = joystick_map_btn_usb;
+- const u32* joystick_map_axis = joystick_map_axis_usb;
++ const DreamcastControllerCodes* joystick_map_btn = joystick_map_btn_usb;
++ const DreamcastControllerCodes* joystick_map_axis = joystick_map_axis_usb;
+
+ int input_joystick_init(const char* device)
+ {
+@@ -73,18 +73,18 @@
+
+ if (mt == 0)
+ {
+- kcode[port] |= mo;
+- kcode[port] |= mo*2;
++ maple_controller[port].buttons |= mo;
++ maple_controller[port].buttons |= mo*2;
+ if (v<-64)
+ {
+- kcode[port] &= ~mo;
++ maple_controller[port].buttons &= ~mo;
+ }
+ else if (v>64)
+ {
+- kcode[port] &= ~(mo*2);
++ maple_controller[port].buttons &= ~(mo*2);
+ }
+
+- //printf("Mapped to %d %d %d\n",mo,kcode[port]&mo,kcode[port]&(mo*2));
++ //printf("Mapped to %d %d %d\n",mo,maple_controller[port].buttons&mo,maple_controller[port].buttons&(mo*2));
+ }
+ else if (mt == 1)
+ {
+@@ -95,11 +95,11 @@
+ //printf("AXIS %d,%d Mapped to %d %d %d\n",JE.number,JE.value,mo,v,v+127);
+ if (mo == 0)
+ {
+- lt[port] = (v + 127);
++ maple_controller[port].trigger_left = (v + 127);
+ }
+ else if (mo == 1)
+ {
+- rt[port] = (v + 127);
++ maple_controller[port].trigger_right = (v + 127);
+ }
+ }
+ else if (mt == 2)
+@@ -107,11 +107,11 @@
+ // printf("AXIS %d,%d Mapped to %d %d [%d]",JE.number,JE.value,mo,v);
+ if (mo == 0)
+ {
+- joyx[port] = v;
++ maple_controller[port].stick_x = v;
+ }
+ else if (mo == 1)
+ {
+- joyy[port] = v;
++ maple_controller[port].stick_y = v;
+ }
+ }
+ }
+@@ -129,11 +129,11 @@
+ // printf("Mapped to %d\n",mo);
+ if (JE.value)
+ {
+- kcode[port] &= ~mo;
++ maple_controller[port].buttons &= ~mo;
+ }
+ else
+ {
+- kcode[port] |= mo;
++ maple_controller[port].buttons |= mo;
+ }
+ }
+ else if (mt == 1)
+@@ -141,11 +141,11 @@
+ // printf("Mapped to %d %d\n",mo,JE.value?255:0);
+ if (mo==0)
+ {
+- lt[port] = JE.value ? 255 : 0;
++ maple_controller[port].trigger_left = JE.value ? 255 : 0;
+ }
+ else if (mo==1)
+ {
+- rt[port] = JE.value ? 255 : 0;
++ maple_controller[port].trigger_right = JE.value ? 255 : 0;
+ }
+ }
+ }
+diff -Nur a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp
+--- a/core/linux-dist/main.cpp 2015-10-06 22:07:39.028703879 -0300
++++ b/core/linux-dist/main.cpp 2015-10-06 22:08:18.381992699 -0300
+@@ -69,22 +69,11 @@
+ return x11_disp;
+ }
+
+-u16 kcode[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF};
+-u8 rt[4] = {0, 0, 0, 0};
+-u8 lt[4] = {0, 0, 0, 0};
+-u32 vks[4];
+-s8 joyx[4], joyy[4];
+-
+ void emit_WriteCodeCache();
+
+ #if defined(USE_EVDEV)
+ /* evdev input */
+- static EvdevController evdev_controllers[4] = {
+- { -1, NULL },
+- { -1, NULL },
+- { -1, NULL },
+- { -1, NULL }
+- };
++ static EvdevController evdev_controllers[MAPLE_NUM_PORTS];
+ #endif
+
+ #if defined(USE_JOYSTICK)
+@@ -95,14 +84,17 @@
+ void SetupInput()
+ {
+ #if defined(USE_EVDEV)
+- int evdev_device_id[4] = { -1, -1, -1, -1 };
++ int evdev_device_id[MAPLE_NUM_PORTS];
+ size_t size_needed;
+ int port, i;
+
+ char* evdev_device;
+
+- for (port = 0; port < 4; port++)
++ for (port = 0; port < MAPLE_NUM_PORTS; port++)
+ {
++ evdev_controllers[port] = { -1, NULL };
++ evdev_device_id[port] = -1;
++
+ size_needed = snprintf(NULL, 0, EVDEV_DEVICE_CONFIG_KEY, port+1) + 1;
+ char* evdev_config_key = (char*)malloc(size_needed);
+ sprintf(evdev_config_key, EVDEV_DEVICE_CONFIG_KEY, port+1);
+@@ -135,6 +127,7 @@
+ free(evdev_config_key);
+
+ input_evdev_init(&evdev_controllers[port], evdev_device, mapping);
++ maple_controller[port].enabled = true;
+
+ free(evdev_device);
+ }
+diff -Nur a/core/linux-dist/main.h b/core/linux-dist/main.h
+--- a/core/linux-dist/main.h 2015-10-06 21:43:53.042336401 -0300
++++ b/core/linux-dist/main.h 2015-10-06 22:08:18.381992699 -0300
+@@ -1,35 +1,6 @@
+ #pragma once
+ #include "types.h"
+-
+-extern u16 kcode[4];
+-extern u32 vks[4];
+-extern u8 rt[4], lt[4];
+-extern s8 joyx[4], joyy[4];
++#include "hw/maple/maple_controller.h"
+
+ extern void* x11_win;
+ extern void* x11_disp;
+-
+-enum DreamcastController
+-{
+- DC_BTN_C = 1,
+- DC_BTN_B = 1<<1,
+- DC_BTN_A = 1<<2,
+- DC_BTN_START = 1<<3,
+- DC_DPAD_UP = 1<<4,
+- DC_DPAD_DOWN = 1<<5,
+- DC_DPAD_LEFT = 1<<6,
+- DC_DPAD_RIGHT = 1<<7,
+- DC_BTN_Z = 1<<8,
+- DC_BTN_Y = 1<<9,
+- DC_BTN_X = 1<<10,
+- DC_BTN_D = 1<<11,
+- DC_DPAD2_UP = 1<<12,
+- DC_DPAD2_DOWN = 1<<13,
+- DC_DPAD2_LEFT = 1<<14,
+- DC_DPAD2_RIGHT = 1<<15,
+-
+- DC_AXIS_LT = 0X10000,
+- DC_AXIS_RT = 0X10001,
+- DC_AXIS_X = 0X20000,
+- DC_AXIS_Y = 0X20001,
+-};
+diff -Nur a/core/linux-dist/x11.cpp b/core/linux-dist/x11.cpp
+--- a/core/linux-dist/x11.cpp 2015-10-06 22:04:14.520204440 -0300
++++ b/core/linux-dist/x11.cpp 2015-10-06 22:08:18.381992699 -0300
+@@ -82,11 +82,11 @@
+ int dc_key = x11_keymap[e.xkey.keycode];
+ if (e.type == KeyPress)
+ {
+- kcode[0] &= ~dc_key;
++ maple_controller[0].buttons &= ~dc_key;
+ }
+ else
+ {
+- kcode[0] |= dc_key;
++ maple_controller[0].buttons |= dc_key;
+ }
+ }
+ //printf("KEY: %d -> %d: %d\n",e.xkey.keycode, dc_key, x11_dc_buttons );
+@@ -98,11 +98,11 @@
+
+ void input_x11_init()
+ {
+- x11_keymap[113] = DC_DPAD_LEFT;
+- x11_keymap[114] = DC_DPAD_RIGHT;
++ x11_keymap[113] = DC_BTN_DPAD_LEFT;
++ x11_keymap[114] = DC_BTN_DPAD_RIGHT;
+
+- x11_keymap[111] = DC_DPAD_UP;
+- x11_keymap[116] = DC_DPAD_DOWN;
++ x11_keymap[111] = DC_BTN_DPAD_UP;
++ x11_keymap[116] = DC_BTN_DPAD_DOWN;
+
+ x11_keymap[53] = DC_BTN_X;
+ x11_keymap[54] = DC_BTN_B;
+diff -Nur a/core/nacl/nacl.cpp b/core/nacl/nacl.cpp
+--- a/core/nacl/nacl.cpp 2015-10-06 22:07:39.028703879 -0300
++++ b/core/nacl/nacl.cpp 2015-10-06 22:08:18.381992699 -0300
+@@ -15,6 +15,7 @@
+ #include "ppapi/utility/completion_callback_factory.h"
+
+ #include "types.h"
++#include "hw/maple/maple_controller.h"
+
+ #include <GLES2/gl2.h>
+
+@@ -234,12 +235,6 @@
+ }
+ } // namespace pp
+
+-
+-u16 kcode[4];
+-u32 vks[4];
+-s8 joyx[4],joyy[4];
+-u8 rt[4],lt[4];
+-
+ int get_mic_data(u8* buffer) { return 0; }
+ int push_vmu_screen(u8* buffer) { return 0; }
+
+diff -Nur a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp
+--- a/core/rend/gles/gles.cpp 2015-10-06 22:07:39.029703886 -0300
++++ b/core/rend/gles/gles.cpp 2015-10-06 22:08:18.382992707 -0300
+@@ -2,6 +2,7 @@
+ #include "gles.h"
+ #include "rend/TexCache.h"
+ #include "cfg/cfg.h"
++#include "hw/maple/maple_controller.h"
+
+ #ifdef TARGET_PANDORA
+ #include <unistd.h>
+@@ -1043,28 +1044,6 @@
+ //printf("%f\n",B*log(maxdev)/log(2.0)+A);
+ }
+
+-
+-
+-extern u16 kcode[4];
+-extern u8 rt[4],lt[4];
+-
+-#define key_CONT_C (1 << 0)
+-#define key_CONT_B (1 << 1)
+-#define key_CONT_A (1 << 2)
+-#define key_CONT_START (1 << 3)
+-#define key_CONT_DPAD_UP (1 << 4)
+-#define key_CONT_DPAD_DOWN (1 << 5)
+-#define key_CONT_DPAD_LEFT (1 << 6)
+-#define key_CONT_DPAD_RIGHT (1 << 7)
+-#define key_CONT_Z (1 << 8)
+-#define key_CONT_Y (1 << 9)
+-#define key_CONT_X (1 << 10)
+-#define key_CONT_D (1 << 11)
+-#define key_CONT_DPAD2_UP (1 << 12)
+-#define key_CONT_DPAD2_DOWN (1 << 13)
+-#define key_CONT_DPAD2_LEFT (1 << 14)
+-#define key_CONT_DPAD2_RIGHT (1 << 15)
+-
+ u32 osd_base;
+ u32 osd_count;
+
+@@ -1260,21 +1239,21 @@
+ osd_count=0;
+
+ #ifndef TARGET_PANDORA
+- DrawButton2(vjoy_pos[0],kcode[0]&key_CONT_DPAD_LEFT);
+- DrawButton2(vjoy_pos[1],kcode[0]&key_CONT_DPAD_UP);
+- DrawButton2(vjoy_pos[2],kcode[0]&key_CONT_DPAD_RIGHT);
+- DrawButton2(vjoy_pos[3],kcode[0]&key_CONT_DPAD_DOWN);
++ DrawButton2(vjoy_pos[0], maple_controller[0].buttons & DC_BTN_DPAD_LEFT);
++ DrawButton2(vjoy_pos[1], maple_controller[0].buttons & DC_BTN_DPAD_UP);
++ DrawButton2(vjoy_pos[2], maple_controller[0].buttons & DC_BTN_DPAD_RIGHT);
++ DrawButton2(vjoy_pos[3], maple_controller[0].buttons & DC_BTN_DPAD_DOWN);
+
+- DrawButton2(vjoy_pos[4],kcode[0]&key_CONT_X);
+- DrawButton2(vjoy_pos[5],kcode[0]&key_CONT_Y);
+- DrawButton2(vjoy_pos[6],kcode[0]&key_CONT_B);
+- DrawButton2(vjoy_pos[7],kcode[0]&key_CONT_A);
++ DrawButton2(vjoy_pos[4], maple_controller[0].buttons & DC_BTN_X);
++ DrawButton2(vjoy_pos[5], maple_controller[0].buttons & DC_BTN_Y);
++ DrawButton2(vjoy_pos[6], maple_controller[0].buttons & DC_BTN_B);
++ DrawButton2(vjoy_pos[7], maple_controller[0].buttons & DC_BTN_A);
+
+- DrawButton2(vjoy_pos[8],kcode[0]&key_CONT_START);
++ DrawButton2(vjoy_pos[8], maple_controller[0].buttons & DC_BTN_START);
+
+- DrawButton(vjoy_pos[9],lt[0]);
++ DrawButton(vjoy_pos[9], maple_controller[0].trigger_left);
+
+- DrawButton(vjoy_pos[10],rt[0]);
++ DrawButton(vjoy_pos[10], maple_controller[0].trigger_right);
+
+ DrawButton2(vjoy_pos[11],1);
+ DrawButton2(vjoy_pos[12],0);
+@@ -1818,8 +1797,6 @@
+ #endif
+ #endif
+
+-extern u16 kcode[4];
+-
+ /*
+ bool rend_single_frame()
+ {
+@@ -1833,7 +1810,7 @@
+ }
+
+ bool do_swp=false;
+- //if (kcode[0]&(1<<9))
++ //if (maple_controller[0].buttons&(1<<9))
+ {
+
+
+diff -Nur a/core/sdl/main.cpp b/core/sdl/main.cpp
+--- a/core/sdl/main.cpp 2015-10-06 22:07:39.029703886 -0300
++++ b/core/sdl/main.cpp 2015-10-06 22:08:18.382992707 -0300
+@@ -21,6 +21,7 @@
+ #include <execinfo.h>
+
+ #include "hw/mem/_vmem.h"
++#include "hw/maple/maple_controller.h"
+
+ #ifdef TARGET_PANDORA
+ #define WINDOW_WIDTH 800
+@@ -56,42 +57,10 @@
+ return MBX_OK;
+ }
+
+-
+-
+-u16 kcode[4];
+-u32 vks[4];
+-s8 joyx[4],joyy[4];
+-u8 rt[4],lt[4];
+-
+ extern bool KillTex;
+
+ extern void dc_term();
+
+-enum DCPad {
+- Btn_C = 1,
+- Btn_B = 1<<1,
+- Btn_A = 1<<2,
+- Btn_Start = 1<<3,
+- DPad_Up = 1<<4,
+- DPad_Down = 1<<5,
+- DPad_Left = 1<<6,
+- DPad_Right = 1<<7,
+- Btn_Z = 1<<8,
+- Btn_Y = 1<<9,
+- Btn_X = 1<<10,
+- Btn_D = 1<<11,
+- DPad2_Up = 1<<12,
+- DPad2_Down = 1<<13,
+- DPad2_Left = 1<<14,
+- DPad2_Right = 1<<15,
+-
+- Axis_LT= 0x10000,
+- Axis_RT= 0x10001,
+- Axis_X= 0x20000,
+- Axis_Y= 0x20001,
+-};
+-
+-
+ void emit_WriteCodeCache();
+
+ static SDL_Joystick *JoySDL = 0;
+@@ -100,33 +69,26 @@
+
+ #define MAP_SIZE 32
+
+-const u32 JMapBtn_USB[MAP_SIZE] =
+- { Btn_Y,Btn_B,Btn_A,Btn_X,0,0,0,0,0,Btn_Start };
++const DreamcastControllerCodes JMapBtn_USB[MAP_SIZE] =
++ { DC_BTN_Y, DC_BTN_B, DC_BTN_A, DC_BTN_X, 0, 0, 0, 0, 0, DC_BTN_START };
+
+-const u32 JMapAxis_USB[MAP_SIZE] =
+- { Axis_X,Axis_Y,0,0,0,0,0,0,0,0 };
++const DreamcastControllerCodes JMapAxis_USB[MAP_SIZE] =
++ { DC_AXIS_X, DC_AXIS_Y, 0 ,0, 0, 0, 0, 0, 0, 0 };
+
+-const u32 JMapBtn_360[MAP_SIZE] =
+- { Btn_A,Btn_B,Btn_X,Btn_Y,0,0,0,Btn_Start,0,0 };
++const DreamcastControllerCodes JMapBtn_360[MAP_SIZE] =
++ { DC_BTN_A, DC_BTN_B, DC_BTN_X, DC_BTN_Y, 0, 0, 0, DC_BTN_START, 0, 0 };
+
+-const u32 JMapAxis_360[MAP_SIZE] =
+- { Axis_X,Axis_Y,Axis_LT,0,0,Axis_RT,DPad_Left,DPad_Up,0,0 };
++const DreamcastControllerCodes JMapAxis_360[MAP_SIZE] =
++ { DC_AXIS_X, DC_AXIS_Y, DC_AXIS_LT, 0, 0, DC_AXIS_RT, DC_BTN_DPAD_LEFT, DC_BTN_DPAD_UP, 0, 0 };
+
+-const u32* JMapBtn=JMapBtn_USB;
+-const u32* JMapAxis=JMapAxis_USB;
++const DreamcastControllerCodes* JMapBtn = JMapBtn_USB;
++const DreamcastControllerCodes* JMapAxis = JMapAxis_USB;
+ #ifdef TARGET_PANDORA
+ u32 JSensitivity[256]; // To have less sensitive value on nubs
+ #endif
+
+ void SetupInput()
+ {
+- for (int port=0;port<4;port++)
+- {
+- kcode[port]=0xFFFF;
+- rt[port]=0;
+- lt[port]=0;
+- }
+-
+ // Open joystick device
+ int numjoys = SDL_NumJoysticks();
+ printf("Number of Joysticks found = %i\n", numjoys);
+@@ -236,17 +198,17 @@
+ {
+ // printf("Mapped to %d\n",mo);
+ if (value)
+- kcode[port]&=~mo;
++ maple_controller[port].buttons &= ~mo;
+ else
+- kcode[port]|=mo;
++ maple_controller[port].buttons |= mo;
+ }
+ else if (mt==1)
+ {
+ // printf("Mapped to %d %d\n",mo,JE.value?255:0);
+ if (mo==0)
+- lt[port]=value?255:0;
++ maple_controller[port].trigger_left = value ? 255 : 0;
+ else if (mo==1)
+- rt[port]=value?255:0;
++ maple_controller[port].trigger_right = value ? 255 : 0;
+ }
+
+ }
+@@ -266,18 +228,18 @@
+
+ if (mt==0)
+ {
+- kcode[port]|=mo;
+- kcode[port]|=mo*2;
++ maple_controller[port].buttons |= mo;
++ maple_controller[port].buttons |= mo*2;
+ if (v<-64)
+ {
+- kcode[port]&=~mo;
++ maple_controller[port].buttons &= ~mo;
+ }
+ else if (v>64)
+ {
+- kcode[port]&=~(mo*2);
++ maple_controller[port].buttons &= ~(mo*2);
+ }
+
+- // printf("Mapped to %d %d %d\n",mo,kcode[port]&mo,kcode[port]&(mo*2));
++ // printf("Mapped to %d %d %d\n",mo,maple_controller[port].buttons&mo,maple_controller[port].buttons&(mo*2));
+ }
+ else if (mt==1)
+ {
+@@ -286,17 +248,17 @@
+ // printf("AXIS %d,%d Mapped to %d %d %d\n",JE.number,JE.value,mo,v,v+127);
+
+ if (mo==0)
+- lt[port]=v+127;
++ maple_controller[port].trigger_left = v+127;
+ else if (mo==1)
+- rt[port]=v+127;
++ maple_controller[port].trigger_right = v+127;
+ }
+ else if (mt==2)
+ {
+ // printf("AXIS %d,%d Mapped to %d %d [%d]",JE.number,JE.value,mo,v);
+ if (mo==0)
+- joyx[port]=v;
++ maple_controller[port].stick_x = v;
+ else if (mo==1)
+- joyy[port]=v;
++ maple_controller[port].stick_y = v;
+ }
+ }
+ break;
+@@ -314,18 +276,18 @@
+ case 0: // nothing
+ break;
+ case 1: // Up=RT, Down=LT
+- if (yy<0) rt[port]=-yy;
+- if (yy>0) lt[port]=yy;
++ if (yy<0) maple_controller[port].trigger_right = -yy;
++ if (yy>0) maple_controller[port].trigger_left = yy;
+ break;
+ case 2: // Left=LT, Right=RT
+- if (xx<0) lt[port]=-xx;
+- if (xx>0) rt[port]=xx;
++ if (xx<0) maple_controller[port].trigger_left = -xx;
++ if (xx>0) maple_controller[port].trigger_right = xx;
+ break;
+ case 3: // Nub = ABXY
+- if (xx<-127) kcode[port] &= ~Btn_X;
+- if (xx>127) kcode[port] &= ~Btn_B;
+- if (yy<-127) kcode[port] &= ~Btn_Y;
+- if (yy>127) kcode[port] &= ~Btn_A;
++ if (xx<-127) maple_controller[port].buttons &= ~DC_BTN_X;
++ if (xx>127) maple_controller[port].buttons &= ~DC_BTN_B;
++ if (yy<-127) maple_controller[port].buttons &= ~DC_BTN_Y;
++ if (yy>127) maple_controller[port].buttons &= ~DC_BTN_A;
+ break;
+ }
+ break;
+@@ -333,16 +295,16 @@
+
+ }
+
+- if (keys[0]) { kcode[port] &= ~Btn_C; }
+- if (keys[6]) { kcode[port] &= ~Btn_A; }
+- if (keys[7]) { kcode[port] &= ~Btn_B; }
+- if (keys[5]) { kcode[port] &= ~Btn_Y; }
+- if (keys[8]) { kcode[port] &= ~Btn_X; }
+- if (keys[1]) { kcode[port] &= ~DPad_Up; }
+- if (keys[2]) { kcode[port] &= ~DPad_Down; }
+- if (keys[3]) { kcode[port] &= ~DPad_Left; }
+- if (keys[4]) { kcode[port] &= ~DPad_Right; }
+- if (keys[12]){ kcode[port] &= ~Btn_Start; }
++ if (keys[0]) { maple_controller[port].buttons &= ~DC_BTN_C; }
++ if (keys[6]) { maple_controller[port].buttons &= ~DC_BTN_A; }
++ if (keys[7]) { maple_controller[port].buttons &= ~DC_BTN_B; }
++ if (keys[5]) { maple_controller[port].buttons &= ~DC_BTN_Y; }
++ if (keys[8]) { maple_controller[port].buttons &= ~DC_BTN_X; }
++ if (keys[1]) { maple_controller[port].buttons &= ~DC_BTN_DPAD_UP; }
++ if (keys[2]) { maple_controller[port].buttons &= ~DC_BTN_DPAD_DOWN; }
++ if (keys[3]) { maple_controller[port].buttons &= ~DC_BTN_DPAD_LEFT; }
++ if (keys[4]) { maple_controller[port].buttons &= ~DC_BTN_DPAD_RIGHT; }
++ if (keys[12]){ maple_controller[port].buttons &= ~DC_BTN_START; }
+ if (keys[9]){
+ //die("death by escape key");
+ //printf("death by escape key\n");
+@@ -352,8 +314,8 @@
+ // is there a proper way to exit? dc_term() doesn't end the dc_run() loop it seems
+ die("death by escape key");
+ }
+- if (keys[10]) rt[port]=255;
+- if (keys[11]) lt[port]=255;
++ if (keys[10]) maple_controller[port].trigger_right=255;
++ if (keys[11]) maple_controller[port].trigger_left=255;
+
+ return true;
+ }
+@@ -362,9 +324,9 @@
+ {
+ static char key = 0;
+
+- kcode[port]=0xFFFF;
+- rt[port]=0;
+- lt[port]=0;
++ maple_controller[port].buttons=0xFFFF;
++ maple_controller[port].trigger_right=0;
++ maple_controller[port].trigger_left=0;
+
+ HandleEvents(port);
+ }
+diff -Nur a/core/windows/winmain.cpp b/core/windows/winmain.cpp
+--- a/core/windows/winmain.cpp 2015-10-06 22:07:39.030703893 -0300
++++ b/core/windows/winmain.cpp 2015-10-06 22:08:18.383992714 -0300
+@@ -1,5 +1,6 @@
+ #include "oslib\oslib.h"
+ #include "oslib\audiostream.h"
++#include "hw\maple\maple_controller.h"
+ #include "imgread\common.h"
+
+ #define _WIN32_WINNT 0x0500
+@@ -173,66 +174,46 @@
+ return MessageBox(NULL,temp,VER_SHORTNAME,type | MB_TASKMODAL);
+ }
+
+-u16 kcode[4];
+-u32 vks[4];
+-s8 joyx[4],joyy[4];
+-u8 rt[4],lt[4];
+-#define key_CONT_C (1 << 0)
+-#define key_CONT_B (1 << 1)
+-#define key_CONT_A (1 << 2)
+-#define key_CONT_START (1 << 3)
+-#define key_CONT_DPAD_UP (1 << 4)
+-#define key_CONT_DPAD_DOWN (1 << 5)
+-#define key_CONT_DPAD_LEFT (1 << 6)
+-#define key_CONT_DPAD_RIGHT (1 << 7)
+-#define key_CONT_Z (1 << 8)
+-#define key_CONT_Y (1 << 9)
+-#define key_CONT_X (1 << 10)
+-#define key_CONT_D (1 << 11)
+-#define key_CONT_DPAD2_UP (1 << 12)
+-#define key_CONT_DPAD2_DOWN (1 << 13)
+-#define key_CONT_DPAD2_LEFT (1 << 14)
+-#define key_CONT_DPAD2_RIGHT (1 << 15)
+ void UpdateInputState(u32 port)
+ {
+- //joyx[port]=pad.Lx;
+- //joyy[port]=pad.Ly;
+- lt[port]=GetAsyncKeyState('A')?255:0;
+- rt[port]=GetAsyncKeyState('S')?255:0;
++ //maple_controller[port].stick_x = pad.Lx;
++ //maple_controller[port].stick_y = pad.Ly;
++ maple_controller[port].trigger_left = GetAsyncKeyState('A') ? 255 : 0;
++ maple_controller[port].trigger_right = GetAsyncKeyState('S') ? 255 : 0;
+
+- joyx[port]=joyy[port]=0;
++ maple_controller[port].stick_x = maple_controller[port].stick_y = 0;
+
+ if (GetAsyncKeyState('J'))
+- joyx[port]-=126;
++ maple_controller[port].stick_x -= 126;
+ if (GetAsyncKeyState('L'))
+- joyx[port]+=126;
++ maple_controller[port].stick_x += 126;
+
+ if (GetAsyncKeyState('I'))
+- joyy[port]-=126;
++ maple_controller[port].stick_y -= 126;
+ if (GetAsyncKeyState('K'))
+- joyy[port]+=126;
++ maple_controller[port].stick_y += 126;
+
+- kcode[port]=0xFFFF;
++ maple_controller[port].buttons = 0xFFFF;
+ if (GetAsyncKeyState('V'))
+- kcode[port]&=~key_CONT_A;
++ maple_controller[port].buttons &= ~DC_BTN_A;
+ if (GetAsyncKeyState('C'))
+- kcode[port]&=~key_CONT_B;
++ maple_controller[port].buttons &= ~DC_BTN_B;
+ if (GetAsyncKeyState('X'))
+- kcode[port]&=~key_CONT_Y;
++ maple_controller[port].buttons &= ~DC_BTN_Y;
+ if (GetAsyncKeyState('Z'))
+- kcode[port]&=~key_CONT_X;
++ maple_controller[port].buttons &= ~DC_BTN_X;
+
+ if (GetAsyncKeyState(VK_SHIFT))
+- kcode[port]&=~key_CONT_START;
++ maple_controller[port].buttons &= ~DC_BTN_START;
+
+ if (GetAsyncKeyState(VK_UP))
+- kcode[port]&=~key_CONT_DPAD_UP;
++ maple_controller[port].buttons &= ~DC_BTN_DPAD_UP;
+ if (GetAsyncKeyState(VK_DOWN))
+- kcode[port]&=~key_CONT_DPAD_DOWN;
++ maple_controller[port].buttons &= ~DC_BTN_DPAD_DOWN;
+ if (GetAsyncKeyState(VK_LEFT))
+- kcode[port]&=~key_CONT_DPAD_LEFT;
++ maple_controller[port].buttons &= ~DC_BTN_DPAD_LEFT;
+ if (GetAsyncKeyState(VK_RIGHT))
+- kcode[port]&=~key_CONT_DPAD_RIGHT;
++ maple_controller[port].buttons &= ~DC_BTN_DPAD_RIGHT;
+
+ if (GetAsyncKeyState(VK_F1))
+ settings.pvr.ta_skip = 100;
+diff -Nur a/shell/android/jni/src/Android.cpp b/shell/android/jni/src/Android.cpp
+--- a/shell/android/jni/src/Android.cpp 2015-10-06 22:07:39.030703893 -0300
++++ b/shell/android/jni/src/Android.cpp 2015-10-06 22:08:18.383992714 -0300
+@@ -16,6 +16,7 @@
+ #include "rend/TexCache.h"
+ #include "hw/maple/maple_devs.h"
+ #include "hw/maple/maple_if.h"
++#include "hw/maple/maple_controller.h"
+ #include "oslib/audiobackend_android.h"
+
+ #include "util.h"
+@@ -175,10 +176,6 @@
+ // Additonal controllers 2, 3 and 4 connected ?
+ static bool add_controllers[3] = { false, false, false };
+
+-u16 kcode[4];
+-u32 vks[4];
+-s8 joyx[4],joyy[4];
+-u8 rt[4],lt[4];
+ float vjoy_pos[14][8];
+
+ extern bool print_stats;
+@@ -211,7 +208,7 @@
+ }
+
+ // Add additonal controllers
+- for (int i = 0; i < 3; i++)
++ for (int i = 0; i < (MAPLE_NUM_PORTS - 1); i++)
+ {
+ if (add_controllers[i])
+ mcfg_Create(MDT_SegaController,i+1,5);
+@@ -443,13 +440,13 @@
+ jint *jx_body = env->GetIntArrayElements(jx, 0);
+ jint *jy_body = env->GetIntArrayElements(jy, 0);
+
+- for(int i = 0; i < 4; i++)
++ for(int i = 0; i < MAPLE_NUM_PORTS; i++)
+ {
+- kcode[i] = k_code_body[i];
+- lt[i] = l_t_body[i];
+- rt[i] = r_t_body[i];
+- joyx[i] = jx_body[i];
+- joyy[i] = jy_body[i];
++ maple_controller[i].buttons = k_code_body[i];
++ maple_controller[i].trigger_left = l_t_body[i];
++ maple_controller[i].trigger_right = r_t_body[i];
++ maple_controller[i].stick_x = jx_body[i];
++ maple_controller[i].stick_y = jy_body[i];
+ }
+
+ env->ReleaseIntArrayElements(k_code, k_code_body, 0);
+diff -Nur a/shell/apple/emulator-ios/emulator/EmulatorView.mm b/shell/apple/emulator-ios/emulator/EmulatorView.mm
+--- a/shell/apple/emulator-ios/emulator/EmulatorView.mm 2015-10-06 21:43:53.121336967 -0300
++++ b/shell/apple/emulator-ios/emulator/EmulatorView.mm 2015-10-06 22:08:18.383992714 -0300
+@@ -9,15 +9,7 @@
+ #import "EmulatorView.h"
+
+ #include "types.h"
+-
+-extern u16 kcode[4];
+-extern u32 vks[4];
+-extern s8 joyx[4],joyy[4];
+-extern u8 rt[4],lt[4];
+-
+-#define key_CONT_A (1 << 2)
+-#define key_CONT_START (1 << 3)
+-#define key_CONT_DPAD_LEFT (1 << 6)
++#include "hw/maple/maple_controller.h"
+
+ int dpad_or_btn = 0;
+
+@@ -34,9 +26,9 @@
+ -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
+
+ if (dpad_or_btn &1)
+- kcode[0] &= ~(key_CONT_START|key_CONT_A);
++ maple_controller[0].buttons &= ~(DC_BTN_START|DC_BTN_A);
+ else
+- kcode[0] &= ~(key_CONT_DPAD_LEFT);
++ maple_controller[0].buttons &= ~(DC_BTN_DPAD_LEFT);
+ }
+
+ -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+@@ -44,9 +36,9 @@
+ // [event allTouches];
+
+ if (dpad_or_btn &1)
+- kcode[0] |= (key_CONT_START|key_CONT_A);
++ maple_controller[0].buttons |= (DC_BTN_START|DC_BTN_A);
+ else
+- kcode[0] |= (key_CONT_DPAD_LEFT);
++ maple_controller[0].buttons |= (DC_BTN_DPAD_LEFT);
+
+ dpad_or_btn++;
+ }
+diff -Nur a/shell/apple/emulator-ios/emulator/ios_main.mm b/shell/apple/emulator-ios/emulator/ios_main.mm
+--- a/shell/apple/emulator-ios/emulator/ios_main.mm 2015-10-06 22:07:39.030703893 -0300
++++ b/shell/apple/emulator-ios/emulator/ios_main.mm 2015-10-06 22:08:18.383992714 -0300
+@@ -20,6 +20,7 @@
+ #include <sys/mman.h>
+ #include <sys/time.h>
+ #include "hw/sh4/dyna/blockmanager.h"
++#include "hw/maple/maple_controller.h"
+ #include <unistd.h>
+
+
+@@ -55,11 +56,6 @@
+ int dc_init(int argc,wchar* argv[]);
+ void dc_run();
+
+-u16 kcode[4];
+-u32 vks[4];
+-s8 joyx[4],joyy[4];
+-u8 rt[4],lt[4];
+-
+ extern "C" int reicast_main(int argc, wchar* argv[])
+ {
+ //if (argc==2)
+diff -Nur a/shell/apple/emulator-osx/emulator-osx/osx-main.mm b/shell/apple/emulator-osx/emulator-osx/osx-main.mm
+--- a/shell/apple/emulator-osx/emulator-osx/osx-main.mm 2015-10-06 22:07:39.030703893 -0300
++++ b/shell/apple/emulator-osx/emulator-osx/osx-main.mm 2015-10-06 22:08:18.383992714 -0300
+@@ -8,6 +8,7 @@
+ #import <Carbon/Carbon.h>
+
+ #include "types.h"
++#include "hw/maple/maple_controller.h"
+ #include <sys/stat.h>
+
+ #include <OpenGL/gl3.h>
+@@ -38,11 +39,6 @@
+ return 0;
+ }
+
+-u16 kcode[4] = { 0xFFFF };
+-u32 vks[4];
+-s8 joyx[4],joyy[4];
+-u8 rt[4],lt[4];
+-
+ int get_mic_data(u8* buffer) { return 0; }
+ int push_vmu_screen(u8* buffer) { return 0; }
+
+@@ -131,35 +127,11 @@
+ gles_init();
+ }
+
+-enum DCPad {
+- Btn_C = 1,
+- Btn_B = 1<<1,
+- Btn_A = 1<<2,
+- Btn_Start = 1<<3,
+- DPad_Up = 1<<4,
+- DPad_Down = 1<<5,
+- DPad_Left = 1<<6,
+- DPad_Right = 1<<7,
+- Btn_Z = 1<<8,
+- Btn_Y = 1<<9,
+- Btn_X = 1<<10,
+- Btn_D = 1<<11,
+- DPad2_Up = 1<<12,
+- DPad2_Down = 1<<13,
+- DPad2_Left = 1<<14,
+- DPad2_Right = 1<<15,
+-
+- Axis_LT= 0x10000,
+- Axis_RT= 0x10001,
+- Axis_X= 0x20000,
+- Axis_Y= 0x20001,
+-};
+-
+ void handle_key(int dckey, int state) {
+ if (state)
+- kcode[0] &= ~dckey;
++ maple_controller[0].buttons &= ~dckey;
+ else
+- kcode[0] |= dckey;
++ maple_controller[0].buttons |= dckey;
+ }
+
+ void handle_trig(u8* dckey, int state) {
+@@ -172,18 +144,18 @@
+ extern "C" void emu_key_input(char* keyt, int state) {
+ int key = keyt[0];
+ switch(key) {
+- case 'z': handle_key(Btn_X, state); break;
+- case 'x': handle_key(Btn_Y, state); break;
+- case 'c': handle_key(Btn_B, state); break;
+- case 'v': handle_key(Btn_A, state); break;
++ case 'z': handle_key(DC_BTN_X, state); break;
++ case 'x': handle_key(DC_BTN_Y, state); break;
++ case 'c': handle_key(DC_BTN_B, state); break;
++ case 'v': handle_key(DC_BTN_A, state); break;
+
+ case 'a': handle_trig(lt, state); break;
+ case 's': handle_trig(rt, state); break;
+
+- case 'j': handle_key(DPad_Left, state); break;
+- case 'k': handle_key(DPad_Down, state); break;
+- case 'l': handle_key(DPad_Right, state); break;
+- case 'i': handle_key(DPad_Up, state); break;
+- case 0xa: handle_key(Btn_Start, state); break;
++ case 'j': handle_key(DC_BTN_DPAD_LEFT, state); break;
++ case 'k': handle_key(DC_BTN_DPAD_DOWN, state); break;
++ case 'l': handle_key(DC_BTN_DPAD_RIGHT, state); break;
++ case 'i': handle_key(DC_BTN_DPAD_UP, state); break;
++ case 0xa: handle_key(DC_BTN_START, state); break;
+ }
+ }
+\ No newline at end of file
+diff -Nur a/shell/reicast.vcxproj b/shell/reicast.vcxproj
+--- a/shell/reicast.vcxproj 2015-10-06 22:07:39.030703893 -0300
++++ b/shell/reicast.vcxproj 2015-10-06 22:08:18.383992714 -0300
+@@ -92,6 +92,7 @@
+ <ClCompile Include="..\core\hw\holly\sb_dma.cpp" />
+ <ClCompile Include="..\core\hw\holly\sb_mem.cpp" />
+ <ClCompile Include="..\core\hw\maple\maple_cfg.cpp" />
++ <ClCompile Include="..\core\hw\maple\maple_controller.cpp" />
+ <ClCompile Include="..\core\hw\maple\maple_devs.cpp" />
+ <ClCompile Include="..\core\hw\maple\maple_helper.cpp" />
+ <ClCompile Include="..\core\hw\maple\maple_if.cpp" />
diff --git a/pcr/reicast-multilib-git/refactor-rend-stuff.patch b/pcr/reicast-multilib-git/refactor-rend-stuff.patch
new file mode 100644
index 000000000..187ccb8d0
--- /dev/null
+++ b/pcr/reicast-multilib-git/refactor-rend-stuff.patch
@@ -0,0 +1,1244 @@
+diff -Nur a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp
+--- a/core/rend/gles/gles.cpp 2015-10-06 21:43:53.047336437 -0300
++++ b/core/rend/gles/gles.cpp 2015-10-06 21:51:21.723570329 -0300
+@@ -1,27 +1,3 @@
+-#include <math.h>
+-#include "gles.h"
+-#include "rend/TexCache.h"
+-#include "cfg/cfg.h"
+-
+-#ifdef TARGET_PANDORA
+-#include <unistd.h>
+-#include <fcntl.h>
+-#include <sys/ioctl.h>
+-#include <linux/fb.h>
+-
+-#ifndef FBIO_WAITFORVSYNC
+- #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
+-#endif
+-int fbdev = -1;
+-#endif
+-
+-#ifndef GLES
+-#if HOST_OS != OS_DARWIN
+-#include <GL3/gl3w.c>
+-#pragma comment(lib,"Opengl32.lib")
+-#endif
+-#endif
+-
+ /*
+ GL|ES 2
+ Slower, smaller subset of gl2
+@@ -54,305 +30,126 @@
+
+ */
+
++#include <math.h>
++#include "cfg/cfg.h"
+ #include "oslib/oslib.h"
+ #include "rend/rend.h"
++#include "rend/TexCache.h"
+ #include "hw/pvr/Renderer_if.h"
++#include "deps/libpng/png.h"
++#include "gles.h"
++#include "glshaders.h"
+
+-void GenSorted();
+-
+-float fb_scale_x,fb_scale_y;
+-
+-#ifndef GLES
+-#define attr "in"
+-#define vary "out"
+-#else
+-#define attr "attribute"
+-#define vary "varying"
++#ifdef TARGET_PANDORA
++ #include <unistd.h>
++ #include <fcntl.h>
++ #include <sys/ioctl.h>
++ #include <linux/fb.h>
+ #endif
+-#if 1
+
+-//Fragment and vertex shaders code
+-//pretty much 1:1 copy of the d3d ones for now
+-const char* VertexShaderSource =
+-#ifndef GLES
+- "#version 140 \n"
++#if !defined(GLES) && HOST_OS != OS_DARWIN
++ #include <GL3/gl3w.c>
++ #pragma comment(lib,"Opengl32.lib")
+ #endif
+-"\
+-/* Vertex constants*/ \n\
+-uniform highp vec4 scale; \n\
+-uniform highp vec4 depth_scale; \n\
+-uniform highp float sp_FOG_DENSITY; \n\
+-/* Vertex input */ \n\
+-" attr " highp vec4 in_pos; \n\
+-" attr " lowp vec4 in_base; \n\
+-" attr " lowp vec4 in_offs; \n\
+-" attr " mediump vec2 in_uv; \n\
+-/* output */ \n\
+-" vary " lowp vec4 vtx_base; \n\
+-" vary " lowp vec4 vtx_offs; \n\
+-" vary " mediump vec2 vtx_uv; \n\
+-" vary " highp vec3 vtx_xyz; \n\
+-void main() \n\
+-{ \n\
+- vtx_base=in_base; \n\
+- vtx_offs=in_offs; \n\
+- vtx_uv=in_uv; \n\
+- vec4 vpos=in_pos; \n\
+- vtx_xyz.xy = vpos.xy; \n\
+- vtx_xyz.z = vpos.z*sp_FOG_DENSITY; \n\
+- vpos.w=1.0/vpos.z; \n\
+- vpos.xy=vpos.xy*scale.xy-scale.zw; \n\
+- vpos.xy*=vpos.w; \n\
+- vpos.z=depth_scale.x+depth_scale.y*vpos.w; \n\
+- gl_Position = vpos; \n\
+-}";
+-
+
+-#else
+-
+-
+-
+-const char* VertexShaderSource =
+- ""
+- "/* Test Projection Matrix */"
+- ""
+- "uniform highp mat4 Projection;"
+- ""
+- ""
+- "/* Vertex constants */"
+- ""
+- "uniform highp float sp_FOG_DENSITY;"
+- "uniform highp vec4 scale;"
+- ""
+- "/* Vertex output */"
+- ""
+- "attribute highp vec4 in_pos;"
+- "attribute lowp vec4 in_base;"
+- "attribute lowp vec4 in_offs;"
+- "attribute mediump vec2 in_uv;"
+- ""
+- "/* Transformed input */"
+- ""
+- "varying lowp vec4 vtx_base;"
+- "varying lowp vec4 vtx_offs;"
+- "varying mediump vec2 vtx_uv;"
+- "varying highp vec3 vtx_xyz;"
+- ""
+- "void main()"
+- "{"
+- " vtx_base = in_base;"
+- " vtx_offs = in_offs;"
+- " vtx_uv = in_uv;"
+- ""
+- " vec4 vpos = in_pos;"
+- " vtx_xyz.xy = vpos.xy; "
+- " vtx_xyz.z = vpos.z*sp_FOG_DENSITY; "
+- ""
+- " vpos.w = 1.0/vpos.z; "
+- " vpos.z *= -scale.w; "
+- " vpos.xy = vpos.xy*scale.xy-sign(scale.xy); "
+- " vpos.xy *= vpos.w; "
+- ""
+- " gl_Position = vpos;"
+- // " gl_Position = vpos * Projection;"
+- "}"
+- ;
++#define OSD_TEX_W 512
++#define OSD_TEX_H 256
+
++#define key_CONT_C (1 << 0)
++#define key_CONT_B (1 << 1)
++#define key_CONT_A (1 << 2)
++#define key_CONT_START (1 << 3)
++#define key_CONT_DPAD_UP (1 << 4)
++#define key_CONT_DPAD_DOWN (1 << 5)
++#define key_CONT_DPAD_LEFT (1 << 6)
++#define key_CONT_DPAD_RIGHT (1 << 7)
++#define key_CONT_Z (1 << 8)
++#define key_CONT_Y (1 << 9)
++#define key_CONT_X (1 << 10)
++#define key_CONT_D (1 << 11)
++#define key_CONT_DPAD2_UP (1 << 12)
++#define key_CONT_DPAD2_DOWN (1 << 13)
++#define key_CONT_DPAD2_LEFT (1 << 14)
++#define key_CONT_DPAD2_RIGHT (1 << 15)
+
+-#endif
++gl_ctx gl;
+
++float fb_scale_x;
++float fb_scale_y;
++int screen_width;
++int screen_height;
+
++GLuint osd_tex;
+
++extern u16 kcode[4];
++extern u8 rt[4];
++extern u8 lt[4];
+
++u32 osd_base;
++u32 osd_count;
+
++#if defined(_ANDROID)
++ extern float vjoy_pos[14][8];
++#else
++ float vjoy_pos[14][8]=
++ {
++ {24+0,24+64,64,64}, //LEFT
++ {24+64,24+0,64,64}, //UP
++ {24+128,24+64,64,64}, //RIGHT
++ {24+64,24+128,64,64}, //DOWN
++
++ {440+0,280+64,64,64}, //X
++ {440+64,280+0,64,64}, //Y
++ {440+128,280+64,64,64}, //B
++ {440+64,280+128,64,64}, //A
++
++ {320-32,360+32,64,64}, //Start
++
++ {440,200,90,64}, //RT
++ {542,200,90,64}, //LT
++
++ {-24,128+224,128,128}, //ANALOG_RING
++ {96,320,64,64}, //ANALOG_POINT
++ {1}
++ };
++#endif // !_ANDROID
+
+-/*
++float vjoy_sz[2][14] = {
++ { 64,64,64,64, 64,64,64,64, 64, 90,90, 128, 64 },
++ { 64,64,64,64, 64,64,64,64, 64, 64,64, 128, 64 },
++};
+
+-cp_AlphaTest 0 1 2 2
+-pp_ClipTestMode -1 0 1 3 6
+-pp_UseAlpha 0 1 2 12
+-pp_Texture 1
+- pp_IgnoreTexA 0 1 2 2
+- pp_ShadInstr 0 1 2 3 4 8
+- pp_Offset 0 1 2 16
+- pp_FogCtrl 0 1 2 3 4 64
+-pp_Texture 0
+- pp_FogCtrl 0 2 3 4 4
+-
+-pp_Texture: off -> 12*4=48 shaders
+-pp_Texture: on -> 12*64=768 shaders
+-Total: 816 shaders
+-
+-highp float fdecp(highp float flt,out highp float e) \n\
+-{ \n\
+- highp float lg2=log2(flt); //ie , 2.5 \n\
+- highp float frc=fract(lg2); //ie , 0.5 \n\
+- e=lg2-frc; //ie , 2.5-0.5=2 (exp) \n\
+- return pow(2.0,frc); //2^0.5 (manitsa) \n\
+-} \n\
+-lowp float fog_mode2(highp float invW) \n\
+-{ \n\
+- highp float foginvW=invW; \n\
+- foginvW=clamp(foginvW,1.0,255.0); \n\
+- \n\
+- highp float fogexp; //0 ... 7 \n\
+- highp float fogman=fdecp(foginvW, fogexp); //[1,2) mantissa bits. that is 1.m \n\
+- \n\
+- highp float fogman_hi=fogman*16.0-16.0; //[16,32) -16 -> [0,16) \n\
+- highp float fogman_idx=floor(fogman_hi); //[0,15] \n\
+- highp float fogman_blend=fract(fogman_hi); //[0,1) -- can also be fogman_idx-fogman_idx ! \n\
+- highp float fog_idx_fr=fogexp*16.0+fogman_idx; //[0,127] \n\
+- \n\
+- highp float fog_idx_pixel_fr=fog_idx_fr+0.5; \n\
+- highp float fog_idx_pixel_n=fog_idx_pixel_fr/128.0;//normalise to [0.5/128,127.5/128) coordinates \n\
+- \n\
+- //fog is 128x1 texure \n\
+- lowp vec2 fog_coefs=texture2D(fog_table,vec2(fog_idx_pixel_n)).rg; \n\
+- \n\
+- lowp float fog_coef=mix(fog_coefs.r,fog_coefs.g,fogman_blend); \n\
+- \n\
+- return fog_coef; \n\
+-} \n\
+-*/
++#ifdef TARGET_PANDORA
++ #ifndef FBIO_WAITFORVSYNC
++ #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
++ #endif
+
+-#ifndef GLES
+-#define FRAGCOL "FragColor"
+-#define TEXLOOKUP "texture"
+-#define vary "in"
+-#else
+-#define FRAGCOL "gl_FragColor"
+-#define TEXLOOKUP "texture2D"
++ int fbdev = -1;
++ char OSD_Info[128];
++ int OSD_Delay = 0;
++ char OSD_Counters[256];
++ int OSD_Counter = 0;
++ GLuint osd_font;
+ #endif
+
+-
+-const char* PixelPipelineShader =
+-#ifndef GLES
+- "#version 140 \n"
+- "out vec4 FragColor; \n"
++#if !defined(_ANDROID) && !defined(TARGET_NACL32) && HOST_OS==OS_LINUX
++ #define SET_AFNT 1
+ #endif
+-"\
+-\
+-#define cp_AlphaTest %d \n\
+-#define pp_ClipTestMode %d.0 \n\
+-#define pp_UseAlpha %d \n\
+-#define pp_Texture %d \n\
+-#define pp_IgnoreTexA %d \n\
+-#define pp_ShadInstr %d \n\
+-#define pp_Offset %d \n\
+-#define pp_FogCtrl %d \n\
+-/* Shader program params*/ \n\
+-/* gles has no alpha test stage, so its emulated on the shader */ \n\
+-uniform lowp float cp_AlphaTestValue; \n\
+-uniform lowp vec4 pp_ClipTest; \n\
+-uniform lowp vec3 sp_FOG_COL_RAM,sp_FOG_COL_VERT; \n\
+-uniform highp vec2 sp_LOG_FOG_COEFS; \n\
+-uniform sampler2D tex,fog_table; \n\
+-/* Vertex input*/ \n\
+-" vary " lowp vec4 vtx_base; \n\
+-" vary " lowp vec4 vtx_offs; \n\
+-" vary " mediump vec2 vtx_uv; \n\
+-" vary " highp vec3 vtx_xyz; \n\
+-lowp float fog_mode2(highp float val) \n\
+-{ \n\
+- highp float fog_idx=clamp(val,0.0,127.99); \n\
+- return clamp(sp_LOG_FOG_COEFS.y*log2(fog_idx)+sp_LOG_FOG_COEFS.x,0.001,1.0); //the clamp is required due to yet another bug !\n\
+-} \n\
+-void main() \n\
+-{ \n\
+- lowp vec4 color=vtx_base; \n\
+- #if pp_UseAlpha==0 \n\
+- color.a=1.0; \n\
+- #endif\n\
+- #if pp_FogCtrl==3 \n\
+- color=vec4(sp_FOG_COL_RAM.rgb,fog_mode2(vtx_xyz.z)); \n\
+- #endif\n\
+- #if pp_Texture==1 \n\
+- { \n\
+- lowp vec4 texcol=" TEXLOOKUP "(tex,vtx_uv); \n\
+- \n\
+- #if pp_IgnoreTexA==1 \n\
+- texcol.a=1.0; \n\
+- #endif\n\
+- \n\
+- #if pp_ShadInstr==0 \n\
+- { \n\
+- color.rgb=texcol.rgb; \n\
+- color.a=texcol.a; \n\
+- } \n\
+- #endif\n\
+- #if pp_ShadInstr==1 \n\
+- { \n\
+- color.rgb*=texcol.rgb; \n\
+- color.a=texcol.a; \n\
+- } \n\
+- #endif\n\
+- #if pp_ShadInstr==2 \n\
+- { \n\
+- color.rgb=mix(color.rgb,texcol.rgb,texcol.a); \n\
+- } \n\
+- #endif\n\
+- #if pp_ShadInstr==3 \n\
+- { \n\
+- color*=texcol; \n\
+- } \n\
+- #endif\n\
+- \n\
+- #if pp_Offset==1 \n\
+- { \n\
+- color.rgb+=vtx_offs.rgb; \n\
+- if (pp_FogCtrl==1) \n\
+- color.rgb=mix(color.rgb,sp_FOG_COL_VERT.rgb,vtx_offs.a); \n\
+- } \n\
+- #endif\n\
+- } \n\
+- #endif\n\
+- #if pp_FogCtrl==0 \n\
+- { \n\
+- color.rgb=mix(color.rgb,sp_FOG_COL_RAM.rgb,fog_mode2(vtx_xyz.z)); \n\
+- } \n\
+- #endif\n\
+- #if cp_AlphaTest == 1 \n\
+- if (cp_AlphaTestValue>color.a) discard;\n\
+- #endif \n\
+- //color.rgb=vec3(vtx_xyz.z/255.0);\n\
+- " FRAGCOL "=color; \n\
+-}";
+
+-const char* ModifierVolumeShader =
+-#ifndef GLES
+- "#version 140 \n"
+- "out vec4 FragColor; \n"
+-#endif
+-" \
+-uniform lowp float sp_ShaderColor; \n\
+-/* Vertex input*/ \n\
+-void main() \n\
+-{ \n\
+- " FRAGCOL "=vec4(0.0, 0.0, 0.0, sp_ShaderColor); \n\
+-}";
++FILE* pngfile;
+
+-const char* OSD_Shader =
+-#ifndef GLES
+- "#version 140 \n"
+- "out vec4 FragColor; \n"
+-#endif
+-" \
+-" vary " lowp vec4 vtx_base; \n\
+-" vary " mediump vec2 vtx_uv; \n\
+-/* Vertex input*/ \n\
+-uniform sampler2D tex; \n\
+-void main() \n\
+-{ \n\
+- mediump vec2 uv=vtx_uv; \n\
+- uv.y=1.0-uv.y; \n\
+- " FRAGCOL "=vtx_base*" TEXLOOKUP "(tex,uv.st); \n\n\
+-}";
++void GenSorted();
+
++bool gl_init(void* wind, void* disp);
+
+-gl_ctx gl;
++//swap buffers
++void gl_swap();
++//destroy the gles context and free resources
++void gl_term();
+
+-int screen_width;
+-int screen_height;
++GLuint gl_CompileShader(const char* shader,GLuint type);
++
++bool gl_create_resources();
+
+ #if (HOST_OS != OS_DARWIN) && !defined(TARGET_NACL32)
+ #ifdef GLES
+@@ -843,11 +640,6 @@
+ return glIsProgram(s->program)==GL_TRUE;
+ }
+
+-GLuint osd_tex;
+-#ifdef TARGET_PANDORA
+-GLuint osd_font;
+-#endif
+-
+ bool gl_create_resources()
+ {
+
+@@ -938,51 +730,8 @@
+ return true;
+ }
+
+-bool gl_init(void* wind, void* disp);
+-
+-//swap buffers
+-void gl_swap();
+-//destroy the gles context and free resources
+-void gl_term();
+-
+-GLuint gl_CompileShader(const char* shader,GLuint type);
+-
+-bool gl_create_resources();
+-
+ //setup
+
+-
+-bool gles_init()
+-{
+-
+- if (!gl_init((void*)libPvr_GetRenderTarget(),
+- (void*)libPvr_GetRenderSurface()))
+- return false;
+-
+- if (!gl_create_resources())
+- return false;
+-
+-#if defined(GLES) && HOST_OS != OS_DARWIN && !defined(TARGET_NACL32)
+- #ifdef TARGET_PANDORA
+- fbdev=open("/dev/fb0", O_RDONLY);
+- #else
+- eglSwapInterval(gl.setup.display,1);
+- #endif
+-#endif
+-
+- //clean up all buffers ...
+- for (int i=0;i<10;i++)
+- {
+- glClearColor(0.f, 0.f, 0.f, 0.f);
+- glClear(GL_COLOR_BUFFER_BIT);
+- gl_swap();
+- }
+-
+- return true;
+-}
+-
+-
+-
+ float fog_coefs[]={0,0};
+ void tryfit(float* x,float* y)
+ {
+@@ -1043,64 +792,6 @@
+ //printf("%f\n",B*log(maxdev)/log(2.0)+A);
+ }
+
+-
+-
+-extern u16 kcode[4];
+-extern u8 rt[4],lt[4];
+-
+-#define key_CONT_C (1 << 0)
+-#define key_CONT_B (1 << 1)
+-#define key_CONT_A (1 << 2)
+-#define key_CONT_START (1 << 3)
+-#define key_CONT_DPAD_UP (1 << 4)
+-#define key_CONT_DPAD_DOWN (1 << 5)
+-#define key_CONT_DPAD_LEFT (1 << 6)
+-#define key_CONT_DPAD_RIGHT (1 << 7)
+-#define key_CONT_Z (1 << 8)
+-#define key_CONT_Y (1 << 9)
+-#define key_CONT_X (1 << 10)
+-#define key_CONT_D (1 << 11)
+-#define key_CONT_DPAD2_UP (1 << 12)
+-#define key_CONT_DPAD2_DOWN (1 << 13)
+-#define key_CONT_DPAD2_LEFT (1 << 14)
+-#define key_CONT_DPAD2_RIGHT (1 << 15)
+-
+-u32 osd_base;
+-u32 osd_count;
+-
+-
+-#if defined(_ANDROID)
+-extern float vjoy_pos[14][8];
+-#else
+-
+-float vjoy_pos[14][8]=
+-{
+- {24+0,24+64,64,64}, //LEFT
+- {24+64,24+0,64,64}, //UP
+- {24+128,24+64,64,64}, //RIGHT
+- {24+64,24+128,64,64}, //DOWN
+-
+- {440+0,280+64,64,64}, //X
+- {440+64,280+0,64,64}, //Y
+- {440+128,280+64,64,64}, //B
+- {440+64,280+128,64,64}, //A
+-
+- {320-32,360+32,64,64}, //Start
+-
+- {440,200,90,64}, //RT
+- {542,200,90,64}, //LT
+-
+- {-24,128+224,128,128}, //ANALOG_RING
+- {96,320,64,64}, //ANALOG_POINT
+- {1}
+-};
+-#endif // !_ANDROID
+-
+-float vjoy_sz[2][14] = {
+- { 64,64,64,64, 64,64,64,64, 64, 90,90, 128, 64 },
+- { 64,64,64,64, 64,64,64,64, 64, 64,64, 128, 64 },
+-};
+-
+ static void DrawButton(float* xy, u32 state)
+ {
+ Vertex vtx;
+@@ -1139,12 +830,6 @@
+ osd_count+=4;
+ }
+
+-static void ClearBG()
+-{
+-
+-}
+-
+-
+ void DrawButton2(float* xy, bool state) { DrawButton(xy,state?0:255); }
+ #ifdef TARGET_PANDORA
+ static void DrawCenteredText(float yy, float scale, int transparency, const char* text)
+@@ -1247,13 +932,6 @@
+ }
+ #endif
+
+-#ifdef TARGET_PANDORA
+-char OSD_Info[128];
+-int OSD_Delay=0;
+-char OSD_Counters[256];
+-int OSD_Counter=0;
+-#endif
+-
+ static void OSD_HOOK()
+ {
+ osd_base=pvrrc.verts.used();
+@@ -1290,128 +968,95 @@
+ #endif
+ }
+
+-extern GLuint osd_tex;
+-#ifdef TARGET_PANDORA
+-extern GLuint osd_font;
+-#endif
+-
+-#define OSD_TEX_W 512
+-#define OSD_TEX_H 256
+-
+-void OSD_DRAW()
++/*
++bool rend_single_frame()
+ {
+- #ifndef TARGET_PANDORA
+- if (osd_tex)
+- {
+- float u=0;
+- float v=0;
+-
+- for (int i=0;i<13;i++)
+- {
+- //umin,vmin,umax,vmax
+- vjoy_pos[i][4]=(u+1)/OSD_TEX_W;
+- vjoy_pos[i][5]=(v+1)/OSD_TEX_H;
+-
+- vjoy_pos[i][6]=((u+vjoy_sz[0][i]-1))/OSD_TEX_W;
+- vjoy_pos[i][7]=((v+vjoy_sz[1][i]-1))/OSD_TEX_H;
+-
+- u+=vjoy_sz[0][i];
+- if (u>=OSD_TEX_W)
+- {
+- u-=OSD_TEX_W;
+- v+=vjoy_sz[1][i];
+- }
+- //v+=vjoy_pos[i][3];
+- }
+-
+- verify(glIsProgram(gl.OSD_SHADER.program));
++ //wait render start only if no frame pending
++ _pvrrc = DequeueRender();
+
+- glBindTexture(GL_TEXTURE_2D,osd_tex);
+- glUseProgram(gl.OSD_SHADER.program);
++ while (!_pvrrc)
++ {
++ rs.Wait();
++ _pvrrc = DequeueRender();
++ }
+
+- //reset rendering scale
+-/*
+- float dc_width=640;
+- float dc_height=480;
++ bool do_swp=false;
++ //if (kcode[0]&(1<<9))
++ {
+
+- float dc2s_scale_h=screen_height/480.0f;
+- float ds2s_offs_x=(screen_width-dc2s_scale_h*640)/2;
+
+- //-1 -> too much to left
+- ShaderUniforms.scale_coefs[0]=2.0f/(screen_width/dc2s_scale_h);
+- ShaderUniforms.scale_coefs[1]=-2/dc_height;
+- ShaderUniforms.scale_coefs[2]=1-2*ds2s_offs_x/(screen_width);
+- ShaderUniforms.scale_coefs[3]=-1;
++ //clear up & free data ..
++ tactx_Recycle(_pvrrc);
++ _pvrrc=0;
+
+- glUniform4fv( gl.OSD_SHADER.scale, 1, ShaderUniforms.scale_coefs);
++ return do_swp;
++}
+ */
+
+- glEnable(GL_BLEND);
+- glDisable(GL_DEPTH_TEST);
+- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+-
+- glDepthMask(false);
+- glDepthFunc(GL_ALWAYS);
+-
+- glDisable(GL_CULL_FACE);
+- glDisable(GL_SCISSOR_TEST);
+-
+- int dfa=osd_count/4;
+-
+- for (int i=0;i<dfa;i++)
+- glDrawArrays(GL_TRIANGLE_STRIP,osd_base+i*4,4);
+- }
+-#endif
+-#ifdef TARGET_PANDORA
+- if (osd_font)
+- {
+- float u=0;
+- float v=0;
+-
+- verify(glIsProgram(gl.OSD_SHADER.program));
+-
+- float dc_width=640;
+- float dc_height=480;
+-
+- float dc2s_scale_h=screen_height/480.0f;
+- float ds2s_offs_x=(screen_width-dc2s_scale_h*640)/2;
+
++void rend_set_fb_scale(float x,float y)
++{
++ fb_scale_x=x;
++ fb_scale_y=y;
++}
+
+- glBindTexture(GL_TEXTURE_2D,osd_font);
+- glUseProgram(gl.OSD_SHADER.program);
++struct glesrend : Renderer
++{
++ bool Init();
++ void Resize(int width, int height);
++ void Term();
+
+- /*
+- //-1 -> too much to left
+- ShaderUniforms.scale_coefs[0]=2.0f/(screen_width/dc2s_scale_h);
+- ShaderUniforms.scale_coefs[1]=-2/dc_height;
+- ShaderUniforms.scale_coefs[2]=1-2*ds2s_offs_x/(screen_width);
+- ShaderUniforms.scale_coefs[3]=-1;
++ bool Process(TA_context* ctx);
++ bool Render();
+
+- glUniform4fv( gl.OSD_SHADER.scale, 1, ShaderUniforms.scale_coefs);
+-*/
++ void Present();
+
+- glEnable(GL_BLEND);
+- glDisable(GL_DEPTH_TEST);
+- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
++ void DrawOSD();
+
++ virtual u32 GetTexture(TSP tsp, TCW tcw);
++};
+
+- glDepthMask(false);
+- glDepthFunc(GL_ALWAYS);
++bool glesrend::Init()
++{
++
++ if (!gl_init((void*)libPvr_GetRenderTarget(), (void*)libPvr_GetRenderSurface()))
++ {
++ return false;
++ }
+
++ if (!gl_create_resources())
++ {
++ return false;
++ }
+
+- glDisable(GL_CULL_FACE);
+- glDisable(GL_SCISSOR_TEST);
++ #if defined(GLES) && HOST_OS != OS_DARWIN && !defined(TARGET_NACL32)
++ #ifdef TARGET_PANDORA
++ fbdev = open("/dev/fb0", O_RDONLY);
++ #else
++ eglSwapInterval(gl.setup.display,1);
++ #endif
++ #endif
+
++ //clean up all buffers ...
++ for (int i=0;i<10;i++)
++ {
++ glClearColor(0.f, 0.f, 0.f, 0.f);
++ glClear(GL_COLOR_BUFFER_BIT);
++ gl_swap();
++ }
+
+- int dfa=osd_count/4;
++ return true;
++}
+
+- for (int i=0;i<dfa;i++)
+- glDrawArrays(GL_TRIANGLE_STRIP,osd_base+i*4,4);
+- }
+-#endif
++void glesrend::Resize(int width, int height)
++{
++ screen_width = width;
++ screen_height = height;
+ }
+
+-bool ProcessFrame(TA_context* ctx)
++void glesrend::Term() { };
++
++bool glesrend::Process(TA_context* ctx)
+ {
+ //disable RTTs for now ..
+ if (ctx->rend.isRTT)
+@@ -1435,7 +1080,7 @@
+ return true;
+ }
+
+-bool RenderFrame()
++bool glesrend::Render()
+ {
+ DoCleanup();
+
+@@ -1737,10 +1382,10 @@
+ }
+ else
+ {
+-#if HOST_OS != OS_DARWIN
+- //Fix this in a proper way
+- glBindFramebuffer(GL_FRAMEBUFFER,0);
+-#endif
++ #if HOST_OS != OS_DARWIN
++ //Fix this in a proper way
++ glBindFramebuffer(GL_FRAMEBUFFER,0);
++ #endif
+ }
+
+ //Clear depth
+@@ -1812,68 +1457,93 @@
+ return !is_rtt;
+ }
+
+-#if !defined(_ANDROID) && !defined(TARGET_NACL32)
+-#if HOST_OS==OS_LINUX
+-#define SET_AFNT 1
+-#endif
+-#endif
+-
+-extern u16 kcode[4];
++void glesrend::Present()
++{
++ gl_swap();
++ glViewport(0, 0, screen_width, screen_height);
++}
+
+-/*
+-bool rend_single_frame()
++void glesrend::DrawOSD()
+ {
+- //wait render start only if no frame pending
+- _pvrrc = DequeueRender();
++ #ifdef TARGET_PANDORA
++ GLuint osd_texture = osd_font;
++ #else
++ GLuint osd_texture = osd_tex;
++ #endif
+
+- while (!_pvrrc)
++ if (!osd_texture)
+ {
+- rs.Wait();
+- _pvrrc = DequeueRender();
++ return;
+ }
+
+- bool do_swp=false;
+- //if (kcode[0]&(1<<9))
+- {
++ verify(glIsProgram(gl.OSD_SHADER.program));
+
++ #ifndef TARGET_PANDORA
++ float u = 0;
++ float v = 0;
++
++ for (int i = 0; i < 13; i++)
++ {
++ //umin,vmin,umax,vmax
++ vjoy_pos[i][4]=(u+1)/OSD_TEX_W;
++ vjoy_pos[i][5]=(v+1)/OSD_TEX_H;
+
+- //clear up & free data ..
+- tactx_Recycle(_pvrrc);
+- _pvrrc=0;
++ vjoy_pos[i][6]=((u+vjoy_sz[0][i]-1))/OSD_TEX_W;
++ vjoy_pos[i][7]=((v+vjoy_sz[1][i]-1))/OSD_TEX_H;
+
+- return do_swp;
+-}
+-*/
++ u+=vjoy_sz[0][i];
++ if (u>=OSD_TEX_W)
++ {
++ u-=OSD_TEX_W;
++ v+=vjoy_sz[1][i];
++ }
++ //v+=vjoy_pos[i][3];
++ }
++ #endif
+
++ glBindTexture(GL_TEXTURE_2D, osd_texture);
++ glUseProgram(gl.OSD_SHADER.program);
+
+-void rend_set_fb_scale(float x,float y)
+-{
+- fb_scale_x=x;
+- fb_scale_y=y;
+-}
++ /*
++ //reset rendering scale
+
+-struct glesrend : Renderer
+-{
+- bool Init() { return gles_init(); }
+- void Resize(int w, int h) { screen_width=w; screen_height=h; }
+- void Term() { }
++ float dc_width=640;
++ float dc_height=480;
+
+- bool Process(TA_context* ctx) { return ProcessFrame(ctx); }
+- bool Render() { return RenderFrame(); }
++ float dc2s_scale_h=screen_height/480.0f;
++ float ds2s_offs_x=(screen_width-dc2s_scale_h*640)/2;
+
+- void Present() { gl_swap(); glViewport(0, 0, screen_width, screen_height); }
++ //-1 -> too much to left
++ ShaderUniforms.scale_coefs[0]=2.0f/(screen_width/dc2s_scale_h);
++ ShaderUniforms.scale_coefs[1]=-2/dc_height;
++ ShaderUniforms.scale_coefs[2]=1-2*ds2s_offs_x/(screen_width);
++ ShaderUniforms.scale_coefs[3]=-1;
+
+- void DrawOSD() { OSD_DRAW(); }
++ glUniform4fv( gl.OSD_SHADER.scale, 1, ShaderUniforms.scale_coefs);
++ */
+
+- virtual u32 GetTexture(TSP tsp, TCW tcw) {
+- return gl_GetTexture(tsp, tcw);
+- }
+-};
++ glEnable(GL_BLEND);
++ glDisable(GL_DEPTH_TEST);
++ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
++ glDepthMask(false);
++ glDepthFunc(GL_ALWAYS);
+
+-#include "deps/libpng/png.h"
++ glDisable(GL_CULL_FACE);
++ glDisable(GL_SCISSOR_TEST);
+
+-FILE* pngfile;
++ int dfa = osd_count/4;
++
++ for (int i = 0; i < dfa; i++)
++ {
++ glDrawArrays(GL_TRIANGLE_STRIP, osd_base + i*4, 4);
++ }
++}
++
++u32 glesrend::GetTexture(TSP tsp, TCW tcw)
++{
++ return gl_GetTexture(tsp, tcw);
++}
+
+ void png_cstd_read(png_structp png_ptr, png_bytep data, png_size_t length)
+ {
+@@ -2021,5 +1691,4 @@
+ return texture;
+ }
+
+-
+ Renderer* rend_GLES2() { return new glesrend(); }
+diff -Nur a/core/rend/gles/glshaders.cpp b/core/rend/gles/glshaders.cpp
+--- a/core/rend/gles/glshaders.cpp 1969-12-31 21:00:00.000000000 -0300
++++ b/core/rend/gles/glshaders.cpp 2015-10-06 21:51:21.723570329 -0300
+@@ -0,0 +1,280 @@
++#ifndef GLES
++#define attr "in"
++#define vary "out"
++#else
++#define attr "attribute"
++#define vary "varying"
++#endif
++#if 1
++
++//Fragment and vertex shaders code
++//pretty much 1:1 copy of the d3d ones for now
++const char* VertexShaderSource =
++#ifndef GLES
++ "#version 140 \n"
++#endif
++"\
++/* Vertex constants*/ \n\
++uniform highp vec4 scale; \n\
++uniform highp vec4 depth_scale; \n\
++uniform highp float sp_FOG_DENSITY; \n\
++/* Vertex input */ \n\
++" attr " highp vec4 in_pos; \n\
++" attr " lowp vec4 in_base; \n\
++" attr " lowp vec4 in_offs; \n\
++" attr " mediump vec2 in_uv; \n\
++/* output */ \n\
++" vary " lowp vec4 vtx_base; \n\
++" vary " lowp vec4 vtx_offs; \n\
++" vary " mediump vec2 vtx_uv; \n\
++" vary " highp vec3 vtx_xyz; \n\
++void main() \n\
++{ \n\
++ vtx_base=in_base; \n\
++ vtx_offs=in_offs; \n\
++ vtx_uv=in_uv; \n\
++ vec4 vpos=in_pos; \n\
++ vtx_xyz.xy = vpos.xy; \n\
++ vtx_xyz.z = vpos.z*sp_FOG_DENSITY; \n\
++ vpos.w=1.0/vpos.z; \n\
++ vpos.xy=vpos.xy*scale.xy-scale.zw; \n\
++ vpos.xy*=vpos.w; \n\
++ vpos.z=depth_scale.x+depth_scale.y*vpos.w; \n\
++ gl_Position = vpos; \n\
++}";
++
++
++#else
++
++
++
++const char* VertexShaderSource =
++ ""
++ "/* Test Projection Matrix */"
++ ""
++ "uniform highp mat4 Projection;"
++ ""
++ ""
++ "/* Vertex constants */"
++ ""
++ "uniform highp float sp_FOG_DENSITY;"
++ "uniform highp vec4 scale;"
++ ""
++ "/* Vertex output */"
++ ""
++ "attribute highp vec4 in_pos;"
++ "attribute lowp vec4 in_base;"
++ "attribute lowp vec4 in_offs;"
++ "attribute mediump vec2 in_uv;"
++ ""
++ "/* Transformed input */"
++ ""
++ "varying lowp vec4 vtx_base;"
++ "varying lowp vec4 vtx_offs;"
++ "varying mediump vec2 vtx_uv;"
++ "varying highp vec3 vtx_xyz;"
++ ""
++ "void main()"
++ "{"
++ " vtx_base = in_base;"
++ " vtx_offs = in_offs;"
++ " vtx_uv = in_uv;"
++ ""
++ " vec4 vpos = in_pos;"
++ " vtx_xyz.xy = vpos.xy; "
++ " vtx_xyz.z = vpos.z*sp_FOG_DENSITY; "
++ ""
++ " vpos.w = 1.0/vpos.z; "
++ " vpos.z *= -scale.w; "
++ " vpos.xy = vpos.xy*scale.xy-sign(scale.xy); "
++ " vpos.xy *= vpos.w; "
++ ""
++ " gl_Position = vpos;"
++ // " gl_Position = vpos * Projection;"
++ "}"
++ ;
++
++
++#endif
++
++/*
++
++cp_AlphaTest 0 1 2 2
++pp_ClipTestMode -1 0 1 3 6
++pp_UseAlpha 0 1 2 12
++pp_Texture 1
++ pp_IgnoreTexA 0 1 2 2
++ pp_ShadInstr 0 1 2 3 4 8
++ pp_Offset 0 1 2 16
++ pp_FogCtrl 0 1 2 3 4 64
++pp_Texture 0
++ pp_FogCtrl 0 2 3 4 4
++
++pp_Texture: off -> 12*4=48 shaders
++pp_Texture: on -> 12*64=768 shaders
++Total: 816 shaders
++
++highp float fdecp(highp float flt,out highp float e) \n\
++{ \n\
++ highp float lg2=log2(flt); //ie , 2.5 \n\
++ highp float frc=fract(lg2); //ie , 0.5 \n\
++ e=lg2-frc; //ie , 2.5-0.5=2 (exp) \n\
++ return pow(2.0,frc); //2^0.5 (manitsa) \n\
++} \n\
++lowp float fog_mode2(highp float invW) \n\
++{ \n\
++ highp float foginvW=invW; \n\
++ foginvW=clamp(foginvW,1.0,255.0); \n\
++ \n\
++ highp float fogexp; //0 ... 7 \n\
++ highp float fogman=fdecp(foginvW, fogexp); //[1,2) mantissa bits. that is 1.m \n\
++ \n\
++ highp float fogman_hi=fogman*16.0-16.0; //[16,32) -16 -> [0,16) \n\
++ highp float fogman_idx=floor(fogman_hi); //[0,15] \n\
++ highp float fogman_blend=fract(fogman_hi); //[0,1) -- can also be fogman_idx-fogman_idx ! \n\
++ highp float fog_idx_fr=fogexp*16.0+fogman_idx; //[0,127] \n\
++ \n\
++ highp float fog_idx_pixel_fr=fog_idx_fr+0.5; \n\
++ highp float fog_idx_pixel_n=fog_idx_pixel_fr/128.0;//normalise to [0.5/128,127.5/128) coordinates \n\
++ \n\
++ //fog is 128x1 texure \n\
++ lowp vec2 fog_coefs=texture2D(fog_table,vec2(fog_idx_pixel_n)).rg; \n\
++ \n\
++ lowp float fog_coef=mix(fog_coefs.r,fog_coefs.g,fogman_blend); \n\
++ \n\
++ return fog_coef; \n\
++} \n\
++*/
++
++#ifndef GLES
++#define FRAGCOL "FragColor"
++#define TEXLOOKUP "texture"
++#define vary "in"
++#else
++#define FRAGCOL "gl_FragColor"
++#define TEXLOOKUP "texture2D"
++#endif
++
++
++const char* PixelPipelineShader =
++#ifndef GLES
++ "#version 140 \n"
++ "out vec4 FragColor; \n"
++#endif
++"\
++\
++#define cp_AlphaTest %d \n\
++#define pp_ClipTestMode %d.0 \n\
++#define pp_UseAlpha %d \n\
++#define pp_Texture %d \n\
++#define pp_IgnoreTexA %d \n\
++#define pp_ShadInstr %d \n\
++#define pp_Offset %d \n\
++#define pp_FogCtrl %d \n\
++/* Shader program params*/ \n\
++/* gles has no alpha test stage, so its emulated on the shader */ \n\
++uniform lowp float cp_AlphaTestValue; \n\
++uniform lowp vec4 pp_ClipTest; \n\
++uniform lowp vec3 sp_FOG_COL_RAM,sp_FOG_COL_VERT; \n\
++uniform highp vec2 sp_LOG_FOG_COEFS; \n\
++uniform sampler2D tex,fog_table; \n\
++/* Vertex input*/ \n\
++" vary " lowp vec4 vtx_base; \n\
++" vary " lowp vec4 vtx_offs; \n\
++" vary " mediump vec2 vtx_uv; \n\
++" vary " highp vec3 vtx_xyz; \n\
++lowp float fog_mode2(highp float val) \n\
++{ \n\
++ highp float fog_idx=clamp(val,0.0,127.99); \n\
++ return clamp(sp_LOG_FOG_COEFS.y*log2(fog_idx)+sp_LOG_FOG_COEFS.x,0.001,1.0); //the clamp is required due to yet another bug !\n\
++} \n\
++void main() \n\
++{ \n\
++ lowp vec4 color=vtx_base; \n\
++ #if pp_UseAlpha==0 \n\
++ color.a=1.0; \n\
++ #endif\n\
++ #if pp_FogCtrl==3 \n\
++ color=vec4(sp_FOG_COL_RAM.rgb,fog_mode2(vtx_xyz.z)); \n\
++ #endif\n\
++ #if pp_Texture==1 \n\
++ { \n\
++ lowp vec4 texcol=" TEXLOOKUP "(tex,vtx_uv); \n\
++ \n\
++ #if pp_IgnoreTexA==1 \n\
++ texcol.a=1.0; \n\
++ #endif\n\
++ \n\
++ #if pp_ShadInstr==0 \n\
++ { \n\
++ color.rgb=texcol.rgb; \n\
++ color.a=texcol.a; \n\
++ } \n\
++ #endif\n\
++ #if pp_ShadInstr==1 \n\
++ { \n\
++ color.rgb*=texcol.rgb; \n\
++ color.a=texcol.a; \n\
++ } \n\
++ #endif\n\
++ #if pp_ShadInstr==2 \n\
++ { \n\
++ color.rgb=mix(color.rgb,texcol.rgb,texcol.a); \n\
++ } \n\
++ #endif\n\
++ #if pp_ShadInstr==3 \n\
++ { \n\
++ color*=texcol; \n\
++ } \n\
++ #endif\n\
++ \n\
++ #if pp_Offset==1 \n\
++ { \n\
++ color.rgb+=vtx_offs.rgb; \n\
++ if (pp_FogCtrl==1) \n\
++ color.rgb=mix(color.rgb,sp_FOG_COL_VERT.rgb,vtx_offs.a); \n\
++ } \n\
++ #endif\n\
++ } \n\
++ #endif\n\
++ #if pp_FogCtrl==0 \n\
++ { \n\
++ color.rgb=mix(color.rgb,sp_FOG_COL_RAM.rgb,fog_mode2(vtx_xyz.z)); \n\
++ } \n\
++ #endif\n\
++ #if cp_AlphaTest == 1 \n\
++ if (cp_AlphaTestValue>color.a) discard;\n\
++ #endif \n\
++ //color.rgb=vec3(vtx_xyz.z/255.0);\n\
++ " FRAGCOL "=color; \n\
++}";
++
++const char* ModifierVolumeShader =
++#ifndef GLES
++ "#version 140 \n"
++ "out vec4 FragColor; \n"
++#endif
++" \
++uniform lowp float sp_ShaderColor; \n\
++/* Vertex input*/ \n\
++void main() \n\
++{ \n\
++ " FRAGCOL "=vec4(0.0, 0.0, 0.0, sp_ShaderColor); \n\
++}";
++
++const char* OSD_Shader =
++#ifndef GLES
++ "#version 140 \n"
++ "out vec4 FragColor; \n"
++#endif
++" \
++" vary " lowp vec4 vtx_base; \n\
++" vary " mediump vec2 vtx_uv; \n\
++/* Vertex input*/ \n\
++uniform sampler2D tex; \n\
++void main() \n\
++{ \n\
++ mediump vec2 uv=vtx_uv; \n\
++ uv.y=1.0-uv.y; \n\
++ " FRAGCOL "=vtx_base*" TEXLOOKUP "(tex,uv.st); \n\n\
++}";
+diff -Nur a/core/rend/gles/glshaders.h b/core/rend/gles/glshaders.h
+--- a/core/rend/gles/glshaders.h 1969-12-31 21:00:00.000000000 -0300
++++ b/core/rend/gles/glshaders.h 2015-10-06 21:51:21.723570329 -0300
+@@ -0,0 +1,5 @@
++#pragma once
++extern const char* VertexShaderSource;
++extern const char* PixelPipelineShader;
++extern const char* ModifierVolumeShader;
++extern const char* OSD_Shader;
+\ No newline at end of file
diff --git a/pcr/reicast-multilib-git/sdl-opengl.patch b/pcr/reicast-multilib-git/sdl-opengl.patch
new file mode 100644
index 000000000..372ec9a52
--- /dev/null
+++ b/pcr/reicast-multilib-git/sdl-opengl.patch
@@ -0,0 +1,251 @@
+diff -Nur a/core/khronos/GL3/gl3w.c b/core/khronos/GL3/gl3w.c
+--- a/core/khronos/GL3/gl3w.c 2015-10-06 21:43:53.040336386 -0300
++++ b/core/khronos/GL3/gl3w.c 2015-10-06 22:04:39.682388782 -0300
+@@ -1,6 +1,25 @@
+ #include <GL3/gl3w.h>
+
+-#ifdef _WIN32
++#if defined(USE_SDL2)
++ #include <SDL2/SDL.h>
++ static void open_libgl(void)
++ {
++ SDL_GL_LoadLibrary(NULL);
++ }
++
++ static void close_libgl(void)
++ {
++ SDL_GL_UnloadLibrary();
++ }
++
++ static void *get_proc(const char *proc)
++ {
++ void *res = NULL;
++ res = (void*)SDL_GL_GetProcAddress(proc);
++ return res;
++ }
++
++#elif defined(_WIN32)
+ #define WIN32_LEAN_AND_MEAN 1
+ #include <windows.h>
+
+diff -Nur a/core/sdl/sdl.cpp b/core/sdl/sdl.cpp
+--- a/core/sdl/sdl.cpp 2015-10-06 21:43:53.048336444 -0300
++++ b/core/sdl/sdl.cpp 2015-10-06 22:04:39.683388790 -0300
+@@ -5,6 +5,18 @@
+ #include "sdl/sdl.h"
+ #ifdef GLES
+ #include <EGL/egl.h>
++#else
++ #ifndef USE_SDL2
++ #error "Our SDL1.2 implementation only supports GLES. You need SDL2 for OpenGL 3 support!"
++ #endif
++ #include "khronos/GL3/gl3w.h"
++#endif
++
++#ifdef USE_SDL2
++ static SDL_Window* window = NULL;
++ static SDL_GLContext glcontext;
++#else
++ SDL_Surface *screen = NULL;
+ #endif
+
+ #ifdef TARGET_PANDORA
+@@ -14,8 +26,6 @@
+ #endif
+ #define WINDOW_HEIGHT 480
+
+-SDL_Surface *screen = NULL;
+-
+ static SDL_Joystick *JoySDL = 0;
+
+ extern bool FrameSkipping;
+@@ -80,11 +90,15 @@
+
+ AxisCount = SDL_JoystickNumAxes(JoySDL);
+ ButtonCount = SDL_JoystickNumButtons(JoySDL);
+- Name = SDL_JoystickName(0);
+-
++ #ifdef USE_SDL2
++ Name = SDL_JoystickName(JoySDL);
++ #else
++ Name = SDL_JoystickName(0);
++ #endif
++
+ printf("SDK: Found '%s' joystick with %d axes and %d buttons\n", Name, AxisCount, ButtonCount);
+
+- if (strcmp(Name,"Microsoft X-Box 360 pad")==0)
++ if (Name != NULL && strcmp(Name,"Microsoft X-Box 360 pad")==0)
+ {
+ sdl_map_btn = sdl_map_btn_xbox360;
+ sdl_map_axis = sdl_map_axis_xbox360;
+@@ -113,12 +127,16 @@
+ }
+ #endif
+
+- SDL_ShowCursor(0);
++ #ifndef USE_SDL2
++ SDL_ShowCursor(0);
+
+- if (SDL_WM_GrabInput( SDL_GRAB_ON ) != SDL_GRAB_ON)
+- {
+- printf("SDK: Error while grabbing mouse\n");
+- }
++ if (SDL_WM_GrabInput( SDL_GRAB_ON ) != SDL_GRAB_ON)
++ {
++ printf("SDL: Error while grabbing mouse\n");
++ }
++ #else
++ SDL_SetRelativeMouseMode(SDL_TRUE);
++ #endif
+ }
+
+ void input_sdl_handle(u32 port)
+@@ -397,7 +415,14 @@
+ #ifdef TARGET_PANDORA
+ strncpy(OSD_Counters, text, 256);
+ #else
+- SDL_WM_SetCaption(text, NULL); // *TODO* Set Icon also...
++ #ifdef USE_SDL2
++ if(window)
++ {
++ SDL_SetWindowTitle(window, text); // *TODO* Set Icon also...
++ }
++ #else
++ SDL_WM_SetCaption(text, NULL);
++ #endif
+ #endif
+ }
+
+@@ -415,17 +440,79 @@
+
+ int window_width = cfgLoadInt("x11","width", WINDOW_WIDTH);
+ int window_height = cfgLoadInt("x11","height", WINDOW_HEIGHT);
++
+ #ifdef TARGET_PANDORA
+ int flags = SDL_FULLSCREEN;
+ #else
+ int flags = SDL_SWSURFACE;
+ #endif
+- screen = SDL_SetVideoMode(window_width, window_height, 0, flags);
+- if (!screen)
+- {
+- die("error creating SDL screen");
+- }
+- x11_disp = EGL_DEFAULT_DISPLAY;
+- printf("Created SDL Windows (%ix%i) successfully\n", window_width, window_height);
++
++ #if !defined(GLES) && defined(USE_SDL2)
++ flags |= SDL_WINDOW_OPENGL;
++
++ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
++ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
++ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
++ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
++ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
++ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
++ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
++ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
++ SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
++ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
++
++ window = SDL_CreateWindow("Reicast Emulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, window_width, window_height, flags);
++ if (!window)
++ {
++ die("error creating SDL window");
++ }
++
++ glcontext = SDL_GL_CreateContext(window);
++ if (!glcontext)
++ {
++ die("Error creating SDL GL context");
++ }
++ SDL_GL_MakeCurrent(window, NULL);
++ #else
++ screen = SDL_SetVideoMode(window_width, window_height, 0, flags);
++ if (!screen)
++ {
++ die("error creating SDL screen");
++ }
++
++ x11_disp = EGL_DEFAULT_DISPLAY;
++ #endif
++
++ printf("Created SDL Window (%ix%i) and GL Context successfully\n", window_width, window_height);
+ }
+ #endif
++
++#if !defined(GLES) && defined(USE_SDL2)
++ extern int screen_width, screen_height;
++
++ bool gl_init(void* wind, void* disp)
++ {
++ SDL_GL_MakeCurrent(window, glcontext);
++ return gl3wInit() != -1 && gl3wIsSupported(3, 1);
++ }
++
++ void gl_swap()
++ {
++ SDL_GL_SwapWindow(window);
++
++ /* Check if drawable has been resized */
++ int new_width, new_height;
++ SDL_GL_GetDrawableSize(window, &new_width, &new_height);
++
++ if (new_width != screen_width || new_height != screen_height)
++ {
++ screen_width = new_width;
++ screen_height = new_height;
++ }
++ }
++
++ void gl_term()
++ {
++ SDL_GL_DeleteContext(glcontext);
++ }
++#endif
+\ No newline at end of file
+diff -Nur a/core/sdl/sdl.h b/core/sdl/sdl.h
+--- a/core/sdl/sdl.h 2015-10-06 21:43:53.048336444 -0300
++++ b/core/sdl/sdl.h 2015-10-06 22:04:39.683388790 -0300
+@@ -1,5 +1,9 @@
+ #pragma once
+-#include <SDL/SDL.h>
++#ifdef USE_SDL2
++ #include <SDL2/SDL.h>
++#else
++ #include <SDL/SDL.h>
++#endif
+ extern void* sdl_glc;
+ extern void input_sdl_init();
+ extern void input_sdl_handle(u32 port);
+diff -Nur a/shell/linux/Makefile b/shell/linux/Makefile
+--- a/shell/linux/Makefile 2015-10-06 21:43:53.161337253 -0300
++++ b/shell/linux/Makefile 2015-10-06 22:04:39.683388790 -0300
+@@ -175,6 +175,10 @@
+ $(error Unknown platform)
+ endif
+
++ifdef USE_SDL2
++ USE_SDL := 1
++endif
++
+ RZDCY_SRC_DIR = ../../core
+ include $(RZDCY_SRC_DIR)/core.mk
+
+@@ -202,8 +206,14 @@
+ endif
+
+ ifdef USE_SDL
+- CXXFLAGS += `sdl-config --cflags` -D USE_SDL
+- LIBS += `sdl-config --libs`
++ CXXFLAGS += -D USE_SDL
++ ifdef USE_SDL2
++ CXXFLAGS += `sdl2-config --cflags` -D USE_SDL2
++ LIBS += `sdl2-config --libs`
++ else
++ CXXFLAGS += `sdl-config --cflags`
++ LIBS += `sdl-config --libs`
++ endif
+ endif
+
+ ifdef PGO_MAKE
diff --git a/pcr/reicast-multilib-git/sh-block-graphs.patch b/pcr/reicast-multilib-git/sh-block-graphs.patch
new file mode 100644
index 000000000..c8e1bcf15
--- /dev/null
+++ b/pcr/reicast-multilib-git/sh-block-graphs.patch
@@ -0,0 +1,296 @@
+diff -Nur a/core/hw/sh4/dyna/blockmanager.cpp b/core/hw/sh4/dyna/blockmanager.cpp
+--- a/core/hw/sh4/dyna/blockmanager.cpp 2015-10-06 21:43:53.030336315 -0300
++++ b/core/hw/sh4/dyna/blockmanager.cpp 2015-10-06 21:58:25.685653822 -0300
+@@ -122,7 +122,11 @@
+ }
+ else
+ {
+- printf("bm_GetBlock(%08X) failed ..\n",dynarec_code);
++ for (iter = blkmap.begin(); iter != blkmap.end(); iter++) {
++ if ((*iter)->contains_code((u8*)dynarec_code))
++ return *iter;
++ }
++ //printf("bm_GetBlock(%p, %p) failed ..\n",dynarec_code, ngen_FailedToFindBlock);
+ return 0;
+ }
+ }
+@@ -158,6 +162,8 @@
+ verify((void*)bm_GetCode(blk->addr)==(void*)ngen_FailedToFindBlock);
+ FPCA(blk->addr)=blk->code;
+
++ verify(bm_GetBlock(blk->addr) == blk);
++
+ #ifdef DYNA_OPROF
+ if (oprofHandle)
+ {
+diff -Nur a/core/hw/sh4/dyna/blockmanager.h b/core/hw/sh4/dyna/blockmanager.h
+--- a/core/hw/sh4/dyna/blockmanager.h 2015-10-06 21:43:53.030336315 -0300
++++ b/core/hw/sh4/dyna/blockmanager.h 2015-10-06 21:58:25.685653822 -0300
+@@ -71,6 +71,7 @@
+
+ u32 memops;
+ u32 linkedmemops;
++ bool entry_block;
+ };
+
+ struct CachedBlockInfo: RuntimeBlockInfo_Core
+diff -Nur a/core/hw/sh4/dyna/decoder.cpp b/core/hw/sh4/dyna/decoder.cpp
+--- a/core/hw/sh4/dyna/decoder.cpp 2015-10-06 21:43:53.030336315 -0300
++++ b/core/hw/sh4/dyna/decoder.cpp 2015-10-06 21:58:25.685653822 -0300
+@@ -14,6 +14,7 @@
+ #include "hw/sh4/sh4_core.h"
+ #include "hw/sh4/sh4_mem.h"
+ #include "decoder_opcodes.h"
++#include "../interpr/sh4_opcodes.h"
+
+ #define BLOCK_MAX_SH_OPS_SOFT 500
+ #define BLOCK_MAX_SH_OPS_HARD 511
+@@ -1098,6 +1099,13 @@
+ else
+ blk->guest_cycles+=CPU_RATIO;
+
++ if ((state.cpu.is_delayslot && OpDesc[op]->SetPC()) ||
++ OpDesc[op]->oph == iNotImplemented) {
++ blk->addr = -1;
++ return;
++ }
++
++
+ verify(!(state.cpu.is_delayslot && OpDesc[op]->SetPC()));
+ if (state.ngen.OnlyDynamicEnds || !OpDesc[op]->rec_oph)
+ {
+@@ -1168,6 +1176,8 @@
+ if (settings.dynarec.idleskip)
+ {
+ //Experimental hash-id based idle skip
++ if (blk->addr == 0x8C0B926A)
++ blk->guest_cycles *= 100;
+ if (strstr(idle_hash,blk->hash(false,true)))
+ {
+ //printf("IDLESKIP: %08X reloc match %s\n",blk->addr,blk->hash(false,true));
+diff -Nur a/core/hw/sh4/dyna/driver.cpp b/core/hw/sh4/dyna/driver.cpp
+--- a/core/hw/sh4/dyna/driver.cpp 2015-10-06 21:43:53.030336315 -0300
++++ b/core/hw/sh4/dyna/driver.cpp 2015-10-06 21:58:25.685653822 -0300
+@@ -72,11 +72,24 @@
+ LastAddr=LastAddr_min;
+ memset(emit_GetCCPtr(),0xCC,emit_FreeSpace());
+ }
++
++#include <map>
++#include <algorithm>
++#include <set>
++
++typedef map<u32, RuntimeBlockInfo*> BlockGraph;
++
++vector<BlockGraph*> graphs;
++map<u32, BlockGraph*> blockgraphs;
++
+ void recSh4_ClearCache()
+ {
+ LastAddr=LastAddr_min;
+ bm_Reset();
+
++ graphs.clear();
++ blockgraphs.clear();
++
+ printf("recSh4:Dynarec Cache clear at %08X\n",curr_pc);
+ }
+
+@@ -212,9 +225,145 @@
+ AnalyseBlock(this);
+ }
+
++void merge_graphs(BlockGraph* one, BlockGraph* two) {
++ for (BlockGraph::iterator it = two->begin(); it != two->end(); it++) {
++ blockgraphs[it->second->addr] = one;
++ (*one)[it->second->addr] = it->second;
++ }
++ graphs.erase(find(graphs.begin(), graphs.end(), two));
++}
++
++void discover_graph(u32 bootstrap) {
++ BlockGraph* graph;
++ set<u32> resolved;
++ vector<u32> unresolved;
++
++ if (blockgraphs.count(bootstrap)) {
++ graph = blockgraphs[bootstrap];
++ (*graph)[bootstrap]->entry_block = true;
++ return;
++ } else {
++ graph = new BlockGraph();
++
++ graphs.push_back(graph);
++ }
++
++ unresolved.push_back(bootstrap);
++
++ while (unresolved.size()) {
++ u32 pc = unresolved[unresolved.size()-1];
++ unresolved.pop_back();
++
++ if (resolved.count(pc))
++ continue;
++
++ if (graph->count(pc))
++ continue;
++
++ if (blockgraphs.count(pc)) {
++ verify(blockgraphs[pc] != graph);
++ merge_graphs(blockgraphs[pc], graph);
++ graph = blockgraphs[pc];
++ resolved.clear();
++ continue;
++ }
++
++ resolved.insert(pc);
++ //printf("resolving %08X\n", pc);
++
++ RuntimeBlockInfo* rbi = ngen_AllocateBlock();
++ rbi->Setup(pc,fpscr);
++ rbi->entry_block = pc == bootstrap;
++
++ if (rbi->addr == -1)
++ continue;
++
++ (*graph)[pc] = rbi;
++ blockgraphs[pc] = graph;
++
++ if (rbi->BranchBlock !=-1 && rbi->BlockType != BET_StaticCall)
++ unresolved.push_back(rbi->BranchBlock);
++
++ if (rbi->NextBlock != -1)
++ unresolved.push_back(rbi->NextBlock);
++ }
++
++ int entrypoints = 0;
++
++ for (BlockGraph::iterator it = graph->begin(); it != graph->end(); it++) {
++ entrypoints += it->second->entry_block;
++ }
++
++ //printf("Graph: %d blocks w/ %d entrypoints, %d graphs\n", graph->size(), entrypoints, graphs.size());
++}
++
++void print_graphs() {
++ int top_runs = 0;
++ int total_runs = 0;
++ map<BlockGraph*, u32> graph_runs;
++
++ for (size_t i = 0; i < graphs.size(); i++) {
++ BlockGraph* graph = graphs[i];
++ for (BlockGraph::iterator it = graphs[i]->begin(); it != graphs[i]->end(); it++) {
++
++ RuntimeBlockInfo* natblock = bm_GetBlock(it->first);
++ if (!natblock || natblock->runs == 0)
++ continue;
++
++ if (natblock->runs > top_runs)
++ top_runs = natblock->runs;
++
++ total_runs += natblock->runs;
++
++ graph_runs[graph] += natblock->runs;
++ }
++ }
++
++ int baseline = top_runs / 100;
++
++ printf("<Graphdump\n");
++ for (size_t i = 0; i < graphs.size(); i++) {
++ BlockGraph* graph = graphs[i];
++ if (graph_runs[graph] < baseline)
++ continue;
++ printf("\tGraph: %p\n", graphs[i]);
++
++ int cnt = 0;
++ int cnt2 = 0;
++ for (BlockGraph::iterator it = graphs[i]->begin(); it != graphs[i]->end(); it++) {
++
++ RuntimeBlockInfo* natblock = bm_GetBlock(it->first);
++ if (!natblock || natblock->runs == 0 || natblock->runs < baseline) {
++ if (natblock) {
++ cnt2++;
++ natblock->runs = 0;
++ } else {
++ cnt++;
++ }
++ continue;
++ }
++ printf("\t\tBlock %08X, compiled: %d, entrypoint: %d, type: %d, len: %d, cycles: %d, runs %d, loop %d\n",
++ it->first, natblock != 0, it->second->entry_block, it->second->BlockType, it->second->oplist.size(),
++ it->second->guest_cycles, natblock ? natblock->runs : 0,
++ (it->second->BlockType == BET_Cond_0 || it->second->BlockType == BET_Cond_1) && graph->count(it->second->BranchBlock) );
++
++ if (natblock)
++ natblock->runs = 0;
++ }
++ if (cnt2) {
++ printf("\t\t and %d more not worth mentioning\n", cnt2);
++ }
++ if (cnt) {
++ printf("\t\t and %d more that never run\n", cnt);
++ }
++ }
++ printf("Graphdump>\n");
++}
++
+ DynarecCodeEntryPtr rdv_CompilePC()
+ {
+ u32 pc=next_pc;
++ discover_graph(pc);
+
+ if (emit_FreeSpace()<16*1024 || pc==0x8c0000e0 || pc==0xac010000 || pc==0xac008300)
+ recSh4_ClearCache();
+@@ -232,6 +381,7 @@
+ rbi->staging_runs=do_opts?100:-100;
+ ngen_Compile(rbi,DoCheck(rbi->addr),(pc&0xFFFFFF)==0x08300 || (pc&0xFFFFFF)==0x10000,false,do_opts);
+ verify(rbi->code!=0);
++ verify(rbi->host_code_size!=0);
+
+ bm_AddBlock(rbi);
+
+diff -Nur a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp
+--- a/core/linux-dist/main.cpp 2015-10-06 21:43:53.042336401 -0300
++++ b/core/linux-dist/main.cpp 2015-10-06 21:58:25.685653822 -0300
+@@ -189,6 +189,18 @@
+ #if defined(USE_SDL)
+ input_sdl_handle(port);
+ #endif
++
++ //Whatever happened to these?
++#if FEAT_SHREC != DYNAREC_NONE
++ /*
++ void print_graphs();
++ if ('b' == key) emit_WriteCodeCache();
++ if ('n' == key) bm_Reset();
++ if ('m' == key) bm_Sort();
++ if (',' == key) { emit_WriteCodeCache(); bm_Sort(); }
++ if ('q' == key) print_graphs();
++ */
++#endif
+ }
+
+ void os_DoEvents()
+diff -Nur a/core/rec-x64/rec_x64.cpp b/core/rec-x64/rec_x64.cpp
+--- a/core/rec-x64/rec_x64.cpp 2015-10-06 21:43:53.045336422 -0300
++++ b/core/rec-x64/rec_x64.cpp 2015-10-06 21:58:25.685653822 -0300
+@@ -162,6 +162,10 @@
+
+ sub(dword[rax], block->guest_cycles);
+
++ mov(rax, (size_t)&block->runs);
++ add(dword[rax], 1);
++
++
+ sub(rsp, 0x28);
+
+ for (size_t i = 0; i < block->oplist.size(); i++) {
+@@ -355,6 +359,7 @@
+
+ block->code = (DynarecCodeEntryPtr)getCode();
+
++ block->host_code_size = getSize();
+ emit_Skip(getSize());
+ }
+
diff --git a/pcr/reicast-multilib-git/ta-hash-logs.patch b/pcr/reicast-multilib-git/ta-hash-logs.patch
new file mode 100644
index 000000000..1d120852f
--- /dev/null
+++ b/pcr/reicast-multilib-git/ta-hash-logs.patch
@@ -0,0 +1,120 @@
+diff -Nur a/core/hw/pvr/Renderer_if.cpp b/core/hw/pvr/Renderer_if.cpp
+--- a/core/hw/pvr/Renderer_if.cpp 2015-10-06 21:43:53.028336301 -0300
++++ b/core/hw/pvr/Renderer_if.cpp 2015-10-06 21:49:25.016725724 -0300
+@@ -5,10 +5,16 @@
+
+ #include "deps/zlib/zlib.h"
+
++#include "deps/crypto/md5.h"
++
+ #if FEAT_HAS_NIXPROF
+ #include "profiler/profiler.h"
+ #endif
+
++#define FRAME_MD5 0x1
++FILE* fLogFrames;
++FILE* fCheckFrames;
++
+ /*
+
+ rendv3 ideas
+@@ -291,6 +297,48 @@
+
+ if (ctx)
+ {
++ if (fLogFrames || fCheckFrames) {
++ MD5Context md5;
++ u8 digest[16];
++
++ MD5Init(&md5);
++ MD5Update(&md5, ctx->tad.thd_root, ctx->tad.End() - ctx->tad.thd_root);
++ MD5Final(digest, &md5);
++
++ if (fLogFrames) {
++ fputc(FRAME_MD5, fLogFrames);
++ fwrite(digest, 1, 16, fLogFrames);
++ fflush(fLogFrames);
++ }
++
++ if (fCheckFrames) {
++ u8 v;
++ u8 digest2[16];
++ int ch = fgetc(fCheckFrames);
++
++ if (ch == EOF) {
++ printf("Testing: TA Hash log matches, exiting\n");
++ exit(1);
++ }
++
++ verify(ch == FRAME_MD5);
++
++ fread(digest2, 1, 16, fCheckFrames);
++
++ verify(memcmp(digest, digest2, 16) == 0);
++
++
++ }
++
++ /*
++ u8* dig = digest;
++ printf("FRAME: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n",
++ digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
++ digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
++ );
++ */
++ }
++
+ if (!ctx->rend.Overrun)
+ {
+ //printf("REP: %.2f ms\n",render_end_pending_cycles/200000.0);
+@@ -366,6 +414,13 @@
+
+ bool rend_init()
+ {
++ if (fLogFrames = fopen(settings.pvr.HashLogFile.c_str(), "wb")) {
++ printf("Saving frame hashes to: '%s'\n", settings.pvr.HashLogFile.c_str());
++ }
++
++ if (fCheckFrames = fopen(settings.pvr.HashCheckFile.c_str(), "rb")) {
++ printf("Comparing frame hashes against: '%s'\n", settings.pvr.HashCheckFile.c_str());
++ }
+
+ #ifdef NO_REND
+ renderer = rend_norend();
+@@ -435,6 +490,11 @@
+
+ void rend_term()
+ {
++ if (fCheckFrames)
++ fclose(fCheckFrames);
++
++ if (fLogFrames)
++ fclose(fLogFrames);
+ }
+
+ void rend_vblank()
+diff -Nur a/core/nullDC.cpp b/core/nullDC.cpp
+--- a/core/nullDC.cpp 2015-10-06 21:43:53.043336408 -0300
++++ b/core/nullDC.cpp 2015-10-06 21:49:25.017725731 -0300
+@@ -260,6 +260,8 @@
+ #endif
+
+ settings.bios.UseReios = cfgLoadInt("config", "bios.UseReios", 0);
++ settings.pvr.HashLogFile = cfgLoadStr("testing", "ta.HashLogFile", "");
++ settings.pvr.HashCheckFile = cfgLoadStr("testing", "ta.HashCheckFile", "");
+
+ #if (HOST_OS != OS_LINUX || defined(_ANDROID) || defined(TARGET_PANDORA))
+ settings.aica.BufferSize=2048;
+diff -Nur a/core/types.h b/core/types.h
+--- a/core/types.h 2015-10-06 21:43:53.048336444 -0300
++++ b/core/types.h 2015-10-06 21:49:25.017725731 -0300
+@@ -696,6 +696,9 @@
+
+ u32 MaxThreads;
+ u32 SynchronousRendering;
++
++ string HashLogFile;
++ string HashCheckFile;
+ } pvr;
+
+ struct {