Esempio n. 1
0
        private DownloadType IdentifyFileType(IFileProvider provider)
        {
            if (provider.FileExists("beatonmod.json"))
            {
                return(DownloadType.ModFile);
            }
            else if (provider.FileExists("info.dat"))
            {
                return(DownloadType.SongFile);
            }
            else if (provider.FileExists("info.json"))
            {
                return(DownloadType.OldSongFile);
            }
            else
            {
                var files = provider.FindFiles("*");
                //check if all of the files are .json files, and guess that maybe it's a playlist or two
                if (!files.Any(x => !x.ToLower().EndsWith(".json")))
                {
                    //going to guess maybe this is a playlist, becuase there aren't any other options right now
                    return(DownloadType.Playlist);
                }
            }

            return(DownloadType.Unknown);
        }
Esempio n. 2
0
        /// <summary>
        /// Extracts a mod from a provider and returns the path RELATIVE TO THE BEATONDATAROOT
        /// </summary>
        private void ExtractAndInstallMod(IFileProvider provider)
        {
            try
            {
                var def = _getEngine().ModManager.LoadDefinitionFromProvider(provider);

                if (def.Platform != "Quest")
                {
                    Log.LogErr($"Attempted to load a mod for a different platform, '{def.Platform ?? "(null)"}', only 'Quest' is supported");
                    _showToast("Incompatible Mod", "This mod is not supported on the Quest.", ToastType.Error, 5);
                    return;
                }
                var modOutputPath = _qaeConfig.ModsSourcePath.CombineFwdSlash(def.ID);

                if (_qaeConfig.RootFileProvider.DirectoryExists(modOutputPath))
                {
                    Log.LogMsg($"Installing mod ID {def.ID} but it seems to exist.  Deleting the existing mod folder.");
                    _qaeConfig.RootFileProvider.RmRfDir(modOutputPath);
                }

                _qaeConfig.RootFileProvider.MkDir(modOutputPath);

                var allFiles = provider.FindFiles("*");
                foreach (var file in allFiles)
                {
                    try
                    {
                        var targetFile = modOutputPath.CombineFwdSlash(file);
                        var dir        = targetFile.GetDirectoryFwdSlash();
                        if (!_qaeConfig.RootFileProvider.DirectoryExists(dir))
                        {
                            _qaeConfig.RootFileProvider.MkDir(dir);
                        }

                        using (Stream fs = _qaeConfig.RootFileProvider.GetWriteStream(targetFile))
                        {
                            using (Stream rs = provider.GetReadStream(file, true))
                            {
                                rs.CopyTo(fs);
                            }
                            //TODO: this won't work with zip file provider on the root because the stream has to stay open until Save()
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.LogErr($"Exception extracting {file} from provider {provider.SourceName} on mod ID {def.ID}", ex);
                        throw new ImportException($"Exception extracting {file} from provider {provider.SourceName}", $"Unable to extract files from mod file {provider.SourceName}!", ex);
                    }
                }
                _getEngine().ModManager.ResetCache();
                QueueModInstall(def);
            }
            catch (Exception ex)
            {
                Log.LogErr($"Exception trying to load mod from provider {provider.SourceName}", ex);
                throw new ImportException($"Exception trying to load mod from provider {provider.SourceName}", $"Unable to load mod from {provider.SourceName}, it does not appear to be a valid mod file.", ex);
            }
        }
Esempio n. 3
0
        //if file ends in .split0 yes
        //if file ends in .assets yes
        //if file has no extension yes

        public List <string> FindAndLoadAllAssets()
        {
            List <string> loadedFiles = new List <string>();
            var           foundFiles  = _fileProvider.FindFiles(_assetsRootPath + "*");
            List <string> tryFiles    = new List <string>();

            foreach (var foundFile in foundFiles)
            {
                var filename = foundFile.Substring(_assetsRootPath.Length);

                if (filename.Any(x => x == '/'))
                {
                    if (!filename.Substring(filename.LastIndexOf("/")).Contains("."))
                    {
                        tryFiles.Add(filename);
                        continue;
                    }
                }
                else if (!filename.Contains("."))
                {
                    tryFiles.Add(filename);
                    continue;
                }

                if (filename.ToLower().EndsWith(".split0"))
                {
                    filename = filename.Substring(0, filename.Length - ".split0".Length);
                    tryFiles.Add(filename);
                    continue;
                }

                if (filename.ToLower().EndsWith(".assets"))
                {
                    tryFiles.Add(filename);
                    continue;
                }
            }



            foreach (var tryFile in tryFiles)
            {
                if (_openAssetsFiles.ContainsKey(tryFile.ToLower()))
                {
                    loadedFiles.Add(tryFile);
                    continue;
                }
                AssetsFile file;
                if (TryGetAssetsFile(tryFile, out file))
                {
                    loadedFiles.Add(tryFile);
                }
            }
            return(loadedFiles);
        }
Esempio n. 4
0
        public static Stream ReadCombinedAssets(this IFileProvider fp, string assetsFilePath, out bool wasCombined)
        {
            string actualName = fp.CorrectAssetFilename(assetsFilePath);

            List <string> assetFiles = new List <string>();

            if (actualName.ToLower().EndsWith("split0"))
            {
                assetFiles.AddRange(fp.FindFiles(actualName.Replace(".split0", ".split*"))
                                    .OrderBy(x => Convert.ToInt32(x.Split(new string[] { ".split" }, StringSplitOptions.None).Last())));
            }
            else
            {
                wasCombined = false;
                return(fp.GetReadStream(actualName));
            }
            wasCombined = true;
            //TODO: property or something on the file provider interface letting this code know if it should use the combined stream
            //      I think combined stream may perform horribly on zip files or cause other issues.
            return(new CombinedStream(assetFiles, fp));
        }
Esempio n. 5
0
        private void ImportPlaylistFilesFromProvider(IFileProvider provider)
        {
            int success = 0;
            int fail    = 0;

            foreach (var file in provider.FindFiles("*.json"))
            {
                try
                {
                    Log.LogMsg($"Importing playlist file '{file}' from zip '{provider.SourceName}'");
                    ImportFile(file.GetFilenameFwdSlash(), "application/json", provider.Read(file));
                    success++;
                }
                catch (Exception ex)
                {
                    Log.LogErr($"Exception trying to add playlist file '{file}' from zip '{provider.SourceName}'", ex);
                    fail++;
                    continue;
                }
            }
            //todo: show a toast here?
        }
Esempio n. 6
0
        public void Sign(IFileProvider fileProvider)
        {
            MemoryStream msManifestFile = new MemoryStream();
            MemoryStream msSigFile      = new MemoryStream();

            byte[]       keyBlock;
            MemoryStream msSignatureFileBody = new MemoryStream();

            try
            {
                //create the MF file header
                using (StreamWriter swManifest = GetSW(msManifestFile))
                {
                    swManifest.WriteLine("Manifest-Version: 1.0");
                    swManifest.WriteLine("Created-By: emulamer");
                    swManifest.WriteLine();
                }

                //so that we can do it in one pass, write the MF and SF line items at the same time to their respective streams
                foreach (var infFile in fileProvider.FindFiles("*").Where(x => !x.StartsWith("META-INF")))
                {
                    WriteEntryHashes(fileProvider, infFile, msManifestFile, msSignatureFileBody);
                }

                //compute the hash on the entirety of the manifest file for the SF file
                msManifestFile.Seek(0, SeekOrigin.Begin);
                var manifestFileHash = _sha.ComputeHash(msManifestFile);

                //write the SF to memory then copy it out to the actual file- contents will be needed later to use for signing, don't want to hit the zip stream twice

                byte[] sigFileBytes = null;

                using (StreamWriter swSignatureFile = GetSW(msSigFile))
                {
                    swSignatureFile.WriteLine("Signature-Version: 1.0");
                    swSignatureFile.WriteLine($"SHA1-Digest-Manifest: {Convert.ToBase64String(manifestFileHash)}");
                    swSignatureFile.WriteLine("Created-By: emulamer");
                    swSignatureFile.WriteLine();
                }
                msSignatureFileBody.Seek(0, SeekOrigin.Begin);
                msSignatureFileBody.CopyTo(msSigFile);
                msSigFile.Seek(0, SeekOrigin.Begin);
                sigFileBytes = msSigFile.ToArray();

                //get the key block (all the hassle distilled into one line), then write it out to the RSA file
                keyBlock = SignIt(sigFileBytes);

                //delete all the META-INF stuff that exists already
                fileProvider.DeleteFiles("META-INF*");

                //write the 3 files
                msManifestFile.Seek(0, SeekOrigin.Begin);

                fileProvider.Write("META-INF/MANIFEST.MF", msManifestFile.ToArray(), true, true);

                fileProvider.Write("META-INF/BS.SF", sigFileBytes, true, true);

                fileProvider.Write("META-INF/BS.RSA", keyBlock, true, true);
                fileProvider.Save();
            }
            finally
            {
                if (msManifestFile != null)
                {
                    msManifestFile.Dispose();
                }
                if (msSignatureFileBody != null)
                {
                    msSignatureFileBody.Dispose();
                }
                if (msManifestFile != null)
                {
                    msManifestFile.Dispose();
                }
                if (msSigFile != null)
                {
                    msSigFile.Dispose();
                }
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Extracts a song from a provider and returns the path RELATIVE TO THE BEATONDATAROOT
        /// </summary>
        private string ExtractSongGetPath(IFileProvider provider)
        {
            try
            {
                //use the incoming file or folder name (without extension) as the song ID.  Since the filesystem has to have unique names, this should help keep songIDs unique also
                var targetSongID = Path.GetFileNameWithoutExtension(provider.SourceName);
                if (targetSongID.Contains("."))
                {
                    targetSongID = targetSongID.Substring(0, targetSongID.LastIndexOf("."));
                }

                var targetOutputDir = _qaeConfig.SongsPath.CombineFwdSlash(targetSongID);

                if (!_qaeConfig.RootFileProvider.DirectoryExists(targetOutputDir))
                {
                    _qaeConfig.RootFileProvider.MkDir(targetOutputDir);
                }

                if (_qaeConfig.RootFileProvider.FileExists(targetOutputDir.CombineFwdSlash("info.dat")))
                {
                    Log.LogMsg($"ImportManager skipping extract because {targetOutputDir} already exists and has an info.dat.");
                    return(targetOutputDir);
                }
                var allFiles     = provider.FindFiles("*");
                var firstInfoDat = allFiles.FirstOrDefault(x => x.ToLower() == "info.dat");
                if (firstInfoDat == null)
                {
                    throw new ImportException($"Unable to find info.dat in provider {provider.SourceName}", $"Zip file {provider.SourceName} doesn't seem to be a song (no info.dat).");
                }

                //get the path of where the info.dat is and assume that the rest of the files will be in the same directory as it.
                //  This is to handle zips that have duplicate info or nested folders
                var infoDatPath = firstInfoDat.GetDirectoryFwdSlash();
                foreach (var file in allFiles)
                {
                    try
                    {
                        if (Path.GetExtension(file).ToLower() == "zip")
                        {
                            Log.LogMsg($"Skipped {file} because it looks like a nested zip file.");
                            continue;
                        }
                        //if the file isn't in the same path as the located info.dat, skip it
                        if (file.GetDirectoryFwdSlash() != infoDatPath)
                        {
                            Log.LogMsg($"Skipped zip file {file} because it wasn't in the path with info.dat at {infoDatPath}");
                            continue;
                        }

                        var targetFile = targetOutputDir.CombineFwdSlash(file.GetFilenameFwdSlash());

                        using (Stream fs = _qaeConfig.RootFileProvider.GetWriteStream(targetFile))
                        {
                            using (Stream rs = provider.GetReadStream(file, true))
                            {
                                rs.CopyTo(fs);
                            }
                            //TODO: this won't work with zip file provider on the root because the stream has to stay open until Save()
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.LogErr($"Exception extracting song file {file} from provider {provider.SourceName}", ex);
                        throw new ImportException($"Exception extracting song file {file} from provider {provider.SourceName}", $"Unable to import song from {provider.SourceName}!  Extracting files failed!", ex);
                    }
                }
                return(targetOutputDir);
            }
            catch (Exception ex)
            {
                Log.LogErr($"Exception importing song from provider {provider.SourceName}", ex);
                throw new ImportException($"Exception importing song from provider {provider.SourceName}", $"Error importing song from {provider.SourceName}", ex);
            }
        }
Esempio n. 8
0
        /// <summary>
        /// Extracts a mod from a provider and returns the path RELATIVE TO THE BEATONDATAROOT
        /// </summary>
        private void ExtractAndInstallMod(IFileProvider provider)
        {
            lock (_modInstallLock)
            {
                try
                {
                    var def = _getEngine().ModManager.LoadDefinitionFromProvider(provider);

                    if (def.Platform != "Quest")
                    {
                        Log.LogErr($"Attempted to load a mod for a different platform, '{def.Platform ?? "(null)"}', only 'Quest' is supported");
                        _showToast("Incompatible Mod", "This mod is not supported on the Quest.", ToastType.Error, 5);
                        return;
                    }
                    var modOutputPath = _qaeConfig.ModsSourcePath.CombineFwdSlash(def.ID);

                    if (_qaeConfig.RootFileProvider.DirectoryExists(modOutputPath))
                    {
                        Log.LogMsg($"Installing mod ID {def.ID} but it seems to exist.  Deleting the existing mod folder.");
                        _qaeConfig.RootFileProvider.RmRfDir(modOutputPath);
                    }

                    _qaeConfig.RootFileProvider.MkDir(modOutputPath);

                    var allFiles = provider.FindFiles("*");
                    foreach (var file in allFiles)
                    {
                        try
                        {
                            var targetFile = modOutputPath.CombineFwdSlash(file);
                            var dir        = targetFile.GetDirectoryFwdSlash();
                            if (!_qaeConfig.RootFileProvider.DirectoryExists(dir))
                            {
                                _qaeConfig.RootFileProvider.MkDir(dir);
                            }

                            using (Stream fs = _qaeConfig.RootFileProvider.GetWriteStream(targetFile))
                            {
                                using (Stream rs = provider.GetReadStream(file, true))
                                {
                                    rs.CopyTo(fs);
                                }
                                //TODO: this won't work with zip file provider on the root because the stream has to stay open until Save()
                            }
                        }
                        catch (Exception ex)
                        {
                            Log.LogErr($"Exception extracting {file} from provider {provider.SourceName} on mod ID {def.ID}", ex);
                            throw new ImportException($"Exception extracting {file} from provider {provider.SourceName}", $"Unable to extract files from mod file {provider.SourceName}!", ex);
                        }
                    }
                    var eng = _getEngine();
                    eng.ModManager.ModAdded(def);
                    _getEngine().ModManager.ResetCache();
                    var bsVer = _getConfig().BeatSaberVersion;
                    if (_getConfig().BeatSaberVersion != def.TargetBeatSaberVersion)
                    {
                        Log.LogErr($"Mod ID {def.ID} was imported but will not be automatically activated because it's target version {def.TargetBeatSaberVersion} doesn't match beat saber's version {bsVer}");
                        _getConfig().Config = _getEngine().GetCurrentConfig();
                        _triggerConfigChanged();
                        _showToast("Mod Not Enabled", "Mod was not enabled because it may not be compatible.", ToastType.Warning, 5);
                    }
                    else
                    {
                        QueueModInstall(def);
                    }
                }
                catch (Exception ex)
                {
                    Log.LogErr($"Exception trying to load mod from provider {provider.SourceName}", ex);
                    throw new ImportException($"Exception trying to load mod from provider {provider.SourceName}", $"Unable to load mod from {provider.SourceName}, it may be an invalid file or another mod of the same type failed to uninstall.", ex);
                }
            }
        }
Esempio n. 9
0
        /// <summary>
        /// Extracts a song from a provider and returns the path RELATIVE TO THE BEATONDATAROOT
        /// </summary>
        private string ExtractSongGetPath(IFileProvider provider, bool overwriteIfExists)
        {
            try
            {
                //use the incoming file or folder name (without extension) as the song ID.  Since the filesystem has to have unique names, this should help keep songIDs unique also
                var targetSongID = Path.GetFileNameWithoutExtension(provider.SourceName);
                if (targetSongID.Contains("."))
                {
                    targetSongID = targetSongID.Substring(0, targetSongID.LastIndexOf("."));
                }

                //checking this first to maintain compability with song IDs that are just <songid> and are not <songid>_<name> - <author>
                var targetOutputDir = _qaeConfig.SongsPath.CombineFwdSlash(targetSongID);
                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);
                }

                var allFiles     = provider.FindFiles("*");
                var firstInfoDat = allFiles.FirstOrDefault(x => x.ToLower() == "info.dat");
                if (firstInfoDat == null)
                {
                    throw new ImportException($"Unable to find info.dat in provider {provider.SourceName}", $"Zip file {provider.SourceName} doesn't seem to be a song (no info.dat).");
                }

                //deserialize the info to get song name and stuff
                try
                {
                    var infoStr = provider.ReadToString(firstInfoDat);
                    var bml     = JsonConvert.DeserializeObject <BeatmapLevelDataObject>(infoStr);
                    if (bml == null)
                    {
                        throw new Exception("Info.dat returned null from deserializer.");
                    }
                    if (bml.SongName == null)
                    {
                        throw new Exception("Info.dat deserialized with a null SongName.");
                    }
                    var songName = $"{bml.SongName} - {bml.SongAuthorName}";
                    songName     = Regex.Replace(songName, "[^a-zA-Z0-9 -]", "");
                    targetSongID = $"{targetSongID}_{songName}";
                }
                catch (Exception ex)
                {
                    Log.LogErr($"Deserializing song info for file {provider.SourceName} failed", ex);
                    throw new ImportException($"Deserializing song info for file {provider.SourceName} failed", $"Info.dat file in zip {provider.SourceName} doesn't appear to be a valid song.", ex);
                }

                targetOutputDir = _qaeConfig.SongsPath.CombineFwdSlash(targetSongID);

                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);
                }
                if (!_qaeConfig.RootFileProvider.DirectoryExists(targetOutputDir))
                {
                    _qaeConfig.RootFileProvider.MkDir(targetOutputDir);
                }

                //get the path of where the info.dat is and assume that the rest of the files will be in the same directory as it.
                //  This is to handle zips that have duplicate info or nested folders
                var infoDatPath = firstInfoDat.GetDirectoryFwdSlash();
                foreach (var file in allFiles)
                {
                    try
                    {
                        if (Path.GetExtension(file).ToLower() == "zip")
                        {
                            Log.LogMsg($"Skipped {file} because it looks like a nested zip file.");
                            continue;
                        }
                        //if the file isn't in the same path as the located info.dat, skip it
                        if (file.GetDirectoryFwdSlash() != infoDatPath)
                        {
                            Log.LogMsg($"Skipped zip file {file} because it wasn't in the path with info.dat at {infoDatPath}");
                            continue;
                        }

                        var targetFile = targetOutputDir.CombineFwdSlash(file.GetFilenameFwdSlash());

                        using (Stream fs = _qaeConfig.RootFileProvider.GetWriteStream(targetFile))
                        {
                            using (Stream rs = provider.GetReadStream(file, true))
                            {
                                rs.CopyTo(fs);
                            }
                            //TODO: this won't work with zip file provider on the root because the stream has to stay open until Save()
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.LogErr($"Exception extracting song file {file} from provider {provider.SourceName}", ex);
                        throw new ImportException($"Exception extracting song file {file} from provider {provider.SourceName}", $"Unable to import song from {provider.SourceName}!  Extracting files failed!", ex);
                    }
                }
                return(targetOutputDir);
            }
            catch (Exception ex)
            {
                Log.LogErr($"Exception importing song from provider {provider.SourceName}", ex);
                throw new ImportException($"Exception importing song from provider {provider.SourceName}", $"Error importing song from {provider.SourceName}", ex);
            }
        }
Esempio n. 10
0
        private void BtnLoad_Click(object sender, EventArgs e)
        {
            ContextMenu cm = new ContextMenu(new MenuItem[]
            {
                new MenuItem("APK", (s, e2) =>
                {
                    OpenFileDialog ofd = new OpenFileDialog()
                    {
                        CheckFileExists = true,
                        Title           = "Open Bundle File",
                        Multiselect     = false
                    };
                    if (ofd.ShowDialog() == DialogResult.Cancel)
                    {
                        return;
                    }
                    CloseStuff();
                    try
                    {
                        _fileProvider = new ZipFileProvider(ofd.FileName, FileCacheMode.Memory, false);
                        _manager      = new AssetsManager(_fileProvider, BSConst.KnownFiles.AssetsRootPath, BSConst.GetAssetTypeMap());
                        if (_fileProvider.FindFiles("globalgamemanagers").Count > 0)
                        {
                            _manager.GetAssetsFile("globalgamemanagers.assets");
                        }
                        if (_fileProvider.FindFiles("globalgamemanagers.assets*").Count > 0)
                        {
                            _manager.GetAssetsFile("globalgamemanagers.assets");
                        }
                        _manager.FindAndLoadAllAssets();
                        FillAssetsFiles();
                        this.Text = "Assets Explorer - " + Path.GetFileName(ofd.FileName);
                    }
                    catch (Exception ex)
                    {
                        Log.LogErr("Couldn't load APK!", ex);
                        MessageBox.Show("Failed to load!");
                        if (_fileProvider != null)
                        {
                            _fileProvider.Dispose();
                            _fileProvider = null;
                        }
                        return;
                    }
                }),
                new MenuItem("Folder", (s, e2) =>
                {
                    FolderBrowserDialog fbd = new FolderBrowserDialog()
                    {
                        ShowNewFolderButton = false,
                        Description         = "Select Assets Root Folder"
                    };
                    if (fbd.ShowDialog() == DialogResult.Cancel)
                    {
                        return;
                    }
                    CloseStuff();
                    try
                    {
                        _fileProvider = new FolderFileProvider(fbd.SelectedPath, false);
                        _manager      = new AssetsManager(_fileProvider, "", BSConst.GetAssetTypeMap());
                        if (_fileProvider.FindFiles("globalgamemanagers").Count > 0)
                        {
                            _manager.GetAssetsFile("globalgamemanagers.assets");
                        }
                        if (_fileProvider.FindFiles("globalgamemanagers.assets*").Count > 0)
                        {
                            _manager.GetAssetsFile("globalgamemanagers.assets");
                        }
                        _manager.FindAndLoadAllAssets();
                        FillAssetsFiles();
                        this.Text = "Assets Explorer - " + Path.GetFileName(fbd.SelectedPath);
                    }
                    catch (Exception ex)
                    {
                        Log.LogErr("Couldn't load folder!", ex);
                        MessageBox.Show("Failed to load!");
                        if (_fileProvider != null)
                        {
                            _fileProvider.Dispose();
                            _fileProvider = null;
                        }
                        return;
                    }
                }),
                new MenuItem("Bundle", (s, e2) =>
                {
                    OpenFileDialog ofd = new OpenFileDialog()
                    {
                        CheckFileExists = true,
                        Title           = "Open Bundle File",
                        Multiselect     = false
                    };
                    if (ofd.ShowDialog() == DialogResult.Cancel)
                    {
                        return;
                    }
                    CloseStuff();
                    try
                    {
                        _fileProvider = new BundleFileProvider(ofd.FileName, true);
                        _manager      = new AssetsManager(_fileProvider, "", BSConst.GetAssetTypeMap());
                        _manager.FindAndLoadAllAssets();
                        FillAssetsFiles();
                        this.Text = "Assets Explorer - " + Path.GetFileName(ofd.FileName);
                    }
                    catch (Exception ex)
                    {
                        Log.LogErr("Couldn't load bundle!", ex);
                        MessageBox.Show("Failed to load!");
                        if (_fileProvider != null)
                        {
                            _fileProvider.Dispose();
                            _fileProvider = null;
                        }
                        return;
                    }
                })
            });

            cm.Show(btnLoad, new Point(0, btnLoad.Height));
            return;
        }