Пример #1
0
        public void UpdateConfig(BeatSaberQuestomConfig config)
        {
            //todo: basic validation of the config
            if (_readOnly)
            {
                throw new InvalidOperationException("Cannot update in read only mode.");
            }

            using (var apkFileProvider = new ApkAssetsFileProvider(_apkFilename, ApkAssetsFileProvider.FileCacheMode.Memory, false))
            {
                var manager = new AssetsManager(apkFileProvider, BSConst.GetAssetTypeMap(), false);
                manager.GetAssetsFile("globalgamemanagers");

                //get existing playlists and their songs
                //compare with new ones
                //generate a diff
                //etc.

                UpdateColorConfig(manager, config.Colors);

                UpdateTextConfig(manager, config.TextChanges);

                if (!UpdateSaberConfig(manager, config.Saber))
                {
                    Log.LogErr("Saber failed to update.  Aborting all changes.");
                }

                if (config.Playlists != null)
                {
                    UpdateMusicConfig(manager, config, apkFileProvider);
                }
                else
                {
                    Log.LogMsg("Playlists is null, song configuration will not be changed.");
                }

                Log.LogMsg("Serializing all assets...");
                manager.WriteAllOpenAssets();

                apkFileProvider.Save();
            }
        }
Пример #2
0
        //private bool SaberExists(AssetsManager manager, string saberID)
        //{
        //    var file11 = manager.GetAssetsFile(BSConst.KnownFiles.File11);
        //    return file11.FindAsset<GameObject>(x => x.Object.Name == $"{saberID}Saber") != null;
        //}

        //private string GetCurrentSaberID(AssetsManager manager)
        //{
        //    var saberChild = GetSaberObjectParentTransform(manager)?.GameObject?.Object;
        //    if (saberChild == null)
        //        throw new Exception("Couldn't find child saber game object of transform.");
        //    return saberChild.Name.Substring(0, saberChild.Name.Length - 5);
        //}

        //private Transform GetSaberObjectParentTransform(AssetsManager manager)
        //{
        //    var file11 = manager.GetAssetsFile(BSConst.KnownFiles.File11);
        //    var basicSaberModel = file11.FindAsset<GameObject>(x => x.Object.Name == "BasicSaberModel");

        //    if (basicSaberModel == null)
        //        throw new Exception("Couldn't find BasicSaberModel!");

        //    var transform = basicSaberModel.Object.Components.FirstOrDefault(x => x.Object is Transform)?.Object as Transform;
        //    if (transform == null)
        //        throw new Exception("Couldn't find Transform on BasicSaberModel!");

        //    var saberParent = (transform.Children.FirstOrDefault(x => x.Object is Transform
        //            && ((x.Object as Transform).GameObject?.Object?.Name?.EndsWith("Saber") ?? false)).Object as Transform);
        //    if (saberParent == null)
        //        throw new Exception("Could not find child transform of BasicSaberModel!");
        //    return saberParent;
        //}

        //private void SwapToSaberID(AssetsManager manager, string saberID)
        //{
        //    var file11 = manager.GetAssetsFile(BSConst.KnownFiles.File11);

        //    var newSaber = file11.FindAsset<GameObject>(x => x.Object.Name == $"{saberID}Saber")?.Object;
        //    if (newSaber == null)
        //        throw new Exception($"Saber with ID {saberID} does not exist!");

        //    var newSaberTransform = newSaber.Components.FirstOrDefault(x => x.Object is Transform).Object as Transform;
        //    if (newSaberTransform == null)
        //        throw new Exception($"Saber with ID {saberID} is missing its parent transform!");

        //    var basicSaberModel = file11.FindAsset<GameObject>(x => x.Object.Name == "BasicSaberModel");

        //    if (basicSaberModel == null)
        //        throw new Exception("Couldn't find BasicSaberModel!");

        //    var transform = basicSaberModel.Object.Components.FirstOrDefault(x => x.Object is Transform)?.Object as Transform;
        //    if (transform == null)
        //        throw new Exception("Couldn't find Transform on BasicSaberModel!");

        //    var saberChild = transform.Children.FirstOrDefault(x => x.Object.GameObject?.Object?.Name?.EndsWith("Saber")??false);
        //    if (saberChild == null)
        //        throw new Exception("Couldn't find a game object on the BasicSaberModel Transform that ended with -Saber!");
        //    int saberIndex = transform.Children.IndexOf(saberChild);
        //    saberChild.Object.Father = null;
        //    transform.Children[saberIndex] = newSaberTransform.PtrFrom(transform) as ISmartPtr<Transform>;
        //    newSaberTransform.Father = transform.PtrFrom(newSaberTransform);
        //}
        #endregion

        private BeatSaberQuestomConfig GetConfig()
        {
            lock (this)
            {
                BeatSaberQuestomConfig config = new BeatSaberQuestomConfig();
                var mainPack             = GetMainLevelPack();
                CustomLevelLoader loader = new CustomLevelLoader(GetSongsAssetsFile(), _config);
                foreach (var packPtr in mainPack.BeatmapLevelPacks)
                {
                    var pack = packPtr.Target.Object;
                    if (HideOriginalPlaylists && BSConst.KnownLevelPackIDs.Contains(pack.PackID))
                    {
                        continue;
                    }

                    var packModel = new BeatSaberPlaylist()
                    {
                        PlaylistName = pack.PackName, PlaylistID = pack.PackID, LevelPackObject = pack
                    };
                    var collection = pack.BeatmapLevelCollection.Object;

                    foreach (var songPtr in collection.BeatmapLevels)
                    {
                        var songObj   = songPtr.Object;
                        var songModel = new BeatSaberSong()
                        {
                            LevelAuthorName = songObj.LevelAuthorName,
                            SongID          = songObj.LevelID,
                            SongAuthorName  = songObj.SongAuthorName,
                            SongName        = songObj.SongName,
                            SongSubName     = songObj.SongSubName,
                            LevelData       = songObj
                        };
                        songModel.CoverArtFilename = loader.GetCoverImageFilename(songObj);
                        packModel.SongList.Add(songModel);
                    }
                    config.Playlists.Add(packModel);
                }
                return(config);
            }
        }
Пример #3
0
        public void UpdateConfig(BeatSaberQuestomConfig config)
        {
            lock (this)
            {
                //todo: basic validation of the config

                PreloadFiles();
                //
                //get existing playlists and their songs
                //compare with new ones
                //generate a diff
                //etc.

                //TODO; fix
                //UpdateColorConfig(config.Colors);

                //TODO: something broke
                UpdateTextConfig(config.TextChanges);

                //if (!UpdateSaberConfig(manager, config.Saber))
                //{
                //    Log.LogErr("Saber failed to update.  Aborting all changes.");
                //}

                if (config.Playlists != null)
                {
                    UpdateMusicConfig(config);
                }
                else
                {
                    Log.LogMsg("Playlists is null, song configuration will not be changed.");
                }

                Log.LogMsg("Serializing all assets...");
                _manager.WriteAllOpenAssets();

                FileProvider.Save();
            }
        }
Пример #4
0
        //private void UpdateSaberConfig(AssetsManager manager)
        //{
        //    var currentSaber = GetCurrentSaberID(manager);
        //    if (saberInfo == null || saberInfo.ID == null)
        //    {
        //        Log.LogMsg("No SaberID provided, saber will not be changed.");
        //        return;
        //    }
        //    if (saberInfo.ID.ToLower() == currentSaber.ToLower())
        //    {
        //        Log.LogMsg("Current saber ID is already set, no change needed.");
        //        return;
        //    }
        //}

        private void UpdateMusicConfig(AssetsManager manager, BeatSaberQuestomConfig config, IAssetsFileProvider apkFileProvider)
        {
            //get the old config before we start on this
            var originalConfig = GetConfig(manager, false);
            var songsAssetFile = manager.GetAssetsFile(BSConst.KnownFiles.SongsAssetsFilename);

            foreach (var playlist in config.Playlists)
            {
                UpdatePlaylistConfig(manager, playlist);
            }

            //open the assets with the main levels collection, find the file index of sharedassets17.assets, and add the playlists to it
            var mainLevelsFile = manager.GetAssetsFile(BSConst.KnownFiles.MainCollectionAssetsFilename);
            var file17Index    = mainLevelsFile.GetFileIDForFilename(BSConst.KnownFiles.SongsAssetsFilename);
            var mainLevelPack  = GetMainLevelPack(manager);


            var packsToUnlink = mainLevelPack.BeatmapLevelPacks.Where(x => !HideOriginalPlaylists || !BSConst.KnownLevelPackIDs.Contains(x.Object.PackID)).ToList();
            var packsToRemove = mainLevelPack.BeatmapLevelPacks.Where(x => !BSConst.KnownLevelPackIDs.Contains(x.Object.PackID) && !config.Playlists.Any(y => y.PlaylistID == x.Object.PackID)).Select(x => x.Object).ToList();

            foreach (var unlink in packsToUnlink)
            {
                mainLevelPack.BeatmapLevelPacks.Remove(unlink);
                unlink.Dispose();
            }

            var oldSongs = originalConfig.Playlists.SelectMany(x => x.SongList).Select(x => x.LevelData).Distinct();
            var newSongs = config.Playlists.SelectMany(x => x.SongList).Select(x => x.LevelData).Distinct();

            //don't allow removal of the actual tracks or level packs that are built in, although you can unlink them from the main list
            var removeSongs = oldSongs.Where(x => !newSongs.Contains(x) && !BSConst.KnownLevelIDs.Contains(x.LevelID)).Distinct().ToList();

            var addedSongs = newSongs.Where(x => !oldSongs.Contains(x));

            var removedPlaylistCount = originalConfig.Playlists.Where(x => !config.Playlists.Any(y => y.PlaylistID == x.PlaylistID)).Count();
            var newPlaylistCount     = config.Playlists.Where(x => !originalConfig.Playlists.Any(y => y.PlaylistID == x.PlaylistID)).Count();
            //
            //
            //TODO: clean up cover art, it's leaking!
            //
            //
            List <string> audioFilesToDelete = new List <string>();

            removeSongs.ForEach(x => RemoveLevelAssets(manager, x, audioFilesToDelete));

            packsToRemove.ForEach(x => RemoveLevelPackAssets(manager, x));

            //relink all the level packs in order
            var addPacks = config.Playlists.Select(x => x.LevelPackObject.PtrFrom(mainLevelPack));

            mainLevelPack.BeatmapLevelPacks.AddRange(addPacks);

            //do a first loop to guess at the file size
            Int64 originalApkSize = new FileInfo(_apkFilename).Length;
            Int64 sizeGuess       = originalApkSize;

            foreach (var pl in config.Playlists)
            {
                foreach (var sng in pl.SongList)
                {
                    if (sng.SourceOgg != null)
                    {
                        var clip = sng.LevelData.AudioClip.Object;
                        sizeGuess += new FileInfo(sng.SourceOgg).Length;
                    }
                }
            }
            foreach (var toDelete in audioFilesToDelete)
            {
                sizeGuess -= apkFileProvider.GetFileSize(BSConst.KnownFiles.AssetsRootPath + toDelete);
            }

            Log.LogMsg("");
            Log.LogMsg("Playlists:");
            Log.LogMsg($"  Added:   {newPlaylistCount}");
            Log.LogMsg($"  Removed: {removedPlaylistCount}");
            Log.LogMsg("");
            Log.LogMsg("Songs:");
            Log.LogMsg($"  Added:   {addedSongs.Count()}");
            Log.LogMsg($"  Removed: {removeSongs.Count()}");
            Log.LogMsg("");
            Log.LogMsg($"Original APK size:     {originalApkSize:n0}");
            Log.LogMsg($"Guesstimated new size: {sizeGuess:n0}");
            Log.LogMsg("");

            if (sizeGuess > Int32.MaxValue)
            {
                Log.LogErr("***************ERROR*****************");
                Log.LogErr($"Guesstimating a file size around {sizeGuess / (Int64)1000000}MB , this will crash immediately upon launch.");
                Log.LogErr($"The file size MUST be less than {Int32.MaxValue / (int)1000000}MB");
                Log.LogErr("***************ERROR*****************");
                throw new OverflowException("File might exceed 2.1GB, aborting.");
            }

            ////////START WRITING DATA



            //todo: save here?


            foreach (var pl in config.Playlists)
            {
                foreach (var sng in pl.SongList)
                {
                    if (sng.SourceOgg != null)
                    {
                        var clip = sng.LevelData.AudioClip.Object;
                        apkFileProvider.WriteFile(sng.SourceOgg, BSConst.KnownFiles.AssetsRootPath + clip.Resource.Source, true, false);
                        //saftey check to make sure we aren't removing a file we just put here
                        if (audioFilesToDelete.Contains(clip.Resource.Source))
                        {
                            Log.LogErr($"Level id '{sng.LevelData.LevelID}' wrote file '{clip.Resource.Source}' that was on the delete list...");
                            audioFilesToDelete.Remove(clip.Resource.Source);
                        }
                    }

                    //todo: save on some interval to save ram?
                }
            }

            if (audioFilesToDelete.Count > 0)
            {
                Log.LogMsg($"Deleting {audioFilesToDelete.ToString()} audio files");
                foreach (var toDelete in audioFilesToDelete)
                {
                    //Log.LogMsg($"Deleting audio file {toDelete}");
                    apkFileProvider.Delete(BSConst.KnownFiles.AssetsRootPath + toDelete);
                }
            }
        }
Пример #5
0
        private BeatSaberQuestomConfig GetConfig(AssetsManager manager, bool suppressImages)
        {
            BeatSaberQuestomConfig config = new BeatSaberQuestomConfig();
            var file19   = manager.GetAssetsFile(BSConst.KnownFiles.MainCollectionAssetsFilename);
            var file17   = manager.GetAssetsFile(BSConst.KnownFiles.SongsAssetsFilename);
            var mainPack = GetMainLevelPack(manager);

            foreach (var packPtr in mainPack.BeatmapLevelPacks)
            {
                var pack = packPtr.Target.Object;
                if (HideOriginalPlaylists && BSConst.KnownLevelPackIDs.Contains(pack.PackID))
                {
                    continue;
                }

                var packModel = new BeatSaberPlaylist()
                {
                    PlaylistName = pack.PackName, PlaylistID = pack.PackID, LevelPackObject = pack
                };
                var collection = pack.BeatmapLevelCollection.Object;

                //get cover art for playlist
                if (!suppressImages)
                {
                    try
                    {
                        var coverSprite = pack.CoverImage.Object;
                        var coverTex    = coverSprite.Texture.Object;
                        packModel.CoverArt          = coverTex.ToBitmap();
                        packModel.CoverArtBase64PNG = packModel.CoverArt.ToBase64PNG();
                    }
                    catch (Exception ex)
                    {
                        Log.LogErr($"Unable to convert texture for playlist ID '{pack.PackID}' cover art", ex);
                    }
                }
                foreach (var songPtr in collection.BeatmapLevels)
                {
                    var songObj   = songPtr.Object;
                    var songModel = new BeatSaberSong()
                    {
                        LevelAuthorName = songObj.LevelAuthorName,
                        SongID          = songObj.LevelID,
                        SongAuthorName  = songObj.SongAuthorName,
                        SongName        = songObj.SongName,
                        SongSubName     = songObj.SongSubName,
                        LevelData       = songObj
                    };
                    if (!suppressImages)
                    {
                        try
                        {
                            var songCover = songObj.CoverImageTexture2D.Object;
                            try
                            {
                                songModel.CoverArt          = songCover.ToBitmap();
                                songModel.CoverArtBase64PNG = songModel.CoverArt.ToBase64PNG();
                            }
                            catch (Exception ex)
                            {
                                Log.LogErr($"Unable to convert texture for song ID '{songModel.SongID}' cover", ex);
                            }
                        }
                        catch (Exception ex)
                        {
                            Log.LogErr($"Exception loading/converting the cover image for song id '{songObj.LevelID}'", ex);
                        }
                    }
                    packModel.SongList.Add(songModel);
                }
                config.Playlists.Add(packModel);
            }
            return(config);
        }