mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-21 18:22:33 +01:00
Implement PINE IPC Server
This commit is contained in:
parent
a6237e5473
commit
4262794668
622
3rdparty/pine/pine_server.h
vendored
Normal file
622
3rdparty/pine/pine_server.h
vendored
Normal file
@ -0,0 +1,622 @@
|
||||
// Based on https://github.com/PCSX2/pcsx2/blob/edeb0d7bd7258c58273cc4a88a9f9a823d71e48c/pcsx2/IPC.h
|
||||
// and https://github.com/PCSX2/pcsx2/blob/edeb0d7bd7258c58273cc4a88a9f9a823d71e48c/pcsx2/IPC.cpp
|
||||
// Relicensed as GPLv2 for use in RPCS3 with permission from copyright owner (Govanify).
|
||||
|
||||
#pragma once
|
||||
|
||||
//#include "Utilities/Thread.h"
|
||||
#include <string>
|
||||
#include "stdafx.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <thread>
|
||||
#include <sys/types.h>
|
||||
#if _WIN32
|
||||
#define read_portable(a, b, c) (recv(a, b, c, 0))
|
||||
#define write_portable(a, b, c) (send(a, b, c, 0))
|
||||
#define close_portable(a) (closesocket(a))
|
||||
#define bzero(b, len) (memset((b), '\0', (len)), (void)0)
|
||||
#include <WinSock2.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#define read_portable(a, b, c) (read(a, b, c))
|
||||
#define write_portable(a, b, c) (write(a, b, c))
|
||||
#define close_portable(a) (close(a))
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace pine
|
||||
{
|
||||
/**
|
||||
* Emulator status enum. @n
|
||||
* A list of possible emulator statuses. @n
|
||||
*/
|
||||
enum EmuStatus : uint32_t
|
||||
{
|
||||
Running = 0, /**< Game is running */
|
||||
Paused = 1, /**< Game is paused */
|
||||
Shutdown = 2, /**< Game is shutdown */
|
||||
};
|
||||
|
||||
typedef unsigned long long SOCKET;
|
||||
|
||||
template<typename Impl>
|
||||
class pine_server : public Impl
|
||||
{
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
// windows claim to have support for AF_UNIX sockets but that is a blatant lie,
|
||||
// their SDK won't even run their own examples, so we go on TCP sockets.
|
||||
SOCKET m_sock;
|
||||
// the message socket used in thread's accept().
|
||||
SOCKET m_msgsock;
|
||||
#else
|
||||
// absolute path of the socket. Stored in XDG_RUNTIME_DIR, if unset /tmp
|
||||
std::string m_socket_name;
|
||||
int m_sock = 0;
|
||||
// the message socket used in thread's accept().
|
||||
int m_msgsock = 0;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Maximum memory used by an IPC message request.
|
||||
* Equivalent to 50,000 Write64 requests.
|
||||
*/
|
||||
#define MAX_IPC_SIZE 650000
|
||||
|
||||
/**
|
||||
* Maximum memory used by an IPC message reply.
|
||||
* Equivalent to 50,000 Read64 replies.
|
||||
*/
|
||||
#define MAX_IPC_RETURN_SIZE 450000
|
||||
|
||||
/**
|
||||
* IPC return buffer.
|
||||
* A preallocated buffer used to store all IPC replies.
|
||||
* to the size of 50.000 MsgWrite64 IPC calls.
|
||||
*/
|
||||
char* m_ret_buffer;
|
||||
|
||||
/**
|
||||
* IPC messages buffer.
|
||||
* A preallocated buffer used to store all IPC messages.
|
||||
*/
|
||||
char* m_ipc_buffer;
|
||||
|
||||
/**
|
||||
* IPC Command messages opcodes.
|
||||
* A list of possible operations possible by the IPC.
|
||||
* Each one of them is what we call an "opcode" and is the first
|
||||
* byte sent by the IPC to differentiate between commands.
|
||||
*/
|
||||
enum IPCCommand : unsigned char
|
||||
{
|
||||
MsgRead8 = 0, /**< Read 8 bit value to memory. */
|
||||
MsgRead16 = 1, /**< Read 16 bit value to memory. */
|
||||
MsgRead32 = 2, /**< Read 32 bit value to memory. */
|
||||
MsgRead64 = 3, /**< Read 64 bit value to memory. */
|
||||
MsgWrite8 = 4, /**< Write 8 bit value to memory. */
|
||||
MsgWrite16 = 5, /**< Write 16 bit value to memory. */
|
||||
MsgWrite32 = 6, /**< Write 32 bit value to memory. */
|
||||
MsgWrite64 = 7, /**< Write 64 bit value to memory. */
|
||||
MsgVersion = 8, /**< Returns RPCS3 version. */
|
||||
MsgTitle = 0xB, /**< Returns the game title. */
|
||||
MsgID = 0xC, /**< Returns the game ID. */
|
||||
MsgUUID = 0xD, /**< Returns the game UUID. */
|
||||
MsgGameVersion = 0xE, /**< Returns the game verion. */
|
||||
MsgStatus = 0xF, /**< Returns the emulator status. */
|
||||
MsgUnimplemented = 0xFF /**< Unimplemented IPC message. */
|
||||
};
|
||||
|
||||
/**
|
||||
* IPC message buffer.
|
||||
* A list of all needed fields to store an IPC message.
|
||||
*/
|
||||
struct IPCBuffer
|
||||
{
|
||||
int size; /**< Size of the buffer. */
|
||||
char* buffer; /**< Buffer. */
|
||||
};
|
||||
|
||||
/**
|
||||
* IPC result codes.
|
||||
* A list of possible result codes the IPC can send back.
|
||||
* Each one of them is what we call an "opcode" or "tag" and is the
|
||||
* first byte sent by the IPC to differentiate between results.
|
||||
*/
|
||||
enum IPCResult : unsigned char
|
||||
{
|
||||
IPC_OK = 0, /**< IPC command successfully completed. */
|
||||
IPC_FAIL = 0xFF /**< IPC command failed to complete. */
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal function, Parses an IPC command.
|
||||
* buf: buffer containing the IPC command.
|
||||
* buf_size: size of the buffer announced.
|
||||
* ret_buffer: buffer that will be used to send the reply.
|
||||
* return value: IPCBuffer containing a buffer with the result
|
||||
* of the command and its size.
|
||||
*/
|
||||
IPCBuffer ParseCommand(char* buf, char* ret_buffer, u32 buf_size)
|
||||
{
|
||||
u32 ret_cnt = 5;
|
||||
u32 buf_cnt = 0;
|
||||
|
||||
while (buf_cnt < buf_size)
|
||||
{
|
||||
if (!SafetyChecks(buf_cnt, 1, ret_cnt, 0, buf_size))
|
||||
return IPCBuffer{ 5, MakeFailIPC(ret_buffer) };
|
||||
buf_cnt++;
|
||||
// example IPC messages: MsgRead/Write
|
||||
// refer to the client doc for more info on the format
|
||||
// IPC Message event (1 byte)
|
||||
// | Memory address (4 byte)
|
||||
// | | argument (VLE)
|
||||
// | | |
|
||||
// format: XX YY YY YY YY ZZ ZZ ZZ ZZ
|
||||
// reply code: 00 = OK, FF = NOT OK
|
||||
// | return value (VLE)
|
||||
// | |
|
||||
// reply: XX ZZ ZZ ZZ ZZ
|
||||
IPCCommand command = std::bit_cast<IPCCommand>(buf[buf_cnt - 1]);
|
||||
switch (command)
|
||||
{
|
||||
case MsgRead8:
|
||||
{
|
||||
if (!SafetyChecks(buf_cnt, 4, ret_cnt, 1, buf_size))
|
||||
goto error;
|
||||
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
|
||||
if (!Impl::template check_addr(a))
|
||||
goto error;
|
||||
const u8 res = Impl::read8(a);
|
||||
ToArray(ret_buffer, res, ret_cnt);
|
||||
ret_cnt += 1;
|
||||
buf_cnt += 4;
|
||||
break;
|
||||
}
|
||||
case MsgRead16:
|
||||
{
|
||||
if (!SafetyChecks(buf_cnt, 4, ret_cnt, 2, buf_size))
|
||||
goto error;
|
||||
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
|
||||
if (!Impl::template check_addr<2>(a))
|
||||
goto error;
|
||||
const u16 res = Impl::read16(a);
|
||||
ToArray(ret_buffer, res, ret_cnt);
|
||||
ret_cnt += 2;
|
||||
buf_cnt += 4;
|
||||
break;
|
||||
}
|
||||
case MsgRead32:
|
||||
{
|
||||
if (!SafetyChecks(buf_cnt, 4, ret_cnt, 4, buf_size))
|
||||
goto error;
|
||||
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
|
||||
if (!Impl::template check_addr<4>(a))
|
||||
goto error;
|
||||
const u32 res = Impl::read32(a);
|
||||
ToArray(ret_buffer, res, ret_cnt);
|
||||
ret_cnt += 4;
|
||||
buf_cnt += 4;
|
||||
break;
|
||||
}
|
||||
case MsgRead64:
|
||||
{
|
||||
if (!SafetyChecks(buf_cnt, 4, ret_cnt, 8, buf_size))
|
||||
goto error;
|
||||
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
|
||||
if (!Impl::template check_addr<8>(a))
|
||||
goto error;
|
||||
u64 res = Impl::read64(a);
|
||||
ToArray(ret_buffer, res, ret_cnt);
|
||||
ret_cnt += 8;
|
||||
buf_cnt += 4;
|
||||
break;
|
||||
}
|
||||
case MsgWrite8:
|
||||
{
|
||||
if (!SafetyChecks(buf_cnt, 1 + 4, ret_cnt, 0, buf_size))
|
||||
goto error;
|
||||
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
|
||||
if (!Impl::template check_addr(a, vm::page_writable))
|
||||
goto error;
|
||||
Impl::write8(a, FromArray<u8>(&buf[buf_cnt], 4));
|
||||
buf_cnt += 5;
|
||||
break;
|
||||
}
|
||||
case MsgWrite16:
|
||||
{
|
||||
if (!SafetyChecks(buf_cnt, 2 + 4, ret_cnt, 0, buf_size))
|
||||
goto error;
|
||||
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
|
||||
if (!Impl::template check_addr<2>(a, vm::page_writable))
|
||||
goto error;
|
||||
Impl::write16(a, FromArray<u16>(&buf[buf_cnt], 4));
|
||||
buf_cnt += 6;
|
||||
break;
|
||||
}
|
||||
case MsgWrite32:
|
||||
{
|
||||
if (!SafetyChecks(buf_cnt, 4 + 4, ret_cnt, 0, buf_size))
|
||||
goto error;
|
||||
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
|
||||
if (!Impl::template check_addr<4>(a, vm::page_writable))
|
||||
goto error;
|
||||
Impl::write32(a, FromArray<u32>(&buf[buf_cnt], 4));
|
||||
buf_cnt += 8;
|
||||
break;
|
||||
}
|
||||
case MsgWrite64:
|
||||
{
|
||||
if (!SafetyChecks(buf_cnt, 8 + 4, ret_cnt, 0, buf_size))
|
||||
goto error;
|
||||
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
|
||||
if (!Impl::template check_addr<8>(a, vm::page_writable))
|
||||
goto error;
|
||||
Impl::write64(a, FromArray<u64>(&buf[buf_cnt], 4));
|
||||
buf_cnt += 12;
|
||||
break;
|
||||
}
|
||||
case MsgVersion:
|
||||
{
|
||||
char version[256] = {};
|
||||
sprintf(version, "RPCS3 %s", Impl::get_version_and_branch().c_str());
|
||||
const u32 size = strlen(version) + 1;
|
||||
version[size] = 0x00;
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
ToArray(ret_buffer, size, ret_cnt);
|
||||
ret_cnt += 4;
|
||||
memcpy(&ret_buffer[ret_cnt], version, size);
|
||||
ret_cnt += size;
|
||||
break;
|
||||
}
|
||||
case MsgStatus:
|
||||
{
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, 4, buf_size))
|
||||
goto error;
|
||||
EmuStatus status = Impl::get_status();
|
||||
ToArray(ret_buffer, status, ret_cnt);
|
||||
ret_cnt += 4;
|
||||
buf_cnt += 4;
|
||||
break;
|
||||
}
|
||||
case MsgTitle:
|
||||
{
|
||||
const auto title_string = Impl::get_title();
|
||||
const auto size = title_string.size() + 1;
|
||||
char* title = new char[size];
|
||||
sprintf(title, "%s", title_string.c_str());
|
||||
title[size] = 0x00;
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
ToArray(ret_buffer, size, ret_cnt);
|
||||
ret_cnt += 4;
|
||||
memcpy(&ret_buffer[ret_cnt], title, size);
|
||||
ret_cnt += size;
|
||||
delete[] title;
|
||||
break;
|
||||
}
|
||||
case MsgID:
|
||||
{
|
||||
const auto title_id_string = Impl::get_title_ID();
|
||||
const auto size = title_id_string.size() + 1;
|
||||
char* title_id = new char[size];
|
||||
sprintf(title_id, "%s", title_id_string.c_str());
|
||||
title_id[size] = 0x00;
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
ToArray(ret_buffer, size, ret_cnt);
|
||||
ret_cnt += 4;
|
||||
memcpy(&ret_buffer[ret_cnt], title_id, size);
|
||||
ret_cnt += size;
|
||||
delete[] title_id;
|
||||
break;
|
||||
}
|
||||
case MsgUUID:
|
||||
{
|
||||
const auto hash_string = Impl::get_executable_hash();
|
||||
const auto size = hash_string.size() + 1;
|
||||
char* hash = new char[size];
|
||||
sprintf(hash, "%s", hash_string.c_str());
|
||||
hash[size] = 0x00;
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
ToArray(ret_buffer, size, ret_cnt);
|
||||
ret_cnt += 4;
|
||||
memcpy(&ret_buffer[ret_cnt], hash, size);
|
||||
ret_cnt += size;
|
||||
delete[] hash;
|
||||
break;
|
||||
}
|
||||
case MsgGameVersion:
|
||||
{
|
||||
const auto game_version_string = Impl::get_app_version();
|
||||
const auto size = game_version_string.size() + 1;
|
||||
char* game_version = new char[size];
|
||||
sprintf(game_version, "%s", game_version_string.c_str());
|
||||
game_version[size] = 0x00;
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size, buf_size))
|
||||
goto error;
|
||||
ToArray(ret_buffer, size, ret_cnt);
|
||||
ret_cnt += 4;
|
||||
memcpy(&ret_buffer[ret_cnt], game_version, size);
|
||||
ret_cnt += size;
|
||||
delete[] game_version;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
error:
|
||||
return IPCBuffer{ 5, MakeFailIPC(ret_buffer) };
|
||||
}
|
||||
}
|
||||
}
|
||||
return IPCBuffer{ static_cast<int>(ret_cnt), MakeOkIPC(ret_buffer, ret_cnt) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an IPC buffer
|
||||
* ret_buffer: return buffer to use.
|
||||
* size: size of the IPC buffer.
|
||||
* return value: buffer containing the status code allocated of size
|
||||
*/
|
||||
static inline char* MakeOkIPC(char* ret_buffer, uint32_t size = 5)
|
||||
{
|
||||
ToArray<uint32_t>(ret_buffer, size, 0);
|
||||
ret_buffer[4] = IPC_OK;
|
||||
return ret_buffer;
|
||||
}
|
||||
|
||||
static inline char* MakeFailIPC(char* ret_buffer, uint32_t size = 5)
|
||||
{
|
||||
ToArray<uint32_t>(ret_buffer, size, 0);
|
||||
ret_buffer[4] = IPC_FAIL;
|
||||
return ret_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an open socket for IPC communication.
|
||||
* return value: -1 if a fatal failure happened, 0 otherwise.
|
||||
*/
|
||||
int StartSocket()
|
||||
{
|
||||
m_msgsock = accept(m_sock, 0, 0);
|
||||
|
||||
if (m_msgsock == -1)
|
||||
{
|
||||
// everything else is non recoverable in our scope
|
||||
// we also mark as recoverable socket errors where it would block a
|
||||
// non blocking socket, even though our socket is blocking, in case
|
||||
// we ever have to implement a non blocking socket.
|
||||
#ifdef _WIN32
|
||||
int errno_w = WSAGetLastError();
|
||||
if (!(errno_w == WSAECONNRESET || errno_w == WSAEINTR || errno_w == WSAEINPROGRESS || errno_w == WSAEMFILE || errno_w == WSAEWOULDBLOCK))
|
||||
{
|
||||
#else
|
||||
if (!(errno == ECONNABORTED || errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
{
|
||||
#endif
|
||||
Impl::error("IPC: An unrecoverable error happened! Shutting down...");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Thread used to relay IPC commands.
|
||||
void operator()()
|
||||
{
|
||||
// we allocate once buffers to not have to do mallocs for each IPC
|
||||
// request, as malloc is expansive when we optimize for µs.
|
||||
m_ret_buffer = new char[MAX_IPC_RETURN_SIZE];
|
||||
m_ipc_buffer = new char[MAX_IPC_SIZE];
|
||||
|
||||
if (StartSocket() < 0)
|
||||
return;
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
// either int or ssize_t depending on the platform, so we have to
|
||||
// use a bunch of auto
|
||||
auto receive_length = 0;
|
||||
auto end_length = 4;
|
||||
|
||||
// while we haven't received the entire packet, maybe due to
|
||||
// socket datagram splittage, we continue to read
|
||||
while (receive_length < end_length)
|
||||
{
|
||||
auto tmp_length = read_portable(m_msgsock, &m_ipc_buffer[receive_length], MAX_IPC_SIZE - receive_length);
|
||||
|
||||
// we recreate the socket if an error happens
|
||||
if (tmp_length <= 0)
|
||||
{
|
||||
receive_length = 0;
|
||||
if (StartSocket() < 0)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
receive_length += tmp_length;
|
||||
|
||||
// if we got at least the final size then update
|
||||
if (end_length == 4 && receive_length >= 4)
|
||||
{
|
||||
end_length = FromArray<u32>(m_ipc_buffer, 0);
|
||||
// we'd like to avoid a client trying to do OOB
|
||||
if (end_length > MAX_IPC_SIZE || end_length < 4)
|
||||
{
|
||||
receive_length = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we remove 4 bytes to get the message size out of the IPC command
|
||||
// size in ParseCommand.
|
||||
// also, if we got a failed command, let's reset the state so we don't
|
||||
// end up deadlocking by getting out of sync, eg when a client
|
||||
// disconnects
|
||||
if (receive_length != 0)
|
||||
{
|
||||
pine_server::IPCBuffer res = ParseCommand(&m_ipc_buffer[4], m_ret_buffer, static_cast<u32>(end_length) - 4);
|
||||
|
||||
// if we cannot send back our answer restart the socket
|
||||
if (write_portable(m_msgsock, res.buffer, res.size) < 0)
|
||||
{
|
||||
if (StartSocket() < 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an uint to an char* in little endian
|
||||
* res_array: the array to modify
|
||||
* res: the value to convert
|
||||
* i: when to insert it into the array
|
||||
* return value: res_array
|
||||
* NB: implicitely inlined
|
||||
*/
|
||||
template <typename T>
|
||||
static char* ToArray(char* res_array, T res, int i)
|
||||
{
|
||||
memcpy((res_array + i), reinterpret_cast<char*>(&res), sizeof(T));
|
||||
return res_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a char* to an uint in little endian
|
||||
* arr: the array to convert
|
||||
* i: when to load it from the array
|
||||
* return value: the converted value
|
||||
* NB: implicitely inlined
|
||||
*/
|
||||
template <typename T>
|
||||
static T FromArray(char* arr, int i)
|
||||
{
|
||||
return *reinterpret_cast<T*>(arr + i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures an IPC message isn't too big.
|
||||
* return value: false if checks failed, true otherwise.
|
||||
*/
|
||||
static inline bool SafetyChecks(u32 command_len, int command_size, u32 reply_len, int reply_size = 0, u32 buf_size = MAX_IPC_SIZE - 1)
|
||||
{
|
||||
bool res = ((command_len + command_size) > buf_size ||
|
||||
(reply_len + reply_size) >= MAX_IPC_RETURN_SIZE);
|
||||
if (res) [[unlikely]]
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
/* Initializers */
|
||||
pine_server() noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsa;
|
||||
struct sockaddr_in server;
|
||||
m_sock = INVALID_SOCKET;
|
||||
m_msgsock = INVALID_SOCKET;
|
||||
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
|
||||
{
|
||||
Impl::error("IPC: Cannot initialize winsock! Shutting down...");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
|
||||
{
|
||||
Impl::error("IPC: Cannot open socket! Shutting down...");
|
||||
return;
|
||||
}
|
||||
|
||||
// yes very good windows s/sun/sin/g sure is fine
|
||||
server.sin_family = AF_INET;
|
||||
// localhost only
|
||||
server.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
server.sin_port = htons(Impl::get_port());
|
||||
|
||||
if (bind(m_sock, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
|
||||
{
|
||||
Impl::error("IPC: Error while binding to socket! Shutting down...");
|
||||
return;
|
||||
}
|
||||
|
||||
#else
|
||||
char* runtime_dir = nullptr;
|
||||
#ifdef __APPLE__
|
||||
runtime_dir = std::getenv("TMPDIR");
|
||||
#else
|
||||
runtime_dir = std::getenv("XDG_RUNTIME_DIR");
|
||||
#endif
|
||||
// fallback in case macOS or other OSes don't implement the XDG base
|
||||
// spec
|
||||
if (runtime_dir == nullptr)
|
||||
m_socket_name = "/tmp/rpcs3.sock";
|
||||
else
|
||||
{
|
||||
m_socket_name = runtime_dir;
|
||||
m_socket_name += "/rpcs3.sock";
|
||||
}
|
||||
|
||||
m_socket_name = fmt::format("%s.%d", m_socket_name, Impl::get_port());
|
||||
|
||||
struct sockaddr_un server;
|
||||
|
||||
m_sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (m_sock < 0)
|
||||
{
|
||||
Impl::error("IPC: Cannot open socket! Shutting down...");
|
||||
return;
|
||||
}
|
||||
server.sun_family = AF_UNIX;
|
||||
strcpy(server.sun_path, m_socket_name.c_str());
|
||||
|
||||
// we unlink the socket so that when releasing this thread the socket gets
|
||||
// freed even if we didn't close correctly the loop
|
||||
unlink(m_socket_name.c_str());
|
||||
if (bind(m_sock, std::bit_cast<struct sockaddr*>(&server), sizeof(struct sockaddr_un)))
|
||||
{
|
||||
Impl::error("IPC: Error while binding to socket! Shutting down...");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// maximum queue of 4096 commands before refusing, approximated to the
|
||||
// nearest legal value. We do not use SOMAXCONN as windows have this idea
|
||||
// that a "reasonable" value is 5, which is not.
|
||||
listen(m_sock, 4096);
|
||||
}
|
||||
|
||||
pine_server(const pine_server&) = delete;
|
||||
pine_server& operator=(const pine_server&) = delete;
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#else
|
||||
unlink(m_socket_name.c_str());
|
||||
#endif
|
||||
close_portable(m_sock);
|
||||
close_portable(m_msgsock);
|
||||
}
|
||||
|
||||
~pine_server()
|
||||
{
|
||||
Cleanup();
|
||||
delete[] m_ret_buffer;
|
||||
delete[] m_ipc_buffer;
|
||||
}
|
||||
|
||||
}; // class pine_server
|
||||
}
|
@ -85,6 +85,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcubeb", "3rdparty\cubeb\
|
||||
EndProject
|
||||
Project("{508c291a-3d18-49f5-b25d-f7c8db92cb21}") = "soundtouch", "3rdparty\SoundTouch\soundtouch.vcxproj", "{508c291a-3d18-49f5-b25d-f7c8db92cb21}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pine", "pine", "{A55DA1B5-CC17-4525-BE7F-1659CE17BB56}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
3rdparty\pine\pine_server.h = 3rdparty\pine\pine_server.h
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
@ -202,6 +207,7 @@ Global
|
||||
{9610627D-20FE-4B07-8CE3-9FF68A5F1EC2} = {10FBF193-D532-4CCF-B875-4C7091A7F6C2}
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
{508c291a-3d18-49f5-b25d-f7c8db92cb21} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
{A55DA1B5-CC17-4525-BE7F-1659CE17BB56} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {06CC7920-E085-4B81-9582-8DE8AAD42510}
|
||||
|
@ -13,6 +13,8 @@ add_library(rpcs3_emu
|
||||
title.cpp
|
||||
perf_meter.cpp
|
||||
perf_monitor.cpp
|
||||
IPC_config.cpp
|
||||
IPC_socket.cpp
|
||||
)
|
||||
|
||||
# prevent WolfSSL from warning about not having harden options
|
||||
|
@ -1455,6 +1455,8 @@ bool ppu_load_exec(const ppu_exec_object& elf)
|
||||
hash[5 + i * 2] = pal[_main.sha1[i] & 15];
|
||||
}
|
||||
|
||||
Emu.SetExecutableHash(hash);
|
||||
|
||||
// Apply the patch
|
||||
auto applied = g_fxo->get<patch_engine>().apply(hash, vm::g_base_addr);
|
||||
|
||||
|
70
rpcs3/Emu/IPC_config.cpp
Normal file
70
rpcs3/Emu/IPC_config.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
#include "stdafx.h"
|
||||
#include "IPC_config.h"
|
||||
|
||||
cfg_ipc g_cfg_ipc;
|
||||
|
||||
LOG_CHANNEL(IPC);
|
||||
|
||||
void cfg_ipc::load()
|
||||
{
|
||||
const std::string path = cfg_ipc::get_path();
|
||||
|
||||
fs::file cfg_file(path, fs::read);
|
||||
if (cfg_file)
|
||||
{
|
||||
IPC.notice("Loading IPC config. Path: %s", path);
|
||||
from_string(cfg_file.to_string());
|
||||
}
|
||||
else
|
||||
{
|
||||
IPC.notice("IPC config missing. Using default settings. Path: %s", path);
|
||||
from_default();
|
||||
}
|
||||
}
|
||||
|
||||
void cfg_ipc::save() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const std::string path_to_cfg = fs::get_config_dir() + "config/";
|
||||
if (!fs::create_path(path_to_cfg))
|
||||
{
|
||||
IPC.error("Could not create path: %s", path_to_cfg);
|
||||
}
|
||||
#endif
|
||||
|
||||
fs::pending_file cfg_file(cfg_ipc::get_path());
|
||||
|
||||
if (!cfg_file.file || (cfg_file.file.write(to_string()), !cfg_file.commit()))
|
||||
{
|
||||
IPC.error("Could not save config: %s", cfg_ipc::get_path());
|
||||
}
|
||||
}
|
||||
|
||||
std::string cfg_ipc::get_path()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return fs::get_config_dir() + "config/ipc.yml";
|
||||
#else
|
||||
return fs::get_config_dir() + "ipc.yml";
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cfg_ipc::get_server_enabled() const
|
||||
{
|
||||
return ipc_server_enabled.get();
|
||||
}
|
||||
|
||||
int cfg_ipc::get_port() const
|
||||
{
|
||||
return ipc_port;
|
||||
}
|
||||
|
||||
void cfg_ipc::set_server_enabled(const bool enabled)
|
||||
{
|
||||
this->ipc_server_enabled.set(enabled);
|
||||
}
|
||||
|
||||
void cfg_ipc::set_port(const int port)
|
||||
{
|
||||
this->ipc_port.set(port);
|
||||
}
|
23
rpcs3/Emu/IPC_config.h
Normal file
23
rpcs3/Emu/IPC_config.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/Config.h"
|
||||
|
||||
struct cfg_ipc : cfg::node
|
||||
{
|
||||
cfg::_bool ipc_server_enabled{ this, "IPC Server enabled", false };
|
||||
cfg::_int<1025, 65535> ipc_port{ this, "IPC Port", 28012 };
|
||||
|
||||
void load();
|
||||
void save() const;
|
||||
|
||||
bool get_server_enabled() const;
|
||||
int get_port() const;
|
||||
|
||||
void set_server_enabled(const bool enabled);
|
||||
void set_port(const int port);
|
||||
|
||||
private:
|
||||
static std::string get_path();
|
||||
};
|
||||
|
||||
extern cfg_ipc g_cfg_ipc;
|
114
rpcs3/Emu/IPC_socket.cpp
Normal file
114
rpcs3/Emu/IPC_socket.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
#include "stdafx.h"
|
||||
#include "System.h"
|
||||
#include "Emu/IPC_config.h"
|
||||
#include "IPC_socket.h"
|
||||
#include "rpcs3_version.h"
|
||||
|
||||
|
||||
namespace IPC_socket
|
||||
{
|
||||
const u8& IPC_impl::read8(u32 addr)
|
||||
{
|
||||
return vm::read8(addr);
|
||||
}
|
||||
|
||||
void IPC_impl::write8(u32 addr, u8 value)
|
||||
{
|
||||
vm::write8(addr, value);
|
||||
}
|
||||
|
||||
const be_t<u16>& IPC_impl::read16(u32 addr)
|
||||
{
|
||||
return vm::read16(addr);
|
||||
}
|
||||
|
||||
void IPC_impl::write16(u32 addr, be_t<u16> value)
|
||||
{
|
||||
vm::write16(addr, value);
|
||||
}
|
||||
|
||||
const be_t<u32>& IPC_impl::read32(u32 addr)
|
||||
{
|
||||
return vm::read32(addr);
|
||||
}
|
||||
|
||||
void IPC_impl::write32(u32 addr, be_t<u32> value)
|
||||
{
|
||||
vm::write32(addr, value);
|
||||
}
|
||||
|
||||
const be_t<u64>& IPC_impl::read64(u32 addr)
|
||||
{
|
||||
return vm::read64(addr);
|
||||
}
|
||||
|
||||
void IPC_impl::write64(u32 addr, be_t<u64> value)
|
||||
{
|
||||
vm::write64(addr, value);
|
||||
}
|
||||
|
||||
const int IPC_impl::get_port()
|
||||
{
|
||||
return g_cfg_ipc.get_port();
|
||||
}
|
||||
|
||||
pine::EmuStatus IPC_impl::get_status()
|
||||
{
|
||||
switch (Emu.GetStatus())
|
||||
{
|
||||
case system_state::running:
|
||||
return pine::EmuStatus::Running;
|
||||
case system_state::paused:
|
||||
return pine::EmuStatus::Paused;
|
||||
default:
|
||||
return pine::EmuStatus::Shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& IPC_impl::get_title()
|
||||
{
|
||||
return Emu.GetTitle();
|
||||
}
|
||||
|
||||
const std::string& IPC_impl::get_title_ID()
|
||||
{
|
||||
return Emu.GetTitleID();
|
||||
}
|
||||
|
||||
const std::string& IPC_impl::get_executable_hash()
|
||||
{
|
||||
return Emu.GetExecutableHash();
|
||||
}
|
||||
|
||||
const std::string& IPC_impl::get_app_version()
|
||||
{
|
||||
return Emu.GetAppVersion();
|
||||
}
|
||||
|
||||
const std::string IPC_impl::get_version_and_branch()
|
||||
{
|
||||
return rpcs3::get_version_and_branch();
|
||||
}
|
||||
|
||||
IPC_impl& IPC_impl::operator=(thread_state)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
void IPC_server_manager::set_server_enabled(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
int port = g_cfg_ipc.get_port();
|
||||
if (!m_ipc_server || port != m_old_port)
|
||||
{
|
||||
m_ipc_server = std::make_unique<IPC_server>();
|
||||
m_old_port = port;
|
||||
}
|
||||
}
|
||||
else if (!enabled && m_ipc_server)
|
||||
{
|
||||
m_ipc_server.reset(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
59
rpcs3/Emu/IPC_socket.h
Normal file
59
rpcs3/Emu/IPC_socket.h
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/Thread.h"
|
||||
#include "util/logs.hpp"
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "../pine/pine_server.h"
|
||||
|
||||
LOG_CHANNEL(IPC);
|
||||
|
||||
namespace IPC_socket
|
||||
{
|
||||
class IPC_impl
|
||||
{
|
||||
protected:
|
||||
template <u32 Size = 1>
|
||||
static bool check_addr(u32 addr, u8 flags = vm::page_readable)
|
||||
{
|
||||
return vm::check_addr<Size>(addr, flags);
|
||||
}
|
||||
|
||||
static const u8& read8(u32 addr);
|
||||
static void write8(u32 addr, u8 value);
|
||||
static const be_t<u16>& read16(u32 addr);
|
||||
static void write16(u32 addr, be_t<u16> value);
|
||||
static const be_t<u32>& read32(u32 addr);
|
||||
static void write32(u32 addr, be_t<u32> value);
|
||||
static const be_t<u64>& read64(u32 addr);
|
||||
static void write64(u32 addr, be_t<u64> value);
|
||||
|
||||
template<typename... Args>
|
||||
static void error(const const_str& fmt, const Args&&... args)
|
||||
{
|
||||
IPC.error(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static const int get_port();
|
||||
static pine::EmuStatus get_status();
|
||||
static const std::string& get_title();
|
||||
static const std::string& get_title_ID();
|
||||
static const std::string& get_executable_hash();
|
||||
static const std::string& get_app_version();
|
||||
static const std::string get_version_and_branch();
|
||||
|
||||
public:
|
||||
static auto constexpr thread_name = "IPC Server"sv;
|
||||
IPC_impl& operator=(thread_state);
|
||||
};
|
||||
|
||||
class IPC_server_manager
|
||||
{
|
||||
using IPC_server = named_thread<pine::pine_server<IPC_socket::IPC_impl>>;
|
||||
|
||||
std::unique_ptr<IPC_server> m_ipc_server;
|
||||
int m_old_port = 0;
|
||||
|
||||
public:
|
||||
void set_server_enabled(bool enabled);
|
||||
};
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
#include "Emu/perf_meter.hpp"
|
||||
#include "Emu/perf_monitor.hpp"
|
||||
#include "Emu/vfs_config.h"
|
||||
#include "Emu/IPC_config.h"
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
@ -43,6 +44,8 @@
|
||||
|
||||
#include "display_sleep_control.h"
|
||||
|
||||
#include "Emu/IPC_socket.h"
|
||||
|
||||
#if defined(HAVE_VULKAN)
|
||||
#include "Emu/RSX/VK/VulkanAPI.h"
|
||||
#endif
|
||||
@ -408,6 +411,11 @@ void Emulator::Init(bool add_only)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load IPC config
|
||||
g_cfg_ipc.load();
|
||||
sys_log.notice("Using IPC config:\n%s", g_cfg_ipc.to_string());
|
||||
g_fxo->get<IPC_socket::IPC_server_manager>().set_server_enabled(g_cfg_ipc.get_server_enabled());
|
||||
}
|
||||
|
||||
void Emulator::SetUsr(const std::string& user)
|
||||
|
@ -105,6 +105,7 @@ class Emulator final
|
||||
std::string m_title_id;
|
||||
std::string m_title;
|
||||
std::string m_app_version;
|
||||
std::string m_hash;
|
||||
std::string m_cat;
|
||||
std::string m_dir;
|
||||
std::string m_sfo_dir;
|
||||
@ -213,6 +214,13 @@ public:
|
||||
return m_app_version;
|
||||
}
|
||||
|
||||
const std::string& GetExecutableHash() const
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
void SetExecutableHash(std::string hash) { m_hash = std::move(hash); }
|
||||
|
||||
const std::string& GetCat() const
|
||||
{
|
||||
return m_cat;
|
||||
|
@ -71,6 +71,8 @@
|
||||
<ClCompile Include="Emu\Io\KeyboardHandler.cpp" />
|
||||
<ClCompile Include="Emu\Io\pad_config.cpp" />
|
||||
<ClCompile Include="Emu\Io\pad_config_types.cpp" />
|
||||
<ClCompile Include="Emu\IPC_config.cpp" />
|
||||
<ClCompile Include="Emu\IPC_socket.cpp" />
|
||||
<ClCompile Include="Emu\localized_string.cpp" />
|
||||
<ClCompile Include="Emu\NP\rpcn_config.cpp" />
|
||||
<ClCompile Include="Emu\perf_monitor.cpp" />
|
||||
@ -481,6 +483,8 @@
|
||||
<ClInclude Include="Emu\Io\Keyboard.h" />
|
||||
<ClInclude Include="Emu\Io\pad_config.h" />
|
||||
<ClInclude Include="Emu\Io\pad_config_types.h" />
|
||||
<ClInclude Include="Emu\IPC_config.h" />
|
||||
<ClInclude Include="Emu\IPC_socket.h" />
|
||||
<ClInclude Include="Emu\localized_string.h" />
|
||||
<ClInclude Include="Emu\localized_string_id.h" />
|
||||
<ClInclude Include="Emu\NP\generated\np2_structs_generated.h" />
|
||||
|
@ -1072,6 +1072,12 @@
|
||||
<ClCompile Include="Emu\RSX\RSXZCULL.cpp">
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\IPC_socket.cpp">
|
||||
<Filter>Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\IPC_config.cpp">
|
||||
<Filter>Emu</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
@ -2131,6 +2137,12 @@
|
||||
<ClInclude Include="Emu\RSX\RSXZCULL.h">
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\IPC_socket.h">
|
||||
<Filter>Emu</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\IPC_config.h">
|
||||
<Filter>Emu</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl">
|
||||
|
@ -255,6 +255,9 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_instruction_editor_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_ipc_settings_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_kernel_explorer.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
@ -471,6 +474,9 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_instruction_editor_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_ipc_settings_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_kernel_explorer.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
@ -622,6 +628,7 @@
|
||||
<ClCompile Include="rpcs3qt\game_list.cpp" />
|
||||
<ClCompile Include="rpcs3qt\gui_application.cpp" />
|
||||
<ClCompile Include="rpcs3qt\input_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\ipc_settings_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\localized.cpp" />
|
||||
<ClCompile Include="rpcs3qt\log_viewer.cpp" />
|
||||
<ClCompile Include="rpcs3qt\microphone_creator.cpp" />
|
||||
@ -1123,6 +1130,16 @@
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\ipc_settings_dialog.h">
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\movie_item.h" />
|
||||
<ClInclude Include="rpcs3qt\numbered_widget_item.h" />
|
||||
<CustomBuild Include="rpcs3qt\patch_creator_dialog.h">
|
||||
|
@ -139,6 +139,9 @@
|
||||
<Filter Include="Io\music">
|
||||
<UniqueIdentifier>{44c3b8d8-cc4d-4d5f-8681-21241ab220d0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Gui\ipc">
|
||||
<UniqueIdentifier>{91389c5b-9ebd-43fe-a288-1bbad775e528}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
@ -849,6 +852,15 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_vfs_dialog_path_widget.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\ipc_settings_dialog.cpp">
|
||||
<Filter>Gui\ipc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_ipc_settings_dialog.cpp">
|
||||
<Filter>Generated Files\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_ipc_settings_dialog.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
@ -1246,6 +1258,9 @@
|
||||
<CustomBuild Include="rpcs3qt\vfs_dialog_path_widget.h">
|
||||
<Filter>Gui\vfs</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\ipc_settings_dialog.h">
|
||||
<Filter>Gui\ipc</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="rpcs3.ico" />
|
||||
|
@ -32,6 +32,7 @@ set(SRC_FILES
|
||||
gui_settings.cpp
|
||||
input_dialog.cpp
|
||||
instruction_editor_dialog.cpp
|
||||
ipc_settings_dialog.cpp
|
||||
kernel_explorer.cpp
|
||||
localized.cpp
|
||||
localized_emu.h
|
||||
|
66
rpcs3/rpcs3qt/ipc_settings_dialog.cpp
Normal file
66
rpcs3/rpcs3qt/ipc_settings_dialog.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include <QMessageBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGroupBox>
|
||||
#include <QCheckBox>
|
||||
#include <QLineEdit>
|
||||
#include <QIntValidator>
|
||||
|
||||
#include "ipc_settings_dialog.h"
|
||||
#include "Emu/IPC_config.h"
|
||||
|
||||
ipc_settings_dialog::ipc_settings_dialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("IPC Settings"));
|
||||
setObjectName("ipc_settings_dialog");
|
||||
setMinimumSize(QSize(200, 100));
|
||||
|
||||
QVBoxLayout* vbox_global = new QVBoxLayout();
|
||||
|
||||
QCheckBox* checkbox_server_enabled = new QCheckBox(tr("Enable IPC Server"));
|
||||
|
||||
QGroupBox* group_server_port = new QGroupBox(tr("IPC Server Port"));
|
||||
QHBoxLayout* hbox_group_port = new QHBoxLayout();
|
||||
QLineEdit* line_edit_server_port = new QLineEdit();
|
||||
line_edit_server_port->setValidator(new QIntValidator(1025, 65535, this));
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Save);
|
||||
buttons->button(QDialogButtonBox::Save)->setDefault(true);
|
||||
|
||||
hbox_group_port->addWidget(line_edit_server_port);
|
||||
group_server_port->setLayout(hbox_group_port);
|
||||
|
||||
vbox_global->addWidget(checkbox_server_enabled);
|
||||
vbox_global->addWidget(group_server_port);
|
||||
vbox_global->addWidget(buttons);
|
||||
|
||||
setLayout(vbox_global);
|
||||
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, [this, checkbox_server_enabled, line_edit_server_port]()
|
||||
{
|
||||
bool ok = true;
|
||||
const bool server_enabled = checkbox_server_enabled->isChecked();
|
||||
const int server_port = line_edit_server_port->text().toInt(&ok);
|
||||
|
||||
if (!ok || server_port < 1025 || server_port > 65535)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Invalid port"), tr("The server port must be an integer in the range 1025 - 65535!"), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
g_cfg_ipc.set_server_enabled(server_enabled);
|
||||
g_cfg_ipc.set_port(server_port);
|
||||
g_cfg_ipc.save();
|
||||
|
||||
accept();
|
||||
});
|
||||
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
g_cfg_ipc.load();
|
||||
|
||||
checkbox_server_enabled->setChecked(g_cfg_ipc.get_server_enabled());
|
||||
line_edit_server_port->setText(QString::number(g_cfg_ipc.get_port()));
|
||||
}
|
10
rpcs3/rpcs3qt/ipc_settings_dialog.h
Normal file
10
rpcs3/rpcs3qt/ipc_settings_dialog.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class ipc_settings_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ipc_settings_dialog(QWidget* parent = nullptr);
|
||||
};
|
@ -29,6 +29,7 @@
|
||||
#include "gui_settings.h"
|
||||
#include "input_dialog.h"
|
||||
#include "camera_settings_dialog.h"
|
||||
#include "ipc_settings_dialog.h"
|
||||
|
||||
#include <thread>
|
||||
#include <charconv>
|
||||
@ -2084,6 +2085,12 @@ void main_window::CreateConnects()
|
||||
dlg.exec();
|
||||
});
|
||||
|
||||
connect(ui->confIPCAct, &QAction::triggered, this, [this]()
|
||||
{
|
||||
ipc_settings_dialog dlg(this);
|
||||
dlg.exec();
|
||||
});
|
||||
|
||||
connect(ui->confAutopauseManagerAct, &QAction::triggered, this, [this]()
|
||||
{
|
||||
auto_pause_settings_dialog dlg(this);
|
||||
|
@ -241,6 +241,7 @@
|
||||
<addaction name="confGuiAct"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="confRPCNAct"/>
|
||||
<addaction name="confIPCAct"/>
|
||||
<addaction name="confAutopauseManagerAct"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuManage">
|
||||
@ -1148,6 +1149,14 @@
|
||||
<string>Configure RPCN</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="confIPCAct">
|
||||
<property name="text">
|
||||
<string>IPC</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Configure IPC</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLog_Viewer">
|
||||
<property name="text">
|
||||
<string>Log Viewer</string>
|
||||
|
Loading…
Reference in New Issue
Block a user