diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index b2d4ed8d39..8d193e75ec 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -283,6 +283,8 @@ public final class NativeLibrary */ public static native String GetVersionString(); + public static native String GetGitRevision(); + /** * Saves a screen capture of the game */ @@ -302,11 +304,6 @@ public final class NativeLibrary */ public static native void LoadState(int slot); - /** - * Creates the initial folder structure in /sdcard/dolphin-emu/ - */ - public static native void CreateUserFolders(); - /** * Sets the current working user directory * If not set, it auto-detects a location diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/DirectoryInitializationService.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/DirectoryInitializationService.java index 8f4a8a6bca..7d41df25ac 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/DirectoryInitializationService.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/services/DirectoryInitializationService.java @@ -9,6 +9,8 @@ package org.dolphinemu.dolphinemu.services; import android.app.IntentService; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import org.dolphinemu.dolphinemu.NativeLibrary; @@ -59,7 +61,8 @@ public final class DirectoryInitializationService extends IntentService } else if (PermissionsHandler.hasWriteAccess(this)) { - initDolphinDirectories(); + initializeInternalStorage(); + initializeExternalStorage(); directoryState = DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED; sendBroadcastState(directoryState); } @@ -69,21 +72,55 @@ public final class DirectoryInitializationService extends IntentService } } - private void initDolphinDirectories() + private void initializeInternalStorage() { - String BaseDir = NativeLibrary.GetUserDirectory(); - String ConfigDir = BaseDir + File.separator + "Config"; + File sysDirectory = new File(getFilesDir(), "Sys"); - // Copy assets if needed - NativeLibrary.CreateUserFolders(); - copyAssetFolder("GC", BaseDir + File.separator + "GC", false); - copyAssetFolder("Shaders", BaseDir + File.separator + "Shaders", false); - copyAssetFolder("Wii", BaseDir + File.separator + "Wii", false); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + String revision = NativeLibrary.GetGitRevision(); + if (!preferences.getString("sysDirectoryVersion", "").equals(revision)) + { + // There is no extracted Sys directory, or there is a Sys directory from another + // version of Dolphin that might contain outdated files. Let's (re-)extract Sys. + deleteDirectoryRecursively(sysDirectory); + copyAssetFolder("Sys", sysDirectory, true); - // Always copy over the GCPad config in case of change or corruption. - // Not a user configurable file. - copyAsset("GCPadNew.ini", ConfigDir + File.separator + "GCPadNew.ini", true); - copyAsset("WiimoteNew.ini", ConfigDir + File.separator + "WiimoteNew.ini", false); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString("sysDirectoryVersion", revision); + editor.apply(); + } + + // Let the native code know where the Sys directory is. + SetSysDirectory(sysDirectory.getPath()); + } + + private void initializeExternalStorage() + { + // Create User directory structure and copy some NAND files from the extracted Sys directory. + CreateUserDirectories(); + + // GCPadNew.ini and WiimoteNew.ini must contain specific values in order for controller + // input to work as intended (they aren't user configurable), so we overwrite them just + // in case the user has tried to modify them manually. + // + // ...Except WiimoteNew.ini contains the user configurable settings for Wii Remote + // extensions in addition to all of its lines that aren't user configurable, so since we + // don't want to lose the selected extensions, we don't overwrite that file if it exists. + // + // TODO: Redo the Android controller system so that we don't have to extract these INIs. + String configDirectory = NativeLibrary.GetUserDirectory() + File.separator + "Config"; + copyAsset("GCPadNew.ini", new File(configDirectory, "GCPadNew.ini"), true); + copyAsset("WiimoteNew.ini", new File(configDirectory,"WiimoteNew.ini"), false); + } + + private static void deleteDirectoryRecursively(File file) + { + if (file.isDirectory()) + { + for (File child : file.listFiles()) + deleteDirectoryRecursively(child); + } + file.delete(); } public static boolean areDolphinDirectoriesReady() @@ -99,19 +136,16 @@ public final class DirectoryInitializationService extends IntentService LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent); } - private void copyAsset(String asset, String output, Boolean overwrite) + private void copyAsset(String asset, File output, Boolean overwrite) { Log.verbose("[DirectoryInitializationService] Copying File " + asset + " to " + output); - InputStream in; - OutputStream out; try { - File file = new File(output); - if (!file.exists() || overwrite) + if (!output.exists() || overwrite) { - in = getAssets().open(asset); - out = new FileOutputStream(output); + InputStream in = getAssets().open(asset); + OutputStream out = new FileOutputStream(output); copyFile(in, out); in.close(); out.close(); @@ -123,16 +157,22 @@ public final class DirectoryInitializationService extends IntentService } } - private void copyAssetFolder(String assetFolder, String outputFolder, Boolean overwrite) + private void copyAssetFolder(String assetFolder, File outputFolder, Boolean overwrite) { Log.verbose("[DirectoryInitializationService] Copying Folder " + assetFolder + " to " + outputFolder); try { + boolean createdFolder = false; for (String file : getAssets().list(assetFolder)) { - copyAssetFolder(assetFolder + File.separator + file, outputFolder + File.separator + file, overwrite); - copyAsset(assetFolder + File.separator + file, outputFolder + File.separator + file, overwrite); + if (!createdFolder) + { + outputFolder.mkdir(); + createdFolder = true; + } + copyAssetFolder(assetFolder + File.separator + file, new File(outputFolder, file), overwrite); + copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), overwrite); } } catch (IOException e) @@ -151,4 +191,7 @@ public final class DirectoryInitializationService extends IntentService out.write(buffer, 0, read); } } + + private static native void CreateUserDirectories(); + private static native void SetSysDirectory(String path); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java index 398f569741..11cdf861c6 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/StartupHandler.java @@ -13,33 +13,31 @@ public final class StartupHandler { public static boolean HandleInit(FragmentActivity parent) { - NativeLibrary.SetUserDirectory(""); // Auto-Detect - - if (PermissionsHandler.checkWritePermission(parent)) { - DirectoryInitializationService.startService(parent); - } - - Intent intent = parent.getIntent(); - Bundle extras = intent.getExtras(); + String user_dir = ""; + String start_file = ""; + Bundle extras = parent.getIntent().getExtras(); if (extras != null) { - String user_dir = extras.getString("UserDir"); - String start_file = extras.getString("AutoStartFile"); - - if (!TextUtils.isEmpty(user_dir)) - NativeLibrary.SetUserDirectory(user_dir); - - if (!TextUtils.isEmpty(start_file)) - { - // Start the emulation activity, send the ISO passed in and finish the main activity - Intent emulation_intent = new Intent(parent, EmulationActivity.class); - emulation_intent.putExtra("SelectedGame", start_file); - parent.startActivity(emulation_intent); - parent.finish(); - return false; - } + user_dir = extras.getString("UserDir"); + start_file = extras.getString("AutoStartFile"); } + + NativeLibrary.SetUserDirectory(user_dir); // Uses default path if user_dir equals "" + + if (PermissionsHandler.checkWritePermission(parent)) + DirectoryInitializationService.startService(parent); + + if (!TextUtils.isEmpty(start_file)) + { + // Start the emulation activity, send the ISO passed in and finish the main activity + Intent emulation_intent = new Intent(parent, EmulationActivity.class); + emulation_intent.putExtra("SelectedGame", start_file); + parent.startActivity(emulation_intent); + parent.finish(); + return false; + } + return false; } } diff --git a/Source/Android/jni/CMakeLists.txt b/Source/Android/jni/CMakeLists.txt index 4c2514e6db..69aba8b68f 100644 --- a/Source/Android/jni/CMakeLists.txt +++ b/Source/Android/jni/CMakeLists.txt @@ -15,6 +15,8 @@ ${LIBS} ) file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/) -file(COPY ${CMAKE_SOURCE_DIR}/Data/Sys/GameSettings ${CMAKE_SOURCE_DIR}/Data/Sys/GC ${CMAKE_SOURCE_DIR}/Data/Sys/Wii ${CMAKE_SOURCE_DIR}/Data/Sys/Shaders DESTINATION ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/) +file(REMOVE_RECURSE ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/Sys/) +file(COPY ${CMAKE_SOURCE_DIR}/Data/Sys DESTINATION ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/) +file(REMOVE_RECURSE ${CMAKE_SOURCE_DIR}/Source/Android/app/src/main/assets/Sys/Resources/) # not used on Android set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} ${SHARED_LIB}) diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 3e128997b4..9fc8781cfa 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -432,6 +432,8 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetPlatform( jstring jFilename); JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv* env, jobject obj); +JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGitRevision(JNIEnv* env, + jobject obj); JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveScreenShot(JNIEnv* env, jobject obj); JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_eglBindAPI(JNIEnv* env, @@ -450,8 +452,9 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveState(JN JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadState(JNIEnv* env, jobject obj, jint slot); -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_CreateUserFolders(JNIEnv* env, - jobject obj); +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_services_DirectoryInitializationService_CreateUserDirectories( + JNIEnv* env, jobject obj); JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserDirectory( JNIEnv* env, jobject obj, jstring jDirectory); JNIEXPORT jstring JNICALL @@ -588,6 +591,12 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersio return env->NewStringUTF(Common::scm_rev_str.c_str()); } +JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGitRevision(JNIEnv* env, + jobject obj) +{ + return env->NewStringUTF(Common::scm_rev_git_str.c_str()); +} + JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveScreenShot(JNIEnv* env, jobject obj) { @@ -649,28 +658,19 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadState(JN State::Load(slot); } -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_CreateUserFolders(JNIEnv* env, - jobject obj) +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_services_DirectoryInitializationService_SetSysDirectory( + JNIEnv* env, jobject obj, jstring jPath) { - File::CreateFullPath(File::GetUserPath(D_CONFIG_IDX)); - File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX)); - File::CreateFullPath(File::GetUserPath(D_WIIROOT_IDX) + DIR_SEP WII_WC24CONF_DIR DIR_SEP - "mbox" DIR_SEP); - File::CreateFullPath(File::GetUserPath(D_WIIROOT_IDX) + DIR_SEP "shared2" DIR_SEP - "succession" DIR_SEP); - File::CreateFullPath(File::GetUserPath(D_WIIROOT_IDX) + DIR_SEP "shared2" DIR_SEP "ec" DIR_SEP); - File::CreateFullPath(File::GetUserPath(D_WIIROOT_IDX) + DIR_SEP WII_SYSCONF_DIR DIR_SEP); - File::CreateFullPath(File::GetUserPath(D_CACHE_IDX)); - File::CreateFullPath(File::GetUserPath(D_DUMPDSP_IDX)); - File::CreateFullPath(File::GetUserPath(D_DUMPTEXTURES_IDX)); - File::CreateFullPath(File::GetUserPath(D_HIRESTEXTURES_IDX)); - File::CreateFullPath(File::GetUserPath(D_SCREENSHOTS_IDX)); - File::CreateFullPath(File::GetUserPath(D_STATESAVES_IDX)); - File::CreateFullPath(File::GetUserPath(D_MAILLOGS_IDX)); - File::CreateFullPath(File::GetUserPath(D_SHADERS_IDX) + "Anaglyph" DIR_SEP); - File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + USA_DIR DIR_SEP); - File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + EUR_DIR DIR_SEP); - File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + JAP_DIR DIR_SEP); + const std::string path = GetJString(env, jPath); + File::SetSysDirectory(path); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_services_DirectoryInitializationService_CreateUserDirectories( + JNIEnv* env, jobject obj) +{ + UICommon::CreateDirectories(); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserDirectory( diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 056e3004ba..6393ae24b2 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -27,21 +27,6 @@ #define DOLPHIN_DATA_DIR "dolphin-emu" #endif -// Shared data dirs (Sys and shared User for Linux) -#if defined(_WIN32) || defined(LINUX_LOCAL_DEV) -#define SYSDATA_DIR "Sys" -#elif defined __APPLE__ -#define SYSDATA_DIR "Contents/Resources/Sys" -#elif defined ANDROID -#define SYSDATA_DIR "/sdcard/dolphin-emu" -#else -#ifdef DATA_DIR -#define SYSDATA_DIR DATA_DIR "sys" -#else -#define SYSDATA_DIR "sys" -#endif -#endif - // Dirs in both User and Sys #define EUR_DIR "EUR" #define USA_DIR "USA" diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index 78141d6095..e798826cf5 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -13,6 +13,7 @@ #include #include +#include "Common/Assert.h" #include "Common/Common.h" #include "Common/CommonFuncs.h" #include "Common/CommonPaths.h" @@ -52,6 +53,10 @@ // REMEMBER: strdup considered harmful! namespace File { +#ifdef ANDROID +static std::string s_android_sys_directory; +#endif + #ifdef _WIN32 FileInfo::FileInfo(const std::string& path) { @@ -692,10 +697,25 @@ std::string GetSysDirectory() { std::string sysDir; +#if defined(_WIN32) || defined(LINUX_LOCAL_DEV) +#define SYSDATA_DIR "Sys" +#elif defined __APPLE__ +#define SYSDATA_DIR "Contents/Resources/Sys" +#else +#ifdef DATA_DIR +#define SYSDATA_DIR DATA_DIR "sys" +#else +#define SYSDATA_DIR "sys" +#endif +#endif + #if defined(__APPLE__) sysDir = GetBundleDirectory() + DIR_SEP + SYSDATA_DIR; #elif defined(_WIN32) || defined(LINUX_LOCAL_DEV) sysDir = GetExeDirectory() + DIR_SEP + SYSDATA_DIR; +#elif defined ANDROID + sysDir = s_android_sys_directory; + _assert_msg_(COMMON, !sysDir.empty(), "Sys directory has not been set"); #else sysDir = SYSDATA_DIR; #endif @@ -705,6 +725,14 @@ std::string GetSysDirectory() return sysDir; } +#ifdef ANDROID +void SetSysDirectory(const std::string& path) +{ + INFO_LOG(COMMON, "Setting Sys directory to %s", path.c_str()); + s_android_sys_directory = path; +} +#endif + static std::string s_user_paths[NUM_PATH_INDICES]; static void RebuildUserDirectories(unsigned int dir_index) { diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 007fb3c541..694ec11128 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -183,6 +183,10 @@ std::string GetThemeDir(const std::string& theme_name); // Returns the path to where the sys file are std::string GetSysDirectory(); +#ifdef ANDROID +void SetSysDirectory(const std::string& path); +#endif + #ifdef __APPLE__ std::string GetBundleDirectory(); #endif diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp index 507213f25d..96719f5051 100644 --- a/Source/Core/UICommon/UICommon.cpp +++ b/Source/Core/UICommon/UICommon.cpp @@ -77,7 +77,9 @@ void CreateDirectories() File::CreateFullPath(File::GetUserPath(D_SHADERS_IDX)); File::CreateFullPath(File::GetUserPath(D_SHADERS_IDX) + ANAGLYPH_DIR DIR_SEP); File::CreateFullPath(File::GetUserPath(D_STATESAVES_IDX)); +#ifndef ANDROID File::CreateFullPath(File::GetUserPath(D_THEMES_IDX)); +#endif } void SetUserDirectory(const std::string& custom_path)