diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Attachment.cpp b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Attachment.cpp
index bf4cacf..ee4831a 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Attachment.cpp
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Attachment.cpp
@@ -13,14 +13,12 @@ static const u8 nothing_id[]	= { 0x00, 0x00, 0x00, 0x00, 0x2e, 0x2e };
 // The id for a partially inserted extension
 static const u8 partially_id[]	= { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff };
 
-Attachment::Attachment( const char* const _name, WiimoteEmu::ExtensionReg& _reg )
-	: name( _name ), reg( _reg )
+Attachment::Attachment( const char* const _name ) : name( _name )
 {
-	memset(id, 0, sizeof(id));
-	memset(calibration, 0, sizeof(calibration));
+	reg.resize( WIIMOTE_REG_EXT_SIZE, 0);
 }
 
-None::None( WiimoteEmu::ExtensionReg& _reg ) : Attachment( "None", _reg )
+None::None() : Attachment( "None" )
 {
 	// set up register
 	memcpy(&id, nothing_id, sizeof(nothing_id));
@@ -34,9 +32,8 @@ std::string Attachment::GetName() const
 void Attachment::Reset()
 {
 	// set up register
-	memset( &reg, 0, WIIMOTE_REG_EXT_SIZE );
-	memcpy( &reg.constant_id, id, sizeof(id) );
-	memcpy( &reg.calibration, calibration, sizeof(calibration) );
+	reg.clear();
+	reg.resize( WIIMOTE_REG_EXT_SIZE, 0);
 }
 
 }
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Attachment.h b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Attachment.h
index dfa292b..7eb937a 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Attachment.h
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Attachment.h
@@ -14,14 +14,14 @@ namespace WiimoteEmu
 class Attachment : public ControllerEmu
 {
 public:
-	Attachment( const char* const _name, WiimoteEmu::ExtensionReg& _reg );
+	Attachment( const char* const _name );
 
 	virtual void GetState( u8* const data, const bool focus = true ) {}
 	void Reset();
 	std::string GetName() const;
 
 	const char*	const			name;
-	WiimoteEmu::ExtensionReg&	reg;
+	std::vector<u8>             reg;
 
 	u8 id[6];
 	u8 calibration[0x10];
@@ -30,7 +30,7 @@ public:
 class None : public Attachment
 {
 public:
-	None( WiimoteEmu::ExtensionReg& _reg );
+	None();
 };
 
 }
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Classic.cpp b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Classic.cpp
index 5b0d962..4f92c36 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Classic.cpp
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Classic.cpp
@@ -50,10 +50,10 @@ static const char* const classic_trigger_names[] =
 
 static const u16 classic_dpad_bitmasks[] =
 {
-	Classic::PAD_UP, Classic::PAD_DOWN, Classic::PAD_LEFT, Classic::PAD_RIGHT 
+	Classic::PAD_UP, Classic::PAD_DOWN, Classic::PAD_LEFT, Classic::PAD_RIGHT
 };
 
-Classic::Classic(WiimoteEmu::ExtensionReg& _reg) : Attachment(_trans("Classic"), _reg)
+Classic::Classic() : Attachment(_trans("Classic"))
 {
 	// buttons
 	groups.push_back(m_buttons = new Buttons("Buttons"));
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Classic.h b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Classic.h
index ac68b2a..852eb74 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Classic.h
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Classic.h
@@ -10,7 +10,7 @@ namespace WiimoteEmu
 class Classic : public Attachment
 {
 public:
-	Classic(WiimoteEmu::ExtensionReg& _reg);
+	Classic();
 	void GetState( u8* const data, const bool focus );
 
 	enum
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Drums.cpp b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Drums.cpp
index 1ae774b..2846fb6 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Drums.cpp
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Drums.cpp
@@ -32,7 +32,7 @@ static const u16 drum_button_bitmasks[] =
 	Drums::BUTTON_PLUS,
 };
 
-Drums::Drums(WiimoteEmu::ExtensionReg& _reg) : Attachment(_trans("Drums"), _reg)
+Drums::Drums() : Attachment(_trans("Drums"))
 {
 	// pads
 	groups.push_back(m_pads = new Buttons(_trans("Pads")));
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Drums.h b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Drums.h
index 3771140..fc0d485 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Drums.h
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Drums.h
@@ -10,7 +10,7 @@ namespace WiimoteEmu
 class Drums : public Attachment
 {
 public:
-	Drums(WiimoteEmu::ExtensionReg& _reg);
+	Drums();
 	void GetState( u8* const data, const bool focus );
 
 	enum
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Guitar.cpp b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Guitar.cpp
index 23a2f93..8097c08 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Guitar.cpp
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Guitar.cpp
@@ -36,7 +36,7 @@ static const u16 guitar_strum_bitmasks[] =
 	Guitar::BAR_DOWN,
 };
 
-Guitar::Guitar(WiimoteEmu::ExtensionReg& _reg) : Attachment(_trans("Guitar"), _reg)
+Guitar::Guitar() : Attachment(_trans("Guitar"))
 {
 	// frets
 	groups.push_back(m_frets = new Buttons(_trans("Frets")));
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Guitar.h b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Guitar.h
index 245ebab..8d1e718 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Guitar.h
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Guitar.h
@@ -10,7 +10,7 @@ namespace WiimoteEmu
 class Guitar : public Attachment
 {
 public:
-	Guitar(WiimoteEmu::ExtensionReg& _reg);
+	Guitar();
 	void GetState( u8* const data, const bool focus );
 
 	enum
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Nunchuk.cpp b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Nunchuk.cpp
index 63dd485..5dbf285 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Nunchuk.cpp
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Nunchuk.cpp
@@ -10,29 +10,14 @@
 namespace WiimoteEmu
 {
 
-static const u8 nunchuck_id[] = { 0x00, 0x00, 0xa4, 0x20, 0x00, 0x00 };
-/* Default calibration for the nunchuck. It should be written to 0x20 - 0x3f of the
-   extension register. 0x80 is the neutral x and y accelerators and 0xb3 is the
-   neutral z accelerometer that is adjusted for gravity. */
-static const u8 nunchuck_calibration[] =
-{
-	0x80, 0x80, 0x80, 0x00, // accelerometer x, y, z neutral
-	0xb3, 0xb3, 0xb3, 0x00, //  x, y, z g-force values 
-
-	// 0x80 = analog stick x and y axis center
-	0xff, 0x00, 0x80,
-	0xff, 0x00, 0x80,
-	0xec, 0x41 // checksum on the last two bytes
-};
-
 static const u8 nunchuk_button_bitmasks[] =
 {
 	Nunchuk::BUTTON_C,
 	Nunchuk::BUTTON_Z,
 };
 
-Nunchuk::Nunchuk(UDPWrapper *wrp, WiimoteEmu::ExtensionReg& _reg)
-	: Attachment(_trans("Nunchuk"), _reg) , m_udpWrap(wrp)
+Nunchuk::Nunchuk(UDPWrapper *wrp)
+	: Attachment(_trans("Nunchuk")) , m_udpWrap(wrp)
 {
 	// buttons
 	groups.push_back(m_buttons = new Buttons("Buttons"));
@@ -49,7 +34,7 @@ Nunchuk::Nunchuk(UDPWrapper *wrp, WiimoteEmu::ExtensionReg& _reg)
 	groups.push_back(m_tilt = new Tilt("Tilt"));
 
 	// shake
-	groups.push_back(m_shake = new Buttons("Shake"));
+	groups.push_back(m_shake = new Buttons("Shake", true));
 	m_shake->controls.push_back(new ControlGroup::Input("X"));
 	m_shake->controls.push_back(new ControlGroup::Input("Y"));
 	m_shake->controls.push_back(new ControlGroup::Input("Z"));
@@ -66,98 +51,66 @@ Nunchuk::Nunchuk(UDPWrapper *wrp, WiimoteEmu::ExtensionReg& _reg)
 
 void Nunchuk::GetState(u8* const data, const bool focus)
 {
-	wm_extension* const ncdata = (wm_extension*)data;
-	ncdata->bt = 0;
-
-	// stick
-	ControlState state[2];
-	m_stick->GetState(&state[0], &state[1], 0, 1);
-
-	nu_cal &cal = *(nu_cal*)&reg.calibration;
-	nu_js cal_js[2];
-	cal_js[0] = *&cal.jx;
-	cal_js[1] = *&cal.jy;
-
-	for (int i = 0; i < 2; i++) {
-		ControlState &s = *&state[i];
-		nu_js c = *&cal_js[i];
-		if (s < 0)
-			s = s * abs(c.min - c.center) + c.center;
-		else if (s > 0)
-			s = s * abs(c.max - c.center) + c.center;
-		else
-			s = c.center;
-	}
-
-	ncdata->jx = u8(trim(state[0]));
-	ncdata->jy = u8(trim(state[1]));
-
-	if (ncdata->jx != cal.jx.center || ncdata->jy != cal.jy.center)
-	{
-		if (ncdata->jy == cal.jy.center)
-			ncdata->jy = cal.jy.center + 1;
-		if (ncdata->jx == cal.jx.center)
-			ncdata->jx = cal.jx.center + 1;
-	}
-
-	if (!focus)
-	{
-		ncdata->jx = cal.jx.center;
-		ncdata->jy = cal.jy.center;
-	}
-
-	AccelData accel;
-	accel_cal* calib = (accel_cal*)&reg.calibration[0];
-
-	// tilt
-	EmulateTilt(&accel, m_tilt, focus);
-
-	if (focus)
-	{
-		// swing
-		EmulateSwing(&accel, m_swing);
-		// shake
-		EmulateShake(&accel, calib, m_shake, m_shake_step);
-		// buttons
-		m_buttons->GetState(&ncdata->bt, nunchuk_button_bitmasks);
-	}
-	
-	// flip the button bits :/
-	ncdata->bt ^= 0x03;
-	
-	if (m_udpWrap->inst)
-	{
-		if (m_udpWrap->updNun)
-		{
-			u8 mask;
-			float x, y;
-			m_udpWrap->inst->getNunchuck(x, y, mask);
-			// buttons
-			if (mask & UDPWM_NC)
-				ncdata->bt &= ~WiimoteEmu::Nunchuk::BUTTON_C;
-			if (mask & UDPWM_NZ)
-				ncdata->bt &= ~WiimoteEmu::Nunchuk::BUTTON_Z;
-			// stick
-			if (ncdata->jx == 0x80 && ncdata->jy == 0x80)
-			{
-				ncdata->jx = u8(0x80 + x*127);
-				ncdata->jy = u8(0x80 + y*127);
-			}
-		}
-		if (m_udpWrap->updNunAccel)
-		{
-			float x, y, z;
-			m_udpWrap->inst->getNunchuckAccel(x, y, z);
-			accel.x = x;
-			accel.y = y;
-			accel.z = z;
-		}	
-	}
-
-	wm_accel* dt = (wm_accel*)&ncdata->ax;
-	dt->x = u8(trim(accel.x * (calib->one_g.x - calib->zero_g.x) + calib->zero_g.x));
-	dt->y = u8(trim(accel.y * (calib->one_g.y - calib->zero_g.y) + calib->zero_g.y));
-	dt->z = u8(trim(accel.z * (calib->one_g.z - calib->zero_g.z) + calib->zero_g.z));
+    wm_nc* const ncdata = (wm_nc*)data;
+    memset(&ncdata->bt, 0, sizeof(ncdata->bt));
+
+    // stick / not using calibration data for stick, o well
+    m_stick->GetState(&ncdata->jx, &ncdata->jy, 0x80, focus ? 127 : 0);
+
+    accel_cal* calib = (accel_cal*)&reg[0x20];
+
+    // tilt
+    EmulateTilt(&m_accel, m_tilt, focus);
+
+    if (focus)
+    {
+        // swing
+        EmulateSwing(&m_accel, m_swing);
+        // shake
+        EmulateShake(&m_accel, calib, m_shake, m_shake_step);
+        // buttons
+        m_buttons->GetState((u8*)&ncdata->bt, nunchuk_button_bitmasks);
+    }
+
+    // flip the button bits :/
+    *(u8*)&ncdata->bt ^= 0x03;
+
+    if (m_udpWrap->inst)
+    {
+        if (m_udpWrap->updNun)
+        {
+            u8 mask;
+            float x, y;
+            m_udpWrap->inst->getNunchuck(x, y, mask);
+            // buttons
+            if (mask & UDPWM_NC)
+                ncdata->bt.c = 0;
+            if (mask & UDPWM_NZ)
+                ncdata->bt.z = 0;
+            // stick
+            if (ncdata->jx == 0x80 && ncdata->jy == 0x80)
+            {
+                ncdata->jx = u8(0x80 + x*127);
+                ncdata->jy = u8(0x80 + y*127);
+            }
+        }
+        if (m_udpWrap->updNunAccel)
+        {
+            float x, y, z;
+            m_udpWrap->inst->getNunchuckAccel(x, y, z);
+            m_accel.x = x;
+            m_accel.y = y;
+            m_accel.z = z;
+        }
+    }
+
+    wm_accel* dt = (wm_accel*)&ncdata->ax;
+    //dt->x = u8(Common::trim8(m_accel.x * (calib->one_g.x - calib->zero_g.x) + calib->zero_g.x));
+    dt->x = u8(m_accel.x * (calib->one_g.x - calib->zero_g.x) + calib->zero_g.x <= 0 ? 0 : (m_accel.x * (calib->one_g.x - calib->zero_g.x) + calib->zero_g.x >= 0xff ? 0xff : m_accel.x * (calib->one_g.x - calib->zero_g.x) + calib->zero_g.x));
+    //dt->y = u8(Common::trim8(m_accel.y * (calib->one_g.y - calib->zero_g.y) + calib->zero_g.y));
+    dt->y = u8(m_accel.y * (calib->one_g.y - calib->zero_g.y) + calib->zero_g.y <= 0 ? 0 : (m_accel.y * (calib->one_g.y - calib->zero_g.y) + calib->zero_g.y >= 0xff ? 0xff : m_accel.y * (calib->one_g.y - calib->zero_g.y) + calib->zero_g.y));
+    //dt->z = u8(Common::trim8(m_accel.z * (calib->one_g.z - calib->zero_g.z) + calib->zero_g.z));
+    dt->z = u8(m_accel.z * (calib->one_g.z - calib->zero_g.z) + calib->zero_g.z <= 0 ? 0 : (m_accel.z * (calib->one_g.z - calib->zero_g.z) + calib->zero_g.z >= 0xff ? 0xff : m_accel.z * (calib->one_g.z - calib->zero_g.z) + calib->zero_g.z));
 }
 
 void Nunchuk::LoadDefaults(const ControllerInterface& ciface)
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Nunchuk.h b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Nunchuk.h
index 3b720f7..904ecab 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Nunchuk.h
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Nunchuk.h
@@ -12,10 +12,24 @@ class UDPWrapper;
 namespace WiimoteEmu
 {
 
+static const u8 nunchuck_id[] = { 0x00, 0x00, 0xa4, 0x20, 0x00, 0x00 };
+
+/* Default calibration for the nunchuck. It should be written to 0x20 - 0x3f of the
+   extension register. 0x80 is the neutral x and y accelerators and 0xb3 is the
+   neutral z accelerometer that is adjusted for gravity. */
+static const u8 nunchuck_calibration[] =
+{
+	0x80,0x80,0x80,0x00, // accelerometer x, y, z neutral
+	0x9a,0x9a,0x9a,0x00, //  x, y, z g-force values
+
+	0xff, 0x00, 0x80, 0xff, // 0xff max, 0x00 min, 0x80 = analog stick x and y axis center
+	0x00, 0x80, 0xa1, 0xf6	// checksum on the last two bytes
+};
+
 class Nunchuk : public Attachment
 {
 public:
-	Nunchuk(UDPWrapper * wrp, WiimoteEmu::ExtensionReg& _reg);
+	Nunchuk(UDPWrapper * wrp);
 
 	virtual void GetState( u8* const data, const bool focus );
 
@@ -27,6 +41,8 @@ public:
 
 	void LoadDefaults(const ControllerInterface& ciface);
 
+	AccelData		m_accel;
+
 private:
 	Tilt*			m_tilt;
 	Force*			m_swing;
@@ -37,7 +53,7 @@ private:
 	AnalogStick*	m_stick;
 
 	u8	m_shake_step[3];
-	
+
 	UDPWrapper* const m_udpWrap;
 };
 
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Turntable.cpp b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Turntable.cpp
index 901ad27..fe0c390 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Turntable.cpp
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Turntable.cpp
@@ -30,7 +30,7 @@ static const char* const turntable_button_names[] =
 	"-", "+", _trans("Euphoria"),
 };
 
-Turntable::Turntable(WiimoteEmu::ExtensionReg& _reg) : Attachment(_trans("Turntable"), _reg)
+Turntable::Turntable() : Attachment(_trans("Turntable"))
 {
 	// buttons
 	groups.push_back(m_buttons = new Buttons("Buttons"));
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Turntable.h b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Turntable.h
index b2c73e1..cc2a07f 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Turntable.h
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/Attachment/Turntable.h
@@ -10,7 +10,7 @@ namespace WiimoteEmu
 class Turntable : public Attachment
 {
 public:
-	Turntable(WiimoteEmu::ExtensionReg& _reg);
+	Turntable();
 	void GetState(u8* const data, const bool focus);
 
 	enum
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/Src/HW/WiimoteEmu/EmuSubroutines.cpp
index 235d5cb..4fe565f 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/EmuSubroutines.cpp
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/EmuSubroutines.cpp
@@ -23,167 +23,351 @@
 #include "Common.h"
 #include "FileUtil.h"
 
+#include "../Wiimote.h"
 #include "WiimoteEmu.h"
 #include "WiimoteHid.h"
 #include "../WiimoteReal/WiimoteReal.h"
 
 #include "Attachment/Attachment.h"
+#include "Attachment/Nunchuk.h"
 
 namespace WiimoteEmu
 {
 
+Wiimote* spy_wm = 0;
+
+struct last_report_
+{
+	u8 type;
+	u8 data[MAX_PAYLOAD];
+};
+
 void Spy(Wiimote* wm_, const void* data_, int size_)
 {
 #if 0
+	if (size_ <= 0 || size_ > MAX_PAYLOAD)
+		return;
+
 	// enable log
-	bool logCom = true;
-	bool logData = true;
-	bool logMP = false;
-	bool logAudio = false;
+	bool log_com = true;
+	bool log_data = true;
+	bool log_mp = false;
+	bool log_audio = false;
 
-	std::string Name, TmpData;
-	static int c;
-	int size;
-	u16 SampleValue;
-	bool AudioData = false;
-	bool DataReport = false;
-	static std::queue<u32> dataRep;
-	static u8 dataReply[3] = {0};
-	bool emu = wm_;
-	static Wiimote* wm = 0;
+	// save data
+	bool save = false;
 
-	// a container for f.e. the extension encryption key
-	if (!wm_ && !wm)
-		wm = new Wiimote(0);
-	else
-		wm = wm_;
+	// use empty container or emulated Wiimote 0
+	bool empty_container = false;
 
-	// ignore emulated Wiimote data
-	if (emu) return;
+	static Wiimote* wm_emu;
 
-	const hid_packet* const hidp = (hid_packet*)data_;
-	const wm_report* const sr = (wm_report*)hidp->data;
+	if (!spy_wm)
+	{
+		wm_emu = 0;
+
+		if (empty_container)
+			spy_wm = new Wiimote(0);
+		else
+			if (wm_)
+				spy_wm = wm_;
+	}
+
+	if (!spy_wm)
+		return;
+
+	if (wm_)
+		wm_emu = wm_;
+
+	// short name
+	Wiimote* wm = &*spy_wm;
+
+	bool audio_data = false;
+	bool data_report = false;
+
+	bool emu = wm_;
+	string com = "", formatted = "";
+	static int c;
+	u8 report_mode = 0;
+	u16 reg_type = 0;
+	static wm_read_data g_rd;
+
+	// last report
+	static last_report_ last_report_emu;
+	static last_report_ last_report_real;
+	last_report_ &last_data = emu ? last_report_emu : last_report_real;
 
 	// use a non-pointer array because that makes read syntax shorter
-	u8 data[32] = {};
+	u8 data[MAX_PAYLOAD] = {};
 	memcpy(data, data_, size_);
 
-	switch(data[1])
+	const hid_packet* const hidp = (hid_packet*)data;
+	const wm_report* const sr = (wm_report*)hidp->data;
+
+	// ignore emulated Wiimote data
+	//if (emu) return;
+
+	if (g_wiimote_sources[0]
+		&& WIIMOTE_SRC_HYBRID == g_wiimote_sources[0]
+		&& !emu)
+		return;
+
+	// log controls
+	static bool key_down[0xff] = {false};
+	static int modify = 0;
+
+	if (!key_down[VK_PRIOR] && GetAsyncKeyState(VK_PRIOR))
+	{
+		log_data = !log_data; key_down[VK_PRIOR] = true;
+		WARN_LOG(CONSOLE, "log_data: %d", log_data);
+	}
+	if (!GetAsyncKeyState(VK_PRIOR)) key_down[VK_PRIOR] = false;
+
+	if (!key_down[VK_NEXT] && GetAsyncKeyState(VK_NEXT))
+	{
+		log_mp = !log_mp;
+		key_down[VK_NEXT] = true;
+		WARN_LOG(CONSOLE, "log_mp: %d", log_mp);
+	}
+	if (!GetAsyncKeyState(VK_NEXT)) key_down[VK_NEXT] = false;
+
+	if (!key_down[VK_INSERT] && GetAsyncKeyState(VK_INSERT))
+	{
+		modify++;
+		if (modify > 2)
+			modify = 0;
+		key_down[VK_INSERT] = true;
+		WARN_LOG(CONSOLE, "Modify: %d", modify);
+	}
+	if (!GetAsyncKeyState(VK_INSERT)) key_down[VK_INSERT] = false;
+
+	if (!key_down[VK_HOME] && GetAsyncKeyState(VK_HOME))
+	{
+		wm->m_options->settings[3]->value = !wm->GetMotionPlusAttached();
+		key_down[VK_HOME] = true;
+		WARN_LOG(CONSOLE, "M+: %f", wm->m_options->settings[3]->value);
+	}
+	if (!GetAsyncKeyState(VK_HOME)) key_down[VK_HOME] = false;
+
+
+	if (!key_down[VK_END] && GetAsyncKeyState(VK_END))
+	{
+		wm->m_extension->switch_extension = wm->m_extension->active_extension != 1 ? 1 : 0; key_down[VK_END] = true;
+		WARN_LOG(CONSOLE, "NC: %d (%d)", wm->m_extension->switch_extension, wm->m_extension->active_extension);
+	}
+	if(!GetAsyncKeyState(VK_END)) key_down[VK_END] = false;
+
+	if(!key_down[VK_DELETE] && GetAsyncKeyState(VK_DELETE)) {
+		wm->m_options->settings[SETTING_IR_HIDE]->value = wm->m_options->settings[SETTING_IR_HIDE]->value != 1 ? 1 : 0;
+		key_down[VK_DELETE] = true;
+		WARN_LOG(CONSOLE, "IR: %d", wm->m_options->settings[SETTING_IR_HIDE]->value);
+	}
+	if(!GetAsyncKeyState(VK_DELETE)) key_down[VK_DELETE] = false;
+
+	//WARN_LOG_S(CONSOLE, "Data: %s", ArrayToString((const u8*)data_, Size, 0, 30).c_str());
+
+
+	// print data
+	//DEBUG_LOG(CONSOLE, "DATA: %s", ArrayToString((u8*)data_, Size, 0, 30).c_str());
+	//DEBUG_LOG(CONSOLE, "E: %s", ArrayToString(((u8*)&wm->m_reg_ext), sizeof(tmp), 0, 30).c_str());
+	//DEBUG_LOG(CONSOLE, "+: %s", ArrayToString(((u8*)&wm->m_reg_motion_plus)[0xfa], 6, 0, 30).c_str());
+	//DEBUG_LOG(CONSOLE, "+: %s", ArrayToString(((u8*)&wm->m_reg_motion_plus), sizeof(wm->m_reg_motion_plus), 0, 30).c_str());
+	//DEBUG_LOG(CONSOLE, "E: %s", ArrayToString(((u8*)&wm->m_reg_ext.constant_id), sizeof(wm->m_reg_ext.constant_id)).c_str());
+	//DEBUG_LOG(CONSOLE, "+: %s", ArrayToString(((u8*)&wm->m_reg_motion_plus.ext_identifier), sizeof(wm->m_reg_motion_plus.ext_identifier)).c_str());
+
+	// modify report
+	if (modify)
+	{
+		if (sr->wm >= WM_REPORT_CORE && sr->wm <= WM_REPORT_CORE_ACCEL_IR10_EXT6)
+		{
+			if (sr->wm == WM_REPORT_CORE_ACCEL_IR10_EXT6 && ((wm_motionplus*)&sr->data[15])->is_mp_data)
+			{
+				//ERROR_LOG_S(CONSOLE, "1 | %s", ArrayToString(sr->data, size).c_str());
+				accel_cal* calib = (accel_cal*)&wm->m_eeprom[0x16];
+
+				if (modify == 1)
+				{
+					((wm_accel*)&sr->data[2])->x = calib->zero_g.x; ((wm_core*)&sr->data[0])->xL = calib->zero_g.xL;
+					((wm_accel*)&sr->data[2])->y = calib->zero_g.y; ((wm_core*)&sr->data[0])->yL = calib->zero_g.yL;
+					((wm_accel*)&sr->data[2])->z = calib->one_g.z; ((wm_core*)&sr->data[0])->zL = calib->one_g.zL;
+				}
+				if (modify == 2)
+				{
+					((wm_motionplus*)&sr->data[15])->yaw2 = 0x1f; ((wm_motionplus*)&sr->data[15])->yaw1 = 0x7f;
+					((wm_motionplus*)&sr->data[15])->roll2 = 0x1f; ((wm_motionplus*)&sr->data[15])->roll1 = 0x7f;
+					((wm_motionplus*)&sr->data[15])->pitch2 = 0x1f; ((wm_motionplus*)&sr->data[15])->pitch1 = 0x7f;
+				}
+				//ERROR_LOG_S(CONSOLE, "2 | %s", ArrayToString(sr->data, size).c_str());
+			}
+		}
+	}
+
+	switch(sr->wm)
 	{
 	case WM_RUMBLE:
-		size = 1;
-		if (logCom) Name.append("WM_RUMBLE");
+		if (log_com) com.append("WM_RUMBLE");
 		break;
 
 	case WM_LEDS:
-		size = sizeof(wm_leds);
-		if (logCom) Name.append("WM_LEDS");
+		if (log_com) com.append("WM_LEDS");
 		break;
 
 	case WM_REPORT_MODE:
-		size = sizeof(wm_report_mode);
-		if (logCom) Name.append("WM_REPORT_MODE");
-		ERROR_LOG(CONSOLE, "WM_REPORT_MODE: 0x%02x", data[3]);
+		if (log_com)
+		{
+			com.append("WM_REPORT_MODE");
+			formatted = StringFromFormat("mode: 0x%02x", data[3]);
+		}
+		wm->m_reporting_mode = data[3];
 		break;
 
 	case WM_IR_PIXEL_CLOCK:
-		if (logCom) Name.append("WM_IR_PIXEL_CLOCK");
+		if (log_com) com.append("WM_IR_PIXEL_CLOCK");
 		break;
 
 	case WM_SPEAKER_ENABLE:
-		if (logCom) Name.append("WM_SPEAKER_ENABLE");
-		NOTICE_LOG(CONSOLE, "Speaker on: %d", sr->enable);
+		if (log_com)
+		{
+			com.append("WM_SPEAKER_ENABLE");
+			formatted = StringFromFormat("on: %d", sr->enable);
+		}
 		break;
 
 	case WM_REQUEST_STATUS:
-		size = sizeof(wm_request_status);
-		if (logCom) Name.append("WM_REQUEST_STATUS");
-		NOTICE_LOG(CONSOLE, "WM_REQUEST_STATUS: %s", ArrayToString(data, size+2, 0).c_str());
+	{
+		wm_request_status* status = (wm_request_status*)sr->data;
+		if (log_com)
+		{
+			com.append("WM_REQUEST_STATUS");
+			formatted.append(StringFromFormat("rumble %d", status->rumble));
+		}
 		break;
+	}
 
 	case WM_WRITE_DATA:
 	{
-		if (logCom) Name.append("W 0x16");
-		size = sizeof(wm_write_data);
-
 		wm_write_data* wd = (wm_write_data*)sr->data;
+		//memcpy(wd, &data[7], 0x10);
 		u32 address = Common::swap24(wd->address);
 		address &= ~0x010000;
+		reg_type;
+
+		if (log_com)
+		{
+			com.append("WRITE");
+			formatted.append(StringFromFormat("%02x %08x %02x: %s\n", wd->space, address, wd->size, ArrayToString(wd->data, wd->size, 0).c_str()));
+		}
 
-		switch(data[2] >> 0x01)
+		switch(wd->space)
 		{
 		case WM_SPACE_EEPROM:
-			if (logCom) Name.append(" REG_EEPROM"); break;
-		case WM_SPACE_REGS1:
-		case WM_SPACE_REGS2: {
+			if (log_com) com.append(" REG_EEPROM");
+			if (save)
+				memcpy(wm->m_eeprom + address, wd->data, wd->size);
+			break;
 
+		case WM_SPACE_REGS1:
+		case WM_SPACE_REGS2:
+		{
+			// reg
+			reg_type = address >> 16;
 			const u8 region_offset = (u8)address;
 			void *region_ptr = NULL;
 			int region_size = 0;
 
-			switch(data[3])
+			switch(reg_type)
+			{
+			case WIIMOTE_REG_SPEAKER :
+				if (log_com) com.append(" REG_SPEAKER");
+				region_ptr = &wm->m_reg_speaker;
+				region_size = WIIMOTE_REG_SPEAKER_SIZE;
+				break;
+
+			case WIIMOTE_REG_EXT:
+				if (log_com) com.append(" REG_EXT");
+				region_ptr = wm->GetMotionPlusActive() ? (void*)&wm->m_reg_motion_plus : (void*)&wm->m_reg_ext;
+				region_size = WIIMOTE_REG_EXT_SIZE;
+				break;
+
+			case WIIMOTE_REG_MP :
+				if (log_com) com.append(" REG_M+");
+				if (!wm->GetMotionPlusActive())
+				{
+					region_ptr = &wm->m_reg_motion_plus;
+					region_size = WIIMOTE_REG_EXT_SIZE;
+				}
+				break;
+
+			case WIIMOTE_REG_IR:
+				 if (log_com) com.append(" REG_IR");
+				region_ptr = &wm->m_reg_ir;
+				region_size = WIIMOTE_REG_IR_SIZE;
+				 break;
+			}
+
+			// save register
+			if (save && region_ptr && (region_offset + wd->size <= region_size))
+				memcpy((u8*)region_ptr + region_offset, wd->data, wd->size);
+
+			// save key
+			if (save && region_ptr == &wm->m_reg_ext && address >= 0xa40040 && address <= 0xa4004c)
+			{
+				wiimote_gen_key(&wm->m_ext_key, wm->m_reg_ext.encryption_key);
+				formatted.append(StringFromFormat("writing key: %s", ArrayToString((u8*)&wm->m_ext_key, sizeof(wm->m_ext_key), 0, 30).c_str()));
+			}
+
+			switch(wd->space)
+			{
+			case WM_SPACE_REGS1:
+			case WM_SPACE_REGS2:
+			{
+			switch(reg_type)
 			{
-			case 0xa2:
-				if (logCom)
+			case WIIMOTE_REG_SPEAKER:
+				if (log_com)
 				{
-					Name.append(" REG_SPEAKER");
-					if(data[6] == 7)
+					com.append(" REG_SPEAKER");
+					if(wd->data[0] == 7)
 					{
-						//INFO_LOG(CONSOLE, "Sound configuration:");
-						if(data[8] == 0x00)
+						u16 sampleValue;
+
+						formatted.append("Sound configuration\n");
+						if (wd->data[2] == 0x00)
 						{
-							//memcpy(&SampleValue, &data[9], 2);
-							//NOTICE_LOG(CONSOLE, "    Data format: 4-bit ADPCM (%i Hz)", 6000000 / SampleValue);
-							//NOTICE_LOG(CONSOLE, "    Volume: %02i%%", (data[11] / 0x40) * 100);
+							memcpy(&sampleValue, &data[9], 2);
+							formatted.append(StringFromFormat(
+								"    format: 4-bit ADPCM (%i Hz)"
+								"    volume: %02i%%"
+
+								, 6000000 / sampleValue
+								, (data[11] / 0x40) * 100
+								));
 						}
-						else if (data[8] == 0x40)
+						else if (wd->data[2] == 0x40)
 						{
-							//memcpy(&SampleValue, &data[9], 2);
-							//NOTICE_LOG(CONSOLE, "    Data format: 8-bit PCM (%i Hz)", 12000000 / SampleValue);
-							//NOTICE_LOG(CONSOLE, "    Volume: %02i%%", (data[11] / 0xff) * 100);
+							memcpy(&sampleValue, &data[9], 2);
+							formatted.append(StringFromFormat(
+							"    format: 8-bit PCM (%i Hz)"
+							"    volume: %02i%%"
+
+							, 12000000 / sampleValue
+							, (data[11] / 0xff) * 100
+							));
 						}
 					}
 				}
 				break;
-			case 0xa4:
-				if (logCom) Name.append(" REG_EXT");
-				// Update the encryption mode
-				if (data[5] == 0xf0) {
-					if (!emu)
-						wm->m_reg_ext.encryption = wd->data[0];
-					//NOTICE_LOG(CONSOLE, "Extension encryption: %u", wm->m_reg_ext.encryption);
-				}
-				region_ptr = &wm->m_reg_ext;
-				break;
-			case 0xa6 :
-				if (logCom) Name.append(" REG_M+");
-				// update the encryption mode
-				if (data[5] == 0xf0) {
-					if (!emu)
-						wm->m_reg_motion_plus.activated = wd->data[0];
-					//NOTICE_LOG(CONSOLE, "Extension enryption: %u", wm->m_reg_ext.encryption);
-				}
-				region_ptr = &wm->m_reg_motion_plus;
+
+				case WIIMOTE_REG_EXT:
+				case WIIMOTE_REG_MP :
+					if (data[5] == 0xf0)
+						formatted.append(StringFromFormat("extension encryption: %02x", wm->m_reg_ext.encryption));
+					break;
+
 				break;
-			case 0xb0:
-				 if (logCom) Name.append(" REG_IR"); break;
 			}
-
-			// save register
-			if (!emu && region_ptr)
-				memcpy((u8*)region_ptr + region_offset, wd->data, wd->size);
-			// save key
-			if (region_offset >= 0x40 && region_offset <= 0x4c) {
-				if(!emu)
-					wiimote_gen_key(&wm->m_ext_key, wm->m_reg_ext.encryption_key);
-				INFO_LOG(CONSOLE, "Writing key: %s", ArrayToString((u8*)&wm->m_ext_key, sizeof(wm->m_ext_key), 0, 30).c_str());
 			}
-			if (data[3] == 0xa4 || data[3] == 0xa6) {
-				//DEBUG_LOG(CONSOLE, "M+: %s", ArrayToString((u8*)&wm->m_reg_motion_plus, sizeof(wm->m_reg_motion_plus), 0, 30).c_str());
-				//DEBUG_LOG(CONSOLE, "M+: %s", ArrayToString((u8*)&wm->m_reg_motion_plus.ext_identifier, sizeof(wm->m_reg_motion_plus.ext_identifier), 0, 30).c_str());
-				NOTICE_LOG(CONSOLE, "W[0x%02x 0x%02x|%d]: %s", data[3], region_offset,  wd->size, ArrayToString(wd->data, wd->size, 0).c_str());
 			}
-			break;
 		}
 		}
 
@@ -192,495 +376,654 @@ void Spy(Wiimote* wm_, const void* data_, int size_)
 
 	case WM_READ_DATA:
 	{
-		if (logCom) Name.append("R");
-		size = sizeof(wm_read_data);
-
 		wm_read_data* rd = (wm_read_data*)sr->data;
 		u32 address = Common::swap24(rd->address);
-		u8 addressLO = address & 0xFFFF;
 		address &= 0xFEFFFF;
-		u16 size = Common::swap16(rd->size);
-		u8 *const block = new u8[size];
+		rd->size = Common::swap16(rd->size);
+		u8 *const block = new u8[rd->size];
+
 		void *region_ptr = NULL;
+		int region_size = 0;
 
-		dataRep.push(((data[2]>>1)<<16) + ((data[3])<<8) + addressLO);
+		g_rd = *rd;
 
-		switch(data[2]>>1)
+		if (log_com)
+		{
+			com.append("READ");
+			formatted.append(StringFromFormat("%02x %06x %04x", rd->space, address, rd->size));
+		}
+
+		switch(rd->space)
 		{
 		case WM_SPACE_EEPROM:
-			if (logCom) Name.append(" REG_EEPROM");
-			if (!emu)
-				memcpy(block, wm->m_eeprom + address, size);
+			if (log_com) com.append(" REG_EEPROM");
+			if (save)
+				memcpy(block, wm->m_eeprom + address, rd->size);
 			break;
+
 		case WM_SPACE_REGS1:
 		case WM_SPACE_REGS2:
-			// ignore second byte for extension area
-			if (address>>16 == 0xA4) address &= 0xFF00FF;
+
+			if (address >> 16 == 0xA4)
+				address &= 0xFF00FF;
+			reg_type = address >> 16;
+
 			const u8 region_offset = (u8)address;
-			switch(data[3])
+
+			switch(reg_type)
 			{
-			case 0xa2:
-				if (logCom) Name.append(" REG_SPEAKER");
+			case WIIMOTE_REG_SPEAKER:
+				if (log_com) com.append(" REG_SPEAKER");
 				region_ptr = &wm->m_reg_speaker;
+				region_size = WIIMOTE_REG_SPEAKER_SIZE;
 				break;
-			case 0xa4:
-				 if (logCom) Name.append(" REG_EXT");
-				 region_ptr = &wm->m_reg_motion_plus;
-				 break;
-			case 0xa6:
-				 if (logCom) Name.append(" REG_M+");
-				 region_ptr = &wm->m_reg_motion_plus;
+
+			case WIIMOTE_REG_EXT:
+				if (log_com) com.append(" REG_EXT");
+				region_ptr = wm->GetMotionPlusActive() ? (void*)&wm->m_reg_motion_plus : (void*)&wm->m_reg_ext;
+				region_size = WIIMOTE_REG_EXT_SIZE;
 				break;
-			case 0xb0:
-				if (logCom) Name.append(" REG_IR");
+
+			case WIIMOTE_REG_MP:
+				if (log_com) com.append(" REG_M+");
+				if (!wm->GetMotionPlusActive())
+				{
+					region_ptr = &wm->m_reg_motion_plus;
+					region_size = WIIMOTE_REG_EXT_SIZE;
+				}
+				break;
+
+			case WIIMOTE_REG_IR:
+				if (log_com) com.append(" REG_IR");
 				region_ptr = &wm->m_reg_ir;
+				region_size = WIIMOTE_REG_IR_SIZE;
 				break;
 			}
-			//if (!emu && region_ptr)
-				//memcpy(block, (u8*)region_ptr + region_offset, size);
-			//WARN_LOG(CONSOLE, "READING[0x%02x 0x%02x|%d]: %s", data[3], region_offset, size, ArrayToString(block, size, 0, 30).c_str());
-			break;
-		}
 
+			// print data to read
+			//if (region_ptr)
+			//	memcpy(block, (u8*)region_ptr + region_offset, rd->size);
+			//LOG3(CONSOLE, "READING[0x%02x 0x%02x %d]: %s", data[3], region_offset, rd->size, ArrayToString(block, size, 0, 30).c_str());
+
+			break;
+		}}
 		break;
-	}
 
 	case WM_WRITE_SPEAKER_DATA:
-		if (logCom) Name.append("WM_SPEAKER_DATA");
-		size = 21;
+		if (log_audio)
+			com.append("WM_SPEAKER_DATA");
+		else
+			return;
 		break;
 
 	case WM_SPEAKER_MUTE:
-		if (logCom) Name.append("WM_SPEAKER");
-		size = 1;
-		NOTICE_LOG(CONSOLE, "Speaker mute: %d", sr->enable);
+		if (log_com)
+		{
+			com.append("WM_SPEAKER");
+			formatted.append(StringFromFormat("mute: %d", sr->enable));
+		}
 		break;
 
 	case WM_IR_LOGIC:
-		if (logCom) Name.append("WM_IR");
-		size = 1;
+		if (log_com) com.append("WM_IR");
 		break;
 
 	case WM_STATUS_REPORT:
-		size = sizeof(wm_status_report);
-		Name = "WM_STATUS_REPORT";
-		//INFO_LOG(CONSOLE, "WM_STATUS_REPORT: %s", ArrayToString(data, size+2, 0).c_str());
-		{
+	{
+		if (log_com) {
+			com.append("WM_STATUS_REPORT");
 			wm_status_report* pStatus = (wm_status_report*)(data + 2);
-			ERROR_LOG(CONSOLE, ""
-				"Statusreport extension: %i",
-				//"Speaker enabled: %i"
-				//"IR camera enabled: %i"
-				//"LED 1: %i\n"
-				//"LED 2: %i\n"
-				//"LED 3: %i\n"
-				//"LED 4: %i\n"
-				//"Battery low: %i\n"
-				//"Battery level: %i",
-				pStatus->extension
-				//pStatus->speaker,
-				//pStatus->ir,
-				//(pStatus->leds >> 0),
-				//(pStatus->leds >> 1),
-				//(pStatus->leds >> 2),
-				//(pStatus->leds >> 3),
-				//pStatus->battery_low,
-				//pStatus->battery
+
+			formatted = StringFromFormat(
+				"extension: %i"
+				//"speaker enabled: %i"
+				//"IR enabled: %i"
+				//"LED 1: %i"
+				//"LED 2: %i"
+				//"LED 3: %i"
+				//"LED 4: %i"
+				//"Battery low: %i"
+				//"Battery level: %i"
+				, pStatus->extension
+				//, pStatus->speaker
+				//, pStatus->ir
+				//, (pStatus->leds >> 0)
+				//, (pStatus->leds >> 1)
+				//, (pStatus->leds >> 2)
+				//, (pStatus->leds >> 3)
+				//, pStatus->battery_low
+				//, pStatus->battery
 				);
+			// Update the global (for both the real and emulated) extension settings from whatever
+			//   the real Wiimote use. We will enable the extension from the 0x21 report.
+			if(!emu && !pStatus->extension)
+			{
+				//DisableExtensions();
+				//if (m_BasicConfigFrame) m_BasicConfigFrame->UpdateGUI();
+			}
 		}
+
 		break;
+	}
 
 	case WM_READ_DATA_REPLY:
 	{
-		size = sizeof(wm_read_data_reply);
-		Name = "R_REPLY";
+		wm_read_data_reply* const rdr = (wm_read_data_reply*)(data + 2);
+		u16 address = Common::swap16(rdr->address);
+		u8 size = rdr->size + 1;
 
-		u8 data2[32];
-		memset(data2, 0, sizeof(data2));
-		memcpy(data2, data, size_);
-		wm_read_data_reply* const rdr = (wm_read_data_reply*)(data2 + 2);
+		if (address != u16(Common::swap24(g_rd.address)))
+			LOG3(COMMON, "WM_READ_DATA_REPLY address %04x is different from WM_READ_DATA address %06x", address, Common::swap24(g_rd.address));
 
-		bool decrypted = false;
-		if(!dataRep.empty())
+		g_rd.address[2] += size;
+
+		if (log_com)
 		{
-			dataReply[0] = (dataRep.front()>>16)&0x00FF;
-			dataReply[1] = (dataRep.front()>>8)&0x00FF;
-			dataReply[2] = dataRep.front()&0x00FF;
-			dataRep.pop();
+			com.append("READ_REPLY");
+			formatted.append(StringFromFormat("%02x %04x %02x %02x: %s\n", g_rd.space, address, size, rdr->error, ArrayToString(rdr->data, size, 0).c_str()));
 		}
 
-		switch(dataReply[0])
+		switch(g_rd.space)
 		{
 		case WM_SPACE_EEPROM:
-			if (logCom)
-				Name.append(" REG_EEPROM");
+			if (log_com)
+				com.append(" REG_EEPROM");
+
+			//LOG3(CONSOLE, "%s", ArrayToString(rdr->data, size).c_str());
+
+			// save
+			if (save)
+				memcpy(wm->m_eeprom + address, rdr->data, size);
+
 			// Wiimote calibration
-			if(data[4] == 0xf0 && data[5] == 0x00 && data[6] == 0x10) {
-				if(data[6] == 0x10) {
-					accel_cal* calib = (accel_cal*)&rdr->data[6];
-					ERROR_LOG(CONSOLE, "Wiimote calibration:");
-					//SERROR_LOG(CONSOLE, "%s", ArrayToString(rdr->data, rdr->size).c_str());
-					ERROR_LOG(CONSOLE, "Cal_zero.x: %i", calib->zero_g.x);
-					ERROR_LOG(CONSOLE, "Cal_zero.y: %i", calib->zero_g.y);
-					ERROR_LOG(CONSOLE, "Cal_zero.z: %i", calib->zero_g.z);
-					ERROR_LOG(CONSOLE, "Cal_g.x: %i", calib->one_g.x);
-					ERROR_LOG(CONSOLE, "Cal_g.y: %i", calib->one_g.y);
-					ERROR_LOG(CONSOLE, "Cal_g.z: %i", calib->one_g.z);
-					// save
-					if (!emu)
-						memcpy(wm->m_eeprom + 0x16, rdr->data + 6, rdr->size);
-				}
+			if (address == 0x10) {
+				accel_cal* calib = (accel_cal*)&wm->m_eeprom[0x16];
+				formatted = StringFromFormat(
+					"Wiimote calibration\n"
+					"zero x: %u\n"
+					"zero y: %u\n"
+					"zero z: %u\n"
+
+					"g x: %u\n"
+					"g y: %u\n"
+					"g z: %u"
+
+					, calib->zero_g.x
+					, calib->zero_g.y
+					, calib->zero_g.z
+
+					, calib->one_g.x
+					, calib->one_g.y
+					, calib->one_g.z
+					);
 			}
 			break;
 
 		case WM_SPACE_REGS1:
 		case WM_SPACE_REGS2:
-			switch(dataReply[1])
-			{
-			case 0xa2: if (logCom) Name.append(" REG_SPEAKER"); break;
-			case 0xa4: if (logCom) Name.append(" REG_EXT"); break;
-			case 0xa6: if (logCom) Name.append(" REG_M+"); break;
-			case 0xb0: if (logCom) Name.append(" REG_IR"); break;
-			}
-		}
-
-		// save key
-		if (!emu && rdr->address>>8 == 0x40)
 		{
-			memcpy(((u8*)&wm->m_reg_ext.encryption_key), rdr->data, rdr->size+1);
-			wiimote_gen_key(&wm->m_ext_key, wm->m_reg_ext.encryption_key);
-			NOTICE_LOG(CONSOLE, "Reading key: %s", ArrayToString(((u8*)&wm->m_ext_key), sizeof(wm->m_ext_key), 0, 30).c_str());
-		}
-
-		// select decryption
-		//if(((!wm->GetMotionPlusActive() && ((u8*)&wm->m_reg_ext)[0xf0] == 0xaa) || (wm->GetMotionPlusActive() && ((u8*)&wm->m_reg_motion_plus)[0xf0] == 0xaa)) && rdr->address>>8 < 0xf0) {
+			reg_type = Common::swap24(g_rd.address) >> 16;
+			const u8 region_offset = (u8)address;
+			void *region_ptr = NULL;
+			int region_size = 0;
 
-		//if(((((u8*)&wm->m_reg_ext)[0xf0] == 0xaa) || ((u8*)&wm->m_reg_motion_plus)[0xf0] == 0xaa) && rdr->address>>8 < 0xf0) {
+			switch(reg_type)
+			{
+			case WIIMOTE_REG_SPEAKER:
+				if (log_com) com.append(" REG_SPEAKER");
+				region_ptr = &wm->m_reg_speaker;
+				region_size = WIIMOTE_REG_SPEAKER_SIZE;
+				break;
 
-		//if(!wm->GetMotionPlusActive() && ((u8*)&wm->m_reg_ext)[0xf0] == 0xaa && rdr->address>>8 < 0xf0) {
+			case WIIMOTE_REG_EXT:
+				if (log_com) com.append(" REG_EXT");
+				region_ptr = wm->GetMotionPlusActive() ? (void*)&wm->m_reg_motion_plus : (void*)&wm->m_reg_ext;
+				region_size = WIIMOTE_REG_EXT_SIZE;
+				break;
 
-		//if(!wm->GetMotionPlusActive() && ((u8*)&wm->m_reg_ext)[0xf0] == 0xaa) {
+			case WIIMOTE_REG_MP:
+				if (log_com) com.append(" REG_M+");
+				if (!wm->GetMotionPlusActive())
+				{
+					region_ptr = &wm->m_reg_motion_plus;
+					region_size = WIIMOTE_REG_EXT_SIZE;
+				}
+				break;
 
-		//	SWARN_LOG(CONSOLE, "key %s", ArrayToString(((u8*)&wm->m_ext_key), sizeof(wm->m_ext_key), 0, 30).c_str());
-		//	SWARN_LOG(CONSOLE, "decrypt %s", ArrayToString(rdr->data, rdr->size+1, 0, 30).c_str());
-		//	wiimote_decrypt(&wm->m_ext_key, rdr->data, dataReply[2]&0xffff, rdr->size+1);
-		//	SWARN_LOG(CONSOLE, "decrypt %s", ArrayToString(rdr->data, rdr->size+1, 0, 30).c_str());
-		//	decrypted = true;
-		//}
+			case WIIMOTE_REG_IR:
+				if (log_com) com.append(" REG_IR");
+				region_ptr = &wm->m_reg_ir;
+				region_size = WIIMOTE_REG_IR_SIZE;
+				break;
+			}
 
-		// save data
-		if (!emu && !rdr->error)
-		{
-			//if (dataReply[1] == 0xa4 && wm->GetMotionPlusActive())
-				//memcpy(&((u8*)&wm->m_reg_motion_plus)[rdr->address>>8], rdr->data, rdr->size+1);
-			//if (dataReply[1] == 0xa4 && !wm->GetMotionPlusActive())
-			//if (dataReply[1] == 0xa4)
-			//	memcpy(&((u8*)&wm->m_reg_ext)[rdr->address>>8], rdr->data, rdr->size+1);
-			//if (!wm->GetMotionPlusActive() && wm->GetMotionPlusAttached())
-			//if (dataReply[1] == 0xa6)
-			//	memcpy(&((u8*)&wm->m_reg_motion_plus)[rdr->address>>8], rdr->data, rdr->size+1);
-			//INFO_LOG(CONSOLE, "Saving[0x%2x:0x%2x]: %s", dataReply[1], rdr->address>>8, ArrayToString(rdr->data, rdr->size+1).c_str());
-		}
+			if (wm->m_reg_ext.encryption == 0xaa && region_ptr == &wm->m_reg_ext) {
 
-		if (!rdr->error && rdr->address>>8 >= 0xf0 && rdr->address>>8 <= 0xff)
-		{
-			//INFO_LOG(CONSOLE, "Extension ID: %s", ArrayToString(rdr->data, rdr->size+1).c_str());
-		}
+				//formatted.append(StringFromFormat("key %s\n", ArrayToString(((u8*)&wm->m_ext_key), sizeof(wm->m_ext_key), 0, 30).c_str()));
+				formatted.append(StringFromFormat("encrypted %s\n", ArrayToString(rdr->data, size, 0, 30).c_str()));
+				wiimote_decrypt(&wm->m_ext_key, rdr->data, address, size);
+				formatted.append(StringFromFormat("decrypted %s\n", ArrayToString(rdr->data, size, 0, 30).c_str()));
+			}
 
-		// Nunchuck calibration
-		if(data[4] == 0xf0 && data[5] == 0x00 && (data[6] == 0x20 || data[6] == 0x30))
-		{
-			// log
-			//TmpData = StringFromFormat("Read[%s] (enc): %s", (Emu ? "Emu" : "Real"), ArrayToString(data, size + 2).c_str());
+			// save data
+			if (save && !rdr->error && region_ptr == &wm->m_reg_ext && (region_offset + size <= region_size))
+				memcpy((u8*)region_ptr + region_offset, rdr->data, size);
 
-			// decrypt
-			//if(((u8*)&wm->m_reg_ext)[0xf0] == 0xaa) {
-			//	wiimote_decrypt(&wm->m_ext_key, &data[0x07], 0x00, (data[4] >> 0x04) + 1);
-
-			//if (wm->m_extension->name == "NUNCHUCK") {
-			//	INFO_LOG(CONSOLE, "\nGame got the Nunchuck calibration:\n");
-			//	INFO_LOG(CONSOLE, "Cal_zero.x: %i\n", data[7 + 0]);
-			//	INFO_LOG(CONSOLE, "Cal_zero.y: %i\n", data[7 + 1]);
-			//	INFO_LOG(CONSOLE, "Cal_zero.z: %i\n",  data[7 + 2]);
-			//	INFO_LOG(CONSOLE, "Cal_g.x: %i\n", data[7 + 4]);
-			//	INFO_LOG(CONSOLE, "Cal_g.y: %i\n",  data[7 + 5]);
-			//	INFO_LOG(CONSOLE, "Cal_g.z: %i\n",  data[7 + 6]);
-			//	INFO_LOG(CONSOLE, "Js.Max.x: %i\n",  data[7 + 8]);
-			//	INFO_LOG(CONSOLE, "Js.Min.x: %i\n",  data[7 + 9]);
-			//	INFO_LOG(CONSOLE, "Js.Center.x: %i\n", data[7 + 10]);
-			//	INFO_LOG(CONSOLE, "Js.Max.y: %i\n",  data[7 + 11]);
-			//	INFO_LOG(CONSOLE, "Js.Min.y: %i\n",  data[7 + 12]);
-			//	INFO_LOG(CONSOLE, "JS.Center.y: %i\n\n", data[7 + 13]);
-			//}
-			//else // g_Config.bClassicControllerConnected {
-			//	INFO_LOG(CONSOLE, "\nGame got the Classic Controller calibration:\n");
-			//	INFO_LOG(CONSOLE, "Lx.Max: %i\n", data[7 + 0]);
-			//	INFO_LOG(CONSOLE, "Lx.Min: %i\n", data[7 + 1]);
-			//	INFO_LOG(CONSOLE, "Lx.Center: %i\n",  data[7 + 2]);
-			//	INFO_LOG(CONSOLE, "Ly.Max: %i\n", data[7 + 3]);
-			//	INFO_LOG(CONSOLE, "Ly.Min: %i\n",  data[7 + 4]);
-			//	INFO_LOG(CONSOLE, "Ly.Center: %i\n",  data[7 + 5]);
-			//	INFO_LOG(CONSOLE, "Rx.Max.x: %i\n",  data[7 + 6]);
-			//	INFO_LOG(CONSOLE, "Rx.Min.x: %i\n",  data[7 + 7]);
-			//	INFO_LOG(CONSOLE, "Rx.Center.x: %i\n", data[7 + 8]);
-			//	INFO_LOG(CONSOLE, "Ry.Max.y: %i\n",  data[7 + 9]);
-			//	INFO_LOG(CONSOLE, "Ry.Min: %i\n",  data[7 + 10]);
-			//	INFO_LOG(CONSOLE, "Ry.Center: %i\n\n", data[7 + 11]);
-			//	INFO_LOG(CONSOLE, "Lt.Neutral: %i\n",  data[7 + 12]);
-			//	INFO_LOG(CONSOLE, "Rt.Neutral %i\n\n", data[7 + 13]);
-			//}
-
-			// save values
-			if (!emu)
+			if (!rdr->error && address >= 0xf0 && address <= 0xff)
 			{
-				// Save to registry
-				if(data[7 + 0] != 0xff)
+				formatted.append(StringFromFormat("extension ID: %s\n", ArrayToString(rdr->data, size).c_str()));
+				/*
+				u16 id = Common::swap16(*(u16*)rdr->data);
+				int i = 0;
+				for (int i = 0; i < wm->m_extension->attachments.size(); i++)
 				{
-					//memcpy((u8*)&wm->m_reg_ext.calibration, &data[7], 0x10);
-					//memcpy((u8*)&wm->m_reg_ext.unknown3, &data[7], 0x10);
-				}
-				// Save the default values that should work with Wireless Nunchucks
-				else
-				{
-					//WiimoteEmu::SetDefaultExtensionRegistry();
+					WiimoteEmu::Attachment* a = (WiimoteEmu::Attachment*)wm->m_extension->attachments.at(i);
+					u16 id_ = Common::swap16(*(u16*)&a->reg.constant_id[4]);
+					if (id_ == id)
+					{
+						wm->m_extension->active_extension = i;
+						break;
+					}
 				}
-				//WiimoteEmu::UpdateEeprom();
-			}
-			// third party Nunchuck
-			else if(data[7] == 0xff)
-			{
-				//memcpy(wm->m_reg_ext + 0x20, WiimoteEmu::wireless_nunchuck_calibration, sizeof(WiimoteEmu::wireless_nunchuck_calibration));
-				//memcpy(wm->m_reg_ext + 0x30, WiimoteEmu::wireless_nunchuck_calibration, sizeof(WiimoteEmu::wireless_nunchuck_calibration));
+				*/
 			}
 
-			// print encrypted data
-			//INFO_LOG(CONSOLE, "WM_READ_DATA_REPLY: Extension calibration: %s", TmpData.c_str());
-		}
-
-		if (dataReply[1] == 0xa4 || dataReply[1] == 0xa6)
-		{
-			if(rdr->error == 7 || rdr->error == 8)
+			// save key
+			if (save && region_ptr == &wm->m_reg_ext && address == 0x40)
 			{
-				WARN_LOG(CONSOLE, "R%s[0x%02x 0x%02x]: e-%d", decrypted?"*":"", dataReply[1], rdr->address>>8, rdr->error);
+				memcpy(((u8*)&wm->m_reg_ext.encryption_key), rdr->data, size);
+				wiimote_gen_key(&wm->m_ext_key, wm->m_reg_ext.encryption_key);
+				formatted.append(StringFromFormat("reading key: %s", ArrayToString(((u8*)&wm->m_ext_key), sizeof(wm->m_ext_key), 0, 30).c_str()));
 			}
-			else
+
+			// Nunchuk calibration
+			if (region_ptr == &wm->m_reg_ext && address == 0x20)
 			{
-				WARN_LOG(CONSOLE, "R%s[0x%02x 0x%02x|%d]: %s", decrypted?"*":"", dataReply[1], rdr->address>>8, rdr->size+1, ArrayToString(rdr->data, rdr->size+1, 0).c_str());
+				u16 type = *(u16*)&wm->m_reg_ext.constant_id[4];
+
+				if (type == 0x0000)
+				{
+					nu_cal* cal = (nu_cal*)&wm->m_reg_ext.calibration;
+
+					formatted.append(StringFromFormat(
+					"Nunchuk calibration\n"
+
+					"zero x: %u\n"
+					"zero y: %u\n"
+					"zero z: %u\n"
+					"g x: %u\n"
+					"g y: %u\n"
+					"g z: %u\n"
+
+					"j x center: %u\n"
+					"j x max: %i\n"
+					"j x min: %i\n"
+
+					"j y center: %u\n"
+					"j y max: %u\n"
+					"j y min: %u"
+
+					, cal->cal_zero.x
+					, cal->cal_zero.y
+					, cal->cal_zero.z
+
+					, cal->cal_g.x
+					, cal->cal_g.y
+					, cal->cal_g.z
+
+					, cal->jx.center
+					, cal->jx.max
+					, cal->jx.min
+
+					, cal->jy.center
+					, cal->jy.max
+					, cal->jy.min
+					));
+				}
+
+				else if (type == 0x0101)
+				{
+					cc_cal* cal = (cc_cal*)&rdr->data;
+
+					formatted.append(StringFromFormat(
+						"Classic Controller calibration\n"
+
+						"l x center: %u\n"
+						"l x max: %u\n"
+						"l x min: %u\n"
+
+						"l y center: %u\n"
+						"l y max: %u\n"
+						"l y min: %u\n"
+
+						"r x center.x: %u\n"
+						"r x max.x: %u\n"
+						"r x min.x: %u\n"
+
+						"r y center: %u\n"
+						"r y max.y: %u\n"
+						"r y min: %u\n"
+
+						"lt neutral: %u\n"
+						"rt neutral %u"
+
+						, cal->Lx.center
+						, cal->Lx.max
+						, cal->Lx.min
+
+						, cal->Ly.center
+						, cal->Ly.max
+						, cal->Ly.min
+
+						, cal->Rx.center
+						, cal->Rx.max
+						, cal->Rx.min
+
+						, cal->Ry.center
+						, cal->Ry.max
+						, cal->Ry.min
+
+						, cal->Tl.neutral
+						, cal->Tr.neutral
+						));
+				}
 			}
+
+			break;
+		}
 		}
 
-		break;
-	}
+			break;
+		}
 
 	case WM_ACK_DATA:
-		size = sizeof(wm_acknowledge);
-		Name = "WM_ACK_DATA";
-		//INFO_LOG(CONSOLE, "ACK 0x%02x", data[4]);
+	{
+		wm_acknowledge* const ack = (wm_acknowledge*)sr->data;
+		if (log_com) com.append(StringFromFormat("WM_ACK_DATA %02x %02x", ack->reportID, ack->errorID));
 		break;
+	}
 
 	case WM_REPORT_CORE:
-		size = sizeof(wm_report_core);
-		DataReport = true;
-		break;
 	case WM_REPORT_CORE_ACCEL:
-		size = sizeof(wm_report_core_accel);
-		DataReport = true;
-		break;
 	case WM_REPORT_CORE_EXT8:
-		size = sizeof(wm_report_core_accel_ir12);
-		DataReport = true;
-		break;
 	case WM_REPORT_CORE_ACCEL_IR12:
-		size = sizeof(wm_report_core_accel_ir12);
-		DataReport = true;
-		break;
 	case WM_REPORT_CORE_EXT19:
-		size = sizeof(wm_report_core_accel_ext16);
-		DataReport = true;
-		break;
 	case WM_REPORT_CORE_ACCEL_EXT16:
-		size = sizeof(wm_report_core_accel_ext16);
-		DataReport = true;
-		break;
 	case WM_REPORT_CORE_IR10_EXT9:
-		size = sizeof(wm_report_core_accel_ir10_ext6);
-		DataReport = true;
-		break;
 	case WM_REPORT_CORE_ACCEL_IR10_EXT6:
-		size = sizeof(wm_report_core_accel_ir10_ext6);
-		DataReport = true;
+		data_report = true;
 		break;
 
 	default:
-		size = 15;
-		NOTICE_LOG(CONSOLE, "Debugging[%s]: Unknown channel 0x%02x", (emu ? "E" : "R"), data[1]);
+		if (log_com)
+			com = StringFromFormat("unknown report 0x%02x", sr->wm);
 		break;
 	}
 
-	//if (DataReport && wm->GetMotionPlusActive())
+	if (data_report)
+		report_mode = data[1];
+
+	//if (data_report && wm->GetMotionPlusActive())
 	//{
 	//	if (data[1] == WM_REPORT_CORE_ACCEL_IR10_EXT6)
 	//		static bool extension = false;
 	//	if (extension != (bool)(data[17+4]&1))
-	//		ERROR_LOG(CONSOLE, "Datareport extension %d", data[17+4]&1);
+	//		LOG3(CONSOLE, "Datareport extension %d", data[17+4]&1);
 	//		extension = data[17+4]&1;
 	//}
 
-	if (!DataReport && logCom && size <= 30)
+	if (log_com && !com.empty())
 	{
-		ERROR_LOG_S(CONSOLE, "Com[%s] %s: %s", (emu ? "E" : "R"), Name.c_str(), ArrayToString(data, size + 2, 0).c_str());
+		switch(sr->wm)
+		{
+		case WM_WRITE_DATA:
+		case WM_READ_DATA:
+		case WM_READ_DATA_REPLY:
+			switch(reg_type)
+			{
+			case WIIMOTE_REG_EXT:
+				LOG6(CONSOLE, "com[%s] %s", emu ? "E" : "R", com.c_str());
+				break;
+			default:
+				LOG8(CONSOLE, "com[%s] %s", emu ? "E" : "R", com.c_str());
+				break;
+			}
+			break;
+
+		default:
+			LOG2(CONSOLE, "com[%s] %s", emu ? "E" : "R", com.c_str());
+		}
+
+		if (!formatted.empty())
+			LOG1(CONSOLE, "%s", formatted.c_str());
 	}
 
-	if (logAudio && AudioData)
+	if (log_audio && audio_data)
 	{
-		//DEBUG_LOG(CONSOLE, "%s: %s\n", Name.c_str(), ArrayToString(data, std::min(10,size), 0, 30).c_str());
+		//DEBUG_LOG(CONSOLE, "%s: %s\n", com.c_str(), ArrayToString(data, min(10, data_size), 0, 30).c_str());
 	}
 
-	if (DataReport && (logData || logMP))
+	if (data_report && (log_data || log_mp))
 	{
-		// decrypt extension data
-		//if (data[1] == 0x37 && !wm->GetMotionPlusActive())
-		//if (data[1] == 0x37)
-		//	wiimote_decrypt(&wm->m_ext_key, &data[17], 0x00, 0x06);
-		//if (data[1] == 0x35)
-		//	wiimote_decrypt(&wm->m_ext_key, &data[7], 0x00, 0x06);
+		u8 mode = data[1];
+		const ReportFeatures& rptf = reporting_mode_features[mode - WM_REPORT_CORE];
 
-		//if (data[1] == 0x35 || data[1] == 0x37)
-		//{
-		//	if (!g_DebugMP && mp->is_mp_data) return;
-		//	if (!g_DebugData && !mp->is_mp_data) return;
-		//}
+		//if (wm->m_reporting_mode != mode)
+			//LOG2(WIIMOTE, "state mode %x, data mode %x", wm->m_reporting_mode, mode);
 
-		std::string SCore = "", SAcc = "", SIR = "", SExt = "", SExtID = "";
+		string s_core = "", s_acc = "", s_ir = "", s_ext = "", s_ext_id = "";
 
 		wm_core* core = (wm_core*)sr->data;
 		accel_cal* calib = (accel_cal*)&wm->m_eeprom[0x16];
 		wm_accel* accel = (wm_accel*)&data[4];
 
-		//SCore = StringFromFormat(
-		//	"%d %d %d %d ",
-		//	core->xL,
-		//	core->yL,
-		//	core->zL,
-		//	core->unknown);
-
-		SAcc = StringFromFormat(
+		s_acc = StringFromFormat(
+			//"%3d %3d %3d"
 			//"%3d %3d %3d"
-			//" | %3d %3d %3d"
-			//" | %3d %3d %3d"
-			"| %5.2f %5.2f %5.2f"
+			//"%3d %3d %3d"
+			"%5.2f %5.2f %5.2f"
+
 			//, calib->zero_g.x, calib->zero_g.y, calib->zero_g.z
-			//, (calib->zero_g.x<<2) + calib->zero_g.xL, (calib->zero_g.y<<2) + calib->zero_g.yL, (calib->zero_g.z<<2) + calib->zero_g.zL
+
+			//, (calib->zero_g.x<<2) + calib->zero_g.xL, (calib->zero_g.y<<2) + calib->zero_g.yL, (calib->zero_g.z << 2) + calib->zero_g.zL
+
 			//, calib->one_g.x, calib->one_g.y, calib->one_g.z
-			//, (calib->one_g.x<<2) + calib->one_g.xL, (calib->one_g.y<<2) + calib->one_g.yL, (calib->one_g.z<<2) + calib->one_g.zL
+			//, (calib->one_g.x << 2) + calib->one_g.xL, (calib->one_g.y << 2) + calib->one_g.yL, (calib->one_g.z << 2) + calib->one_g.zL
+
 			//, accel->x, accel->y, accel->z
-			//, (accel->x<<2) + core->xL, (accel->y<<2) + core->yL, (accel->z<<2) + core->zL
-			, (accel->x - calib->zero_g.x) / float(calib->one_g.x-calib->zero_g.x), (accel->y - calib->zero_g.y) / float(calib->one_g.y-calib->zero_g.y), (accel->z - calib->zero_g.z) / float(calib->one_g.z-calib->zero_g.z));
+			//, (accel->x << 2) + core->xL, (accel->y << 2) + core->yL, (accel->z << 2) + core->zL
 
-		NOTICE_LOG(CONSOLE, "%d", size);
+			, (accel->x - calib->zero_g.x) / float(calib->one_g.x - calib->zero_g.x)
+			, (accel->y - calib->zero_g.y) / float(calib->one_g.y - calib->zero_g.y)
+			, (accel->z - calib->zero_g.z) / float(calib->one_g.z - calib->zero_g.z));
 
-		if (data[1] == WM_REPORT_CORE_ACCEL_IR12)
+		if (rptf.ir)
 		{
-			wm_ir_extended *ir = (wm_ir_extended*)&data[7];
-
-			SIR = StringFromFormat(
-				"%4u %4u | %u"
-				, ir->x | ir->xhi << 8
-				, ir->y | ir->yhi << 8
-				, ir->size);
-		}
+			if (mode == WM_REPORT_CORE_ACCEL_IR12)
+			{
+				wm_ir_extended *ir = (wm_ir_extended*)&data[rptf.ir];
 
-		if (data[1] == WM_REPORT_CORE_ACCEL_EXT16)
-		{
-			wm_extension *nc = (wm_extension*)&data[7];
+				s_ir = StringFromFormat(
+					"%4u %4u | %u"
+					, ir->x | ir->xhi << 8
+					, ir->y | ir->yhi << 8
+					, ir->size);
+			}
 
-			SExt = StringFromFormat(
-				"%02x %02x | %02x %02x %02x | %02x"
-				, nc->jx, nc->jy
-				, nc->ax, nc->ay, nc->az
-				, nc->bt);
+			else if (mode == WM_REPORT_CORE_ACCEL_IR10_EXT6)
+			{
+				wm_ir_basic *ir = (wm_ir_basic*)&data[rptf.ir];
+
+				s_ir = StringFromFormat(
+					"%4u %4u %4u %4u"
+					, ir->x1 | ir->x1hi << 8
+					, ir->y1 | ir->y1hi << 8
+					, ir->x2 | ir->x2hi << 8
+					, ir->y2 | ir->y1hi << 8);
+			}
 		}
 
-		if (data[1] == WM_REPORT_CORE_ACCEL_IR10_EXT6)
+		if (rptf.ext)
 		{
-			wm_ir_basic *ir = (wm_ir_basic*)&data[7];
+			// decrypt
+			//if (wm->m_reg_ext.encryption == 0xaa && !wm->GetMotionPlusActive())
+			if (wm->m_reg_ext.encryption == 0xaa)
+				wiimote_decrypt(&wm->m_ext_key, &data[rptf.ext], 0x00, 0x06);
 
-			SIR = StringFromFormat(
-				"%4u %4u %4u %4u"
-				, ir->x1 | ir->x1hi << 8
-				, ir->y1 | ir->y1hi << 8
-				, ir->x2 | ir->x2hi << 8
-				, ir->y2 | ir->y1hi << 8);
+			if (wm->GetMotionPlusActive())
+			{
+				/*
+				wm_motionplus_data *mp = (wm_motionplus_data*)&data[17];
+				wm_nc_mp *nc_mp = (wm_nc_mp*)&data[17];
 
-			/*
-			wm_motionplus *mp = (wm_motionplus*)&data[17];
-			wm_nc_mp  *nc_mp = (wm_nc_mp *)&data[17];
+				if (!log_mp && mp->is_mp_data)
+					return;
+				if (!log_data && !mp->is_mp_data)
+					return;
 
-			if (mp->is_mp_data)
-			{
-				SExt = StringFromFormat(""
-					//"%02x %02x %02x %02x %02x %02x"
-					//"| %04x %04x %04x
-					" %5.2f %5.2f %5.2f"
-					" %s%s%s"
-					//, mp->roll1, mp->roll2
-					//, mp->pitch1, mp->pitch2
-					//, mp->yaw1, mp->yaw2
-					//, mp->pitch2<<8 | mp->pitch1
-					//, mp->roll2<<8 | mp->roll1
-					//, mp->yaw2<<8 | mp->yaw1
-					//, mp->pitch2<<8 | mp->pitch1
-					//, mp->roll2<<8 | mp->roll1
-					//, mp->yaw2<<8 | mp->yaw1
-					, float((mp->pitch2<<8 | mp->pitch1) - 0x1f7f) / float(0x1fff)
-					, float((mp->roll2<<8 | mp->roll1) - 0x1f7f) / float(0x1fff)
-					, float((mp->yaw2<<8 | mp->yaw1) - 0x1f7f) / float(0x1fff)
-					, mp->pitch_slow?"*":" ", mp->roll_slow?"*":" ", mp->yaw_slow?"*":" ");
+				if (mp->is_mp_data)
+				{
+					s_ext = StringFromFormat(""
+						//"%02x %02x %02x %02x %02x %02x"
+						//"| %04x %04x %04x
+						" %5.2f %5.2f %5.2f"
+						" %s%s%s"
+						//, mp->roll1, mp->roll2
+						//, mp->pitch1, mp->pitch2
+						//, mp->yaw1, mp->yaw2
+						//, mp->pitch2<<8 | mp->pitch1
+						//, mp->roll2<<8 | mp->roll1
+						//, mp->yaw2<<8 | mp->yaw1
+						//, mp->pitch2<<8 | mp->pitch1
+						//, mp->roll2<<8 | mp->roll1
+						//, mp->yaw2<<8 | mp->yaw1
+						, float((mp->pitch2<<8 | mp->pitch1) - 0x1f7f) / float(0x1fff)
+						, float((mp->roll2<<8 | mp->roll1) - 0x1f7f) / float(0x1fff)
+						, float((mp->yaw2<<8 | mp->yaw1) - 0x1f7f) / float(0x1fff)
+						, mp->pitch_slow?"*":" ", mp->roll_slow?"*":" ", mp->yaw_slow?"*":" ");
+				}
+				else
+				{
+					s_ext = StringFromFormat(
+						"%02x %02x | %02x %02x | %02x %02x %02x | %02x %02x %02x",
+						nc_mp->bt.z, nc_mp->bt.c,
+						nc_mp->jx, nc_mp->jy,
+						nc_mp->ax + nc_mp->axL, nc_mp->ay + nc_mp->ayL, (nc_mp->az << 1) + nc_mp->azL);
+				}
+
+				s_ext_id = StringFromFormat(
+					"%s %d %d"
+					, mp->is_mp_data ? "+" : "e"
+					, mp->is_mp_data ? mp->extension_connected : wm_nc_mp->extension_connected
+					, wm->m_extension->active_extension);
+				*/
 			}
 			else
 			{
-				SExt = StringFromFormat(
-					"%02x %02x | %02x %02x | %02x %02x %02x | %02x %02x %02x",
-					nc_mp->bz, nc_mp->bc,
-					nc_mp->jx, nc_mp->jy,
-					nc_mp->ax+nc_mp->axL, nc_mp->ay+nc_mp->ayL, (nc_mp->az<<1)+nc_mp->azL);
-			}
+				nu_cal *cal = (nu_cal*)&wm->m_reg_ext.calibration;
+				wm_nc *nc = (wm_nc*)&data[rptf.ext];
 
-			SExtID = StringFromFormat(
-				"[%s|%d|%d]"
-				, mp->is_mp_data ? "+" : "e"
-				, mp->is_mp_data ? mp->extension_connected : nc_mp->extension_connected
-				, wm->m_extension->active_extension);
-			*/
+				s_ext = StringFromFormat(
+					"%d %d"
+
+					", %3u %3u"
+					", %.2f [%.2f, %.2f] %.2f [%.2f, %.2f]"
+
+					", %4d %4d %4d"
+					, nc->bt.z, nc->bt.c
 
-			//DEBUG_LOG_S(CONSOLE, "M+ %d Extension %d %d %s", mp->is_mp_data, mp->is_mp_data ?
+					, nc->jx, nc->jy
+
+					, double(nc->jx - cal->jx.center) / (double(abs(cal->jx.min - cal->jx.center) + abs(cal->jx.max - cal->jx.center)) / 2.0)
+					, double(cal->jx.min - cal->jx.center) / double(abs(cal->jx.min - cal->jx.center))
+					, double(cal->jx.max - cal->jx.center) /double(abs(cal->jx.max - cal->jx.center))
+
+					, double(nc->jy - cal->jy.center) / (double(abs(cal->jy.min - cal->jy.center) + abs(cal->jy.max - cal->jy.center)) / 2.0)
+					, double(cal->jy.min - cal->jy.center) / double(abs(cal->jy.min - cal->jx.center))
+					, double(cal->jy.max - cal->jy.center) / double(abs(cal->jy.max - cal->jx.center))
+
+					, nc->ax
+					, nc->ay
+					, nc->az
+
+					//, (nc->ax << 1) + nc->axL
+					//, (nc->ay << 1) + nc->ayL
+					//, (nc->az << 1) + nc->azL
+					);
+			}
+
+			//LOG1(CONSOLE, "M+ %d Extension %d %d %s", mp->is_mp_data, mp->is_mp_data ?
 			//		mp->extension_connected : ((wm_nc_mp*)&data[17])->extension_connected, wm->m_extension->active_extension,
 			//		ArrayToString(((u8*)&wm->m_reg_motion_plus.ext_identifier), sizeof(wm->m_reg_motion_plus.ext_identifier), 0).c_str());
 		}
 
-		// select log data
-		WARN_LOG_S(CONSOLE, "Data"
-			"[%s]"
-			" | id %s"
-			" | %s"
-			" | c %s"
-			" | a %s"
-			" | ir %s"
-			" | ext %s"
-			//" | %s"
-			//" | %s"
-			//" | %s"
-			, (emu ? "E" : "R")
-			, SExtID.c_str()
-			, ArrayToString(data, 2, 0).c_str()
-			, SCore.c_str()
-			, SAcc.c_str()
-			, SIR.c_str()
-			, SExt.c_str()
-			//, ArrayToString(&data[4], 3, 0).c_str()
-			//, (accel->x - 0x7f) / float(0xff), (accel->y - 0x7f) / float(0xff), (accel->z - 0x7f) / float(0xff)
-			//, ArrayToString(&data[17], 6, 0).c_str(),
-			);
+		// log data
+		string s_data = StringFromFormat("data[%s] %02x:", (emu ? "E" : "R"), report_mode);
+
+		if (!s_ext_id.empty())
+			s_data.append(StringFromFormat(" | e %s", s_ext_id.c_str()));
+
+		if (!s_core.empty())
+			s_data.append(StringFromFormat(" | c %s", s_core.c_str()));
+
+		if (!s_acc.empty())
+			s_data.append(StringFromFormat(" | a %s", s_acc.c_str()));
+
+		if (!s_ir.empty())
+			s_data.append(StringFromFormat(" | ir %s", s_ir.c_str()));
+
+		if (!s_ext.empty())
+			s_data.append(StringFromFormat(" | ext %s", s_ext.c_str()));
+
+		// unformatted
+		//s_data = StringFromFormat("data[%s]: %s", (emu ? "E" : "R"), ArrayToString(data, rptf.size).c_str());
+
+		// data report changed
+		u16 unset = 0x20 | 0x40 | 0x80 | 0x2000 | 0x4000;
+		*(u16*)&data[rptf.core] &= ~unset;
+
+		// ignore ir because it change often
+		if (rptf.ir)
+		{
+			if (rptf.ext)
+				memset(&data[rptf.ir], 0xff, rptf.ext - rptf.ir);
+			else
+				memset(&data[rptf.ir], 0xff, rptf.size - rptf.ir);
+		}
+
+		wm_accel &acc = *(wm_accel*)&data[rptf.accel];
+		wm_accel &acc_last = *(wm_accel*)&last_data.data[rptf.accel];
+		if (abs(acc.x - acc_last.x) < 10)
+			acc_last.x = acc.x;
+		if (abs(acc.y - acc_last.y) < 10)
+			acc_last.y = acc.y;
+		if (abs(acc.z - acc_last.z) < 10)
+			acc_last.z = acc.z;
+
+		if (rptf.ext)
+		{
+			wm_nc &ext = *(wm_nc*)&data[rptf.ext];
+			wm_nc &ext_last = *(wm_nc*)&last_data.data[rptf.ext];
+			if (abs(ext.ax - ext_last.ax) < 10)
+				ext_last.ax = ext.ax;
+			if (abs(ext.ay - ext_last.ay) < 10)
+				ext_last.ay = ext.ay;
+			if (abs(ext.az - ext_last.az) < 10)
+				ext_last.az = ext.az;
+			ext.bt.axL = 0;
+			ext.bt.ayL = 0;
+			ext.bt.azL = 0;
+		}
+
+		bool data_report_change = memcmp(last_data.data, data, rptf.size);
+
+		if (data_report_change)
+			LOG3(CONSOLE, "%s", s_data.c_str());
+		else
+			LOG4(CONSOLE, "%s", s_data.c_str());
+
+		memcpy(last_data.data, data, rptf.size);
+		last_data.type = sr->wm;
 	}
 #endif
 }
@@ -835,11 +1178,16 @@ void Wiimote::SendAck(u8 _reportID)
 	ack->reportID = _reportID;
 	ack->errorID = 0;
 
+	WiimoteEmu::Spy(this, data, (int)sizeof(data));
 	Core::Callback_WiimoteInterruptChannel( m_index, m_reporting_channel, data, sizeof(data));
 }
 
 void Wiimote::HandleExtensionSwap()
 {
+	// m+ switch
+	if (GetMotionPlusActive() && !GetMotionPlusAttached())
+		m_reg_motion_plus.ext_identifier[2] = 0xa6;
+
 	// handle switch extension
 	if (m_extension->active_extension != m_extension->switch_extension)
 	{
@@ -851,8 +1199,13 @@ void Wiimote::HandleExtensionSwap()
 			// set the wanted extension
 			m_extension->active_extension = m_extension->switch_extension;
 
-		// reset register
-		((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension])->Reset();
+		// set register, I hate this
+		const std::vector<u8> &reg = ((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension])->reg;
+		memset(&m_reg_ext, 0, WIIMOTE_REG_EXT_SIZE);
+		memcpy(&m_reg_ext, &reg[0], reg.size());
+		if (m_extension->active_extension == EXT_NUNCHUK
+			&& GetMotionPlusAttached())
+			memcpy(&m_reg_motion_plus.ext_calib, nunchuck_calibration, sizeof(nunchuck_calibration));
 	}
 }
 
@@ -861,12 +1214,17 @@ void Wiimote::HandleExtensionSwap()
    the status request rs and all its eventual instructions it may include (for
    example turn off rumble or something else) and just send the status
    report. */
-void Wiimote::RequestStatus(const wm_request_status* const rs)
+void Wiimote::RequestStatus(const wm_request_status* const rs, int ext)
 {
 	HandleExtensionSwap();
 
 	// update status struct
-	m_status.extension = (m_extension->active_extension || m_motion_plus_active) ? 1 : 0;
+	if (ext == -1)
+		m_status.extension = (m_extension->active_extension || GetMotionPlusActive()) ? 1 : 0;
+	else
+		m_status.extension = ext;
+
+	m_status.speaker = m_status.speaker;
 
 	// set up report
 	u8 data[8];
@@ -893,6 +1251,7 @@ void Wiimote::RequestStatus(const wm_request_status* const rs)
 	}
 
 	// send report
+	WiimoteEmu::Spy(this, data, sizeof(data));
 	Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data));
 }
 
@@ -900,8 +1259,8 @@ void Wiimote::RequestStatus(const wm_request_status* const rs)
 void Wiimote::WriteData(const wm_write_data* const wd)
 {
 	u32 address = Common::swap24(wd->address);
-
-	// ignore the 0x010000 bit
+	u8 addressHI = (address >> 16) & 0xFE;
+	u8 addressLO = address & 0xFFFF;
 	address &= ~0x010000;
 
 	if (wd->size > 16)
@@ -941,32 +1300,27 @@ void Wiimote::WriteData(const wm_write_data* const wd)
 	case WM_SPACE_REGS2 :
 		{
 			// Write to Control Register
-
-			// ignore second byte for extension area
-			if (0xA4 == (address >> 16))
-				address &= 0xFF00FF;
-
 			const u8 region_offset = (u8)address;
 			void *region_ptr = NULL;
 			int region_size = 0;
 
-			switch (address >> 16)
+			switch (addressHI)
 			{
 			// speaker
-			case 0xa2 :
+			case WIIMOTE_REG_SPEAKER :
 				region_ptr = &m_reg_speaker;
 				region_size = WIIMOTE_REG_SPEAKER_SIZE;
 				break;
 
 			// extension register
-			case 0xa4 :
-				region_ptr = m_motion_plus_active ? (void*)&m_reg_motion_plus : (void*)&m_reg_ext;
+			case WIIMOTE_REG_EXT :
+				region_ptr = GetMotionPlusActive() ? (void*)&m_reg_motion_plus : (void*)&m_reg_ext;
 				region_size = WIIMOTE_REG_EXT_SIZE;
 				break;
 
 			// motion plus
-			case 0xa6 :
-				if (false == m_motion_plus_active)
+			case WIIMOTE_REG_MP :
+				if (!GetMotionPlusActive() && GetMotionPlusAttached())
 				{
 					region_ptr = &m_reg_motion_plus;
 					region_size = WIIMOTE_REG_EXT_SIZE;
@@ -974,7 +1328,7 @@ void Wiimote::WriteData(const wm_write_data* const wd)
 				break;
 
 			// ir
-			case 0xB0 :
+			case WIIMOTE_REG_IR :
 				region_ptr = &m_reg_ir;
 				region_size = WIIMOTE_REG_IR_SIZE;
 
@@ -1000,23 +1354,78 @@ void Wiimote::WriteData(const wm_write_data* const wd)
 
 			if (&m_reg_ext == region_ptr)
 			{
-				// Run the key generation on all writes in the key area, it doesn't matter
-				// that we send it parts of a key, only the last full key will have an effect
-				if (address >= 0xa40040 && address <= 0xa4004c)
+				// Run the key generation on all writes in the key area, only the last full key will have an effect
+				if (addressLO >= 0x40 && addressLO <= 0x4c)
 					wiimote_gen_key(&m_ext_key, m_reg_ext.encryption_key);
 			}
+
 			else if (&m_reg_motion_plus == region_ptr)
 			{
-				// activate/deactivate motion plus
-				if (0x55 == m_reg_motion_plus.activated)
+				switch (addressHI)
 				{
-					// maybe hacky
-					m_reg_motion_plus.activated = 0;
-					m_motion_plus_active ^= 1;
-
-					RequestStatus();
+				case 0xa4:
+				switch (region_offset)
+				{
+					case 0xfb:
+						if (mp_last_write_reg == 0xfe)
+						{
+						}
+						if (mp_last_write_reg == 0xf0)
+						{
+							DEBUG_LOG(WIIMOTE, "W[0x%02x 0x%02x:%04x]: WM+ already disabled [ext:%i]", wd->data[0], addressHI, region_offset, m_extension->active_extension);
+							m_reg_motion_plus.ext_identifier[4] = 0x05;
+							m_reg_motion_plus.state = 0x08;
+						}
+					break;
+					// calibration
+					case 0xf1:
+						// failed, try again. TODO: make it never fail
+						if (!wd->data[0])
+						{
+							m_reg_motion_plus.ext_identifier[2] = 0xa6;
+							m_reg_motion_plus.state = 0x08;
+							RequestStatus(NULL, 0);
+							if (m_extension->active_extension != EXT_NONE)
+							{
+								RequestStatus(NULL, 1);
+								RequestStatus(NULL, 0);
+							}
+							RequestStatus(NULL, 1);
+						}
+						else
+						{
+							m_reg_motion_plus.state = 0x1a;
+							memcpy(&m_reg_motion_plus.gyro_calib, mp_gyro_calib2, sizeof(mp_gyro_calib2));
+						}
+					break;
+					case 0xf2:
+						if(m_reg_motion_plus.state < 0x0e) {
+							m_reg_motion_plus.state = 0x0e;
+							memcpy(&m_reg_motion_plus.gyro_calib, mp_gyro_calib, sizeof(mp_gyro_calib));
+						}
+					break;
+				}
+				break;
+				case 0xa6:
+				switch (region_offset)
+				{
+					case 0xfe:
+					// activate
+					if (!GetMotionPlusActive() && 0x05 == wd->data[0])
+					{
+						DEBUG_LOG(WIIMOTE, "W[0x%02x 0x%02x:%04x]: Enabling WM+", wd->data[0], addressHI, region_offset);
+						m_reg_motion_plus.ext_identifier[2] = 0xa4;
+						m_reg_motion_plus.ext_identifier[4] = wd->data[0];
+						m_reg_motion_plus.state = 0x08;
+						RequestStatus(NULL, 0);
+						RequestStatus(NULL, 1);
+					}
+					break;
+				}
+				break;
 				}
 			}
+			mp_last_write_reg = region_offset;
 		}
 		break;
 
@@ -1031,7 +1440,6 @@ void Wiimote::ReadData(const wm_read_data* const rd)
 {
 	u32 address = Common::swap24(rd->address);
 	u16 size = Common::swap16(rd->size);
-
 	// ignore the 0x010000 bit
 	address &= 0xFEFFFF;
 
@@ -1098,21 +1506,21 @@ void Wiimote::ReadData(const wm_read_data* const rd)
 			switch (address >> 16)
 			{
 			// speaker
-			case 0xa2:
+			case WIIMOTE_REG_SPEAKER:
 				region_ptr = &m_reg_speaker;
 				region_size = WIIMOTE_REG_SPEAKER_SIZE;
 				break;
 
 			// extension
-			case 0xa4:
-				region_ptr = m_motion_plus_active ? (void*)&m_reg_motion_plus : (void*)&m_reg_ext;
+			case WIIMOTE_REG_EXT:
+				region_ptr = GetMotionPlusActive() ? (void*)&m_reg_motion_plus : (void*)&m_reg_ext;
 				region_size = WIIMOTE_REG_EXT_SIZE;
 				break;
 
 			// motion plus
-			case 0xa6:
+			case WIIMOTE_REG_MP:
 				// reading from 0xa6 returns error when mplus is activated
-				if (false == m_motion_plus_active)
+				if (false == GetMotionPlusActive())
 				{
 					region_ptr = &m_reg_motion_plus;
 					region_size = WIIMOTE_REG_EXT_SIZE;
@@ -1120,7 +1528,7 @@ void Wiimote::ReadData(const wm_read_data* const rd)
 				break;
 
 			// ir
-			case 0xb0:
+			case WIIMOTE_REG_IR:
 				region_ptr = &m_reg_ir;
 				region_size = WIIMOTE_REG_IR_SIZE;
 				break;
@@ -1135,10 +1543,9 @@ void Wiimote::ReadData(const wm_read_data* const rd)
 
 			if (&m_reg_ext == region_ptr)
 			{
-				// Encrypt data read from extension register
-				// Check if encrypted reads is on
+				// encrypt
 				if (0xaa == m_reg_ext.encryption)
-					wiimote_encrypt(&m_ext_key, block, address & 0xffff, (u8)size);
+					wiimote_encrypt(&m_ext_key, block, address&0xffff, (u8)size);
 			}
 		}
 		break;
@@ -1221,6 +1628,7 @@ void Wiimote::SendReadDataReply(ReadRequest& _request)
 	}
 
 	// Send a piece
+	WiimoteEmu::Spy(this, data, sizeof(data));
 	Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data));
 }
 
@@ -1235,8 +1643,6 @@ void Wiimote::DoState(PointerWrap& p)
 	p.Do(ir_cos);
 	p.Do(m_rumble_on);
 	p.Do(m_speaker_mute);
-	p.Do(m_motion_plus_present);
-	p.Do(m_motion_plus_active);
 	p.Do(m_reporting_auto);
 	p.Do(m_reporting_mode);
 	p.Do(m_reporting_channel);
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/UDPTLayer.h b/Source/Core/Core/Src/HW/WiimoteEmu/UDPTLayer.h
index e7d9cb9..4d31aaa 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/UDPTLayer.h
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/UDPTLayer.h
@@ -12,7 +12,7 @@
 
 namespace UDPTLayer
 {
-	void GetButtons(UDPWrapper * m , wm_core * butt)
+	void GetButtons(UDPWrapper * m , u16 * butt)
 	{
 		if (!(m->inst)) return;
 		if (!(m->updButt)) return;
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp
index 94e7d88..a0353ed 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp
@@ -39,28 +39,7 @@ auto const PI = TAU / 2.0;
 namespace WiimoteEmu
 {
 
-/* An example of a factory default first bytes of the Eeprom memory. There are differences between
-   different Wiimotes, my Wiimote had different neutral values for the accelerometer. */
-static const u8 eeprom_data_0[] = {
-	// IR, maybe more
-	// assuming last 2 bytes are checksum
-	0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, /*0x74, 0xD3,*/ 0x00, 0x00,	// messing up the checksum on purpose
-	0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, /*0x74, 0xD3,*/ 0x00, 0x00,
-	// Accelerometer
-	// 0g x,y,z, 1g x,y,z, idk, last byte is a checksum
-	0x80, 0x80, 0x80, 0x00, 0x9A, 0x9A, 0x9A, 0x00, 0x40, 0xE3,
-	0x80, 0x80, 0x80, 0x00, 0x9A, 0x9A, 0x9A, 0x00, 0x40, 0xE3,
-};
-
-static const u8 motion_plus_id[] = { 0x00, 0x00, 0xA6, 0x20, 0x00, 0x05 };
-
-static const u8 eeprom_data_16D0[] = {
-	0x00, 0x00, 0x00, 0xFF, 0x11, 0xEE, 0x00, 0x00,
-	0x33, 0xCC, 0x44, 0xBB, 0x00, 0x00, 0x66, 0x99,
-	0x77, 0x88, 0x00, 0x00, 0x2B, 0x01, 0xE8, 0x13
-};
-
-const ReportFeatures reporting_mode_features[] = 
+const ReportFeatures reporting_mode_features[] =
 {
 	//0x30: Core Buttons
 	{ 2, 0, 0, 0, 4 },
@@ -97,19 +76,17 @@ void EmulateShake(AccelData* const accel
 
 	// peak G-force
 	double shake_intensity;
-	
-	// shake is a bitfield of X,Y,Z shake button states
-	static const unsigned int btns[] = { 0x01, 0x02, 0x04 };
-	unsigned int shake = 0;
-	buttons_group->GetState( &shake, btns );
+
+	ControlState button[3];
+	buttons_group->GetState( button );
 
 	for (int i = 0; i != 3; ++i)
 	{
-		if (shake & (1 << i))
+		if (button[i] != 0)
 		{
 			double zero = double((&(calib->zero_g.x))[i]);
 			double one = double((&(calib->one_g.x))[i]);
-			shake_intensity = max(zero / (one - zero), (255.f - zero) / (one - zero));
+			shake_intensity = button[i] * max(zero / (one - zero), (255.f - zero) / (one - zero));
 			(&(accel->x))[i] = std::sin(TAU * shake_step[i] / shake_step_max) * shake_intensity;
 			shake_step[i] = (shake_step[i] + 1) % shake_step_max;
 		}
@@ -120,11 +97,12 @@ void EmulateShake(AccelData* const accel
 
 void EmulateTilt(AccelData* const accel
 	, ControllerEmu::Tilt* const tilt_group
-	, const bool focus, const bool sideways, const bool upright)
+	, const bool focus, const bool sideways, const bool upright
+	, const bool fast)
 {
-	float roll, pitch;
+	float pitch, roll, yaw;
 	// 180 degrees
-	tilt_group->GetState(&roll, &pitch, 0, focus ? PI : 0);
+	tilt_group->GetState(&pitch, &roll, &yaw, false, 0.0, focus ? PI * tilt_group->settings[tilt_group->HasGyro() ? T_ACC_RANGE_S : T_RANGE]->value : 0);
 
 	unsigned int	ud = 0, lr = 0, fb = 0;
 
@@ -149,9 +127,22 @@ void EmulateTilt(AccelData* const accel
 	(&accel->x)[ud] = (sin((PI / 2) - std::max(fabsf(roll), fabsf(pitch))))*sgn[ud];
 	(&accel->x)[lr] = -sin(roll)*sgn[lr];
 	(&accel->x)[fb] = sin(pitch)*sgn[fb];
+
+	// rotation g-force
+	if (tilt_group->HasGyro()
+		&& (tilt_group->controls[T_FAST]->control_ref->State() != 0
+		|| fast))
+	{
+		tilt_group->GetState( &pitch, &roll, &yaw, true, 0.0, focus ? 1.0 : 0, false );
+		if (abs(yaw) > 0 || abs(pitch) > 0)		accel->y += 5;
+		if (yaw > 0)							accel->x += 5;
+		if (yaw < 0)							accel->x -= 5;
+		if (pitch > 0)							accel->z += 5;
+		if (pitch < 0)							accel->z -= 5;
+	}
 }
 
-#define SWING_INTENSITY		2.5f//-uncalibrated(aprox) 0x40-calibrated
+#define SWING_INTENSITY		5.0f//-uncalibrated(aprox) 0x40-calibrated
 
 void EmulateSwing(AccelData* const accel
 	, ControllerEmu::Force* const swing_group
@@ -204,6 +195,16 @@ const char* const named_buttons[] =
 	"A", "B", "1", "2", "-", "+", "Home",
 };
 
+bool Wiimote::GetMotionPlusAttached() const
+{
+	return m_options->settings[SETTING_MOTIONPLUS]->value != 0;
+}
+
+bool Wiimote::GetMotionPlusActive() const
+{
+	return m_reg_motion_plus.ext_identifier[2] == 0xa4;
+}
+
 void Wiimote::Reset()
 {
 	m_reporting_mode = WM_REPORT_CORE;
@@ -213,9 +214,7 @@ void Wiimote::Reset()
 
 	m_rumble_on = false;
 	m_speaker_mute = false;
-	m_motion_plus_present = false;
-	m_motion_plus_active = false;
-
+	mp_passthrough = false;
 	// will make the first Update() call send a status request
 	// the first call to RequestStatus() will then set up the status struct extension bit
 	m_extension->active_extension = -1;
@@ -233,7 +232,9 @@ void Wiimote::Reset()
 	memset(&m_reg_ext, 0, sizeof(m_reg_ext));
 	memset(&m_reg_motion_plus, 0, sizeof(m_reg_motion_plus));
 
-	memcpy(&m_reg_motion_plus.ext_identifier, motion_plus_id, sizeof(motion_plus_id));
+	memcpy(&m_reg_motion_plus.calibration, motion_plus_calibration, sizeof(motion_plus_calibration));
+	memcpy(&m_reg_motion_plus.gyro_calib, mp_gyro_calib, sizeof(mp_gyro_calib));
+	memcpy(&m_reg_motion_plus.ext_identifier, mp_id, sizeof(mp_id));
 
 	// status
 	memset(&m_status, 0, sizeof(m_status));
@@ -262,13 +263,17 @@ Wiimote::Wiimote( const unsigned int index )
 {
 	// ---- set up all the controls ----
 
+	m_udp = new UDPWrapper(m_index, _trans("UDP Wiimote"));
+
 	// buttons
 	groups.push_back(m_buttons = new Buttons("Buttons"));
 	for (unsigned int i=0; i < sizeof(named_buttons)/sizeof(*named_buttons); ++i)
 		m_buttons->controls.push_back(new ControlGroup::Input( named_buttons[i]));
 
-	// udp
-	groups.push_back(m_udp = new UDPWrapper(m_index, _trans("UDP Wiimote")));
+	// dpad
+	groups.push_back(m_dpad = new Buttons("D-Pad"));
+	for (unsigned int i=0; i < 4; ++i)
+		m_dpad->controls.push_back(new ControlGroup::Input(named_directions[i]));
 
 	// ir
 	groups.push_back(m_ir = new Cursor(_trans("IR")));
@@ -276,23 +281,23 @@ Wiimote::Wiimote( const unsigned int index )
 	// swing
 	groups.push_back(m_swing = new Force(_trans("Swing")));
 
-	// tilt
-	groups.push_back(m_tilt = new Tilt(_trans("Tilt")));
-
 	// shake
-	groups.push_back(m_shake = new Buttons(_trans("Shake")));
+	groups.push_back(m_shake = new Buttons(_trans("Shake"), true));
 	m_shake->controls.push_back(new ControlGroup::Input("X"));
 	m_shake->controls.push_back(new ControlGroup::Input("Y"));
 	m_shake->controls.push_back(new ControlGroup::Input("Z"));
 
+	// tilt
+	groups.push_back(m_tilt = new Tilt(_trans("Tilt"), true));
+
 	// extension
 	groups.push_back(m_extension = new Extension(_trans("Extension")));
-	m_extension->attachments.push_back(new WiimoteEmu::None(m_reg_ext));
-	m_extension->attachments.push_back(new WiimoteEmu::Nunchuk(m_udp, m_reg_ext));
-	m_extension->attachments.push_back(new WiimoteEmu::Classic(m_reg_ext));
-	m_extension->attachments.push_back(new WiimoteEmu::Guitar(m_reg_ext));
-	m_extension->attachments.push_back(new WiimoteEmu::Drums(m_reg_ext));
-	m_extension->attachments.push_back(new WiimoteEmu::Turntable(m_reg_ext));
+	m_extension->attachments.push_back(new WiimoteEmu::None());
+	m_extension->attachments.push_back(new WiimoteEmu::Nunchuk(m_udp));
+	m_extension->attachments.push_back(new WiimoteEmu::Classic());
+	m_extension->attachments.push_back(new WiimoteEmu::Guitar());
+	m_extension->attachments.push_back(new WiimoteEmu::Drums());
+	m_extension->attachments.push_back(new WiimoteEmu::Turntable());
 
 	m_extension->settings.push_back(new ControlGroup::Setting(_trans("Motion Plus"), 0, 0, 1));
 
@@ -300,16 +305,17 @@ Wiimote::Wiimote( const unsigned int index )
 	groups.push_back(m_rumble = new ControlGroup(_trans("Rumble")));
 	m_rumble->controls.push_back(new ControlGroup::Output(_trans("Motor")));
 
-	// dpad
-	groups.push_back(m_dpad = new Buttons("D-Pad"));
-	for (unsigned int i=0; i < 4; ++i)
-		m_dpad->controls.push_back(new ControlGroup::Input(named_directions[i]));
-
 	// options
-	groups.push_back( m_options = new ControlGroup(_trans("Options")));
+	groups.push_back(m_options = new ControlGroup(_trans("Options"), GROUP_TYPE_OPTIONS));
 	m_options->settings.push_back(new ControlGroup::Setting(_trans("Background Input"), false));
 	m_options->settings.push_back(new ControlGroup::Setting(_trans("Sideways Wiimote"), false));
 	m_options->settings.push_back(new ControlGroup::Setting(_trans("Upright Wiimote"), false));
+	m_options->settings.push_back(new ControlGroup::Setting(_trans("MotionPlus"), false));
+	m_options->settings.push_back(new ControlGroup::Setting(_trans("MotionPlus Fast"), false));
+	m_options->settings.push_back(new ControlGroup::Setting(_trans("Hide IR"), false));
+
+	// udp
+	groups.push_back(m_udp);
 
 	// TODO: This value should probably be re-read if SYSCONF gets changed
 	m_sensor_bar_on_top = SConfig::GetInstance().m_SYSCONF->GetData<u8>("BT.BAR") != 0;
@@ -330,9 +336,6 @@ bool Wiimote::Step()
 {
 	const bool has_focus = HAS_FOCUS;
 
-	// TODO: change this a bit
-	m_motion_plus_present = m_extension->settings[0]->value != 0;
-
 	// no rumble if no focus
 	if (false == has_focus)
 		m_rumble_on = false;
@@ -378,6 +381,12 @@ bool Wiimote::Step()
 
 		return true;
 	}
+	// m+ switch
+	if (GetMotionPlusActive() && !GetMotionPlusAttached()) {
+		RequestStatus(NULL, 0);
+		if (m_extension->active_extension != EXT_NONE)
+			RequestStatus();
+	}
 
 	return false;
 }
@@ -385,16 +394,25 @@ bool Wiimote::Step()
 void Wiimote::UpdateButtonsStatus(bool has_focus)
 {
 	// update buttons in status struct
-	m_status.buttons = 0;
+	memset(&m_status.buttons, 0, sizeof(m_status.buttons));
 	if (has_focus)
 	{
 		const bool is_sideways = m_options->settings[1]->value != 0;
-		m_buttons->GetState(&m_status.buttons, button_bitmasks);
-		m_dpad->GetState(&m_status.buttons, is_sideways ? dpad_sideways_bitmasks : dpad_bitmasks);
-		UDPTLayer::GetButtons(m_udp, &m_status.buttons);
+		m_buttons->GetState((u16*)&m_status.buttons, button_bitmasks);
+		m_dpad->GetState((u16*)&m_status.buttons, is_sideways ? dpad_sideways_bitmasks : dpad_bitmasks);
+		UDPTLayer::GetButtons(m_udp, (u16*)&m_status.buttons);
 	}
 }
 
+void Wiimote::GetSettings()
+{
+	std::vector<ControlGroup::Setting*>::const_iterator
+			si = m_options->settings.begin(),
+			se = m_options->settings.end();
+		for (; si!=se; ++si)
+			(*si)->GetState();
+}
+
 void Wiimote::GetCoreData(u8* const data)
 {
 	// when a movie is active, the button update happens here instead of Wiimote::Step, to avoid potential desync issues.
@@ -403,18 +421,18 @@ void Wiimote::GetCoreData(u8* const data)
 		UpdateButtonsStatus(HAS_FOCUS);
 	}
 
-	*(wm_core*)data |= m_status.buttons;
+	memcpy(data, &m_status.buttons, sizeof(m_status.buttons));
 }
 
 void Wiimote::GetAccelData(u8* const data, u8* const buttons)
 {
 	const bool has_focus = HAS_FOCUS;
-	const bool is_sideways = m_options->settings[1]->value != 0;
-	const bool is_upright = m_options->settings[2]->value != 0;
+	const bool is_sideways = m_options->settings[SETTING_SIDEWAYS_WIIMOTE]->value != 0;
+	const bool is_upright = m_options->settings[SETTING_UPRIGHT_WIIMOTE]->value != 0;
 	accel_cal* calib = (accel_cal*)&m_eeprom[0x16];
 
 	// ----TILT----
-	EmulateTilt(&m_accel, m_tilt, has_focus, is_sideways, is_upright);
+	EmulateTilt(&m_accel, m_tilt, has_focus, is_sideways, is_upright, m_options->settings[SETTING_MOTIONPLUS_FAST]->value != 0);
 
 	// ----SWING----
 	// ----SHAKE----
@@ -424,11 +442,18 @@ void Wiimote::GetAccelData(u8* const data, u8* const buttons)
 		EmulateShake(&m_accel, calib, m_shake, m_shake_step);
 		UDPTLayer::GetAcceleration(m_udp, &m_accel);
 	}
+
 	wm_accel* dt = (wm_accel*)data;
 	double cx,cy,cz;
-	cx=trim(m_accel.x*(calib->one_g.x-calib->zero_g.x)+calib->zero_g.x);
-	cy=trim(m_accel.y*(calib->one_g.y-calib->zero_g.y)+calib->zero_g.y);
-	cz=trim(m_accel.z*(calib->one_g.z-calib->zero_g.z)+calib->zero_g.z);
+	//cx=Common::trim8(m_accel.x*(calib->one_g.x-calib->zero_g.x)+calib->zero_g.x);
+	//cy=Common::trim8(m_accel.y*(calib->one_g.y-calib->zero_g.y)+calib->zero_g.y);
+	//cz=Common::trim8(m_accel.z*(calib->one_g.z-calib->zero_g.z)+calib->zero_g.z);
+	cx=m_accel.x*(calib->one_g.x-calib->zero_g.x)+calib->zero_g.x;
+	cx=(cx <= 0 ? 0 : (cx >= 0xff ? 0xff : cx));
+	cy=m_accel.y*(calib->one_g.y-calib->zero_g.y)+calib->zero_g.y;
+	cy=(cy <= 0 ? 0 : (cy >= 0xff ? 0xff : cy));
+	cz=m_accel.z*(calib->one_g.z-calib->zero_g.z)+calib->zero_g.z;
+	cz=(cz <= 0 ? 0 : (cz >= 0xff ? 0xff : cz));
 	dt->x=u8(cx);
 	dt->y=u8(cy);
 	dt->z=u8(cz);
@@ -448,6 +473,14 @@ inline void LowPassFilter(double & var, double newval, double period)
 
 void Wiimote::GetIRData(u8* const data, bool use_accel)
 {
+	if ((m_options->settings[SETTING_IR_HIDE]->value != 0
+		|| m_ir->controls[C_HIDE]->control_ref->State())
+		&& !m_ir->controls[C_SHOW]->control_ref->State())
+	{
+		memset(data, 0xFF, 10);
+		return;
+	}
+
 	const bool has_focus = HAS_FOCUS;
 
 	u16 x[4], y[4];
@@ -457,7 +490,7 @@ void Wiimote::GetIRData(u8* const data, bool use_accel)
 	{
 		float xx = 10000, yy = 0, zz = 0;
 		double nsin,ncos;
-		
+
 		if (use_accel)
 		{
 			double ax,az,len;
@@ -466,7 +499,7 @@ void Wiimote::GetIRData(u8* const data, bool use_accel)
 			len=sqrt(ax*ax+az*az);
 			if (len)
 			{
-				ax/=len; 
+				ax/=len;
 				az/=len; //normalizing the vector
 				nsin=ax;
 				ncos=az;
@@ -489,17 +522,17 @@ void Wiimote::GetIRData(u8* const data, bool use_accel)
 		LowPassFilter(ir_sin,nsin,1.0f/60);
 		LowPassFilter(ir_cos,ncos,1.0f/60);
 
-		m_ir->GetState(&xx, &yy, &zz, true);
+		m_ir->GetState(&xx, &yy, &zz, m_ir->settings[C_RANGE]->value, true, false, true);
 		UDPTLayer::GetIR(m_udp, &xx, &yy, &zz);
 
 		Vertex v[4];
-		
+
 		static const int camWidth=1024;
 		static const int camHeight=768;
-		static const double bndup=-0.315447;	
-		static const double bnddown=0.85;	
-		static const double bndleft=0.443364;		
-		static const double bndright=-0.443364;	
+		static const double bndup=-0.315447;
+		static const double bnddown=0.85;
+		static const double bndleft=0.443364;
+		static const double bndright=-0.443364;
 		static const double dist1=100.f/camWidth; //this seems the optimal distance for zelda
 		static const double dist2=1.2f*dist1;
 
@@ -553,7 +586,7 @@ void Wiimote::GetIRData(u8* const data, bool use_accel)
 		wm_ir_basic* const irdata = (wm_ir_basic*)data;
 		for (unsigned int i=0; i<2; ++i)
 		{
-			if (x[i*2] < 1024 && y[i*2] < 768) 
+			if (x[i*2] < 1024 && y[i*2] < 768)
 			{
 				irdata[i].x1 = u8(x[i*2]);
 				irdata[i].x1hi = x[i*2] >> 8;
@@ -600,51 +633,160 @@ void Wiimote::GetIRData(u8* const data, bool use_accel)
 
 void Wiimote::GetExtData(u8* const data)
 {
-	m_extension->GetState(data, HAS_FOCUS);
-
-	// i dont think anything accesses the extension data like this, but ill support it. Indeed, commercial games don't do this.
-	// i think it should be unencrpyted in the register, encrypted when read.
-	memcpy(m_reg_ext.controller_data, data, sizeof(wm_extension));
-
 	// motionplus pass-through modes
-	if (m_motion_plus_active)
+	if (GetMotionPlusActive())
 	{
-		switch (m_reg_motion_plus.ext_identifier[0x4])
+		if (mp_passthrough && m_extension->active_extension != EXT_NONE)
 		{
-		// nunchuck pass-through mode
-		// Bit 7 of byte 5 is moved to bit 6 of byte 5, overwriting it
-		// Bit 0 of byte 4 is moved to bit 7 of byte 5
-		// Bit 3 of byte 5 is moved to bit 4 of byte 5, overwriting it
-		// Bit 1 of byte 5 is moved to bit 3 of byte 5
-		// Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it 
-		case 0x5:
-			//data[5] & (1 << 7)
-			//data[4] & (1 << 0)
-			//data[5] & (1 << 3)
-			//data[5] & (1 << 1)
-			//data[5] & (1 << 0)
-			break;
+			m_extension->GetState(data, HAS_FOCUS);
+			memcpy(m_reg_ext.controller_data, data, sizeof(wm_nc));
+			wm_nc* nc = (wm_nc*)data;
+			wm_nc_mp* nc_mp = (wm_nc_mp*)data;
 
-		// classic controller/musical instrument pass-through mode
-		// Bit 0 of Byte 4 is overwritten
-		// Bits 0 and 1 of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting
-		case 0x7:
-			//data[4] & (1 << 0)
-			//data[5] & (1 << 0)
-			//data[5] & (1 << 1)
-			break;
+			switch (m_reg_motion_plus.ext_identifier[0x4])
+			{
+			// nunchuck pass-through mode
+			case 0x5:
+				nc_mp->jx = nc->jx;
+				nc_mp->jy = nc->jy;
+				nc_mp->ax = nc->ax;
+				nc_mp->ay = nc->ay;
+				nc_mp->az = nc->az >> 1;
+				nc_mp->azL = nc->az & 1;
+				nc_mp->bz = nc->bt.z;
+				nc_mp->bc = nc->bt.c;
+				nc_mp->is_mp_data = 0;
+				nc_mp->extension_connected = 1;
+				nc_mp->dummy = 0;
+				break;
+
+			// classic controller/musical instrument pass-through mode
+			// Bit 0 of Byte 4 is overwritten
+			// Bits 0 and 1 of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting
+			case 0x7:
+				//data[4] & (1 << 0)
+				//data[5] & (1 << 0)
+				//data[5] & (1 << 1)
+				break;
+
+			// unknown pass-through mode
+			default:
+				break;
+			}
+		}
+		else
+		{
+			accel_cal* calib = (accel_cal*)&m_eeprom[0x16];
 
-		// unknown pass-through mode
-		default:
-			break;
+			memset(data, 0, sizeof(wm_motionplus));
+			wm_motionplus* mp = (wm_motionplus*)data;
+
+			AccelData sh;
+			memset(&sh, 0, sizeof(sh));
+			static u8 m_shake_step_mp[3];
+			float ry = 0, rp = 0, rr = 0;
+			float p = 0, r = 0, y = 0;
+			static u16 _p, _r, _y;
+
+			const bool is_sideways = m_options->settings[SETTING_SIDEWAYS_WIIMOTE]->value != 0;
+			const bool is_upright = m_options->settings[SETTING_UPRIGHT_WIIMOTE]->value != 0;
+			const bool fast = m_tilt->controls[T_FAST]->control_ref->State() != 0
+					|| m_options->settings[SETTING_MOTIONPLUS_FAST]->value != 0;
+
+			if (HAS_FOCUS)
+			{
+				// shake
+				EmulateShake(&sh, calib, m_shake, m_shake_step_mp);
+				for (unsigned int i=0; i<3; ++i)
+					(&(sh.x))[i] *= m_shake->settings[B_RANGE]->value;
+
+				// tilt
+				m_tilt->GetState(&rp, &rr, &ry, true, 0, 1.0, false);
+				//rp = MathUtil::Trim(rp, -1, 1);
+				rp = (rp < -1 ? -1 : (rp > 1 ? 1 : rp));
+				//rr = MathUtil::Trim(rr, -1, 1);
+				rr = (rr < -1 ? -1 : (rr > 1 ? 1 : rr));
+				//ry = MathUtil::Trim(ry, -1, 1);
+				ry = (ry < -1 ? -1 : (ry > 1 ? 1 : ry));
+
+				// gyro controls
+				p = +rp	+sh.y;
+				r = -rr	-sh.z;
+				y = -ry	-sh.x;
+			}
+
+			// fast or slow
+			if (!fast)
+			{
+				mp->pitch_slow = 1;
+				mp->roll_slow = 1;
+				mp->yaw_slow = 1;
+			}
+
+			// report
+			//_p = Common::trim14(p * 0x1fff + 0x1f7f);
+			_p = p * 0x1fff + 0x1f7f;
+			_p = (_p <= 0 ? 0 : (_p >= 0x3fff ? 0x3fff : _p));
+			//_r = Common::trim14(r * 0x1fff + 0x1f7f);
+			_r = r * 0x1fff + 0x1f7f;
+			_r = (_r <= 0 ? 0 : (_r >= 0x3fff ? 0x3fff : _r));
+			//_y = Common::trim14(y * 0x1fff + 0x1f7f);
+			_y = y * 0x1fff + 0x1f7f;
+			_y = (_y <= 0 ? 0 : (_y >= 0x3fff ? 0x3fff : _y));
+			mp->pitch1 = _p & 0xff; mp->pitch2 = ((_p >> 8) & 0x3f);
+			mp->roll1 = _r & 0xff; mp->roll2 = ((_r >> 8) & 0x3f);
+			mp->yaw1 = _y & 0xff; mp->yaw2 = ((_y >> 8) & 0x3f);
+			mp->is_mp_data = 1;
+			mp->extension_connected = (m_extension->active_extension != EXT_NONE) ? 1 : 0;
+			mp->dummy = 0;
+
+#if 0
+			// log
+			float mx = 0, my = 0, mz = 0;
+			m_ir->GetState(&mx, &my, &mz, 0, false, false, false);
+
+			AccelData n_accel;
+			memset(&n_accel, 0, sizeof(n_accel));
+			if (m_extension->active_extension > 0)
+				n_accel = ((WiimoteEmu::Nunchuk*)m_extension->attachments[m_extension->active_extension])->m_accel;
+			accel_cal* n_calib = (accel_cal*)&((WiimoteEmu::Nunchuk*)m_extension->attachments[m_extension->active_extension])->reg[0x20];
+			NOTICE_LOG_S(CONSOLE,
+			"%5.2f %5.2f%s"
+			//" | %02x %02x %02x"
+			" | %5.2f %5.2f %5.2f"
+			//" | %02x %02x %02x"
+			" | %5.2f %5.2f %5.2f"
+			//" | %5.2f %5.2f %5.2f"
+			" | %5.2f %5.2f %5.2f"
+			//" | %4x %4x %4x"
+			" %s%s%s"
+			//" (%02x %02x %02x %02x %02x %02x)",
+				, mx, my, (m_options->settings[SETTING_IR_HIDE]->value != 0 ? "*" : " ")
+				, m_accel.x, m_accel.y, m_accel.z
+				//, (u8)trim(m_accel.x*(calib->one_g.x-calib->zero_g.x)+calib->zero_g.x), (u8)trim(m_accel.y*(calib->one_g.y-calib->zero_g.y)+calib->zero_g.y), (u8)trim(m_accel.z*(calib->one_g.z-calib->zero_g.z)+calib->zero_g.z)
+				, n_accel.x, n_accel.y, n_accel.z
+				//, (u8)trim(n_accel.x*(n_calib->one_g.x-n_calib->zero_g.x)+n_calib->zero_g.x), (u8)trim(n_accel.y*(n_calib->one_g.y-n_calib->zero_g.y)+n_calib->zero_g.y), (u8)trim(n_accel.z*(n_calib->one_g.z-n_calib->zero_g.z)+n_calib->zero_g.z)
+				//, sh.y, sh.z, sh.x
+				//, rp, rr, ry
+				, p, r, y
+				//, _p, _r, _y
+				//, mp->yaw1, mp->yaw2, mp->pitch1, mp->pitch2, mp->roll1, mp->roll2
+				, mp->pitch_slow ? "*":" ", mp->roll_slow ? "*":" ", mp->yaw_slow ? "*":" "
+				);
+#endif
 		}
 
-		((wm_motionplus_data*)data)->is_mp_data = 0;
-		((wm_motionplus_data*)data)->extension_connected = m_extension->active_extension;
+		mp_passthrough = !mp_passthrough;
+	}
+	else
+	{
+		m_extension->GetState(data, HAS_FOCUS);
+		// i dont think anything accesses the extension data like this, but ill support it. Indeed, commercial games don't do this.
+		// i think it should be unencrpyted in the register, encrypted when read.
+		memcpy(m_reg_ext.controller_data, data, sizeof(wm_nc));
+		if (0xAA == m_reg_ext.encryption)
+			wiimote_encrypt(&m_ext_key, data, 0x00, sizeof(wm_nc));
 	}
-
-	if (0xAA == m_reg_ext.encryption)
-		wiimote_encrypt(&m_ext_key, data, 0x00, sizeof(wm_extension));
 }
 
 void Wiimote::Update()
@@ -659,7 +801,7 @@ void Wiimote::Update()
 
 	u8 data[MAX_PAYLOAD];
 	memset(data, 0, sizeof(data));
-	
+
 	// figure out what data we need
 	s8 rptf_size = MAX_PAYLOAD;
 
@@ -676,28 +818,30 @@ void Wiimote::Update()
 	{
 		data[0] = 0xA1;
 		data[1] = m_reporting_mode;
-	
+
+		GetSettings();
+
 		// core buttons
 		if (rptf.core)
 			GetCoreData(data + rptf.core);
-	
+
 		// acceleration
 		if (rptf.accel)
 			GetAccelData(data + rptf.accel, rptf.core?(data+rptf.core):NULL);
-	
+
 		// IR
 		if (rptf.ir)
-			GetIRData(data + rptf.ir, (rptf.accel != 0)); 
-	
+			GetIRData(data + rptf.ir, (rptf.accel != 0));
+
 		// extension
 		if (rptf.ext)
 			GetExtData(data + rptf.ext);
-	
+
 		// hybrid wiimote stuff (for now, it's not supported while recording)
 		if (WIIMOTE_SRC_HYBRID == g_wiimote_sources[m_index] && !Movie::IsRecordingInput())
 		{
 			using namespace WiimoteReal;
-	
+
 			std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
 			if (g_wiimotes[m_index])
 			{
@@ -712,31 +856,31 @@ void Wiimote::Update()
 						if (real_data[1] >= WM_REPORT_CORE)
 						{
 							const ReportFeatures& real_rptf = reporting_mode_features[real_data[1] - WM_REPORT_CORE];
-	
+
 							// force same report type from real-wiimote
 							if (&real_rptf != &rptf)
 								rptf_size = 0;
-	
+
 							// core
 							// mix real-buttons with emu-buttons in the status struct, and in the report
 							if (real_rptf.core && rptf.core)
 							{
-								m_status.buttons |= *(wm_core*)(real_data + real_rptf.core);
-								*(wm_core*)(data + rptf.core) = m_status.buttons;
+								memcpy(&m_status.buttons, real_data + real_rptf.core, sizeof(m_status.buttons));
+								memcpy(data + rptf.core, &m_status.buttons, sizeof(m_status.buttons));
 							}
-	
+
 							// accel
 							// use real-accel data always i guess
 							if (real_rptf.accel && rptf.accel)
 								memcpy(data + rptf.accel, real_data + real_rptf.accel, sizeof(wm_accel));
-	
+
 							// ir
 							// TODO
-	
+
 							// ext
 							// use real-ext data if an emu-extention isn't chosen
 							if (real_rptf.ext && rptf.ext && (0 == m_extension->switch_extension))
-								memcpy(data + rptf.ext, real_data + real_rptf.ext, sizeof(wm_extension));
+								memcpy(data + rptf.ext, real_data + real_rptf.ext, sizeof(wm_nc));
 						}
 						else if (WM_ACK_DATA != real_data[1] || m_extension->active_extension > 0)
 							rptf_size = 0;
@@ -744,7 +888,7 @@ void Wiimote::Update()
 							// use real-acks if an emu-extension isn't chosen
 							rptf_size = -1;
 						break;
-	
+
 						// use all status reports, after modification of the extension bit
 					case WM_STATUS_REPORT :
 						//if (m_extension->switch_extension)
@@ -753,14 +897,14 @@ void Wiimote::Update()
 							((wm_status_report*)(real_data + 2))->extension = 1;
 						rptf_size = -1;
 						break;
-	
+
 						// use all read-data replies
 					case WM_READ_DATA_REPLY:
 						rptf_size = -1;
 						break;
-	
+
 					}
-	
+
 					// copy over report from real-wiimote
 					if (-1 == rptf_size)
 					{
@@ -794,7 +938,7 @@ void Wiimote::Update()
 	}
 }
 
-void Wiimote::ControlChannel(const u16 _channelID, const void* _pData, u32 _Size) 
+void Wiimote::ControlChannel(const u16 _channelID, const void* _pData, u32 _Size)
 {
 	// Check for custom communication
 	if (99 == _channelID)
@@ -823,7 +967,7 @@ void Wiimote::ControlChannel(const u16 _channelID, const void* _pData, u32 _Size
 	case HID_TYPE_SET_REPORT :
 		if (HID_PARAM_INPUT == hidp->param)
 		{
-			PanicAlert("HID_TYPE_SET_REPORT - INPUT"); 
+			PanicAlert("HID_TYPE_SET_REPORT - INPUT");
 		}
 		else
 		{
@@ -832,6 +976,7 @@ void Wiimote::ControlChannel(const u16 _channelID, const void* _pData, u32 _Size
 			HidOutputReport((wm_report*)hidp->data);
 
 			u8 handshake = HID_HANDSHAKE_SUCCESS;
+			WiimoteEmu::Spy(this, &handshake, 1);
 			Core::Callback_WiimoteInterruptChannel(m_index, _channelID, &handshake, 1);
 		}
 		break;
@@ -849,6 +994,8 @@ void Wiimote::ControlChannel(const u16 _channelID, const void* _pData, u32 _Size
 
 void Wiimote::InterruptChannel(const u16 _channelID, const void* _pData, u32 _Size)
 {
+	Spy(this, _pData, _Size);
+
 	// this all good?
 	m_reporting_channel = _channelID;
 
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.h
index 9ed58f8..ded1339 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.h
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.h
@@ -32,6 +32,58 @@ class Wiimote;
 namespace WiimoteEmu
 {
 
+/* An example of a factory default first bytes of the Eeprom memory. There are differences between
+   different Wiimotes, my Wiimote had different neutral values for the accelerometer. */
+static const u8 eeprom_data_0[] = {
+	// IR, maybe more
+	// assuming last 2 bytes are checksum
+	0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, /*0x74, 0xD3,*/ 0x00, 0x00,	// messing up the checksum on purpose - why?
+	0xA1, 0xAA, 0x8B, 0x99, 0xAE, 0x9E, 0x78, 0x30, 0xA7, /*0x74, 0xD3,*/ 0x00, 0x00,
+	// Accelerometer
+	// 0g x,y,z, 1g x,y,z, idk, last byte is a checksum
+	0x80, 0x80, 0x80, 0x00, 0x9A, 0x9A, 0x9A, 0x00, 0x40, 0xE3,
+	0x80, 0x80, 0x80, 0x00, 0x9A, 0x9A, 0x9A, 0x00, 0x40, 0xE3,
+};
+
+static const u8 eeprom_data_16D0[] = {
+	0x00, 0x00, 0x00, 0xFF, 0x11, 0xEE, 0x00, 0x00,
+	0x33, 0xCC, 0x44, 0xBB, 0x00, 0x00, 0x66, 0x99,
+	0x77, 0x88, 0x00, 0x00, 0x2B, 0x01, 0xE8, 0x13
+};
+
+static const u8 mp_id[] = { 0x00, 0x00, 0xA6, 0x20, 0x00, 0x05 };
+
+/* Default calibration for the motion plus, 0xA60020 */
+static const u8 motion_plus_calibration[] =
+{
+	0x7b, 0xec, 0x76, 0xca, 0x76, 0x2c, // gyroscope neutral values (each 14 bit, last 2bits unknown) fast motion p/r/y
+	0x32, 0xdc, 0xcc, 0xd7,				// "" min/max p
+	0x2e, 0xa8, 0xc8, 0x77,				// "" min/max r
+	0x5e, 0x02,
+
+	0x76, 0x0f, 0x79, 0x3d, 0x77, 0x9b, // gyroscope neutral values (each 14 bit, last 2bits unknown) slow motion
+	0x39, 0x43, 0xca, 0xa8,				// "" min/max p
+	0x31, 0xc8,							// "" min r
+	0x2d, 0x1c, 0xbc, 0x33
+}; // TODO: figure out remaining parts;
+
+// 0xA60050
+static const u8 mp_gyro_calib[] =
+{
+	0xab,0x8c,0x00,0xe8,0x24,0xeb,0xf1,0xb8,0x77,0x62,0x52,0x44,0x3e,0x97,0x6f,0x5a,
+	0xf2,0x5e,0x7f,0x6d,0xe3,0xaf,0x9e,0xa4,0x45,0xec,0xe7,0x2f,0x2c,0xb9,0x22,0xb3,
+	0xe1,0x77,0x52,0xdf,0xac,0x6a,0x2e,0x1a,0xf1,0x91,0x63,0x13,0xa7,0xb7,0x86,0xaa,
+	0x6a,0x64,0xbb,0x74,0x7f,0x56,0xa0,0x50,0x9d,0x00,0xdd,0x76,0x97,0xf7,0x3e,0x7a,
+};
+
+static const u8 mp_gyro_calib2[] =
+{
+	0x52,0x16,0x81,0xaf,0xf8,0xad,0x40,0xfd,0xc7,0xb5,0xab,0x33,0xa3,0x38,0x9e,0xdb,
+	0xb0,0xa2,0xcf,0xbf,0x69,0x3a,0xfc,0x78,0x16,0x80,0x4b,0xe0,0x97,0xbd,0x3e,0x58,
+	0x71,0x64,0x88,0x5a,0x44,0x22,0x05,0x00,0x1e,0xa9,0xa5,0x35,0xf1,0xd0,0x0e,0x06,
+	0xa6,0xe9,0x9c,0x6c,0x4b,0xa8,0x2e,0x1a,0xac,0x9a,0x02,0x17,0x54,0xe7,0xba,0x3e,
+};
+
 struct ReportFeatures
 {
 	u8 core, accel, ir, ext, size;
@@ -80,23 +132,18 @@ void EmulateShake(AccelData* const accel_data
 
 void EmulateTilt(AccelData* const accel
 	 , ControllerEmu::Tilt* const tilt_group
-	 , const bool focus, const bool sideways = false, const bool upright = false);
+	 , const bool focus, const bool sideways = false, const bool upright = false
+	 , const bool fast = false);
 
 void EmulateSwing(AccelData* const accel
 	 , ControllerEmu::Force* const tilt_group
 	 , const bool sideways = false, const bool upright = false);
 
-inline double trim(double a)
-{
-	if (a<=0) return 0;
-	if (a>=255) return 255;
-	return a;
-}
-
 class Wiimote : public ControllerEmu
 {
 friend class WiimoteReal::Wiimote;
 friend void Spy(Wiimote* wm_, const void* data_, int size_);
+
 public:
 
 	enum
@@ -114,6 +161,14 @@ public:
 		BUTTON_MINUS =	 0x1000,
 		BUTTON_HOME =	0x8000,
 	};
+	enum
+	{
+		EXT_NONE = 0,
+		EXT_NUNCHUK,
+		EXT_CLASSIC_CONTROLLER,
+		EXT_GUITARHERO,
+		EXT_WBB,
+	};
 
 	Wiimote( const unsigned int index );
 	std::string GetName() const;
@@ -133,6 +188,7 @@ protected:
 	void HandleExtensionSwap();
 	void UpdateButtonsStatus(bool has_focus);
 
+	void GetSettings();
 	void GetCoreData(u8* const data);
 	void GetAccelData(u8* const data, u8* const buttons);
 	void GetIRData(u8* const data, bool use_accel);
@@ -153,13 +209,16 @@ private:
 
 	void ReportMode(const wm_report_mode* const dr);
 	void SendAck(const u8 _reportID);
-	void RequestStatus(const wm_request_status* const rs = NULL);
+	void RequestStatus(const wm_request_status* const rs = NULL, int ext = -1);
 	void ReadData(const wm_read_data* const rd);
 	void WriteData(const wm_write_data* const wd);
 	void SendReadDataReply(ReadRequest& _request);
 	void SpeakerData(wm_speaker_data* sd);
 	bool NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size);
 
+	bool GetMotionPlusAttached() const;
+	bool GetMotionPlusActive() const;
+
 	// control groups
 	Buttons		*m_buttons, *m_dpad, *m_shake;
 	Cursor*			m_ir;
@@ -181,12 +240,12 @@ private:
 
 	bool	m_rumble_on;
 	bool	m_speaker_mute;
-	bool	m_motion_plus_present;
-	bool	m_motion_plus_active;
 
+	bool	mp_passthrough;
 	bool	m_reporting_auto;
 	u8		m_reporting_mode;
 	u16		m_reporting_channel;
+	u8		mp_last_write_reg;
 
 	u8		m_shake_step[3];
 
@@ -207,15 +266,29 @@ private:
 
 	struct MotionPlusReg
 	{
-		u8 unknown[0xF0];
+		u8 unknown1[0x20];
+
+		// address 0x20
+		u8 calibration[0x20];
+
+		// address 0x40
+		u8 ext_calib[0x10];
+
+		// address 0x50
+		u8 gyro_calib[0xA0];
 
 		// address 0xF0
-		u8	activated;
+		u8 activated;
+
+		u8 unknown3[6];
+
+		// address 0xF7
+		u8 state;
 
-		u8 unknown2[9];
+		u8 unknown4[2];
 
 		// address 0xFA
-		u8	ext_identifier[6];
+		u8 ext_identifier[6];
 
 	}	m_reg_motion_plus;
 
@@ -245,6 +318,7 @@ private:
 	}	m_reg_speaker;
 };
 
+extern Wiimote* spy_wm;
 void Spy(Wiimote* wm_, const void* data_, int size_);
 
 }
diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteHid.h b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteHid.h
index 751c177..0119cba 100644
--- a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteHid.h
+++ b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteHid.h
@@ -63,14 +63,23 @@ struct wm_ir_extended
 	u8 yhi : 2;
 };
 
-struct wm_extension
+struct wm_nc_bt
+{
+	u8 z : 1; // buttons
+	u8 c : 1;
+	u8 axL : 2; // accelerometer
+	u8 ayL : 2;
+	u8 azL : 2;
+};
+
+struct wm_nc
 {
 	u8 jx; // joystick x, y
 	u8 jy;
 	u8 ax; // accelerometer
 	u8 ay;
 	u8 az;
-	u8 bt; // buttons
+	wm_nc_bt bt;
 };
 
 struct wm_classic_extension
@@ -147,25 +156,77 @@ struct wm_turntable_extension
 	};
 };
 
-struct wm_motionplus_data
+struct wm_nc_mp //nunchuk data on motion-plus pass-through
 {
-	u8 yaw1;
+	u8 jx;
+	u8 jy;
+	u8 ax;
+	u8 ay;
 
-	u8 roll1;
+	u8 extension_connected : 1; // 1 usually
+	u8 az : 7;
+	u8 dummy : 1; //0 always
 
+	u8 is_mp_data : 1; //0 when nunchuk interleaved data
+	u8 bz : 1;
+	u8 bc : 1;
+	u8 axL : 1; // ls 1, ls0 = 0 by default,
+	u8 ayL : 1;
+	u8 azL : 2;
+};
+
+struct wm_motionplus
+{
+	u8 yaw1;
+	u8 roll1;
 	u8 pitch1;
 
-	u8 yaw2 : 6;
-	u8 yaw_slow : 1;
 	u8 pitch_slow : 1;
+	u8 yaw_slow : 1;
+	u8 yaw2 : 6;
+	u8 extension_connected : 1;
 
-	u8 roll2 : 6;
 	u8 roll_slow : 1;
-	u8 extension_connected : 1;
+	u8 roll2 : 6;
+	u8 dummy : 1;
 
-	u8 pitch2 : 6;
 	u8 is_mp_data : 1;
-	u8 zero : 1;
+	u8 pitch2 : 6;
+};
+
+struct wm_motionplus_calibration
+{
+	u16 pitch : 14;
+	u8 : 2;
+	u16 roll : 14;
+	u8 : 2;
+	u16 yaw : 14;
+	u8 : 2;
+
+	u16 pitch_min : 14;
+	u8 : 2;
+	u16 pitch_max : 14;
+	u8 : 2;
+	u16 roll_min : 14;
+	u8 : 2;
+	u16 roll_max : 14;
+	u8 : 2;
+
+	u16 pitch_slow : 14;
+	u8 : 2;
+	u16 roll_slow  : 14;
+	u8 : 2;
+	u16 yaw_slow  : 14;
+	u8 : 2;
+
+	u16 pitch_min_slow : 14;
+	u8 : 2;
+	u16 pitch_max_slow : 14;
+	u8 : 2;
+	u16 roll_min_slow : 14;
+	u8 : 2;
+	u16 : 16;
+	u16 : 16;
 };
 
 struct wm_report
@@ -263,6 +324,11 @@ struct wm_read_data
 #define WM_SPACE_REGS2 2
 #define WM_SPACE_INVALID 3
 
+#define WIIMOTE_REG_SPEAKER 0xa2
+#define WIIMOTE_REG_EXT 0xa4
+#define WIIMOTE_REG_MP 0xa6
+#define WIIMOTE_REG_IR 0xb0
+
 #define WM_READ_DATA_REPLY 0x21
 struct wm_read_data_reply
 {
@@ -308,7 +374,7 @@ struct wm_report_core_accel_ext16
 {
 	wm_core c;
 	wm_accel a;
-	wm_extension ext;
+	wm_nc ext;
 	//wm_ir_basic ir[2];
 	u8 pad[10];
 
@@ -322,8 +388,7 @@ struct wm_report_core_accel_ir10_ext6
 	wm_core c;
 	wm_accel a;
 	wm_ir_basic ir[2];
-	//u8 ext[6];
-	wm_extension ext;
+	wm_nc ext;
 };
 
 #define WM_REPORT_EXT21 0x3d // never used?
@@ -356,17 +421,17 @@ struct accel_cal
 	struct
 	{
 		u8 x, y, z;
-		u8 xlo : 2;
-		u8 ylo : 2;
-		u8 zlo : 2;
+		u8 xL : 2;
+		u8 yL : 2;
+		u8 zL : 2;
 	} zero_g;
 
 	struct
 	{
 		u8 x, y, z;
-		u8 xlo : 2;
-		u8 ylo : 2;
-		u8 zlo : 2;
+		u8 xL : 2;
+		u8 yL : 2;
+		u8 zL : 2;
 	} one_g;
 };
 
diff --git a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp
index ee6de5f..550cd22 100644
--- a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp
+++ b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp
@@ -293,8 +293,11 @@ void Wiimote::Update()
 
 	// Send the report
 	if (!rpt.empty() && m_channel > 0)
+	{
+		WiimoteEmu::Spy(NULL, rpt.data(), rpt.size());
 		Core::Callback_WiimoteInterruptChannel(index, m_channel,
 			rpt.data(), rpt.size());
+	}
 }
 
 void Wiimote::Prepare(int _index)
diff --git a/Source/Core/DolphinWX/Src/InputConfigDiagBitmaps.cpp b/Source/Core/DolphinWX/Src/InputConfigDiagBitmaps.cpp
index 6aa37d7..a0655f6 100644
--- a/Source/Core/DolphinWX/Src/InputConfigDiagBitmaps.cpp
+++ b/Source/Core/DolphinWX/Src/InputConfigDiagBitmaps.cpp
@@ -4,6 +4,8 @@
 
 #include "InputConfigDiag.h"
 #include "WxUtils.h"
+#include "Host.h"
+#include "../../Core/Src/Core.h"
 
 void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
 {
@@ -17,12 +19,59 @@ void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
 		return;
 
 	GamepadPage* const current_page = (GamepadPage*)m_pad_notebook->GetPage(m_pad_notebook->GetSelection());
+	static bool gyro_disabled;
+	static bool hide_ir;
 
 	std::vector< ControlGroupBox* >::iterator
 		g = current_page->control_groups.begin(),
 		ge = current_page->control_groups.end();
 	for ( ; g != ge; ++g  )
 	{
+		switch ( (*g)->control_group->type )
+		{
+		case GROUP_TYPE_OPTIONS :
+		{
+			gyro_disabled = (*g)->control_group->settings.size() > SETTING_MOTIONPLUS
+				&& (*g)->control_group->settings[SETTING_MOTIONPLUS]->value == 0;
+
+			hide_ir = (*g)->control_group->settings.size() > SETTING_IR_HIDE
+				&& (*g)->control_group->settings[SETTING_IR_HIDE]->value != 0;
+
+			// update ui
+			std::vector<ControllerEmu::ControlGroup::Setting*>::const_iterator
+				si = (*g)->control_group->settings.begin(),
+				se = (*g)->control_group->settings.end();
+			for (; si!=se; ++si)
+			{
+				(*si)->GetState();
+			}
+
+			std::vector<ControlButton*> cb = (*g)->control_buttons;
+			std::vector<ControlButton*>::const_iterator bi = (*g)->control_buttons.begin()
+				, be = (*g)->control_buttons.end();
+			for (int n = 0; bi!=be; ++bi, ++n)
+			{
+				(*bi)->Enable(!(gyro_disabled
+					&& n == SETTING_MOTIONPLUS_FAST));
+			}
+
+			std::vector<PadSetting*>::const_iterator
+				oi = (*g)->options.begin(),
+				oe = (*g)->options.end();
+			for (int n = 0; oi!=oe; ++oi, ++n)
+			{
+				if ((*oi)->wxcontrol->IsKindOf(CLASSINFO(wxCheckBox)))
+				{
+					(*oi)->wxcontrol->Enable(!(gyro_disabled
+						&& n == SETTING_MOTIONPLUS_FAST));
+					(*oi)->UpdateGUI();
+				}
+			}
+
+			break;
+		}
+		}
+
 		// if this control group has a bitmap
 		if ( (*g)->static_bitmap )
 		{
@@ -40,37 +89,40 @@ void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
 
 			switch ( (*g)->control_group->type )
 			{
-			case GROUP_TYPE_TILT :
+
 			case GROUP_TYPE_STICK :
 			case GROUP_TYPE_CURSOR :
 				{
 					// this is starting to be a mess combining all these in one case
 
 					float x = 0, y = 0, z = 0;
-					float xx, yy;
+					float xx, yy, zz;
 
 					switch ((*g)->control_group->type)
 					{
 					case GROUP_TYPE_STICK :
 						((ControllerEmu::AnalogStick*)(*g)->control_group)->GetState( &x, &y, 32.0, 32-1.5 );
+
+						xx = (*g)->control_group->controls[C_RIGHT]->control_ref->State() - (*g)->control_group->controls[C_LEFT]->control_ref->State();
+						yy = (*g)->control_group->controls[C_DOWN]->control_ref->State() - (*g)->control_group->controls[C_UP]->control_ref->State();
+
+						xx *= 32 - 1; xx += 32;
+						yy *= 32 - 1; yy += 32;
 						break;
-					case GROUP_TYPE_TILT :
-						((ControllerEmu::Tilt*)(*g)->control_group)->GetState( &x, &y, 32.0, 32-1.5 );
-						break;
+
+					// read through Cursor::GetState instead of directly from State because that doesn't remove relative input from the emulation
 					case GROUP_TYPE_CURSOR :
-						((ControllerEmu::Cursor*)(*g)->control_group)->GetState( &x, &y, &z );
+						((ControllerEmu::Cursor*)(*g)->control_group)->GetState( &x, &y, &z, ((ControllerEmu::Cursor*)(*g)->control_group)->settings[C_RANGE]->value, true, false );
 						x *= (32-1.5); x+= 32;
 						y *= (32-1.5); y+= 32;
+
+						((ControllerEmu::Cursor*)(*g)->control_group)->GetState( &xx, &yy, &zz, 1.0, false, false );
+						xx *= (32-1.5); xx+= 32;
+						yy *= (32-1.5) * -1; yy+= 32;
+
 						break;
 					}
 
-					xx = (*g)->control_group->controls[3]->control_ref->State();
-					xx -= (*g)->control_group->controls[2]->control_ref->State();
-					yy = (*g)->control_group->controls[1]->control_ref->State();
-					yy -= (*g)->control_group->controls[0]->control_ref->State();
-					xx *= 32 - 1; xx += 32;
-					yy *= 32 - 1; yy += 32;
-
 					// draw the shit
 
 					// ir cursor forward movement
@@ -146,7 +198,7 @@ void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
 					{
 						// deadzone circle
 						dc.SetBrush(*wxLIGHT_GREY_BRUSH);
-						dc.DrawCircle( 32, 32, ((*g)->control_group)->settings[SETTING_DEADZONE]->value * 32 );
+						dc.DrawCircle( 32, 32, ((*g)->control_group)->settings[AS_DEADZONE]->value * 32 );
 					}
 
 					// raw dot
@@ -157,6 +209,10 @@ void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
 					//dc.DrawRectangle( xx-1, 64-yy-4, 2, 8 );
 					//dc.DrawRectangle( xx-4, 64-yy-1, 8, 2 );
 
+					if (hide_ir
+						&& (*g)->control_group->type == GROUP_TYPE_CURSOR)
+						break;
+
 					// adjusted dot
 					if (x!=32 || y!=32)
 					{
@@ -167,17 +223,56 @@ void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
 						//dc.DrawRectangle( x-1, 64-y-4, 2, 8 );
 						//dc.DrawRectangle( x-4, 64-y-1, 8, 2 );
 					}
-
 				}
 				break;
 			case GROUP_TYPE_FORCE :
+			case GROUP_TYPE_TILT :
 				{
 					float raw_dot[3];
 					float adj_dot[3];
-					const float deadzone = 32 * ((*g)->control_group)->settings[0]->value;
+					float gyro_dot[3];
+					float deadzone;
 
 					// adjusted
-					((ControllerEmu::Force*)(*g)->control_group)->GetState( adj_dot, 32.0, 32-1.5 );
+					switch ((*g)->control_group->type)
+					{
+					case GROUP_TYPE_FORCE :
+						deadzone = 32 * ((*g)->control_group)->settings[F_DEADZONE]->value;
+						((ControllerEmu::Force*)(*g)->control_group)->GetState( adj_dot, 32.0, 32-1.5 );
+						break;
+
+					case GROUP_TYPE_TILT :
+						ControllerEmu::Tilt *cg = (ControllerEmu::Tilt*)(*g)->control_group;
+						bool has_gyro = cg->HasGyro();
+
+						deadzone = 32 * cg->settings[T_DEADZONE]->value;
+						cg->GetState( &(*adj_dot), &(*adj_dot)+1, &(*adj_dot)+2, false, 32.0, 32-1.5, !Core::IsRunning() );
+						if (cg->HasGyro())
+							cg->GetState( &(*gyro_dot), &(*gyro_dot)+1, &(*gyro_dot)+2, true, 32.0, 32-1.5, !Core::IsRunning() );
+
+						// update ui
+						std::vector<ControlButton*>::const_iterator bi = (*g)->control_buttons.begin()
+							, be = (*g)->control_buttons.end();
+						for (int n = 0; bi!=be; ++bi, ++n)
+						{
+							(*bi)->Enable(!(has_gyro
+							&& gyro_disabled
+							&& (n == T_FAST
+							|| n == T_GYRO_RANGE_1
+							|| n == T_GYRO_RANGE_2)));
+						}
+
+						std::vector<PadSetting*>::const_iterator si = (*g)->options.begin()
+							, se = (*g)->options.end();
+						for (int n = 0; si!=se; ++si, ++n)
+						{
+							(*si)->wxcontrol->Enable(!(has_gyro
+							&& gyro_disabled
+							&& (n == T_GYRO_RANGE
+							|| n == T_GYRO_SETTLE)));
+						}
+						break;
+					}
 
 					// raw
 					for ( unsigned int i=0; i<3; ++i )
@@ -204,6 +299,15 @@ void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
 						dc.SetBrush(*wxRED_BRUSH);
 						dc.DrawRectangle( 0, adj_dot[2] - 1, 64, 2 );
 					}
+					if ( (*g)->control_group->type == GROUP_TYPE_TILT )
+					{
+						if ( ((ControllerEmu::Tilt*)(*g)->control_group)->HasGyro() )
+						{
+							dc.SetPen(*wxBLUE_PEN);
+							dc.SetBrush(*wxBLUE_BRUSH);
+							dc.DrawRectangle( 0, gyro_dot[2], 64, 1 );
+						}
+					}
 
 					// a rectangle, for looks i guess
 					dc.SetBrush(*wxWHITE_BRUSH);
@@ -226,6 +330,15 @@ void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
 						dc.SetBrush(*wxRED_BRUSH);
 						dc.DrawRectangle( adj_dot[1]-2, adj_dot[0]-2, 4, 4 );
 					}
+					if ( (*g)->control_group->type == GROUP_TYPE_TILT )
+					{
+						if ( ((ControllerEmu::Tilt*)(*g)->control_group)->HasGyro() )
+						{
+							dc.SetPen(*wxBLUE_PEN);
+							dc.SetBrush(*wxBLUE_BRUSH);
+							dc.DrawRectangle( gyro_dot[1] - 1, gyro_dot[0]-  1, 2, 2 );
+						}
+					}
 
 				}
 				break;
@@ -263,7 +376,7 @@ void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
 					}
 
 					delete[] bitmasks;
-					
+
 				}
 				break;
 			case GROUP_TYPE_TRIGGERS :
diff --git a/Source/Core/DolphinWX/Src/WiimoteConfigDiag.h b/Source/Core/DolphinWX/Src/WiimoteConfigDiag.h
index 977760f..32a455b 100644
--- a/Source/Core/DolphinWX/Src/WiimoteConfigDiag.h
+++ b/Source/Core/DolphinWX/Src/WiimoteConfigDiag.h
@@ -1,4 +1,3 @@
-
 #ifndef WIIMOTE_CONFIG_DIAG_H
 #define WIIMOTE_CONFIG_DIAG_H
 
@@ -22,58 +21,58 @@
 class WiimoteConfigDiag : public wxDialog
 {
 public:
-	WiimoteConfigDiag(wxWindow* const parent, InputPlugin& plugin);
+    WiimoteConfigDiag(wxWindow* const parent, InputPlugin& plugin);
 
-	void RefreshRealWiimotes(wxCommandEvent& event);
+    void RefreshRealWiimotes(wxCommandEvent& event);
 
-	void SelectSource(wxCommandEvent& event);
-	void RevertSource();
+    void SelectSource(wxCommandEvent& event);
+    void RevertSource();
 
-	void ConfigEmulatedWiimote(wxCommandEvent& event);
-	void Save(wxCommandEvent& event);
+    void ConfigEmulatedWiimote(wxCommandEvent& event);
+    void Save(wxCommandEvent& event);
 
-	void OnSensorBarPos(wxCommandEvent& event)
-	{
-		SConfig::GetInstance().m_SYSCONF->SetData("BT.BAR", event.GetInt());
-		event.Skip();
-	}
-	void OnSensorBarSensitivity(wxCommandEvent& event)
-	{
-		SConfig::GetInstance().m_SYSCONF->SetData("BT.SENS", event.GetInt());
-		event.Skip();
-	}
-	void OnSpeakerVolume(wxCommandEvent& event)
-	{
-		SConfig::GetInstance().m_SYSCONF->SetData("BT.SPKV", event.GetInt());
-		event.Skip();
-	}
-	void OnMotor(wxCommandEvent& event)
-	{
-		SConfig::GetInstance().m_SYSCONF->SetData("BT.MOT", event.GetInt());
-		event.Skip();
-	}
-	void OnContinuousScanning(wxCommandEvent& event)
-	{
-		SConfig::GetInstance().m_WiimoteContinuousScanning = event.IsChecked();
-		WiimoteReal::Initialize();
-		event.Skip();
-	}
-	void OnEnableSpeaker(wxCommandEvent& event)
-	{
-		SConfig::GetInstance().m_WiimoteEnableSpeaker = event.IsChecked();
-		event.Skip();
-	}
+    void OnSensorBarPos(wxCommandEvent& event)
+    {
+        SConfig::GetInstance().m_SYSCONF->SetData("BT.BAR", event.GetInt());
+        event.Skip();
+    }
+    void OnSensorBarSensitivity(wxCommandEvent& event)
+    {
+        SConfig::GetInstance().m_SYSCONF->SetData("BT.SENS", event.GetInt());
+        event.Skip();
+    }
+    void OnSpeakerVolume(wxCommandEvent& event)
+    {
+        SConfig::GetInstance().m_SYSCONF->SetData("BT.SPKV", event.GetInt());
+        event.Skip();
+    }
+    void OnMotor(wxCommandEvent& event)
+    {
+        SConfig::GetInstance().m_SYSCONF->SetData("BT.MOT", event.GetInt());
+        event.Skip();
+    }
+    void OnContinuousScanning(wxCommandEvent& event)
+    {
+        SConfig::GetInstance().m_WiimoteContinuousScanning = event.IsChecked();
+        WiimoteReal::Initialize();
+        event.Skip();
+    }
+    void OnEnableSpeaker(wxCommandEvent& event)
+    {
+        SConfig::GetInstance().m_WiimoteEnableSpeaker = event.IsChecked();
+        event.Skip();
+    }
 
 private:
-	void Cancel(wxCommandEvent& event);
+    void Cancel(wxCommandEvent& event);
 
-	InputPlugin&	m_plugin;
+    InputPlugin&    m_plugin;
 
-	std::map<wxWindowID, unsigned int> m_wiimote_index_from_ctrl_id;
-	unsigned int m_orig_wiimote_sources[MAX_BBMOTES];
+    std::map<wxWindowID, unsigned int> m_wiimote_index_from_ctrl_id;
+    unsigned int m_orig_wiimote_sources[MAX_BBMOTES];
 
-	wxButton* wiimote_configure_bt[MAX_WIIMOTES];
-	std::map<wxWindowID, unsigned int> m_wiimote_index_from_conf_bt_id;
+    wxButton* wiimote_configure_bt[MAX_WIIMOTES];
+    std::map<wxWindowID, unsigned int> m_wiimote_index_from_conf_bt_id;
 };
 
 
diff --git a/Source/Core/InputCommon/Src/ControllerEmu.cpp b/Source/Core/InputCommon/Src/ControllerEmu.cpp
index 328fcb8..76c5e2e 100644
--- a/Source/Core/InputCommon/Src/ControllerEmu.cpp
+++ b/Source/Core/InputCommon/Src/ControllerEmu.cpp
@@ -3,6 +3,7 @@
 // Refer to the license.txt file included.
 
 #include "ControllerEmu.h"
+#include "../../Core/Src/Core.h"
 
 #if defined(HAVE_X11) && HAVE_X11
 #include <X11/Xlib.h>
@@ -62,6 +63,12 @@ void ControllerEmu::UpdateReferences(ControllerInterface& devi)
 		for (; ci!=ce; ++ci)
 			devi.UpdateReference((*ci)->control_ref, default_device);
 
+		std::vector<ControlGroup::Setting*>::const_iterator
+			si = (*i)->settings.begin(),
+			se = (*i)->settings.end();
+		for (; si!=se; ++si)
+			devi.UpdateReference((*si)->control->control_ref, default_device);
+
 		// extension
 		if (GROUP_TYPE_EXTENSION == (*i)->type)
 		{
@@ -114,6 +121,7 @@ void ControllerEmu::ControlGroup::LoadConfig(IniFile::Section *sec, const std::s
 	{
 		sec->Get((group+(*si)->name).c_str(), &(*si)->value, (*si)->default_value*100);
 		(*si)->value /= 100;
+		sec->Get((group+(*si)->name+"/Input").c_str(), &(*si)->control->control_ref->expression, "");
 	}
 
 	// controls
@@ -178,7 +186,10 @@ void ControllerEmu::ControlGroup::SaveConfig(IniFile::Section *sec, const std::s
 		si = settings.begin(),
 		se = settings.end();
 	for (; si!=se; ++si)
+	{
 		sec->Set((group+(*si)->name).c_str(), (*si)->value*100.0f, (*si)->default_value*100.0f);
+		sec->Set((group+(*si)->name+"/Input").c_str(), (*si)->control->control_ref->expression, "");
+	}
 
 	// controls
 	std::vector<ControlGroup::Control*>::const_iterator
@@ -219,6 +230,22 @@ void ControllerEmu::SaveConfig(IniFile::Section *sec, const std::string& base)
 		(*i)->SaveConfig(sec, defdev, base);
 }
 
+void ControllerEmu::ControlGroup::Setting::GetState()
+{
+	bool state = control->control_ref->State() ? true : false;
+
+	if (state == lastState)
+		return;
+	lastState = state;
+
+	if (!state)
+		return;
+
+	value = !value;
+
+	Core::DisplayMessage(StringFromFormat("%s: %s", name, value ? "Enabled" : "Disabled").c_str(), 2000);
+}
+
 ControllerEmu::AnalogStick::AnalogStick(const char* const _name) : ControlGroup(_name, GROUP_TYPE_STICK)
 {
 	for (unsigned int i = 0; i < 4; ++i)
@@ -232,8 +259,10 @@ ControllerEmu::AnalogStick::AnalogStick(const char* const _name) : ControlGroup(
 
 }
 
-ControllerEmu::Buttons::Buttons(const char* const _name) : ControlGroup(_name, GROUP_TYPE_BUTTONS)
+ControllerEmu::Buttons::Buttons(const char* const _name, bool has_range) : m_has_range(has_range), ControlGroup(_name, GROUP_TYPE_BUTTONS)
 {
+	if (has_range)
+		settings.push_back(new Setting(_trans("Range"), 1.0f, 0, 500));
 	settings.push_back(new Setting(_trans("Threshold"), 0.5f));
 }
 
@@ -258,6 +287,7 @@ ControllerEmu::Slider::Slider(const char* const _name) : ControlGroup(_name, GRO
 ControllerEmu::Force::Force(const char* const _name) : ControlGroup(_name, GROUP_TYPE_FORCE)
 {
 	memset(m_swing, 0, sizeof(m_swing));
+	memset(m_state, 0, sizeof(m_state));
 
 	controls.push_back(new Input(_trans("Up")));
 	controls.push_back(new Input(_trans("Down")));
@@ -266,21 +296,40 @@ ControllerEmu::Force::Force(const char* const _name) : ControlGroup(_name, GROUP
 	controls.push_back(new Input(_trans("Forward")));
 	controls.push_back(new Input(_trans("Backward")));
 
+	settings.push_back(new Setting(_trans("Range"), 1.0f, 0, 500));
 	settings.push_back(new Setting(_trans("Dead Zone"), 0, 0, 50));
 }
 
-ControllerEmu::Tilt::Tilt(const char* const _name)
-	: ControlGroup(_name, GROUP_TYPE_TILT)
+ControllerEmu::Tilt::Tilt(const char* const _name, bool gyro)
+	: m_has_gyro(gyro)
+	, ControlGroup(_name, GROUP_TYPE_TILT)
 {
-	memset(m_tilt, 0, sizeof(m_tilt));
+	memset(m_acc, 0, sizeof(m_acc));
+	memset(m_gyro, 0, sizeof(m_gyro));
+	memset(m_settle, 0, sizeof(m_settle));
 
 	controls.push_back(new Input("Forward"));
 	controls.push_back(new Input("Backward"));
 	controls.push_back(new Input("Left"));
 	controls.push_back(new Input("Right"));
+	controls.push_back(new Input("Yaw Left"));
+	controls.push_back(new Input("Yaw Right"));
 
-	controls.push_back(new Input(_trans("Modifier")));
-
+	if (gyro)
+	{
+		controls.push_back(new Input(_trans("Fast")));
+		controls.push_back(new Input(_trans("Acc Range")));
+		controls.push_back(new Input(_trans("Gyro Range 1")));
+		controls.push_back(new Input(_trans("Gyro Range 2")));
+		settings.push_back(new Setting(_trans("Acc Range"), 1.0f, 0, 500));
+		settings.push_back(new Setting(_trans("Gyro Range"), 1.0f, 0, 500));
+		settings.push_back(new Setting(_trans("Gyro Settle"), 0.25f, 0, 9999));
+	}
+	else
+	{
+		controls.push_back(new Input(_trans("Range")));
+		settings.push_back(new Setting(_trans("Range"), 1.0f, 0, 500));
+	}
 	settings.push_back(new Setting(_trans("Dead Zone"), 0, 0, 50));
 	settings.push_back(new Setting(_trans("Circle Stick"), 0));
 	settings.push_back(new Setting(_trans("Angle"), 0.9f, 0, 180));
@@ -288,18 +337,23 @@ ControllerEmu::Tilt::Tilt(const char* const _name)
 
 ControllerEmu::Cursor::Cursor(const char* const _name)
 	: ControlGroup(_name, GROUP_TYPE_CURSOR)
-	, m_z(0)
 {
+	memset(m_state, 0, sizeof(m_state));
+	memset(m_absolute, 0, sizeof(m_absolute));
+	memset(m_last, 0, sizeof(m_last));
+
 	for (unsigned int i = 0; i < 4; ++i)
 		controls.push_back(new Input(named_directions[i]));
 	controls.push_back(new Input("Forward"));
 	controls.push_back(new Input("Backward"));
+	controls.push_back(new Input(_trans("Range")));
 	controls.push_back(new Input(_trans("Hide")));
+	controls.push_back(new Input(_trans("Show")));
 
+	settings.push_back(new Setting(_trans("Range"), 1.0f, 0, 500));
 	settings.push_back(new Setting(_trans("Center"), 0.5f));
 	settings.push_back(new Setting(_trans("Width"), 0.5f));
 	settings.push_back(new Setting(_trans("Height"), 0.5f));
-
 }
 
 void ControllerEmu::LoadDefaults(const ControllerInterface &ciface)
diff --git a/Source/Core/InputCommon/Src/ControllerEmu.h b/Source/Core/InputCommon/Src/ControllerEmu.h
index 5dc54b4..e53e148 100644
--- a/Source/Core/InputCommon/Src/ControllerEmu.h
+++ b/Source/Core/InputCommon/Src/ControllerEmu.h
@@ -10,6 +10,7 @@
 
 #include <cmath>
 #include <vector>
+#include <list>
 #include <string>
 #include <algorithm>
 
@@ -17,12 +18,15 @@
 
 #include "ControllerInterface/ControllerInterface.h"
 #include "IniFile.h"
+#include "MathUtil.h"
+#include "Timer.h"
 
 #define sign(x) ((x)?(x)<0?-1:1:0)
 
 enum
 {
 	GROUP_TYPE_OTHER,
+	GROUP_TYPE_OPTIONS,
 	GROUP_TYPE_STICK,
 	GROUP_TYPE_MIXED_TRIGGERS,
 	GROUP_TYPE_BUTTONS,
@@ -34,15 +38,111 @@ enum
 	GROUP_TYPE_UDPWII,
 	GROUP_TYPE_SLIDER,
 };
+enum
+{
+	SETTING_BACKGROUND_INPUT,
+	SETTING_SIDEWAYS_WIIMOTE,
+	SETTING_UPRIGHT_WIIMOTE,
+	SETTING_MOTIONPLUS,
+	SETTING_MOTIONPLUS_FAST,
+	SETTING_IR_HIDE,
+};
+enum
+{
+	AS_LEFT,
+	AS_RIGHT,
+	AS_UP,
+	AS_DOWN,
+	AS_MODIFIER,
+};
+enum
+{
+	AS_RADIUS,
+	AS_DEADZONE,
+	AS_SQUARE,
+};
+enum
+{
+	B_THRESHOLD,
+};
+enum
+{
+	B_RANGE,
+	B_THRESHOLD_R,
+};
+enum
+{
+	F_FORWARD,
+	F_BACKWARD,
+	F_LEFT,
+	F_RIGHT,
+	F_UP,
+	F_DOWN,
+};
+enum
+{
+	F_RANGE,
+	F_DEADZONE,
+};
 
 enum
 {
-	SETTING_RADIUS,
-	SETTING_DEADZONE,
-	SETTING_SQUARE,
+	T_FORWARD,
+	T_BACKWARD,
+	T_LEFT,
+	T_RIGHT,
+	T_UP,
+	T_DOWN,
+};
+enum
+{
+	T_FAST = 6,
+	T_ACC_RANGE,
+	T_GYRO_RANGE_1,
+	T_GYRO_RANGE_2,
+};
+enum
+{
+	T_MODIFIER = 6,
+};
+enum
+{
+	T_ACC_RANGE_S,
+	T_GYRO_RANGE,
+	T_GYRO_SETTLE,
+	T_DEADZONE,
+	T_CIRCLESTICK,
+	T_ANGLE,
+};
+enum
+{
+	T_RANGE,
+	T_DEADZONE_N,
+	T_CIRCLESTICK_N,
+	T_ANGLE_N,
+};
+
+enum
+{
+	C_UP,
+	C_DOWN,
+	C_LEFT,
+	C_RIGHT,
+	C_FORWARD,
+	C_BACKWARD,
+	C_RANGE_MODIFIER,
+	C_HIDE,
+	C_SHOW,
+};
+enum
+{
+	C_RANGE,
+	C_CENTER,
+	C_WIDTH,
+	C_HEIGHT,
 };
 
-const char * const named_directions[] = 
+const char * const named_directions[] =
 {
 	"Up",
 	"Down",
@@ -94,22 +194,29 @@ public:
 		public:
 
 			Setting(const char* const _name, const ControlState def_value
-				, const unsigned int _low = 0, const unsigned int _high = 100 )
+				, const unsigned int _low = 0, const unsigned int _high = 100)
 				: name(_name)
 				, value(def_value)
 				, default_value(def_value)
 				, low(_low)
-				, high(_high){}
+				, high(_high)
+				, lastState(false)
+			{
+				control = new Input("");
+			}
+			void GetState();
 
 			const char* const	name;
 			ControlState		value;
 			const ControlState	default_value;
 			const unsigned int	low, high;
+			Control*			control;
+			bool				lastState;
 		};
 
 		ControlGroup(const char* const _name, const unsigned int _type = GROUP_TYPE_OTHER) : name(_name), type(_type) {}
 		virtual ~ControlGroup();
-	
+
 		virtual void LoadConfig(IniFile::Section *sec, const std::string& defdev = "", const std::string& base = "" );
 		virtual void SaveConfig(IniFile::Section *sec, const std::string& defdev = "", const std::string& base = "" );
 
@@ -133,10 +240,10 @@ public:
 			ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State();
 			ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State();
 
-			ControlState radius = settings[SETTING_RADIUS]->value;
-			ControlState deadzone = settings[SETTING_DEADZONE]->value;
-			ControlState square = settings[SETTING_SQUARE]->value;
-			ControlState m = controls[4]->control_ref->State();
+			ControlState radius = settings[AS_RADIUS]->value;
+			ControlState deadzone = settings[AS_DEADZONE]->value;
+			ControlState square = settings[AS_SQUARE]->value;
+			ControlState m = controls[AS_MODIFIER]->control_ref->State();
 
 			// modifier code
 			if (m)
@@ -189,7 +296,9 @@ public:
 	class Buttons : public ControlGroup
 	{
 	public:
-		Buttons(const char* const _name);
+		Buttons(const char* const _name, bool has_range = false);
+
+		bool HasRange() { return m_has_range; }
 
 		template <typename C>
 		void GetState(C* const buttons, const C* bitmasks)
@@ -200,11 +309,30 @@ public:
 
 			for (; i!=e; ++i, ++bitmasks)
 			{
-				if ((*i)->control_ref->State() > settings[0]->value) // threshold
+				if ((*i)->control_ref->State() > settings[m_has_range ? B_THRESHOLD_R : B_THRESHOLD]->value) // threshold
 					*buttons |= *bitmasks;
 			}
 		}
 
+		void GetState(ControlState* buttons)
+		{
+			auto range = settings[B_RANGE]->value;
+
+			std::vector<Control*>::iterator
+				i = controls.begin(),
+				e = controls.end();
+
+			for (; i!=e; ++i)
+			{
+				if ((*i)->control_ref->State() > settings[m_has_range ? B_THRESHOLD_R : B_THRESHOLD]->value)
+					*buttons++ = (sign((*i)->control_ref->State()) * range);
+				else
+					*buttons++ = 0.f;
+			}
+		}
+
+	private:
+		bool	m_has_range;
 	};
 
 	class MixedTriggers : public ControlGroup
@@ -276,58 +404,120 @@ public:
 		Force(const char* const _name);
 
 		template <typename C, typename R>
-		void GetState(C* axis, const u8 base, const R range)
+		void GetState(C* axis, const u8 base, const R range, bool step = true)
 		{
-			const float deadzone = settings[0]->value;
-			for (unsigned int i=0; i<6; i+=2)
+			const float master_range = settings[F_RANGE]->value;
+			const float deadzone = settings[F_DEADZONE]->value;
+
+			for (unsigned int i = 0; i < 3; i++)
 			{
-				float tmpf = 0;
-				const float state = controls[i+1]->control_ref->State() - controls[i]->control_ref->State();
+				float dz = 0;
+				const float state = controls[i*2 + 1]->control_ref->State() - controls[i*2]->control_ref->State();
+
 				if (fabsf(state) > deadzone)
-					tmpf = ((state - (deadzone * sign(state))) / (1 - deadzone));
+					dz = ((state - (deadzone * sign(state))) / (1 - deadzone));
+
+				if (step)
+				{
+					if (state > m_swing[i])
+						m_swing[i] = std::min(m_swing[i] + 0.1f, state);
+					else if (state < m_swing[i])
+						m_swing[i] = std::max(m_swing[i] - 0.1f, state);
+				}
+
+				// deceleration switch
+				m_state[i] = (abs(m_swing[i]) >= 0.7
+					? -2 * sign(state) + m_swing[i] * 2
+					: m_swing[i]) * sign(state);
 
-				float &ax = m_swing[i >> 1];
-				*axis++	= (C)((tmpf - ax) * range + base);
-				ax = tmpf;
+				*axis++ = (C)(m_state[i] * range * master_range + base);
 			}
 		}
 	private:
-		float	m_swing[3];
+		float	m_swing[3], m_state[3];
 	};
 
 	class Tilt : public ControlGroup
 	{
 	public:
-		Tilt(const char* const _name);
+		Tilt(const char* const _name, bool gyro = false);
+
+		bool HasGyro() { return m_has_gyro; }
 
 		template <typename C, typename R>
-		void GetState(C* const x, C* const y, const unsigned int base, const R range, const bool step = true)
+		void GetState(C* const pitch, C* const roll, C* const yaw, const bool gyro = false, const unsigned int base = 0.0, const R range = 1.0, const bool step = true)
 		{
-			// this is all a mess
+			ControlState state;
+			ControlState r_acc = controls[m_has_gyro ? T_ACC_RANGE : T_RANGE]->control_ref->State();
+			auto const r_acc_s = settings[m_has_gyro ? T_ACC_RANGE_S : T_RANGE]->value;
+			auto const deadzone = settings[m_has_gyro ? T_DEADZONE : T_DEADZONE_N]->value;
+			auto const circle = settings[m_has_gyro ? T_CIRCLESTICK : T_CIRCLESTICK_N]->value;
+			auto const angle = settings[m_has_gyro ? T_ANGLE : T_ANGLE_N]->value / 1.8f;
+
+			ControlState settle = 0
+				, r_gyro_1 = 1.0
+				, r_gyro_2 = 1.0
+				, r_gyro_s = 1.0;
+			if (m_has_gyro)
+			{
+				r_gyro_1 = controls[T_GYRO_RANGE_1]->control_ref->State();
+				r_gyro_2 = controls[T_GYRO_RANGE_2]->control_ref->State();
+				r_gyro_s = settings[T_GYRO_RANGE]->value;
+				settle = settings[T_GYRO_SETTLE]->value;
+			}
 
-			ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State();
-			ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State();
+			for (unsigned int i = 0; i < 3; i++)
+			{
+				state = controls[i*2 + 1]->control_ref->State() - controls[i*2]->control_ref->State();
 
-			ControlState deadzone = settings[0]->value;
-			ControlState circle = settings[1]->value;
-			auto const angle = settings[2]->value / 1.8f;
-			ControlState m = controls[4]->control_ref->State();
+				// deadzone
+				state *= fabsf(state)>deadzone
+					? (fabsf(state)-deadzone)/(1.0-deadzone)
+					: 0.0;
 
-			// modifier code
-			if (m)
-			{
-				yy = (fabsf(yy)>deadzone) * sign(yy) * (m + deadzone/2);
-				xx = (fabsf(xx)>deadzone) * sign(xx) * (m + deadzone/2);
+				if (step)
+				{
+					if (state == m_acc[i])
+						m_settle[i]++;
+					else
+						m_settle[i] = 0;
+
+					// step towards state
+					if (state > m_acc[i])
+						m_acc[i] = std::min(m_acc[i] + 0.1f, state);
+					else if (state < m_acc[i])
+						m_acc[i] = std::max(m_acc[i] - 0.1f, state);
+
+					// step gyro towards 0
+					if (m_settle[i] > settle * 100.0)
+					{
+						if (0 > m_gyro[i])
+							m_gyro[i] = std::min(m_gyro[i] + 0.05f, 0.0f);
+						else if (0 < m_gyro[i])
+							m_gyro[i] = std::max(m_gyro[i] - 0.05f, 0.0f);
+					}
+
+					// step gyro towards state
+					else
+					{
+						if (m_acc[i] > m_gyro[i])
+							m_gyro[i] = std::min(m_gyro[i] + 0.05f, m_acc[i]);
+						else if (m_acc[i] < m_gyro[i])
+							m_gyro[i] = std::max(m_gyro[i] - 0.05f, m_acc[i]);
+					}
+				}
 			}
 
-			// deadzone / circle stick code
-			if (deadzone || circle)
-			{
-				// this section might be all wrong, but its working good enough, I think
+			ControlState y = m_acc[0];
+			ControlState x = m_acc[1];
 
-				ControlState ang = atan2(yy, xx); 
+			// circle stick
+			if (circle)
+			{
+				ControlState ang = atan2(y, x);
 				ControlState ang_sin = sin(ang);
 				ControlState ang_cos = cos(ang);
+				ControlState rad = sqrt(x*x + y*y);
 
 				// the amt a full square stick would have at current angle
 				ControlState square_full = std::min(ang_sin ? 1/fabsf(ang_sin) : 2, ang_cos ? 1/fabsf(ang_cos) : 2);
@@ -336,42 +526,41 @@ public:
 				// I think this is more like a pointed circle rather than a rounded square like it should be
 				ControlState stick_full = (square_full * (1 - circle)) + (circle);
 
-				ControlState dist = sqrt(xx*xx + yy*yy);
+				// dead zone
+				rad = std::max(0.0f, rad - deadzone * stick_full);
+				rad /= (1.0 - deadzone);
 
-				// dead zone code
-				dist = std::max(0.0f, dist - deadzone * stick_full);
-				dist /= (1 - deadzone);
-
-				// circle stick code
-				ControlState amt = dist / stick_full;
-				dist += (square_full - 1) * amt * circle;
+				// circle stick
+				ControlState amt = rad / stick_full;
+				rad += (square_full - 1) * amt * circle;
 
-				yy = std::max(-1.0f, std::min(1.0f, ang_sin * dist));
-				xx = std::max(-1.0f, std::min(1.0f, ang_cos * dist));
+				y = ang_sin * rad;
+				x = ang_cos * rad;
 			}
 
-			// this is kinda silly here
-			// gui being open will make this happen 2x as fast, o well
-			
-			// silly
-			if (step)
+			m_acc[0] = y;
+			m_acc[1] = x;
+
+			if (gyro)
 			{
-				if (xx > m_tilt[0])
-					m_tilt[0] = std::min(m_tilt[0] + 0.1f, xx);
-				else if (xx < m_tilt[0])
-					m_tilt[0] = std::max(m_tilt[0] - 0.1f, xx);
-
-				if (yy > m_tilt[1])
-					m_tilt[1] = std::min(m_tilt[1] + 0.1f, yy);
-				else if (yy < m_tilt[1])
-					m_tilt[1] = std::max(m_tilt[1] - 0.1f, yy);
+				if (!r_gyro_1) r_gyro_1 = 1.0;
+				if (!r_gyro_2) r_gyro_2 = 1.0;
+				*pitch = C(m_gyro[0] * angle * range * r_gyro_1 * r_gyro_2 * r_gyro_s + base);
+				*roll = C(m_gyro[1] * angle * range * r_gyro_1 * r_gyro_2 * r_gyro_s + base);
+				*yaw = C(m_gyro[2] * angle * range * r_gyro_1 * r_gyro_2 * r_gyro_s + base);
+			}
+			else
+			{
+				if (!r_acc) r_acc = 1.0;
+				*pitch = C(m_acc[0] * angle * range * r_acc * r_acc_s + base);
+				*roll = C(m_acc[1] * angle * range * r_acc * r_acc_s + base);
+				*yaw = C(m_acc[2] * angle * range * r_acc * r_acc_s + base);
 			}
-
-			*y = C(m_tilt[1] * angle * range + base);
-			*x = C(m_tilt[0] * angle * range + base);
 		}
 	private:
-		float	m_tilt[2];
+		float	m_acc[3], m_gyro[3];
+		int		m_settle[3];
+		bool	m_has_gyro;
 	};
 
 	class Cursor : public ControlGroup
@@ -379,43 +568,88 @@ public:
 	public:
 		Cursor(const char* const _name);
 
-		template <typename C>
-		void GetState(C* const x, C* const y, C* const z, const bool adjusted = false)
+		template <typename C, typename R>
+		void GetState(C* const x, C* const y, C* const z, const R range = 1.0, const bool adjusted = true, const bool relative = false, const bool step = false)
 		{
-			const float zz = controls[4]->control_ref->State() - controls[5]->control_ref->State();
-
-			// silly being here
-			if (zz > m_z)
-				m_z = std::min(m_z + 0.1f, zz);
-			else if (zz < m_z)
-				m_z = std::max(m_z - 0.1f, zz);
-
-			*z = m_z;
-			
-			// hide
-			if (controls[6]->control_ref->State() > 0.5f)
-			{
-				*x = 10000; *y = 0;
-			}
-			else
+			std::lock_guard<std::mutex> lk(m_mutex);
+
+			// smooth frames
+			const u8 NUM_FRAMES = 10;
+			// absolute conversion range
+			const float ABS_RANGE = .2,
+			// raw input range
+			RAW_RANGE = .1;
+
+			ControlState r = controls[C_RANGE_MODIFIER]->control_ref->State();
+			if (!r)
+				r = 1.0;
+
+			for (unsigned int i=0; i<3; i++)
 			{
-				float yy = controls[0]->control_ref->State() - controls[1]->control_ref->State();
-				float xx = controls[3]->control_ref->State() - controls[2]->control_ref->State();
+				//bool is_relative = controls[i*2 + 1]->control_ref->IsRelative() || controls[i*2]->control_ref->IsRelative();
+				bool is_relative = false; // NEED TO DO SOMETHING WITH THIS
+				ControlState state = controls[i*2 + 1]->control_ref->State() - controls[i*2]->control_ref->State();
+
+				// change sign
+				if (i == 0)
+					state = -state;
+
+				// update absolute position
+				if (is_relative)
+				{
+					// moving average smooth input
+					m_list[i].push_back(state);
+					float fsum = 0; int j = 0;
+					if (m_list[i].size() > 0)
+					{
+						for (std::list<float>::reverse_iterator it = m_list[i].rbegin(); relative ? it != m_list[i].rend() : j < 1; it++, j++)
+							fsum += *it;
+						state = fsum / float(m_list[i].size());
+					}
+					if (m_list[i].size() >= NUM_FRAMES)
+						m_list[i].pop_front();
+
+					// update position
+					m_absolute[i] += state * range * r * ABS_RANGE;
+
+					//m_absolute[i] = MathUtil::Trim(m_absolute[i], -1, 1);
+					m_absolute[i] = (m_absolute[i] < -1 ? -1 : (m_absolute[i] > 1 ? 1 : m_absolute[i]));
+					m_state[i] = m_absolute[i];
+				}
+				else
+				{
+					//m_absolute[i] = MathUtil::Trim(state, -1, 1);
+					m_absolute[i] = (state < -1 ? -1 : (state > 1 ? 1 : state));
+				}
 
-				// adjust cursor according to settings
-				if (adjusted)
+				if (relative)
+				{
+					m_state[i] = (m_absolute[i] - m_last[i]) * RAW_RANGE;
+					if (step)
+						m_last[i] = m_absolute[i];
+				}
+				else
 				{
-					xx *= (settings[1]->value * 2);
-					yy *= (settings[2]->value * 2);
-					yy += (settings[0]->value - 0.5f);
+					m_state[i] = m_absolute[i];
 				}
+			}
+
+			*y = m_state[0];
+			*x = m_state[1];
+			*z = m_state[2];
 
-				*x = xx;
-				*y = yy;
+			// adjust absolute cursor
+			if (adjusted && !relative)
+			{
+				*x *= (settings[C_WIDTH]->value * range * 2.0);
+				*y *= (settings[C_HEIGHT]->value * range * 2.0);
+				*y += (settings[C_CENTER]->value - 0.5f);
 			}
 		}
 
-		float	m_z;
+		float				m_state[3], m_absolute[3], m_last[3];
+		std::list<float>	m_list[3];
+		std::mutex			m_mutex;
 	};
 
 	class Extension : public ControlGroup
