Assorted plumbing.

wip

Buildfix
This commit is contained in:
Henrik Rydgård 2021-04-24 22:47:57 +02:00
parent beb0b4e9f3
commit b397e5c455
16 changed files with 195 additions and 120 deletions

View file

@ -1156,6 +1156,7 @@ list(APPEND NativeAppSource
UI/Store.cpp
UI/CwCheatScreen.cpp
UI/InstallZipScreen.cpp
UI/MemStickScreen.cpp
UI/ProfilerDraw.cpp
UI/TextureUtil.cpp
UI/ComboKeyMappingScreen.cpp

View file

@ -24,6 +24,8 @@
#include "ppsspp_config.h"
#include "android/jni/app-android.h"
#ifdef __MINGW32__
#include <unistd.h>
#ifndef _POSIX_THREAD_SAFE_FUNCTIONS
@ -257,7 +259,9 @@ bool IsDirectory(const Path &filename) {
WIN32_FILE_ATTRIBUTE_DATA data{};
if (!GetFileAttributesEx(copy.c_str(), GetFileExInfoStandard, &data) || data.dwFileAttributes == INVALID_FILE_ATTRIBUTES) {
auto err = GetLastError();
WARN_LOG(COMMON, "GetFileAttributes failed on %s: %08x %s", fn.c_str(), (uint32_t)err, GetStringErrorMsg(err).c_str());
if (err != ERROR_FILE_NOT_FOUND) {
WARN_LOG(COMMON, "GetFileAttributes failed on %s: %08x %s", fn.c_str(), (uint32_t)err, GetStringErrorMsg(err).c_str());
}
return false;
}
DWORD result = data.dwFileAttributes;

View file

@ -27,6 +27,8 @@
#include "Common/Common.h"
#include "Common/File/Path.h"
// Some functions here support Android content URIs. These are marked as such.
#ifdef _MSC_VER
inline struct tm* localtime_r(const time_t *clock, struct tm *result) {
if (localtime_s(result, clock) == 0)

View file

@ -121,8 +121,12 @@ bool AndroidDirectoryFileHandle::Open(const std::string &basePath, std::string &
success = false;
}
// Seek to end if append mode.
Seek(0, FILEMOVE_END);
// TODO: Experiment with fstat to see if we can extract more info.
// Seek to end to simulate append mode, if requested.
if (access & FILEACCESS_APPEND) {
Seek(0, FILEMOVE_END);
}
// Try to detect reads/writes to PSP/GAME to avoid them in replays.
if (basePath.find("/PSP/GAME/") != std::string::npos) {
@ -439,10 +443,11 @@ std::vector<PSPFileInfo> AndroidStorageFileSystem::GetDirListing(std::string pat
bool hideISOFiles = PSP_CoreParameter().compat.flags().HideISOFiles;
for (auto &info : fileInfo) {
PSPFileInfo entry;
if (info.isDirectory)
if (info.isDirectory) {
entry.type = FILETYPE_DIRECTORY;
else
} else {
entry.type = FILETYPE_NORMAL;
}
entry.access = info.isWritable ? 0777 : 0666;
entry.name = info.name;
if (Flags() & FileSystemFlags::SIMULATE_FAT32) {
@ -466,8 +471,9 @@ std::vector<PSPFileInfo> AndroidStorageFileSystem::GetDirListing(std::string pat
localtime_r((time_t*)&atime, &entry.atime);
localtime_r((time_t*)&ctime, &entry.ctime);
localtime_r((time_t*)&mtime, &entry.mtime);
if (!hideFile && (!listingRoot || (strcmp(info.name.c_str(), "..") && strcmp(info.name.c_str(), "."))))
if (!hideFile && (!listingRoot || (strcmp(info.name.c_str(), "..") && strcmp(info.name.c_str(), ".")))) {
myVector.push_back(entry);
}
}
return ReplayApplyDiskListing(myVector, CoreTiming::GetGlobalTimeUs());

View file

@ -285,8 +285,7 @@ int VirtualDiscFileSystem::getFileListIndex(std::string &fileName)
#endif
}
FileType type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
if (type == FILETYPE_DIRECTORY)
if (File::IsDirectory(fullName))
return -1;
FileListEntry entry = {""};

View file

@ -51,6 +51,7 @@
#include "UI/TiltEventProcessor.h"
#include "UI/ComboKeyMappingScreen.h"
#include "UI/GPUDriverTestScreen.h"
#include "UI/MemStickScreen.h"
#include "Common/File/FileUtil.h"
#include "Common/OSVersion.h"
@ -931,6 +932,7 @@ void GameSettingsScreen::CreateViews() {
#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)
systemSettings->Add(new CheckBox(&g_Config.bBypassOSKWithKeyboard, sy->T("Use system native keyboard")));
#endif
#if PPSSPP_PLATFORM(ANDROID)
memstickDisplay_ = g_Config.memStickDirectory.ToVisualString();
auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&memstickDisplay_, sy->T("Change Memory Stick folder"), (const char *)nullptr));
@ -1098,40 +1100,12 @@ UI::EventReturn GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) {
return UI::EVENT_DONE;
}
#if PPSSPP_PLATFORM(ANDROID)
UI::EventReturn GameSettingsScreen::OnChangeMemStickDir(UI::EventParams &e) {
auto sy = GetI18NCategory("System");
System_InputBoxGetString(sy->T("Memory Stick Folder"), g_Config.memStickDirectory.ToString(), [&](bool result, const std::string &value) {
auto sy = GetI18NCategory("System");
auto di = GetI18NCategory("Dialog");
if (result) {
std::string newPath = value;
size_t pos = newPath.find_last_not_of("/");
// Gotta have at least something but a /, and also needs to start with a /.
if (newPath.empty() || pos == newPath.npos || newPath[0] != '/') {
settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr);
return;
}
if (pos != newPath.size() - 1) {
newPath = newPath.substr(0, pos + 1);
}
pendingMemstickFolder_ = newPath;
std::string promptMessage = sy->T("ChangingMemstickPath", "Save games, save states, and other data will not be copied to this folder.\n\nChange the Memory Stick folder?");
if (!File::Exists(Path(newPath))) {
promptMessage = sy->T("ChangingMemstickPathNotExists", "That folder doesn't exist yet.\n\nSave games, save states, and other data will not be copied to this folder.\n\nCreate a new Memory Stick folder?");
}
// Add the path for clarity and proper confirmation.
promptMessage += "\n\n" + newPath + "/";
screenManager()->push(new PromptScreen(promptMessage, di->T("Yes"), di->T("No"), std::bind(&GameSettingsScreen::CallbackMemstickFolder, this, std::placeholders::_1)));
}
});
screenManager()->push(new MemStickScreen());
return UI::EVENT_DONE;
}
#elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
UI::EventReturn GameSettingsScreen::OnSavePathMydoc(UI::EventParams &e) {
const Path &PPSSPPpath = File::GetExeDirectory();
@ -2006,52 +1980,3 @@ void HostnameSelectScreen::OnCompleted(DialogResult result) {
if (result == DR_OK)
*value_ = StripSpaces(addrView_->GetText());
}
SettingInfoMessage::SettingInfoMessage(int align, UI::AnchorLayoutParams *lp)
: UI::LinearLayout(UI::ORIENT_HORIZONTAL, lp) {
using namespace UI;
SetSpacing(0.0f);
Add(new UI::Spacer(10.0f));
text_ = Add(new UI::TextView("", align, false, new LinearLayoutParams(1.0, Margins(0, 10))));
Add(new UI::Spacer(10.0f));
}
void SettingInfoMessage::Show(const std::string &text, UI::View *refView) {
if (refView) {
Bounds b = refView->GetBounds();
const UI::AnchorLayoutParams *lp = GetLayoutParams()->As<UI::AnchorLayoutParams>();
if (b.y >= cutOffY_) {
ReplaceLayoutParams(new UI::AnchorLayoutParams(lp->width, lp->height, lp->left, 80.0f, lp->right, lp->bottom, lp->center));
} else {
ReplaceLayoutParams(new UI::AnchorLayoutParams(lp->width, lp->height, lp->left, dp_yres - 80.0f - 40.0f, lp->right, lp->bottom, lp->center));
}
}
text_->SetText(text);
timeShown_ = time_now_d();
}
void SettingInfoMessage::Draw(UIContext &dc) {
static const double FADE_TIME = 1.0;
static const float MAX_ALPHA = 0.9f;
// Let's show longer messages for more time (guesstimate at reading speed.)
// Note: this will give multibyte characters more time, but they often have shorter words anyway.
double timeToShow = std::max(1.5, text_->GetText().size() * 0.05);
double sinceShow = time_now_d() - timeShown_;
float alpha = MAX_ALPHA;
if (timeShown_ == 0.0 || sinceShow > timeToShow + FADE_TIME) {
alpha = 0.0f;
} else if (sinceShow > timeToShow) {
alpha = MAX_ALPHA - MAX_ALPHA * (float)((sinceShow - timeToShow) / FADE_TIME);
}
if (alpha >= 0.1f) {
UI::Style style = dc.theme->popupTitle;
style.background.color = colorAlpha(style.background.color, alpha - 0.1f);
dc.FillRect(style.background, bounds_);
}
text_->SetTextColor(whiteAlpha(alpha));
ViewGroup::Draw(dc);
}

View file

@ -24,8 +24,6 @@
#include "Common/UI/UIScreen.h"
#include "UI/MiscScreens.h"
class SettingInfoMessage;
// Per-game settings screen - enables you to configure graphic options, control options, etc
// per game.
class GameSettingsScreen : public UIDialogScreenWithGameBackground {
@ -108,9 +106,8 @@ private:
UI::EventReturn OnMicDeviceChange(UI::EventParams& e);
UI::EventReturn OnAudioDevice(UI::EventParams &e);
UI::EventReturn OnJitAffectingSetting(UI::EventParams &e);
#if PPSSPP_PLATFORM(ANDROID)
UI::EventReturn OnChangeMemStickDir(UI::EventParams &e);
#elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
UI::EventReturn OnSavePathMydoc(UI::EventParams &e);
UI::EventReturn OnSavePathOther(UI::EventParams &e);
#endif
@ -141,23 +138,6 @@ private:
std::string pendingMemstickFolder_;
};
class SettingInfoMessage : public UI::LinearLayout {
public:
SettingInfoMessage(int align, UI::AnchorLayoutParams *lp);
void SetBottomCutoff(float y) {
cutOffY_ = y;
}
void Show(const std::string &text, UI::View *refView = nullptr);
void Draw(UIContext &dc);
private:
UI::TextView *text_ = nullptr;
double timeShown_ = 0.0;
float cutOffY_;
};
class DeveloperToolsScreen : public UIDialogScreenWithBackground {
public:
DeveloperToolsScreen() {}

View file

@ -948,3 +948,52 @@ void CreditsScreen::render() {
dc.Flush();
}
SettingInfoMessage::SettingInfoMessage(int align, UI::AnchorLayoutParams *lp)
: UI::LinearLayout(UI::ORIENT_HORIZONTAL, lp) {
using namespace UI;
SetSpacing(0.0f);
Add(new UI::Spacer(10.0f));
text_ = Add(new UI::TextView("", align, false, new LinearLayoutParams(1.0, Margins(0, 10))));
Add(new UI::Spacer(10.0f));
}
void SettingInfoMessage::Show(const std::string &text, UI::View *refView) {
if (refView) {
Bounds b = refView->GetBounds();
const UI::AnchorLayoutParams *lp = GetLayoutParams()->As<UI::AnchorLayoutParams>();
if (b.y >= cutOffY_) {
ReplaceLayoutParams(new UI::AnchorLayoutParams(lp->width, lp->height, lp->left, 80.0f, lp->right, lp->bottom, lp->center));
} else {
ReplaceLayoutParams(new UI::AnchorLayoutParams(lp->width, lp->height, lp->left, dp_yres - 80.0f - 40.0f, lp->right, lp->bottom, lp->center));
}
}
text_->SetText(text);
timeShown_ = time_now_d();
}
void SettingInfoMessage::Draw(UIContext &dc) {
static const double FADE_TIME = 1.0;
static const float MAX_ALPHA = 0.9f;
// Let's show longer messages for more time (guesstimate at reading speed.)
// Note: this will give multibyte characters more time, but they often have shorter words anyway.
double timeToShow = std::max(1.5, text_->GetText().size() * 0.05);
double sinceShow = time_now_d() - timeShown_;
float alpha = MAX_ALPHA;
if (timeShown_ == 0.0 || sinceShow > timeToShow + FADE_TIME) {
alpha = 0.0f;
} else if (sinceShow > timeToShow) {
alpha = MAX_ALPHA - MAX_ALPHA * (float)((sinceShow - timeToShow) / FADE_TIME);
}
if (alpha >= 0.1f) {
UI::Style style = dc.theme->popupTitle;
style.background.color = colorAlpha(style.background.color, alpha - 0.1f);
dc.FillRect(style.background, bounds_);
}
text_->SetTextColor(whiteAlpha(alpha));
ViewGroup::Draw(dc);
}

View file

@ -164,3 +164,20 @@ private:
double startTime_ = 0.0;
};
class SettingInfoMessage : public UI::LinearLayout {
public:
SettingInfoMessage(int align, UI::AnchorLayoutParams *lp);
void SetBottomCutoff(float y) {
cutOffY_ = y;
}
void Show(const std::string &text, UI::View *refView = nullptr);
void Draw(UIContext &dc);
private:
UI::TextView *text_ = nullptr;
double timeShown_ = 0.0;
float cutOffY_;
};

View file

@ -511,10 +511,12 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
Path memstickPath(memstickDir);
if (!memstickPath.empty() && File::Exists(memstickPath)) {
g_Config.memStickDirectory = memstickPath;
INFO_LOG(SYSTEM, "Memstick Directory from memstick_dir.txt: %s", g_Config.memStickDirectory.c_str());
} else {
ERROR_LOG(SYSTEM, "Couldn't read directory '%s' specified by memstick_dir.txt.", memstickDir.c_str());
ERROR_LOG(SYSTEM, "Couldn't read directory '%s' specified by memstick_dir.txt.", memstickDir.c_str());
}
}
#elif PPSSPP_PLATFORM(IOS)
g_Config.defaultCurrentDirectory = g_Config.internalDataDirectory;
g_Config.memStickDirectory = Path(user_data_path);

View file

@ -51,6 +51,7 @@
<ClCompile Include="GameSettingsScreen.cpp" />
<ClCompile Include="GPUDriverTestScreen.cpp" />
<ClCompile Include="MainScreen.cpp" />
<ClCompile Include="MemStickScreen.cpp" />
<ClCompile Include="MiscScreens.cpp" />
<ClCompile Include="NativeApp.cpp" />
<ClCompile Include="OnScreenDisplay.cpp" />
@ -85,6 +86,7 @@
<ClInclude Include="GPUDriverTestScreen.h" />
<ClInclude Include="HostTypes.h" />
<ClInclude Include="MainScreen.h" />
<ClInclude Include="MemStickScreen.h" />
<ClInclude Include="MiscScreens.h" />
<ClInclude Include="OnScreenDisplay.h" />
<ClInclude Include="PauseScreen.h" />
@ -394,4 +396,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View file

@ -78,6 +78,9 @@
<ClCompile Include="Store.cpp">
<Filter>Screens</Filter>
</ClCompile>
<ClCompile Include="MemStickScreen.cpp">
<Filter>Screens</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="GameInfoCache.h" />
@ -157,6 +160,9 @@
<ClInclude Include="Store.h">
<Filter>Screens</Filter>
</ClInclude>
<ClInclude Include="MemStickScreen.h">
<Filter>Screens</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Screens">

View file

@ -9,9 +9,12 @@
// content://com.android.externalstorage.documents/tree/primary%3APSP%20ISO
// content://com.android.externalstorage.documents/tree/primary%3APSP%20ISO/document/primary%3APSP%20ISO
// This file compiles on all platforms, to reduce the need for ifdefs.
// I am not 100% sure it's OK to rely on the internal format of file content URIs.
// On the other hand, I'm sure tons of apps would break if these changed, so I think we can
// consider them pretty stable.
// consider them pretty stable. Additionally, the official Document library just manipulates the URIs
// in similar ways...
class AndroidStorageContentURI {
private:
std::string provider;

View file

@ -177,6 +177,8 @@ static jmethodID contentUriCreateFile;
static jmethodID contentUriCreateDirectory;
static jmethodID contentUriRemoveFile;
static jmethodID contentUriGetFileInfo;
static jmethodID contentUriGetFreeStorageSpace;
static jmethodID filePathGetFreeStorageSpace;
static jobject nativeActivity;
static volatile bool exitRenderLoop;
@ -342,6 +344,21 @@ std::vector<File::FileInfo> Android_ListContentUri(const std::string &path) {
return items;
}
int64_t Android_GetFreeSpaceByContentUri(const std::string &uri) {
auto env = getEnv();
jstring param = env->NewStringUTF(uri.c_str());
return env->CallLongMethod(nativeActivity, contentUriGetFreeStorageSpace, param);
}
int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath) {
auto env = getEnv();
jstring param = env->NewStringUTF(filePath.c_str());
return env->CallLongMethod(nativeActivity, filePathGetFreeStorageSpace, param);
}
class ContentURIFileLoader : public ProxiedFileLoader {
public:
ContentURIFileLoader(const Path &filename)
@ -627,6 +644,10 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeActivity_registerCallbacks(JNIEnv *
_dbg_assert_(contentUriRemoveFile);
contentUriGetFileInfo = env->GetMethodID(env->GetObjectClass(obj), "contentUriGetFileInfo", "(Ljava/lang/String;)Ljava/lang/String;");
_dbg_assert_(contentUriGetFileInfo);
contentUriGetFreeStorageSpace = env->GetMethodID(env->GetObjectClass(obj), "contentUriGetFreeStorageSpace", "(Ljava/lang/String;)J");
_dbg_assert_(contentUriGetFreeStorageSpace);
filePathGetFreeStorageSpace = env->GetMethodID(env->GetObjectClass(obj), "filePathGetFreeStorageSpace", "(Ljava/lang/String;)J");
_dbg_assert_(filePathGetFreeStorageSpace);
}
extern "C" void Java_org_ppsspp_ppsspp_NativeActivity_unregisterCallbacks(JNIEnv *env, jobject obj) {

View file

@ -4,10 +4,19 @@
#include <string>
#include <vector>
#include <cstdint>
#include "Common/LogManager.h"
#include "Common/File/DirListing.h"
// To emphasize that Android storage mode strings are different, let's just use
// an enum.
enum class Android_OpenContentUriMode {
READ = 0, // "r"
READ_WRITE = 1, // "rw"
READ_WRITE_TRUNCATE = 2, // "rwt"
};
#if PPSSPP_PLATFORM(ANDROID)
#include <jni.h>
@ -25,21 +34,30 @@ extern std::string g_extFilesDir;
// Called from PathBrowser for example.
bool Android_IsContentUri(const std::string &uri);
// To emphasize that Android storage mode strings are different, let's just use
// an enum.
enum class Android_OpenContentUriMode {
READ = 0, // "r"
READ_WRITE = 1, // "rw"
READ_WRITE_TRUNCATE = 2, // "rwt"
};
int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode);
bool Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName);
bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName);
bool Android_RemoveFile(const std::string &fileUri);
bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *info);
int64_t Android_GetFreeSpaceByContentUri(const std::string &uri);
int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath);
std::vector<File::FileInfo> Android_ListContentUri(const std::string &uri);
#else
// Stub out the Android Storage wrappers, so that we can avoid ifdefs everywhere.
inline bool Android_IsContentUri(const std::string &uri) { return false; }
inline int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode) { return -1; }
inline bool Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName) { return false; }
inline bool Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) { return false; }
inline bool Android_RemoveFile(const std::string &fileUri) { return false; }
inline bool Android_GetFileInfo(const std::string &fileUri, File::FileInfo *info) { return false; }
inline int64_t Android_GetFreeSpaceByContentUri(const std::string &uri) { return -1; }
inline int64_t Android_GetFreeSpaceByFilePath(const std::string &filePath) { return -1; }
inline std::vector<File::FileInfo> Android_ListContentUri(const std::string &uri) {
return std::vector<File::FileInfo>();
}
#endif

View file

@ -8,8 +8,11 @@ import android.os.Bundle;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.os.storage.StorageManager;
import androidx.documentfile.provider.DocumentFile;
import java.util.ArrayList;
import java.util.UUID;
import java.io.File;
public class PpssppActivity extends NativeActivity {
private static final String TAG = "PpssppActivity";
@ -223,4 +226,41 @@ public class PpssppActivity extends NativeActivity {
return null;
}
}
// The example in Android documentation uses this.getFilesDir for path.
// There's also a way to beg the OS for more space, which might clear caches, but
// let's just not bother with that for now.
public long contentUriGetFreeStorageSpace(String uriString) {
try {
StorageManager storageManager = getApplicationContext().getSystemService(StorageManager.class);
// In 29 and later, we can directly get the UUID for the storage volume
// through the URI.
UUID volumeUUID;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Uri uri = Uri.parse(uriString);
volumeUUID = UUID.fromString(storageManager.getStorageVolume(uri).getUuid());
} else {
volumeUUID = storageManager.getUuidForPath(this.getFilesDir());
}
long availableBytes = storageManager.getAllocatableBytes(volumeUUID);
return availableBytes;
} catch (Exception e) {
Log.e(TAG, "Exception checking free space: " + e.toString());
return -1;
}
}
public long filePathGetFreeStorageSpace(String filePath) {
try {
StorageManager storageManager = getApplicationContext().getSystemService(StorageManager.class);
File file = new File(filePath);
UUID volumeUUID = storageManager.getUuidForPath(file);
long availableBytes = storageManager.getAllocatableBytes(volumeUUID);
return availableBytes;
} catch (Exception e) {
Log.e(TAG, "Exception checking free space: " + e.toString());
return -1;
}
}
}