mirror of
https://github.com/libretro/Mesen.git
synced 2025-01-18 14:52:57 +00:00
Removed all code from header files (Utilities project)
This commit is contained in:
parent
e7e77ccfa7
commit
bb52c3f69c
@ -28,7 +28,7 @@ GameServer::~GameServer()
|
||||
void GameServer::AcceptConnections()
|
||||
{
|
||||
while(true) {
|
||||
shared_ptr<Socket> socket = _listener->Accept(NULL, NULL);
|
||||
shared_ptr<Socket> socket = _listener->Accept();
|
||||
if(!socket->ConnectionError()) {
|
||||
_openConnections.push_back(shared_ptr<GameServerConnection>(new GameServerConnection(socket, 1, this)));
|
||||
std::cout << "Client connected." << std::endl;
|
||||
|
@ -27,7 +27,7 @@ ButtonState StandardController::GetButtonState()
|
||||
{
|
||||
ButtonState state;
|
||||
|
||||
for(int i = 0, len = _keyMappings.size(); i < len; i++) {
|
||||
for(size_t i = 0, len = _keyMappings.size(); i < len; i++) {
|
||||
KeyMapping keyMapping = _keyMappings[i];
|
||||
|
||||
state.A |= ControlManager::IsKeyPressed(keyMapping.A);
|
||||
|
@ -7,11 +7,6 @@
|
||||
|
||||
#include "targetver.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
// Windows Header Files:
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#if _WIN32 || _WIN64
|
||||
#if _WIN64
|
||||
#define ENVIRONMENT64
|
||||
|
40
Utilities/CRC32.cpp
Normal file
40
Utilities/CRC32.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "CRC32.h"
|
||||
|
||||
void CRC32::AddData(const uint8_t* pData, const std::streamoff length)
|
||||
{
|
||||
uint8_t* pCur = (uint8_t*)pData;
|
||||
for(std::streamoff remaining = length; remaining--; ++pCur) {
|
||||
_crc = (_crc >> 8) ^ kCrc32Table[(_crc ^ *pCur) & 0xff];
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t CRC32::GetCRC(uint8_t *buffer, std::streamoff length)
|
||||
{
|
||||
CRC32 crc;
|
||||
crc.AddData(buffer, length);
|
||||
return crc._crc;
|
||||
}
|
||||
|
||||
uint32_t CRC32::GetCRC(string filename)
|
||||
{
|
||||
uint32_t crc = 0;
|
||||
|
||||
ifstream file(filename, std::ios::in | std::ios::binary);
|
||||
|
||||
if(file) {
|
||||
file.seekg(0, std::ios::end);
|
||||
std::streamoff fileSize = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
uint8_t* buffer = new uint8_t[(uint32_t)fileSize];
|
||||
|
||||
file.read((char*)buffer, fileSize);
|
||||
file.close();
|
||||
|
||||
crc = GetCRC(buffer, fileSize);
|
||||
|
||||
delete[] buffer;
|
||||
}
|
||||
return crc;
|
||||
}
|
@ -75,41 +75,9 @@ class CRC32
|
||||
private:
|
||||
uint32_t _crc = 0xFFFFFFFF;
|
||||
|
||||
void AddData(const uint8_t* pData, const std::streamoff length)
|
||||
{
|
||||
uint8_t* pCur = (uint8_t*)pData;
|
||||
for(std::streamoff remaining = length; remaining--; ++pCur) {
|
||||
_crc = (_crc >> 8) ^ kCrc32Table[(_crc ^ *pCur) & 0xff];
|
||||
}
|
||||
}
|
||||
void AddData(const uint8_t* pData, const std::streamoff length);
|
||||
|
||||
public:
|
||||
static uint32_t GetCRC(uint8_t *buffer, std::streamoff length)
|
||||
{
|
||||
CRC32 crc;
|
||||
crc.AddData(buffer, length);
|
||||
return crc._crc;
|
||||
}
|
||||
|
||||
static uint32_t GetCRC(string filename)
|
||||
{
|
||||
uint32_t crc = 0;
|
||||
|
||||
ifstream file(filename, ios::in | ios::binary);
|
||||
|
||||
if(file) {
|
||||
file.seekg(0, ios::end);
|
||||
std::streamoff fileSize = file.tellg();
|
||||
file.seekg(0, ios::beg);
|
||||
uint8_t* buffer = new uint8_t[(uint32_t)fileSize];
|
||||
|
||||
file.read((char*)buffer, fileSize);
|
||||
file.close();
|
||||
|
||||
crc = GetCRC(buffer, fileSize);
|
||||
|
||||
delete[] buffer;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
static uint32_t GetCRC(uint8_t *buffer, std::streamoff length);
|
||||
static uint32_t GetCRC(string filename);
|
||||
};
|
@ -1,5 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include <commdlg.h>
|
||||
#include <shlobj.h>
|
||||
#include "FolderUtilities.h"
|
||||
#include "UTF8Util.h"
|
||||
|
20
Utilities/PNGWriter.cpp
Normal file
20
Utilities/PNGWriter.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "PNGWriter.h"
|
||||
#include "miniz.h"
|
||||
|
||||
bool PNGWriter::WritePNG(string filename, uint8_t* buffer, uint32_t xSize, uint32_t ySize, uint32_t bitsPerPixel)
|
||||
{
|
||||
size_t pngSize = 0;
|
||||
void *pngData = tdefl_write_image_to_png_file_in_memory_ex(buffer, xSize, ySize, bitsPerPixel/8, &pngSize, MZ_DEFAULT_LEVEL, MZ_FALSE);
|
||||
if(!pngData) {
|
||||
std::cout << "tdefl_write_image_to_png_file_in_memory_ex() failed!" << std::endl;
|
||||
return false;
|
||||
} else {
|
||||
ofstream file(filename, std::ios::out | std::ios::binary);
|
||||
file.write((char*)pngData, pngSize);
|
||||
file.close();
|
||||
mz_free(pngData);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,24 +1,8 @@
|
||||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "miniz.h"
|
||||
|
||||
class PNGWriter
|
||||
{
|
||||
public:
|
||||
static bool WritePNG(string filename, uint8_t* buffer, uint32_t xSize, uint32_t ySize, uint32_t bitsPerPixel = 32)
|
||||
{
|
||||
size_t pngSize = 0;
|
||||
void *pngData = tdefl_write_image_to_png_file_in_memory_ex(buffer, xSize, ySize, bitsPerPixel/8, &pngSize, MZ_DEFAULT_LEVEL, MZ_FALSE);
|
||||
if(!pngData) {
|
||||
std::cout << "tdefl_write_image_to_png_file_in_memory_ex() failed!" << std::endl;
|
||||
return false;
|
||||
} else {
|
||||
ofstream file(filename, ios::out | ios::binary);
|
||||
file.write((char*)pngData, pngSize);
|
||||
file.close();
|
||||
mz_free(pngData);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool WritePNG(string filename, uint8_t* buffer, uint32_t xSize, uint32_t ySize, uint32_t bitsPerPixel = 32);
|
||||
};
|
@ -1,5 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
#include "SimpleLock.h"
|
||||
#include <Windows.h>
|
||||
|
||||
SimpleLock::SimpleLock()
|
||||
{
|
||||
|
@ -4,7 +4,7 @@
|
||||
class SimpleLock
|
||||
{
|
||||
private:
|
||||
DWORD _holderThreadID;
|
||||
uint32_t _holderThreadID;
|
||||
uint32_t _lockCount;
|
||||
atomic_flag _lock;
|
||||
|
||||
|
223
Utilities/Socket.cpp
Normal file
223
Utilities/Socket.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#pragma comment(lib,"ws2_32.lib") //Winsock Library
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <Windows.h>
|
||||
#include "UPnPPortMapper.h"
|
||||
#include "Socket.h"
|
||||
|
||||
Socket::Socket()
|
||||
{
|
||||
WSADATA wsaDat;
|
||||
if(WSAStartup(MAKEWORD(2, 2), &wsaDat) != 0) {
|
||||
std::cout << "WSAStartup failed." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
_cleanupWSA = true;
|
||||
|
||||
_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if(_socket == INVALID_SOCKET) {
|
||||
std::cout << "Socket creation failed." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
SetSocketOptions();
|
||||
}
|
||||
}
|
||||
|
||||
_sendBuffer = new char[200000];
|
||||
_bufferPosition = 0;
|
||||
}
|
||||
|
||||
Socket::Socket(uintptr_t socket)
|
||||
{
|
||||
_socket = socket;
|
||||
|
||||
if(socket == INVALID_SOCKET) {
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
SetSocketOptions();
|
||||
}
|
||||
|
||||
_sendBuffer = new char[200000];
|
||||
_bufferPosition = 0;
|
||||
}
|
||||
|
||||
Socket::~Socket()
|
||||
{
|
||||
if(_UPnPPort != -1) {
|
||||
UPnPPortMapper::RemoveNATPortMapping(_UPnPPort, IPProtocol::TCP);
|
||||
}
|
||||
|
||||
if(_socket != INVALID_SOCKET) {
|
||||
Close();
|
||||
}
|
||||
if(_cleanupWSA) {
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
delete[] _sendBuffer;
|
||||
}
|
||||
|
||||
void Socket::SetSocketOptions()
|
||||
{
|
||||
//Non-blocking mode
|
||||
u_long iMode = 1;
|
||||
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||
|
||||
//Set send/recv buffers to 256k
|
||||
int bufferSize = 0x40000;
|
||||
setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char*)&bufferSize, sizeof(int));
|
||||
setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char*)&bufferSize, sizeof(int));
|
||||
|
||||
//Disable nagle's algorithm to improve latency
|
||||
u_long value = 1;
|
||||
setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (char*)&value, sizeof(value));
|
||||
}
|
||||
|
||||
void Socket::SetConnectionErrorFlag()
|
||||
{
|
||||
_connectionError = true;
|
||||
}
|
||||
|
||||
void Socket::Close()
|
||||
{
|
||||
std::cout << "Socket closed." << std::endl;
|
||||
shutdown(_socket, SD_SEND);
|
||||
closesocket(_socket);
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
|
||||
bool Socket::ConnectionError()
|
||||
{
|
||||
return _connectionError;
|
||||
}
|
||||
|
||||
void Socket::Bind(uint16_t port)
|
||||
{
|
||||
SOCKADDR_IN serverInf;
|
||||
serverInf.sin_family = AF_INET;
|
||||
serverInf.sin_addr.s_addr = INADDR_ANY;
|
||||
serverInf.sin_port = htons(port);
|
||||
|
||||
if(UPnPPortMapper::AddNATPortMapping(port, port, IPProtocol::TCP)) {
|
||||
_UPnPPort = port;
|
||||
}
|
||||
|
||||
if(bind(_socket, (SOCKADDR*)(&serverInf), sizeof(serverInf)) == SOCKET_ERROR) {
|
||||
std::cout << "Unable to bind socket." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
}
|
||||
|
||||
bool Socket::Connect(const char* hostname, uint16_t port)
|
||||
{
|
||||
// Resolve IP address for hostname
|
||||
struct hostent *host;
|
||||
if((host = gethostbyname(hostname)) == NULL) {
|
||||
std::cout << "Failed to resolve hostname." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
// Setup our socket address structure
|
||||
SOCKADDR_IN SockAddr;
|
||||
SockAddr.sin_port = htons(port);
|
||||
SockAddr.sin_family = AF_INET;
|
||||
SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
|
||||
|
||||
u_long iMode = 0;
|
||||
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||
|
||||
// Attempt to connect to server
|
||||
if(connect(_socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR) {
|
||||
std::cout << "Failed to establish connection with server." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
iMode = 1;
|
||||
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Socket::Listen(int backlog)
|
||||
{
|
||||
if(listen(_socket, backlog) == SOCKET_ERROR) {
|
||||
std::cout << "listen failed." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<Socket> Socket::Accept()
|
||||
{
|
||||
SOCKET socket = accept(_socket, nullptr, nullptr);
|
||||
return shared_ptr<Socket>(new Socket(socket));
|
||||
}
|
||||
|
||||
int Socket::Send(char *buf, int len, int flags)
|
||||
{
|
||||
int retryCount = 15;
|
||||
int nError = 0;
|
||||
int returnVal;
|
||||
do {
|
||||
//Loop until everything has been sent (shouldn't loop at all in the vast majority of cases)
|
||||
if(nError == WSAEWOULDBLOCK) {
|
||||
retryCount--;
|
||||
if(retryCount == 0) {
|
||||
//Connection seems dead, close it.
|
||||
std::cout << "Unable to send data, closing socket." << std::endl;
|
||||
Close();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
returnVal = send(_socket, buf, len, flags);
|
||||
|
||||
nError = WSAGetLastError();
|
||||
if(nError != 0) {
|
||||
if(nError != WSAEWOULDBLOCK) {
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
if(returnVal > 0) {
|
||||
//Sent partial data, adjust pointer & length
|
||||
buf += returnVal;
|
||||
len -= returnVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(nError == WSAEWOULDBLOCK);
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
void Socket::BufferedSend(char *buf, int len)
|
||||
{
|
||||
memcpy(_sendBuffer+_bufferPosition, buf, len);
|
||||
_bufferPosition += len;
|
||||
}
|
||||
|
||||
void Socket::SendBuffer()
|
||||
{
|
||||
Send(_sendBuffer, _bufferPosition, 0);
|
||||
_bufferPosition = 0;
|
||||
}
|
||||
|
||||
int Socket::Recv(char *buf, int len, int flags)
|
||||
{
|
||||
int returnVal = recv(_socket, buf, len, flags);
|
||||
|
||||
int nError = WSAGetLastError();
|
||||
if(nError != WSAEWOULDBLOCK && nError != 0) {
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
|
||||
if(returnVal == 0) {
|
||||
//Socket closed
|
||||
std::cout << "Socket closed by peer." << std::endl;
|
||||
Close();
|
||||
}
|
||||
|
||||
return returnVal;
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
#pragma once
|
||||
#pragma comment(lib,"ws2_32.lib") //Winsock Library
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "UPnPPortMapper.h"
|
||||
#include <winsock2.h>
|
||||
|
||||
class Socket
|
||||
{
|
||||
private:
|
||||
SOCKET _socket = INVALID_SOCKET;
|
||||
uintptr_t _socket = ~0;
|
||||
bool _connectionError = false;
|
||||
bool _cleanupWSA = false;
|
||||
char* _sendBuffer;
|
||||
@ -15,218 +13,23 @@ private:
|
||||
int32_t _UPnPPort = -1;
|
||||
|
||||
public:
|
||||
Socket()
|
||||
{
|
||||
WSADATA wsaDat;
|
||||
if(WSAStartup(MAKEWORD(2, 2), &wsaDat) != 0) {
|
||||
std::cout << "WSAStartup failed." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
_cleanupWSA = true;
|
||||
Socket();
|
||||
Socket(uintptr_t socket);
|
||||
~Socket();
|
||||
|
||||
_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if(_socket == INVALID_SOCKET) {
|
||||
std::cout << "Socket creation failed." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
SetSocketOptions();
|
||||
}
|
||||
}
|
||||
void SetSocketOptions();
|
||||
void SetConnectionErrorFlag();
|
||||
|
||||
_sendBuffer = new char[200000];
|
||||
_bufferPosition = 0;
|
||||
}
|
||||
void Close();
|
||||
bool ConnectionError();
|
||||
|
||||
Socket(SOCKET socket)
|
||||
{
|
||||
_socket = socket;
|
||||
void Bind(uint16_t port);
|
||||
bool Connect(const char* hostname, uint16_t port);
|
||||
void Listen(int backlog);
|
||||
shared_ptr<Socket> Accept();
|
||||
|
||||
if(socket == INVALID_SOCKET) {
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
SetSocketOptions();
|
||||
}
|
||||
|
||||
_sendBuffer = new char[200000];
|
||||
_bufferPosition = 0;
|
||||
}
|
||||
|
||||
~Socket()
|
||||
{
|
||||
if(_UPnPPort != -1) {
|
||||
UPnPPortMapper::RemoveNATPortMapping(_UPnPPort, IPProtocol::TCP);
|
||||
}
|
||||
|
||||
if(_socket != INVALID_SOCKET) {
|
||||
Close();
|
||||
}
|
||||
if(_cleanupWSA) {
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
delete[] _sendBuffer;
|
||||
}
|
||||
|
||||
void SetSocketOptions()
|
||||
{
|
||||
//Non-blocking mode
|
||||
u_long iMode = 1;
|
||||
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||
|
||||
//Set send/recv buffers to 256k
|
||||
int bufferSize = 0x40000;
|
||||
setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char*)&bufferSize, sizeof(int));
|
||||
setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char*)&bufferSize, sizeof(int));
|
||||
|
||||
//Disable nagle's algorithm to improve latency
|
||||
u_long value = 1;
|
||||
setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (char*)&value, sizeof(value));
|
||||
}
|
||||
|
||||
void SetConnectionErrorFlag()
|
||||
{
|
||||
_connectionError = true;
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
std::cout << "Socket closed." << std::endl;
|
||||
shutdown(_socket, SD_SEND);
|
||||
closesocket(_socket);
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
|
||||
bool ConnectionError()
|
||||
{
|
||||
return _connectionError;
|
||||
}
|
||||
|
||||
void Bind(u_short port)
|
||||
{
|
||||
SOCKADDR_IN serverInf;
|
||||
serverInf.sin_family = AF_INET;
|
||||
serverInf.sin_addr.s_addr = INADDR_ANY;
|
||||
serverInf.sin_port = htons(port);
|
||||
|
||||
if(UPnPPortMapper::AddNATPortMapping(port, port, IPProtocol::TCP)) {
|
||||
_UPnPPort = port;
|
||||
}
|
||||
|
||||
if(bind(_socket, (SOCKADDR*)(&serverInf), sizeof(serverInf)) == SOCKET_ERROR) {
|
||||
std::cout << "Unable to bind socket." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
}
|
||||
|
||||
bool Connect(const char* hostname, u_short port)
|
||||
{
|
||||
// Resolve IP address for hostname
|
||||
struct hostent *host;
|
||||
if((host = gethostbyname(hostname)) == NULL) {
|
||||
std::cout << "Failed to resolve hostname." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
// Setup our socket address structure
|
||||
SOCKADDR_IN SockAddr;
|
||||
SockAddr.sin_port = htons(port);
|
||||
SockAddr.sin_family = AF_INET;
|
||||
SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
|
||||
|
||||
u_long iMode = 0;
|
||||
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||
|
||||
// Attempt to connect to server
|
||||
if(connect(_socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR) {
|
||||
std::cout << "Failed to establish connection with server." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
iMode = 1;
|
||||
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Listen(int backlog)
|
||||
{
|
||||
if(listen(_socket, backlog) == SOCKET_ERROR) {
|
||||
std::cout << "listen failed." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<Socket> Accept(sockaddr* addr, int *addrlen)
|
||||
{
|
||||
SOCKET socket = accept(_socket, addr, addrlen);
|
||||
return shared_ptr<Socket>(new Socket(socket));
|
||||
}
|
||||
|
||||
int Send(char *buf, int len, int flags)
|
||||
{
|
||||
int retryCount = 15;
|
||||
int nError = 0;
|
||||
int returnVal;
|
||||
do {
|
||||
//Loop until everything has been sent (shouldn't loop at all in the vast majority of cases)
|
||||
if(nError == WSAEWOULDBLOCK) {
|
||||
retryCount--;
|
||||
if(retryCount == 0) {
|
||||
//Connection seems dead, close it.
|
||||
std::cout << "Unable to send data, closing socket." << std::endl;
|
||||
Close();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
returnVal = send(_socket, buf, len, flags);
|
||||
|
||||
nError = WSAGetLastError();
|
||||
if(nError != 0) {
|
||||
if(nError != WSAEWOULDBLOCK) {
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
if(returnVal > 0) {
|
||||
//Sent partial data, adjust pointer & length
|
||||
buf += returnVal;
|
||||
len -= returnVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(nError == WSAEWOULDBLOCK);
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
void BufferedSend(char *buf, int len)
|
||||
{
|
||||
memcpy(_sendBuffer+_bufferPosition, buf, len);
|
||||
_bufferPosition += len;
|
||||
}
|
||||
|
||||
void SendBuffer()
|
||||
{
|
||||
Send(_sendBuffer, _bufferPosition, 0);
|
||||
_bufferPosition = 0;
|
||||
}
|
||||
|
||||
int Recv(char *buf, int len, int flags)
|
||||
{
|
||||
int returnVal = recv(_socket, buf, len, flags);
|
||||
|
||||
int nError = WSAGetLastError();
|
||||
if(nError != WSAEWOULDBLOCK && nError != 0) {
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
|
||||
if(returnVal == 0) {
|
||||
//Socket closed
|
||||
std::cout << "Socket closed by peer." << std::endl;
|
||||
Close();
|
||||
}
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
int Send(char *buf, int len, int flags);
|
||||
void BufferedSend(char *buf, int len);
|
||||
void SendBuffer();
|
||||
int Recv(char *buf, int len, int flags);
|
||||
};
|
||||
|
31
Utilities/Timer.cpp
Normal file
31
Utilities/Timer.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include "Timer.h"
|
||||
|
||||
Timer::Timer()
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
if(!QueryPerformanceFrequency(&li)) {
|
||||
throw;
|
||||
}
|
||||
|
||||
_frequency = double(li.QuadPart) / 1000.0;
|
||||
|
||||
QueryPerformanceCounter(&li);
|
||||
_start = li.QuadPart;
|
||||
}
|
||||
|
||||
void Timer::Reset()
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceCounter(&li);
|
||||
_start = li.QuadPart;
|
||||
}
|
||||
|
||||
double Timer::GetElapsedMS()
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceCounter(&li);
|
||||
return double(li.QuadPart - _start) / _frequency;
|
||||
}
|
@ -4,32 +4,10 @@ class Timer
|
||||
{
|
||||
private:
|
||||
double _frequency = 0.0;
|
||||
LARGE_INTEGER _start;
|
||||
uint64_t _start;
|
||||
|
||||
public:
|
||||
Timer() {
|
||||
LARGE_INTEGER li;
|
||||
if(!QueryPerformanceFrequency(&li)) {
|
||||
throw;
|
||||
}
|
||||
|
||||
_frequency = double(li.QuadPart) / 1000.0;
|
||||
|
||||
QueryPerformanceCounter(&li);
|
||||
_start = li;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceCounter(&li);
|
||||
_start = li;
|
||||
}
|
||||
|
||||
double GetElapsedMS()
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceCounter(&li);
|
||||
return double(li.QuadPart - _start.QuadPart) / _frequency;
|
||||
}
|
||||
Timer();
|
||||
void Reset();
|
||||
double GetElapsedMS();
|
||||
};
|
126
Utilities/UPnPPortMapper.cpp
Normal file
126
Utilities/UPnPPortMapper.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
#include "stdafx.h"
|
||||
#include <winsock2.h>
|
||||
#include <natupnp.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include "UPnPPortMapper.h"
|
||||
|
||||
bool UPnPPortMapper::AddNATPortMapping(uint16_t internalPort, uint16_t externalPort, IPProtocol protocol)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
|
||||
IUPnPNAT *nat = nullptr;
|
||||
HRESULT hResult = CoCreateInstance(__uuidof(UPnPNAT), nullptr, CLSCTX_ALL, __uuidof(IUPnPNAT), (void**)&nat);
|
||||
|
||||
BSTR proto = SysAllocString((protocol == IPProtocol::TCP) ? L"TCP" : L"UDP");
|
||||
|
||||
if(SUCCEEDED(hResult) && nat) {
|
||||
IStaticPortMappingCollection *spmc = nullptr;
|
||||
hResult = nat->get_StaticPortMappingCollection(&spmc);
|
||||
if(SUCCEEDED(hResult) && spmc) {
|
||||
IStaticPortMapping *spm = nullptr;
|
||||
hResult = spmc->get_Item(externalPort, proto, &spm);
|
||||
if(spm != nullptr) {
|
||||
//An identical mapping already exists, remove it
|
||||
if(RemoveNATPortMapping(externalPort, protocol)) {
|
||||
std::cout << "Removed existing UPnP mapping." << std::endl;
|
||||
spm->Release();
|
||||
spm = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(!SUCCEEDED(hResult) || spm == nullptr) {
|
||||
std::cout << "Attempting to automatically forward port via UPnP..." << std::endl;
|
||||
|
||||
wstring localIP = GetLocalIP();
|
||||
BSTR desc = SysAllocString(L"NESEmu for NetPlay");
|
||||
BSTR clientStr = SysAllocString(localIP.c_str());
|
||||
hResult = spmc->Add(externalPort, proto, internalPort, clientStr, true, desc, &spm);
|
||||
SysFreeString(clientStr);
|
||||
SysFreeString(desc);
|
||||
|
||||
if(SUCCEEDED(hResult) && spm) {
|
||||
//Successfully added a new port mapping
|
||||
std::cout << std::dec << "Forwarded port " << externalPort << " to IP ";
|
||||
std::wcout << localIP.c_str() << std::endl;
|
||||
spm->Release();
|
||||
result = true;
|
||||
} else {
|
||||
std::cout << "Unable to add UPnP port mapping. ";
|
||||
std::cout << "IP: ";
|
||||
std::wcout << localIP.c_str();
|
||||
std::cout << " HRESULT: 0x" << std::hex << hResult << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "Unable to add UPnP port mapping." << std::endl;
|
||||
}
|
||||
spmc->Release();
|
||||
}
|
||||
nat->Release();
|
||||
}
|
||||
|
||||
SysFreeString(proto);
|
||||
|
||||
CoUninitialize();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool UPnPPortMapper::RemoveNATPortMapping(uint16_t externalPort, IPProtocol protocol)
|
||||
{
|
||||
IUPnPNAT *nat = nullptr;
|
||||
IStaticPortMappingCollection *spmc;
|
||||
bool result = false;
|
||||
|
||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
|
||||
HRESULT hResult = ::CoCreateInstance(__uuidof(UPnPNAT), nullptr, CLSCTX_ALL, __uuidof(IUPnPNAT), (void**)&nat);
|
||||
|
||||
BSTR proto = SysAllocString((protocol == IPProtocol::TCP) ? L"TCP" : L"UDP");
|
||||
|
||||
if(SUCCEEDED(hResult) && nat) {
|
||||
hResult = nat->get_StaticPortMappingCollection(&spmc);
|
||||
if(SUCCEEDED(hResult) && spmc) {
|
||||
spmc->Remove(externalPort, proto);
|
||||
spmc->Release();
|
||||
result = true;
|
||||
}
|
||||
nat->Release();
|
||||
}
|
||||
|
||||
SysFreeString(proto);
|
||||
|
||||
CoUninitialize();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
wstring UPnPPortMapper::GetLocalIP()
|
||||
{
|
||||
wstring localIP;
|
||||
|
||||
ADDRINFOW *result = nullptr;
|
||||
ADDRINFOW hints;
|
||||
|
||||
ZeroMemory(&hints, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
wchar_t hostName[255];
|
||||
DWORD hostSize = 255;
|
||||
GetComputerName(hostName, &hostSize);
|
||||
|
||||
if(GetAddrInfoW(hostName, nullptr, &hints, &result) == 0) {
|
||||
wchar_t ipAddr[255];
|
||||
DWORD ipSize = 255;
|
||||
if(WSAAddressToString(result->ai_addr, (DWORD)result->ai_addrlen, nullptr, ipAddr, &ipSize) == 0) {
|
||||
localIP = ipAddr;
|
||||
}
|
||||
FreeAddrInfoW(result);
|
||||
}
|
||||
return localIP;
|
||||
}
|
||||
|
||||
|
@ -2,9 +2,6 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <natupnp.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
using std::wstring;
|
||||
|
||||
enum class IPProtocol
|
||||
@ -15,125 +12,10 @@ enum class IPProtocol
|
||||
|
||||
class UPnPPortMapper
|
||||
{
|
||||
private:
|
||||
static wstring GetLocalIP();
|
||||
|
||||
public:
|
||||
static bool AddNATPortMapping(WORD internalPort, WORD externalPort, IPProtocol protocol)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
|
||||
IUPnPNAT *nat = nullptr;
|
||||
HRESULT hResult = CoCreateInstance(__uuidof(UPnPNAT), nullptr, CLSCTX_ALL, __uuidof(IUPnPNAT), (void**)&nat);
|
||||
|
||||
BSTR proto = SysAllocString((protocol == IPProtocol::TCP) ? L"TCP" : L"UDP");
|
||||
|
||||
if(SUCCEEDED(hResult) && nat) {
|
||||
IStaticPortMappingCollection *spmc = nullptr;
|
||||
hResult = nat->get_StaticPortMappingCollection(&spmc);
|
||||
if(SUCCEEDED(hResult) && spmc) {
|
||||
IStaticPortMapping *spm = nullptr;
|
||||
hResult = spmc->get_Item(externalPort, proto, &spm);
|
||||
if(spm != nullptr) {
|
||||
//An identical mapping already exists, remove it
|
||||
if(RemoveNATPortMapping(externalPort, protocol)) {
|
||||
std::cout << "Removed existing UPnP mapping." << std::endl;
|
||||
spm->Release();
|
||||
spm = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(!SUCCEEDED(hResult) || spm == nullptr) {
|
||||
std::cout << "Attempting to automatically forward port via UPnP..." << std::endl;
|
||||
|
||||
wstring localIP = GetLocalIP();
|
||||
BSTR desc = SysAllocString(L"NESEmu for NetPlay");
|
||||
BSTR clientStr = SysAllocString(localIP.c_str());
|
||||
hResult = spmc->Add(externalPort, proto, internalPort, clientStr, true, desc, &spm);
|
||||
SysFreeString(clientStr);
|
||||
SysFreeString(desc);
|
||||
|
||||
if(SUCCEEDED(hResult) && spm) {
|
||||
//Successfully added a new port mapping
|
||||
std::cout << std::dec << "Forwarded port " << externalPort << " to IP ";
|
||||
std::wcout << localIP.c_str() << std::endl;
|
||||
spm->Release();
|
||||
result = true;
|
||||
} else {
|
||||
std::cout << "Unable to add UPnP port mapping. ";
|
||||
std::cout << "IP: ";
|
||||
std::wcout << localIP.c_str();
|
||||
std::cout << " HRESULT: 0x" << std::hex << hResult << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << "Unable to add UPnP port mapping." << std::endl;
|
||||
}
|
||||
spmc->Release();
|
||||
}
|
||||
nat->Release();
|
||||
}
|
||||
|
||||
SysFreeString(proto);
|
||||
|
||||
CoUninitialize();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool RemoveNATPortMapping(WORD externalPort, IPProtocol protocol)
|
||||
{
|
||||
IUPnPNAT *nat = nullptr;
|
||||
IStaticPortMappingCollection *spmc;
|
||||
bool result = false;
|
||||
|
||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
|
||||
HRESULT hResult = ::CoCreateInstance(__uuidof(UPnPNAT), nullptr, CLSCTX_ALL, __uuidof(IUPnPNAT), (void**)&nat);
|
||||
|
||||
BSTR proto = SysAllocString((protocol == IPProtocol::TCP) ? L"TCP" : L"UDP");
|
||||
|
||||
if(SUCCEEDED(hResult) && nat) {
|
||||
hResult = nat->get_StaticPortMappingCollection(&spmc);
|
||||
if(SUCCEEDED(hResult) && spmc) {
|
||||
spmc->Remove(externalPort, proto);
|
||||
spmc->Release();
|
||||
result = true;
|
||||
}
|
||||
nat->Release();
|
||||
}
|
||||
|
||||
SysFreeString(proto);
|
||||
|
||||
CoUninitialize();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static wstring GetLocalIP()
|
||||
{
|
||||
wstring localIP;
|
||||
|
||||
ADDRINFOW *result = nullptr;
|
||||
ADDRINFOW hints;
|
||||
|
||||
ZeroMemory(&hints, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
wchar_t hostName[255];
|
||||
DWORD hostSize = 255;
|
||||
GetComputerName(hostName, &hostSize);
|
||||
|
||||
if(GetAddrInfoW(hostName, nullptr, &hints, &result) == 0) {
|
||||
wchar_t ipAddr[255];
|
||||
DWORD ipSize = 255;
|
||||
if(WSAAddressToString(result->ai_addr, (DWORD)result->ai_addrlen, nullptr, ipAddr, &ipSize) == 0) {
|
||||
localIP = ipAddr;
|
||||
}
|
||||
FreeAddrInfoW(result);
|
||||
}
|
||||
return localIP;
|
||||
}
|
||||
|
||||
|
||||
static bool AddNATPortMapping(uint16_t internalPort, uint16_t externalPort, IPProtocol protocol);
|
||||
static bool RemoveNATPortMapping(uint16_t externalPort, IPProtocol protocol);
|
||||
};
|
28
Utilities/UTF8Util.cpp
Normal file
28
Utilities/UTF8Util.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "stdafx.h"
|
||||
#include "UTF8Util.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define _WINSOCKAPI_
|
||||
#include <Windows.h>
|
||||
|
||||
namespace utf8 {
|
||||
std::wstring utf8::decode(const std::string &str)
|
||||
{
|
||||
if(str.empty()) return std::wstring();
|
||||
int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
|
||||
std::wstring wstrTo(size_needed, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
|
||||
return wstrTo;
|
||||
}
|
||||
|
||||
std::string utf8::encode(const std::wstring &wstr)
|
||||
{
|
||||
if(wstr.empty()) return std::string();
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
|
||||
std::string strTo(size_needed, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
|
||||
return strTo;
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,67 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#ifdef _MSC_VER
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace utf8 {
|
||||
#ifdef _WIN32
|
||||
class utf8
|
||||
{
|
||||
public:
|
||||
static std::wstring decode(const std::string &str)
|
||||
{
|
||||
if(str.empty()) return std::wstring();
|
||||
int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
|
||||
std::wstring wstrTo(size_needed, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
|
||||
return wstrTo;
|
||||
}
|
||||
|
||||
static std::string encode(const std::wstring &wstr)
|
||||
{
|
||||
if(wstr.empty()) return std::string();
|
||||
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
|
||||
std::string strTo(size_needed, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
|
||||
return strTo;
|
||||
}
|
||||
static std::wstring decode(const std::string &str);
|
||||
static std::string encode(const std::wstring &wstr);
|
||||
};
|
||||
|
||||
class ifstream : public std::ifstream
|
||||
{
|
||||
public:
|
||||
ifstream(const std::string& _Str, ios_base::openmode _Mode = ios_base::in, int _Prot = (int)ios_base::_Openprot) : std::ifstream(utf8::decode(_Str), _Mode, _Prot)
|
||||
{
|
||||
}
|
||||
|
||||
ifstream() : std::ifstream()
|
||||
{
|
||||
}
|
||||
|
||||
void open(const std::string& _Str, ios_base::openmode _Mode = ios_base::in, int _Prot = (int)ios_base::_Openprot)
|
||||
{
|
||||
std::ifstream::open(utf8::decode(_Str), _Mode, _Prot);
|
||||
}
|
||||
ifstream(const std::string& _Str, ios_base::openmode _Mode = ios_base::in, int _Prot = (int)ios_base::_Openprot) : std::ifstream(utf8::decode(_Str), _Mode, _Prot) { }
|
||||
ifstream() : std::ifstream() { }
|
||||
void open(const std::string& _Str, ios_base::openmode _Mode = ios_base::in, int _Prot = (int)ios_base::_Openprot) { std::ifstream::open(utf8::decode(_Str), _Mode, _Prot); }
|
||||
};
|
||||
|
||||
class ofstream : public std::ofstream
|
||||
{
|
||||
public:
|
||||
ofstream(const std::string& _Str, ios_base::openmode _Mode = ios_base::in, int _Prot = (int)ios_base::_Openprot) : std::ofstream(utf8::decode(_Str), _Mode, _Prot)
|
||||
{
|
||||
}
|
||||
|
||||
ofstream() : std::ofstream()
|
||||
{
|
||||
}
|
||||
|
||||
void open(const std::string& _Str, ios_base::openmode _Mode = ios_base::in, int _Prot = (int)ios_base::_Openprot)
|
||||
{
|
||||
std::ofstream::open(utf8::decode(_Str), _Mode, _Prot);
|
||||
}
|
||||
ofstream(const std::string& _Str, ios_base::openmode _Mode = ios_base::in, int _Prot = (int)ios_base::_Openprot) : std::ofstream(utf8::decode(_Str), _Mode, _Prot) { }
|
||||
ofstream() : std::ofstream() { }
|
||||
void open(const std::string& _Str, ios_base::openmode _Mode = ios_base::in, int _Prot = (int)ios_base::_Openprot) { std::ofstream::open(utf8::decode(_Str), _Mode, _Prot); }
|
||||
};
|
||||
#else
|
||||
using std::ifstream;
|
||||
|
@ -128,6 +128,7 @@
|
||||
<CallingConvention>Cdecl</CallingConvention>
|
||||
<AdditionalIncludeDirectories>
|
||||
</AdditionalIncludeDirectories>
|
||||
<ShowIncludes>true</ShowIncludes>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@ -241,9 +242,12 @@
|
||||
<ClInclude Include="ZIPReader.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CRC32.cpp" />
|
||||
<ClCompile Include="FolderUtilities.cpp" />
|
||||
<ClCompile Include="miniz.cpp" />
|
||||
<ClCompile Include="PNGWriter.cpp" />
|
||||
<ClCompile Include="SimpleLock.cpp" />
|
||||
<ClCompile Include="Socket.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
@ -252,6 +256,9 @@
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release x64|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release x64|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Timer.cpp" />
|
||||
<ClCompile Include="UPnPPortMapper.cpp" />
|
||||
<ClCompile Include="UTF8Util.cpp" />
|
||||
<ClCompile Include="ZIPReader.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
@ -64,5 +64,23 @@
|
||||
<ClCompile Include="SimpleLock.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Socket.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UPnPPortMapper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UTF8Util.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Timer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PNGWriter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CRC32.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -12,6 +12,7 @@
|
||||
|
||||
using std::shared_ptr;
|
||||
using utf8::ifstream;
|
||||
using utf8::ofstream;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::atomic;
|
||||
|
Loading…
x
Reference in New Issue
Block a user