Skip to content
Permalink
Browse files

Merge pull request #7880 from jordan-woyak/udraw-tablet-emu

WiimoteEmu: Implement 3rd-Party extension encryption and uDraw GameTablet.
  • Loading branch information...
JMC47 committed Apr 30, 2019
2 parents c0aacdf + 2babbd7 commit e39aa5b0264f3b098db08253a1e6a9966e672a82
Showing with 863 additions and 270 deletions.
  1. +1 −0 Source/Core/Core/CMakeLists.txt
  2. +2 −0 Source/Core/Core/Core.vcxproj
  3. +7 −0 Source/Core/Core/Core.vcxproj.filters
  4. +6 −0 Source/Core/Core/HW/Wiimote.cpp
  5. +2 −0 Source/Core/Core/HW/Wiimote.h
  6. +462 −231 Source/Core/Core/HW/WiimoteEmu/Encryption.cpp
  7. +43 −5 Source/Core/Core/HW/WiimoteEmu/Encryption.h
  8. +3 −2 Source/Core/Core/HW/WiimoteEmu/Extension/Classic.cpp
  9. +1 −1 Source/Core/Core/HW/WiimoteEmu/Extension/Classic.h
  10. +3 −2 Source/Core/Core/HW/WiimoteEmu/Extension/Drums.cpp
  11. +2 −2 Source/Core/Core/HW/WiimoteEmu/Extension/Drums.h
  12. +41 −14 Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp
  13. +25 −2 Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h
  14. +3 −2 Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.cpp
  15. +2 −2 Source/Core/Core/HW/WiimoteEmu/Extension/Guitar.h
  16. +3 −2 Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.cpp
  17. +1 −1 Source/Core/Core/HW/WiimoteEmu/Extension/Nunchuk.h
  18. +3 −2 Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.cpp
  19. +2 −2 Source/Core/Core/HW/WiimoteEmu/Extension/Turntable.h
  20. +147 −0 Source/Core/Core/HW/WiimoteEmu/Extension/UDrawTablet.cpp
  21. +68 −0 Source/Core/Core/HW/WiimoteEmu/Extension/UDrawTablet.h
  22. +1 −0 Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h
  23. +9 −0 Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp
  24. +2 −0 Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h
  25. +22 −0 Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.cpp
  26. +2 −0 Source/Core/DolphinQt/Config/Mapping/WiimoteEmuExtension.h
@@ -148,6 +148,7 @@ add_library(core
HW/WiimoteEmu/Extension/Drums.cpp
HW/WiimoteEmu/Extension/Guitar.cpp
HW/WiimoteEmu/Extension/Turntable.cpp
HW/WiimoteEmu/Extension/UDrawTablet.cpp
HW/WiimoteReal/WiimoteReal.cpp
HW/WiiSave.cpp
IOS/Device.cpp
@@ -182,6 +182,7 @@
<ClCompile Include="HW\WiimoteEmu\Extension\Guitar.cpp" />
<ClCompile Include="HW\WiimoteEmu\Extension\Nunchuk.cpp" />
<ClCompile Include="HW\WiimoteEmu\Extension\Turntable.cpp" />
<ClCompile Include="HW\WiimoteEmu\Extension\UDrawTablet.cpp" />
<ClCompile Include="HW\WiimoteEmu\I2CBus.cpp" />
<ClCompile Include="HW\WiimoteEmu\MotionPlus.cpp" />
<ClCompile Include="HW\WiimoteEmu\Speaker.cpp" />
@@ -449,6 +450,7 @@
<ClInclude Include="HW\WiimoteEmu\Extension\Guitar.h" />
<ClInclude Include="HW\WiimoteEmu\Extension\Nunchuk.h" />
<ClInclude Include="HW\WiimoteEmu\Extension\Turntable.h" />
<ClInclude Include="HW\WiimoteEmu\Extension\UDrawTablet.h" />
<ClInclude Include="HW\WiimoteEmu\I2CBus.h" />
<ClInclude Include="HW\WiimoteEmu\MotionPlus.h" />
<ClInclude Include="HW\WiimoteEmu\Speaker.h" />
@@ -907,6 +907,9 @@
<ClCompile Include="HW\WiimoteEmu\Extension\Turntable.cpp">
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
</ClCompile>
<ClCompile Include="HW\WiimoteEmu\Extension\UDrawTablet.cpp">
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
</ClCompile>
<ClCompile Include="HW\WiimoteEmu\Extension\Extension.cpp">
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
</ClCompile>
@@ -1619,6 +1622,9 @@
<ClInclude Include="HW\WiimoteEmu\Extension\Turntable.h">
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
</ClInclude>
<ClInclude Include="HW\WiimoteEmu\Extension\UDrawTablet.h">
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
</ClInclude>
<ClInclude Include="HW\WiimoteEmu\Extension\Extension.h">
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu\Extension</Filter>
</ClInclude>
@@ -1628,6 +1634,7 @@
<ClInclude Include="HW\WiimoteCommon\DataReport.h">
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Common</Filter>
</ClInclude>
<ClInclude Include="PowerPC\ConditionRegister.h" />
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
@@ -66,6 +66,12 @@ ControllerEmu::ControlGroup* GetTurntableGroup(int number, WiimoteEmu::Turntable
->GetTurntableGroup(group);
}

ControllerEmu::ControlGroup* GetUDrawTabletGroup(int number, WiimoteEmu::UDrawTabletGroup group)
{
return static_cast<WiimoteEmu::Wiimote*>(s_config.GetController(number))
->GetUDrawTabletGroup(group);
}

void Shutdown()
{
s_config.UnregisterHotplugCallback();
@@ -26,6 +26,7 @@ enum class ClassicGroup;
enum class GuitarGroup;
enum class DrumsGroup;
enum class TurntableGroup;
enum class UDrawTabletGroup;
} // namespace WiimoteEmu

enum
@@ -78,6 +79,7 @@ ControllerEmu::ControlGroup* GetClassicGroup(int number, WiimoteEmu::ClassicGrou
ControllerEmu::ControlGroup* GetGuitarGroup(int number, WiimoteEmu::GuitarGroup group);
ControllerEmu::ControlGroup* GetDrumsGroup(int number, WiimoteEmu::DrumsGroup group);
ControllerEmu::ControlGroup* GetTurntableGroup(int number, WiimoteEmu::TurntableGroup group);
ControllerEmu::ControlGroup* GetUDrawTabletGroup(int number, WiimoteEmu::UDrawTabletGroup group);

void ControlChannel(int number, u16 channel_id, const void* data, u32 size);
void InterruptChannel(int number, u16 channel_id, const void* data, u32 size);

Large diffs are not rendered by default.

@@ -6,21 +6,59 @@

#pragma once

#include <array>

#include "Common/CommonTypes.h"

namespace WiimoteEmu
{
class EncryptionKey
{
public:
void Generate(const u8* keydata);
void Encrypt(u8* data, u32 addr, u32 len) const;
void Decrypt(u8* data, u32 addr, u32 len) const;

using RandData = std::array<u8, 10>;
using KeyData = std::array<u8, 6>;

void GenerateTables(const RandData& rand, const KeyData& key, const u32 idx, const u8 offset);

std::array<u8, 8> ft = {};
std::array<u8, 8> sb = {};
};

class KeyGen
{
public:
virtual ~KeyGen() = default;

void Encrypt(u8* data, int addr, u8 len) const;
void Decrypt(u8* data, int addr, u8 len) const;
using ExtKeyData = std::array<u8, 16>;

EncryptionKey GenerateFromExtensionKeyData(const ExtKeyData& key_data) const;

protected:
virtual EncryptionKey::KeyData GenerateKeyData(const EncryptionKey::RandData& rand,
u32 idx) const = 0;
virtual EncryptionKey GenerateTables(const EncryptionKey::RandData& rand,
const EncryptionKey::KeyData& key, u32 idx) const = 0;
};

class KeyGen1stParty final : public KeyGen
{
private:
EncryptionKey::KeyData GenerateKeyData(const EncryptionKey::RandData& rand,
u32 idx) const final override;
EncryptionKey GenerateTables(const EncryptionKey::RandData& rand,
const EncryptionKey::KeyData& key, u32 idx) const final override;
};

class KeyGen3rdParty final : public KeyGen
{
private:
u8 ft[8] = {};
u8 sb[8] = {};
EncryptionKey::KeyData GenerateKeyData(const EncryptionKey::RandData& rand,
u32 idx) const final override;
EncryptionKey GenerateTables(const EncryptionKey::RandData& rand,
const EncryptionKey::KeyData& key, u32 idx) const final override;
};

} // namespace WiimoteEmu
@@ -72,7 +72,7 @@ constexpr std::array<u16, 4> classic_dpad_bitmasks{{
Classic::PAD_RIGHT,
}};

Classic::Classic() : EncryptedExtension(_trans("Classic"))
Classic::Classic() : Extension1stParty(_trans("Classic"))
{
// buttons
groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons")));
@@ -172,7 +172,8 @@ bool Classic::IsButtonPressed() const

void Classic::Reset()
{
m_reg = {};
EncryptedExtension::Reset();

m_reg.identifier = classic_id;

// Build calibration data:
@@ -26,7 +26,7 @@ enum class ClassicGroup
RightStick
};

class Classic : public EncryptedExtension
class Classic : public Extension1stParty
{
public:
union ButtonFormat
@@ -44,7 +44,7 @@ constexpr std::array<u16, 2> drum_button_bitmasks{{
Drums::BUTTON_PLUS,
}};

Drums::Drums() : EncryptedExtension(_trans("Drums"))
Drums::Drums() : Extension1stParty(_trans("Drums"))
{
// pads
groups.emplace_back(m_pads = new ControllerEmu::Buttons(_trans("Pads")));
@@ -106,7 +106,8 @@ bool Drums::IsButtonPressed() const

void Drums::Reset()
{
m_reg = {};
EncryptedExtension::Reset();

m_reg.identifier = drums_id;

// TODO: Is there calibration data?
@@ -22,8 +22,8 @@ enum class DrumsGroup
Stick
};

// TODO: Do the drums ever use encryption?
class Drums : public EncryptedExtension
// The Drums use the "1st-party" extension encryption scheme.
class Drums : public Extension1stParty
{
public:
struct DataFormat
@@ -12,6 +12,8 @@
#include "Common/Compiler.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"

#include "Common/Logging/Log.h"

namespace WiimoteEmu
{
Extension::Extension(const char* name) : m_name(name)
@@ -85,15 +87,16 @@ int EncryptedExtension::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)

auto const result = RawRead(&m_reg, addr, count, data_out);

// Encrypt data read from extension register
// Encrypt data read from extension register.
if (ENCRYPTION_ENABLED == m_reg.encryption)
{
// INFO_LOG(WIIMOTE, "Encrypted read.");
ext_key.Encrypt(data_out, addr, (u8)count);
}
else
{
// INFO_LOG(WIIMOTE, "Unencrypted read.");
if (m_is_key_dirty)
{
UpdateEncryptionKey();
m_is_key_dirty = false;
}

ext_key.Encrypt(data_out, addr, count);
}

return result;
@@ -106,24 +109,48 @@ int EncryptedExtension::BusWrite(u8 slave_addr, u8 addr, int count, const u8* da

auto const result = RawWrite(&m_reg, addr, count, data_in);

// TODO: make this check less ugly:
if (addr + count > 0x40 && addr < 0x50)
constexpr u8 ENCRYPTION_KEY_DATA_BEGIN = offsetof(Register, encryption_key_data);
constexpr u8 ENCRYPTION_KEY_DATA_END = ENCRYPTION_KEY_DATA_BEGIN + 0x10;

if (addr + count > ENCRYPTION_KEY_DATA_BEGIN && addr < ENCRYPTION_KEY_DATA_END)
{
// 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
ext_key.Generate(m_reg.encryption_key_data);
// FYI: Real extensions seem to require the key data written in specifically sized chunks.
// We just run the key generation on all writes to the key area.
m_is_key_dirty = true;
}

return result;
}

void EncryptedExtension::Reset()
{
// Clear register state.
m_reg = {};

// Clear encryption key state.
ext_key = {};
m_is_key_dirty = true;
}

void EncryptedExtension::DoState(PointerWrap& p)
{
p.Do(m_reg);

// No need to sync this when we can regenerate it:
if (p.GetMode() == PointerWrap::MODE_READ)
ext_key.Generate(m_reg.encryption_key_data);
{
// No need to sync the key when we can just regenerate it.
m_is_key_dirty = true;
}
}

void Extension1stParty::UpdateEncryptionKey()
{
ext_key = KeyGen1stParty().GenerateFromExtensionKeyData(m_reg.encryption_key_data);
}

void Extension3rdParty::UpdateEncryptionKey()
{
ext_key = KeyGen3rdParty().GenerateFromExtensionKeyData(m_reg.encryption_key_data);
}

} // namespace WiimoteEmu
@@ -64,7 +64,7 @@ class EncryptedExtension : public Extension

// TODO: This is public for TAS reasons.
// TODO: TAS handles encryption poorly.
WiimoteEmu::EncryptionKey ext_key = {};
EncryptionKey ext_key;

protected:
static constexpr int CALIBRATION_CHECKSUM_BYTES = 2;
@@ -82,7 +82,7 @@ class EncryptedExtension : public Extension
u8 unknown3[0x10];

// address 0x40
u8 encryption_key_data[0x10];
std::array<u8, 0x10> encryption_key_data;
u8 unknown4[0xA0];

// address 0xF0
@@ -98,9 +98,14 @@ class EncryptedExtension : public Extension

Register m_reg = {};

void Reset() override;
void DoState(PointerWrap& p) override;

virtual void UpdateEncryptionKey() = 0;

private:
bool m_is_key_dirty = true;

static constexpr u8 ENCRYPTION_ENABLED = 0xaa;

bool ReadDeviceDetectPin() const override;
@@ -109,4 +114,22 @@ class EncryptedExtension : public Extension
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;
};

class Extension1stParty : public EncryptedExtension
{
protected:
using EncryptedExtension::EncryptedExtension;

private:
void UpdateEncryptionKey() final override;
};

class Extension3rdParty : public EncryptedExtension
{
protected:
using EncryptedExtension::EncryptedExtension;

private:
void UpdateEncryptionKey() final override;
};

} // namespace WiimoteEmu
@@ -62,7 +62,7 @@ constexpr std::array<u16, 2> guitar_strum_bitmasks{{
Guitar::BAR_DOWN,
}};

Guitar::Guitar() : EncryptedExtension(_trans("Guitar"))
Guitar::Guitar() : Extension1stParty(_trans("Guitar"))
{
// frets
groups.emplace_back(m_frets = new ControllerEmu::Buttons(_trans("Frets")));
@@ -152,7 +152,8 @@ bool Guitar::IsButtonPressed() const

void Guitar::Reset()
{
m_reg = {};
EncryptedExtension::Reset();

m_reg.identifier = guitar_id;

// TODO: Is there calibration data?
@@ -27,8 +27,8 @@ enum class GuitarGroup
SliderBar
};

// TODO: Does the guitar ever use encryption?
class Guitar : public EncryptedExtension
// The Guitar uses the "1st-party" extension encryption scheme.
class Guitar : public Extension1stParty
{
public:
struct DataFormat
@@ -31,7 +31,7 @@ constexpr std::array<u8, 2> nunchuk_button_bitmasks{{
Nunchuk::BUTTON_Z,
}};

Nunchuk::Nunchuk() : EncryptedExtension(_trans("Nunchuk"))
Nunchuk::Nunchuk() : Extension1stParty(_trans("Nunchuk"))
{
// buttons
groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons")));
@@ -121,7 +121,8 @@ bool Nunchuk::IsButtonPressed() const

void Nunchuk::Reset()
{
m_reg = {};
EncryptedExtension::Reset();

m_reg.identifier = nunchuk_id;

m_swing_state = {};

0 comments on commit e39aa5b

Please sign in to comment.
You can’t perform that action at this time.