diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt
index 6311abd07d..61a7b13930 100644
--- a/pcsx2/CMakeLists.txt
+++ b/pcsx2/CMakeLists.txt
@@ -75,6 +75,7 @@ set(pcsx2Sources
IopIrq.cpp
IopMem.cpp
IopSio2.cpp
+ IPC.cpp
Mdec.cpp
Memory.cpp
MMI.cpp
@@ -147,6 +148,7 @@ set(pcsx2Headers
IopHw.h
IopMem.h
IopSio2.h
+ IPC.h
Mdec.h
MTVU.h
Memory.h
diff --git a/pcsx2/Config.h b/pcsx2/Config.h
index 1c3cddee9d..8331a91327 100644
--- a/pcsx2/Config.h
+++ b/pcsx2/Config.h
@@ -454,6 +454,7 @@ struct Pcsx2Config
CdvdShareWrite :1, // allows the iso to be modified while it's loaded
EnablePatches :1, // enables patch detection and application
EnableCheats :1, // enables cheat detection and application
+ EnableIPC :1, // enables inter-process communication
EnableWideScreenPatches :1,
#ifndef DISABLE_RECORDING
EnableRecordingTools :1,
diff --git a/pcsx2/IPC.cpp b/pcsx2/IPC.cpp
new file mode 100644
index 0000000000..51068f1e71
--- /dev/null
+++ b/pcsx2/IPC.cpp
@@ -0,0 +1,256 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2020 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 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 PCSX2.
+ * If not, see .
+ */
+
+#include "PrecompiledHeader.h"
+
+#include
+#include
+#include
+#include
+#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 bzero(b, len) (memset((b), '\0', (len)), (void)0)
+#include
+#else
+#define read_portable(a, b, c) (read(a, b, c))
+#define write_portable(a, b, c) (write(a, b, c))
+#include
+#include
+#endif
+
+#include "Common.h"
+#include "Memory.h"
+#include "System/SysThreads.h"
+#include "IPC.h"
+
+SocketIPC::SocketIPC(SysCoreThread* vm)
+ : pxThread("IPC_Socket")
+{
+#ifdef _WIN32
+ WSADATA wsa;
+ SOCKET new_socket;
+ struct sockaddr_in server, client;
+ int c;
+
+
+ if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
+ {
+ Console.WriteLn(Color_Red, "IPC: Cannot initialize winsock! Shutting down...");
+ return;
+ }
+
+ if ((m_sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
+ {
+ Console.WriteLn(Color_Red, "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(PORT);
+
+ if (bind(m_sock, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
+ {
+ Console.WriteLn(Color_Red, "IPC: Error while binding to socket! Shutting down...");
+ return;
+ }
+
+#else
+ struct sockaddr_un server;
+
+ m_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (m_sock < 0)
+ {
+ Console.WriteLn(Color_Red, "IPC: Cannot open socket! Shutting down...");
+ return;
+ }
+ server.sun_family = AF_UNIX;
+ strcpy(server.sun_path, SOCKET_NAME);
+
+ // we unlink the socket so that when releasing this thread the socket gets
+ // freed even if we didn't close correctly the loop
+ unlink(SOCKET_NAME);
+ if (bind(m_sock, (struct sockaddr*)&server, sizeof(struct sockaddr_un)))
+ {
+ Console.WriteLn(Color_Red, "IPC: Error while binding to socket! Shutting down...");
+ return;
+ }
+#endif
+
+ // maximum queue of SOMAXCONN commands before refusing, which stops the thread
+ listen(m_sock, SOMAXCONN);
+
+ // we save a handle of the main vm object
+ m_vm = vm;
+
+ // we start the thread
+ Start();
+}
+
+void SocketIPC::ExecuteTaskInThread()
+{
+ int msgsock = 0;
+ // for the sake of speed we malloc once a return buffer and reuse it by just
+ // cropping its size when needed, it is 450k long which is the size of 50k
+ // MsgWrite64 replies, should be good enough even if we implement batch IPC
+ // processing. Coincidentally 650k is the size of 50k MsgWrite64 REQUESTS so
+ // we just allocate a 1mb buffer in the end, lul
+ ret_buffer = (char*)malloc(450000 * sizeof(char));
+ ipc_buffer = (char*)malloc(650000 * sizeof(char));
+ while (true)
+ {
+ msgsock = accept(m_sock, 0, 0);
+ if (msgsock == -1)
+ {
+ return;
+ }
+ else
+ {
+ if (read_portable(msgsock, ipc_buffer, 650000) < 0)
+ {
+ return;
+ }
+ else
+ {
+ auto res = ParseCommand(ipc_buffer, ret_buffer);
+ if (write_portable(msgsock, res.second, res.first) < 0)
+ {
+ return;
+ }
+ }
+ }
+ }
+}
+
+SocketIPC::~SocketIPC()
+{
+#ifdef _WIN32
+ closesocket(m_sock);
+ WSACleanup();
+#else
+ close(m_sock);
+ unlink(SOCKET_NAME);
+#endif
+ free(ret_buffer);
+ free(ipc_buffer);
+ // destroy the thread
+ try
+ {
+ pxThread::Cancel();
+ }
+ DESTRUCTOR_CATCHALL
+}
+
+char* SocketIPC::MakeOkIPC(char* ret_buffer)
+{
+ ret_buffer[0] = (unsigned char)IPC_OK;
+ return ret_buffer;
+}
+
+char* SocketIPC::MakeFailIPC(char* ret_buffer)
+{
+ ret_buffer[0] = (unsigned char)IPC_FAIL;
+ return ret_buffer;
+}
+
+std::pair SocketIPC::ParseCommand(char* buf, char* ret_buffer)
+{
+ // currently all our instructions require a running VM so we check once
+ // here, will help perf when/if we implement multi-ipc processing in one
+ // socket roundtrip.
+ if (!m_vm->HasActiveMachine())
+ return std::make_pair(1, MakeFailIPC(ret_buffer));
+
+ // 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 opcode = (IPCCommand)buf[0];
+
+ // return value
+ std::pair rval;
+
+ // YY YY YY YY from schema above
+ u32 a = FromArray(buf, 1);
+ switch (opcode)
+ {
+ case MsgRead8:
+ {
+ u8 res;
+ res = memRead8(a);
+ rval = std::make_pair(2, ToArray(MakeOkIPC(ret_buffer), res, 1));
+ break;
+ }
+ case MsgRead16:
+ {
+ u16 res;
+ res = memRead16(a);
+ rval = std::make_pair(3, ToArray(MakeOkIPC(ret_buffer), res, 1));
+ break;
+ }
+ case MsgRead32:
+ {
+ u32 res;
+ res = memRead32(a);
+ rval = std::make_pair(5, ToArray(MakeOkIPC(ret_buffer), res, 1));
+ break;
+ }
+ case MsgRead64:
+ {
+ u64 res;
+ memRead64(a, &res);
+ rval = std::make_pair(9, ToArray(MakeOkIPC(ret_buffer), res, 1));
+ break;
+ }
+ case MsgWrite8:
+ {
+ memWrite8(a, FromArray(buf, 5));
+ rval = std::make_pair(1, MakeOkIPC(ret_buffer));
+ break;
+ }
+ case MsgWrite16:
+ {
+ memWrite16(a, FromArray(buf, 5));
+ rval = std::make_pair(1, MakeOkIPC(ret_buffer));
+ break;
+ }
+ case MsgWrite32:
+ {
+ memWrite32(a, FromArray(buf, 5));
+ rval = std::make_pair(1, MakeOkIPC(ret_buffer));
+ break;
+ }
+ case MsgWrite64:
+ {
+ memWrite64(a, FromArray(buf, 5));
+ rval = std::make_pair(1, MakeOkIPC(ret_buffer));
+ break;
+ }
+ default:
+ {
+ rval = std::make_pair(1, MakeFailIPC(ret_buffer));
+ break;
+ }
+ }
+ return rval;
+}
diff --git a/pcsx2/IPC.h b/pcsx2/IPC.h
new file mode 100644
index 0000000000..fba77683fb
--- /dev/null
+++ b/pcsx2/IPC.h
@@ -0,0 +1,122 @@
+/* PCSX2 - PS2 Emulator for PCs
+ * Copyright (C) 2002-2020 PCSX2 Dev Team
+ *
+ * PCSX2 is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU Lesser General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * PCSX2 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 PCSX2.
+ * If not, see .
+ */
+
+/* Client code example for interfacing with the IPC interface is available
+ * here: https://code.govanify.com/govanify/pcsx2_ipc/ */
+
+#pragma once
+
+#include "Utilities/PersistentThread.h"
+#include "System/SysThreads.h"
+
+using namespace Threading;
+
+class SocketIPC : public pxThread
+{
+
+ typedef pxThread _parent;
+
+protected:
+#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.
+#define PORT 28011
+#else
+ // absolute path of the socket. Stored in the temporary directory in linux since
+ // /run requires superuser permission
+ const char* SOCKET_NAME = "/tmp/pcsx2.sock";
+#endif
+
+ // socket handlers
+#ifdef _WIN32
+ SOCKET m_sock = INVALID_SOCKET;
+#else
+ int m_sock = 0;
+#endif
+
+ // buffers that store the ipc request and reply messages.
+ char* ret_buffer;
+ char* ipc_buffer;
+
+ // possible command messages
+ enum IPCCommand
+ {
+ MsgRead8 = 0,
+ MsgRead16 = 1,
+ MsgRead32 = 2,
+ MsgRead64 = 3,
+ MsgWrite8 = 4,
+ MsgWrite16 = 5,
+ MsgWrite32 = 6,
+ MsgWrite64 = 7
+ };
+
+ // possible result codes
+ enum IPCResult
+ {
+ IPC_OK = 0,
+ IPC_FAIL = 0xFF
+ };
+
+ // handle to the main vm thread
+ SysCoreThread* m_vm;
+
+ /* Thread used to relay IPC commands. */
+ void ExecuteTaskInThread();
+
+ /* Internal function, Parses an IPC command.
+ * buf: buffer containing the IPC command.
+ * ret_buffer: buffer that will be used to send the reply.
+ * return value: pair containing a buffer with the result
+ * of the command and its size. */
+ std::pair ParseCommand(char* buf, char* ret_buffer);
+
+ /* Formats an IPC buffer
+ * ret_buffer: return buffer to use.
+ * return value: buffer containing the status code allocated of size
+ * size */
+ static inline char* MakeOkIPC(char* ret_buffer);
+ static inline char* MakeFailIPC(char* ret_buffer);
+
+ /* 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
+ static char* ToArray(char* res_array, T res, int i)
+ {
+ memcpy((res_array + i), (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
+ static T FromArray(char* arr, int i)
+ {
+ return *(T*)(arr + i);
+ }
+
+public:
+ /* Initializers */
+ SocketIPC(SysCoreThread* vm);
+ virtual ~SocketIPC();
+
+}; // class SocketIPC
diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp
index 4b41eeeb06..dd53674b85 100644
--- a/pcsx2/Pcsx2Config.cpp
+++ b/pcsx2/Pcsx2Config.cpp
@@ -436,6 +436,7 @@ void Pcsx2Config::LoadSave( IniInterface& ini )
IniBitBool( CdvdShareWrite );
IniBitBool( EnablePatches );
IniBitBool( EnableCheats );
+ IniBitBool( EnableIPC );
IniBitBool( EnableWideScreenPatches );
#ifndef DISABLE_RECORDING
IniBitBool( EnableRecordingTools );
diff --git a/pcsx2/System/SysCoreThread.cpp b/pcsx2/System/SysCoreThread.cpp
index b26d182688..2c38c06d59 100644
--- a/pcsx2/System/SysCoreThread.cpp
+++ b/pcsx2/System/SysCoreThread.cpp
@@ -24,6 +24,7 @@
#include "Patch.h"
#include "SysThreads.h"
#include "MTVU.h"
+#include "IPC.h"
#include "../DebugTools/MIPSAnalyst.h"
#include "../DebugTools/SymbolMap.h"
@@ -243,6 +244,11 @@ void SysCoreThread::GameStartingInThread()
#ifdef USE_SAVESLOT_UI_UPDATES
UI_UpdateSysControls();
#endif
+ if(EmuConfig.EnableIPC && m_IpcState == OFF)
+ {
+ m_IpcState = ON;
+ m_socketIpc = std::make_unique(this);
+ }
}
bool SysCoreThread::StateCheckInThread()
diff --git a/pcsx2/System/SysThreads.h b/pcsx2/System/SysThreads.h
index 6ddf09fadb..790f47f37a 100644
--- a/pcsx2/System/SysThreads.h
+++ b/pcsx2/System/SysThreads.h
@@ -19,6 +19,7 @@
#include "Utilities/PersistentThread.h"
#include "x86emitter/tools.h"
+#include "IPC.h"
using namespace Threading;
@@ -170,6 +171,17 @@ protected:
bool m_resetVsyncTimers;
bool m_resetVirtualMachine;
+ // Stores the state of the socket IPC thread.
+ std::unique_ptr m_socketIpc;
+
+ // Current state of the IPC thread
+ enum StateIPC
+ {
+ OFF,
+ ON
+ };
+ StateIPC m_IpcState = OFF;
+
// Indicates if the system has an active virtual machine state. Pretty much always
// true anytime between plugins being initialized and plugins being shutdown. Gets
// set false when plugins are shutdown, the corethread is canceled, or when an error
diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h
index 5b0031b5bc..fcc61b20e2 100644
--- a/pcsx2/gui/App.h
+++ b/pcsx2/gui/App.h
@@ -123,6 +123,7 @@ enum MenuIdentifiers
MenuId_GameSettingsSubMenu,
MenuId_EnablePatches,
MenuId_EnableCheats,
+ MenuId_EnableIPC,
MenuId_EnableWideScreenPatches,
MenuId_EnableInputRecording,
MenuId_EnableLuaTools,
diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp
index 142ed15e5b..85b8202f35 100644
--- a/pcsx2/gui/MainFrame.cpp
+++ b/pcsx2/gui/MainFrame.cpp
@@ -216,6 +216,7 @@ void MainEmuFrame::ConnectMenus()
Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnablePatches_Click, this, MenuId_EnablePatches);
Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableCheats_Click, this, MenuId_EnableCheats);
+ Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableIPC_Click, this, MenuId_EnableIPC);
Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableWideScreenPatches_Click, this, MenuId_EnableWideScreenPatches);
#ifndef DISABLE_RECORDING
Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableRecordingTools_Click, this, MenuId_EnableInputRecording);
@@ -368,6 +369,9 @@ void MainEmuFrame::CreatePcsx2Menu()
m_GameSettingsSubmenu.Append(MenuId_EnableCheats, _("Enable &Cheats"),
wxEmptyString, wxITEM_CHECK);
+ m_GameSettingsSubmenu.Append(MenuId_EnableIPC, _("Enable &IPC"),
+ wxEmptyString, wxITEM_CHECK);
+
m_GameSettingsSubmenu.Append(MenuId_EnableWideScreenPatches, _("Enable &Widescreen Patches"),
_("Enabling Widescreen Patches may occasionally cause issues."), wxITEM_CHECK);
@@ -744,6 +748,7 @@ void MainEmuFrame::ApplyConfigToGui(AppConfig& configToApply, int flags)
{//these should not be affected by presets
menubar.Check( MenuId_EnableBackupStates, configToApply.EmuOptions.BackupSavestate );
menubar.Check( MenuId_EnableCheats, configToApply.EmuOptions.EnableCheats );
+ menubar.Check( MenuId_EnableIPC, configToApply.EmuOptions.EnableIPC );
menubar.Check( MenuId_EnableWideScreenPatches, configToApply.EmuOptions.EnableWideScreenPatches );
#ifndef DISABLE_RECORDING
menubar.Check( MenuId_EnableInputRecording, configToApply.EmuOptions.EnableRecordingTools);
diff --git a/pcsx2/gui/MainFrame.h b/pcsx2/gui/MainFrame.h
index 81bc9f5466..b83689fafe 100644
--- a/pcsx2/gui/MainFrame.h
+++ b/pcsx2/gui/MainFrame.h
@@ -193,6 +193,7 @@ protected:
void Menu_EnableBackupStates_Click(wxCommandEvent &event);
void Menu_EnablePatches_Click(wxCommandEvent &event);
void Menu_EnableCheats_Click(wxCommandEvent &event);
+ void Menu_EnableIPC_Click(wxCommandEvent &event);
void Menu_EnableWideScreenPatches_Click(wxCommandEvent &event);
#ifndef DISABLE_RECORDING
void Menu_EnableRecordingTools_Click(wxCommandEvent &event);
diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp
index aa72cf0cde..e8809900e1 100644
--- a/pcsx2/gui/MainMenuClicks.cpp
+++ b/pcsx2/gui/MainMenuClicks.cpp
@@ -528,6 +528,13 @@ void MainEmuFrame::Menu_EnableCheats_Click( wxCommandEvent& )
AppSaveSettings();
}
+void MainEmuFrame::Menu_EnableIPC_Click( wxCommandEvent& )
+{
+ g_Conf->EmuOptions.EnableIPC = GetMenuBar()->IsChecked( MenuId_EnableIPC );
+ AppApplySettings();
+ AppSaveSettings();
+}
+
void MainEmuFrame::Menu_EnableWideScreenPatches_Click( wxCommandEvent& )
{
g_Conf->EmuOptions.EnableWideScreenPatches = GetMenuBar()->IsChecked( MenuId_EnableWideScreenPatches );
diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj b/pcsx2/windows/VCprojects/pcsx2.vcxproj
index 1dbd93413c..73e726f90c 100644
--- a/pcsx2/windows/VCprojects/pcsx2.vcxproj
+++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj
@@ -188,6 +188,7 @@
+
@@ -437,6 +438,7 @@
+
@@ -624,4 +626,4 @@
-
\ No newline at end of file
+
diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters
index 6009783928..4677afbed7 100644
--- a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters
+++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters
@@ -888,6 +888,8 @@
AppHost
+
+ System
@@ -1343,6 +1345,8 @@
AppHost\Include
+
+ System\Include
@@ -1428,4 +1432,4 @@
AppHost\Resources
-
\ No newline at end of file
+