|
| 1 | +// Copyright 2022 Dolphin Emulator Project |
| 2 | +// SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | + |
| 4 | +#include "Core/HW/WiimoteEmu/Extension/Shinkansen.h" |
| 5 | + |
| 6 | +#include <array> |
| 7 | + |
| 8 | +#include "Common/Assert.h" |
| 9 | +#include "Common/Common.h" |
| 10 | +#include "Common/CommonTypes.h" |
| 11 | +#include "Core/HW/WiimoteEmu/WiimoteEmu.h" |
| 12 | + |
| 13 | +#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h" |
| 14 | +#include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h" |
| 15 | + |
| 16 | +namespace WiimoteEmu |
| 17 | +{ |
| 18 | +// TODO: check this (only the last byte is known good) |
| 19 | +constexpr std::array<u8, 6> shinkansen_id{{0x00, 0x00, 0xa4, 0x20, 0x01, 0x10}}; |
| 20 | + |
| 21 | +Shinkansen::Shinkansen() : Extension3rdParty("Shinkansen", _trans("Shinkansen Controller")) |
| 22 | +{ |
| 23 | + // Button layout on the controller: |
| 24 | + // |
| 25 | + // Up Select Start D |
| 26 | + // Left Right A C |
| 27 | + // Down B |
| 28 | + // |
| 29 | + groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons"))); |
| 30 | + m_buttons->AddInput(ControllerEmu::Translate, _trans("Left")); |
| 31 | + m_buttons->AddInput(ControllerEmu::Translate, _trans("Down")); |
| 32 | + m_buttons->AddInput(ControllerEmu::Translate, _trans("Right")); |
| 33 | + m_buttons->AddInput(ControllerEmu::Translate, _trans("Up")); |
| 34 | + m_buttons->AddInput(ControllerEmu::DoNotTranslate, "A"); |
| 35 | + m_buttons->AddInput(ControllerEmu::DoNotTranslate, "B"); |
| 36 | + m_buttons->AddInput(ControllerEmu::DoNotTranslate, "C"); |
| 37 | + m_buttons->AddInput(ControllerEmu::DoNotTranslate, "D"); |
| 38 | + m_buttons->AddInput(ControllerEmu::Translate, _trans("Select")); |
| 39 | + m_buttons->AddInput(ControllerEmu::Translate, _trans("Start")); |
| 40 | + |
| 41 | + // For easier axis mapping the right lever is inverted in Dolphin, |
| 42 | + // so that the train coasts when no trigger is squeezed. |
| 43 | + groups.emplace_back(m_levers = new ControllerEmu::MixedTriggers(_trans("Levers"))); |
| 44 | + m_levers->AddInput(ControllerEmu::Translate, _trans("L")); |
| 45 | + m_levers->AddInput(ControllerEmu::Translate, _trans("R")); |
| 46 | + m_levers->AddInput(ControllerEmu::Translate, _trans("L-Analog")); |
| 47 | + m_levers->AddInput(ControllerEmu::Translate, _trans("R-Analog")); |
| 48 | + |
| 49 | + groups.emplace_back(m_led = new ControllerEmu::ControlGroup(_trans("Light"))); |
| 50 | + m_led->AddOutput(ControllerEmu::Translate, _trans("Doors Locked")); |
| 51 | +} |
| 52 | + |
| 53 | +void Shinkansen::Update() |
| 54 | +{ |
| 55 | + DataFormat ext_data = {}; |
| 56 | + |
| 57 | + u16 digital = 0; |
| 58 | + const u16 lever_bitmasks[2] = {}; |
| 59 | + double analog[2] = {}; |
| 60 | + m_levers->GetState(&digital, lever_bitmasks, analog); |
| 61 | + // The game requires these specific values, all other values are treated like 0/255 (which are |
| 62 | + // guesses). |
| 63 | + const u8 brake_values[] = {0, 53, 79, 105, 132, 159, 187, 217, 250}; |
| 64 | + const u8 power_values[] = {255, 229, 208, 189, 170, 153, 135, 118, 101, 85, 68, 51, 35, 17}; |
| 65 | + ext_data.brake = brake_values[size_t(analog[0] * (sizeof(brake_values) - 1))]; |
| 66 | + ext_data.power = power_values[size_t(analog[1] * (sizeof(power_values) - 1))]; |
| 67 | + |
| 68 | + // Note: This currently assumes a little-endian host. |
| 69 | + const u16 button_bitmasks[] = { |
| 70 | + 0x0200, // Left |
| 71 | + 0x0040, // Down |
| 72 | + 0x0080, // Right |
| 73 | + 0x0100, // Up |
| 74 | + 0x2000, // A |
| 75 | + 0x4000, // B |
| 76 | + 0x1000, // C |
| 77 | + 0x0800, // D |
| 78 | + 0x0010, // Select |
| 79 | + 0x0004, // Start |
| 80 | + }; |
| 81 | + m_buttons->GetState(&ext_data.buttons, button_bitmasks); |
| 82 | + ext_data.buttons ^= 0xFFFF; |
| 83 | + Common::BitCastPtr<DataFormat>(&m_reg.controller_data) = ext_data; |
| 84 | + |
| 85 | + const auto lock = GetStateLock(); |
| 86 | + m_led->controls[0]->control_ref->State(m_reg.identifier[1]); |
| 87 | +} |
| 88 | + |
| 89 | +void Shinkansen::Reset() |
| 90 | +{ |
| 91 | + EncryptedExtension::Reset(); |
| 92 | + |
| 93 | + m_reg = {}; |
| 94 | + m_reg.identifier = shinkansen_id; |
| 95 | + m_reg.calibration.fill(0xff); |
| 96 | +} |
| 97 | + |
| 98 | +ControllerEmu::ControlGroup* Shinkansen::GetGroup(ShinkansenGroup group) |
| 99 | +{ |
| 100 | + switch (group) |
| 101 | + { |
| 102 | + case ShinkansenGroup::Levers: |
| 103 | + return m_levers; |
| 104 | + case ShinkansenGroup::Buttons: |
| 105 | + return m_buttons; |
| 106 | + case ShinkansenGroup::Light: |
| 107 | + return m_led; |
| 108 | + default: |
| 109 | + ASSERT(false); |
| 110 | + return nullptr; |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +} // namespace WiimoteEmu |
0 commit comments