/ src / common / android_storage.cpp
android_storage.cpp
  1  // Copyright 2023 Citra Emulator Project
  2  // Licensed under GPLv2 or any later version
  3  // Refer to the license.txt file included.
  4  
  5  #ifdef ANDROID
  6  #include "common/android_storage.h"
  7  
  8  namespace AndroidStorage {
  9  JNIEnv* GetEnvForThread() {
 10      thread_local static struct OwnedEnv {
 11          OwnedEnv() {
 12              status = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
 13              if (status == JNI_EDETACHED)
 14                  g_jvm->AttachCurrentThread(&env, nullptr);
 15          }
 16  
 17          ~OwnedEnv() {
 18              if (status == JNI_EDETACHED)
 19                  g_jvm->DetachCurrentThread();
 20          }
 21  
 22          int status;
 23          JNIEnv* env = nullptr;
 24      } owned;
 25      return owned.env;
 26  }
 27  
 28  AndroidOpenMode ParseOpenmode(const std::string_view openmode) {
 29      AndroidOpenMode android_open_mode = AndroidOpenMode::NEVER;
 30      const char* mode = openmode.data();
 31      int o = 0;
 32      switch (*mode++) {
 33      case 'r':
 34          android_open_mode = AndroidStorage::AndroidOpenMode::READ;
 35          break;
 36      case 'w':
 37          android_open_mode = AndroidStorage::AndroidOpenMode::WRITE;
 38          o = O_TRUNC;
 39          break;
 40      case 'a':
 41          android_open_mode = AndroidStorage::AndroidOpenMode::WRITE;
 42          o = O_APPEND;
 43          break;
 44      }
 45  
 46      // [rwa]\+ or [rwa]b\+ means read and write
 47      if (*mode == '+' || (*mode == 'b' && mode[1] == '+')) {
 48          android_open_mode = AndroidStorage::AndroidOpenMode::READ_WRITE;
 49      }
 50  
 51      return android_open_mode | o;
 52  }
 53  
 54  void InitJNI(JNIEnv* env, jclass clazz) {
 55      env->GetJavaVM(&g_jvm);
 56      native_library = clazz;
 57  
 58  #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature)                   \
 59      F(JMethodID, JMethodName, Signature)
 60  #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature)               \
 61      F(JMethodID, JMethodName, Signature)
 62  #define F(JMethodID, JMethodName, Signature)                                                       \
 63      JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
 64      ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
 65      ANDROID_STORAGE_FUNCTIONS(FS)
 66  #undef F
 67  #undef FS
 68  #undef FR
 69  }
 70  
 71  void CleanupJNI() {
 72  #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
 73  #define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
 74  #define F(JMethodID) JMethodID = nullptr;
 75      ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
 76      ANDROID_STORAGE_FUNCTIONS(FS)
 77  #undef F
 78  #undef FS
 79  #undef FR
 80  }
 81  
 82  bool CreateFile(const std::string& directory, const std::string& filename) {
 83      if (create_file == nullptr)
 84          return false;
 85      auto env = GetEnvForThread();
 86      jstring j_directory = env->NewStringUTF(directory.c_str());
 87      jstring j_filename = env->NewStringUTF(filename.c_str());
 88      return env->CallStaticBooleanMethod(native_library, create_file, j_directory, j_filename);
 89  }
 90  
 91  bool CreateDir(const std::string& directory, const std::string& filename) {
 92      if (create_dir == nullptr)
 93          return false;
 94      auto env = GetEnvForThread();
 95      jstring j_directory = env->NewStringUTF(directory.c_str());
 96      jstring j_directory_name = env->NewStringUTF(filename.c_str());
 97      return env->CallStaticBooleanMethod(native_library, create_dir, j_directory, j_directory_name);
 98  }
 99  
100  int OpenContentUri(const std::string& filepath, AndroidOpenMode openmode) {
101      if (open_content_uri == nullptr)
102          return -1;
103  
104      const char* mode = "";
105      switch (openmode) {
106      case AndroidOpenMode::READ:
107          mode = "r";
108          break;
109      case AndroidOpenMode::WRITE:
110          mode = "w";
111          break;
112      case AndroidOpenMode::READ_WRITE:
113          mode = "rw";
114          break;
115      case AndroidOpenMode::WRITE_TRUNCATE:
116          mode = "wt";
117          break;
118      case AndroidOpenMode::WRITE_APPEND:
119          mode = "wa";
120          break;
121      case AndroidOpenMode::READ_WRITE_APPEND:
122          mode = "rwa";
123          break;
124      case AndroidOpenMode::READ_WRITE_TRUNCATE:
125          mode = "rwt";
126          break;
127      case AndroidOpenMode::NEVER:
128          return -1;
129      }
130      auto env = GetEnvForThread();
131      jstring j_filepath = env->NewStringUTF(filepath.c_str());
132      jstring j_mode = env->NewStringUTF(mode);
133      return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode);
134  }
135  
136  std::vector<std::string> GetFilesName(const std::string& filepath) {
137      auto vector = std::vector<std::string>();
138      if (get_files_name == nullptr)
139          return vector;
140      auto env = GetEnvForThread();
141      jstring j_filepath = env->NewStringUTF(filepath.c_str());
142      auto j_object =
143          (jobjectArray)env->CallStaticObjectMethod(native_library, get_files_name, j_filepath);
144      const jsize j_size = env->GetArrayLength(j_object);
145      vector.reserve(j_size);
146      for (int i = 0; i < j_size; i++) {
147          auto string = (jstring)(env->GetObjectArrayElement(j_object, i));
148          vector.emplace_back(env->GetStringUTFChars(string, nullptr));
149      }
150      return vector;
151  }
152  
153  bool CopyFile(const std::string& source, const std::string& destination_path,
154                const std::string& destination_filename) {
155      if (copy_file == nullptr)
156          return false;
157      auto env = GetEnvForThread();
158      jstring j_source_path = env->NewStringUTF(source.c_str());
159      jstring j_destination_path = env->NewStringUTF(destination_path.c_str());
160      jstring j_destination_filename = env->NewStringUTF(destination_filename.c_str());
161      return env->CallStaticBooleanMethod(native_library, copy_file, j_source_path,
162                                          j_destination_path, j_destination_filename);
163  }
164  
165  bool RenameFile(const std::string& source, const std::string& filename) {
166      if (rename_file == nullptr)
167          return false;
168      auto env = GetEnvForThread();
169      jstring j_source_path = env->NewStringUTF(source.c_str());
170      jstring j_destination_path = env->NewStringUTF(filename.c_str());
171      return env->CallStaticBooleanMethod(native_library, rename_file, j_source_path,
172                                          j_destination_path);
173  }
174  
175  #define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature)                   \
176      F(FunctionName, ReturnValue, JMethodID, Caller)
177  #define F(FunctionName, ReturnValue, JMethodID, Caller)                                            \
178      ReturnValue FunctionName(const std::string& filepath) {                                        \
179          if (JMethodID == nullptr) {                                                                \
180              return 0;                                                                              \
181          }                                                                                          \
182          auto env = GetEnvForThread();                                                              \
183          jstring j_filepath = env->NewStringUTF(filepath.c_str());                                  \
184          return env->Caller(native_library, JMethodID, j_filepath);                                 \
185      }
186  ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
187  #undef F
188  #undef FR
189  
190  } // namespace AndroidStorage
191  #endif