diff --git a/CMakeLists.txt b/CMakeLists.txt index 28d99b3fb8d9..0dc6e2114d7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1449,6 +1449,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/CwCheat.h Core/HDRemaster.cpp Core/HDRemaster.h + Core/Instance.cpp + Core/Instance.h Core/ThreadEventQueue.h Core/WebServer.cpp Core/WebServer.h diff --git a/Core/Config.cpp b/Core/Config.cpp index 892482ca428b..606f3321bea5 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -42,6 +42,7 @@ #include "Core/ConfigValues.h" #include "Core/Loaders.h" #include "Core/HLE/sceUtility.h" +#include "Core/Instance.h" #include "GPU/Common/FramebufferCommon.h" // TODO: Find a better place for this. @@ -1107,8 +1108,13 @@ static void IterateSettings(IniFile &iniFile, std::function> GetLangValuesMapping() { std::map> langValuesMapping; @@ -1290,9 +1296,23 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { jitForcedOff = true; g_Config.iCpuCore = (int)CPUCore::INTERPRETER; } + + // Automatically silence secondary instances. Could be an option I guess, but meh. + if (PPSSPP_ID > 1) { + g_Config.iGlobalVolume = 0; + } + + INFO_LOG(LOADER, "Config loaded: '%s'", iniFilename_.c_str()); } void Config::Save(const char *saveReason) { + if (!IsFirstInstance()) { + // TODO: Should we allow saving config if started from a different directory? + // How do we tell? + WARN_LOG(LOADER, "Not saving config - secondary instances don't."); + return; + } + if (jitForcedOff) { // if JIT has been forced off, we don't want to screw up the user's ppsspp.ini g_Config.iCpuCore = (int)CPUCore::JIT; diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 6679ddbf34b1..6ad9a0c3c031 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -388,6 +388,7 @@ + @@ -918,6 +919,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 5cfb4de0f592..b99fc00e07f1 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -746,6 +746,9 @@ Core + + Core + @@ -1385,6 +1388,9 @@ Core + + Core + diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index a7619de4a921..7059b8eda307 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -32,6 +32,7 @@ #include "Core/HLE/sceKernelInterrupt.h" #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceKernelMemory.h" +#include "Core/Instance.h" #include "proAdhoc.h" #include "i18n/i18n.h" @@ -68,7 +69,6 @@ bool updateChatScreen = false; int newChat = 0; bool isLocalServer = false; -uint8_t PPSSPP_ID = 0; sockaddr localIP; // This might serves the same purpose with existing "localip" above, but since this is copied from my old code so here it is (too lazy to rewrite the code) int isLocalMAC(const SceNetEtherAddr * addr) { diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 21470fc7a353..2a7ee4913210 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -798,7 +798,6 @@ extern std::map adhocctlHandlers; extern uint16_t portOffset; extern bool isLocalServer; -extern uint8_t PPSSPP_ID; extern sockaddr localIP; extern uint32_t fakePoolSize; extern SceNetAdhocMatchingContext * contexts; diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index dacb2904a8d5..e9996c464ea4 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -42,6 +42,7 @@ #include "Core/HLE/sceNetAdhoc.h" #include "Core/HLE/sceNet.h" #include "Core/Reporting.h" +#include "Core/Instance.h" static bool netInited; static bool netInetInited; @@ -56,124 +57,6 @@ static struct SceNetMallocStat netMallocStat; static std::map apctlHandlers; -#ifdef _WIN32 -static HANDLE hIDMapFile = NULL; -#elif __linux__ || __APPLE__ -static int hIDMapFile = 0; -#endif -static int32_t* pIDBuf = NULL; -#define ID_SHM_NAME "/PPSSPP_ID" - -// Get current number of instance of PPSSPP running. -// Must be called only once during init. -static uint8_t getPPSSPPInstanceNumber() { -#ifdef _WIN32 - uint32_t BUF_SIZE = 4096; - SYSTEM_INFO sysInfo; - - GetSystemInfo(&sysInfo); - int gran = sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x10000; - BUF_SIZE = (BUF_SIZE + gran - 1) & ~(gran - 1); - - hIDMapFile = CreateFileMapping( - INVALID_HANDLE_VALUE, // use paging file - NULL, // default security - PAGE_READWRITE, // read/write access - 0, // maximum object size (high-order DWORD) - BUF_SIZE, // maximum object size (low-order DWORD) - TEXT(ID_SHM_NAME)); // name of mapping object - - DWORD lasterr = GetLastError(); - if (hIDMapFile == NULL) - { - ERROR_LOG(SCENET, "Could not create %s file mapping object (%d).", ID_SHM_NAME, lasterr); - return 1; - } - pIDBuf = (int32_t*)MapViewOfFile(hIDMapFile, // handle to map object - FILE_MAP_ALL_ACCESS, // read/write permission - 0, - 0, - sizeof(int32_t)); //BUF_SIZE - - if (pIDBuf == NULL) - { - ERROR_LOG(SCENET, "Could not map view of file %s (%d).", ID_SHM_NAME, GetLastError()); - //CloseHandle(hIDMapFile); - return 1; - } - - (*pIDBuf)++; - int id = *pIDBuf; - UnmapViewOfFile(pIDBuf); - //CloseHandle(hIDMapFile); //Should be called when program exits - //hIDMapFile = NULL; - - return id; -#elif __ANDROID__ - // TODO : replace shm_open & shm_unlink with ashmem or android-shmem - return 1; -#elif __linux__ || __APPLE__ - long BUF_SIZE = 4096; - //caddr_t pIDBuf; - int status; - - // Create shared memory object - - hIDMapFile = shm_open(ID_SHM_NAME, O_CREAT | O_RDWR, 0); - BUF_SIZE = (BUF_SIZE < sysconf(_SC_PAGE_SIZE)) ? sysconf(_SC_PAGE_SIZE) : BUF_SIZE; - - if ((ftruncate(hIDMapFile, BUF_SIZE)) == -1) { // Set the size - ERROR_LOG(SCENET, "ftruncate(%s) failure.", ID_SHM_NAME); - return 1; - } - - pIDBuf = (int32_t*)mmap(0, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, hIDMapFile, 0); - if (pIDBuf == MAP_FAILED) { // Set the size - ERROR_LOG(SCENET, "mmap(%s) failure.", ID_SHM_NAME); - pIDBuf = NULL; - return 1; - } - - int id = 1; - if (mlock(pIDBuf, BUF_SIZE) == 0) { - (*pIDBuf)++; - id = *pIDBuf; - munlock(pIDBuf, BUF_SIZE); - } - - status = munmap(pIDBuf, BUF_SIZE); // Unmap the page - //status = close(hIDMapFile); // Close file, should be called when program exits? - //status = shm_unlink(ID_SHM_NAME); // Unlink [& delete] shared-memory object, should be called when program exits - - return id; -#else - return 1; -#endif -} - -static void PPSSPPIDCleanup() { -#ifdef _WIN32 - if (hIDMapFile != NULL) { - CloseHandle(hIDMapFile); // If program exited(or crashed?) or the last handle reference closed the shared memory object will be deleted. - hIDMapFile = NULL; - } -#elif __ANDROID__ - if (hIDMapFile != 0) { - close(hIDMapFile); - // TODO : replace shm_unlink with ashmem or android-shmem - //shm_unlink(ID_SHM_NAME); // If program exited or crashed before unlinked the shared memory object and it's contents will persist. - hIDMapFile = 0; - } -#elif __linux__ || __APPLE__ - // TODO : This unlink should be called when program exits instead of everytime the game reset. - if (hIDMapFile != 0) { - close(hIDMapFile); - shm_unlink(ID_SHM_NAME); // If program exited or crashed before unlinked the shared memory object and it's contents will persist. - hIDMapFile = 0; - } -#endif -} - static int InitLocalIP() { // find local IP addrinfo* localAddr; @@ -232,10 +115,6 @@ static void __ResetInitNetLib() { void __NetInit() { portOffset = g_Config.iPortOffset; - if (PPSSPP_ID == 0) // For persistent ID/IP, New instances may have a possibility to have the same ID/IP if PPSSPPIDCleanup() being called on every reset - { - PPSSPP_ID = getPPSSPPInstanceNumber(); // This should be called when program started instead of when the game started/reseted - } net::Init(); InitLocalIP(); diff --git a/Core/Instance.cpp b/Core/Instance.cpp new file mode 100644 index 000000000000..70976427de51 --- /dev/null +++ b/Core/Instance.cpp @@ -0,0 +1,153 @@ +// Copyright (c) 2020 PPSSPP Project. + +// This program 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, version 2.0 or later versions. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Instance.h" + +#if __linux__ || __APPLE__ +#include +#include +#include +#include +#endif + +#include "Common/Log.h" + +#if PPSSPP_PLATFORM(WINDOWS) + +#include "Common/CommonWindows.h" + +#endif + +#include + +uint8_t PPSSPP_ID = 0; + +#if PPSSPP_PLATFORM(WINDOWS) +static HANDLE hIDMapFile = NULL; +#else +static int hIDMapFile = 0; +#endif + +static int32_t* pIDBuf = NULL; +#define ID_SHM_NAME "/PPSSPP_ID" + +// Get current number of instance of PPSSPP running. +// Must be called only once during init. +void InitInstanceCounter() { +#if PPSSPP_PLATFORM(WINDOWS) + uint32_t BUF_SIZE = 4096; + SYSTEM_INFO sysInfo; + + GetSystemInfo(&sysInfo); + int gran = sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x10000; + BUF_SIZE = (BUF_SIZE + gran - 1) & ~(gran - 1); + + hIDMapFile = CreateFileMapping( + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_READWRITE, // read/write access + 0, // maximum object size (high-order DWORD) + BUF_SIZE, // maximum object size (low-order DWORD) + TEXT(ID_SHM_NAME)); // name of mapping object + + DWORD lasterr = GetLastError(); + if (hIDMapFile == NULL) + { + ERROR_LOG(SCENET, "Could not create %s file mapping object (%d).", ID_SHM_NAME, lasterr); + PPSSPP_ID = 1; + return; + } + + pIDBuf = (int32_t*)MapViewOfFile(hIDMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + sizeof(int32_t)); //BUF_SIZE + + if (pIDBuf == NULL) { + ERROR_LOG(SCENET, "Could not map view of file %s (%d).", ID_SHM_NAME, GetLastError()); + //CloseHandle(hIDMapFile); + PPSSPP_ID = 1; + return; + } + + (*pIDBuf)++; + int id = *pIDBuf; + UnmapViewOfFile(pIDBuf); + //CloseHandle(hIDMapFile); //Should be called when program exits + //hIDMapFile = NULL; + + PPSSPP_ID = id; +#elif PPSSPP_PLATFORM(ANDROID) + // TODO : replace shm_open & shm_unlink with ashmem or android-shmem + PPSSPP_ID = 1; +#else + long BUF_SIZE = 4096; + //caddr_t pIDBuf; + int status; + + // Create shared memory object + + hIDMapFile = shm_open(ID_SHM_NAME, O_CREAT | O_RDWR, 0); + BUF_SIZE = (BUF_SIZE < sysconf(_SC_PAGE_SIZE)) ? sysconf(_SC_PAGE_SIZE) : BUF_SIZE; + + if ((ftruncate(hIDMapFile, BUF_SIZE)) == -1) { // Set the size + ERROR_LOG(SCENET, "ftruncate(%s) failure.", ID_SHM_NAME); + PPSSPP_ID = 1; + return; + } + + pIDBuf = (int32_t*)mmap(0, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, hIDMapFile, 0); + if (pIDBuf == MAP_FAILED) { // Set the size + ERROR_LOG(SCENET, "mmap(%s) failure.", ID_SHM_NAME); + pIDBuf = NULL; + PPSSPP_ID = 1; + return; + } + + int id = 1; + if (mlock(pIDBuf, BUF_SIZE) == 0) { + (*pIDBuf)++; + id = *pIDBuf; + munlock(pIDBuf, BUF_SIZE); + } + + status = munmap(pIDBuf, BUF_SIZE); // Unmap the page + //status = close(hIDMapFile); // Close file, should be called when program exits? + //status = shm_unlink(ID_SHM_NAME); // Unlink [& delete] shared-memory object, should be called when program exits + + PPSSPP_ID = id; +#endif +} + +void ShutdownInstanceCounter() { +#if PPSSPP_PLATFORM(WINDOWS) + if (hIDMapFile != NULL) { + CloseHandle(hIDMapFile); // If program exited(or crashed?) or the last handle reference closed the shared memory object will be deleted. + hIDMapFile = NULL; + } +#elif PPSSPP_PLATFORM(ANDROID) + // Do nothing +#else + // TODO : This unlink should be called when program exits instead of everytime the game reset. + if (hIDMapFile != 0) { + close(hIDMapFile); + shm_unlink(ID_SHM_NAME); // If program exited or crashed before unlinked the shared memory object and it's contents will persist. + hIDMapFile = 0; + } +#endif +} diff --git a/Core/Instance.h b/Core/Instance.h new file mode 100644 index 000000000000..5fdfd8106cb3 --- /dev/null +++ b/Core/Instance.h @@ -0,0 +1,31 @@ +// Copyright (c) 2020 PPSSPP Project. + +// This program 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, version 2.0 or later versions. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "ppsspp_config.h" + +#include + +extern uint8_t PPSSPP_ID; + +void InitInstanceCounter(); +void ShutdownInstanceCounter(); + +inline bool IsFirstInstance() { + return PPSSPP_ID == 1; +} diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 900df8f61af3..80830c4cc36f 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -432,6 +432,7 @@ + @@ -648,6 +649,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 8055a958ffac..5e9b91ec567c 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -80,6 +80,7 @@ + @@ -718,6 +719,7 @@ + diff --git a/Windows/WindowsHost.cpp b/Windows/WindowsHost.cpp index 84d09875bd7b..f0735eb2022d 100644 --- a/Windows/WindowsHost.cpp +++ b/Windows/WindowsHost.cpp @@ -46,6 +46,8 @@ #include "Core/CoreParameter.h" #include "Core/System.h" #include "Core/Debugger/SymbolMap.h" +#include "Core/Instance.h" + #include "Windows/EmuThread.h" #include "Windows/WindowsAudio.h" #include "Windows/WindowsHost.h" @@ -101,8 +103,9 @@ WindowsHost::WindowsHost(HINSTANCE hInstance, HWND mainWindow, HWND displayWindo void WindowsHost::SetConsolePosition() { HWND console = GetConsoleWindow(); - if (console != NULL && g_Config.iConsoleWindowX != -1 && g_Config.iConsoleWindowY != -1) + if (console != NULL && g_Config.iConsoleWindowX != -1 && g_Config.iConsoleWindowY != -1) { SetWindowPos(console, NULL, g_Config.iConsoleWindowX, g_Config.iConsoleWindowY, 0, 0, SWP_NOSIZE | SWP_NOZORDER); + } } void WindowsHost::UpdateConsolePosition() { @@ -167,6 +170,9 @@ void WindowsHost::SetWindowTitle(const char *message) { #ifdef _DEBUG winTitle.append(L" (debug)"); #endif + if (PPSSPP_ID >= 1) { + winTitle.append(ConvertUTF8ToWString(StringFromFormat(" (instance: %d)", (int)PPSSPP_ID))); + } MainWindow::SetWindowTitle(winTitle.c_str()); PostMessage(mainWindow_, MainWindow::WM_USER_WINDOW_TITLE_CHANGED, 0, 0); @@ -280,7 +286,7 @@ static std::string SymbolMapFilename(const char *currentFilename, const char* ex return result + ".ppsspp-symbols" + ext; } else { - size_t dot = result.rfind('.'); + const size_t dot = result.rfind('.'); if (dot == result.npos) return result + ext; @@ -360,8 +366,8 @@ bool WindowsHost::CreateDesktopShortcut(std::string argumentPath, std::string ga // Sanitize the game title for banned characters. const char bannedChars[] = "<>:\"/\\|?*"; for (size_t i = 0; i < gameTitle.size(); i++) { - for (size_t c = 0; c < strlen(bannedChars); c++) { - if (gameTitle[i] == bannedChars[c]) { + for (char c : bannedChars) { + if (gameTitle[i] == c) { gameTitle[i] = '_'; break; } diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 5a0866546496..7aa1cd2d4a7b 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -291,6 +291,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/CoreTiming.cpp \ $(SRC)/Core/CwCheat.cpp \ $(SRC)/Core/HDRemaster.cpp \ + $(SRC)/Core/Instance.cpp \ $(SRC)/Core/Host.cpp \ $(SRC)/Core/Loaders.cpp \ $(SRC)/Core/PSPLoaders.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 3b12a0ba0765..b9fe4993bcad 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -342,6 +342,7 @@ SOURCES_CXX += $(NATIVEDIR)/math/dataconv.cpp \ $(COREDIR)/CoreTiming.cpp \ $(COREDIR)/CwCheat.cpp \ $(COREDIR)/HDRemaster.cpp \ + $(COREDIR)/Instance.cpp \ $(COREDIR)/Debugger/Breakpoints.cpp \ $(COREDIR)/Debugger/SymbolMap.cpp \ $(COREDIR)/Dialog/PSPDialog.cpp \