android: Fix resolving android URIs in native code
This commit is contained in:
		@@ -5,6 +5,7 @@ package org.yuzu.yuzu_emu
 | 
			
		||||
 | 
			
		||||
import android.app.Dialog
 | 
			
		||||
import android.content.DialogInterface
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.text.Html
 | 
			
		||||
import android.text.method.LinkMovementMethod
 | 
			
		||||
@@ -16,7 +17,7 @@ import androidx.fragment.app.DialogFragment
 | 
			
		||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
 | 
			
		||||
import java.lang.ref.WeakReference
 | 
			
		||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.DocumentsTree
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.FileUtil
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.Log
 | 
			
		||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
 | 
			
		||||
@@ -68,7 +69,7 @@ object NativeLibrary {
 | 
			
		||||
    @Keep
 | 
			
		||||
    @JvmStatic
 | 
			
		||||
    fun openContentUri(path: String?, openmode: String?): Int {
 | 
			
		||||
        return if (isNativePath(path!!)) {
 | 
			
		||||
        return if (DocumentsTree.isNativePath(path!!)) {
 | 
			
		||||
            YuzuApplication.documentsTree!!.openContentUri(path, openmode)
 | 
			
		||||
        } else {
 | 
			
		||||
            FileUtil.openContentUri(path, openmode)
 | 
			
		||||
@@ -78,7 +79,7 @@ object NativeLibrary {
 | 
			
		||||
    @Keep
 | 
			
		||||
    @JvmStatic
 | 
			
		||||
    fun getSize(path: String?): Long {
 | 
			
		||||
        return if (isNativePath(path!!)) {
 | 
			
		||||
        return if (DocumentsTree.isNativePath(path!!)) {
 | 
			
		||||
            YuzuApplication.documentsTree!!.getFileSize(path)
 | 
			
		||||
        } else {
 | 
			
		||||
            FileUtil.getFileSize(path)
 | 
			
		||||
@@ -88,7 +89,7 @@ object NativeLibrary {
 | 
			
		||||
    @Keep
 | 
			
		||||
    @JvmStatic
 | 
			
		||||
    fun exists(path: String?): Boolean {
 | 
			
		||||
        return if (isNativePath(path!!)) {
 | 
			
		||||
        return if (DocumentsTree.isNativePath(path!!)) {
 | 
			
		||||
            YuzuApplication.documentsTree!!.exists(path)
 | 
			
		||||
        } else {
 | 
			
		||||
            FileUtil.exists(path)
 | 
			
		||||
@@ -98,13 +99,31 @@ object NativeLibrary {
 | 
			
		||||
    @Keep
 | 
			
		||||
    @JvmStatic
 | 
			
		||||
    fun isDirectory(path: String?): Boolean {
 | 
			
		||||
        return if (isNativePath(path!!)) {
 | 
			
		||||
        return if (DocumentsTree.isNativePath(path!!)) {
 | 
			
		||||
            YuzuApplication.documentsTree!!.isDirectory(path)
 | 
			
		||||
        } else {
 | 
			
		||||
            FileUtil.isDirectory(path)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Keep
 | 
			
		||||
    @JvmStatic
 | 
			
		||||
    fun getParentDirectory(path: String): String =
 | 
			
		||||
        if (DocumentsTree.isNativePath(path)) {
 | 
			
		||||
            YuzuApplication.documentsTree!!.getParentDirectory(path)
 | 
			
		||||
        } else {
 | 
			
		||||
            path
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @Keep
 | 
			
		||||
    @JvmStatic
 | 
			
		||||
    fun getFilename(path: String): String =
 | 
			
		||||
        if (DocumentsTree.isNativePath(path)) {
 | 
			
		||||
            YuzuApplication.documentsTree!!.getFilename(path)
 | 
			
		||||
        } else {
 | 
			
		||||
            FileUtil.getFilename(Uri.parse(path))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns true if pro controller isn't available and handheld is
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,23 @@ class DocumentsTree {
 | 
			
		||||
        return node != null && node.isDirectory
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getParentDirectory(filepath: String): String {
 | 
			
		||||
        val node = resolvePath(filepath)!!
 | 
			
		||||
        val parentNode = node.parent
 | 
			
		||||
        if (parentNode != null && parentNode.isDirectory) {
 | 
			
		||||
            return parentNode.uri!!.toString()
 | 
			
		||||
        }
 | 
			
		||||
        return node.uri!!.toString()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getFilename(filepath: String): String {
 | 
			
		||||
        val node = resolvePath(filepath)
 | 
			
		||||
        if (node != null) {
 | 
			
		||||
            return node.name!!
 | 
			
		||||
        }
 | 
			
		||||
        return filepath
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun resolvePath(filepath: String): DocumentsNode? {
 | 
			
		||||
        val tokens = StringTokenizer(filepath, File.separator, false)
 | 
			
		||||
        var iterator = root
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,14 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
#include <android/native_window_jni.h>
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/perf_stats.h"
 | 
			
		||||
#include "jni/emu_window/emu_window.h"
 | 
			
		||||
#include "jni/applets/software_keyboard.h"
 | 
			
		||||
#include "video_core/rasterizer_interface.h"
 | 
			
		||||
#include "common/detached_tasks.h"
 | 
			
		||||
#include "core/hle/service/acc/profile_manager.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/file_sys/registered_cache.h"
 | 
			
		||||
#include "core/hle/service/acc/profile_manager.h"
 | 
			
		||||
#include "core/perf_stats.h"
 | 
			
		||||
#include "jni/applets/software_keyboard.h"
 | 
			
		||||
#include "jni/emu_window/emu_window.h"
 | 
			
		||||
#include "video_core/rasterizer_interface.h"
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
 | 
			
		||||
#include "common/fs/fs_android.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
 | 
			
		||||
namespace Common::FS::Android {
 | 
			
		||||
 | 
			
		||||
@@ -28,28 +29,35 @@ void RegisterCallbacks(JNIEnv* env, jclass clazz) {
 | 
			
		||||
    env->GetJavaVM(&g_jvm);
 | 
			
		||||
    native_library = clazz;
 | 
			
		||||
 | 
			
		||||
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature)                                \
 | 
			
		||||
    F(JMethodID, JMethodName, Signature)
 | 
			
		||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature)                   \
 | 
			
		||||
    F(JMethodID, JMethodName, Signature)
 | 
			
		||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature)               \
 | 
			
		||||
    F(JMethodID, JMethodName, Signature)
 | 
			
		||||
#define F(JMethodID, JMethodName, Signature)                                                       \
 | 
			
		||||
    JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
 | 
			
		||||
    ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
 | 
			
		||||
    ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
 | 
			
		||||
    ANDROID_STORAGE_FUNCTIONS(FS)
 | 
			
		||||
#undef F
 | 
			
		||||
#undef FS
 | 
			
		||||
#undef FR
 | 
			
		||||
#undef FH
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UnRegisterCallbacks() {
 | 
			
		||||
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
 | 
			
		||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
 | 
			
		||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
 | 
			
		||||
#define F(JMethodID) JMethodID = nullptr;
 | 
			
		||||
    ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
 | 
			
		||||
    ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
 | 
			
		||||
    ANDROID_STORAGE_FUNCTIONS(FS)
 | 
			
		||||
#undef F
 | 
			
		||||
#undef FS
 | 
			
		||||
#undef FR
 | 
			
		||||
#undef FH
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool IsContentUri(const std::string& path) {
 | 
			
		||||
@@ -95,4 +103,29 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
 | 
			
		||||
#undef F
 | 
			
		||||
#undef FR
 | 
			
		||||
 | 
			
		||||
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature)                                \
 | 
			
		||||
    F(FunctionName, JMethodID, Caller)
 | 
			
		||||
#define F(FunctionName, JMethodID, Caller)                                                         \
 | 
			
		||||
    std::string FunctionName(const std::string& filepath) {                                        \
 | 
			
		||||
        if (JMethodID == nullptr) {                                                                \
 | 
			
		||||
            return 0;                                                                              \
 | 
			
		||||
        }                                                                                          \
 | 
			
		||||
        auto env = GetEnvForThread();                                                              \
 | 
			
		||||
        jstring j_filepath = env->NewStringUTF(filepath.c_str());                                  \
 | 
			
		||||
        jstring j_return =                                                                         \
 | 
			
		||||
            static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath));              \
 | 
			
		||||
        if (!j_return) {                                                                           \
 | 
			
		||||
            return {};                                                                             \
 | 
			
		||||
        }                                                                                          \
 | 
			
		||||
        const jchar* jchars = env->GetStringChars(j_return, nullptr);                              \
 | 
			
		||||
        const jsize length = env->GetStringLength(j_return);                                       \
 | 
			
		||||
        const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length);  \
 | 
			
		||||
        const std::string converted_string = Common::UTF16ToUTF8(string_view);                     \
 | 
			
		||||
        env->ReleaseStringChars(j_return, jchars);                                                 \
 | 
			
		||||
        return converted_string;                                                                   \
 | 
			
		||||
    }
 | 
			
		||||
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
 | 
			
		||||
#undef F
 | 
			
		||||
#undef FH
 | 
			
		||||
 | 
			
		||||
} // namespace Common::FS::Android
 | 
			
		||||
 
 | 
			
		||||
@@ -17,19 +17,28 @@
 | 
			
		||||
      "(Ljava/lang/String;)Z")                                                                     \
 | 
			
		||||
    V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
 | 
			
		||||
 | 
			
		||||
#define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V)                                                    \
 | 
			
		||||
    V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory",      \
 | 
			
		||||
      "(Ljava/lang/String;)Ljava/lang/String;")                                                    \
 | 
			
		||||
    V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename",                            \
 | 
			
		||||
      "(Ljava/lang/String;)Ljava/lang/String;")
 | 
			
		||||
 | 
			
		||||
namespace Common::FS::Android {
 | 
			
		||||
 | 
			
		||||
static JavaVM* g_jvm = nullptr;
 | 
			
		||||
static jclass native_library = nullptr;
 | 
			
		||||
 | 
			
		||||
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
 | 
			
		||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
 | 
			
		||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
 | 
			
		||||
#define F(JMethodID) static jmethodID JMethodID = nullptr;
 | 
			
		||||
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
 | 
			
		||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
 | 
			
		||||
ANDROID_STORAGE_FUNCTIONS(FS)
 | 
			
		||||
#undef F
 | 
			
		||||
#undef FS
 | 
			
		||||
#undef FR
 | 
			
		||||
#undef FH
 | 
			
		||||
 | 
			
		||||
enum class OpenMode {
 | 
			
		||||
    Read,
 | 
			
		||||
@@ -62,4 +71,10 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
 | 
			
		||||
#undef F
 | 
			
		||||
#undef FR
 | 
			
		||||
 | 
			
		||||
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName)
 | 
			
		||||
#define F(FunctionName) std::string FunctionName(const std::string& filepath);
 | 
			
		||||
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
 | 
			
		||||
#undef F
 | 
			
		||||
#undef FH
 | 
			
		||||
 | 
			
		||||
} // namespace Common::FS::Android
 | 
			
		||||
 
 | 
			
		||||
@@ -401,6 +401,16 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string_view GetParentPath(std::string_view path) {
 | 
			
		||||
    if (path.empty()) {
 | 
			
		||||
        return path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef ANDROID
 | 
			
		||||
    if (path[0] != '/') {
 | 
			
		||||
        std::string path_string{path};
 | 
			
		||||
        return FS::Android::GetParentDirectory(path_string);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    const auto name_bck_index = path.rfind('\\');
 | 
			
		||||
    const auto name_fwd_index = path.rfind('/');
 | 
			
		||||
    std::size_t name_index;
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,10 @@
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef ANDROID
 | 
			
		||||
#include <common/fs/fs_android.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace Common {
 | 
			
		||||
 | 
			
		||||
/// Make a string lowercase
 | 
			
		||||
@@ -63,6 +67,14 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
 | 
			
		||||
    if (full_path.empty())
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
#ifdef ANDROID
 | 
			
		||||
    if (full_path[0] != '/') {
 | 
			
		||||
        *_pPath = Common::FS::Android::GetParentDirectory(full_path);
 | 
			
		||||
        *_pFilename = Common::FS::Android::GetFilename(full_path);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    std::size_t dir_end = full_path.find_last_of("/"
 | 
			
		||||
// windows needs the : included for something like just "C:" to be considered a directory
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user