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