77
88#include "si/commands.h"
99#include "si/device/gc_controller.h"
10+ #include "si/device/n64_controller.h"
1011#include "wavebird/message.h"
1112#include "wavebird/packet.h"
1213#include "wavebird/radio.h"
@@ -32,6 +33,9 @@ typedef enum {
3233
3334 // Present as a wired GameCube controller without rumble
3435 WP_CONT_TYPE_GC_WIRED_NOMOTOR ,
36+
37+ // Present as an OEM N64 controller
38+ WP_CONT_TYPE_N64 ,
3539} wp_controller_type_t ;
3640
3741// Settings structure
@@ -77,7 +81,14 @@ struct {
7781} packet_stats = {0 };
7882
7983// SI state
80- static struct si_device_gc_controller si_device ;
84+ static union {
85+ struct si_device_gc_controller gc ;
86+ struct si_device_n64_controller n64 ;
87+ } si_device ;
88+
89+ static uint8_t n64_stick_x_origin = 0x80 ;
90+ static uint8_t n64_stick_y_origin = 0x80 ;
91+
8192static bool enable_si_command_handling = true;
8293
8394// Buttons, switches, and LEDs
@@ -106,14 +117,18 @@ static void initialize_controller(uint8_t controller_type)
106117{
107118 if (controller_type == WP_CONT_TYPE_GC_WAVEBIRD ) {
108119 // Present as an OEM WaveBird receiver
109- si_device_gc_init (& si_device , SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_NOMOTOR );
120+ si_device_gc_init (& si_device . gc , SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_NOMOTOR );
110121 enable_si_command_handling = true;
111122 } else if (controller_type == WP_CONT_TYPE_GC_WIRED_NOMOTOR ) {
112123 // Present as a wired GameCube controller without rumble
113- si_device_gc_init (& si_device , SI_TYPE_GC | SI_GC_STANDARD | SI_GC_NOMOTOR );
124+ si_device_gc_init (& si_device . gc , SI_TYPE_GC | SI_GC_STANDARD | SI_GC_NOMOTOR );
114125 } else if (controller_type == WP_CONT_TYPE_GC_WIRED ) {
115126 // Present as an OEM wired GameCube controller
116- si_device_gc_init (& si_device , SI_TYPE_GC | SI_GC_STANDARD );
127+ si_device_gc_init (& si_device .gc , SI_TYPE_GC | SI_GC_STANDARD );
128+ } else if (controller_type == WP_CONT_TYPE_N64 ) {
129+ // Present as an OEM N64 controller
130+ si_device_n64_init (& si_device .n64 );
131+ enable_si_command_handling = true;
117132 }
118133}
119134
@@ -143,25 +158,25 @@ static void handle_channel_wheel_change(struct channel_wheel *channel_wheel, uin
143158#endif
144159
145160// Update the input state of a GC controller from a WaveBird packet
146- static void update_gc_input_state (struct si_device_gc_controller * si_device , const uint8_t * message )
161+ static void update_gc_input_state (struct si_device_gc_controller * device , const uint8_t * message )
147162{
148163 // Clear the buttons in the SI input state
149- si_device -> input .buttons .bytes [0 ] &= ~0x1F ;
150- si_device -> input .buttons .bytes [1 ] &= ~0x7F ;
164+ device -> input .buttons .bytes [0 ] &= ~0x1F ;
165+ device -> input .buttons .bytes [1 ] &= ~0x7F ;
151166
152167 // Copy the buttons from the WaveBird message
153- si_device -> input .buttons .bytes [0 ] |= (message [3 ] & 0x80 ) >> 7 | (message [2 ] & 0x0F ) << 1 ;
154- si_device -> input .buttons .bytes [1 ] |= (message [3 ] & 0x7F );
168+ device -> input .buttons .bytes [0 ] |= (message [3 ] & 0x80 ) >> 7 | (message [2 ] & 0x0F ) << 1 ;
169+ device -> input .buttons .bytes [1 ] |= (message [3 ] & 0x7F );
155170
156171 // Copy the stick, substick, and trigger values
157- memcpy (& si_device -> input .stick_x , & message [4 ], 6 );
172+ memcpy (& device -> input .stick_x , & message [4 ], 6 );
158173
159174 // Set the input state as valid
160- si_device_gc_set_input_valid (si_device , true);
175+ si_device_gc_set_input_valid (device , true);
161176}
162177
163178// Update the origin state of a GC controller from a WaveBird packet
164- static void update_gc_origin_state (struct si_device_gc_controller * si_device , const uint8_t * message )
179+ static void update_gc_origin_state (struct si_device_gc_controller * device , const uint8_t * message )
165180{
166181 // Copy the origin values from the packet
167182 uint8_t new_origin [] = {
@@ -171,15 +186,49 @@ static void update_gc_origin_state(struct si_device_gc_controller *si_device, co
171186 };
172187
173188 // Check if the origin packet is different from the last known origin
174- if (memcmp (& si_device -> origin .stick_x , new_origin , 6 ) != 0 ) {
189+ if (memcmp (& device -> origin .stick_x , new_origin , 6 ) != 0 ) {
175190 // Update the origin state
176- memcpy (& si_device -> origin .stick_x , new_origin , 6 );
191+ memcpy (& device -> origin .stick_x , new_origin , 6 );
177192
178193 // Set the "need origin" flag to true so the host knows to fetch the new origin
179- si_device -> input .buttons .need_origin = true;
194+ device -> input .buttons .need_origin = true;
180195 }
181196}
182197
198+ // Update the input state of an N64 controller from a WaveBird packet
199+ static void update_n64_input_state (struct si_device_n64_controller * device , const uint8_t * message )
200+ {
201+ // Map the buttons
202+ uint16_t buttons = wavebird_input_state_get_buttons (message );
203+ device -> input .buttons .a = (buttons & WB_BUTTONS_A ) ? 1 : 0 ;
204+ device -> input .buttons .b = (buttons & WB_BUTTONS_B ) ? 1 : 0 ;
205+ device -> input .buttons .z = (buttons & WB_BUTTONS_Z ) ? 1 : 0 ;
206+ device -> input .buttons .start = (buttons & WB_BUTTONS_START ) ? 1 : 0 ;
207+ device -> input .buttons .up = (buttons & WB_BUTTONS_UP ) ? 1 : 0 ;
208+ device -> input .buttons .down = (buttons & WB_BUTTONS_DOWN ) ? 1 : 0 ;
209+ device -> input .buttons .left = (buttons & WB_BUTTONS_LEFT ) ? 1 : 0 ;
210+ device -> input .buttons .right = (buttons & WB_BUTTONS_RIGHT ) ? 1 : 0 ;
211+ device -> input .buttons .l = (buttons & WB_BUTTONS_L ) ? 1 : 0 ;
212+ device -> input .buttons .r = (buttons & WB_BUTTONS_R ) ? 1 : 0 ;
213+
214+ // Map the substick to the C buttons
215+ device -> input .buttons .c_left = wavebird_input_state_get_substick_x (message ) < 64 ? 1 : 0 ;
216+ device -> input .buttons .c_right = wavebird_input_state_get_substick_x (message ) > 192 ? 1 : 0 ;
217+ device -> input .buttons .c_up = wavebird_input_state_get_substick_y (message ) > 192 ? 1 : 0 ;
218+ device -> input .buttons .c_down = wavebird_input_state_get_substick_y (message ) < 64 ? 1 : 0 ;
219+
220+ // Copy the main stick values
221+ device -> input .stick_x = (uint8_t )(int8_t )((wavebird_input_state_get_stick_x (message ) - n64_stick_x_origin ) * 0.8 );
222+ device -> input .stick_y = (uint8_t )(int8_t )((wavebird_input_state_get_stick_y (message ) - n64_stick_y_origin ) * 0.8 );
223+ }
224+
225+ // Update the origin state of an N64 controller from a WaveBird packet
226+ static void update_n64_origin_state (struct si_device_n64_controller * device , const uint8_t * message )
227+ {
228+ n64_stick_x_origin = wavebird_origin_get_stick_x (message );
229+ n64_stick_y_origin = wavebird_origin_get_stick_y (message );
230+ }
231+
183232// Handle packets from the WaveBird radio
184233static void handle_wavebird_packet (const uint8_t * packet )
185234{
@@ -203,13 +252,13 @@ static void handle_wavebird_packet(const uint8_t *packet)
203252 // Check the controller id is as expected
204253 if (settings .cont_type == WP_CONT_TYPE_GC_WAVEBIRD ) {
205254 // Implement wireless ID pinning exactly as OEM WaveBird receivers do
206- if (si_device_gc_wireless_id_fixed (& si_device )) {
255+ if (si_device_gc_wireless_id_fixed (& si_device . gc )) {
207256 // Drop packets from other controllers if the ID has been fixed
208- if (si_device_gc_get_wireless_id (& si_device ) != wireless_id )
257+ if (si_device_gc_get_wireless_id (& si_device . gc ) != wireless_id )
209258 return ;
210259 } else {
211260 // Set the controller ID if it is not fixed
212- si_device_gc_set_wireless_id (& si_device , wireless_id );
261+ si_device_gc_set_wireless_id (& si_device . gc , wireless_id );
213262 }
214263 } else {
215264 // Emulate wireless ID pinning for wired controllers
@@ -231,7 +280,11 @@ static void handle_wavebird_packet(const uint8_t *packet)
231280 // Handle the packet
232281 if (wavebird_message_get_type (message ) == WB_MESSAGE_TYPE_INPUT_STATE ) {
233282 // Update the SI input state
234- update_gc_input_state (& si_device , message );
283+ if (settings .cont_type == WP_CONT_TYPE_N64 ) {
284+ update_n64_input_state (& si_device .n64 , message );
285+ } else {
286+ update_gc_input_state (& si_device .gc , message );
287+ }
235288
236289 // We have a good input state, enable SI command handling if it was disabled
237290 enable_si_command_handling = true;
@@ -240,7 +293,11 @@ static void handle_wavebird_packet(const uint8_t *packet)
240293 stale_input_timeout = millis + INPUT_VALID_MS ;
241294 } else {
242295 // Update the SI origin state
243- update_gc_origin_state (& si_device , message );
296+ if (settings .cont_type == WP_CONT_TYPE_N64 ) {
297+ update_n64_origin_state (& si_device .n64 , message );
298+ } else {
299+ update_gc_origin_state (& si_device .gc , message );
300+ }
244301 }
245302}
246303
@@ -401,6 +458,7 @@ int main(void)
401458
402459 // Initialize persistent settings
403460 settings_init (& settings , sizeof (wp_settings_t ), SETTINGS_SIGNATURE , & DEFAULT_SETTINGS );
461+ settings .cont_type = WP_CONT_TYPE_N64 ; // TODO: Remove
404462
405463 // Initialize and configure the WaveBird radio
406464 wavebird_radio_configure_qualification (qualify_packet , 5 );
@@ -427,9 +485,11 @@ int main(void)
427485 DEBUG_PRINT ("WavePhoenix receiver ready!\n" );
428486 DEBUG_PRINT ("- Firmware version: %d.%d.%d\n" , VERSION_MAJOR , VERSION_MINOR , VERSION_PATCH );
429487 DEBUG_PRINT ("- Radio channel: %u\n" , settings .chan + 1 );
430- DEBUG_PRINT ("- Controller type: %s\n" , (settings .cont_type == WP_CONT_TYPE_GC_WAVEBIRD ) ? "WaveBird"
431- : (settings .cont_type == WP_CONT_TYPE_GC_WIRED ) ? "Wired"
432- : "Wired (no motor)" );
488+ DEBUG_PRINT ("- Controller type: %s\n" , (settings .cont_type == WP_CONT_TYPE_GC_WAVEBIRD ) ? "WaveBird"
489+ : (settings .cont_type == WP_CONT_TYPE_GC_WIRED ) ? "Wired"
490+ : (settings .cont_type == WP_CONT_TYPE_GC_WIRED_NOMOTOR ) ? "Wired (no motor)"
491+ : (settings .cont_type == WP_CONT_TYPE_N64 ) ? "N64"
492+ : "Unknown" );
433493 DEBUG_PRINT ("\n" );
434494
435495 // Wait for the SI bus to be idle before starting the main loop
@@ -449,7 +509,12 @@ int main(void)
449509 led_effect_update (status_led , millis );
450510
451511 // Invalidate stale inputs
452- if (si_device .input_valid && (int32_t )(millis - stale_input_timeout ) >= 0 )
453- si_device_gc_set_input_valid (& si_device , false);
512+ if (settings .cont_type == WP_CONT_TYPE_N64 ) {
513+ // TODO
514+ } else {
515+ if (si_device .gc .input_valid && (int32_t )(millis - stale_input_timeout ) >= 0 ) {
516+ si_device_gc_set_input_valid (& si_device .gc , false);
517+ }
518+ }
454519 }
455520}
0 commit comments