public static string PackageTheme(string themeDirectory, string targetPath, ApplicationMode mode) { var dirInfo = new DirectoryInfo(themeDirectory); var extInfo = ExtensionInstaller.GetThemeManifest(Path.Combine(themeDirectory, PlaynitePaths.ThemeManifestFileName)); if (extInfo.Id.IsNullOrEmpty()) { throw new Exception("Cannot package theme, ID is missing!"); } extInfo.VerifyManifest(); var apiVer = extInfo.Mode == ApplicationMode.Desktop ? ThemeManager.DesktopApiVersion : ThemeManager.FullscreenApiVersion; var themeApiVer = Version.Parse(extInfo.ThemeApiVersion); if (themeApiVer > apiVer) { throw new Exception($"Cannot package theme. Unsupported API version detected: {themeApiVer}"); } else if (themeApiVer != apiVer) { logger.Warn("Selected theme has not been updated to the latest supported API version. Please consider updating the theme!"); logger.Warn("https://github.com/JosefNemec/Playnite/issues/1259"); } var defaultThemeDir = Path.Combine(Paths.GetThemesPath(mode), "Default"); targetPath = Path.Combine(targetPath, $"{Common.Paths.GetSafeFilename(extInfo.Name).Replace(' ', '_')}_{extInfo.Version.ToString().Replace(".", "_")}{PlaynitePaths.PackedThemeFileExtention}"); FileSystem.PrepareSaveFile(targetPath); using (var zipStream = new FileStream(targetPath, FileMode.Create)) { using (var zipFile = new ZipArchive(zipStream, ZipArchiveMode.Create)) { zipFile.CreateEntryFromFile(Path.Combine(themeDirectory, PlaynitePaths.ThemeManifestFileName), PlaynitePaths.ThemeManifestFileName); foreach (var file in Directory.GetFiles(themeDirectory, "*.*", SearchOption.AllDirectories)) { var subName = file.Replace(themeDirectory, "").TrimStart(Path.DirectorySeparatorChar); if (file == targetPath || PackageFileBlackList.ContainsString(subName) || subName.StartsWith("Fonts\\") || subName.StartsWith(".vs\\") || subName.StartsWith("bin\\") || subName.StartsWith("obj\\") || subName.StartsWith("backup_")) { continue; } var defaultFile = Path.Combine(defaultThemeDir, subName); if (File.Exists(defaultFile)) { if (!AreFilesEqual(file, defaultFile)) { zipFile.CreateEntryFromFile(file, subName, CompressionLevel.Optimal); } } else { zipFile.CreateEntryFromFile(file, subName); } } } } return(targetPath); }