diff --git a/.travis.sh b/.travis.sh index f75ef266a1..c944daf15f 100644 --- a/.travis.sh +++ b/.travis.sh @@ -2,7 +2,7 @@ export USE_CCACHE=1 export NDK_CCACHE=ccache -NDK_VER=android-ndk-r16b +NDK_VER=android-ndk-r18b download_extract() { aria2c -x 16 $1 -o $2 @@ -18,53 +18,19 @@ download_extract_zip() { unzip $2 2>&1 | pv > /dev/null } -brew_install() { - brew install $1 - brew outdated $1 || brew upgrade $1 -} - travis_before_install() { git submodule update --init --recursive - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then - sudo apt-get update -qq - sudo apt-get install software-properties-common aria2 pv build-essential libgl1-mesa-dev libglu1-mesa-dev -qq - elif [ "$TRAVIS_OS_NAME" = "osx" ]; then - brew update - brew_install ccache - fi -} - -setup_ccache_script() { - if [ ! -e "$1" ]; then - mkdir "$1" - fi - - echo "#!/bin/bash" > "$1/$3" - echo "ccache $2/$3 \$*" >> "$1/$3" - chmod +x "$1/$3" } travis_install() { # Ubuntu Linux + GCC 4.8 if [ "$PPSSPP_BUILD_TYPE" = "Linux" ]; then - # For libsdl2-dev. - sudo add-apt-repository ppa:zoogie/sdl2-snapshots -y if [ "$CXX" = "g++" ]; then - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - fi - if [ "$QT" = "TRUE" ]; then - sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa - fi - - sudo apt-get update - sudo apt-get install libsdl2-dev -qq - if [ "$CXX" = "g++" ]; then - sudo apt-get install g++-4.8 -qq + sudo apt-get install -qq g++-4.8 fi if [ "$QT" = "TRUE" ]; then - sudo apt-get install -qq qt5-qmake qtmultimedia5-dev qtsystems5-dev qtbase5-dev qtdeclarative5-dev qttools5-dev-tools libqt5webkit5-dev libsqlite3-dev qt5-default + sudo apt-get install -qq qt5-qmake qtmultimedia5-dev qtsystems5-dev qtbase5-dev qtdeclarative5-dev qttools5-dev-tools libqt5webkit5-dev libqt5opengl5-dev libsqlite3-dev qt5-default fi download_extract "https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz" cmake-3.6.2-Linux-x86_64.tar.gz @@ -72,16 +38,12 @@ travis_install() { # Android NDK + GCC 4.8 if [ "$PPSSPP_BUILD_TYPE" = "Android" ]; then - free -m - sudo apt-get install ant -qq download_extract_zip http://dl.google.com/android/repository/${NDK_VER}-linux-x86_64.zip ${NDK_VER}-linux-x86_64.zip fi - if [ "$PPSSPP_BUILD_TYPE" = "macOS" ]; then - brew_install sdl2 - brew upgrade python - elif [ "$PPSSPP_BUILD_TYPE" = "iOS" ]; then - brew upgrade python + if [ "$PPSSPP_BUILD_TYPE" = "Windows" ]; then + curl -L https://github.com/frerich/clcache/releases/download/v4.2.0/clcache.4.2.0.nupkg --output clcache.4.2.0.nupkg + choco install clcache --source=. fi # Ensure we're using ccache @@ -135,10 +97,28 @@ travis_script() { if [ "$PPSSPP_BUILD_TYPE" = "macOS" ]; then ./b.sh --headless fi + if [ "$PPSSPP_BUILD_TYPE" = "Windows" ]; then + export "MSBUILD_PATH=/c/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/MSBuild/15.0/Bin" + export "PATH=$MSBUILD_PATH:$PATH" + export CLCACHE_OBJECT_CACHE_TIMEOUT_MS=120000 + + # Set DebugInformationFormat to nothing, the default is ProgramDatabase which breaks clcache. + # Turns out it's not possible to pass this on the msbuild command line. + for f in `find . -name *.vcxproj`; do + sed -i 's/>ProgramDatabase<\/DebugInformationFormat>/><\/DebugInformationFormat>/g' $f + done + + msbuild.exe Windows\\PPSSPP.sln -m -p:CLToolExe=clcache.exe -p:Configuration=Release -p:Platform=x64 -p:TrackFileAccess=false + fi } travis_after_success() { - ccache -s + if [ "$PPSSPP_BUILD_TYPE" != "Windows" ]; then + ccache -s + else + clcache -s + clcache -z + fi if [ "$PPSSPP_BUILD_TYPE" = "Linux" ]; then ./test.py diff --git a/.travis.yml b/.travis.yml index 2ce914c98e..64aff24a17 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,27 +4,38 @@ sudo: required language: cpp -dist: trusty +dist: xenial addons: apt: + update: true packages: + - ant + - aria2 - build-essential + - cmake - libgl1-mesa-dev - libglu1-mesa-dev - - cmake - - aria2 - - ant - - lib32stdc++6 - - lib32z1 - - lib32z1-dev - - lib32bz2-1.0 + - libsdl2-dev + - pv + - software-properties-common + sources: + - sourceline: 'ppa:zoogie/sdl2-snapshots' + - sourceline: 'ppa:ubuntu-toolchain-r/test' + - sourceline: 'ppa:ubuntu-sdk-team/ppa' + homebrew: + update: true + packages: + - ccache + - python + - sdl2 cache: apt: true ccache: true directories: - $HOME/Library/Caches/Homebrew + - $HOME/clcache # By encrypting the channel name, we disable notifications from forks by default. # It's not actually a big secret. @@ -75,19 +86,22 @@ matrix: env: PPSSPP_BUILD_TYPE=Linux LIBRETRO=TRUE - os: osx - osx_image: xcode8 + osx_image: xcode8.3 compiler: "clang" env: PPSSPP_BUILD_TYPE=macOS - os: osx - osx_image: xcode8 + osx_image: xcode8.3 compiler: "clang" env: PPSSPP_BUILD_TYPE=iOS + - os: windows + compiler: "msvc2017" + env: PPSSPP_BUILD_TYPE=Windows before_install: - - bash .travis.sh travis_before_install + - travis_retry bash .travis.sh travis_before_install install: - - bash .travis.sh travis_install + - travis_retry bash .travis.sh travis_install script: - bash .travis.sh travis_script diff --git a/CMakeLists.txt b/CMakeLists.txt index 288f463b6b..7ad547ef15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1874,8 +1874,6 @@ set(WindowsFiles Windows/InputBox.h Windows/InputDevice.cpp Windows/InputDevice.h - Windows/KeyboardDevice.cpp - Windows/KeyboardDevice.h Windows/W32Util/DialogManager.cpp Windows/W32Util/DialogManager.h Windows/W32Util/Misc.cpp diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 7cd6bb9e56..2146cbb8b1 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -258,6 +258,7 @@ true false MultiThreaded + ProgramDatabase Windows @@ -292,6 +293,7 @@ false MultiThreaded true + ProgramDatabase Windows @@ -326,6 +328,7 @@ false MultiThreaded true + ProgramDatabase Windows @@ -360,6 +363,7 @@ false MultiThreaded true + ProgramDatabase Windows diff --git a/Core/Config.cpp b/Core/Config.cpp index cf1ef3cb61..a80423309b 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -705,10 +705,6 @@ struct ConfigTranslator { typedef ConfigTranslator GPUBackendTranslator; static ConfigSetting graphicsSettings[] = { - ConfigSetting("EnableCardboard", &g_Config.bEnableCardboard, false, true, true), - ConfigSetting("CardboardScreenSize", &g_Config.iCardboardScreenSize, 50, true, true), - ConfigSetting("CardboardXShift", &g_Config.iCardboardXShift, 0, true, true), - ConfigSetting("CardboardYShift", &g_Config.iCardboardXShift, 0, true, true), ConfigSetting("ShowFPSCounter", &g_Config.iShowFPSCounter, 0, true, true), ReportedConfigSetting("GraphicsBackend", &g_Config.iGPUBackend, &DefaultGPUBackend, &GPUBackendTranslator::To, &GPUBackendTranslator::From, true, false), ConfigSetting("FailedGraphicsBackends", &g_Config.sFailedGPUBackends, ""), diff --git a/Core/Config.h b/Core/Config.h index e8efc193a1..a8cdd97744 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -159,11 +159,6 @@ public: bool bAutoFrameSkip; bool bFrameSkipUnthrottle; - bool bEnableCardboard; // Cardboard Master Switch - int iCardboardScreenSize; // Screen Size (in %) - int iCardboardXShift; // X-Shift of Screen (in %) - int iCardboardYShift; // Y-Shift of Screen (in %) - int iWindowX; int iWindowY; int iWindowWidth; // Windows and other windowed environments diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 5a95746d95..937ab02ac4 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -238,6 +238,7 @@ MultiThreaded true false + ProgramDatabase true @@ -271,6 +272,7 @@ false MultiThreaded true + ProgramDatabase true @@ -300,6 +302,7 @@ false MultiThreaded true + ProgramDatabase true @@ -329,6 +332,7 @@ false MultiThreaded true + ProgramDatabase true diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index cb7cffd1c7..90589b479e 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -98,7 +98,7 @@ static bool FixFilenameCase(const std::string &path, std::string &filename) return retValue; } -bool FixPathCase(std::string& basePath, std::string &path, FixPathCaseBehavior behavior) +bool FixPathCase(const std::string &basePath, std::string &path, FixPathCaseBehavior behavior) { size_t len = path.size(); @@ -158,25 +158,25 @@ DirectoryFileSystem::~DirectoryFileSystem() { CloseAll(); } -std::string DirectoryFileHandle::GetLocalPath(std::string& basePath, std::string localpath) +std::string DirectoryFileHandle::GetLocalPath(const std::string &basePath, std::string localpath) { if (localpath.empty()) return basePath; if (localpath[0] == '/') - localpath.erase(0,1); - //Convert slashes + localpath.erase(0, 1); + + std::string result = basePath + localpath; #ifdef _WIN32 - for (size_t i = 0; i < localpath.size(); i++) { - if (localpath[i] == '/') - localpath[i] = '\\'; + for (char &c : result) { + if (c == '/') + c = '\\'; } #endif - return basePath + localpath; + return result; } -bool DirectoryFileHandle::Open(std::string &basePath, std::string &fileName, FileAccess access, u32 &error) -{ +bool DirectoryFileHandle::Open(const std::string &basePath, std::string &fileName, FileAccess access, u32 &error) { error = 0; #if HOST_IS_CASE_SENSITIVE @@ -190,7 +190,7 @@ bool DirectoryFileHandle::Open(std::string &basePath, std::string &fileName, Fil // else we try fopen first (in case we're lucky) before simulating case insensitivity #endif - std::string fullName = GetLocalPath(basePath,fileName); + std::string fullName = GetLocalPath(basePath, fileName); VERBOSE_LOG(FILESYS,"Actually opening %s", fullName.c_str()); // On the PSP, truncating doesn't lose data. If you seek later, you'll recover it. @@ -322,6 +322,11 @@ bool DirectoryFileHandle::Open(std::string &basePath, std::string &fileName, Fil } #endif + // Try to detect reads/writes to PSP/GAME to avoid them in replays. + if (fullName.find("/PSP/GAME/") != fullName.npos || fullName.find("\\PSP\\GAME\\") != fullName.npos) { + inGameDir_ = true; + } + return success; } @@ -333,7 +338,7 @@ size_t DirectoryFileHandle::Read(u8* pointer, s64 size) // On a PSP. it actually is truncated, but the data wasn't erased. off_t off = (off_t)Seek(0, FILEMOVE_CURRENT); if (needsTrunc_ <= off) { - return replay_ ? ReplayApplyDiskRead(pointer, 0, (uint32_t)size, CoreTiming::GetGlobalTimeUs()) : 0; + return replay_ ? ReplayApplyDiskRead(pointer, 0, (uint32_t)size, inGameDir_, CoreTiming::GetGlobalTimeUs()) : 0; } if (needsTrunc_ < off + size) { size = needsTrunc_ - off; @@ -344,7 +349,7 @@ size_t DirectoryFileHandle::Read(u8* pointer, s64 size) #else bytesRead = read(hFile, pointer, size); #endif - return replay_ ? ReplayApplyDiskRead(pointer, (uint32_t)bytesRead, (uint32_t)size, CoreTiming::GetGlobalTimeUs()) : bytesRead; + return replay_ ? ReplayApplyDiskRead(pointer, (uint32_t)bytesRead, (uint32_t)size, inGameDir_, CoreTiming::GetGlobalTimeUs()) : bytesRead; } size_t DirectoryFileHandle::Write(const u8* pointer, s64 size) @@ -371,6 +376,10 @@ size_t DirectoryFileHandle::Write(const u8* pointer, s64 size) } } + if (replay_) { + bytesWritten = ReplayApplyDiskWrite(pointer, (uint64_t)bytesWritten, (uint64_t)size, &diskFull, inGameDir_, CoreTiming::GetGlobalTimeUs()); + } + if (diskFull) { ERROR_LOG(FILESYS, "Disk full"); I18NCategory *err = GetI18NCategory("Error"); diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index 7301679964..54742cd3a1 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -54,7 +54,7 @@ enum FixPathCaseBehavior { FPC_PARTIAL_ALLOWED, // don't care how many exist (mkdir recursive) }; -bool FixPathCase(std::string& basePath, std::string &path, FixPathCaseBehavior behavior); +bool FixPathCase(const std::string &basePath, std::string &path, FixPathCaseBehavior behavior); #endif struct DirectoryFileHandle { @@ -70,12 +70,13 @@ struct DirectoryFileHandle { #endif s64 needsTrunc_ = -1; bool replay_ = true; + bool inGameDir_ = false; DirectoryFileHandle(Flags flags) : replay_(flags != SKIP_REPLAY) { } - std::string GetLocalPath(std::string& basePath, std::string localpath); - bool Open(std::string& basePath, std::string& fileName, FileAccess access, u32 &err); + std::string GetLocalPath(const std::string &basePath, std::string localpath); + bool Open(const std::string &basePath, std::string &fileName, FileAccess access, u32 &err); size_t Read(u8* pointer, s64 size); size_t Write(const u8* pointer, s64 size); size_t Seek(s32 position, FileMove type); diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 99f40e4551..b6e7179ea1 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -62,6 +62,7 @@ enum latency { int eventAudioUpdate = -1; int eventHostAudioUpdate = -1; int mixFrequency = 44100; +int srcFrequency = 0; const int hwSampleRate = 44100; @@ -107,6 +108,7 @@ static void __AudioCPUMHzChange() { void __AudioInit() { memset(&g_AudioDebugStats, 0, sizeof(g_AudioDebugStats)); mixFrequency = 44100; + srcFrequency = 0; switch (g_Config.iAudioLatency) { case LOW_LATENCY: @@ -149,7 +151,7 @@ void __AudioInit() { } void __AudioDoState(PointerWrap &p) { - auto s = p.Section("sceAudio", 1); + auto s = p.Section("sceAudio", 1, 2); if (!s) return; @@ -159,6 +161,13 @@ void __AudioDoState(PointerWrap &p) { CoreTiming::RestoreRegisterEvent(eventHostAudioUpdate, "AudioUpdateHost", &hleHostAudioUpdate); p.Do(mixFrequency); + if (s >= 2) { + p.Do(srcFrequency); + } else { + // Assume that it was actually the SRC channel frequency. + srcFrequency = mixFrequency; + mixFrequency = 44100; + } // TODO: This never happens because maxVer=1. if (s >= 2) { @@ -176,6 +185,7 @@ void __AudioDoState(PointerWrap &p) { if (chanCount != ARRAY_SIZE(chans)) { ERROR_LOG(SCEAUDIO, "Savestate failure: different number of audio channels."); + p.SetError(p.ERROR_FAILURE); return; } for (int i = 0; i < chanCount; ++i) @@ -329,6 +339,10 @@ void __AudioSetOutputFrequency(int freq) { mixFrequency = freq; } +void __AudioSetSRCFrequency(int freq) { + srcFrequency = freq; +} + // Mix samples from the various audio channels into a single sample queue. // This single sample queue is where __AudioMix should read from. If the sample queue is full, we should // just sleep the main emulator thread a little. @@ -337,6 +351,7 @@ void __AudioUpdate(bool resetRecording) { // to the CPU. Much better to throttle the frame rate on frame display and just throw away audio // if the buffer somehow gets full. bool firstChannel = true; + std::vector srcBuffer; for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) { if (!chans[i].reserved) @@ -348,14 +363,54 @@ void __AudioUpdate(bool resetRecording) { continue; } - if (hwBlockSize * 2 > (int)chans[i].sampleQueue.size()) { - ERROR_LOG(SCEAUDIO, "Channel %i buffer underrun at %i of %i", i, (int)chans[i].sampleQueue.size() / 2, hwBlockSize); + bool needsResample = i == PSP_AUDIO_CHANNEL_SRC && srcFrequency != 0 && srcFrequency != mixFrequency; + size_t sz = needsResample ? (hwBlockSize * 2 * srcFrequency) / mixFrequency : hwBlockSize * 2; + if (sz > chans[i].sampleQueue.size()) { + ERROR_LOG(SCEAUDIO, "Channel %i buffer underrun at %i of %i", i, (int)chans[i].sampleQueue.size() / 2, (int)sz / 2); } const s16 *buf1 = 0, *buf2 = 0; size_t sz1, sz2; - chans[i].sampleQueue.popPointers(hwBlockSize * 2, &buf1, &sz1, &buf2, &sz2); + chans[i].sampleQueue.popPointers(sz, &buf1, &sz1, &buf2, &sz2); + + if (needsResample) { + auto read = [&](size_t i) { + if (i < sz1) + return buf1[i]; + if (i < sz1 + sz2) + return buf2[i - sz1]; + if (buf2) + return buf2[sz2 - 1]; + return buf1[sz1 - 1]; + }; + + srcBuffer.resize(hwBlockSize * 2); + + // TODO: This is terrible, since it's doing it by small chunk and discarding frac. + const uint32_t ratio = (uint32_t)(65536.0 * srcFrequency / (double)mixFrequency); + uint32_t frac = 0; + size_t readIndex = 0; + for (size_t outIndex = 0; readIndex < sz && outIndex < srcBuffer.size(); outIndex += 2) { + size_t readIndex2 = readIndex + 2; + int16_t l1 = read(readIndex); + int16_t r1 = read(readIndex + 1); + int16_t l2 = read(readIndex2); + int16_t r2 = read(readIndex2 + 1); + int sampleL = ((l1 << 16) + (l2 - l1) * (uint16_t)frac) >> 16; + int sampleR = ((r1 << 16) + (r2 - r1) * (uint16_t)frac) >> 16; + srcBuffer[outIndex] = sampleL; + srcBuffer[outIndex + 1] = sampleR; + frac += ratio; + readIndex += 2 * (uint16_t)(frac >> 16); + frac &= 0xffff; + } + + buf1 = srcBuffer.data(); + sz1 = srcBuffer.size(); + buf2 = nullptr; + sz2 = 0; + } if (firstChannel) { for (size_t s = 0; s < sz1; s++) diff --git a/Core/HLE/__sceAudio.h b/Core/HLE/__sceAudio.h index 39f83d8401..c836bd3f19 100644 --- a/Core/HLE/__sceAudio.h +++ b/Core/HLE/__sceAudio.h @@ -36,6 +36,7 @@ void __AudioDoState(PointerWrap &p); void __AudioUpdate(bool resetRecording = false); void __AudioShutdown(); void __AudioSetOutputFrequency(int freq); +void __AudioSetSRCFrequency(int freq); // May return SCE_ERROR_AUDIO_CHANNEL_BUSY if buffer too large u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking); diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index 6fd437bad6..f3c355b655 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -248,10 +248,10 @@ static u32 sceAudioChRelease(u32 chan) { ERROR_LOG(SCEAUDIO, "sceAudioChRelease(%i) - channel not reserved", chan); return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED; } - DEBUG_LOG(SCEAUDIO, "sceAudioChRelease(%i)", chan); + // TODO: Does this error if busy? chans[chan].reset(); chans[chan].reserved = false; - return 1; + return hleLogSuccessI(SCEAUDIO, 0); } static u32 sceAudioSetChannelDataLen(u32 chan, u32 len) { @@ -329,6 +329,7 @@ static u32 sceAudioOutput2Reserve(u32 sampleCount) { chan.sampleCount = sampleCount; chan.format = PSP_AUDIO_FORMAT_STEREO; chan.reserved = true; + __AudioSetSRCFrequency(0); return hleLogSuccessI(SCEAUDIO, 0); } @@ -339,11 +340,15 @@ static u32 sceAudioOutput2OutputBlocking(u32 vol, u32 dataPtr) { } auto &chan = chans[PSP_AUDIO_CHANNEL_OUTPUT2]; + if (!chan.reserved) { + return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved"); + } + chan.leftVolume = vol; chan.rightVolume = vol; chan.sampleAddress = dataPtr; - hleEatCycles(10000); + hleEatCycles(10000); int result = __AudioEnqueue(chan, PSP_AUDIO_CHANNEL_OUTPUT2, true); if (result < 0) return hleLogError(SCEAUDIO, result); @@ -351,22 +356,25 @@ static u32 sceAudioOutput2OutputBlocking(u32 vol, u32 dataPtr) { } static u32 sceAudioOutput2ChangeLength(u32 sampleCount) { - if (!chans[PSP_AUDIO_CHANNEL_OUTPUT2].reserved) { - ERROR_LOG(SCEAUDIO, "sceAudioOutput2ChangeLength(%08x) - channel not reserved ", sampleCount); - return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED; + auto &chan = chans[PSP_AUDIO_CHANNEL_OUTPUT2]; + if (!chan.reserved) { + return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved"); } - DEBUG_LOG(SCEAUDIO, "sceAudioOutput2ChangeLength(%08x)", sampleCount); - chans[PSP_AUDIO_CHANNEL_OUTPUT2].sampleCount = sampleCount; - return 0; + chan.sampleCount = sampleCount; + return hleLogSuccessI(SCEAUDIO, 0); } static u32 sceAudioOutput2GetRestSample() { - if (!chans[PSP_AUDIO_CHANNEL_OUTPUT2].reserved) { - ERROR_LOG(SCEAUDIO, "sceAudioOutput2GetRestSample() - channel not reserved "); - return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED; + auto &chan = chans[PSP_AUDIO_CHANNEL_OUTPUT2]; + if (!chan.reserved) { + return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved"); } - DEBUG_LOG(SCEAUDIO, "sceAudioOutput2GetRestSample()"); - return (u32) chans[PSP_AUDIO_CHANNEL_OUTPUT2].sampleQueue.size() / 2; + u32 size = (u32)chan.sampleQueue.size() / 2; + if (size > chan.sampleCount) { + // If ChangeLength reduces the size, it still gets output but this return is clamped. + size = chan.sampleCount; + } + return hleLogSuccessI(SCEAUDIO, size); } static u32 sceAudioOutput2Release() { @@ -382,6 +390,7 @@ static u32 sceAudioOutput2Release() { } static u32 sceAudioSetFrequency(u32 freq) { + // TODO: Not available from user code. if (freq == 44100 || freq == 48000) { INFO_LOG(SCEAUDIO, "sceAudioSetFrequency(%08x)", freq); __AudioSetOutputFrequency(freq); @@ -424,10 +433,8 @@ static u32 sceAudioSRCChReserve(u32 sampleCount, u32 freq, u32 format) { chan.reserved = true; chan.sampleCount = sampleCount; chan.format = format == 2 ? PSP_AUDIO_FORMAT_STEREO : PSP_AUDIO_FORMAT_MONO; - // TODO: Zero probably means don't change? Or it means default? - if (freq != 0) { - __AudioSetOutputFrequency(freq); - } + // Zero means default to 44.1kHz. + __AudioSetSRCFrequency(freq); return hleLogSuccessI(SCEAUDIO, 0); } @@ -449,6 +456,10 @@ static u32 sceAudioSRCOutputBlocking(u32 vol, u32 buf) { } auto &chan = chans[PSP_AUDIO_CHANNEL_SRC]; + if (!chan.reserved) { + return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved"); + } + chan.leftVolume = vol; chan.rightVolume = vol; chan.sampleAddress = buf; @@ -511,7 +522,7 @@ const HLEFunction sceAudio[] = {0X95FD0C2D, &WrapU_UU, "sceAudioChangeChannelConfig", 'x', "ii" }, {0XB7E1D8E7, &WrapU_UUU, "sceAudioChangeChannelVolume", 'x', "ixx" }, - // Not sure about the point of these, maybe like traditional but with ability to do sample rate conversion? + // Like Output2, but with ability to do sample rate conversion. {0X38553111, &WrapU_UUU, "sceAudioSRCChReserve", 'x', "iii" }, {0X5C37C0AE, &WrapU_V, "sceAudioSRCChRelease", 'x', "" }, {0XE0727056, &WrapU_UU, "sceAudioSRCOutputBlocking", 'x', "xx" }, diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 8116388cf6..26e9af5c4f 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -1626,6 +1626,7 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str if (param_argp) delete[] param_argp; if (param_key) delete[] param_key; } + __KernelShutdown(); return false; } diff --git a/Core/HLE/sceKernelTime.cpp b/Core/HLE/sceKernelTime.cpp index c656a3f8ce..2e16ba9237 100644 --- a/Core/HLE/sceKernelTime.cpp +++ b/Core/HLE/sceKernelTime.cpp @@ -188,3 +188,7 @@ std::string KernelTimeNowFormatted() { std::string timestamp = StringFromFormat("%04d-%02d-%02d_%02d-%02d-%02d", years, months, days, hours, minutes, seconds); return timestamp; } + +void KernelTimeSetBase(int64_t seconds) { + start_time = (time_t)seconds; +} diff --git a/Core/HLE/sceKernelTime.h b/Core/HLE/sceKernelTime.h index d973c20d40..049ab1252e 100644 --- a/Core/HLE/sceKernelTime.h +++ b/Core/HLE/sceKernelTime.h @@ -28,6 +28,7 @@ int sceKernelSysClock2USecWide(u32 lowClock, u32 highClock, u32 lowPtr, u32 high u64 sceKernelUSec2SysClockWide(u32 usec); u32 sceKernelLibcClock(); std::string KernelTimeNowFormatted(); +void KernelTimeSetBase(int64_t seconds); void __KernelTimeInit(); void __KernelTimeDoState(PointerWrap &p); diff --git a/Core/HLE/sceRtc.cpp b/Core/HLE/sceRtc.cpp index b5b4cfe807..1809430d1c 100644 --- a/Core/HLE/sceRtc.cpp +++ b/Core/HLE/sceRtc.cpp @@ -138,6 +138,10 @@ static time_t rtc_timegm(struct tm *tm) #endif +static void RtcUpdateBaseTicks() { + rtcBaseTicks = 1000000ULL * rtcBaseTime.tv_sec + rtcBaseTime.tv_usec + rtcMagicOffset; +} + void __RtcInit() { // This is the base time, the only case we use gettimeofday() for. @@ -147,7 +151,7 @@ void __RtcInit() rtcBaseTime.tv_sec = tv.tv_sec; rtcBaseTime.tv_usec = 0; // Precalculate the current time in microseconds (rtcMagicOffset is offset to 1970.) - rtcBaseTicks = 1000000ULL * rtcBaseTime.tv_sec + rtcBaseTime.tv_usec + rtcMagicOffset; + RtcUpdateBaseTicks(); } void __RtcDoState(PointerWrap &p) @@ -158,7 +162,7 @@ void __RtcDoState(PointerWrap &p) p.Do(rtcBaseTime); // Update the precalc, pointless to savestate this as it's just based on the other value. - rtcBaseTicks = 1000000ULL * rtcBaseTime.tv_sec + rtcBaseTime.tv_usec + rtcMagicOffset; + RtcUpdateBaseTicks(); } void __RtcTimeOfDay(PSPTimeval *tv) @@ -171,6 +175,19 @@ void __RtcTimeOfDay(PSPTimeval *tv) tv->tv_usec = adjustedUs % 1000000UL; } +int32_t RtcBaseTime(int32_t *micro) { + if (micro) { + *micro = rtcBaseTime.tv_usec; + } + return rtcBaseTime.tv_sec; +} + +void RtcSetBaseTime(int32_t seconds, int32_t micro) { + rtcBaseTime.tv_sec = seconds; + rtcBaseTime.tv_usec = micro; + RtcUpdateBaseTicks(); +} + static void __RtcTmToPspTime(ScePspDateTime &t, const tm *val) { t.year = val->tm_year + 1900; diff --git a/Core/HLE/sceRtc.h b/Core/HLE/sceRtc.h index facb4b2d7c..0301da3b19 100644 --- a/Core/HLE/sceRtc.h +++ b/Core/HLE/sceRtc.h @@ -27,6 +27,8 @@ struct PSPTimeval { }; void __RtcTimeOfDay(PSPTimeval *tv); +int32_t RtcBaseTime(int32_t *micro = nullptr); +void RtcSetBaseTime(int32_t seconds, int32_t micro = 0); void Register_sceRtc(); void __RtcInit(); diff --git a/Core/HLE/sceVaudio.cpp b/Core/HLE/sceVaudio.cpp index ca344110ca..e3749d0680 100644 --- a/Core/HLE/sceVaudio.cpp +++ b/Core/HLE/sceVaudio.cpp @@ -56,7 +56,7 @@ static u32 sceVaudioChReserve(int sampleCount, int freq, int format) { chans[PSP_AUDIO_CHANNEL_VAUDIO].leftVolume = 0; chans[PSP_AUDIO_CHANNEL_VAUDIO].rightVolume = 0; vaudioReserved = true; - __AudioSetOutputFrequency(freq); + __AudioSetSRCFrequency(freq); return 0; } diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index f17de4fe61..8bf58c3f4c 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -293,4 +293,6 @@ void StereoResampler::DoState(PointerWrap &p) { auto s = p.Section("resampler", 1); if (!s) return; + if (p.mode == p.MODE_READ) + Clear(); } diff --git a/Core/Loaders.h b/Core/Loaders.h index 527fa52f5b..690376c73b 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -54,9 +54,8 @@ enum class IdentifiedFileType { UNKNOWN, }; - -class FileLoader { // NB: It is a REQUIREMENT that implementations of this class are entirely thread safe! +class FileLoader { public: enum class Flags { NONE, diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index ccf8a26b99..9b4f091f5a 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -2202,7 +2202,7 @@ namespace MIPSInt ReadVector(s.f, sz, vs); ApplySwizzleS(s.f, sz); - ReadVector(t.f, sz, vs); + ReadVector(t.f, sz, vt); ApplySwizzleT(t.f, sz); // Swizzle does apply to the value read as an integer. u8 exp = (u8)(127 + t.i[0]); diff --git a/Core/Replay.cpp b/Core/Replay.cpp index e35458fb26..0e76c2c481 100644 --- a/Core/Replay.cpp +++ b/Core/Replay.cpp @@ -24,6 +24,8 @@ #include "Core/Replay.h" #include "Core/FileSystems/FileSystem.h" #include "Core/HLE/sceCtrl.h" +#include "Core/HLE/sceKernelTime.h" +#include "Core/HLE/sceRtc.h" enum class ReplayState { IDLE, @@ -31,19 +33,42 @@ enum class ReplayState { SAVE, }; +// Overall structure of file format: +// +// - ReplayFileHeader with basic data about replay (mostly timestamp for sync.) +// - An indeterminate sequence of events: +// - ReplayItemHeader (primary event details) +// - Side data of bytes listed in header, if SIDEDATA flag set on action. +// +// The header doesn't say how long the replay is, because new events are +// appended to the file as they occur. It is usually near, and always less than: +// +// (fileSize - sizeof(ReplayFileHeader)) / sizeof(ReplayItemHeader) + // File data formats below. #pragma pack(push, 1) +static const char *REPLAY_MAGIC = "PPREPLAY"; +static const int REPLAY_VERSION_MIN = 1; +static const int REPLAY_VERSION_CURRENT = 1; + +struct ReplayFileHeader { + char magic[8]; + u32_le version = REPLAY_VERSION_CURRENT; + u32_le reserved[3]{}; + u64_le rtcBaseSeconds; +}; + struct ReplayItemHeader { ReplayAction action; - uint64_t timestamp; + u64_le timestamp; union { - uint32_t buttons; + u32_le buttons; uint8_t analog[2][2]; - uint32_t result; - uint64_t result64; + u32_le result; + u64_le result64; // NOTE: Certain action types have data, always sized by this/result. - uint32_t size; + u32_le size; }; ReplayItemHeader(ReplayAction a, uint64_t t) { @@ -68,14 +93,14 @@ static const int REPLAY_MAX_FILENAME = 256; struct ReplayFileInfo { char filename[REPLAY_MAX_FILENAME]{}; - int64_t size = 0; - uint16_t access = 0; + s64_le size = 0; + u16_le access = 0; uint8_t exists = 0; uint8_t isDirectory = 0; - int64_t atime = 0; - int64_t ctime = 0; - int64_t mtime = 0; + s64_le atime = 0; + s64_le ctime = 0; + s64_le mtime = 0; }; #pragma pack(pop) @@ -93,6 +118,7 @@ static std::vector replayItems; static size_t replayExecPos = 0; static bool replaySaveWroteHeader = false; static ReplayState replayState = ReplayState::IDLE; +static bool replaySawGameDirWrite = false; static size_t replayCtrlPos = 0; static uint32_t lastButtons = 0; @@ -101,8 +127,6 @@ static uint8_t lastAnalog[2][2]{}; static size_t replayDiskPos = 0; static bool diskFailed = false; -// TODO: File format either needs rtc and rand seed, or must be paired with a save state. - void ReplayExecuteBlob(const std::vector &data) { ReplayAbort(); @@ -145,28 +169,52 @@ bool ReplayExecuteFile(const std::string &filename) { return false; } - // TODO: Header handling. - // Include initial rand state or timestamp? - - // TODO: Maybe stream instead. - size_t sz = File::GetFileSize(fp); - if (sz == 0) { - ERROR_LOG(SYSTEM, "Empty replay data"); - fclose(fp); - return false; - } - std::vector data; - data.resize(sz); + auto loadData = [&]() { + // TODO: Maybe stream instead. + size_t sz = File::GetFileSize(fp); + if (sz <= sizeof(ReplayFileHeader)) { + ERROR_LOG(SYSTEM, "Empty replay data"); + return false; + } - if (fread(&data[0], sz, 1, fp) != 1) { - ERROR_LOG(SYSTEM, "Could not read replay data"); + ReplayFileHeader fh; + if (fread(&fh, sizeof(fh), 1, fp) != 1) { + ERROR_LOG(SYSTEM, "Could not read replay file header"); + return false; + } + sz -= sizeof(fh); + + if (memcmp(fh.magic, REPLAY_MAGIC, sizeof(fh.magic)) != 0) { + ERROR_LOG(SYSTEM, "Replay header corrupt"); + return false; + } + + if (fh.version < REPLAY_VERSION_MIN) { + ERROR_LOG(SYSTEM, "Replay version %d unsupported", fh.version); + return false; + } else if (fh.version > REPLAY_VERSION_CURRENT) { + WARN_LOG(SYSTEM, "Replay version %d scary and futuristic, trying anyway", fh.version); + } + + data.resize(sz); + + if (fread(&data[0], sz, 1, fp) != 1) { + ERROR_LOG(SYSTEM, "Could not read replay data"); + return false; + } + + return true; + }; + + if (loadData()) { fclose(fp); - return false; + ReplayExecuteBlob(data); + return true; } - ReplayExecuteBlob(data); - return true; + fclose(fp); + return false; } bool ReplayHasMoreEvents() { @@ -219,13 +267,18 @@ bool ReplayFlushFile(const std::string &filename) { return false; } - // TODO: Header handling. - // Include initial rand state or timestamp? - replaySaveWroteHeader = true; + bool success = true; + if (!replaySaveWroteHeader) { + ReplayFileHeader fh; + memcpy(fh.magic, REPLAY_MAGIC, sizeof(fh.magic)); + fh.rtcBaseSeconds = RtcBaseTime(); + + success = fwrite(&fh, sizeof(fh), 1, fp) == 1; + replaySaveWroteHeader = true; + } size_t c = replayItems.size(); - bool success = true; - if (c != 0) { + if (success && c != 0) { // TODO: Maybe stream instead. std::vector data; ReplayFlushBlob(&data); @@ -247,6 +300,7 @@ void ReplayAbort() { replayExecPos = 0; replaySaveWroteHeader = false; replayState = ReplayState::IDLE; + replaySawGameDirWrite = false; replayCtrlPos = 0; lastButtons = 0; @@ -387,7 +441,12 @@ uint64_t ReplayApplyDisk64(ReplayAction action, uint64_t result, uint64_t t) { } } -uint32_t ReplayApplyDiskRead(void *data, uint32_t readSize, uint32_t dataSize, uint64_t t) { +uint32_t ReplayApplyDiskRead(void *data, uint32_t readSize, uint32_t dataSize, bool inGameDir, uint64_t t) { + // Ignore PSP/GAME reads if we haven't seen a write there. + if (inGameDir && !replaySawGameDirWrite) { + return readSize; + } + switch (replayState) { case ReplayState::EXECUTE: { @@ -414,6 +473,23 @@ uint32_t ReplayApplyDiskRead(void *data, uint32_t readSize, uint32_t dataSize, u } } +uint64_t ReplayApplyDiskWrite(const void *data, uint64_t writeSize, uint64_t dataSize, bool *diskFull, bool inGameDir, uint64_t t) { + switch (replayState) { + case ReplayState::EXECUTE: + case ReplayState::SAVE: + // Never let the disk return full during replay. + if (diskFull) + *diskFull = false; + if (inGameDir) + replaySawGameDirWrite = true; + return writeSize; + + case ReplayState::IDLE: + default: + return writeSize; + } +} + static int64_t ConvertFromTm(const struct tm *t) { // Remember, mktime modifies. struct tm copy = *t; diff --git a/Core/Replay.h b/Core/Replay.h index 3c91ce6524..bcbf7dc6e0 100644 --- a/Core/Replay.h +++ b/Core/Replay.h @@ -46,7 +46,7 @@ enum class ReplayAction : uint8_t { struct PSPFileInfo; -// Replay from data in memory. +// Replay from data in memory. Does not manipulate base time / RNG state. void ReplayExecuteBlob(const std::vector &data); // Replay from data in a file. Returns false if invalid. bool ReplayExecuteFile(const std::string &filename); @@ -68,6 +68,7 @@ void ReplayAbort(); void ReplayApplyCtrl(uint32_t &buttons, uint8_t analog[2][2], uint64_t t); uint32_t ReplayApplyDisk(ReplayAction action, uint32_t result, uint64_t t); uint64_t ReplayApplyDisk64(ReplayAction action, uint64_t result, uint64_t t); -uint32_t ReplayApplyDiskRead(void *data, uint32_t readSize, uint32_t dataSize, uint64_t t); +uint32_t ReplayApplyDiskRead(void *data, uint32_t readSize, uint32_t dataSize, bool inGameDir, uint64_t t); +uint64_t ReplayApplyDiskWrite(const void *data, uint64_t writeSize, uint64_t dataSize, bool *diskFull, bool inGameDir, uint64_t t); PSPFileInfo ReplayApplyDiskFileInfo(const PSPFileInfo &data, uint64_t t); std::vector ReplayApplyDiskListing(const std::vector &data, uint64_t t); diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 864a16e5a0..909f798a73 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -16,6 +16,8 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include +#include #include #include #include @@ -30,6 +32,7 @@ #include "Common/Log.h" #include "Common/FileUtil.h" +#include "Common/StringUtils.h" #include "Core/Config.h" #include "Core/System.h" #include "Core/Util/GameManager.h" @@ -58,7 +61,7 @@ bool GameManager::IsGameInstalled(std::string name) { return File::Exists(pspGame + name); } -bool GameManager::DownloadAndInstall(std::string storeZipUrl) { +bool GameManager::DownloadAndInstall(std::string storeFileUrl) { if (curDownload_.get() != 0) { ERROR_LOG(HLE, "Can only process one download at a time"); return false; @@ -69,7 +72,7 @@ bool GameManager::DownloadAndInstall(std::string storeZipUrl) { } std::string filename = GetTempFilename(); - curDownload_ = g_DownloadManager.StartDownload(storeZipUrl, filename); + curDownload_ = g_DownloadManager.StartDownload(storeFileUrl, filename); return true; } @@ -88,114 +91,241 @@ bool GameManager::Uninstall(std::string name) { return false; } std::string gameDir = GetSysDirectory(DIRECTORY_GAME) + name; - INFO_LOG(HLE, "Deleting %s", gameDir.c_str()); + INFO_LOG(HLE, "Deleting '%s'", gameDir.c_str()); if (!File::Exists(gameDir)) { - ERROR_LOG(HLE, "Game %s not installed, cannot uninstall", name.c_str()); + ERROR_LOG(HLE, "Game '%s' not installed, cannot uninstall", name.c_str()); return false; } bool success = File::DeleteDirRecursively(gameDir); if (success) { - INFO_LOG(HLE, "Successfully deleted game %s", name.c_str()); + INFO_LOG(HLE, "Successfully deleted game '%s'", name.c_str()); g_Config.CleanRecent(); return true; } else { - ERROR_LOG(HLE, "Failed to delete game %s", name.c_str()); + ERROR_LOG(HLE, "Failed to delete game '%s'", name.c_str()); return false; } } void GameManager::Update() { if (curDownload_.get() && curDownload_->Done()) { - INFO_LOG(HLE, "Download completed! Status = %i", curDownload_->ResultCode()); - std::string zipName = curDownload_->outfile(); + INFO_LOG(HLE, "Download completed! Status = %d", curDownload_->ResultCode()); + std::string fileName = curDownload_->outfile(); if (curDownload_->ResultCode() == 200) { - if (!File::Exists(zipName)) { - ERROR_LOG(HLE, "Downloaded file %s does not exist :(", zipName.c_str()); + if (!File::Exists(fileName)) { + ERROR_LOG(HLE, "Downloaded file '%s' does not exist :(", fileName.c_str()); curDownload_.reset(); return; } // Game downloaded to temporary file - install it! - InstallGameOnThread(zipName, true); + InstallGameOnThread(curDownload_->url(), fileName, true); } else { - ERROR_LOG(HLE, "Expected HTTP status code 200, got status code %i. Install cancelled, deleting partial file.", curDownload_->ResultCode()); - File::Delete(zipName.c_str()); + ERROR_LOG(HLE, "Expected HTTP status code 200, got status code %d. Install cancelled, deleting partial file '%s'", + curDownload_->ResultCode(), fileName.c_str()); + File::Delete(fileName.c_str()); } curDownload_.reset(); } } -bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { +void countSlashes(std::string fileName, int *slashLocation, int *slashCount) { + *slashCount = 0; + int lastSlashLocation = -1; + *slashLocation = -1; + for (size_t i = 0; i < fileName.size(); i++) { + if (fileName[i] == '/') { + (*slashCount)++; + *slashLocation = lastSlashLocation; + lastSlashLocation = (int)i; + } + } +} + +ZipFileContents DetectZipFileContents(std::string fileName, ZipFileInfo *info) { + int error = 0; +#ifdef _WIN32 + struct zip *z = zip_open(ConvertUTF8ToWString(fileName).c_str(), 0, &error); +#else + struct zip *z = zip_open(fileName.c_str(), 0, &error); +#endif + if (!z) { + return ZipFileContents::UNKNOWN; + } + ZipFileContents retVal = DetectZipFileContents(z, info); + zip_close(z); + return retVal; +} + +inline char asciitolower(char in) { + if (in <= 'Z' && in >= 'A') + return in - ('Z' - 'z'); + return in; +} + +ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info) { + int numFiles = zip_get_num_files(z); + + // Verify that this is a PSP zip file with the correct layout. We also try + // to detect simple zipped ISO files, those we'll just "install" to the current + // directory of the Games tab (where else?). + bool isPSPMemstickGame = false; + bool isZippedISO = false; + int stripChars = 0; + int isoFileIndex = -1; + + for (int i = 0; i < numFiles; i++) { + const char *fn = zip_get_name(z, i, 0); + std::string zippedName = fn; + std::transform(zippedName.begin(), zippedName.end(), zippedName.begin(), + [](unsigned char c) { return asciitolower(c); }); // Not using std::tolower to avoid Turkish I->ı conversion. + if (zippedName.find("eboot.pbp") != std::string::npos) { + int slashCount = 0; + int slashLocation = -1; + countSlashes(zippedName, &slashLocation, &slashCount); + if (slashCount >= 1 && (!isPSPMemstickGame || slashLocation < stripChars + 1)) { + stripChars = slashLocation + 1; + isPSPMemstickGame = true; + } else { + INFO_LOG(HLE, "Wrong number of slashes (%i) in '%s'", slashCount, fn); + } + } else if (endsWith(zippedName, ".iso") || endsWith(zippedName, ".cso")) { + int slashCount = 0; + int slashLocation = -1; + countSlashes(zippedName, &slashLocation, &slashCount); + if (slashCount <= 1) { + // We only do this if the ISO file is in the root or one level down. + isZippedISO = true; + isoFileIndex = i; + } + } + } + + info->stripChars = stripChars; + info->numFiles = numFiles; + info->isoFileIndex = isoFileIndex; + // If a ZIP is detected as both, let's let the memstick game interpretation prevail. + if (isPSPMemstickGame) { + return ZipFileContents::PSP_GAME_DIR; + } else if (isZippedISO) { + return ZipFileContents::ISO_FILE; + } else { + return ZipFileContents::UNKNOWN; + } +} + +bool GameManager::InstallGame(const std::string &url, const std::string &fileName, bool deleteAfter) { if (installInProgress_) { ERROR_LOG(HLE, "Cannot have two installs in progress at the same time"); return false; } - if (!File::Exists(zipfile)) { - ERROR_LOG(HLE, "ZIP file %s doesn't exist", zipfile.c_str()); + if (!File::Exists(fileName)) { + ERROR_LOG(HLE, "Game file '%s' doesn't exist", fileName.c_str()); return false; } + // Examine the URL to guess out what we're installing. + if (endsWithNoCase(url, ".cso") || endsWithNoCase(url, ".iso")) { + // It's a raw ISO or CSO file. We just copy it to the destination. + std::string shortFilename = GetFilenameFromPath(url); + return InstallRawISO(fileName, shortFilename, deleteAfter); + } + I18NCategory *sy = GetI18NCategory("System"); installInProgress_ = true; std::string pspGame = GetSysDirectory(DIRECTORY_GAME); - INFO_LOG(HLE, "Installing %s into %s", zipfile.c_str(), pspGame.c_str()); - int error; + INFO_LOG(HLE, "Installing '%s' into '%s'", fileName.c_str(), pspGame.c_str()); + int error = 0; #ifdef _WIN32 - struct zip *z = zip_open(ConvertUTF8ToWString(zipfile).c_str(), 0, &error); + struct zip *z = zip_open(ConvertUTF8ToWString(fileName).c_str(), 0, &error); #else - struct zip *z = zip_open(zipfile.c_str(), 0, &error); + struct zip *z = zip_open(fileName.c_str(), 0, &error); #endif if (!z) { - ERROR_LOG(HLE, "Failed to open ZIP file %s, error code=%i", zipfile.c_str(), error); + ERROR_LOG(HLE, "Failed to open ZIP file '%s', error code=%i", fileName.c_str(), error); return false; } - int numFiles = zip_get_num_files(z); - - // First, find all the directories, and precreate them before we fill in with files. - // Also, verify that this is a PSP zip file with the correct layout. - bool isPSP = false; - int stripChars = 0; - - for (int i = 0; i < numFiles; i++) { - const char *fn = zip_get_name(z, i, 0); - std::string zippedName = fn; - if (zippedName.find("EBOOT.PBP") != std::string::npos) { - int slashCount = 0; - int lastSlashLocation = -1; - int slashLocation = -1; - for (size_t i = 0; i < zippedName.size(); i++) { - if (zippedName[i] == '/') { - slashCount++; - slashLocation = lastSlashLocation; - lastSlashLocation = (int)i; - } - } - if (slashCount >= 1 && (!isPSP || slashLocation < stripChars + 1)) { - stripChars = slashLocation + 1; - isPSP = true; - } else { - INFO_LOG(HLE, "Wrong number of slashes (%i) in %s", slashCount, zippedName.c_str()); - } - } - } - - if (!isPSP) { + ZipFileInfo info; + ZipFileContents contents = DetectZipFileContents(z, &info); + switch (contents) { + case ZipFileContents::PSP_GAME_DIR: + // InstallMemstickGame contains code to close z. + return InstallMemstickGame(z, fileName, pspGame, info.numFiles, info.stripChars, deleteAfter); + case ZipFileContents::ISO_FILE: + return InstallZippedISO(z, info.isoFileIndex, fileName, deleteAfter); + default: ERROR_LOG(HLE, "File not a PSP game, no EBOOT.PBP found."); installProgress_ = 0.0f; installInProgress_ = false; installError_ = sy->T("Not a PSP game"); InstallDone(); if (deleteAfter) - File::Delete(zipfile); + File::Delete(fileName); return false; } +} - size_t allBytes = 0, bytesCopied = 0; +bool GameManager::ExtractFile(struct zip *z, int file_index, std::string outFilename, size_t *bytesCopied, size_t allBytes) { + struct zip_stat zstat; + zip_stat_index(z, file_index, 0, &zstat); + size_t size = zstat.size; - // Create all the directories in one pass + // Don't spam the log. + if (file_index < 10) { + INFO_LOG(HLE, "Writing %d bytes to '%s'", (int)size, outFilename.c_str()); + } + + zip_file *zf = zip_fopen_index(z, file_index, 0); + FILE *f = File::OpenCFile(outFilename, "wb"); + if (f) { + size_t pos = 0; + const size_t blockSize = 1024 * 128; + u8 *buffer = new u8[blockSize]; + while (pos < size) { + size_t readSize = std::min(blockSize, size - pos); + ssize_t retval = zip_fread(zf, buffer, readSize); + if (retval < readSize) { + ERROR_LOG(HLE, "Failed to read %d bytes from zip (%d) - archive corrupt?", (int)readSize, (int)retval); + delete[] buffer; + fclose(f); + zip_fclose(zf); + File::Delete(outFilename.c_str()); + return false; + } + size_t written = fwrite(buffer, 1, readSize, f); + if (written != readSize) { + ERROR_LOG(HLE, "Wrote %d bytes out of %d - Disk full?", (int)written, (int)readSize); + delete[] buffer; + fclose(f); + zip_fclose(zf); + File::Delete(outFilename.c_str()); + return false; + } + pos += readSize; + + *bytesCopied += readSize; + installProgress_ = (float)*bytesCopied / (float)allBytes; + } + zip_fclose(zf); + fclose(f); + delete[] buffer; + return true; + } else { + ERROR_LOG(HLE, "Failed to open file for writing"); + return false; + } +} + +bool GameManager::InstallMemstickGame(struct zip *z, std::string zipfile, std::string pspGame, int numFiles, int stripChars, bool deleteAfter) { + size_t allBytes = 0; + size_t bytesCopied = 0; + + I18NCategory *sy = GetI18NCategory("System"); + + // Create all the directories first in one pass std::set createdDirs; for (int i = 0; i < numFiles; i++) { const char *fn = zip_get_name(z, i, 0); @@ -224,53 +354,16 @@ bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { // Note that we do NOT write files that are not in a directory, to avoid random // README files etc. if (strchr(fn, '/') != 0) { - struct zip_stat zstat; - zip_stat_index(z, i, 0, &zstat); - size_t size = zstat.size; - fn += stripChars; - std::string outFilename = pspGame + fn; bool isDir = *outFilename.rbegin() == '/'; if (isDir) continue; - if (i < 10) { - INFO_LOG(HLE, "Writing %i bytes to %s", (int)size, outFilename.c_str()); - } - - zip_file *zf = zip_fopen_index(z, i, 0); - FILE *f = File::OpenCFile(outFilename, "wb"); - if (f) { - size_t pos = 0; - const size_t blockSize = 1024 * 128; - u8 *buffer = new u8[blockSize]; - while (pos < size) { - size_t bs = std::min(blockSize, size - pos); - zip_fread(zf, buffer, bs); - size_t written = fwrite(buffer, 1, bs, f); - if (written != bs) { - ERROR_LOG(HLE, "Wrote %i bytes out of %i - Disk full?", (int)written, (int)bs); - delete [] buffer; - buffer = 0; - fclose(f); - zip_fclose(zf); - File::Delete(outFilename.c_str()); - // Yes it's a goto. Sue me. I think it's appropriate here. - goto bail; - } - pos += bs; - - bytesCopied += bs; - installProgress_ = (float)bytesCopied / (float)allBytes; - // printf("Progress: %f\n", installProgress_); - } - zip_fclose(zf); - fclose(f); - createdFiles.push_back(outFilename); - delete [] buffer; + if (!ExtractFile(z, i, outFilename, &bytesCopied, allBytes)) { + goto bail; } else { - ERROR_LOG(HLE, "Failed to open file for writing"); + createdFiles.push_back(outFilename); } } } @@ -293,9 +386,7 @@ bail: installProgress_ = 0.0f; installInProgress_ = false; installError_ = sy->T("Storage full"); - if (deleteAfter) { - File::Delete(zipfile.c_str()); - } + // We don't delete the original in this case. Try to delete the files we created so far. for (size_t i = 0; i < createdFiles.size(); i++) { File::Delete(createdFiles[i].c_str()); } @@ -306,15 +397,64 @@ bail: return false; } -bool GameManager::InstallGameOnThread(std::string zipFile, bool deleteAfter) { +bool GameManager::InstallZippedISO(struct zip *z, int isoFileIndex, std::string zipfile, bool deleteAfter) { + // Let's place the output file in the currently selected Games directory. + + std::string fn = zip_get_name(z, isoFileIndex, 0); + size_t nameOffset = fn.rfind('/'); + if (nameOffset == std::string::npos) { + nameOffset = 0; + } else { + nameOffset++; + } + size_t allBytes = 1; + struct zip_stat zstat; + if (zip_stat_index(z, isoFileIndex, 0, &zstat) >= 0) { + allBytes += zstat.size; + } + + std::string outputISOFilename = g_Config.currentDirectory + "/" + fn.substr(nameOffset); + size_t bytesCopied = 0; + if (ExtractFile(z, isoFileIndex, outputISOFilename, &bytesCopied, allBytes)) { + ILOG("Successfully extracted ISO file to '%s'", outputISOFilename.c_str()); + } + zip_close(z); + if (deleteAfter) { + File::Delete(zipfile.c_str()); + } + + z = 0; + installProgress_ = 1.0f; + installInProgress_ = false; + installError_ = ""; + InstallDone(); + return true; +} + +bool GameManager::InstallGameOnThread(std::string url, std::string fileName, bool deleteAfter) { if (installInProgress_) { return false; } - installThread_.reset(new std::thread(std::bind(&GameManager::InstallGame, this, zipFile, deleteAfter))); + installThread_.reset(new std::thread(std::bind(&GameManager::InstallGame, this, url, fileName, deleteAfter))); installThread_->detach(); return true; } +bool GameManager::InstallRawISO(const std::string &file, const std::string &originalName, bool deleteAfter) { + std::string destPath = g_Config.currentDirectory + "/" + originalName; + // TODO: To save disk space, we should probably attempt a move first. + if (File::Copy(file, destPath)) { + if (deleteAfter) { + File::Delete(file.c_str()); + } + } + installProgress_ = 1.0f; + installInProgress_ = false; + installError_ = ""; + InstallDone(); + return true; +} + void GameManager::InstallDone() { if (installThread_.get() != 0) { installThread_.reset(); diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 55e22312ba..48062f76d7 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -31,6 +31,8 @@ enum class GameManagerState { INSTALLING, }; +struct zip; + class GameManager { public: GameManager(); @@ -64,11 +66,15 @@ public: } // Only returns false if there's already an installation in progress. - bool InstallGameOnThread(std::string zipFile, bool deleteAfter); + bool InstallGameOnThread(std::string url, std::string tempFileName, bool deleteAfter); private: - bool InstallGame(std::string zipfile, bool deleteAfter = false); + bool InstallGame(const std::string &url, const std::string &tempFileName, bool deleteAfter); + bool InstallMemstickGame(struct zip *z, std::string zipFile, std::string pspGame, int numFiles, int stripChars, bool deleteAfter); + bool InstallZippedISO(struct zip *z, int isoFileIndex, std::string zipfile, bool deleteAfter); + bool InstallRawISO(const std::string &zipFile, const std::string &originalName, bool deleteAfter); void InstallDone(); + bool ExtractFile(struct zip *z, int file_index, std::string outFilename, size_t *bytesCopied, size_t allBytes); std::string GetTempFilename() const; std::shared_ptr curDownload_; @@ -79,3 +85,18 @@ private: }; extern GameManager g_GameManager; + +enum class ZipFileContents { + UNKNOWN, + PSP_GAME_DIR, + ISO_FILE, +}; + +struct ZipFileInfo { + int numFiles; + int stripChars; // for PSP game + int isoFileIndex; // for ISO +}; + +ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info); +ZipFileContents DetectZipFileContents(std::string fileName, ZipFileInfo *info); diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 3b5d132231..a03fca92b0 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -784,9 +784,6 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, GEBu float v0 = 0.0f, v1 = 1.0f; MakePixelTexture(srcPixels, srcPixelFormat, srcStride, 512, 272, u1, v1); - struct CardboardSettings cardboardSettings; - GetCardboardSettings(&cardboardSettings); - // This might draw directly at the backbuffer (if so, applyPostShader is set) so if there's a post shader, we need to apply it here. // Should try to unify this path with the regular path somehow, but this simple solution works for most of the post shaders // (it always runs at output resolution so FXAA may look odd). @@ -812,18 +809,9 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, GEBu DrawTextureFlags flags = g_Config.iBufFilter == SCALE_LINEAR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST; flags = flags | DRAWTEX_TO_BACKBUFFER; - if (cardboardSettings.enabled) { - // Left Eye Image - SetViewport2D(cardboardSettings.leftEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags | DRAWTEX_KEEP_TEX); - // Right Eye Image - SetViewport2D(cardboardSettings.rightEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags); - } else { - // Fullscreen Image - SetViewport2D(0, 0, pixelWidth_, pixelHeight_); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags); - } + // Fullscreen Image + SetViewport2D(0, 0, pixelWidth_, pixelHeight_); + DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags); gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE); } @@ -868,9 +856,6 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { u32 offsetX = 0; u32 offsetY = 0; - CardboardSettings cardboardSettings; - GetCardboardSettings(&cardboardSettings); - VirtualFramebuffer *vfb = GetVFBAt(displayFramebufPtr_); if (!vfb) { // Let's search for a framebuf within this range. Note that we also look for @@ -986,19 +971,9 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { // flip V. if (needBackBufferYSwap_) std::swap(v0, v1); - if (cardboardSettings.enabled) { - // Left Eye Image - SetViewport2D(cardboardSettings.leftEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags | DRAWTEX_KEEP_TEX); - - // Right Eye Image - SetViewport2D(cardboardSettings.rightEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags); - } else { - // Fullscreen Image - SetViewport2D(0, 0, pixelWidth_, pixelHeight_); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags); - } + // Fullscreen Image + SetViewport2D(0, 0, pixelWidth_, pixelHeight_); + DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags); } else if (usePostShader_ && extraFBOs_.size() == 1 && !postShaderAtOutputResolution_) { // An additional pass, post-processing shader to the extra FBO. shaderManager_->DirtyLastShader(); // dirty lastShader_ @@ -1033,19 +1008,9 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { Bind2DShader(); flags = (!postShaderIsUpscalingFilter_ && g_Config.iBufFilter == SCALE_LINEAR) ? DRAWTEX_LINEAR : DRAWTEX_NEAREST; flags = flags | DRAWTEX_TO_BACKBUFFER; - if (g_Config.bEnableCardboard) { - // Left Eye Image - SetViewport2D(cardboardSettings.leftEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags | DRAWTEX_KEEP_TEX); - - // Right Eye Image - SetViewport2D(cardboardSettings.rightEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags); - } else { - // Fullscreen Image - SetViewport2D(0, 0, pixelWidth_, pixelHeight_); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags); - } + // Fullscreen Image + SetViewport2D(0, 0, pixelWidth_, pixelHeight_); + DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags); } else { shaderManager_->DirtyLastShader(); // dirty lastShader_ BEFORE drawing draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }); @@ -1061,19 +1026,9 @@ void FramebufferManagerCommon::CopyDisplayToOutput() { PostShaderUniforms uniforms{}; CalculatePostShaderUniforms(vfb->bufferWidth, vfb->bufferHeight, vfb->renderWidth, vfb->renderHeight, &uniforms); BindPostShader(uniforms); - if (g_Config.bEnableCardboard) { - // Left Eye Image - SetViewport2D(cardboardSettings.leftEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags | DRAWTEX_KEEP_TEX); - - // Right Eye Image - SetViewport2D(cardboardSettings.rightEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags); - } else { - // Fullscreen Image - SetViewport2D(0, 0, pixelWidth_, pixelHeight_); - DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags); - } + // Fullscreen Image + SetViewport2D(0, 0, pixelWidth_, pixelHeight_); + DrawActiveTexture(x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, uvRotation, flags); } } else if (useBufferedRendering_) { @@ -1843,27 +1798,6 @@ void FramebufferManagerCommon::CalculatePostShaderUniforms(int bufferWidth, int uniforms->video = textureCache_->VideoIsPlaying(); } -void FramebufferManagerCommon::GetCardboardSettings(CardboardSettings *cardboardSettings) { - // Calculate Cardboard Settings - float cardboardScreenScale = g_Config.iCardboardScreenSize / 100.0f; - float cardboardScreenWidth = pixelWidth_ / 2.0f * cardboardScreenScale; - float cardboardScreenHeight = pixelHeight_ / 2.0f * cardboardScreenScale; - float cardboardMaxXShift = (pixelWidth_ / 2.0f - cardboardScreenWidth) / 2.0f; - float cardboardUserXShift = g_Config.iCardboardXShift / 100.0f * cardboardMaxXShift; - float cardboardLeftEyeX = cardboardMaxXShift + cardboardUserXShift; - float cardboardRightEyeX = pixelWidth_ / 2.0f + cardboardMaxXShift - cardboardUserXShift; - float cardboardMaxYShift = pixelHeight_ / 2.0f - cardboardScreenHeight / 2.0f; - float cardboardUserYShift = g_Config.iCardboardYShift / 100.0f * cardboardMaxYShift; - float cardboardScreenY = cardboardMaxYShift + cardboardUserYShift; - - cardboardSettings->enabled = g_Config.bEnableCardboard; - cardboardSettings->leftEyeXPosition = cardboardLeftEyeX; - cardboardSettings->rightEyeXPosition = cardboardRightEyeX; - cardboardSettings->screenYPosition = cardboardScreenY; - cardboardSettings->screenWidth = cardboardScreenWidth; - cardboardSettings->screenHeight = cardboardScreenHeight; -} - Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u16 h, Draw::FBColorDepth depth) { u64 key = ((u64)reason << 48) | ((u64)depth << 32) | ((u32)w << 16) | h; auto it = tempFBOs_.find(key); @@ -2150,6 +2084,11 @@ void FramebufferManagerCommon::DownloadFramebufferForClut(u32 fb_address, u32 lo int w = std::min(pixels % vfb->fb_stride, (int)vfb->width); int h = std::min((pixels + vfb->fb_stride - 1) / vfb->fb_stride, (int)vfb->height); + if (w == 0 && h > 0) { + // Exactly aligned (means pixels == vfb->fb_stride, so modulus gave zero.) + w = std::min(vfb->fb_stride, (int)vfb->width); + } + // We might still have a pending draw to the fb in question, flush if so. FlushBeforeCopy(); diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 3f1d1a49a5..ce5e0c8aef 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -46,15 +46,6 @@ namespace Draw { class Framebuffer; } -struct CardboardSettings { - bool enabled; - float leftEyeXPosition; - float rightEyeXPosition; - float screenYPosition; - float screenWidth; - float screenHeight; -}; - class VulkanFBO; struct PostShaderUniforms { @@ -325,9 +316,6 @@ protected: virtual void Bind2DShader() = 0; virtual void BindPostShader(const PostShaderUniforms &uniforms) = 0; - // Cardboard Settings Calculator - void GetCardboardSettings(CardboardSettings *cardboardSettings); - bool UpdateSize(); void SetNumExtraFBOs(int num); diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index af8feef17b..53903c75c1 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -268,6 +268,7 @@ true false MultiThreaded + ProgramDatabase true @@ -296,6 +297,7 @@ USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_M_X64=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) MultiThreaded true + ProgramDatabase true @@ -324,6 +326,7 @@ USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_64=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) MultiThreaded true + ProgramDatabase true @@ -352,6 +355,7 @@ USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;WIN32;_ARCH_32=1;_LIB;NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions) MultiThreaded true + ProgramDatabase true diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 8b5c16af1d..db4b88d27a 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -720,6 +720,8 @@ u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, PSPPointerstate != PSP_GE_DL_STATE_PAUSED) return SCE_KERNEL_ERROR_INVALID_VALUE; currentList->state = PSP_GE_DL_STATE_QUEUED; + // Make sure we clear the signal so we don't try to pause it again. + currentList->signal = PSP_GE_SIGNAL_NONE; } dl.state = PSP_GE_DL_STATE_PAUSED; @@ -789,8 +791,7 @@ u32 GPUCommon::Continue() { if (currentList->state == PSP_GE_DL_STATE_PAUSED) { - if (!isbreak) - { + if (!isbreak) { // TODO: Supposedly this returns SCE_KERNEL_ERROR_BUSY in some case, previously it had // currentList->signal == PSP_GE_SIGNAL_HANDLER_PAUSE, but it doesn't reproduce. @@ -802,9 +803,10 @@ u32 GPUCommon::Continue() { // We have a list now, so it's not complete. drawCompleteTicks = (u64)-1; - } - else + } else { currentList->state = PSP_GE_DL_STATE_QUEUED; + currentList->signal = PSP_GE_SIGNAL_NONE; + } } else if (currentList->state == PSP_GE_DL_STATE_RUNNING) { @@ -2453,6 +2455,14 @@ void GPUCommon::InterruptEnd(int listid) { } dl.waitTicks = 0; __GeTriggerWait(GPU_SYNC_LIST, listid); + + // Make sure the list isn't still queued since it's now completed. + if (!dlQueue.empty()) { + if (listid == dlQueue.front()) + PopDLQueue(); + else + dlQueue.remove(listid); + } } ProcessDLQueue(); diff --git a/SDL/SDLGLGraphicsContext.cpp b/SDL/SDLGLGraphicsContext.cpp index 1717f61c63..f7e142f595 100644 --- a/SDL/SDLGLGraphicsContext.cpp +++ b/SDL/SDLGLGraphicsContext.cpp @@ -17,15 +17,12 @@ class GLRenderManager; #if defined(USING_EGL) // TODO: Move these into the class. -static EGLDisplay g_eglDisplay = NULL; -static EGLContext g_eglContext = NULL; -static EGLSurface g_eglSurface = NULL; -#ifdef USING_FBDEV -static EGLNativeDisplayType g_Display = NULL; -#else -static Display* g_Display = NULL; -#endif -static NativeWindowType g_Window = (NativeWindowType)NULL; +static EGLDisplay g_eglDisplay = EGL_NO_DISPLAY; +static EGLContext g_eglContext = nullptr; +static EGLSurface g_eglSurface = nullptr; +static EGLNativeDisplayType g_Display = nullptr; +static bool g_XDisplayOpen = false; +static EGLNativeWindowType g_Window = (EGLNativeWindowType)nullptr; int CheckEGLErrors(const char *file, int line) { EGLenum error; @@ -59,18 +56,82 @@ int CheckEGLErrors(const char *file, int line) { return 1; \ } -int8_t EGL_Open() { -#ifdef USING_FBDEV - g_Display = ((EGLNativeDisplayType)0); -#else - if ((g_Display = XOpenDisplay(NULL)) == NULL) - EGL_ERROR("Unable to get display!", false); -#endif - if ((g_eglDisplay = eglGetDisplay((NativeDisplayType)g_Display)) == EGL_NO_DISPLAY) +static bool EGL_OpenInit() { + if ((g_eglDisplay = eglGetDisplay(g_Display)) == EGL_NO_DISPLAY) { EGL_ERROR("Unable to create EGL display.", true); - if (eglInitialize(g_eglDisplay, NULL, NULL) != EGL_TRUE) + return false; + } + if (eglInitialize(g_eglDisplay, NULL, NULL) != EGL_TRUE) { EGL_ERROR("Unable to initialize EGL display.", true); - return 0; + eglTerminate(g_eglDisplay); + g_eglDisplay = EGL_NO_DISPLAY; + return false; + } + + return true; +} + +static int8_t EGL_Open(SDL_Window *window) { +#if defined(USING_FBDEV) + g_Display = (EGLNativeDisplayType)nullptr; + g_Window = (EGLNativeWindowType)nullptr; +#elif defined(__APPLE__) + g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr); + g_XDisplayOpen = g_Display != nullptr; + if (!g_XDisplayOpen) + EGL_ERROR("Unable to get display!", false); + g_Window = (EGLNativeWindowType)nullptr; +#else + // Get the SDL window native handle + SDL_SysWMinfo sysInfo{}; + SDL_VERSION(&sysInfo.version); + if (!SDL_GetWindowWMInfo(window, &sysInfo)) { + printf("ERROR: Unable to retrieve native window handle\n"); + g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr); + g_XDisplayOpen = g_Display != nullptr; + if (!g_XDisplayOpen) + EGL_ERROR("Unable to get display!", false); + g_Window = (EGLNativeWindowType)nullptr; + } else { + switch (sysInfo.subsystem) { + case SDL_SYSWM_X11: + g_Display = (EGLNativeDisplayType)sysInfo.info.x11.display; + g_Window = (EGLNativeWindowType)sysInfo.info.x11.window; + break; +#if defined(SDL_VIDEO_DRIVER_DIRECTFB) + case SDL_SYSWM_DIRECTFB: + g_Display = (EGLNativeDisplayType)EGL_DEFAULT_DISPLAY; + g_Window = (EGLNativeWindowType)sysInfo.info.dfb.surface; + break; +#endif +#if SDL_VERSION_ATLEAST(2, 0, 2) && defined(SDL_VIDEO_DRIVER_WAYLAND) + case SDL_SYSWM_WAYLAND: + g_Display = (EGLNativeDisplayType)sysInfo.info.wl.display; + g_Window = (EGLNativeWindowType)sysInfo.info.wl.shell_surface; + break; +#endif +#if SDL_VERSION_ATLEAST(2, 0, 5) && defined(SDL_VIDEO_DRIVER_VIVANTE) + case SDL_SYSWM_VIVANTE: + g_Display = (EGLNativeDisplayType)sysInfo.info.vivante.display; + g_Window = (EGLNativeWindowType)sysInfo.info.vivante.window; + break; +#endif + } + + if (!EGL_OpenInit()) { + // Let's try again with X11. + g_Display = (EGLNativeDisplayType)XOpenDisplay(nullptr); + g_XDisplayOpen = g_Display != nullptr; + if (!g_XDisplayOpen) + EGL_ERROR("Unable to get display!", false); + g_Window = (EGLNativeWindowType)nullptr; + } + } + +#endif + if (g_eglDisplay == EGL_NO_DISPLAY) + EGL_OpenInit(); + return g_eglDisplay == EGL_NO_DISPLAY ? 1 : 0; } #ifndef EGL_OPENGL_ES3_BIT_KHR @@ -193,15 +254,6 @@ int8_t EGL_Init(SDL_Window *window) { return 1; } -#if !defined(USING_FBDEV) && !defined(__APPLE__) - // Get the SDL window handle - SDL_SysWMinfo sysInfo; //Will hold our Window information - SDL_VERSION(&sysInfo.version); //Set SDL version - SDL_GetWindowWMInfo(window, &sysInfo); - g_Window = (NativeWindowType)sysInfo.info.x11.window; -#else - g_Window = (NativeWindowType)NULL; -#endif g_eglSurface = eglCreateWindowSurface(g_eglDisplay, eglConfig, g_Window, nullptr); if (g_eglSurface == EGL_NO_SURFACE) { EGL_ERROR("Unable to create EGL surface!", true); @@ -217,7 +269,7 @@ int8_t EGL_Init(SDL_Window *window) { } void EGL_Close() { - if (g_eglDisplay != NULL) { + if (g_eglDisplay != EGL_NO_DISPLAY) { eglMakeCurrent(g_eglDisplay, NULL, NULL, EGL_NO_CONTEXT); if (g_eglContext != NULL) { eglDestroyContext(g_eglDisplay, g_eglContext); @@ -226,13 +278,15 @@ void EGL_Close() { eglDestroySurface(g_eglDisplay, g_eglSurface); } eglTerminate(g_eglDisplay); - g_eglDisplay = NULL; + g_eglDisplay = EGL_NO_DISPLAY; } - if (g_Display != NULL) { + if (g_Display != nullptr) { #if !defined(USING_FBDEV) - XCloseDisplay(g_Display); + if (g_XDisplayOpen) + XCloseDisplay((Display *)g_Display); #endif - g_Display = NULL; + g_XDisplayOpen = false; + g_Display = nullptr; } g_eglSurface = NULL; g_eglContext = NULL; @@ -320,6 +374,10 @@ int SDLGLGraphicsContext::Init(SDL_Window *&window, int x, int y, int mode, std: SDL_ShowWindow(window); #ifdef USING_EGL + if (EGL_Open(window) != 0) { + printf("EGL_Open() failed\n"); + return 1; + } if (EGL_Init(window) != 0) { printf("EGL_Init() failed\n"); return 1; diff --git a/SDL/SDLGLGraphicsContext.h b/SDL/SDLGLGraphicsContext.h index c32d104814..651f7a534b 100644 --- a/SDL/SDLGLGraphicsContext.h +++ b/SDL/SDLGLGraphicsContext.h @@ -7,11 +7,6 @@ #include "gfx/gl_common.h" #include "Common/GraphicsContext.h" -// TODO: Move this to a better place. -#if defined(USING_EGL) -int8_t EGL_Open(); -#endif - class SDLGLGraphicsContext : public DummyGraphicsContext { public: SDLGLGraphicsContext() { @@ -54,5 +49,3 @@ private: SDL_GLContext glContext = nullptr; GLRenderManager *renderManager_ = nullptr; }; - - diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 08d4b7adb0..4c4a8677eb 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -426,12 +426,6 @@ int main(int argc, char *argv[]) { } } - // TODO: How do we get this into the GraphicsContext? -#ifdef USING_EGL - if (EGL_Open()) - return 1; -#endif - // Get the video info before doing anything else, so we don't get skewed resolution results. // TODO: support multiple displays correctly SDL_DisplayMode displayMode; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 25ff58b181..145e4fa86a 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -466,18 +466,6 @@ void GameSettingsScreen::CreateViews() { static const char *bufFilters[] = { "Linear", "Nearest", }; graphicsSettings->Add(new PopupMultiChoice(&g_Config.iBufFilter, gr->T("Screen Scaling Filter"), bufFilters, 1, ARRAY_SIZE(bufFilters), gr->GetName(), screenManager())); -#ifdef __ANDROID__ - graphicsSettings->Add(new ItemHeader(gr->T("Cardboard Settings", "Cardboard Settings"))); - CheckBox *cardboardMode = graphicsSettings->Add(new CheckBox(&g_Config.bEnableCardboard, gr->T("Enable Cardboard", "Enable Cardboard"))); - cardboardMode->SetDisabledPtr(&g_Config.bSoftwareRendering); - PopupSliderChoice * cardboardScreenSize = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iCardboardScreenSize, 30, 100, gr->T("Cardboard Screen Size", "Screen Size (in % of the viewport)"), 1, screenManager(), gr->T("% of viewport"))); - cardboardScreenSize->SetDisabledPtr(&g_Config.bSoftwareRendering); - PopupSliderChoice *cardboardXShift = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iCardboardXShift, -100, 100, gr->T("Cardboard Screen X Shift", "X Shift (in % of the void)"), 1, screenManager(), gr->T("% of the void"))); - cardboardXShift->SetDisabledPtr(&g_Config.bSoftwareRendering); - PopupSliderChoice *cardboardYShift = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iCardboardYShift, -100, 100, gr->T("Cardboard Screen Y Shift", "Y Shift (in % of the void)"), 1, screenManager(), gr->T("% of the void"))); - cardboardYShift->SetDisabledPtr(&g_Config.bSoftwareRendering); -#endif - graphicsSettings->Add(new ItemHeader(gr->T("Hack Settings"))); static const char *bloomHackOptions[] = { "Off", "Safe", "Balanced", "Aggressive" }; diff --git a/UI/InstallZipScreen.cpp b/UI/InstallZipScreen.cpp index 943d0c9908..ea7d206d8a 100644 --- a/UI/InstallZipScreen.cpp +++ b/UI/InstallZipScreen.cpp @@ -42,24 +42,34 @@ void InstallZipScreen::CreateViews() { root_ = new LinearLayout(ORIENT_HORIZONTAL); ViewGroup *leftColumn = new AnchorLayout(new LinearLayoutParams(1.0f)); - root_->Add(leftColumn); - - std::string shortFilename = GetFilenameFromPath(zipPath_); - leftColumn->Add(new TextView(iz->T("Install game from ZIP file?"), ALIGN_LEFT, false, new AnchorLayoutParams(10, 10, NONE, NONE))); - leftColumn->Add(new TextView(shortFilename, ALIGN_LEFT, false, new AnchorLayoutParams(10, 60, NONE, NONE))); - - doneView_ = leftColumn->Add(new TextView("", new AnchorLayoutParams(10, 120, NONE, NONE))); - progressBar_ = leftColumn->Add(new ProgressBar(new AnchorLayoutParams(10, 200, 200, NONE))); - ViewGroup *rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); + root_->Add(leftColumn); root_->Add(rightColumnItems); - installChoice_ = rightColumnItems->Add(new Choice(iz->T("Install"))); - installChoice_->OnClick.Handle(this, &InstallZipScreen::OnInstall); - backChoice_ = rightColumnItems->Add(new Choice(di->T("Back"))); - backChoice_->OnClick.Handle(this, &UIScreen::OnOK); // OK so that EmuScreen will handle it right + std::string shortFilename = GetFilenameFromPath(zipPath_); + + // TODO: Do in the background? + ZipFileInfo zipInfo; + ZipFileContents contents = DetectZipFileContents(zipPath_, &zipInfo); - rightColumnItems->Add(new CheckBox(&deleteZipFile_, iz->T("Delete ZIP file"))); + if (contents == ZipFileContents::ISO_FILE || contents == ZipFileContents::PSP_GAME_DIR) { + std::string question = iz->T("Install game from ZIP file?"); + leftColumn->Add(new TextView(question, ALIGN_LEFT, false, new AnchorLayoutParams(10, 10, NONE, NONE))); + leftColumn->Add(new TextView(shortFilename, ALIGN_LEFT, false, new AnchorLayoutParams(10, 60, NONE, NONE))); + + doneView_ = leftColumn->Add(new TextView("", new AnchorLayoutParams(10, 120, NONE, NONE))); + progressBar_ = leftColumn->Add(new ProgressBar(new AnchorLayoutParams(10, 200, 200, NONE))); + + installChoice_ = rightColumnItems->Add(new Choice(iz->T("Install"))); + installChoice_->OnClick.Handle(this, &InstallZipScreen::OnInstall); + backChoice_ = rightColumnItems->Add(new Choice(di->T("Back"))); + backChoice_->OnClick.Handle(this, &UIScreen::OnOK); // OK so that EmuScreen will handle it right + rightColumnItems->Add(new CheckBox(&deleteZipFile_, iz->T("Delete ZIP file"))); + } else { + leftColumn->Add(new TextView(iz->T("Zip file does not contain PSP software"), ALIGN_LEFT, false, new AnchorLayoutParams(10, 10, NONE, NONE))); + backChoice_ = rightColumnItems->Add(new Choice(di->T("Back"))); + backChoice_->OnClick.Handle(this, &UIScreen::OnOK); // OK so that EmuScreen will handle it right + } } bool InstallZipScreen::key(const KeyInput &key) { @@ -71,7 +81,7 @@ bool InstallZipScreen::key(const KeyInput &key) { } UI::EventReturn InstallZipScreen::OnInstall(UI::EventParams ¶ms) { - if (g_GameManager.InstallGameOnThread(zipPath_, deleteZipFile_)) { + if (g_GameManager.InstallGameOnThread(zipPath_, zipPath_, deleteZipFile_)) { installStarted_ = true; installChoice_->SetEnabled(false); } @@ -87,13 +97,16 @@ void InstallZipScreen::update() { progressBar_->SetProgress(g_GameManager.GetCurrentInstallProgressPercentage()); backChoice_->SetEnabled(false); } else { - progressBar_->SetVisibility(V_GONE); + if (progressBar_) + progressBar_->SetVisibility(V_GONE); backChoice_->SetEnabled(true); std::string err = g_GameManager.GetInstallError(); if (!err.empty()) { - doneView_->SetText(iz->T(err.c_str())); + if (doneView_) + doneView_->SetText(iz->T(err.c_str())); } else if (installStarted_) { - doneView_->SetText(iz->T("Installed!")); + if (doneView_) + doneView_->SetText(iz->T("Installed!")); MainScreen::showHomebrewTab = true; } } diff --git a/UI/InstallZipScreen.h b/UI/InstallZipScreen.h index b84d91e21f..d189041a1e 100644 --- a/UI/InstallZipScreen.h +++ b/UI/InstallZipScreen.h @@ -26,7 +26,7 @@ class InstallZipScreen : public UIDialogScreenWithBackground { public: - InstallZipScreen(std::string zipPath) : installChoice_(0), doneView_(0), zipPath_(zipPath), installStarted_(false), deleteZipFile_(false) {} + InstallZipScreen(std::string zipPath) : zipPath_(zipPath) {} virtual void update() override; virtual bool key(const KeyInput &key) override; @@ -36,12 +36,12 @@ protected: private: UI::EventReturn OnInstall(UI::EventParams ¶ms); - UI::Choice *installChoice_; - UI::Choice *backChoice_; - UI::ProgressBar *progressBar_; - UI::TextView *doneView_; + UI::Choice *installChoice_ = nullptr; + UI::Choice *backChoice_ = nullptr; + UI::ProgressBar *progressBar_ = nullptr; + UI::TextView *doneView_ = nullptr; std::string zipPath_; - bool installStarted_; - bool deleteZipFile_; + bool installStarted_ = false; + bool deleteZipFile_ = false; }; diff --git a/UI/Store.cpp b/UI/Store.cpp index 12ae3bdbf7..f17f869200 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -284,13 +284,16 @@ void ProductView::Update() { } UI::EventReturn ProductView::OnInstall(UI::EventParams &e) { - std::string zipUrl; + std::string fileUrl; if (entry_.downloadURL.empty()) { // Construct the URL, easy to predict from our server - zipUrl = storeBaseUrl + "files/" + entry_.file + ".zip"; + std::string shortName = entry_.file; + if (shortName.find('.') == std::string::npos) + shortName += ".zip"; + fileUrl = storeBaseUrl + "files/" + shortName; } else { // Use the provided URL, for external hosting. - zipUrl = entry_.downloadURL; + fileUrl = entry_.downloadURL; } if (installButton_) { installButton_->SetEnabled(false); @@ -298,8 +301,8 @@ UI::EventReturn ProductView::OnInstall(UI::EventParams &e) { if (cancelButton_) { cancelButton_->SetVisibility(UI::V_VISIBLE); } - INFO_LOG(SYSTEM, "Triggering install of %s", zipUrl.c_str()); - g_GameManager.DownloadAndInstall(zipUrl); + INFO_LOG(SYSTEM, "Triggering install of '%s'", fileUrl.c_str()); + g_GameManager.DownloadAndInstall(fileUrl); return UI::EVENT_DONE; } diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index b580abede9..769c127261 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -317,6 +317,7 @@ false MultiThreaded $(EXTERNAL_COMPILE_OPTIONS) + ProgramDatabase Windows @@ -347,6 +348,7 @@ $(EXTERNAL_COMPILE_OPTIONS) false Size + ProgramDatabase Windows @@ -377,6 +379,7 @@ $(EXTERNAL_COMPILE_OPTIONS) false Size + ProgramDatabase Windows @@ -407,6 +410,7 @@ $(EXTERNAL_COMPILE_OPTIONS) false Size + ProgramDatabase Windows diff --git a/UWP/UWPHost.cpp b/UWP/UWPHost.cpp index 8bbe1cb4a6..49bc413d4f 100644 --- a/UWP/UWPHost.cpp +++ b/UWP/UWPHost.cpp @@ -38,7 +38,7 @@ UWPHost::UWPHost() { // add first XInput device to respond - input.push_back(std::shared_ptr(new XinputDevice())); + input.push_back(std::make_unique()); } UWPHost::~UWPHost() { @@ -86,14 +86,10 @@ void UWPHost::SetDebugMode(bool mode) { } void UWPHost::PollControllers() { - bool doPad = true; - for (auto iter = this->input.begin(); iter != this->input.end(); iter++) + for (const auto& device : this->input) { - auto device = *iter; - if (!doPad && device->IsPad()) - continue; if (device->UpdateState() == InputDevice::UPDATESTATE_SKIP_PAD) - doPad = false; + break; } /* diff --git a/UWP/UWPHost.h b/UWP/UWPHost.h index 76f460a700..597886189e 100644 --- a/UWP/UWPHost.h +++ b/UWP/UWPHost.h @@ -45,5 +45,5 @@ private: void SetConsolePosition(); void UpdateConsolePosition(); - std::list> input; + std::list> input; }; diff --git a/Windows/DinputDevice.h b/Windows/DinputDevice.h index 6314a055ed..796b5e265c 100644 --- a/Windows/DinputDevice.h +++ b/Windows/DinputDevice.h @@ -24,7 +24,7 @@ #include "InputDevice.h" #include "dinput.h" -class DinputDevice : +class DinputDevice final : public InputDevice { public: @@ -32,8 +32,7 @@ public: //getDevices(), enumerates all devices if not done yet DinputDevice(int devnum); ~DinputDevice(); - virtual int UpdateState(); - virtual bool IsPad() { return true; } + virtual int UpdateState() override; static size_t getNumPads(); static void CheckDevices() { needsCheck_ = true; diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 207cdefdf4..d3ad5e460e 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -193,7 +193,6 @@ CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent) } CGEDebugger::~CGEDebugger() { - DestroyWindow(displayList->GetHWND()); CleanupPrimPreview(); delete flags; delete lighting; diff --git a/Windows/InputDevice.cpp b/Windows/InputDevice.cpp index 252d7ee94c..3c59c8323e 100644 --- a/Windows/InputDevice.cpp +++ b/Windows/InputDevice.cpp @@ -15,28 +15,21 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include #include -#include -#include -#include +#include #include "input/input_state.h" #include "thread/threadutil.h" #include "Core/Config.h" #include "Core/Host.h" #include "Windows/InputDevice.h" -#include "Windows/WindowsHost.h" -static volatile bool inputThreadStatus = false; -static volatile bool inputThreadEnabled = false; -static std::thread *inputThread = NULL; -static std::mutex inputMutex; -static std::condition_variable inputEndCond; -static bool focused = true; +static std::atomic_flag threadRunningFlag; +static std::thread inputThread; +static std::atomic_bool focused = true; inline static void ExecuteInputPoll() { - if (host && (focused || !g_Config.bGamepadOnlyFocused)) { + if (host && (focused.load(std::memory_order_relaxed) || !g_Config.bGamepadOnlyFocused)) { host->PollControllers(); } } @@ -47,40 +40,29 @@ static void RunInputThread() { // NOTE: The keyboard and mouse buttons are handled via raw input, not here. // This is mainly for controllers which need to be polled, instead of generating events. - while (inputThreadEnabled) { + while (threadRunningFlag.test_and_set(std::memory_order_relaxed)) { ExecuteInputPoll(); // Try to update 250 times per second. Sleep(4); } - - std::lock_guard guard(inputMutex); - inputThreadStatus = false; - inputEndCond.notify_one(); } void InputDevice::BeginPolling() { - std::lock_guard guard(inputMutex); - inputThreadEnabled = true; - inputThread = new std::thread(&RunInputThread); - inputThread->detach(); + threadRunningFlag.test_and_set(std::memory_order_relaxed); + inputThread = std::thread(&RunInputThread); } void InputDevice::StopPolling() { - inputThreadEnabled = false; + threadRunningFlag.clear(std::memory_order_relaxed); - std::unique_lock guard(inputMutex); - if (inputThreadStatus) { - inputEndCond.wait(guard); - } - delete inputThread; - inputThread = NULL; + inputThread.join(); } void InputDevice::GainFocus() { - focused = true; + focused.store(true, std::memory_order_relaxed); } void InputDevice::LoseFocus() { - focused = false; + focused.store(false, std::memory_order_relaxed); } diff --git a/Windows/InputDevice.h b/Windows/InputDevice.h index 7bd007d4b8..06d0ccd283 100644 --- a/Windows/InputDevice.h +++ b/Windows/InputDevice.h @@ -26,7 +26,6 @@ class InputDevice { public: enum { UPDATESTATE_SKIP_PAD = 0x1234}; virtual int UpdateState() = 0; - virtual bool IsPad() = 0; static void BeginPolling(); static void StopPolling(); diff --git a/Windows/KeyboardDevice.cpp b/Windows/KeyboardDevice.cpp deleted file mode 100644 index ff7033323f..0000000000 --- a/Windows/KeyboardDevice.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include "base/NativeApp.h" -#include "input/input_state.h" -#include "input/keycodes.h" -#include "util/const_map.h" -#include "KeyMap.h" -#include "KeyboardDevice.h" -#include "../Common/CommonTypes.h" -#include "../Common/CommonWindows.h" - - -// TODO: More keys need to be added, but this is more than -// a fair start. -std::map windowsTransTable = InitConstMap - ('A', NKCODE_A) - ('B', NKCODE_B) - ('C', NKCODE_C) - ('D', NKCODE_D) - ('E', NKCODE_E) - ('F', NKCODE_F) - ('G', NKCODE_G) - ('H', NKCODE_H) - ('I', NKCODE_I) - ('J', NKCODE_J) - ('K', NKCODE_K) - ('L', NKCODE_L) - ('M', NKCODE_M) - ('N', NKCODE_N) - ('O', NKCODE_O) - ('P', NKCODE_P) - ('Q', NKCODE_Q) - ('R', NKCODE_R) - ('S', NKCODE_S) - ('T', NKCODE_T) - ('U', NKCODE_U) - ('V', NKCODE_V) - ('W', NKCODE_W) - ('X', NKCODE_X) - ('Y', NKCODE_Y) - ('Z', NKCODE_Z) - ('0', NKCODE_0) - ('1', NKCODE_1) - ('2', NKCODE_2) - ('3', NKCODE_3) - ('4', NKCODE_4) - ('5', NKCODE_5) - ('6', NKCODE_6) - ('7', NKCODE_7) - ('8', NKCODE_8) - ('9', NKCODE_9) - (VK_OEM_PERIOD, NKCODE_PERIOD) - (VK_OEM_COMMA, NKCODE_COMMA) - (VK_NUMPAD0, NKCODE_NUMPAD_0) - (VK_NUMPAD1, NKCODE_NUMPAD_1) - (VK_NUMPAD2, NKCODE_NUMPAD_2) - (VK_NUMPAD3, NKCODE_NUMPAD_3) - (VK_NUMPAD4, NKCODE_NUMPAD_4) - (VK_NUMPAD5, NKCODE_NUMPAD_5) - (VK_NUMPAD6, NKCODE_NUMPAD_6) - (VK_NUMPAD7, NKCODE_NUMPAD_7) - (VK_NUMPAD8, NKCODE_NUMPAD_8) - (VK_NUMPAD9, NKCODE_NUMPAD_9) - (VK_DECIMAL, NKCODE_NUMPAD_DOT) - (VK_DIVIDE, NKCODE_NUMPAD_DIVIDE) - (VK_MULTIPLY, NKCODE_NUMPAD_MULTIPLY) - (VK_SUBTRACT, NKCODE_NUMPAD_SUBTRACT) - (VK_ADD, NKCODE_NUMPAD_ADD) - (VK_SEPARATOR, NKCODE_NUMPAD_COMMA) - (VK_OEM_MINUS, NKCODE_MINUS) - (VK_OEM_PLUS, NKCODE_PLUS) - (VK_LCONTROL, NKCODE_CTRL_LEFT) - (VK_RCONTROL, NKCODE_CTRL_RIGHT) - (VK_LSHIFT, NKCODE_SHIFT_LEFT) - (VK_RSHIFT, NKCODE_SHIFT_RIGHT) - (VK_LMENU, NKCODE_ALT_LEFT) - (VK_RMENU, NKCODE_ALT_RIGHT) - (VK_BACK, NKCODE_DEL) // yes! http://stackoverflow.com/questions/4886858/android-edittext-deletebackspace-key-event - (VK_SPACE, NKCODE_SPACE) - (VK_ESCAPE, NKCODE_ESCAPE) - (VK_UP, NKCODE_DPAD_UP) - (VK_INSERT, NKCODE_INSERT) - (VK_HOME, NKCODE_MOVE_HOME) - (VK_PRIOR, NKCODE_PAGE_UP) - (VK_NEXT, NKCODE_PAGE_DOWN) - (VK_DELETE, NKCODE_FORWARD_DEL) - (VK_END, NKCODE_MOVE_END) - (VK_TAB, NKCODE_TAB) - (VK_DOWN, NKCODE_DPAD_DOWN) - (VK_LEFT, NKCODE_DPAD_LEFT) - (VK_RIGHT, NKCODE_DPAD_RIGHT) - (VK_CAPITAL, NKCODE_CAPS_LOCK) - (VK_CLEAR, NKCODE_CLEAR) - (VK_SNAPSHOT, NKCODE_SYSRQ) - (VK_SCROLL, NKCODE_SCROLL_LOCK) - (VK_OEM_1, NKCODE_SEMICOLON) - (VK_OEM_2, NKCODE_SLASH) - (VK_OEM_3, NKCODE_GRAVE) - (VK_OEM_4, NKCODE_LEFT_BRACKET) - (VK_OEM_5, NKCODE_BACKSLASH) - (VK_OEM_6, NKCODE_RIGHT_BRACKET) - (VK_OEM_7, NKCODE_APOSTROPHE) - (VK_RETURN, NKCODE_ENTER) - (VK_APPS, NKCODE_MENU) // Context menu key, let's call this "menu". - (VK_PAUSE, NKCODE_BREAK) - (VK_F1, NKCODE_F1) - (VK_F2, NKCODE_F2) - (VK_F3, NKCODE_F3) - (VK_F4, NKCODE_F4) - (VK_F5, NKCODE_F5) - (VK_F6, NKCODE_F6) - (VK_F7, NKCODE_F7) - (VK_F8, NKCODE_F8) - (VK_F9, NKCODE_F9) - (VK_F10, NKCODE_F10) - (VK_F11, NKCODE_F11) - (VK_F12, NKCODE_F12) - (VK_OEM_102, NKCODE_EXT_PIPE) - (VK_LBUTTON, NKCODE_EXT_MOUSEBUTTON_1) - (VK_RBUTTON, NKCODE_EXT_MOUSEBUTTON_2) - (VK_MBUTTON, NKCODE_EXT_MOUSEBUTTON_3) - (VK_XBUTTON1, NKCODE_EXT_MOUSEBUTTON_4) - (VK_XBUTTON2, NKCODE_EXT_MOUSEBUTTON_5); - -int KeyboardDevice::UpdateState() { - // Nothing to do, all done in WM_INPUT - return 0; -} diff --git a/Windows/KeyboardDevice.h b/Windows/KeyboardDevice.h deleted file mode 100644 index 0b8fcca808..0000000000 --- a/Windows/KeyboardDevice.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include "InputDevice.h" - -extern std::map windowsTransTable; - -class KeyboardDevice : public InputDevice { -public: - virtual int UpdateState(); - virtual bool IsPad() { return false; } - -private: -}; diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 9be3b5b99a..60f23cc46e 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -1,4 +1,4 @@ - + @@ -740,7 +740,6 @@ true - @@ -926,7 +925,6 @@ - diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index fe8fbc37a3..9334fab6f3 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -82,9 +82,6 @@ Windows\Input - - Windows\Input - Windows\Input @@ -246,9 +243,6 @@ Windows\Input - - Windows\Input - Other Platforms diff --git a/Windows/RawInput.cpp b/Windows/RawInput.cpp index 79f85df699..36d639a4ba 100644 --- a/Windows/RawInput.cpp +++ b/Windows/RawInput.cpp @@ -24,7 +24,6 @@ #include "input/input_state.h" #include "Common/Log.h" #include "Windows/RawInput.h" -#include "Windows/KeyboardDevice.h" #include "Windows/MainWindow.h" #include "Windows/WindowsHost.h" #include "Common/CommonFuncs.h" @@ -65,6 +64,119 @@ namespace WindowsRawInput { static float mouseX = 0.0f; static float mouseY = 0.0f; + // TODO: More keys need to be added, but this is more than + // a fair start. + static std::map windowsTransTable = { + { 'A', NKCODE_A }, + { 'B', NKCODE_B }, + { 'C', NKCODE_C }, + { 'D', NKCODE_D }, + { 'E', NKCODE_E }, + { 'F', NKCODE_F }, + { 'G', NKCODE_G }, + { 'H', NKCODE_H }, + { 'I', NKCODE_I }, + { 'J', NKCODE_J }, + { 'K', NKCODE_K }, + { 'L', NKCODE_L }, + { 'M', NKCODE_M }, + { 'N', NKCODE_N }, + { 'O', NKCODE_O }, + { 'P', NKCODE_P }, + { 'Q', NKCODE_Q }, + { 'R', NKCODE_R }, + { 'S', NKCODE_S }, + { 'T', NKCODE_T }, + { 'U', NKCODE_U }, + { 'V', NKCODE_V }, + { 'W', NKCODE_W }, + { 'X', NKCODE_X }, + { 'Y', NKCODE_Y }, + { 'Z', NKCODE_Z }, + { '0', NKCODE_0 }, + { '1', NKCODE_1 }, + { '2', NKCODE_2 }, + { '3', NKCODE_3 }, + { '4', NKCODE_4 }, + { '5', NKCODE_5 }, + { '6', NKCODE_6 }, + { '7', NKCODE_7 }, + { '8', NKCODE_8 }, + { '9', NKCODE_9 }, + { VK_OEM_PERIOD, NKCODE_PERIOD }, + { VK_OEM_COMMA, NKCODE_COMMA }, + { VK_NUMPAD0, NKCODE_NUMPAD_0 }, + { VK_NUMPAD1, NKCODE_NUMPAD_1 }, + { VK_NUMPAD2, NKCODE_NUMPAD_2 }, + { VK_NUMPAD3, NKCODE_NUMPAD_3 }, + { VK_NUMPAD4, NKCODE_NUMPAD_4 }, + { VK_NUMPAD5, NKCODE_NUMPAD_5 }, + { VK_NUMPAD6, NKCODE_NUMPAD_6 }, + { VK_NUMPAD7, NKCODE_NUMPAD_7 }, + { VK_NUMPAD8, NKCODE_NUMPAD_8 }, + { VK_NUMPAD9, NKCODE_NUMPAD_9 }, + { VK_DECIMAL, NKCODE_NUMPAD_DOT }, + { VK_DIVIDE, NKCODE_NUMPAD_DIVIDE }, + { VK_MULTIPLY, NKCODE_NUMPAD_MULTIPLY }, + { VK_SUBTRACT, NKCODE_NUMPAD_SUBTRACT }, + { VK_ADD, NKCODE_NUMPAD_ADD }, + { VK_SEPARATOR, NKCODE_NUMPAD_COMMA }, + { VK_OEM_MINUS, NKCODE_MINUS }, + { VK_OEM_PLUS, NKCODE_PLUS }, + { VK_LCONTROL, NKCODE_CTRL_LEFT }, + { VK_RCONTROL, NKCODE_CTRL_RIGHT }, + { VK_LSHIFT, NKCODE_SHIFT_LEFT }, + { VK_RSHIFT, NKCODE_SHIFT_RIGHT }, + { VK_LMENU, NKCODE_ALT_LEFT }, + { VK_RMENU, NKCODE_ALT_RIGHT }, + { VK_BACK, NKCODE_DEL }, // yes! http://stackoverflow.com/questions/4886858/android-edittext-deletebackspace-key-event + { VK_SPACE, NKCODE_SPACE }, + { VK_ESCAPE, NKCODE_ESCAPE }, + { VK_UP, NKCODE_DPAD_UP }, + { VK_INSERT, NKCODE_INSERT }, + { VK_HOME, NKCODE_MOVE_HOME }, + { VK_PRIOR, NKCODE_PAGE_UP }, + { VK_NEXT, NKCODE_PAGE_DOWN }, + { VK_DELETE, NKCODE_FORWARD_DEL }, + { VK_END, NKCODE_MOVE_END }, + { VK_TAB, NKCODE_TAB }, + { VK_DOWN, NKCODE_DPAD_DOWN }, + { VK_LEFT, NKCODE_DPAD_LEFT }, + { VK_RIGHT, NKCODE_DPAD_RIGHT }, + { VK_CAPITAL, NKCODE_CAPS_LOCK }, + { VK_CLEAR, NKCODE_CLEAR }, + { VK_SNAPSHOT, NKCODE_SYSRQ }, + { VK_SCROLL, NKCODE_SCROLL_LOCK }, + { VK_OEM_1, NKCODE_SEMICOLON }, + { VK_OEM_2, NKCODE_SLASH }, + { VK_OEM_3, NKCODE_GRAVE }, + { VK_OEM_4, NKCODE_LEFT_BRACKET }, + { VK_OEM_5, NKCODE_BACKSLASH }, + { VK_OEM_6, NKCODE_RIGHT_BRACKET }, + { VK_OEM_7, NKCODE_APOSTROPHE }, + { VK_RETURN, NKCODE_ENTER }, + { VK_APPS, NKCODE_MENU }, // Context menu key, let's call this "menu". + { VK_PAUSE, NKCODE_BREAK }, + { VK_F1, NKCODE_F1 }, + { VK_F2, NKCODE_F2 }, + { VK_F3, NKCODE_F3 }, + { VK_F4, NKCODE_F4 }, + { VK_F5, NKCODE_F5 }, + { VK_F6, NKCODE_F6 }, + { VK_F7, NKCODE_F7 }, + { VK_F8, NKCODE_F8 }, + { VK_F9, NKCODE_F9 }, + { VK_F10, NKCODE_F10 }, + { VK_F11, NKCODE_F11 }, + { VK_F12, NKCODE_F12 }, + { VK_OEM_102, NKCODE_EXT_PIPE }, + { VK_LBUTTON, NKCODE_EXT_MOUSEBUTTON_1 }, + { VK_RBUTTON, NKCODE_EXT_MOUSEBUTTON_2 }, + { VK_MBUTTON, NKCODE_EXT_MOUSEBUTTON_3 }, + { VK_XBUTTON1, NKCODE_EXT_MOUSEBUTTON_4 }, + { VK_XBUTTON2, NKCODE_EXT_MOUSEBUTTON_5 }, + }; + void Init() { RAWINPUTDEVICE dev[3]; memset(dev, 0, sizeof(dev)); diff --git a/Windows/WindowsHost.cpp b/Windows/WindowsHost.cpp index 0195caf18b..fea51c42c7 100644 --- a/Windows/WindowsHost.cpp +++ b/Windows/WindowsHost.cpp @@ -66,7 +66,6 @@ #include "Windows/DinputDevice.h" #endif #include "Windows/XinputDevice.h" -#include "Windows/KeyboardDevice.h" #include "Windows/main.h" #include "UI/OnScreenDisplay.h" @@ -89,17 +88,14 @@ WindowsHost::WindowsHost(HINSTANCE hInstance, HWND mainWindow, HWND displayWindo g_mouseDeltaY = 0; //add first XInput device to respond - input.push_back(std::shared_ptr(new XinputDevice())); + input.push_back(std::make_unique()); #ifndef _M_ARM //find all connected DInput devices of class GamePad numDinputDevices_ = DinputDevice::getNumPads(); for (size_t i = 0; i < numDinputDevices_; i++) { - input.push_back(std::shared_ptr(new DinputDevice(static_cast(i)))); + input.push_back(std::make_unique(static_cast(i))); } #endif - keyboard = std::shared_ptr(new KeyboardDevice()); - input.push_back(keyboard); - SetConsolePosition(); } @@ -221,7 +217,7 @@ void WindowsHost::PollControllers() { if (newCount > numDinputDevices_) { INFO_LOG(SYSTEM, "New controller device detected"); for (size_t i = numDinputDevices_; i < newCount; i++) { - input.push_back(std::shared_ptr(new DinputDevice(static_cast(i)))); + input.push_back(std::make_unique(static_cast(i))); } numDinputDevices_ = newCount; } @@ -229,12 +225,9 @@ void WindowsHost::PollControllers() { checkCounter = 0; } - bool doPad = true; for (const auto &device : input) { - if (!doPad && device->IsPad()) - continue; if (device->UpdateState() == InputDevice::UPDATESTATE_SKIP_PAD) - doPad = false; + break; } // Disabled by default, needs a workaround to map to psp keys. diff --git a/Windows/WindowsHost.h b/Windows/WindowsHost.h index 623543d3cc..3aadb76dc8 100644 --- a/Windows/WindowsHost.h +++ b/Windows/WindowsHost.h @@ -17,7 +17,6 @@ #include "../Core/Host.h" #include "InputDevice.h" -#include "KeyboardDevice.h" #include "Common/CommonWindows.h" #include #include @@ -63,8 +62,6 @@ public: void NotifyUserMessage(const std::string &message, float duration = 1.0f, u32 color = 0x00FFFFFF, const char *id = nullptr) override; void SendUIMessage(const std::string &message, const std::string &value) override; - std::shared_ptr keyboard; - GraphicsContext *GetGraphicsContext() { return gfx_; } private: @@ -77,5 +74,5 @@ private: GraphicsContext *gfx_ = nullptr; size_t numDinputDevices_ = 0; - std::list> input; + std::list> input; }; diff --git a/Windows/XinputDevice.cpp b/Windows/XinputDevice.cpp index e0defafe39..1330f4cba7 100644 --- a/Windows/XinputDevice.cpp +++ b/Windows/XinputDevice.cpp @@ -17,12 +17,8 @@ #if !PPSSPP_PLATFORM(UWP) typedef DWORD (WINAPI *XInputGetState_t) (DWORD dwUserIndex, XINPUT_STATE* pState); -typedef DWORD (WINAPI *XInputSetState_t) (DWORD dwUserIndex, XINPUT_VIBRATION* pVibration); -typedef DWORD (WINAPI *XInputGetCapabilities_t) (DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities); static XInputGetState_t PPSSPP_XInputGetState = NULL; -static XInputSetState_t PPSSPP_XInputSetState = NULL; -static XInputGetCapabilities_t PPSSPP_XInputGetCapabilities = NULL; static DWORD PPSSPP_XInputVersion = 0; static HMODULE s_pXInputDLL = 0; static int s_XInputDLLRefCount = 0; @@ -42,6 +38,10 @@ static int LoadXInputDLL() { if (!s_pXInputDLL) { version = (1 << 16) | 3; s_pXInputDLL = LoadLibrary( L"XInput1_3.dll" ); // 1.3 Ships with Vista and Win7, can be installed as a restributable component. + if (!s_pXInputDLL) { + version = (1 << 16) | 0; + s_pXInputDLL = LoadLibrary( L"XInput9_1_0.dll" ); // 1.0 ships with any Windows since WinXP + } } if (!s_pXInputDLL) { return -1; @@ -50,11 +50,17 @@ static int LoadXInputDLL() { PPSSPP_XInputVersion = version; s_XInputDLLRefCount = 1; - /* 100 is the ordinal for _XInputGetStateEx, which returns the same struct as XinputGetState, but with extra data in wButtons for the guide button, we think... */ - PPSSPP_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, (LPCSTR)100 ); - PPSSPP_XInputSetState = (XInputSetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputSetState" ); - PPSSPP_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetCapabilities" ); - if ( !PPSSPP_XInputGetState || !PPSSPP_XInputSetState || !PPSSPP_XInputGetCapabilities ) { + /* 100 is the ordinal for _XInputGetStateEx, which returns the same struct as XinputGetState, but with extra data in wButtons for the guide button, we think... + Let's try the name first, though - then fall back to ordinal, then to a non-Ex version (xinput9_1_0.dll doesn't have Ex) */ + PPSSPP_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetStateEx" ); + if ( !PPSSPP_XInputGetState ) { + PPSSPP_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, (LPCSTR)100 ); + if ( !PPSSPP_XInputGetState ) { + PPSSPP_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetState" ); + } + } + + if ( !PPSSPP_XInputGetState ) { UnloadXInputDLL(); return -1; } @@ -75,8 +81,6 @@ static void UnloadXInputDLL() { static int LoadXInputDLL() { return 0; } static void UnloadXInputDLL() {} #define PPSSPP_XInputGetState XInputGetState -#define PPSSPP_XInputSetState XInputSetState -#define PPSSPP_XInputGetCapabilities XInputGetCapabilities #endif #ifndef XUSER_MAX_COUNT diff --git a/Windows/XinputDevice.h b/Windows/XinputDevice.h index 2fb48ed582..4d0510498e 100644 --- a/Windows/XinputDevice.h +++ b/Windows/XinputDevice.h @@ -4,12 +4,11 @@ #include "Xinput.h" -class XinputDevice : public InputDevice { +class XinputDevice final : public InputDevice { public: XinputDevice(); ~XinputDevice(); - virtual int UpdateState(); - virtual bool IsPad() { return true; } + virtual int UpdateState() override; private: void UpdatePad(int pad, const XINPUT_STATE &state); diff --git a/Windows/main.cpp b/Windows/main.cpp index ddc6561a46..866f4c0746 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -572,7 +572,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin #ifndef _DEBUG // See #11719 - too many Vulkan drivers crash on basic init. - if (g_Config.IsBackendEnabled(GPUBackend::VULKAN)) { + if (g_Config.IsBackendEnabled(GPUBackend::VULKAN, false)) { VulkanSetAvailable(DetectVulkanInExternalProcess()); } #endif @@ -674,8 +674,6 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin VFSShutdown(); - InputDevice::StopPolling(); - MainWindow::DestroyDebugWindows(); DialogManager::DestroyAll(); timeEndPeriod(1); diff --git a/android/.gitignore b/android/.gitignore index 7011c5d20d..2da69c40f4 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -12,3 +12,4 @@ ui_atlas.zim.png #assets/ui_atlas_luna.zim #jni/ui_atlas.cpp #jni/ui_atlas.h +.cxx diff --git a/assets/lang b/assets/lang index 7c3eae3b49..03707d64bf 160000 --- a/assets/lang +++ b/assets/lang @@ -1 +1 @@ -Subproject commit 7c3eae3b49cc70a2e4019aca89f7c0b11636fc62 +Subproject commit 03707d64bffc594d5707780a34ada77e8f43cdc0 diff --git a/cmake/Toolchains/raspberry.armv7.cmake b/cmake/Toolchains/raspberry.armv7.cmake index 31b8b77c96..a72d23df46 100644 --- a/cmake/Toolchains/raspberry.armv7.cmake +++ b/cmake/Toolchains/raspberry.armv7.cmake @@ -1,4 +1,10 @@ -if(NOT EXISTS "/opt/vc/include/bcm_host.h") +file(READ "/sys/firmware/devicetree/base/compatible" PPSSPP_PI_MODEL) + +if(PPSSPP_PI_MODEL MATCHES "raspberrypi,4") + set(PPSSPP_PI_MODEL4 ON) +endif() + +if(NOT PPSSPP_PI_MODEL4 AND NOT EXISTS "/opt/vc/include/bcm_host.h") message(FATAL_ERROR "RaspberryPI platform not recognized") endif() @@ -8,21 +14,29 @@ include_directories(SYSTEM /opt/vc/include/interface/vmcx_host/linux ) -add_definitions( - -DPPSSPP_PLATFORM_RPI=1 - -U__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 -) +if(NOT PPSSPP_PI_MODEL4) + add_definitions( + -DPPSSPP_PLATFORM_RPI=1 + -U__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 + ) +endif() -set(ARCH_FLAGS "-mcpu=cortex-a7 -mfpu=neon -mfloat-abi=hard") +if(PPSSPP_PI_MODEL4) + set(ARCH_FLAGS "-march=armv7-a -mcpu=cortex-a72 -mtune=cortex-a72 -mfpu=neon-vfpv4 -mfloat-abi=hard") +else() + set(ARCH_FLAGS "-mcpu=cortex-a7 -mfpu=neon -mfloat-abi=hard") +endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ARCH_FLAGS}" CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARCH_FLAGS}" CACHE STRING "" FORCE) set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${ARCH_FLAGS}" CACHE STRING "" FORCE) set(CMAKE_EXE_LINKER_FLAGS "-L/opt/vc/lib" CACHE STRING "" FORCE) -set(OPENGL_LIBRARIES brcmGLESv2 bcm_host) -set(EGL_LIBRARIES brcmEGL) +if(NOT PPSSPP_PI_MODEL4) + set(OPENGL_LIBRARIES brcmGLESv2 bcm_host) + set(EGL_LIBRARIES brcmEGL) + set(USING_FBDEV ON) +endif() set(USING_GLES2 ON) -set(USING_FBDEV ON) set(ARMV7 ON) set(USE_WAYLAND_WSI OFF) diff --git a/ext/SPIRV-Cross.vcxproj b/ext/SPIRV-Cross.vcxproj index 8e916f1d1a..964e42ea6e 100644 --- a/ext/SPIRV-Cross.vcxproj +++ b/ext/SPIRV-Cross.vcxproj @@ -239,6 +239,7 @@ MultiThreaded false true + ProgramDatabase Windows @@ -259,6 +260,7 @@ false true false + ProgramDatabase Windows @@ -279,6 +281,7 @@ false true false + ProgramDatabase Windows @@ -299,6 +302,7 @@ false true false + ProgramDatabase Windows diff --git a/ext/discord-rpc-build/discord-rpc.vcxproj b/ext/discord-rpc-build/discord-rpc.vcxproj index 275ad00e49..3506f7ee6d 100644 --- a/ext/discord-rpc-build/discord-rpc.vcxproj +++ b/ext/discord-rpc-build/discord-rpc.vcxproj @@ -187,6 +187,7 @@ true false false + ProgramDatabase Windows @@ -208,6 +209,7 @@ true false false + ProgramDatabase Windows @@ -229,6 +231,7 @@ true false false + ProgramDatabase Windows @@ -313,6 +316,7 @@ MultiThreaded true false + ProgramDatabase Windows diff --git a/ext/glslang.vcxproj b/ext/glslang.vcxproj index ad3b66bdd7..e1b4524d01 100644 --- a/ext/glslang.vcxproj +++ b/ext/glslang.vcxproj @@ -221,6 +221,7 @@ MultiThreaded false true + ProgramDatabase Windows @@ -243,6 +244,7 @@ false true false + ProgramDatabase Windows @@ -265,6 +267,7 @@ false true false + ProgramDatabase Windows @@ -287,6 +290,7 @@ false true false + ProgramDatabase Windows diff --git a/ext/libarmips.vcxproj b/ext/libarmips.vcxproj index 0173ddf031..b3d288f799 100644 --- a/ext/libarmips.vcxproj +++ b/ext/libarmips.vcxproj @@ -404,6 +404,7 @@ armips true false + ProgramDatabase Windows @@ -426,6 +427,7 @@ true true false + ProgramDatabase Windows @@ -448,6 +450,7 @@ true true false + ProgramDatabase Windows @@ -470,6 +473,7 @@ true true false + ProgramDatabase Windows diff --git a/ext/native/file/file_util.cpp b/ext/native/file/file_util.cpp index 2d6c6df33c..a870518969 100644 --- a/ext/native/file/file_util.cpp +++ b/ext/native/file/file_util.cpp @@ -211,20 +211,19 @@ bool FileInfo::operator <(const FileInfo &other) const { size_t getFilesInDir(const char *directory, std::vector *files, const char *filter, int flags) { size_t foundEntries = 0; std::set filters; - std::string tmp; if (filter) { + std::string tmp; while (*filter) { if (*filter == ':') { - filters.insert(tmp); - tmp = ""; + filters.insert(std::move(tmp)); } else { tmp.push_back(*filter); } filter++; } + if (!tmp.empty()) + filters.insert(std::move(tmp)); } - if (tmp.size()) - filters.insert(tmp); #ifdef _WIN32 // Find the first file in the directory. WIN32_FIND_DATA ffd; @@ -252,14 +251,20 @@ size_t getFilesInDir(const char *directory, std::vector *files, const const std::string virtualName(result->d_name); #endif // check for "." and ".." - if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || - ((virtualName[0] == '.') && (virtualName[1] == '.') && - (virtualName[2] == '\0'))) + if (virtualName == "." || virtualName == "..") continue; // Remove dotfiles (optional with flag.) - if (!(flags & GETFILES_GETHIDDEN) && virtualName[0] == '.') - continue; + if (!(flags & GETFILES_GETHIDDEN)) + { +#ifdef _WIN32 + if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0) + continue; +#else + if (virtualName[0] == '.') + continue; +#endif + } FileInfo info; info.name = virtualName; @@ -283,7 +288,7 @@ size_t getFilesInDir(const char *directory, std::vector *files, const } if (files) - files->push_back(info); + files->push_back(std::move(info)); foundEntries++; #ifdef _WIN32 } while (FindNextFile(hFind, &ffd) != 0); diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index b854ef2030..12111719df 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -257,6 +257,7 @@ true false MultiThreaded + ProgramDatabase false @@ -284,6 +285,7 @@ true false MultiThreaded + ProgramDatabase false true false @@ -313,6 +315,7 @@ true false MultiThreaded + ProgramDatabase false true false @@ -342,6 +345,7 @@ true false MultiThreaded + ProgramDatabase false true false diff --git a/ext/zlib/zlib.vcxproj b/ext/zlib/zlib.vcxproj index b153a92496..23aef8c900 100644 --- a/ext/zlib/zlib.vcxproj +++ b/ext/zlib/zlib.vcxproj @@ -268,6 +268,7 @@ true false MultiThreaded + ProgramDatabase Windows @@ -294,6 +295,7 @@ true false MultiThreaded + ProgramDatabase true @@ -321,6 +323,7 @@ true false MultiThreaded + ProgramDatabase true @@ -348,6 +351,7 @@ true false MultiThreaded + ProgramDatabase true diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 1d28b06e3c..6c3f9185e9 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -379,6 +379,7 @@ int main(int argc, const char* argv[]) g_Config.bHighQualityDepth = true; g_Config.bMemStickInserted = true; g_Config.bFragmentTestCache = true; + g_Config.iAudioLatency = 1; #ifdef _WIN32 InitSysDirectories(); diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index 7a3fb66766..dd42cbe4fc 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -309,6 +309,7 @@ true false MultiThreaded + ProgramDatabase Console @@ -345,6 +346,7 @@ false MultiThreaded true + ProgramDatabase Console @@ -381,6 +383,7 @@ false MultiThreaded true + ProgramDatabase Console @@ -414,6 +417,7 @@ false MultiThreaded true + ProgramDatabase Console diff --git a/ios/main.mm b/ios/main.mm index e52f3f8bf9..dd4bf235a4 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -61,10 +61,12 @@ bool System_GetPropertyBool(SystemProperty prop) { void System_SendMessage(const char *command, const char *parameter) { if (!strcmp(command, "finish")) { - dispatch_async(dispatch_get_main_queue(), ^{ - [sharedViewController shutdown]; - exit(0); - }); + exit(0); + // The below seems right, but causes hangs. See #12140. + // dispatch_async(dispatch_get_main_queue(), ^{ + // [sharedViewController shutdown]; + // exit(0); + // }); } } diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index a802e3fb5a..69e1a2c593 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -324,10 +324,14 @@ void retro_init(void) { g_Config.bFrameSkipUnthrottle = false; g_Config.bMemStickInserted = PSP_MEMORYSTICK_STATE_INSERTED; g_Config.iGlobalVolume = VOLUME_MAX - 1; + g_Config.iAltSpeedVolume = -1; g_Config.bEnableSound = true; g_Config.bAudioResampler = false; g_Config.iCwCheatRefreshRate = 60; - + + g_Config.iFirmwareVersion = PSP_DEFAULT_FIRMWARE; + g_Config.iPSPModel = PSP_MODEL_SLIM; + LogManager::Init(); host = new LibretroHost; diff --git a/unittest/UnitTests.vcxproj b/unittest/UnitTests.vcxproj index d834958c38..e9975c81d7 100644 --- a/unittest/UnitTests.vcxproj +++ b/unittest/UnitTests.vcxproj @@ -270,6 +270,7 @@ true false MultiThreaded + ProgramDatabase Console @@ -298,6 +299,7 @@ true Size false + ProgramDatabase Console @@ -326,6 +328,7 @@ true Size false + ProgramDatabase Console @@ -354,6 +357,7 @@ true Size false + ProgramDatabase Console