diff --git a/Src/Network/UDPPacket.h b/Src/Network/UDPPacket.h new file mode 100644 index 00000000..7e2f792b --- /dev/null +++ b/Src/Network/UDPPacket.h @@ -0,0 +1,83 @@ +#ifndef _UDP_H_ +#define _UDP_H_ +#include + +namespace SMUDP +{ + struct Packet + { + + static const UINT32 BUFFER_SIZE_UDP = 1460; + + UINT32 crc; + UINT16 currentID; + UINT16 totalIDs; + UINT16 flags; + UINT16 length; + UCHAR data[BUFFER_SIZE_UDP]; + + Packet() { + Init(); + } + + enum class PacketFlags { + newConnection = (1 << 0), + resend = (1 << 1), + ping = (1 << 2) + }; + + void Init() { + crc = 0; + currentID = 0; + totalIDs = 0; + flags = 0; + length = 0; + } + + UINT32 CalcCRC() { + crc = CalcCRCVal(); + } + + UINT32 CalcCRCVal() { + + UINT32 val = 0; + + for (int i = 0; i < _countof(data); i++) { + val += data[i]; // crude but will catch the odd off by one error + } + + return val; + } + + bool ValidateCRC() { + return CalcCRCVal() == crc; + } + + void CreatePacket(PacketFlags p) { + flags |= (UINT16)p; + } + + void CalcTotalIDs(int bytes) { + totalIDs = bytes / sizeof(data); + + if (bytes % sizeof(data)) { + totalIDs++; + } + } + + int HeaderSize() { + return 12; + } + + int Size() { + return HeaderSize() + length; + } + + operator char*() { return (char*)this; } + operator const char*() { return (char*)this; } + }; + + typedef char PacketReply; +} + +#endif \ No newline at end of file diff --git a/Src/Network/UDPReceive.cpp b/Src/Network/UDPReceive.cpp new file mode 100644 index 00000000..725550c0 --- /dev/null +++ b/Src/Network/UDPReceive.cpp @@ -0,0 +1,89 @@ +#include +#include +#include "UDPReceive.h" +#include "UDPPacket.h" + +namespace SMUDP +{ + +UDPReceive::UDPReceive() +{ + m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // create the socket + m_readEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + WSAEventSelect(m_socket, m_readEvent, FD_READ); +} + +UDPReceive::~UDPReceive() +{ + closesocket(m_socket); + CloseHandle(m_readEvent); +} + +bool UDPReceive::Bind(UINT16 port) +{ + //=========================== + int err; + SOCKADDR_IN serverInfo = {}; + //=========================== + + serverInfo.sin_family = AF_INET; // address family Internet + serverInfo.sin_port = htons(port); // set server’s port number + serverInfo.sin_addr.s_addr = INADDR_ANY; // set server’s IP + + err = bind(m_socket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr)); + + return (err == 0); +} + +std::vector& UDPReceive::ReadData(int timeout) +{ + m_data.clear(); + + while (true) { + + //======== + DWORD res; + //======== + + res = WaitForSingleObject(m_readEvent, timeout); + + if (res == WAIT_OBJECT_0) { + + //========================= + int result; + int slen; + sockaddr_in si_other; + Packet packet; + //========================= + + slen = sizeof(sockaddr_in); + + result = recvfrom(m_socket, packet, sizeof(packet), 0, (struct sockaddr *) &si_other, &slen); + + if (result == SOCKET_ERROR) { + auto error = WSAGetLastError(); // clear error code + m_data.clear(); + break; + } + + // copy data to array + m_data.insert(m_data.end(), packet.data, packet.data + packet.length); + + PacketReply r = 1; + result = sendto(m_socket, &r, sizeof(r), 0, (struct sockaddr *)&si_other, sizeof(SOCKADDR_IN)); + + if (packet.currentID + 1 == packet.totalIDs) { + break; // reached the end + } + } + else { + m_data.clear(); // reset any memory because we have failed + break; // timeout + } + } + + return m_data; +} + +} diff --git a/Src/Network/UDPReceive.h b/Src/Network/UDPReceive.h new file mode 100644 index 00000000..d4777591 --- /dev/null +++ b/Src/Network/UDPReceive.h @@ -0,0 +1,29 @@ +#ifndef _UDP_RECEIVE_H_ +#define _UDP_RECEIVE_H_ + +#include "UDPPacket.h" +#include "WinSockWrap.h" +#include + +namespace SMUDP +{ + class UDPReceive + { + public: + UDPReceive(); + ~UDPReceive(); + + bool Bind(UINT16 port); + + std::vector& ReadData(int timeout); + + private: + + std::vector m_data; + SOCKET m_socket; + HANDLE m_readEvent; + WinSockWrap m_winSockWrap; + }; +} + +#endif \ No newline at end of file diff --git a/Src/Network/UDPSend.cpp b/Src/Network/UDPSend.cpp new file mode 100644 index 00000000..ed4b3cba --- /dev/null +++ b/Src/Network/UDPSend.cpp @@ -0,0 +1,187 @@ +#include +#include +#include "UDPSend.h" +#include +#include + +#ifdef __GNUC__ +extern "C" { + WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, PCSTR pszAddrString, PVOID pAddrBuf); +} +#endif + +namespace SMUDP +{ + +UDPSend::UDPSend() +{ + m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // create the socket + m_event = CreateEvent(NULL, FALSE, FALSE, NULL); + m_exitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + m_dataReady = CreateEvent(NULL, FALSE, FALSE, NULL); + m_sendComplete = CreateEvent(NULL, FALSE, TRUE, NULL); // start off ready + + m_sendThread = std::thread(&UDPSend::SendThread, this); + + WSAEventSelect(m_socket, m_event, FD_READ | FD_WRITE); +} + +UDPSend::~UDPSend() +{ + SetEvent(m_exitEvent); // trigger thread to exit + m_sendThread.join(); // block until thread has exited + CloseHandle(m_exitEvent); // clean up the rest of the resources + CloseHandle(m_dataReady); + CloseHandle(m_sendComplete); + + closesocket(m_socket); + CloseHandle(m_event); +} + +bool UDPSend::SendAsync(const char* address, int port, int length, const void *data, int timeout) +{ + WaitForSingleObject(m_sendComplete, INFINITE); // block until previous sends have completed, don't want overlapping + + m_data.clear(); + m_data.insert(m_data.end(), (UINT8*)data, (UINT8*)data + length); + + m_address = address; + m_port = port; + m_timeout = timeout; + + SetEvent(m_dataReady); + + return true; +} + +void UDPSend::SendThread() +{ + HANDLE events[2]; + + events[0] = m_exitEvent; + events[1] = m_dataReady; + + while (true) { + + auto result = WaitForMultipleObjects(_countof(events), events, FALSE, INFINITE); + + if (result == WAIT_OBJECT_0) { // exit event triggered + break; + } + else if (result == (WAIT_OBJECT_0 + 1)) { // data ready + Send(m_address.c_str(), m_port, (int)m_data.size(), m_data.data(), m_timeout); + SetEvent(m_sendComplete); + } + } +} + +bool UDPSend::Send(const char* ip, int port, int length, const void *data, int timeout) +{ + UINT8* pData = (UINT8*)data; + + Packet packet; + packet.CalcTotalIDs(length); + + SOCKADDR_IN address = {}; + address.sin_family = AF_INET; // address family Internet + address.sin_port = htons(port); // set server’s port number + inet_pton(AF_INET, ip, &address.sin_addr); + + while (length > 0) { + + packet.flags = 0; // reset the flags (not used currently) + + if (length > packet.BUFFER_SIZE_UDP) { + packet.length = packet.BUFFER_SIZE_UDP; + } + else { + packet.length = length; + } + + memcpy(packet.data, pData, packet.length); + + int sent = SendDataPacket(packet, m_socket, address, m_event); + + if (sent == SOCKET_ERROR) { + return false; // send failure + } + + if (!ProcessReply(m_socket, m_event, timeout)) { + return false; // reply failure + } + + length -= packet.length; + pData += packet.length; + + packet.currentID++; + } + + return true; +} + +bool UDPSend::WaitForEvent(SOCKET s, HANDLE hEvent, long nEvents, int timeout) +{ + //======== + DWORD res; + //======== + + res = WaitForSingleObject(hEvent, timeout); + + if (res == WAIT_OBJECT_0) { + + WSANETWORKEVENTS events = {}; + WSAEnumNetworkEvents(s, hEvent, &events); + + if (events.lNetworkEvents & nEvents) { + return true; + } + } + + return false; +} + +int UDPSend::SendDataPacket(Packet &p, SOCKET s, SOCKADDR_IN& address, HANDLE events) +{ + while (true) { + + int sent = sendto(s, p, p.Size(), 0, (struct sockaddr *)&address, sizeof(SOCKADDR_IN)); + + if (sent == SOCKET_ERROR) { + + int error = WSAGetLastError(); // clear error code + + if (error == WSAEWOULDBLOCK) { + WaitForEvent(s, events, FD_WRITE, INFINITE); // wait until write event is triggered + continue; + } + + return sent; // send failure + } + + return p.Size(); + } +} + +bool UDPSend::ProcessReply(SOCKET s, HANDLE event, int timeout) +{ + //================= + int result; + PacketReply rp; + //================= + + if (WaitForEvent(s, event, FD_READ, timeout)) { + + result = recv(s, &rp, sizeof(rp), 0); + + if (result == SOCKET_ERROR) { + auto error = WSAGetLastError(); // clear error code + return false; + } + + return true; + } + + return false; +} + +} \ No newline at end of file diff --git a/Src/Network/UDPSend.h b/Src/Network/UDPSend.h new file mode 100644 index 00000000..55ff99b7 --- /dev/null +++ b/Src/Network/UDPSend.h @@ -0,0 +1,45 @@ +#ifndef _UDPSEND_H_ +#define _UDPSEND_H_ + +#include "UDPPacket.h" +#include "WinSockWrap.h" +#include +#include +#include + +namespace SMUDP +{ + class UDPSend + { + public: + UDPSend(); + ~UDPSend(); + + bool Send(const char* address, int port, int length, const void *data, int timeout); // blocking + bool SendAsync(const char* address, int port, int length, const void *data, int timeout); // no blocking call + + private: + + bool WaitForEvent(SOCKET s, HANDLE hEvent, long nEvents, int timeout); + bool ProcessReply(SOCKET s, HANDLE event, int timeout); + int SendDataPacket(Packet& p, SOCKET s, SOCKADDR_IN& address, HANDLE events); + + SOCKET m_socket; + HANDLE m_event; + WinSockWrap m_winsockWrap; + + // async + void SendThread(); + + HANDLE m_exitEvent; + HANDLE m_dataReady; + HANDLE m_sendComplete; + int m_port; + int m_timeout; + std::string m_address; + std::vector m_data; + std::thread m_sendThread; + }; +} + +#endif \ No newline at end of file diff --git a/Src/Network/WinSockWrap.cpp b/Src/Network/WinSockWrap.cpp new file mode 100644 index 00000000..5e90a495 --- /dev/null +++ b/Src/Network/WinSockWrap.cpp @@ -0,0 +1,28 @@ +#include "WinSockWrap.h" +#include +#include + +#pragma comment(lib, "ws2_32.lib") + +WinSockWrap::WinSockWrap() +{ + //============== + WSADATA wsaData; + int err; + //============== + + m_count = 0; + + err = WSAStartup(MAKEWORD(2, 2), &wsaData); // don't really need to care for checking supported version, we should be good from win95 onwards + + if (err == 0) { + m_count++; + } +} + +WinSockWrap::~WinSockWrap() +{ + if (m_count) { + WSACleanup(); + } +} \ No newline at end of file diff --git a/Src/Network/WinSockWrap.h b/Src/Network/WinSockWrap.h new file mode 100644 index 00000000..d45498fc --- /dev/null +++ b/Src/Network/WinSockWrap.h @@ -0,0 +1,14 @@ +#ifndef _WINSOCKWRAP_H_ +#define _WINSOCKWRAP_H_ + +class WinSockWrap +{ +public: + WinSockWrap(); + ~WinSockWrap(); + +private: + int m_count; +}; + +#endif \ No newline at end of file diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 1593cd1f..63d94765 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -77,6 +77,7 @@ #ifdef SUPERMODEL_WIN32 #include "DirectInputSystem.h" #include "WinOutputs.h" +#include "NetOutputs.h" #endif #include "Supermodel.h" @@ -1582,6 +1583,9 @@ static Util::Config::Node DefaultConfig() #endif config.Set("Outputs", "none"); config.Set("DumpTextures", false); + config.Set("NetOutputsWithLF", "1"); + config.Set("NetOutputsTCPPort", "8000"); + config.Set("NetOutputsUDPBroadcastPort", "8001"); return config; } @@ -2184,6 +2188,15 @@ int main(int argc, char **argv) Outputs = NULL; else if (outputs == "win") Outputs = new CWinOutputs(); + else if (outputs == "net") + { + CNetOutputs* netOutputs = new CNetOutputs(); + if (s_runtime_config["NetOutputsWithLF"].ValueAs()==1) + netOutputs->FrameEnding = std::string("\r\n"); + netOutputs->TcpPort = s_runtime_config["NetOutputsTCPPort"].ValueAs(); + netOutputs->UdpBroadcastPort = s_runtime_config["NetOutputsUDPBroadcastPort"].ValueAs(); + Outputs = (COutputs*)netOutputs; + } else { ErrorLog("Unknown outputs: %s\n", outputs.c_str()); diff --git a/Src/OSD/Windows/NetOutputs.cpp b/Src/OSD/Windows/NetOutputs.cpp new file mode 100644 index 00000000..c9872c36 --- /dev/null +++ b/Src/OSD/Windows/NetOutputs.cpp @@ -0,0 +1,279 @@ +/*This file is part of Output Blaster. + +Output Blaster is free software : you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Output Blaster is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Output Blaster.If not, see < https://www.gnu.org/licenses/>.*/ + +#include +#include +#include +#include +#include +#include "NetOutputs.h" + + +CNetOutputs::CNetOutputs() +{ +} + +CNetOutputs::~CNetOutputs() +{ + if (ServerSocket) { + Running = false; + closesocket(ServerSocket); + Sleep(16); + CloseHandle(ThreadHandle); + WSACleanup(); + } +} + +bool CNetOutputs::Initialize() +{ + // Create TCP server + if (CreateTcpServerSocket() != 0) { + MessageBoxA(NULL, "Unable to start server thread for tcp outputs", NULL, NULL); + return false; + } + + + // Run tcp server thread + if (CreateServerThread() != 0) { + MessageBoxA(NULL, "Unable to start server thread for tcp outputs", NULL, NULL); + return false; + } + return true; +} + +void CNetOutputs::Attached() +{ + // Broadcast a startup message + if (SendUdpBroadcastWithId() != 0) { + MessageBoxA(NULL, "Unable to notify game with udp broadcast", NULL, NULL); + } +} + +void CNetOutputs::SendOutput(EOutputs output, UINT8 prevValue, UINT8 value) +{ + // Loop through all registered clients and send them new output value + LPARAM param = (LPARAM)output + 1; + for (vector::iterator it = m_clients.begin(), end = m_clients.end(); it != end; it++) { + auto csock = it->ClientSocket; + + std::string name = GetOutputName(output); + std::string strvalue = std::to_string(value); + std::string line = name + SeparatorIdAndValue + strvalue + FrameEnding; + send(csock, line.c_str(), line.length(), 0); + } + + // Send a broadcast every 10s to show we are here? + // Will do SendUdpBroadcastWithId(); +} + +LRESULT CNetOutputs::CreateServerThread() +{ + if (s_createdClass) + return 0; + + // Setup description of window class + // Allocate memory for thread data. + Running = true; + ThreadHandle = CreateThread( + NULL, // default security attributes + 0, // use default stack size + ThreadEntry, // thread function name + this, // argument to thread function + 0, // use default creation flags + &ThreadId); // returns the thread identifier + if (ThreadHandle == NULL) + return 1; + s_createdClass = true; + return 0; +} + + +DWORD CNetOutputs::TcpServerThread() +{ + SOCKADDR_IN csin; + while (Running) { + SOCKET csock; + int sinsize = sizeof(csin); + if ((csock = accept(ServerSocket, (SOCKADDR*)&csin, &sinsize)) != INVALID_SOCKET) { + RegisterClient(csock); + } + Sleep(16); + } + return 0; +} + +LRESULT CNetOutputs::CreateTcpServerSocket() +{ + /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ + WORD wVersionRequested = MAKEWORD(2, 2); + int err = WSAStartup(wVersionRequested, &this->WSAData); + if (err != 0) { + /* Tell the user that we could not find a usable */ + /* Winsock DLL. */ + printf("WSAStartup failed with error: %d\n", err); + perror("WASStartup"); + return err; + } + ServerSocket = socket(AF_INET, SOCK_STREAM, 0); + server_sin.sin_addr.s_addr = INADDR_ANY; + server_sin.sin_family = AF_INET; + server_sin.sin_port = htons(TcpPort); + if ((err = bind(ServerSocket, (SOCKADDR*)&server_sin, sizeof(server_sin))) != 0) { + perror("bind"); + closesocket(ServerSocket); + return err; + } + if ((err = listen(ServerSocket, 0)) != 0) { + perror("listen"); + closesocket(ServerSocket); + return err; + } + return 0; +} +LRESULT CNetOutputs::SendUdpBroadcastWithId() +{ + SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + char broadcast = '1'; + int err; + if ((err = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast))) != 0) { + perror("broadcast options"); + closesocket(sock); + return err; + } + + /* + // UDP server + SOCKADDR_IN Recv_addr; + Recv_addr.sin_family = AF_INET; + Recv_addr.sin_port = htons(UdpBroadcastPort); + Recv_addr.sin_addr.s_addr = INADDR_ANY; + if (bind(sock, (sockaddr*)&Recv_addr, sizeof(Recv_addr)) < 0) { + perror("bind"); + closesocket(sock); + return 2; + }*/ + + SOCKADDR_IN Sender_addr; + Sender_addr.sin_family = AF_INET; + Sender_addr.sin_port = htons(UdpBroadcastPort); + Sender_addr.sin_addr.s_addr = inet_addr("255.255.255.255"); + + std::string lines = "mame_start" + SeparatorIdAndValue + GetGame().name + FrameEnding + "tcp" + SeparatorIdAndValue + std::to_string(TcpPort) + FrameEnding; + if (sendto(sock, lines.c_str(), lines.length(), 0, (sockaddr*)&Sender_addr, sizeof(Sender_addr)) != lines.length()) { + perror("broadcast send"); + closesocket(sock); + return 1; + } + + closesocket(sock); + return 0; +} + + + +bool CNetOutputs::AllocateMessageId(UINT& regId, LPCSTR str) +{ + USES_CONVERSION; + LPCSTR a = str; + LPCWSTR w = A2W(a); + std::wcout << w << std::endl; + return false; +} + + + +LRESULT CNetOutputs::RegisterClient(SOCKET socket) +{ + // Check that given client is not already registered + for (vector::iterator it = m_clients.begin(), end = m_clients.end(); it != end; it++) { + if (it->ClientSocket = socket) { + // If so, just send it current state of all outputs + SendAllToClient(*it); + return 1; + } + } + + // If not, store details about client and send it current state of all outputs + RegisteredClientTcp client; + client.ClientSocket = socket; + m_clients.push_back(client); + SendGameIdString(client); + SendAllToClient(client); + + return 0; +} + +void CNetOutputs::SendAllToClient(RegisteredClientTcp& client) +{ + // Loop through all known outputs and send their current state to given client + for (unsigned i = 0; i < NUM_OUTPUTS; i++) { + EOutputs output = (EOutputs)i; + if (HasValue(output)) { + std::string name = GetOutputName(output); + std::string strvalue = std::to_string(GetValue(output)); + std::string line = name + SeparatorIdAndValue + strvalue + FrameEnding; + send(client.ClientSocket, line.c_str(), line.length(), 0); + } + } +} + +LRESULT CNetOutputs::UnregisterClient(SOCKET socket) +{ + // Find any matching clients and remove them + bool found = false; + vector::iterator it = m_clients.begin(); + while (it != m_clients.end()) { + if (it->ClientSocket == socket) { + SendStopString(*it, true); + it = m_clients.erase(it); + found = true; + closesocket(it->ClientSocket); + } else { + it++; + } + } + + // Return error if no matches found + return (found ? 0 : 1); + return 0; +} + +LRESULT CNetOutputs::SendGameIdString(RegisteredClientTcp& client) +{ + // Id 0 is the name of the game + std::string line = "mame_start" + SeparatorIdAndValue + GetGame().name + FrameEnding; + return send(client.ClientSocket, line.c_str(), line.length(), 0); +} +LRESULT CNetOutputs::SendStopString(RegisteredClientTcp& client, bool stopped) +{ + // Id 0 is the name of the game + std::string line = "mame_stop" + SeparatorIdAndValue + std::string((stopped ? "1" : "0")) + FrameEnding; + return send(client.ClientSocket, line.c_str(), line.length(), 0); +} +LRESULT CNetOutputs::SendPauseString(RegisteredClientTcp& client, bool paused) +{ + // Id 0 is the name of the game + std::string line = "pause" + SeparatorIdAndValue + std::string((paused ? "1" : "0")) + FrameEnding; + return send(client.ClientSocket, line.c_str(), line.length(), 0); +} +LRESULT CNetOutputs::SendValueIdString(RegisteredClientTcp& client, EOutputs output) +{ + // Id 0 is the name of the game + std::string name = GetOutputName(output); + + std::string line = "id:" + name + FrameEnding; + return send(client.ClientSocket, line.c_str(), line.length(), 0); +} + diff --git a/Src/OSD/Windows/NetOutputs.h b/Src/OSD/Windows/NetOutputs.h new file mode 100644 index 00000000..c277ad0d --- /dev/null +++ b/Src/OSD/Windows/NetOutputs.h @@ -0,0 +1,164 @@ +/*This file is part of Output Blaster. + +Output Blaster is free software : you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Output Blaster is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Output Blaster.If not, see < https://www.gnu.org/licenses/>.*/ + + /* + * WinOutputs.h + * + * Implementation of COutputs that sends MAMEHooker compatible messages via Windows messages. + */ + +#pragma once + +#define WIN32_LEAN_AND_MEAN + +#include +#include "Outputs.h" +#include + +#include +#pragma comment(lib, "ws2_32.lib") + +using namespace std; + +// Struct that represents a client (eg MAMEHooker) currently registered with the emulator +struct RegisteredClientTcp +{ + SOCKET ClientSocket; // Client socket +}; + + +class CNetOutputs : public COutputs +{ +public: + int TcpPort = 8000; + int UdpBroadcastPort = 8001; + std::string SeparatorIdAndValue = std::string(" = "); + std::string FrameEnding = std::string("\r"); // Can also be "\r\n" + + /* + * CWinOutputs(): + * ~CWinOutputs(): + * + * Constructor and destructor. + */ + CNetOutputs(); + + virtual ~CNetOutputs(); + + /* + * Initialize(): + * + * Initializes this class. + */ + bool Initialize(); + + /* + * Attached(): + * + * Lets the class know that it has been attached to the emulator. + */ + void Attached(); +protected: + /* + * SendOutput(): + * + * Sends the appropriate output message to all registered clients. + */ + void SendOutput(EOutputs output, UINT8 prevValue, UINT8 value); + +private: + bool s_createdClass = false; + + /* + * CreateServerThread(): + * + * Registers the server and wait for new client + */ + LRESULT CreateServerThread(); + + static DWORD WINAPI ThreadEntry(LPVOID pUserData) { + return ((CNetOutputs*)pUserData)->TcpServerThread(); + } + + HANDLE ThreadHandle; + DWORD ThreadId; + bool Running = false; + + SOCKET ServerSocket; + WSADATA WSAData; + SOCKADDR_IN server_sin; + vector m_clients; + + DWORD TcpServerThread(); + LRESULT CreateTcpServerSocket(); + LRESULT SendUdpBroadcastWithId(); + + /* + * AllocateMessageId(regId, str): + * + * Defines a new window message type with the given name and returns the allocated message id. + */ + bool AllocateMessageId(UINT ®Id, LPCSTR str); + + /* + * RegisterClient(hwnd, id): + * + * Registers a client (eg MAMEHooker) with the emulator. + */ + LRESULT RegisterClient(SOCKET hwnd); + + /* + * SendAllToClient(client): + * + * Sends the current state of all the outputs to the given registered client. + * Called whenever a client is registered with the emulator. + */ + void SendAllToClient(RegisteredClientTcp& client); + + /* + * UnregisterClient(hwnd, id): + * + * Unregisters a client from the emulator. + */ + LRESULT UnregisterClient(SOCKET hwnd); + + /* + * SendGameIdString(hwnd, id): + * + * Sends the name of the current running game. + */ + LRESULT SendGameIdString(RegisteredClientTcp& client); + /* + * SendStopString(hwnd, id): + * + * Sends the mame stops message + */ + LRESULT SendStopString(RegisteredClientTcp& client, bool stopped); + /* + * SendPauseString(hwnd, id): + * + * Sends the name of the current running game. + */ + LRESULT SendPauseString(RegisteredClientTcp& client, bool paused); + + /* + * SendValueIdString(hwnd, id): + * + * Sends the name of the requested output back to a client, or the name of the current running game if an id of zero is requested. + */ + LRESULT SendValueIdString(RegisteredClientTcp& client, EOutputs output); + +}; + diff --git a/VS2008/Supermodel.vcxproj b/VS2008/Supermodel.vcxproj index edd2575a..1186ba2a 100644 --- a/VS2008/Supermodel.vcxproj +++ b/VS2008/Supermodel.vcxproj @@ -359,6 +359,10 @@ xcopy /D /Y "$(ProjectDir)..\Assets\*" "$(TargetDir)Assets" + + + + @@ -367,6 +371,7 @@ xcopy /D /Y "$(ProjectDir)..\Assets\*" "$(TargetDir)Assets" + @@ -539,6 +544,10 @@ xcopy /D /Y "$(ProjectDir)..\Assets\*" "$(TargetDir)Assets" + + + + @@ -549,6 +558,7 @@ xcopy /D /Y "$(ProjectDir)..\Assets\*" "$(TargetDir)Assets" +