dolphin/Source/Core/InputCommon/Src/UDPWiimote.cpp
Lioncash 8a9fcd3014 Kill off dangling else's in the InputCommon project.
Some indentations were also too far for some things. Fixed this.

Also update the license header to show Git instead of SVN.

Got rid of some trailing spaces/tabs too.
2013-04-14 22:53:10 -04:00

435 lines
8.5 KiB
C++

#include "UDPWiimote.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#define sock_t SOCKET
#define ERRNO WSAGetLastError()
#undef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#define BAD_SOCK INVALID_SOCKET
#define close(x) closesocket(x)
#define cleanup do {noinst--; if (noinst==0) WSACleanup();} while (0)
#define blockingoff(sock) ioctlsocket(sock, FIONBIO, &iMode)
#define dataz char*
#ifdef _MSC_VER
#pragma comment (lib, "Ws2_32.lib")
#endif
#else
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#define BAD_SOCK -1
#define ERRNO errno
#define cleanup noinst--
#define blockingoff(sock) fcntl(sock, F_SETFL, O_NONBLOCK)
#define dataz void*
#define sock_t int
#endif
#include "Thread.h"
#include "Timer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <list>
#include <functional>
struct UDPWiimote::_d
{
std::thread thread;
std::list<sock_t> sockfds;
std::mutex termLock, mutex, nameMutex;
volatile bool exit;
sock_t bipv4_fd, bipv6_fd;
};
int UDPWiimote::noinst = 0;
UDPWiimote::UDPWiimote(const char *_port, const char * name, int _index) :
port(_port), displayName(name),
d(new _d) ,x(0),y(0),z(1.0f),naX(0),naY(0),naZ(-1.0f),nunX(0),nunY(0),
pointerX(1001.0f/2),pointerY(0),nunMask(0),mask(0),index(_index), int_port(atoi(_port))
{
static bool sranded=false;
if (!sranded)
{
srand((unsigned int)time(0));
sranded=true;
}
bcastMagic=rand() & 0xFFFF;
#ifdef _WIN32
u_long iMode = 1;
#endif
struct addrinfo hints, *servinfo, *p;
int rv;
#ifdef _WIN32
if (noinst==0)
{
WORD sockVersion;
WSADATA wsaData;
sockVersion = MAKEWORD(2, 2);
WSAStartup(sockVersion, &wsaData);
}
#endif
noinst++;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if (!int_port)
{
cleanup;
err=-1;
return;
}
if ((rv = getaddrinfo(NULL, _port, &hints, &servinfo)) != 0)
{
cleanup;
err=-1;
return;
}
// loop through all the results and bind to everything we can
for(p = servinfo; p != NULL; p = p->ai_next)
{
sock_t sock;
if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == BAD_SOCK)
{
continue;
}
if (bind(sock, p->ai_addr, (int)p->ai_addrlen) == -1)
{
close(sock);
continue;
}
d->sockfds.push_back(sock);
}
if (d->sockfds.empty())
{
cleanup;
err=-2;
return;
}
freeaddrinfo(servinfo);
err=0;
d->exit=false;
initBroadcastIPv4();
initBroadcastIPv6();
std::lock_guard<std::mutex> lk(d->termLock);
d->thread = std::thread(std::mem_fun(&UDPWiimote::mainThread), this);
return;
}
void UDPWiimote::mainThread()
{
std::unique_lock<std::mutex> lk(d->termLock);
Common::Timer time;
fd_set fds;
struct timeval timeout;
timeout.tv_sec=0;
timeout.tv_usec=0;
time.Update();
do
{
int maxfd=0;
FD_ZERO(&fds);
for (std::list<sock_t>::iterator i=d->sockfds.begin(); i!=d->sockfds.end(); i++)
{
FD_SET(*i,&fds);
#ifndef _WIN32
if (*i>=maxfd)
maxfd=(*i)+1;
#endif
}
u64 tleft=timeout.tv_sec*1000+timeout.tv_usec/1000;
u64 telapsed=time.GetTimeDifference();
time.Update();
if (tleft<=telapsed)
{
timeout.tv_sec=1;
timeout.tv_usec=500000;
broadcastPresence();
}
else
{
tleft-=telapsed;
timeout.tv_sec=(long)(tleft/1000);
timeout.tv_usec=(tleft%1000)*1000;
}
lk.unlock(); //VERY hacky. don't like it
if (d->exit) return;
int rt=select(maxfd,&fds,NULL,NULL,&timeout);
if (d->exit) return;
lk.lock();
if (d->exit) return;
if (rt)
{
for (std::list<sock_t>::iterator i=d->sockfds.begin(); i!=d->sockfds.end(); i++)
{
if (FD_ISSET(*i,&fds))
{
sock_t fd=*i;
u8 bf[64];
int size=60;
size_t addr_len;
struct sockaddr_storage their_addr;
addr_len = sizeof their_addr;
if ((size = recvfrom(fd,
(dataz)bf,
size , 0,(struct sockaddr *)&their_addr, (socklen_t*)&addr_len)) == -1)
{
ERROR_LOG(WIIMOTE,"UDPWii Packet error");
}
else
{
std::lock_guard<std::mutex> lkm(d->mutex);
if (pharsePacket(bf,size)==0)
{
//NOTICE_LOG(WIIMOTE,"UDPWII New pack");
}
else
{
//NOTICE_LOG(WIIMOTE,"UDPWII Wrong pack format... ignoring");
}
}
}
}
}
} while (!(d->exit));
}
UDPWiimote::~UDPWiimote()
{
d->exit = true;
{
std::lock_guard<std::mutex> lk(d->termLock);
d->thread.join();
}
for (std::list<sock_t>::iterator i=d->sockfds.begin(); i!=d->sockfds.end(); i++)
close(*i);
close(d->bipv4_fd);
close(d->bipv6_fd);
cleanup;
delete d;
}
#define ACCEL_FLAG (1 << 0)
#define BUTT_FLAG (1 << 1)
#define IR_FLAG (1 << 2)
#define NUN_FLAG (1 << 3)
#define NUNACCEL_FLAG (1 << 4)
int UDPWiimote::pharsePacket(u8 * bf, size_t size)
{
if (size < 3)
return -1;
if (bf[0] != 0xde)
return -1;
//if (bf[1]==0)
// time=0;
//if (bf[1]<time) //NOT LONGER NEEDED TO ALLOW MULTIPLE IPHONES ON A SINGLE PORT
// return -1;
//time=bf[1];
u32 *p=(u32*)(&bf[3]);
if (bf[2] & ACCEL_FLAG)
{
if ((size-(((u8*)p)-bf)) < 12)
return -1;
double ux,uy,uz;
ux=(double)((s32)ntohl(*p)); p++;
uy=(double)((s32)ntohl(*p)); p++;
uz=(double)((s32)ntohl(*p)); p++;
x=ux/1048576; //packet accel data
y=uy/1048576;
z=uz/1048576;
}
if (bf[2] & BUTT_FLAG)
{
if ((size-(((u8*)p)-bf)) < 4)
return -1;
mask=ntohl(*p); p++;
}
if (bf[2] & IR_FLAG)
{
if ((size-(((u8*)p)-bf)) < 8)
return -1;
pointerX=((double)((s32)ntohl(*p)))/1048576; p++;
pointerY=((double)((s32)ntohl(*p)))/1048576; p++;
}
if (bf[2] & NUN_FLAG)
{
if ((size-(((u8*)p)-bf)) < 9)
return -1;
nunMask=*((u8*)p); p=(u32*)(((u8*)p)+1);
nunX=((double)((s32)ntohl(*p)))/1048576; p++;
nunY=((double)((s32)ntohl(*p)))/1048576; p++;
}
if (bf[2] & NUNACCEL_FLAG)
{
if ((size-(((u8*)p)-bf)) < 12)
return -1;
double ux,uy,uz;
ux=(double)((s32)ntohl(*p)); p++;
uy=(double)((s32)ntohl(*p)); p++;
uz=(double)((s32)ntohl(*p)); p++;
naX=ux/1048576; //packet accel data
naY=uy/1048576;
naZ=uz/1048576;
}
return 0;
}
void UDPWiimote::initBroadcastIPv4()
{
d->bipv4_fd=socket(AF_INET, SOCK_DGRAM, 0);
if (d->bipv4_fd == BAD_SOCK)
{
WARN_LOG(WIIMOTE,"socket() failed");
return;
}
int broad=1;
if (setsockopt(d->bipv4_fd,SOL_SOCKET,SO_BROADCAST, (const dataz)(&broad), sizeof broad) == -1)
{
WARN_LOG(WIIMOTE,"setsockopt(SO_BROADCAST) failed");
return;
}
}
void UDPWiimote::broadcastIPv4(const void * data, size_t size)
{
struct sockaddr_in their_addr;
their_addr.sin_family = AF_INET;
their_addr.sin_port = htons(4431);
their_addr.sin_addr.s_addr = INADDR_BROADCAST;
memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
int num;
if ((num=sendto(d->bipv4_fd,(const dataz)data,(int)size,0,(struct sockaddr *) &their_addr, sizeof their_addr)) == -1)
{
WARN_LOG(WIIMOTE,"sendto() failed");
return;
}
}
void UDPWiimote::initBroadcastIPv6()
{
//TODO: IPv6 support
}
void UDPWiimote::broadcastIPv6(const void * data, size_t size)
{
//TODO: IPv6 support
}
void UDPWiimote::broadcastPresence()
{
size_t slen;
u8 bf[512];
bf[0]=0xdf; //magic number
*((u16*)(&(bf[1])))=htons(bcastMagic); //unique per-wiimote 16-bit ID
bf[3]=(u8)index; //wiimote index
*((u16*)(&(bf[4])))=htons(int_port); //port
{
std::lock_guard<std::mutex> lk(d->nameMutex);
slen=displayName.size();
if (slen>=256)
slen=255;
bf[6]=(u8)slen; //display name size (max 255)
memcpy(&(bf[7]),displayName.c_str(),slen); //display name
}
broadcastIPv4(bf,7+slen);
broadcastIPv6(bf,7+slen);
}
void UDPWiimote::getAccel(float &_x, float &_y, float &_z)
{
std::lock_guard<std::mutex> lk(d->mutex);
_x=(float)x;
_y=(float)y;
_z=(float)z;
}
u32 UDPWiimote::getButtons()
{
u32 msk;
std::lock_guard<std::mutex> lk(d->mutex);
msk=mask;
return msk;
}
void UDPWiimote::getIR(float &_x, float &_y)
{
std::lock_guard<std::mutex> lk(d->mutex);
_x=(float)pointerX;
_y=(float)pointerY;
}
void UDPWiimote::getNunchuck(float &_x, float &_y, u8 &_mask)
{
std::lock_guard<std::mutex> lk(d->mutex);
_x=(float)nunX;
_y=(float)nunY;
_mask=nunMask;
}
void UDPWiimote::getNunchuckAccel(float &_x, float &_y, float &_z)
{
std::lock_guard<std::mutex> lk(d->mutex);
_x=(float)naX;
_y=(float)naY;
_z=(float)naZ;
}
const char * UDPWiimote::getPort()
{
return port.c_str();
}
void UDPWiimote::changeName(const char * name)
{
std::lock_guard<std::mutex> lk(d->nameMutex);
displayName=name;
}