Skip to content

Commit

Permalink
0.4.0: IR pointer emulation modes
Browse files Browse the repository at this point in the history
Three IR pointer emulation modes: direct (touchpad, only for DS4), analog axis relative (move the pointer with the right analog) and analog axis absolute (the pointer is moved proportionally to the right analog starting from the center). Press R1+R3 to switch between them
  • Loading branch information
xerpi committed Nov 1, 2021
1 parent dce4ca9 commit 2ecc0e8
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 22 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ STRIP = stripios

# Version
FAKEMOTE_MAJOR = 0
FAKEMOTE_MINOR = 3
FAKEMOTE_PATCH = 1
FAKEMOTE_MINOR = 4
FAKEMOTE_PATCH = 0
FAKEMOTE_HASH = "$(shell git describe --dirty --always --exclude '*')"

# Flags
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ _An IOS module that fakes Wiimotes from the input of USB game controllers._

- DS3 and DS4 support includes LEDs, rumble, and the accelerometer
- DS4's touchpad is used to emulate the Wiimote IR Camera pointer
- Both controllers emulate a Wiimote with the Nunchuk and Classic Controller extensions connected. Press R3 to switch between them
- Both controllers emulate a Wiimote with the Nunchuk and Classic Controller extensions connected. Press L1+L3 to switch between them
- Three IR pointer emulation modes: direct (touchpad, only for DS4), analog axis relative (move the pointer with the right analog) and analog axis absolute (the pointer is moved proportionally to the right analog starting from the center). Press R1+R3 to switch between them

## Installation
1) Download [d2x cIOS Installer for regular Wii](https://wii.guide/cios.html)/[d2x cIOS Installer for vWii](https://wiiu.hacks.guide/#/vwii-modding) and extract it to the SD card
Expand Down
43 changes: 42 additions & 1 deletion include/button_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ enum bm_classic_analog_axis_e {
BM_CLASSIC_ANALOG_AXIS__NUM = BM_CLASSIC_ANALOG_AXIS_RIGHT_Y
};

/* IR pointer emulation */
enum bm_ir_emulation_mode_e {
BM_IR_EMULATION_MODE_NONE,
BM_IR_EMULATION_MODE_DIRECT,
BM_IR_EMULATION_MODE_RELATIVE_ANALOG_AXIS,
BM_IR_EMULATION_MODE_ABSOLUTE_ANALOG_AXIS,
};

enum bm_ir_axis_e {
BM_IR_AXIS_NONE,
BM_IR_AXIS_X,
BM_IR_AXIS_Y,
BM_IR_AXIS__NUM = BM_IR_AXIS_Y,
};

struct bm_ir_emulation_state_t {
u16 position[BM_IR_AXIS__NUM];
};

void bm_map_wiimote(
/* Inputs */
int num_buttons, u32 buttons,
Expand Down Expand Up @@ -51,13 +70,22 @@ void bm_map_classic(
/* Outputs */
struct wiimote_extension_data_format_classic_t *classic);

void bm_calculate_ir(
void bm_map_ir_direct(
/* Inputs */
int num_coordinates, const u16 *x, const u16 *y,
u16 max_x, u16 max_y,
/* Outputs */
struct ir_dot_t ir_dots[static IR_MAX_DOTS]);

void bm_map_ir_analog_axis(
/* Inputs */
enum bm_ir_emulation_mode_e mode,
struct bm_ir_emulation_state_t *state,
int num_analog_axis, const u8 *analog_axis,
const u8 *ir_analog_axis_map,
/* Outputs */
struct ir_dot_t ir_dots[static IR_MAX_DOTS]);

static inline bool bm_check_switch_mapping(u32 buttons, bool *switch_mapping, u32 switch_mapping_combo)
{
bool switch_pressed = (buttons & switch_mapping_combo) == switch_mapping_combo;
Expand Down Expand Up @@ -108,4 +136,17 @@ static inline void bm_classic_format(struct wiimote_extension_data_format_classi
out->bt.hex = (~buttons) & CLASSIC_CTRL_BUTTON_ALL;
}

static inline void bm_ir_emulation_state_reset(struct bm_ir_emulation_state_t *state)
{
state->position[BM_IR_AXIS_X - 1] = IR_CENTER_X;
state->position[BM_IR_AXIS_Y - 1] = IR_CENTER_Y;
}

static inline void bm_ir_dots_set_out_of_screen(struct ir_dot_t ir_dots[static IR_MAX_DOTS])
{
for (int i = 0; i < IR_MAX_DOTS; i++)
ir_dots[i].y = 1023;
}


#endif
2 changes: 1 addition & 1 deletion include/usb_hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "types.h"
#include "fake_wiimote_mgr.h"

#define USB_INPUT_DEVICE_PRIVATE_DATA_SIZE 32
#define USB_INPUT_DEVICE_PRIVATE_DATA_SIZE 64

typedef struct usb_device_driver_t usb_device_driver_t;

Expand Down
62 changes: 52 additions & 10 deletions source/button_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,30 +71,72 @@ void bm_map_classic(
bm_classic_format(classic, classic_buttons, classic_analog_axis);
}

void bm_calculate_ir(
static inline void map_ir_dot(struct ir_dot_t ir_dots[static IR_MAX_DOTS], const struct ir_dot_t *dot)
{
s16 vert_offset = g_sensor_bar_position_top ? IR_VERTICAL_OFFSET : -IR_VERTICAL_OFFSET;

ir_dots[0].x = (IR_HIGH_X - dot->x) - IR_HORIZONTAL_OFFSET;
ir_dots[0].y = dot->y + vert_offset;
ir_dots[1].x = (IR_HIGH_X - dot->x) + IR_HORIZONTAL_OFFSET;
ir_dots[1].y = dot->y + vert_offset;
}

void bm_map_ir_direct(
/* Inputs */
int num_coordinates, const u16 *x, const u16 *y,
u16 max_x, u16 max_y,
/* Outputs */
struct ir_dot_t ir_dots[static IR_MAX_DOTS])
{
struct ir_dot_t dot;
s16 vert_offset = g_sensor_bar_position_top ? IR_VERTICAL_OFFSET : -IR_VERTICAL_OFFSET;

/* TODO: For now we only care about 1 reported coordinate... */
if (num_coordinates == 0) {
ir_dots[0].x = 1023;
ir_dots[0].y = 1023;
ir_dots[1].x = 1023;
ir_dots[1].y = 1023;
bm_ir_dots_set_out_of_screen(ir_dots);
return;
}

dot.x = IR_DOT_CENTER_MIN_X + (x[0] * (IR_DOT_CENTER_MAX_X - IR_DOT_CENTER_MIN_X)) / max_x;
dot.y = IR_DOT_CENTER_MIN_Y + (y[0] * (IR_DOT_CENTER_MAX_Y - IR_DOT_CENTER_MIN_Y)) / max_y;
map_ir_dot(ir_dots, &dot);
}

void bm_map_ir_analog_axis(
/* Inputs */
enum bm_ir_emulation_mode_e mode,
struct bm_ir_emulation_state_t *state,
int num_analog_axis, const u8 *analog_axis,
const u8 *ir_analog_axis_map,
/* Outputs */
struct ir_dot_t ir_dots[static IR_MAX_DOTS])
{
struct ir_dot_t dot;

for (int i = 0; i < num_analog_axis; i++) {
if (ir_analog_axis_map[i]) {
s16 val = (s16)analog_axis[i] - 128;

if (mode == BM_IR_EMULATION_MODE_RELATIVE_ANALOG_AXIS) {
state->position[ir_analog_axis_map[i] - 1] += val / 16;
} else if (mode == BM_IR_EMULATION_MODE_ABSOLUTE_ANALOG_AXIS) {
u16 center = (ir_analog_axis_map[i] == BM_IR_AXIS_X) ? IR_CENTER_X : IR_CENTER_Y;
state->position[ir_analog_axis_map[i] - 1] = center + val;
}
}
}

if (state->position[BM_IR_AXIS_X - 1] < IR_DOT_CENTER_MIN_X)
state->position[BM_IR_AXIS_X - 1] = IR_DOT_CENTER_MIN_X;
else if (state->position[BM_IR_AXIS_X - 1] > IR_DOT_CENTER_MAX_X)
state->position[BM_IR_AXIS_X - 1] = IR_DOT_CENTER_MAX_X;

if (state->position[BM_IR_AXIS_Y - 1] < IR_DOT_CENTER_MIN_Y)
state->position[BM_IR_AXIS_Y - 1] = IR_DOT_CENTER_MIN_Y;
else if (state->position[BM_IR_AXIS_Y - 1] > IR_DOT_CENTER_MAX_Y)
state->position[BM_IR_AXIS_Y - 1] = IR_DOT_CENTER_MAX_Y;


ir_dots[0].x = (IR_HIGH_X - dot.x) - IR_HORIZONTAL_OFFSET;
ir_dots[0].y = dot.y + vert_offset;
ir_dots[1].x = (IR_HIGH_X - dot.x) + IR_HORIZONTAL_OFFSET;
ir_dots[1].y = dot.y + vert_offset;
dot.x = state->position[BM_IR_AXIS_X - 1];
dot.y = IR_HIGH_Y - state->position[BM_IR_AXIS_Y - 1];
map_ir_dot(ir_dots, &dot);
}
39 changes: 37 additions & 2 deletions source/usb_driver_ds3.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,19 @@ struct ds3_private_data_t {
u8 analog_axis[DS3_ANALOG_AXIS__NUM];
s16 acc_x, acc_y, acc_z;
} input;
enum bm_ir_emulation_mode_e ir_emu_mode;
struct bm_ir_emulation_state_t ir_emu_state;
u8 mapping;
u8 ir_emu_mode_idx;
u8 leds;
bool rumble_on;
bool switch_mapping;
bool switch_ir_emu_mode;
};
static_assert(sizeof(struct ds3_private_data_t) <= USB_INPUT_DEVICE_PRIVATE_DATA_SIZE);

#define SWITCH_MAPPING_COMBO (BIT(DS3_BUTTON_R3))
#define SWITCH_MAPPING_COMBO (BIT(DS3_BUTTON_L1) | BIT(DS3_BUTTON_L3))
#define SWITCH_IR_EMU_MODE_COMBO (BIT(DS3_BUTTON_R1) | BIT(DS3_BUTTON_R3))

static const struct {
enum wiimote_ext_e extension;
Expand Down Expand Up @@ -183,6 +188,17 @@ static const struct {
},
};

static const u8 ir_analog_axis_map[DS3_ANALOG_AXIS__NUM] = {
[DS3_ANALOG_AXIS_RIGHT_X] = BM_IR_AXIS_X,
[DS3_ANALOG_AXIS_RIGHT_Y] = BM_IR_AXIS_Y,
};

static const enum bm_ir_emulation_mode_e ir_emu_modes[] = {
BM_IR_EMULATION_MODE_ABSOLUTE_ANALOG_AXIS,
BM_IR_EMULATION_MODE_RELATIVE_ANALOG_AXIS,
BM_IR_EMULATION_MODE_NONE,
};

static inline void ds3_get_buttons(const struct ds3_input_report *report, u32 *buttons)
{
u32 mask = 0;
Expand Down Expand Up @@ -306,10 +322,13 @@ int ds3_driver_ops_init(usb_input_device_t *device, u16 vid, u16 pid)
return ret;

/* Init private state */
priv->ir_emu_mode_idx = 0;
bm_ir_emulation_state_reset(&priv->ir_emu_state);
priv->mapping = 0;
priv->leds = 0;
priv->rumble_on = false;
priv->mapping = 0;
priv->switch_mapping = false;
priv->switch_ir_emu_mode = false;

/* Set initial extension */
fake_wiimote_set_extension(device->wiimote, input_mappings[priv->mapping].extension);
Expand Down Expand Up @@ -355,11 +374,16 @@ bool ds3_report_input(usb_input_device_t *device)
u16 wiimote_buttons = 0;
u16 acc_x, acc_y, acc_z;
union wiimote_extension_data_t extension_data;
struct ir_dot_t ir_dots[IR_MAX_DOTS];
enum bm_ir_emulation_mode_e ir_emu_mode;

if (bm_check_switch_mapping(priv->input.buttons, &priv->switch_mapping, SWITCH_MAPPING_COMBO)) {
priv->mapping = (priv->mapping + 1) % ARRAY_SIZE(input_mappings);
fake_wiimote_set_extension(device->wiimote, input_mappings[priv->mapping].extension);
return false;
} else if (bm_check_switch_mapping(priv->input.buttons, &priv->switch_ir_emu_mode, SWITCH_IR_EMU_MODE_COMBO)) {
priv->ir_emu_mode_idx = (priv->ir_emu_mode_idx + 1) % ARRAY_SIZE(ir_emu_modes);
bm_ir_emulation_state_reset(&priv->ir_emu_state);
}

bm_map_wiimote(DS3_BUTTON__NUM, priv->input.buttons,
Expand All @@ -373,6 +397,17 @@ bool ds3_report_input(usb_input_device_t *device)

fake_wiimote_report_accelerometer(device->wiimote, acc_x, acc_y, acc_z);

ir_emu_mode = ir_emu_modes[priv->ir_emu_mode_idx];
if (ir_emu_mode == BM_IR_EMULATION_MODE_NONE) {
bm_ir_dots_set_out_of_screen(ir_dots);
} else {
bm_map_ir_analog_axis(ir_emu_mode, &priv->ir_emu_state,
DS3_ANALOG_AXIS__NUM, priv->input.analog_axis,
ir_analog_axis_map, ir_dots);
}

fake_wiimote_report_ir_dots(device->wiimote, ir_dots);

if (input_mappings[priv->mapping].extension == WIIMOTE_EXT_NONE) {
fake_wiimote_report_input(device->wiimote, wiimote_buttons);
} else if (input_mappings[priv->mapping].extension == WIIMOTE_EXT_NUNCHUK) {
Expand Down
45 changes: 40 additions & 5 deletions source/usb_driver_ds4.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,19 @@ struct ds4_private_data_t {
} fingers[2];
u8 num_fingers;
} input;
enum bm_ir_emulation_mode_e ir_emu_mode;
struct bm_ir_emulation_state_t ir_emu_state;
u8 mapping;
u8 ir_emu_mode_idx;
u8 leds;
bool rumble_on;
bool switch_mapping;
bool switch_ir_emu_mode;
};
static_assert(sizeof(struct ds4_private_data_t) <= USB_INPUT_DEVICE_PRIVATE_DATA_SIZE);

#define SWITCH_MAPPING_COMBO (BIT(DS4_BUTTON_R3))
#define SWITCH_MAPPING_COMBO (BIT(DS4_BUTTON_L1) | BIT(DS4_BUTTON_L3))
#define SWITCH_IR_EMU_MODE_COMBO (BIT(DS4_BUTTON_R1) | BIT(DS4_BUTTON_R3))

static const struct {
enum wiimote_ext_e extension;
Expand Down Expand Up @@ -198,6 +203,17 @@ static const struct {
},
};

static const u8 ir_analog_axis_map[DS4_ANALOG_AXIS__NUM] = {
[DS4_ANALOG_AXIS_RIGHT_X] = BM_IR_AXIS_X,
[DS4_ANALOG_AXIS_RIGHT_Y] = BM_IR_AXIS_Y,
};

static const enum bm_ir_emulation_mode_e ir_emu_modes[] = {
BM_IR_EMULATION_MODE_DIRECT,
BM_IR_EMULATION_MODE_RELATIVE_ANALOG_AXIS,
BM_IR_EMULATION_MODE_ABSOLUTE_ANALOG_AXIS,
};

static inline void ds4_get_buttons(const struct ds4_input_report *report, u32 *buttons)
{
u32 mask = 0;
Expand Down Expand Up @@ -303,10 +319,13 @@ int ds4_driver_ops_init(usb_input_device_t *device, u16 vid, u16 pid)
struct ds4_private_data_t *priv = (void *)device->private_data;

/* Init private state */
priv->ir_emu_mode_idx = 0;
bm_ir_emulation_state_reset(&priv->ir_emu_state);
priv->mapping = 0;
priv->leds = 0;
priv->rumble_on = false;
priv->mapping = 0;
priv->switch_mapping = false;
priv->switch_ir_emu_mode = false;

/* Set initial extension */
fake_wiimote_set_extension(device->wiimote, input_mappings[priv->mapping].extension);
Expand Down Expand Up @@ -349,11 +368,15 @@ bool ds4_report_input(usb_input_device_t *device)
u16 acc_x, acc_y, acc_z;
union wiimote_extension_data_t extension_data;
struct ir_dot_t ir_dots[IR_MAX_DOTS];
enum bm_ir_emulation_mode_e ir_emu_mode;

if (bm_check_switch_mapping(priv->input.buttons, &priv->switch_mapping, SWITCH_MAPPING_COMBO)) {
priv->mapping = (priv->mapping + 1) % ARRAY_SIZE(input_mappings);
fake_wiimote_set_extension(device->wiimote, input_mappings[priv->mapping].extension);
return false;
} else if (bm_check_switch_mapping(priv->input.buttons, &priv->switch_ir_emu_mode, SWITCH_IR_EMU_MODE_COMBO)) {
priv->ir_emu_mode_idx = (priv->ir_emu_mode_idx + 1) % ARRAY_SIZE(ir_emu_modes);
bm_ir_emulation_state_reset(&priv->ir_emu_state);
}

bm_map_wiimote(DS4_BUTTON__NUM, priv->input.buttons,
Expand All @@ -367,9 +390,21 @@ bool ds4_report_input(usb_input_device_t *device)

fake_wiimote_report_accelerometer(device->wiimote, acc_x, acc_y, acc_z);

bm_calculate_ir(priv->input.num_fingers, &priv->input.fingers[0].x, &priv->input.fingers[0].y,
DS4_TOUCHPAD_W - 1, DS4_TOUCHPAD_H - 1,
ir_dots);
ir_emu_mode = ir_emu_modes[priv->ir_emu_mode_idx];
if (ir_emu_mode == BM_IR_EMULATION_MODE_NONE) {
bm_ir_dots_set_out_of_screen(ir_dots);
} else {
if (ir_emu_mode == BM_IR_EMULATION_MODE_DIRECT) {
bm_map_ir_direct(priv->input.num_fingers,
&priv->input.fingers[0].x, &priv->input.fingers[0].y,
DS4_TOUCHPAD_W - 1, DS4_TOUCHPAD_H - 1,
ir_dots);
} else {
bm_map_ir_analog_axis(ir_emu_mode, &priv->ir_emu_state,
DS4_ANALOG_AXIS__NUM, priv->input.analog_axis,
ir_analog_axis_map, ir_dots);
}
}

fake_wiimote_report_ir_dots(device->wiimote, ir_dots);

Expand Down

0 comments on commit 2ecc0e8

Please sign in to comment.