Skip to content
This repository was archived by the owner on Mar 18, 2022. It is now read-only.

Commit d5b788a

Browse files
committed
Package for npm
1 parent 0b4e7a7 commit d5b788a

7 files changed

Lines changed: 2334 additions & 0 deletions

File tree

GCAdapter.cc

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
#include <iostream>
2+
#include <mutex>
3+
#include <future>
4+
#include <atomic>
5+
#include <nan.h>
6+
#include <bitset>
7+
#include <iomanip>
8+
#include "GCAdapter.h"
9+
#include "Flag.h"
10+
11+
using namespace std;
12+
13+
libusb_device_handle *device_handle = nullptr;
14+
libusb_context *context = nullptr;
15+
16+
uint8_t controller_payload[37];
17+
uint8_t controller_payload_swap[37];
18+
19+
atomic<int> controller_payload_size = { 0 };
20+
21+
thread adapter_thread;
22+
GCAdapter::Flag adapter_thread_running;
23+
24+
mutex main_mutex;
25+
26+
uint8_t endpoint_out = 0;
27+
uint8_t endpoint_in = 0;
28+
29+
NAN_METHOD(Setup) {
30+
auto return_value = Nan::New<v8::Number>(0);
31+
if(info.Length()>0) {
32+
Nan::ThrowError("Load() must not have arguments");
33+
return;
34+
}
35+
libusb_init(&context);
36+
libusb_device **list;
37+
ssize_t count = libusb_get_device_list(context, &list);
38+
bool case_test = false;
39+
for (int i = 0; i < count && !case_test; i++) {
40+
libusb_device *device = list[i];
41+
case_test = IsAccessible(device);
42+
if (case_test) {
43+
return_value = Nan::New<v8::Number>(AddAdapter(device));
44+
}
45+
}
46+
libusb_free_device_list(list, 1);
47+
info.GetReturnValue().Set(return_value);
48+
}
49+
bool IsAccessible(libusb_device *dev) {
50+
int return_value=0,kernel_value=0;
51+
libusb_device_descriptor descriptor;
52+
return_value = libusb_get_device_descriptor(dev, &descriptor);
53+
if (return_value < 0) {
54+
cout << "Error getting descriptor of USB device. Error code: " << return_value << endl;
55+
return false;
56+
}
57+
58+
if (descriptor.idVendor == GAMECUBE_VID && descriptor.idProduct == GAMECUBE_PID) {
59+
cout << "Found GameCube Adapter" << endl;
60+
}
61+
else {
62+
return false;
63+
}
64+
return_value = libusb_open(dev, &device_handle);
65+
switch (return_value) {
66+
case 0:
67+
cout << "This adapter seems reachable. Trying to reach..." << endl;
68+
break;
69+
case LIBUSB_ERROR_ACCESS:
70+
cout << "LIBUSB_ERROR_ACCESS: gca-node does not have access to this adapter." << endl;
71+
return false;
72+
break;
73+
default:
74+
cout << "gca-node couldn't open this adapter. Error code: " << return_value << endl;
75+
return false;
76+
break;
77+
}
78+
return_value = libusb_kernel_driver_active(device_handle, 0);
79+
if (return_value == 1) {
80+
kernel_value = libusb_detach_kernel_driver(device_handle, 0);
81+
if (kernel_value != 0) {
82+
cout << "gca-node can't attach the kernel of this adapter. Error code:" << return_value << endl;
83+
return false;
84+
}
85+
}
86+
if (return_value == 0 || kernel_value == 0) {
87+
return_value = libusb_claim_interface(device_handle, 0);
88+
if (return_value != 0) {
89+
cout << "gca-node couldn't claim interface 0 of adapter. Error code:" << return_value << endl;
90+
}
91+
return return_value == 0;
92+
}
93+
return false;
94+
}
95+
int AddAdapter(libusb_device *dev) {
96+
int endpoint_number = 0;
97+
libusb_config_descriptor *config = nullptr;
98+
libusb_get_config_descriptor(dev, 0, &config);
99+
for (uint8_t iface=0; iface < config->bNumInterfaces; iface++) {
100+
const libusb_interface *interfaceContainer = &config->interface[iface];
101+
for (int i = 0; i < interfaceContainer->num_altsetting; i++) {
102+
const libusb_interface_descriptor *idesc = &interfaceContainer->altsetting[i];
103+
for (uint8_t epoint = 0; epoint < idesc->bNumEndpoints; epoint++) {
104+
const libusb_endpoint_descriptor *endpoint = &idesc->endpoint[epoint];
105+
endpoint_number++;
106+
if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
107+
endpoint_in = endpoint->bEndpointAddress;
108+
}
109+
else {
110+
endpoint_out = endpoint->bEndpointAddress;
111+
}
112+
}
113+
}
114+
}
115+
116+
return endpoint_number;
117+
}
118+
NAN_METHOD(Load) {
119+
if(info.Length()>0) {
120+
Nan::ThrowError("Load() must not have arguments");
121+
return;
122+
}
123+
int payload_size = 0, tmp = 0;
124+
uint8_t payload = 0x13;
125+
auto return_value = Nan::New<v8::Number>(libusb_interrupt_transfer(device_handle, endpoint_out, &payload, sizeof(payload), &tmp, 16));
126+
info.GetReturnValue().Set(return_value);
127+
}
128+
NAN_METHOD(Request) {
129+
adapter_thread_running.Set(true);
130+
adapter_thread = thread(Read);
131+
auto return_string = PollBytes(controller_payload);
132+
auto return_value = Nan::New<v8::String>(return_string).ToLocalChecked();
133+
134+
info.GetReturnValue().Set(return_value);
135+
}
136+
NAN_METHOD(Stop) {
137+
int code = 0;
138+
if (adapter_thread_running.TestAndClear()) {
139+
adapter_thread.join();
140+
}
141+
if (device_handle) {
142+
code = libusb_release_interface(device_handle, 0);
143+
libusb_close(device_handle);
144+
device_handle = nullptr;
145+
}
146+
auto return_value = Nan::New<v8::Number>(code);
147+
info.GetReturnValue().Set(return_value);
148+
}
149+
void Read() {
150+
int payload_size = 0;
151+
int code = libusb_interrupt_transfer(device_handle, endpoint_in, controller_payload_swap, sizeof(controller_payload_swap), &payload_size, 16);
152+
153+
lock_guard<mutex> lock(main_mutex);
154+
swap(controller_payload_swap, controller_payload);
155+
controller_payload_size.store(payload_size);
156+
157+
this_thread::yield();
158+
}
159+
string PollBytes(uint8_t *results)
160+
{
161+
stringstream return_value;
162+
//This is pretty messy. Must clean up.
163+
unsigned int header = results[0];
164+
unsigned int unknown = results[1];
165+
bitset<8> x(unknown);
166+
unsigned int buttonA = GetNthBit(results[2], 1);
167+
unsigned int buttonB = GetNthBit(results[2], 2);
168+
unsigned int buttonX = GetNthBit(results[2], 3);
169+
unsigned int buttonY = GetNthBit(results[2], 4);
170+
unsigned int padLeft = GetNthBit(results[2], 5);
171+
unsigned int padRight = GetNthBit(results[2], 6);
172+
unsigned int padDown = GetNthBit(results[2], 7);
173+
unsigned int padUp = GetNthBit(results[2], 8);
174+
175+
unsigned int buttonL = GetNthBit(results[3], 4);
176+
unsigned int buttonR = GetNthBit(results[3], 3);
177+
unsigned int buttonZ = GetNthBit(results[3], 2);
178+
unsigned int buttonStart = GetNthBit(results[3], 1);
179+
//TODO: Are axes ints, floats or doubles?
180+
float mainStickX = results[4]/128.0f-1;
181+
float mainStickY = results[5]/128.0f-1;
182+
183+
float cStickX = results[6]/128.0f -1;
184+
float cStickY = results[7]/128.0f -1;
185+
186+
float triggerL = results[8]/255.0f;
187+
float triggerR = results[9]/255.0f;
188+
189+
return_value.precision(5);
190+
191+
return_value << "GCA Header = 0x" << hex << header << dec << endl;
192+
return_value << "Unknown value = " << x << endl;
193+
return_value << "A = " << buttonA << ", B = " << buttonB << ", X = " << buttonX << ", Y =" << buttonY << endl;
194+
return_value << "UP = " << padUp << ", DOWN = " << padDown << ", LEFT = " << padLeft << ", RIGHT =" << padRight << endl;
195+
return_value << "L = " << buttonL << ", R = " << buttonR << ", Z = " << buttonZ << ", START =" << buttonStart << endl << endl;
196+
return_value << "Stick Horiz = " << mainStickX << ", Stick Verti =" << mainStickY << endl;
197+
return_value << "C-Stick Horiz = " << cStickX << ", C-Stick Verti =" << cStickY << endl;
198+
return_value << "L Axis = " << triggerL << ", R Axis =" << triggerR << endl;
199+
200+
return return_value.str();
201+
}
202+
203+
unsigned int GetNthBit(uint8_t number,int n) {
204+
unsigned int bit = (unsigned)(number & (1 << n - 1));
205+
return bit >> n-1;
206+
}
207+
208+
NAN_MODULE_INIT(Bridge) {
209+
NAN_EXPORT(target,Load);
210+
NAN_EXPORT(target,Setup);
211+
NAN_EXPORT(target,Request);
212+
NAN_EXPORT(target, Stop);
213+
}
214+
215+
NODE_MODULE(BridgeTest_2,Bridge)

GCAdapter.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "libusb.h"
2+
#include <sstream>
3+
#include <nan.h>
4+
5+
#pragma once
6+
7+
int AddAdapter(libusb_device *dev);
8+
NAN_METHOD(Load);
9+
NAN_METHOD(Setup);
10+
NAN_METHOD(Request);
11+
void Read();
12+
bool IsAccessible(libusb_device *dev);
13+
std::string PollBytes(uint8_t *results);
14+
unsigned int GetNthBit(uint8_t number, int n);
15+
16+
const uint16_t GAMECUBE_VID = 0x057E;
17+
const uint16_t GAMECUBE_PID = 0x0337;

binding.gyp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"targets": [
3+
{
4+
"link_settings": {
5+
"libraries":["../lib/libusb-1.0.lib"]
6+
},
7+
"include_dirs": [
8+
"<!(node -e \"require('nan')\")",
9+
"include"
10+
],
11+
"target_name": "gca-node",
12+
"sources": ["GCAdapter.cc","GCAdapter.h"]
13+
}
14+
]
15+
}

gca.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
exports.gcaInit = function() {
2+
console.log("GCA Node has been succesfully loaded.");
3+
}

include/Flag.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2014 Dolphin Emulator Project
2+
// Licensed under GPLv2+
3+
// Refer to the license.txt file included.
4+
5+
// Abstraction for a simple flag that can be toggled in a multithreaded way.
6+
//
7+
// Simple API:
8+
// * Set(bool = true): sets the Flag
9+
// * IsSet(): tests if the flag is set
10+
// * Clear(): clears the flag (equivalent to Set(false)).
11+
//
12+
// More advanced features:
13+
// * TestAndSet(bool = true): sets the flag to the given value. If a change was
14+
// needed (the flag did not already have this value)
15+
// the function returns true. Else, false.
16+
// * TestAndClear(): alias for TestAndSet(false).
17+
18+
#pragma once
19+
20+
#include <atomic>
21+
22+
namespace GCAdapter
23+
{
24+
class Flag final
25+
{
26+
public:
27+
// Declared as explicit since we do not want "= true" to work on a flag
28+
// object - it should be made explicit that a flag is *not* a normal
29+
// variable.
30+
explicit Flag(bool initial_value = false) : m_val(initial_value) {}
31+
void Set(bool val = true) { m_val.store(val); }
32+
void Clear() { Set(false); }
33+
bool IsSet() const { return m_val.load(); }
34+
bool TestAndSet(bool val = true)
35+
{
36+
bool expected = !val;
37+
return m_val.compare_exchange_strong(expected, val);
38+
}
39+
40+
bool TestAndClear() { return TestAndSet(false); }
41+
private:
42+
std::atomic_bool m_val;
43+
};
44+
45+
} // namespace Common

0 commit comments

Comments
 (0)