summaryrefslogblamecommitdiffstats
path: root/src/input_common/tas/tas_input.h
blob: 0a152a04fa90e5f1a245ec9962548bdb47a3ad07 (plain) (tree)


































































































































































                                                                                                   
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

#include <array>
#include <mutex>
#include <thread>

#include "common/common_types.h"
#include "core/frontend/input.h"
#include "input_common/main.h"

#define PLAYER_NUMBER 8

namespace TasInput {

using TasAnalog = std::tuple<float, float>;

enum class TasButton : u32 {
    BUTTON_A = 0x000001,
    BUTTON_B = 0x000002,
    BUTTON_X = 0x000004,
    BUTTON_Y = 0x000008,
    STICK_L = 0x000010,
    STICK_R = 0x000020,
    TRIGGER_L = 0x000040,
    TRIGGER_R = 0x000080,
    TRIGGER_ZL = 0x000100,
    TRIGGER_ZR = 0x000200,
    BUTTON_PLUS = 0x000400,
    BUTTON_MINUS = 0x000800,
    BUTTON_LEFT = 0x001000,
    BUTTON_UP = 0x002000,
    BUTTON_RIGHT = 0x004000,
    BUTTON_DOWN = 0x008000,
    BUTTON_SL = 0x010000,
    BUTTON_SR = 0x020000,
    BUTTON_HOME = 0x040000,
    BUTTON_CAPTURE = 0x080000,
};

static const std::array<std::pair<std::string, TasButton>, 20> text_to_tas_button = {
    std::pair{"KEY_A", TasButton::BUTTON_A},
    {"KEY_B", TasButton::BUTTON_B},
    {"KEY_X", TasButton::BUTTON_X},
    {"KEY_Y", TasButton::BUTTON_Y},
    {"KEY_LSTICK", TasButton::STICK_L},
    {"KEY_RSTICK", TasButton::STICK_R},
    {"KEY_L", TasButton::TRIGGER_L},
    {"KEY_R", TasButton::TRIGGER_R},
    {"KEY_PLUS", TasButton::BUTTON_PLUS},
    {"KEY_MINUS", TasButton::BUTTON_MINUS},
    {"KEY_DLEFT", TasButton::BUTTON_LEFT},
    {"KEY_DUP", TasButton::BUTTON_UP},
    {"KEY_DRIGHT", TasButton::BUTTON_RIGHT},
    {"KEY_DDOWN", TasButton::BUTTON_DOWN},
    {"KEY_SL", TasButton::BUTTON_SL},
    {"KEY_SR", TasButton::BUTTON_SR},
    {"KEY_CAPTURE", TasButton::BUTTON_CAPTURE},
    {"KEY_HOME", TasButton::BUTTON_HOME},
    {"KEY_ZL", TasButton::TRIGGER_ZL},
    {"KEY_ZR", TasButton::TRIGGER_ZR},
};

enum class TasAxes : u8 {
    StickX,
    StickY,
    SubstickX,
    SubstickY,
    Undefined,
};

struct TasData {
    u32 buttons{};
    std::array<float, 4> axis{};
};

class Tas {
public:
    Tas();
    ~Tas();

    static std::string buttonsToString(u32 button) {
        std::string returns;
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_A)) != 0)
            returns += ", A";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_B)) != 0)
            returns += ", B";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_X)) != 0)
            returns += ", X";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_Y)) != 0)
            returns += ", Y";
        if ((button & static_cast<u32>(TasInput::TasButton::STICK_L)) != 0)
            returns += ", STICK_L";
        if ((button & static_cast<u32>(TasInput::TasButton::STICK_R)) != 0)
            returns += ", STICK_R";
        if ((button & static_cast<u32>(TasInput::TasButton::TRIGGER_L)) != 0)
            returns += ", TRIGGER_L";
        if ((button & static_cast<u32>(TasInput::TasButton::TRIGGER_R)) != 0)
            returns += ", TRIGGER_R";
        if ((button & static_cast<u32>(TasInput::TasButton::TRIGGER_ZL)) != 0)
            returns += ", TRIGGER_ZL";
        if ((button & static_cast<u32>(TasInput::TasButton::TRIGGER_ZR)) != 0)
            returns += ", TRIGGER_ZR";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_PLUS)) != 0)
            returns += ", PLUS";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_MINUS)) != 0)
            returns += ", MINUS";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_LEFT)) != 0)
            returns += ", LEFT";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_UP)) != 0)
            returns += ", UP";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_RIGHT)) != 0)
            returns += ", RIGHT";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_DOWN)) != 0)
            returns += ", DOWN";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_SL)) != 0)
            returns += ", SL";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_SR)) != 0)
            returns += ", SR";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_HOME)) != 0)
            returns += ", HOME";
        if ((button & static_cast<u32>(TasInput::TasButton::BUTTON_CAPTURE)) != 0)
            returns += ", CAPTURE";
        return returns.length() != 0 ? returns.substr(2) : "";
    }

    void RefreshTasFile();
    void LoadTasFiles();
    void RecordInput(u32 buttons, std::array<std::pair<float, float>, 2> axes);
    void UpdateThread();
    std::string GetStatusDescription();

    InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
    InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
    [[nodiscard]] const TasData& GetTasState(std::size_t pad) const;

private:
    struct TASCommand {
        u32 buttons{};
        TasAnalog l_axis{};
        TasAnalog r_axis{};
    };
    void LoadTasFile(int playerIndex);
    void WriteTasFile();
    TasAnalog ReadCommandAxis(const std::string line) const;
    u32 ReadCommandButtons(const std::string line) const;
    std::string WriteCommandButtons(u32 data) const;
    std::string WriteCommandAxis(TasAnalog data) const;
    std::pair<float, float> flipY(std::pair<float, float> old) const;

    size_t scriptLength{0};
    std::array<TasData, PLAYER_NUMBER> tas_data;
    bool update_thread_running{true};
    bool refresh_tas_fle{false};
    std::array<std::vector<TASCommand>, PLAYER_NUMBER> newCommands{};
    std::vector<TASCommand> recordCommands{};
    std::size_t current_command{0};
    TASCommand lastInput{}; // only used for recording
};
} // namespace TasInput