From 7d2b332b2da4ccdc2eb1c3ac34aeb0c368783e61 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 Jun 2019 16:39:14 -0700 Subject: [PATCH 01/31] Audio: Improve error messages for sample counts. --- Core/HLE/sceAudio.cpp | 38 +++++++++++++++++++++++++------------- headless/Headless.cpp | 1 + 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index 6fd437bad6..905c5a60e3 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -339,11 +339,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 +355,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 +389,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); @@ -449,6 +457,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 +523,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/headless/Headless.cpp b/headless/Headless.cpp index 11a9e8843f..77e136baf5 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -377,6 +377,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(); From 9ef3973b4910266f1eb88859c4fe65911b2fd446 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 Jun 2019 23:09:07 -0700 Subject: [PATCH 02/31] Audio: Correct sceAudioChRelease return value. --- Core/HLE/sceAudio.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index 905c5a60e3..2a5465f371 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) { From 20ad7f291430de279f80621ca1529744f76ca3b1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 Jun 2019 23:09:46 -0700 Subject: [PATCH 03/31] Audio: Track SRC frequency. sceAudioSetFrequency is kernel mode only, and it sounds like it changes the audio controller clock (didn't test.) SRC only affects the SRC channel. Currently, this doesn't actually use the sample rate yet, but at least it's tracked appropriately. --- Core/HLE/__sceAudio.cpp | 16 +++++++++++++++- Core/HLE/__sceAudio.h | 1 + Core/HLE/sceAudio.cpp | 7 +++---- Core/HLE/sceVaudio.cpp | 2 +- Core/HW/StereoResampler.cpp | 2 ++ 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 99f40e4551..bf3b86c1ba 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. 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 2a5465f371..f3c355b655 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -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); } @@ -432,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); } 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(); } From 4546db5635e3351078b83be0879d3d8ff81eaaed Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 Jun 2019 23:44:18 -0700 Subject: [PATCH 04/31] Audio: Quick SRC resampling experiment. --- Core/HLE/__sceAudio.cpp | 47 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index bf3b86c1ba..b6e7179ea1 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -351,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) @@ -362,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++) From 7588a9760ef7a91cf92dcef3a500a5fda216eed4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 Jun 2019 23:45:14 -0700 Subject: [PATCH 05/31] Windows: Fix Vulkan out of process check. Oops, forgot to actually pass false after adding this argument... --- Windows/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Windows/main.cpp b/Windows/main.cpp index ddc6561a46..ec1ab99294 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 From e284d9f00d044deddbac5e8c253a3b479c70fb66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 1 Jul 2019 14:47:42 +0200 Subject: [PATCH 06/31] Remove Google Cardboard support. Fixes #11668 --- Core/Config.cpp | 4 -- Core/Config.h | 5 -- GPU/Common/FramebufferCommon.cpp | 90 +++++--------------------------- GPU/Common/FramebufferCommon.h | 12 ----- UI/GameSettingsScreen.cpp | 12 ----- android/.gitignore | 1 + 6 files changed, 13 insertions(+), 111 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 8380017a8b..ff381e219e 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -694,10 +694,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 34a97fc0d0..ae736a912a 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -152,11 +152,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/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 0bce2d0fa2..6417452c19 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -782,9 +782,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). @@ -810,18 +807,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); } @@ -866,9 +854,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 @@ -984,19 +969,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_ @@ -1031,19 +1006,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 }); @@ -1059,19 +1024,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_) { @@ -1841,27 +1796,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); 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/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 49608cf8f3..8804597de3 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -438,18 +438,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", "Hack Settings (these WILL cause glitches)"))); static const char *bloomHackOptions[] = { "Off", "Safe", "Balanced", "Aggressive" }; diff --git a/android/.gitignore b/android/.gitignore index 4aa0d52b74..94ec461233 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -12,3 +12,4 @@ ui_atlas.zim.png #assets/ui_atlas.zim #jni/ui_atlas.cpp #jni/ui_atlas.h +.cxx From 8940a9d9f178af7984ad6eea691dd47118c38717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 2 Jul 2019 09:19:40 +0200 Subject: [PATCH 07/31] Update lang submodule --- assets/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 71ab845564f6fcc4ad3d8a42ce4956bc36cf9ca5 Mon Sep 17 00:00:00 2001 From: Silent Date: Thu, 27 Jun 2019 09:58:26 +0200 Subject: [PATCH 08/31] Windows: Fix an exit crash by not destroying the window in CGEDebugger destructor - this window (and displayList) have already been destroyed and freed at that point --- Windows/GEDebugger/GEDebugger.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 9f20c87d36..384849aedd 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; From 2479427992a2746e7f09dbe6f89184e633129b3d Mon Sep 17 00:00:00 2001 From: Silent Date: Thu, 27 Jun 2019 10:37:49 +0200 Subject: [PATCH 09/31] Windows: Properly check for hidden files when enumerating files --- ext/native/file/file_util.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) 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); From 1acaec24943aa0147659346c1c821ebf470e3574 Mon Sep 17 00:00:00 2001 From: Silent Date: Sat, 29 Jun 2019 00:45:23 +0200 Subject: [PATCH 10/31] Support falling back to xinput9_1_0.dll if neither xinput1_4.dll or xinput1_3.dll are present. This can happen on a Windows XP, Vista or 7 PC without DirectX End-User Runtimes installed. Clean up XinputDevice.cpp from unused imports --- Windows/XinputDevice.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Windows/XinputDevice.cpp b/Windows/XinputDevice.cpp index 575f4891d4..c297533a18 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 From 0b5458f759cc88c8f817dfa6b8c2153baf87fdd9 Mon Sep 17 00:00:00 2001 From: Silent Date: Sat, 29 Jun 2019 00:55:45 +0200 Subject: [PATCH 11/31] Remove KeyboardDevice which was essentially useless Refactor InputDevice to simplify it --- CMakeLists.txt | 2 - UWP/UWPHost.cpp | 10 +-- UWP/UWPHost.h | 2 +- Windows/DinputDevice.h | 5 +- Windows/InputDevice.h | 1 - Windows/KeyboardDevice.cpp | 126 --------------------------------- Windows/KeyboardDevice.h | 14 ---- Windows/PPSSPP.vcxproj | 4 +- Windows/PPSSPP.vcxproj.filters | 6 -- Windows/RawInput.cpp | 114 ++++++++++++++++++++++++++++- Windows/WindowsHost.cpp | 15 ++-- Windows/WindowsHost.h | 5 +- Windows/XinputDevice.h | 5 +- 13 files changed, 127 insertions(+), 182 deletions(-) delete mode 100644 Windows/KeyboardDevice.cpp delete mode 100644 Windows/KeyboardDevice.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d70d60a892..a5d5aebd8d 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/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/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.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); From 0621dbb487b4bd15ddbfa06eecc4c4aeee07884f Mon Sep 17 00:00:00 2001 From: Silent Date: Sat, 29 Jun 2019 01:19:49 +0200 Subject: [PATCH 12/31] Simplify synchronization model in InputDevice.cpp - replaced volatile flags/mutex/condition variable with joining on thread and one atomic flag --- Windows/InputDevice.cpp | 42 ++++++++++++----------------------------- Windows/main.cpp | 2 -- 2 files changed, 12 insertions(+), 32 deletions(-) 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/main.cpp b/Windows/main.cpp index ec1ab99294..866f4c0746 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -674,8 +674,6 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin VFSShutdown(); - InputDevice::StopPolling(); - MainWindow::DestroyDebugWindows(); DialogManager::DestroyAll(); timeEndPeriod(1); From 71c7686280c4309b83dff9aedb02b945fe86fabf Mon Sep 17 00:00:00 2001 From: M4xw Date: Thu, 4 Jul 2019 16:21:37 +0200 Subject: [PATCH 13/31] [Libretro] Fix some default config values -iAltSpeedVolume -iFirmwareVersion -iPSPModel Fixes #12144 --- libretro/libretro.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index b7471d6e6f..27b10e4a41 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -321,10 +321,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; From 62c6d069c9918b36ecfc5559280f4f097cb38a11 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 4 Jul 2019 13:22:11 -0700 Subject: [PATCH 14/31] Travis: Update config to reduce timeout errors. Leverage homebrew/apt addons to install packages since they are documented to automatically perform retries on failure. This updates to Ubuntu Xenial and macOS Sierra, which are supported versions. This isn't to increase minimum requirements necessarily, but to make sure Travis keeps running smoothly. --- .travis.sh | 47 ++--------------------------------------------- .travis.yml | 34 ++++++++++++++++++++++------------ 2 files changed, 24 insertions(+), 57 deletions(-) diff --git a/.travis.sh b/.travis.sh index f75ef266a1..b090434cde 100644 --- a/.travis.sh +++ b/.travis.sh @@ -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,18 +38,9 @@ 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 - fi - # Ensure we're using ccache if [[ "$CXX" = "clang" && "$CC" == "clang" ]]; then export CXX="ccache clang" CC="ccache clang" diff --git a/.travis.yml b/.travis.yml index 2ce914c98e..a6b91b575a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,21 +4,31 @@ 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 @@ -75,19 +85,19 @@ 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 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 From 6bdfd6c9e479b65a082cf5a9e372c8e880bd2e1a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 4 Jul 2019 15:05:06 -0700 Subject: [PATCH 15/31] Travis: Update to NDK r18b. Picked this version because it's much smaller than r17 or r19, and r20 is broken right now. --- .travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index b090434cde..c13802c43d 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 From 4144e71b7ad17eda76858483518c6ab357333ee4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 4 Jul 2019 20:36:17 -0700 Subject: [PATCH 16/31] Replay: Add file header handling. --- Core/HLE/sceKernelTime.cpp | 4 ++ Core/HLE/sceKernelTime.h | 1 + Core/HLE/sceRtc.cpp | 21 ++++++- Core/HLE/sceRtc.h | 2 + Core/Replay.cpp | 120 ++++++++++++++++++++++++++----------- Core/Replay.h | 2 +- 6 files changed, 113 insertions(+), 37 deletions(-) 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/Replay.cpp b/Core/Replay.cpp index e35458fb26..145c5b3477 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) @@ -101,8 +126,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 +168,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 +266,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); diff --git a/Core/Replay.h b/Core/Replay.h index 3c91ce6524..2cb6bbcb85 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); From 33cfac549dfe22880ad529445bb3cee7db3850cf Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 4 Jul 2019 21:00:14 -0700 Subject: [PATCH 17/31] Replay: Attempt to ignore reads in PSP/GAME. Unless there are writes in this directory, we should be able to safely ignore them. Currently detection isn't perfect (like PSP/./GAME/../GAME), but this should catch the vast majority of cases. --- Core/FileSystems/DirectoryFileSystem.cpp | 35 +++++++++++++++--------- Core/FileSystems/DirectoryFileSystem.h | 7 +++-- Core/Replay.cpp | 26 +++++++++++++++++- Core/Replay.h | 3 +- 4 files changed, 53 insertions(+), 18 deletions(-) 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/Replay.cpp b/Core/Replay.cpp index 145c5b3477..0e76c2c481 100644 --- a/Core/Replay.cpp +++ b/Core/Replay.cpp @@ -118,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; @@ -299,6 +300,7 @@ void ReplayAbort() { replayExecPos = 0; replaySaveWroteHeader = false; replayState = ReplayState::IDLE; + replaySawGameDirWrite = false; replayCtrlPos = 0; lastButtons = 0; @@ -439,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: { @@ -466,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 2cb6bbcb85..bcbf7dc6e0 100644 --- a/Core/Replay.h +++ b/Core/Replay.h @@ -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); From 54a393386f08b2aff0c1bec239b41bbbaadc33aa Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 4 Jul 2019 09:34:44 -0700 Subject: [PATCH 18/31] SDL: Use whatever display SDL gives us for EGL. --- SDL/SDLGLGraphicsContext.cpp | 124 +++++++++++++++++++++++++---------- SDL/SDLGLGraphicsContext.h | 7 -- SDL/SDLMain.cpp | 6 -- 3 files changed, 91 insertions(+), 46 deletions(-) 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; From c3ae97e23741f58456a134df8316f51ec19d8162 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 5 Jul 2019 21:29:57 -0700 Subject: [PATCH 19/31] Travis: Build Windows on Travis too. --- .travis.sh | 25 ++++++++++++++++++++++- .travis.yml | 4 ++++ Common/Common.vcxproj | 4 ++++ Core/Core.vcxproj | 4 ++++ GPU/GPU.vcxproj | 4 ++++ UI/UI.vcxproj | 4 ++++ ext/SPIRV-Cross.vcxproj | 4 ++++ ext/discord-rpc-build/discord-rpc.vcxproj | 4 ++++ ext/glslang.vcxproj | 4 ++++ ext/libarmips.vcxproj | 4 ++++ ext/native/native.vcxproj | 4 ++++ ext/zlib/zlib.vcxproj | 4 ++++ headless/Headless.vcxproj | 4 ++++ unittest/UnitTests.vcxproj | 4 ++++ 14 files changed, 76 insertions(+), 1 deletion(-) diff --git a/.travis.sh b/.travis.sh index c13802c43d..c944daf15f 100644 --- a/.travis.sh +++ b/.travis.sh @@ -41,6 +41,11 @@ travis_install() { 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" = "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 if [[ "$CXX" = "clang" && "$CC" == "clang" ]]; then export CXX="ccache clang" CC="ccache clang" @@ -92,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 a6b91b575a..64aff24a17 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,7 @@ cache: 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. @@ -92,6 +93,9 @@ matrix: osx_image: xcode8.3 compiler: "clang" env: PPSSPP_BUILD_TYPE=iOS + - os: windows + compiler: "msvc2017" + env: PPSSPP_BUILD_TYPE=Windows before_install: - travis_retry bash .travis.sh travis_before_install 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/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/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/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/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/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.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/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 From dc03050730ff5ce41c2b20433c11771589b49f2e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Jul 2019 07:37:39 -0700 Subject: [PATCH 20/31] GPU: Download CLUTs when loading at 256 stride. Or otherwise when loading full rows. Should fix #8406. --- GPU/Common/FramebufferCommon.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 6417452c19..ddf3a61d08 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -2082,6 +2082,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(); From 6fdaa1b111629fd8e7b6a10f813929323cebf4b9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 6 Jul 2019 09:38:57 -0700 Subject: [PATCH 21/31] cmake: Automatically detect raspberrypi 4. --- cmake/Toolchains/raspberry.armv7.cmake | 32 ++++++++++++++++++-------- 1 file changed, 23 insertions(+), 9 deletions(-) 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) From da54c8d70c7f0962831ff0f0628afa931d1f2f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 7 Jul 2019 19:58:46 +0200 Subject: [PATCH 22/31] Experimentally revert a small part of #12094, ios external display support, that seemed to break exit. See #12140. --- ios/main.mm | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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); + // }); } } From c87d5a637b2367c6f54a4b022b732ce625c1a411 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Jul 2019 13:10:28 -0700 Subject: [PATCH 23/31] GPU: Forget pause signal on new list. Otherwise, when the list gets executed again, we just pause it when it finishes instead of letting it finish properly. --- GPU/GPUCommon.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 1af5afd5c2..649a37f4d1 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; @@ -786,8 +788,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. @@ -799,9 +800,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) { @@ -2446,6 +2448,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(); From 556b96035535bbe191b73eec66f18e227d6ecbaf Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Jul 2019 18:06:06 -0700 Subject: [PATCH 24/31] Kernel: Shutdown when module load fails. This prevents us from trying to reinit next time, which can crash since we never finished initing in the first place. --- Core/HLE/sceKernelModule.cpp | 1 + 1 file changed, 1 insertion(+) 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; } From 510daa664f0394ab0f136e161e7aeac2a6e52a39 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 7 Jul 2019 22:58:54 -0700 Subject: [PATCH 25/31] interp: Correct vsbn instruction. Accidentally broken in f2be0cb. Rarely used. --- Core/MIPS/MIPSIntVFPU.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/MIPS/MIPSIntVFPU.cpp b/Core/MIPS/MIPSIntVFPU.cpp index 2c4c112cf0..bb6c84abe5 100644 --- a/Core/MIPS/MIPSIntVFPU.cpp +++ b/Core/MIPS/MIPSIntVFPU.cpp @@ -2184,7 +2184,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]); From 4d30a8d04e2c6671400bdda6c9df1082534f7c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 7 Jul 2019 23:12:26 +0200 Subject: [PATCH 26/31] Start refactoring GameManager zip handling to easier support ISOs in zips --- Core/Util/GameManager.cpp | 69 +++++++++++++++++++++++++++++++-------- Core/Util/GameManager.h | 4 +++ 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 864a16e5a0..e08d815e27 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -125,6 +125,19 @@ void GameManager::Update() { } } +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; + } + } +} + bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { if (installInProgress_) { ERROR_LOG(HLE, "Cannot have two installs in progress at the same time"); @@ -155,34 +168,47 @@ bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { 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; + // Also, 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; 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)) { + countSlashes(zippedName, &slashLocation, &slashCount); + // TODO: Rewrite this... + if (slashCount >= 1 && (!isPSPMemstickGame || slashLocation < stripChars + 1)) { stripChars = slashLocation + 1; - isPSP = true; + isPSPMemstickGame = true; + break; } else { INFO_LOG(HLE, "Wrong number of slashes (%i) in %s", slashCount, zippedName.c_str()); } + } else if (endsWithNoCase(zippedName, ".iso")) { + int slashCount = 0; + int slashLocation = -1; + countSlashes(zippedName, &slashLocation, &slashCount); + if (slashCount == 1) { + isZippedISO = true; + isoFileIndex = i; + break; + } } } - if (!isPSP) { + if (isPSPMemstickGame) { + // InstallMemstickGame contains code to close z. + return InstallMemstickGame(z, zipfile, pspGame, numFiles, stripChars, deleteAfter); + } else if (isZippedISO) { + return InstallZippedISO(z, isoFileIndex, zipfile, deleteAfter); + } else { ERROR_LOG(HLE, "File not a PSP game, no EBOOT.PBP found."); installProgress_ = 0.0f; installInProgress_ = false; @@ -192,8 +218,11 @@ bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { File::Delete(zipfile); return false; } +} +bool GameManager::InstallMemstickGame(struct zip *z, std::string zipfile, std::string pspGame, int numFiles, int stripChars, bool deleteAfter) { size_t allBytes = 0, bytesCopied = 0; + I18NCategory *sy = GetI18NCategory("System"); // Create all the directories in one pass std::set createdDirs; @@ -250,7 +279,7 @@ bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { 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); + ERROR_LOG(HLE, "Wrote %d bytes out of %d - Disk full?", (int)written, (int)bs); delete [] buffer; buffer = 0; fclose(f); @@ -293,6 +322,7 @@ bail: installProgress_ = 0.0f; installInProgress_ = false; installError_ = sy->T("Storage full"); + // Should we really delete in this case??? if (deleteAfter) { File::Delete(zipfile.c_str()); } @@ -306,6 +336,17 @@ bail: return false; } +bool GameManager::InstallZippedISO(struct zip *z, int isoFileIndex, std::string zipfile, bool deleteAfter) { + + + zip_close(z); + if (deleteAfter) { + File::Delete(zipfile.c_str()); + } + InstallDone(); + return false; +} + bool GameManager::InstallGameOnThread(std::string zipFile, bool deleteAfter) { if (installInProgress_) { return false; diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 55e22312ba..40dc154c52 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(); @@ -68,6 +70,8 @@ public: private: bool InstallGame(std::string zipfile, bool deleteAfter = false); + 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); void InstallDone(); std::string GetTempFilename() const; From 663f10d08b5f9a18117a5338d39a0c6145d28c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 7 Jul 2019 23:55:17 +0200 Subject: [PATCH 27/31] GameManager: Actually allow installing ISOs from zip files. Refactor. --- Core/Util/GameManager.cpp | 132 ++++++++++++++++++++++++-------------- Core/Util/GameManager.h | 1 + 2 files changed, 84 insertions(+), 49 deletions(-) diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index e08d815e27..1e1d30622f 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -167,8 +167,7 @@ bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { 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. We also try + // 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; @@ -187,22 +186,26 @@ bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { if (slashCount >= 1 && (!isPSPMemstickGame || slashLocation < stripChars + 1)) { stripChars = slashLocation + 1; isPSPMemstickGame = true; - break; } else { INFO_LOG(HLE, "Wrong number of slashes (%i) in %s", slashCount, zippedName.c_str()); } - } else if (endsWithNoCase(zippedName, ".iso")) { + } else if (endsWithNoCase(zippedName, ".iso") || endsWithNoCase(zippedName, ".cso")) { int slashCount = 0; int slashLocation = -1; countSlashes(zippedName, &slashLocation, &slashCount); - if (slashCount == 1) { + if (slashCount <= 1) { + // We only do this if the ISO file is in the root or one level down. isZippedISO = true; isoFileIndex = i; - break; } } } + // If a ZIP is detected as both, let's let the memstick game interpretation prevail. + if (isPSPMemstickGame && isZippedISO) { + isZippedISO = false; + } + if (isPSPMemstickGame) { // InstallMemstickGame contains code to close z. return InstallMemstickGame(z, zipfile, pspGame, numFiles, stripChars, deleteAfter); @@ -220,11 +223,56 @@ bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { } } +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; + + 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 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 %d bytes out of %d - Disk full?", (int)written, (int)bs); + delete[] buffer; + buffer = 0; + fclose(f); + zip_fclose(zf); + File::Delete(outFilename.c_str()); + return false; + } + pos += bs; + + *bytesCopied += bs; + 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, bytesCopied = 0; + size_t allBytes = 0; + size_t bytesCopied = 0; + I18NCategory *sy = GetI18NCategory("System"); - // Create all the directories in one pass + // 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); @@ -253,53 +301,16 @@ bool GameManager::InstallMemstickGame(struct zip *z, std::string zipfile, std::s // 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 %d bytes out of %d - 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); } } } @@ -337,14 +348,37 @@ bail: } 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 false; + return true; } bool GameManager::InstallGameOnThread(std::string zipFile, bool deleteAfter) { diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 40dc154c52..8e5a745de4 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -73,6 +73,7 @@ private: 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); 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_; From 0e978ba2e3f5fd67387c00a484cc53bd10252b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 8 Jul 2019 23:03:27 +0200 Subject: [PATCH 28/31] Pre-check the contents of ZIP files before allowing install. --- Core/Loaders.h | 3 +- Core/Util/GameManager.cpp | 117 ++++++++++++++++++++++++++------------ Core/Util/GameManager.h | 18 +++++- UI/InstallZipScreen.cpp | 47 +++++++++------ UI/InstallZipScreen.h | 14 ++--- 5 files changed, 135 insertions(+), 64 deletions(-) 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/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 1e1d30622f..38ae3aeec8 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -58,7 +58,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,11 +69,11 @@ bool GameManager::DownloadAndInstall(std::string storeZipUrl) { } std::string filename = GetTempFilename(); - curDownload_ = g_DownloadManager.StartDownload(storeZipUrl, filename); + curDownload_ = g_DownloadManager.StartDownload(storeFileUrl, filename); return true; } -bool GameManager::CancelDownload() { +bool GameManager::CancelDownload() { if (!curDownload_) return false; @@ -108,18 +108,18 @@ bool GameManager::Uninstall(std::string name) { void GameManager::Update() { if (curDownload_.get() && curDownload_->Done()) { INFO_LOG(HLE, "Download completed! Status = %i", curDownload_->ResultCode()); - std::string zipName = curDownload_->outfile(); + 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(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()); + File::Delete(fileName.c_str()); } curDownload_.reset(); } @@ -138,33 +138,22 @@ void countSlashes(std::string fileName, int *slashLocation, int *slashCount) { } } -bool GameManager::InstallGame(std::string zipfile, 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()); - return false; - } - - 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; +ZipFileContents DetectZipFileContents(std::string fileName, ZipFileInfo *info) { + 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); #endif if (!z) { - ERROR_LOG(HLE, "Failed to open ZIP file %s, error code=%i", zipfile.c_str(), error); - return false; + return ZipFileContents::UNKNOWN; } + ZipFileContents retVal = DetectZipFileContents(z, info); + zip_close(z); + return retVal; +} +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 @@ -201,24 +190,67 @@ bool GameManager::InstallGame(std::string zipfile, bool deleteAfter) { } } + 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 && isZippedISO) { - isZippedISO = false; + if (isPSPMemstickGame) { + return ZipFileContents::PSP_GAME_DIR; + } else if (isZippedISO) { + return ZipFileContents::ISO_FILE; + } else { + return ZipFileContents::UNKNOWN; + } +} + +bool GameManager::InstallGame(std::string fileName, bool deleteAfter) { + if (installInProgress_) { + ERROR_LOG(HLE, "Cannot have two installs in progress at the same time"); + return false; } - if (isPSPMemstickGame) { + if (!File::Exists(fileName)) { + ERROR_LOG(HLE, "Game file %s doesn't exist", fileName.c_str()); + return false; + } + + bool isRawISO = false; // TODO: Make it possible to pass in this information. + if (isRawISO) { + return InstallRawISO(fileName, fileName); + } + + I18NCategory *sy = GetI18NCategory("System"); + installInProgress_ = true; + + std::string pspGame = GetSysDirectory(DIRECTORY_GAME); + INFO_LOG(HLE, "Installing '%s' into '%s'", fileName.c_str(), pspGame.c_str()); + int error = 0; +#ifdef _WIN32 + struct zip *z = zip_open(ConvertUTF8ToWString(fileName).c_str(), 0, &error); +#else + struct zip *z = zip_open(zipfile.c_str(), 0, &error); +#endif + if (!z) { + ERROR_LOG(HLE, "Failed to open ZIP file %s, error code=%i", fileName.c_str(), error); + return false; + } + + ZipFileInfo info; + ZipFileContents contents = DetectZipFileContents(z, &info); + switch (contents) { + case ZipFileContents::PSP_GAME_DIR: // InstallMemstickGame contains code to close z. - return InstallMemstickGame(z, zipfile, pspGame, numFiles, stripChars, deleteAfter); - } else if (isZippedISO) { - return InstallZippedISO(z, isoFileIndex, zipfile, deleteAfter); - } else { + 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; } } @@ -228,8 +260,9 @@ bool GameManager::ExtractFile(struct zip *z, int file_index, std::string outFile zip_stat_index(z, file_index, 0, &zstat); size_t size = zstat.size; + // Don't spam the log. if (file_index < 10) { - INFO_LOG(HLE, "Writing %d bytes to %s", (int)size, outFilename.c_str()); + INFO_LOG(HLE, "Writing %d bytes to '%s'", (int)size, outFilename.c_str()); } zip_file *zf = zip_fopen_index(z, file_index, 0); @@ -390,6 +423,16 @@ bool GameManager::InstallGameOnThread(std::string zipFile, bool deleteAfter) { return true; } +bool GameManager::InstallRawISO(std::string file, std::string originalName) { + std::string destPath = g_Config.currentDirectory + "/" + originalName; + File::Copy(file, destPath); + 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 8e5a745de4..b654fb8655 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -69,9 +69,10 @@ public: bool InstallGameOnThread(std::string zipFile, bool deleteAfter); private: - bool InstallGame(std::string zipfile, bool deleteAfter = false); + bool InstallGame(std::string zipfile, 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(std::string zipFile, std::string originalName); void InstallDone(); bool ExtractFile(struct zip *z, int file_index, std::string outFilename, size_t *bytesCopied, size_t allBytes); @@ -84,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/UI/InstallZipScreen.cpp b/UI/InstallZipScreen.cpp index 943d0c9908..a5ab9d9634 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) { @@ -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; }; From aca0f35bdf380d6c18043ebaa6a7287c79153a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 8 Jul 2019 23:26:22 +0200 Subject: [PATCH 29/31] Fix build error on non-windows --- Core/Util/GameManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 38ae3aeec8..8997ea97b3 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -143,7 +143,7 @@ ZipFileContents DetectZipFileContents(std::string fileName, ZipFileInfo *info) { #ifdef _WIN32 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) { return ZipFileContents::UNKNOWN; @@ -228,7 +228,7 @@ bool GameManager::InstallGame(std::string fileName, bool deleteAfter) { #ifdef _WIN32 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", fileName.c_str(), error); From 66c3f31ff9d17c12ae31bb327014b950af30702b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 10 Jul 2019 23:37:10 +0200 Subject: [PATCH 30/31] More GameManager improvements. --- Core/Util/GameManager.cpp | 29 +++++++++++++++++++---------- Core/Util/GameManager.h | 6 +++--- UI/InstallZipScreen.cpp | 2 +- UI/Store.cpp | 13 ++++++++----- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 8997ea97b3..e17bd5db2a 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -30,6 +30,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" @@ -116,9 +117,10 @@ void GameManager::Update() { return; } // Game downloaded to temporary file - install it! - InstallGameOnThread(fileName, 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()); + 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(); @@ -203,7 +205,7 @@ ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info) { } } -bool GameManager::InstallGame(std::string fileName, bool deleteAfter) { +bool GameManager::InstallGame(std::string url, std::string fileName, bool deleteAfter) { if (installInProgress_) { ERROR_LOG(HLE, "Cannot have two installs in progress at the same time"); return false; @@ -214,9 +216,11 @@ bool GameManager::InstallGame(std::string fileName, bool deleteAfter) { return false; } - bool isRawISO = false; // TODO: Make it possible to pass in this information. - if (isRawISO) { - return InstallRawISO(fileName, fileName); + // 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"); @@ -414,18 +418,23 @@ bool GameManager::InstallZippedISO(struct zip *z, int isoFileIndex, std::string return true; } -bool GameManager::InstallGameOnThread(std::string zipFile, bool deleteAfter) { +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(std::string file, std::string originalName) { +bool GameManager::InstallRawISO(std::string file, std::string originalName, bool deleteAfter) { std::string destPath = g_Config.currentDirectory + "/" + originalName; - File::Copy(file, destPath); + // 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_ = ""; diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index b654fb8655..92ba4bd7e0 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -66,13 +66,13 @@ 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); + bool InstallGame(std::string url, 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(std::string zipFile, std::string originalName); + bool InstallRawISO(std::string zipFile, std::string originalName, bool deleteAfter); void InstallDone(); bool ExtractFile(struct zip *z, int file_index, std::string outFilename, size_t *bytesCopied, size_t allBytes); diff --git a/UI/InstallZipScreen.cpp b/UI/InstallZipScreen.cpp index a5ab9d9634..ea7d206d8a 100644 --- a/UI/InstallZipScreen.cpp +++ b/UI/InstallZipScreen.cpp @@ -81,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); } diff --git a/UI/Store.cpp b/UI/Store.cpp index 58e780dbda..f403505c65 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; } From 274a8d75387ec65f8c21709e6d723cafc23db808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 14 Jul 2019 11:32:32 +0200 Subject: [PATCH 31/31] Address review feedback, thanks unknownbrackets --- Core/Util/GameManager.cpp | 67 +++++++++++++++++++++++---------------- Core/Util/GameManager.h | 4 +-- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index e17bd5db2a..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 @@ -74,7 +76,7 @@ bool GameManager::DownloadAndInstall(std::string storeFileUrl) { return true; } -bool GameManager::CancelDownload() { +bool GameManager::CancelDownload() { if (!curDownload_) return false; @@ -89,30 +91,30 @@ 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()); + INFO_LOG(HLE, "Download completed! Status = %d", curDownload_->ResultCode()); std::string fileName = curDownload_->outfile(); if (curDownload_->ResultCode() == 200) { if (!File::Exists(fileName)) { - ERROR_LOG(HLE, "Downloaded file %s does not exist :(", fileName.c_str()); + ERROR_LOG(HLE, "Downloaded file '%s' does not exist :(", fileName.c_str()); curDownload_.reset(); return; } @@ -155,6 +157,12 @@ ZipFileContents DetectZipFileContents(std::string fileName, ZipFileInfo *info) { 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); @@ -169,18 +177,19 @@ ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info) { 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) { + 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); - // TODO: Rewrite this... if (slashCount >= 1 && (!isPSPMemstickGame || slashLocation < stripChars + 1)) { stripChars = slashLocation + 1; isPSPMemstickGame = true; } else { - INFO_LOG(HLE, "Wrong number of slashes (%i) in %s", slashCount, zippedName.c_str()); + INFO_LOG(HLE, "Wrong number of slashes (%i) in '%s'", slashCount, fn); } - } else if (endsWithNoCase(zippedName, ".iso") || endsWithNoCase(zippedName, ".cso")) { + } else if (endsWith(zippedName, ".iso") || endsWith(zippedName, ".cso")) { int slashCount = 0; int slashLocation = -1; countSlashes(zippedName, &slashLocation, &slashCount); @@ -205,14 +214,14 @@ ZipFileContents DetectZipFileContents(struct zip *z, ZipFileInfo *info) { } } -bool GameManager::InstallGame(std::string url, std::string fileName, bool deleteAfter) { +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(fileName)) { - ERROR_LOG(HLE, "Game file %s doesn't exist", fileName.c_str()); + ERROR_LOG(HLE, "Game file '%s' doesn't exist", fileName.c_str()); return false; } @@ -235,7 +244,7 @@ bool GameManager::InstallGame(std::string url, std::string fileName, bool delete 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", fileName.c_str(), error); + ERROR_LOG(HLE, "Failed to open ZIP file '%s', error code=%i", fileName.c_str(), error); return false; } @@ -276,21 +285,28 @@ bool GameManager::ExtractFile(struct zip *z, int file_index, std::string outFile 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 %d bytes out of %d - Disk full?", (int)written, (int)bs); + 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; - buffer = 0; fclose(f); zip_fclose(zf); File::Delete(outFilename.c_str()); return false; } - pos += bs; + 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 += bs; + *bytesCopied += readSize; installProgress_ = (float)*bytesCopied / (float)allBytes; } zip_fclose(zf); @@ -370,10 +386,7 @@ bail: installProgress_ = 0.0f; installInProgress_ = false; installError_ = sy->T("Storage full"); - // Should we really delete in this case??? - 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()); } @@ -427,7 +440,7 @@ bool GameManager::InstallGameOnThread(std::string url, std::string fileName, boo return true; } -bool GameManager::InstallRawISO(std::string file, std::string originalName, bool deleteAfter) { +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)) { diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 92ba4bd7e0..48062f76d7 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -69,10 +69,10 @@ public: bool InstallGameOnThread(std::string url, std::string tempFileName, bool deleteAfter); private: - bool InstallGame(std::string url, std::string tempFileName, bool deleteAfter); + 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(std::string zipFile, std::string originalName, 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);