diff --git a/BeatOn/BeatSaberModder.cs b/BeatOn/BeatSaberModder.cs index 677df50..678dd4f 100644 --- a/BeatOn/BeatSaberModder.cs +++ b/BeatOn/BeatSaberModder.cs @@ -23,6 +23,7 @@ public class BeatSaberModder public const string LIBMODLOADER_TARGET_FILE = "lib/armeabi-v7a/libmodloader.so"; public const string LIBMODLOADER64_TARGET_FILE = "lib/arm64-v8a/libmodloader.so"; public const string MOD_TAG_FILE = "beaton.modded"; + public const string MODLOADERV2_TAG_FILE = "beaton.modloader2.modded"; public const string BS_PLAYER_DATA_FILE = "/sdcard/Android/data/com.beatgames.beatsaber/files/PlayerData.dat"; public event EventHandler StatusUpdated; @@ -71,7 +72,7 @@ public bool IsTempApkModded Log.LogErr("IsTempApkModded was called, but the TempApk does not exist!"); throw new ModException("IsTempApkModded was called, but the TempApk does not exist!"); } - return CheckApkHasModTagFile(TempApk); + return CheckApkHasAllTagFiles(TempApk); } } @@ -94,6 +95,31 @@ public void CheckCleanupTempApk() } } + private bool IsInstalledBeatSaberOriginal + { + get + { +#if EMULATOR + return true; +#endif + string bsApk = FindBeatSaberApk(); + if (bsApk == null) + { + Log.LogErr($"Tried to call {nameof(IsInstalledBeatSaberModded)} when beat saber isn't installed."); + throw new ModException("Beat saber is not installed, cannot check if it is modded."); + } + try + { + return !(CheckApkHasModloaderTagFile(bsApk) || CheckApkHasModTagFile(bsApk)); + } + catch (Exception ex) + { + Log.LogErr($"Exception in {nameof(IsInstalledBeatSaberModded)} when trying to check if it is modded.", ex); + throw new ModException("Error checking if installed beat saber is modded.", ex); + } + } + } + public bool IsInstalledBeatSaberModded { get @@ -109,7 +135,7 @@ public bool IsInstalledBeatSaberModded } try { - return CheckApkHasModTagFile(bsApk); + return CheckApkHasAllTagFiles(bsApk); } catch (Exception ex) { @@ -136,7 +162,7 @@ public bool CheckIsTempApkReadyForInstall() { if (TempApk == null) return false; - return CheckApkHasModTagFile(TempApk); + return CheckApkHasAllTagFiles(TempApk); } catch (Exception ex) { @@ -334,7 +360,7 @@ public void BackupOriginalApk() throw new ModException("Beat Saber does not seem to be installed, could not find its APK."); } UpdateStatus("Verifying the installed APK isn't modded..."); - if (IsInstalledBeatSaberModded) + if (!IsInstalledBeatSaberOriginal) { UpdateStatus("Installed beatsaber IS modded!"); if (File.Exists(Constants.BEATSABER_APK_BACKUP_FILE)) @@ -382,17 +408,22 @@ public void ApplyModToTempApk() bool modFailed = false; //keep track of any temp files that may have been used so we can clean them up List tempFiles = new List(); + bool isPartialUpgrade = false; try { - //// delete the assets relocation if it already exists in case the mod has been installed before - if (Directory.Exists(Constants.ASSETS_RELOC_PATH)) - Directory.Delete(Constants.ASSETS_RELOC_PATH, true); + isPartialUpgrade = CheckApkHasModTagFile(TempApk) && !CheckApkHasModloaderTagFile(TempApk) && Directory.Exists(Constants.ASSETS_RELOC_PATH); + + if (!isPartialUpgrade) + { + //// delete the assets relocation if it already exists in case the mod has been installed before + if (Directory.Exists(Constants.ASSETS_RELOC_PATH)) + Directory.Delete(Constants.ASSETS_RELOC_PATH, true); - //// copy asset files from APK to /sdcard/wherever - ExtractAssetsFromApkToExternalStorage(TempApk, new List() { + //// copy asset files from APK to /sdcard/wherever + ExtractAssetsFromApkToExternalStorage(TempApk, new List() { "Managed", "boot.config" }); - + } bool is64bit = IsApk64Bit(TempApk); //// copy libassetredirect.so to the mods folder @@ -401,18 +432,25 @@ public void ApplyModToTempApk() //from this point on, the APK has been modified and isn't definitively recoverable if something goes wrong tempApkModified = true; - //// modify classes.dex and inject the loadlibrary call for libmodloader.so - InjectModLoaderToApk(TempApk, tempFiles); + if (!isPartialUpgrade) + { + //// modify classes.dex and inject the loadlibrary call for libmodloader.so + InjectModLoaderToApk(TempApk, tempFiles); + } //// add libmodloader.so to the apk AddModLoaderToApk(TempApk); - //// fix the manifest - AddManifestModToApk(TempApk); - - //// add a 1 byte file to the APK so we know it's been modded to make verifying it later easier - AddTagFileToApk(TempApk); + if (!isPartialUpgrade) + { + //// fix the manifest + AddManifestModToApk(TempApk); + //// add a 1 byte file to the APK so we know it's been modded to make verifying it later easier + AddTagFileToApk(TempApk); + } + AddModLoaderTagFileToApk(TempApk); + //// re-sign the APK UpdateStatus("Re-signing the modded APK (this takes a minute)..."); SignApk(TempApk); @@ -595,6 +633,28 @@ public void CleanupTempApk() } + private bool CheckApkHasAllTagFiles(string apkFilename) + { + bool hasModTag = false; + bool hasModloaderTag = false; + using (var apk = new ZipFileProvider(apkFilename, FileCacheMode.None, true, QuestomAssets.Utils.FileUtils.GetTempDirectory())) + { + if (apk.FileExists(MOD_TAG_FILE)) + { + hasModTag = true; + } + if (apk.FileExists(MODLOADERV2_TAG_FILE)) + { + hasModloaderTag = true; + } + if (hasModTag && hasModloaderTag) + { + return true; + } + } + return (hasModTag && hasModloaderTag); + } + private bool CheckApkHasModTagFile(string apkFilename) { using (var apk = new ZipFileProvider(apkFilename, FileCacheMode.None, true, QuestomAssets.Utils.FileUtils.GetTempDirectory())) @@ -605,6 +665,17 @@ private bool CheckApkHasModTagFile(string apkFilename) return false; } + private bool CheckApkHasModloaderTagFile(string apkFilename) + { + using (var apk = new ZipFileProvider(apkFilename, FileCacheMode.None, true, QuestomAssets.Utils.FileUtils.GetTempDirectory())) + { + if (apk.FileExists(MODLOADERV2_TAG_FILE)) + return true; + } + return false; + + } + private void UpdateStatus(string message) { StatusUpdated?.Invoke(this, message); @@ -1002,13 +1073,31 @@ private void AddModLoaderToApk(string apkFilename) { if (apk.DirectoryExists(LIBMODLOADER_TARGET_FILE.GetDirectoryFwdSlash())) { + if (apk.FileExists(LIBMODLOADER_TARGET_FILE)) + { + apk.Delete(LIBMODLOADER_TARGET_FILE); + apk.Save(); + } apk.QueueWriteStream(LIBMODLOADER_TARGET_FILE, resStream, true, true); } if (apk.DirectoryExists(LIBMODLOADER64_TARGET_FILE.GetDirectoryFwdSlash())) { + if (apk.FileExists(LIBMODLOADER64_TARGET_FILE)) + { + apk.Delete(LIBMODLOADER64_TARGET_FILE); + apk.Save(); + } apk.QueueWriteStream(LIBMODLOADER64_TARGET_FILE, resStream64, true, true); } - apk.Save(); + try + { + apk.Save(); + } catch (IOException) + { + GC.Collect(); + System.Threading.Thread.Sleep(1000); + apk.Save(); + } } } } @@ -1083,5 +1172,19 @@ private void AddTagFileToApk(string apkFilename) } } + private void AddModLoaderTagFileToApk(string apkFilename) + { + using (var apk = new ZipFileProvider(apkFilename, FileCacheMode.None, false, QuestomAssets.Utils.FileUtils.GetTempDirectory())) + { + if (apk.FileExists(MODLOADERV2_TAG_FILE)) + { + Log.LogMsg("APK file already had the modloader v2 tag file."); + return; + } + apk.Write(MODLOADERV2_TAG_FILE, new byte[1], true, false); + apk.Save(); + } + } + } } \ No newline at end of file diff --git a/BeatOn/Core/BeatOnCore.cs b/BeatOn/Core/BeatOnCore.cs index 7787777..204f16c 100644 --- a/BeatOn/Core/BeatOnCore.cs +++ b/BeatOn/Core/BeatOnCore.cs @@ -77,18 +77,22 @@ private bool CheckReimportSongs() return false; } - private void KillBeatSaber() + private void KillBackgroundProcess(string packageName) { try { ActivityManager am = (ActivityManager)_context.GetSystemService(Context.ActivityService); - am.KillBackgroundProcesses("com.beatgames.beatsaber"); + am.KillBackgroundProcesses(packageName); } catch (Exception ex) { Log.LogErr("Exception trying to kill background process for beatsaber.", ex); } } + private void KillBeatSaber() + { + KillBackgroundProcess("com.beatgames.beatsaber"); + } public void Start() { @@ -486,7 +490,25 @@ private void OpManager_OpStatusChanged(object sender, QuestomAssets.AssetOps.Ass private void SendPackageStop(string packageName) { - //doesn't work + try + { + Intent intent = new Intent("com.oculus.vrshell.intent.action.LAUNCH"); + intent.SetPackage("com.oculus.vrshell"); + intent.PutExtra("intent_data", Android.Net.Uri.Parse("systemux://home")); + intent.PutExtra("blackscreen", false); + //var intent = new Intent("com.oculus.system_activity"); + //intent.SetPackage(packageName); + + //intent.PutExtra("intent_pkg", "com.oculus.vrshell"); + //intent.PutExtra("intent_cmd", "{\"Command\":\"exitToHome\", \"PlatformUIVersion\":3, \"ToPackage\":\"" + packageName + "\"}"); + //_context.SendBroadcast(intent); + //intent.PutExtra("intent_cmd", "{\"Command\":\"returnToLauncher\", \"PlatformUIVersion\":3, \"ToPackage\":\"" + packageName + "\"}"); + _context.SendBroadcast(intent); + Task.Delay(3000).ContinueWith(t => { KillBackgroundProcess(packageName); }); + } catch (Exception e) + { + Log.LogErr("Exception trying to send package exittohome messages", e); + } } private void _SongDownloadManager_StatusChanged(object sender, DownloadStatusChangeArgs e) diff --git a/BeatOn/Core/RequestHandlers/PostFileUpload.cs b/BeatOn/Core/RequestHandlers/PostFileUpload.cs index 3b90c4d..60140b1 100644 --- a/BeatOn/Core/RequestHandlers/PostFileUpload.cs +++ b/BeatOn/Core/RequestHandlers/PostFileUpload.cs @@ -75,6 +75,23 @@ public void HandleRequest(HttpListenerContext context) resp.BadRequest("Didn't get any useable files."); return; } + + bool forceOverwrite = false; + if (!string.IsNullOrWhiteSpace(req.Url.Query)) + { + foreach (string kvp in req.Url.Query.TrimStart('?').Split("&")) + { + var split = kvp.Split('='); + if (split.Count() < 1) + continue; + if (split[0].ToLower() == "overwrite") + { + forceOverwrite = true; + break; + } + } + } + foreach (var file in files.Keys.ToList()) { var s = files[file]; @@ -100,7 +117,7 @@ public void HandleRequest(HttpListenerContext context) { provider.Dispose(); ms.Dispose(); - }); + }, overwriteIfExists: forceOverwrite); } catch { diff --git a/BeatOn/Core/RequestHandlers/PostPackageAction.cs b/BeatOn/Core/RequestHandlers/PostPackageAction.cs index aefae5a..9b2443e 100644 --- a/BeatOn/Core/RequestHandlers/PostPackageAction.cs +++ b/BeatOn/Core/RequestHandlers/PostPackageAction.cs @@ -53,7 +53,7 @@ public void HandleRequest(HttpListenerContext context) if (split[0].ToLower() == "package") { package= Java.Net.URLDecoder.Decode(split[1]); - break; + } else if (split[0].ToLower() == "action") { action = Java.Net.URLDecoder.Decode(split[1]); diff --git a/BeatOn/ImportManager.cs b/BeatOn/ImportManager.cs index 2da1a58..383bff6 100644 --- a/BeatOn/ImportManager.cs +++ b/BeatOn/ImportManager.cs @@ -254,7 +254,7 @@ public void ImportFile(string filename, string mimeType, byte[] fileData) /// The file provider containing the data at its root /// A completion callback for when the operations have all completed. Will not be called if there's an exception during initial processing, but will be called if a background operation fails /// Optionally the playlist to add the song to, if the download is a song - public void ImportFromFileProvider(IFileProvider provider, Action completionCallback, string targetPlaylistID = null, bool suppressToast = false) + public void ImportFromFileProvider(IFileProvider provider, Action completionCallback, string targetPlaylistID = null, bool suppressToast = false, bool overwriteIfExists = false) { try { @@ -275,7 +275,7 @@ public void ImportFromFileProvider(IFileProvider provider, Action completionCall if (targetPlaylistID != null) playlist = _getConfig().Config.Playlists.FirstOrDefault(x => x.PlaylistID == targetPlaylistID); - ImportSongFile(provider, completionCallback, playlist, suppressToast); + ImportSongFile(provider, completionCallback, playlist, suppressToast, overwriteIfExists); break; case DownloadType.Playlist: ImportPlaylistFilesFromProvider(provider); @@ -323,14 +323,14 @@ private void ImportPlaylistFilesFromProvider(IFileProvider provider) //todo: show a toast here? } - private AssetOp ImportSongFile(IFileProvider provider, Action completionCallback, BeatSaberPlaylist addToPlaylist = null, bool suppressToast = false) + private AssetOp ImportSongFile(IFileProvider provider, Action completionCallback, BeatSaberPlaylist addToPlaylist = null, bool suppressToast = false, bool overwriteIfExists = false) { try { - var songPath = ExtractSongGetPath(provider); + var songPath = ExtractSongGetPath(provider, overwriteIfExists); var songID = songPath.GetFilenameFwdSlash(); var playlist = addToPlaylist??GetOrCreateDefaultPlaylist(); - return QueueAddSongToPlaylistOp(songID, songPath, playlist, completionCallback, suppressToast); + return QueueAddSongToPlaylistOp(songID, songPath, playlist, completionCallback, suppressToast, overwriteIfExists); } catch (ImportException) { @@ -363,7 +363,7 @@ private void ImportModFile(IFileProvider provider) /// /// Extracts a song from a provider and returns the path RELATIVE TO THE BEATONDATAROOT /// - private string ExtractSongGetPath(IFileProvider provider) + private string ExtractSongGetPath(IFileProvider provider, bool overwriteIfExists) { try { @@ -376,7 +376,7 @@ private string ExtractSongGetPath(IFileProvider provider) //checking this first to maintain compability with song IDs that are just and are not _ - var targetOutputDir = _qaeConfig.SongsPath.CombineFwdSlash(targetSongID); - if (_qaeConfig.RootFileProvider.FileExists(targetOutputDir.CombineFwdSlash("info.dat"))) + if (!overwriteIfExists && _qaeConfig.RootFileProvider.FileExists(targetOutputDir.CombineFwdSlash("info.dat"))) { Log.LogMsg($"ImportManager skipping extract because {targetOutputDir} already exists and has an info.dat."); return targetOutputDir; @@ -410,7 +410,7 @@ private string ExtractSongGetPath(IFileProvider provider) targetOutputDir = _qaeConfig.SongsPath.CombineFwdSlash(targetSongID); - if (_qaeConfig.RootFileProvider.FileExists(targetOutputDir.CombineFwdSlash("info.dat"))) + if (!overwriteIfExists && _qaeConfig.RootFileProvider.FileExists(targetOutputDir.CombineFwdSlash("info.dat"))) { Log.LogMsg($"ImportManager skipping extract because {targetOutputDir} already exists and has an info.dat."); return targetOutputDir; @@ -546,7 +546,7 @@ private void ExtractAndInstallMod(IFileProvider provider) /// The song ID to use for the imported song /// The path, RELATIVE TO THE BEATONDATA ROOT, of where the custom song exists /// The playlist to add the song to - private AssetOp QueueAddSongToPlaylistOp(string songID, string songPath, BeatSaberPlaylist playlist, Action completionCallback = null, bool suppressToast = false) + private AssetOp QueueAddSongToPlaylistOp(string songID, string songPath, BeatSaberPlaylist playlist, Action completionCallback = null, bool suppressToast = false, bool overwriteIfExists = false) { var qae = _getEngine(); var bsSong = new BeatSaberSong() @@ -554,7 +554,7 @@ private AssetOp QueueAddSongToPlaylistOp(string songID, string songPath, BeatSab SongID = songID, //ref: was Path.GetFileName(toInst.DownloadPath), CustomSongPath = songPath //ref: was toInst.DownloadPath }; - var addOp = new AddNewSongToPlaylistOp(bsSong, playlist.PlaylistID); + var addOp = new AddNewSongToPlaylistOp(bsSong, playlist.PlaylistID, overwriteIfExists); addOp.OpFinished += (s, op) => { diff --git a/BeatOn/Properties/AndroidManifest.xml b/BeatOn/Properties/AndroidManifest.xml index 38cceeb..f1e38c4 100644 --- a/BeatOn/Properties/AndroidManifest.xml +++ b/BeatOn/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + diff --git a/BeatOn/Properties/AssemblyInfo.cs b/BeatOn/Properties/AssemblyInfo.cs index e92da5e..8ec77d5 100644 --- a/BeatOn/Properties/AssemblyInfo.cs +++ b/BeatOn/Properties/AssemblyInfo.cs @@ -26,5 +26,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.3.3")] -[assembly: AssemblyFileVersion("0.1.3.3")] +[assembly: AssemblyVersion("0.1.3.5")] +[assembly: AssemblyFileVersion("0.1.3.5")] diff --git a/BeatOn/Resources/raw/libmodloader.so b/BeatOn/Resources/raw/libmodloader.so index 6b688cc..f912788 100644 Binary files a/BeatOn/Resources/raw/libmodloader.so and b/BeatOn/Resources/raw/libmodloader.so differ diff --git a/BeatOn/Resources/raw/libmodloader64.so b/BeatOn/Resources/raw/libmodloader64.so index 0f20bb6..7be64a7 100644 Binary files a/BeatOn/Resources/raw/libmodloader64.so and b/BeatOn/Resources/raw/libmodloader64.so differ diff --git a/QuestomAssets b/QuestomAssets index 7cac99e..51682c4 160000 --- a/QuestomAssets +++ b/QuestomAssets @@ -1 +1 @@ -Subproject commit 7cac99e2a94dc0c373c6ea55a27ad10bbbf53598 +Subproject commit 51682c46809253383f4844d08b7401b65e188b4f diff --git a/beatsaber-hook b/beatsaber-hook index 7dc424c..5708fd5 160000 --- a/beatsaber-hook +++ b/beatsaber-hook @@ -1 +1 @@ -Subproject commit 7dc424cd71bcf7f3f10755f0222cda79017a7dcf +Subproject commit 5708fd5d131959526910f1dcfad46deb883ebcea