diff --git a/Source/Android/jni/AndroidCommon/AndroidCommon.cpp b/Source/Android/jni/AndroidCommon/AndroidCommon.cpp index 310db6b1f7..2870044ac9 100644 --- a/Source/Android/jni/AndroidCommon/AndroidCommon.cpp +++ b/Source/Android/jni/AndroidCommon/AndroidCommon.cpp @@ -4,6 +4,7 @@ #include "jni/AndroidCommon/AndroidCommon.h" +#include #include #include #include @@ -66,6 +67,28 @@ std::string OpenModeToAndroid(std::string mode) return mode; } +std::string OpenModeToAndroid(std::ios_base::openmode mode) +{ + std::string result; + + if (mode & std::ios_base::in) + result += 'r'; + + if (mode & (std::ios_base::out | std::ios_base::app)) + result += 'w'; + + if (mode & std::ios_base::app) + result += 'a'; + + constexpr std::ios_base::openmode t = std::ios_base::in | std::ios_base::trunc; + if ((mode & t) == t) + result += 't'; + + // The 'b' specifier is not supported. Since we're on POSIX, it's fine to just skip it. + + return result; +} + int OpenAndroidContent(const std::string& uri, const std::string& mode) { JNIEnv* env = IDCache::GetEnvForThread(); diff --git a/Source/Android/jni/AndroidCommon/AndroidCommon.h b/Source/Android/jni/AndroidCommon/AndroidCommon.h index 7ff745f047..a196108cfe 100644 --- a/Source/Android/jni/AndroidCommon/AndroidCommon.h +++ b/Source/Android/jni/AndroidCommon/AndroidCommon.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -17,6 +18,7 @@ bool IsPathAndroidContent(const std::string& uri); // Turns a C/C++ style mode (e.g. "rb") into one which can be used with OpenAndroidContent. std::string OpenModeToAndroid(std::string mode); +std::string OpenModeToAndroid(std::ios_base::openmode mode); // Opens a given file and returns a file descriptor. int OpenAndroidContent(const std::string& uri, const std::string& mode); diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 7e0b2e6990..73ba41d2da 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -18,6 +18,11 @@ #include "Common/StringUtil.h" #endif +#ifdef ANDROID +#include "Common/StringUtil.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#endif + // User directory indices for GetUserPath enum { @@ -213,14 +218,20 @@ std::string GetExeDirectory(); bool WriteStringToFile(const std::string& filename, std::string_view str); bool ReadFileToString(const std::string& filename, std::string& str); -// To deal with Windows being dumb at unicode: +// To deal with Windows not fully supporting UTF-8 and Android not fully supporting paths. template void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) { #ifdef _WIN32 fstream.open(UTF8ToTStr(filename).c_str(), openmode); #else - fstream.open(filename.c_str(), openmode); +#ifdef ANDROID + // Unfortunately it seems like the non-standard __open is the only way to use a file descriptor + if (IsPathAndroidContent(filename)) + fstream.__open(OpenAndroidContent(filename, OpenModeToAndroid(openmode)), openmode); + else +#endif + fstream.open(filename.c_str(), openmode); #endif }